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
227 * @children Roo.bootstrap.Component
228 * Bootstrap Component base class
229 * @cfg {String} cls css class
230 * @cfg {String} style any extra css
231 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
233 * @cfg {string} dataId cutomer id
234 * @cfg {string} name Specifies name attribute
235 * @cfg {string} tooltip Text for the tooltip
236 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
237 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
240 * Do not use directly - it does not do anything..
241 * @param {Object} config The config object
246 Roo.bootstrap.Component = function(config){
247 Roo.bootstrap.Component.superclass.constructor.call(this, config);
251 * @event childrenrendered
252 * Fires when the children have been rendered..
253 * @param {Roo.bootstrap.Component} this
255 "childrenrendered" : true
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
267 allowDomMove : false, // to stop relocations in parent onRender...
277 * Initialize Events for the element
279 initEvents : function() { },
285 can_build_overlaid : true,
287 container_method : false,
294 // returns the parent component..
295 return Roo.ComponentMgr.get(this.parentId)
301 onRender : function(ct, position)
303 // Roo.log("Call onRender: " + this.xtype);
305 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
308 if (this.el.attr('xtype')) {
309 this.el.attr('xtypex', this.el.attr('xtype'));
310 this.el.dom.removeAttribute('xtype');
320 var cfg = Roo.apply({}, this.getAutoCreate());
322 cfg.id = this.id || Roo.id();
324 // fill in the extra attributes
325 if (this.xattr && typeof(this.xattr) =='object') {
326 for (var i in this.xattr) {
327 cfg[i] = this.xattr[i];
332 cfg.dataId = this.dataId;
336 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
339 if (this.style) { // fixme needs to support more complex style data.
340 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344 cfg.name = this.name;
347 this.el = ct.createChild(cfg, position);
350 this.tooltipEl().attr('tooltip', this.tooltip);
353 if(this.tabIndex !== undefined){
354 this.el.dom.setAttribute('tabIndex', this.tabIndex);
361 * Fetch the element to add children to
362 * @return {Roo.Element} defaults to this.el
364 getChildContainer : function()
368 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
370 return Roo.get(document.body);
374 * Fetch the element to display the tooltip on.
375 * @return {Roo.Element} defaults to this.el
377 tooltipEl : function()
382 addxtype : function(tree,cntr)
386 cn = Roo.factory(tree);
387 //Roo.log(['addxtype', cn]);
389 cn.parentType = this.xtype; //??
390 cn.parentId = this.id;
392 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393 if (typeof(cn.container_method) == 'string') {
394 cntr = cn.container_method;
398 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
400 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
402 var build_from_html = Roo.XComponent.build_from_html;
404 var is_body = (tree.xtype == 'Body') ;
406 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
408 var self_cntr_el = Roo.get(this[cntr](false));
410 // do not try and build conditional elements
411 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417 return this.addxtypeChild(tree,cntr, is_body);
420 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
423 return this.addxtypeChild(Roo.apply({}, tree),cntr);
426 Roo.log('skipping render');
432 if (!build_from_html) {
436 // this i think handles overlaying multiple children of the same type
437 // with the sam eelement.. - which might be buggy..
439 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
445 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456 addxtypeChild : function (tree, cntr, is_body)
458 Roo.debug && Roo.log('addxtypeChild:' + cntr);
460 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
463 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464 (typeof(tree['flexy:foreach']) != 'undefined');
468 skip_children = false;
469 // render the element if it's not BODY.
472 // if parent was disabled, then do not try and create the children..
473 if(!this[cntr](true)){
478 cn = Roo.factory(tree);
480 cn.parentType = this.xtype; //??
481 cn.parentId = this.id;
483 var build_from_html = Roo.XComponent.build_from_html;
486 // does the container contain child eleemnts with 'xtype' attributes.
487 // that match this xtype..
488 // note - when we render we create these as well..
489 // so we should check to see if body has xtype set.
490 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
492 var self_cntr_el = Roo.get(this[cntr](false));
493 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
495 //Roo.log(Roo.XComponent.build_from_html);
496 //Roo.log("got echild:");
499 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500 // and are not displayed -this causes this to use up the wrong element when matching.
501 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
504 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
511 //echild.dom.removeAttribute('xtype');
513 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514 Roo.debug && Roo.log(self_cntr_el);
515 Roo.debug && Roo.log(echild);
516 Roo.debug && Roo.log(cn);
522 // if object has flexy:if - then it may or may not be rendered.
523 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
524 // skip a flexy if element.
525 Roo.debug && Roo.log('skipping render');
526 Roo.debug && Roo.log(tree);
528 Roo.debug && Roo.log('skipping all children');
529 skip_children = true;
534 // actually if flexy:foreach is found, we really want to create
535 // multiple copies here...
537 //Roo.log(this[cntr]());
538 // some elements do not have render methods.. like the layouts...
540 if(this[cntr](true) === false){
545 cn.render && cn.render(this[cntr](true));
548 // then add the element..
555 if (typeof (tree.menu) != 'undefined') {
556 tree.menu.parentType = cn.xtype;
557 tree.menu.triggerEl = cn.el;
558 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562 if (!tree.items || !tree.items.length) {
564 //Roo.log(["no children", this]);
569 var items = tree.items;
572 //Roo.log(items.length);
574 if (!skip_children) {
575 for(var i =0;i < items.length;i++) {
576 // Roo.log(['add child', items[i]]);
577 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
583 //Roo.log("fire childrenrendered");
585 cn.fireEvent('childrenrendered', this);
591 * Set the element that will be used to show or hide
593 setVisibilityEl : function(el)
595 this.visibilityEl = el;
599 * Get the element that will be used to show or hide
601 getVisibilityEl : function()
603 if (typeof(this.visibilityEl) == 'object') {
604 return this.visibilityEl;
607 if (typeof(this.visibilityEl) == 'string') {
608 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
615 * Show a component - removes 'hidden' class
619 if(!this.getVisibilityEl()){
623 this.getVisibilityEl().removeClass(['hidden','d-none']);
625 this.fireEvent('show', this);
630 * Hide a component - adds 'hidden' class
634 if(!this.getVisibilityEl()){
638 this.getVisibilityEl().addClass(['hidden','d-none']);
640 this.fireEvent('hide', this);
653 * @class Roo.bootstrap.Element
654 * @extends Roo.bootstrap.Component
655 * @children Roo.bootstrap.Component
656 * Bootstrap Element class (basically a DIV used to make random stuff )
658 * @cfg {String} html contents of the element
659 * @cfg {String} tag tag of the element
660 * @cfg {String} cls class of the element
661 * @cfg {Boolean} preventDefault (true|false) default false
662 * @cfg {Boolean} clickable (true|false) default false
663 * @cfg {String} role default blank - set to button to force cursor pointer
667 * Create a new Element
668 * @param {Object} config The config object
671 Roo.bootstrap.Element = function(config){
672 Roo.bootstrap.Element.superclass.constructor.call(this, config);
678 * When a element is chick
679 * @param {Roo.bootstrap.Element} this
680 * @param {Roo.EventObject} e
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
693 preventDefault: false,
698 getAutoCreate : function(){
702 // cls: this.cls, double assign in parent class Component.js :: onRender
705 if (this.role !== false) {
706 cfg.role = this.role;
712 initEvents: function()
714 Roo.bootstrap.Element.superclass.initEvents.call(this);
717 this.el.on('click', this.onClick, this);
723 onClick : function(e)
725 if(this.preventDefault){
729 this.fireEvent('click', this, e); // why was this double click before?
737 getValue : function()
739 return this.el.dom.innerHTML;
742 setValue : function(value)
744 this.el.dom.innerHTML = value;
759 * @class Roo.bootstrap.DropTarget
760 * @extends Roo.bootstrap.Element
761 * Bootstrap DropTarget class
763 * @cfg {string} name dropable name
766 * Create a new Dropable Area
767 * @param {Object} config The config object
770 Roo.bootstrap.DropTarget = function(config){
771 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
777 * When a element is chick
778 * @param {Roo.bootstrap.Element} this
779 * @param {Roo.EventObject} e
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
788 getAutoCreate : function(){
793 initEvents: function()
795 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
799 drop : this.dragDrop.createDelegate(this),
800 enter : this.dragEnter.createDelegate(this),
801 out : this.dragOut.createDelegate(this),
802 over : this.dragOver.createDelegate(this)
806 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
809 dragDrop : function(source,e,data)
811 // user has to decide how to impliment this.
814 //this.fireEvent('drop', this, source, e ,data);
818 dragEnter : function(n, dd, e, data)
820 // probably want to resize the element to match the dropped element..
822 this.originalSize = this.el.getSize();
823 this.el.setSize( n.el.getSize());
824 this.dropZone.DDM.refreshCache(this.name);
825 Roo.log([n, dd, e, data]);
828 dragOut : function(value)
830 // resize back to normal
832 this.el.setSize(this.originalSize);
833 this.dropZone.resetConstraints();
836 dragOver : function()
853 * @class Roo.bootstrap.Body
854 * @extends Roo.bootstrap.Component
856 * @children Roo.bootstrap.Component
858 * Bootstrap Body class
862 * @param {Object} config The config object
865 Roo.bootstrap.Body = function(config){
867 config = config || {};
869 Roo.bootstrap.Body.superclass.constructor.call(this, config);
870 this.el = Roo.get(config.el ? config.el : document.body );
871 if (this.cls && this.cls.length) {
872 Roo.get(document.body).addClass(this.cls);
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
878 is_body : true,// just to make sure it's constructed?
883 onRender : function(ct, position)
885 /* Roo.log("Roo.bootstrap.Body - onRender");
886 if (this.cls && this.cls.length) {
887 Roo.get(document.body).addClass(this.cls);
906 * @class Roo.bootstrap.ButtonGroup
907 * @extends Roo.bootstrap.Component
908 * Bootstrap ButtonGroup class
909 * @cfg {String} size lg | sm | xs (default empty normal)
910 * @cfg {String} align vertical | justified (default none)
911 * @cfg {String} direction up | down (default down)
912 * @cfg {Boolean} toolbar false | true
913 * @cfg {Boolean} btn true | false
918 * @param {Object} config The config object
921 Roo.bootstrap.ButtonGroup = function(config){
922 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
925 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
933 getAutoCreate : function(){
939 cfg.html = this.html || cfg.html;
950 if (['vertical','justified'].indexOf(this.align)!==-1) {
951 cfg.cls = 'btn-group-' + this.align;
953 if (this.align == 'justified') {
954 console.log(this.items);
958 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
959 cfg.cls += ' btn-group-' + this.size;
962 if (this.direction == 'up') {
963 cfg.cls += ' dropup' ;
969 * Add a button to the group (similar to NavItem API.)
971 addItem : function(cfg)
973 var cn = new Roo.bootstrap.Button(cfg);
975 cn.parentId = this.id;
976 cn.onRender(this.el, null);
990 * @class Roo.bootstrap.Button
991 * @extends Roo.bootstrap.Component
992 * Bootstrap Button class
993 * @cfg {String} html The button content
994 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
995 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
996 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
997 * @cfg {String} size (lg|sm|xs)
998 * @cfg {String} tag (a|input|submit)
999 * @cfg {String} href empty or href
1000 * @cfg {Boolean} disabled default false;
1001 * @cfg {Boolean} isClose default false;
1002 * @cfg {String} glyphicon depricated - use fa
1003 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1004 * @cfg {String} badge text for badge
1005 * @cfg {String} theme (default|glow)
1006 * @cfg {Boolean} inverse dark themed version
1007 * @cfg {Boolean} toggle is it a slidy toggle button
1008 * @cfg {Boolean} pressed default null - if the button ahs active state
1009 * @cfg {String} ontext text for on slidy toggle state
1010 * @cfg {String} offtext text for off slidy toggle state
1011 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1012 * @cfg {Boolean} removeClass remove the standard class..
1013 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1014 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1017 * Create a new button
1018 * @param {Object} config The config object
1022 Roo.bootstrap.Button = function(config){
1023 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029 * When a button is pressed
1030 * @param {Roo.bootstrap.Button} btn
1031 * @param {Roo.EventObject} e
1036 * When a button is double clicked
1037 * @param {Roo.bootstrap.Button} btn
1038 * @param {Roo.EventObject} e
1043 * After the button has been toggles
1044 * @param {Roo.bootstrap.Button} btn
1045 * @param {Roo.EventObject} e
1046 * @param {boolean} pressed (also available as button.pressed)
1052 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1073 preventDefault: true,
1082 getAutoCreate : function(){
1090 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1091 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1092 this.tag = 'button';
1096 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1098 if (this.toggle == true) {
1101 cls: 'slider-frame roo-button',
1105 'data-on-text':'ON',
1106 'data-off-text':'OFF',
1107 cls: 'slider-button',
1112 // why are we validating the weights?
1113 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1114 cfg.cls += ' ' + this.weight;
1121 cfg.cls += ' close';
1123 cfg["aria-hidden"] = true;
1125 cfg.html = "×";
1131 if (this.theme==='default') {
1132 cfg.cls = 'btn roo-button';
1134 //if (this.parentType != 'Navbar') {
1135 this.weight = this.weight.length ? this.weight : 'default';
1137 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1139 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1140 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1141 cfg.cls += ' btn-' + outline + weight;
1142 if (this.weight == 'default') {
1144 cfg.cls += ' btn-' + this.weight;
1147 } else if (this.theme==='glow') {
1150 cfg.cls = 'btn-glow roo-button';
1152 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1154 cfg.cls += ' ' + this.weight;
1160 this.cls += ' inverse';
1164 if (this.active || this.pressed === true) {
1165 cfg.cls += ' active';
1168 if (this.disabled) {
1169 cfg.disabled = 'disabled';
1173 Roo.log('changing to ul' );
1175 this.glyphicon = 'caret';
1176 if (Roo.bootstrap.version == 4) {
1177 this.fa = 'caret-down';
1182 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1184 //gsRoo.log(this.parentType);
1185 if (this.parentType === 'Navbar' && !this.parent().bar) {
1186 Roo.log('changing to li?');
1195 href : this.href || '#'
1198 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1199 cfg.cls += ' dropdown';
1206 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1208 if (this.glyphicon) {
1209 cfg.html = ' ' + cfg.html;
1214 cls: 'glyphicon glyphicon-' + this.glyphicon
1219 cfg.html = ' ' + cfg.html;
1224 cls: 'fa fas fa-' + this.fa
1234 // cfg.cls='btn roo-button';
1238 var value = cfg.html;
1243 cls: 'glyphicon glyphicon-' + this.glyphicon,
1250 cls: 'fa fas fa-' + this.fa,
1255 var bw = this.badge_weight.length ? this.badge_weight :
1256 (this.weight.length ? this.weight : 'secondary');
1257 bw = bw == 'default' ? 'secondary' : bw;
1263 cls: 'badge badge-' + bw,
1272 cfg.cls += ' dropdown';
1273 cfg.html = typeof(cfg.html) != 'undefined' ?
1274 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1277 if (cfg.tag !== 'a' && this.href !== '') {
1278 throw "Tag must be a to set href.";
1279 } else if (this.href.length > 0) {
1280 cfg.href = this.href;
1283 if(this.removeClass){
1288 cfg.target = this.target;
1293 initEvents: function() {
1294 // Roo.log('init events?');
1295 // Roo.log(this.el.dom);
1298 if (typeof (this.menu) != 'undefined') {
1299 this.menu.parentType = this.xtype;
1300 this.menu.triggerEl = this.el;
1301 this.addxtype(Roo.apply({}, this.menu));
1305 if (this.el.hasClass('roo-button')) {
1306 this.el.on('click', this.onClick, this);
1307 this.el.on('dblclick', this.onDblClick, this);
1309 this.el.select('.roo-button').on('click', this.onClick, this);
1310 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1314 if(this.removeClass){
1315 this.el.on('click', this.onClick, this);
1318 if (this.group === true) {
1319 if (this.pressed === false || this.pressed === true) {
1322 this.pressed = false;
1323 this.setActive(this.pressed);
1328 this.el.enableDisplayMode();
1331 onClick : function(e)
1333 if (this.disabled) {
1337 Roo.log('button on click ');
1338 if(this.preventDefault){
1347 this.setActive(true);
1348 var pi = this.parent().items;
1349 for (var i = 0;i < pi.length;i++) {
1350 if (this == pi[i]) {
1353 if (pi[i].el.hasClass('roo-button')) {
1354 pi[i].setActive(false);
1357 this.fireEvent('click', this, e);
1361 if (this.pressed === true || this.pressed === false) {
1362 this.toggleActive(e);
1366 this.fireEvent('click', this, e);
1368 onDblClick: function(e)
1370 if (this.disabled) {
1373 if(this.preventDefault){
1376 this.fireEvent('dblclick', this, e);
1379 * Enables this button
1383 this.disabled = false;
1384 this.el.removeClass('disabled');
1385 this.el.dom.removeAttribute("disabled");
1389 * Disable this button
1391 disable : function()
1393 this.disabled = true;
1394 this.el.addClass('disabled');
1395 this.el.attr("disabled", "disabled")
1398 * sets the active state on/off,
1399 * @param {Boolean} state (optional) Force a particular state
1401 setActive : function(v) {
1403 this.el[v ? 'addClass' : 'removeClass']('active');
1407 * toggles the current active state
1409 toggleActive : function(e)
1411 this.setActive(!this.pressed); // this modifies pressed...
1412 this.fireEvent('toggle', this, e, this.pressed);
1415 * get the current active state
1416 * @return {boolean} true if it's active
1418 isActive : function()
1420 return this.el.hasClass('active');
1423 * set the text of the first selected button
1425 setText : function(str)
1427 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1430 * get the text of the first selected button
1432 getText : function()
1434 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1437 setWeight : function(str)
1439 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1440 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1442 var outline = this.outline ? 'outline-' : '';
1443 if (str == 'default') {
1444 this.el.addClass('btn-default btn-outline-secondary');
1447 this.el.addClass('btn-' + outline + str);
1452 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1454 Roo.bootstrap.Button.weights = [
1474 * @class Roo.bootstrap.Column
1475 * @extends Roo.bootstrap.Component
1476 * @children Roo.bootstrap.Component
1477 * Bootstrap Column class
1478 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1479 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1480 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1481 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1482 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1483 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1484 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1485 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1488 * @cfg {Boolean} hidden (true|false) hide the element
1489 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1490 * @cfg {String} fa (ban|check|...) font awesome icon
1491 * @cfg {Number} fasize (1|2|....) font awsome size
1493 * @cfg {String} icon (info-sign|check|...) glyphicon name
1495 * @cfg {String} html content of column.
1498 * Create a new Column
1499 * @param {Object} config The config object
1502 Roo.bootstrap.Column = function(config){
1503 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1506 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1524 getAutoCreate : function(){
1525 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1533 var sizes = ['xs','sm','md','lg'];
1534 sizes.map(function(size ,ix){
1535 //Roo.log( size + ':' + settings[size]);
1537 if (settings[size+'off'] !== false) {
1538 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1541 if (settings[size] === false) {
1545 if (!settings[size]) { // 0 = hidden
1546 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1548 for (var i = ix; i > -1; i--) {
1549 cfg.cls += ' d-' + sizes[i] + '-none';
1555 cfg.cls += ' col-' + size + '-' + settings[size] + (
1556 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562 cfg.cls += ' hidden';
1565 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1566 cfg.cls +=' alert alert-' + this.alert;
1570 if (this.html.length) {
1571 cfg.html = this.html;
1575 if (this.fasize > 1) {
1576 fasize = ' fa-' + this.fasize + 'x';
1578 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1583 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1602 * @class Roo.bootstrap.Container
1603 * @extends Roo.bootstrap.Component
1605 * @children Roo.bootstrap.Component
1606 * Bootstrap Container class
1607 * @cfg {Boolean} jumbotron is it a jumbotron element
1608 * @cfg {String} html content of element
1609 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1610 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1611 * @cfg {String} header content of header (for panel)
1612 * @cfg {String} footer content of footer (for panel)
1613 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1614 * @cfg {String} tag (header|aside|section) type of HTML tag.
1615 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1616 * @cfg {String} fa font awesome icon
1617 * @cfg {String} icon (info-sign|check|...) glyphicon name
1618 * @cfg {Boolean} hidden (true|false) hide the element
1619 * @cfg {Boolean} expandable (true|false) default false
1620 * @cfg {Boolean} expanded (true|false) default true
1621 * @cfg {String} rheader contet on the right of header
1622 * @cfg {Boolean} clickable (true|false) default false
1626 * Create a new Container
1627 * @param {Object} config The config object
1630 Roo.bootstrap.Container = function(config){
1631 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637 * After the panel has been expand
1639 * @param {Roo.bootstrap.Container} this
1644 * After the panel has been collapsed
1646 * @param {Roo.bootstrap.Container} this
1651 * When a element is chick
1652 * @param {Roo.bootstrap.Container} this
1653 * @param {Roo.EventObject} e
1659 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1677 getChildContainer : function() {
1683 if (this.panel.length) {
1684 return this.el.select('.panel-body',true).first();
1691 getAutoCreate : function(){
1694 tag : this.tag || 'div',
1698 if (this.jumbotron) {
1699 cfg.cls = 'jumbotron';
1704 // - this is applied by the parent..
1706 // cfg.cls = this.cls + '';
1709 if (this.sticky.length) {
1711 var bd = Roo.get(document.body);
1712 if (!bd.hasClass('bootstrap-sticky')) {
1713 bd.addClass('bootstrap-sticky');
1714 Roo.select('html',true).setStyle('height', '100%');
1717 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1721 if (this.well.length) {
1722 switch (this.well) {
1725 cfg.cls +=' well well-' +this.well;
1734 cfg.cls += ' hidden';
1738 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1739 cfg.cls +=' alert alert-' + this.alert;
1744 if (this.panel.length) {
1745 cfg.cls += ' panel panel-' + this.panel;
1747 if (this.header.length) {
1751 if(this.expandable){
1753 cfg.cls = cfg.cls + ' expandable';
1757 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1765 cls : 'panel-title',
1766 html : (this.expandable ? ' ' : '') + this.header
1770 cls: 'panel-header-right',
1776 cls : 'panel-heading',
1777 style : this.expandable ? 'cursor: pointer' : '',
1785 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1790 if (this.footer.length) {
1792 cls : 'panel-footer',
1801 body.html = this.html || cfg.html;
1802 // prefix with the icons..
1804 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1807 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1812 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1813 cfg.cls = 'container';
1819 initEvents: function()
1821 if(this.expandable){
1822 var headerEl = this.headerEl();
1825 headerEl.on('click', this.onToggleClick, this);
1830 this.el.on('click', this.onClick, this);
1835 onToggleClick : function()
1837 var headerEl = this.headerEl();
1853 if(this.fireEvent('expand', this)) {
1855 this.expanded = true;
1857 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1859 this.el.select('.panel-body',true).first().removeClass('hide');
1861 var toggleEl = this.toggleEl();
1867 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1872 collapse : function()
1874 if(this.fireEvent('collapse', this)) {
1876 this.expanded = false;
1878 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1879 this.el.select('.panel-body',true).first().addClass('hide');
1881 var toggleEl = this.toggleEl();
1887 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1891 toggleEl : function()
1893 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1897 return this.el.select('.panel-heading .fa',true).first();
1900 headerEl : function()
1902 if(!this.el || !this.panel.length || !this.header.length){
1906 return this.el.select('.panel-heading',true).first()
1911 if(!this.el || !this.panel.length){
1915 return this.el.select('.panel-body',true).first()
1918 titleEl : function()
1920 if(!this.el || !this.panel.length || !this.header.length){
1924 return this.el.select('.panel-title',true).first();
1927 setTitle : function(v)
1929 var titleEl = this.titleEl();
1935 titleEl.dom.innerHTML = v;
1938 getTitle : function()
1941 var titleEl = this.titleEl();
1947 return titleEl.dom.innerHTML;
1950 setRightTitle : function(v)
1952 var t = this.el.select('.panel-header-right',true).first();
1958 t.dom.innerHTML = v;
1961 onClick : function(e)
1965 this.fireEvent('click', this, e);
1972 * This is BS4's Card element.. - similar to our containers probably..
1976 * @class Roo.bootstrap.Card
1977 * @extends Roo.bootstrap.Component
1978 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <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>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * Bootstrap CardHeader class
2782 * Create a new Card Header - that you can embed children into
2783 * @param {Object} config The config object
2786 Roo.bootstrap.CardHeader = function(config){
2787 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2793 container_method : 'getCardHeader'
2806 * Card footer - holder for the card footer elements.
2811 * @class Roo.bootstrap.CardFooter
2812 * @extends Roo.bootstrap.Element
2813 * Bootstrap CardFooter class
2815 * Create a new Card Footer - that you can embed children into
2816 * @param {Object} config The config object
2819 Roo.bootstrap.CardFooter = function(config){
2820 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2823 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2826 container_method : 'getCardFooter'
2839 * Card header - holder for the card header elements.
2844 * @class Roo.bootstrap.CardImageTop
2845 * @extends Roo.bootstrap.Element
2846 * Bootstrap CardImageTop class
2848 * Create a new Card Image Top container
2849 * @param {Object} config The config object
2852 Roo.bootstrap.CardImageTop = function(config){
2853 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2856 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2859 container_method : 'getCardImageTop'
2874 * @class Roo.bootstrap.ButtonUploader
2875 * @extends Roo.bootstrap.Button
2876 * Bootstrap Button Uploader class - it's a button which when you add files to it
2879 * @cfg {Number} errorTimeout default 3000
2880 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2881 * @cfg {Array} html The button text.
2882 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2885 * Create a new CardUploader
2886 * @param {Object} config The config object
2889 Roo.bootstrap.ButtonUploader = function(config){
2893 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2899 * @event beforeselect
2900 * When button is pressed, before show upload files dialog is shown
2901 * @param {Roo.bootstrap.UploaderButton} this
2904 'beforeselect' : true,
2906 * @event fired when files have been selected,
2907 * When a the download link is clicked
2908 * @param {Roo.bootstrap.UploaderButton} this
2909 * @param {Array} Array of files that have been uploaded
2916 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2919 errorTimeout : 3000,
2923 fileCollection : false,
2928 getAutoCreate : function()
2933 cls : 'd-none roo-card-upload-selector'
2936 if (this.multiple) {
2937 im.multiple = 'multiple';
2943 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2953 initEvents : function()
2956 Roo.bootstrap.Button.prototype.initEvents.call(this);
2962 this.urlAPI = (window.createObjectURL && window) ||
2963 (window.URL && URL.revokeObjectURL && URL) ||
2964 (window.webkitURL && webkitURL);
2969 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2971 this.selectorEl.on('change', this.onFileSelected, this);
2978 onClick : function(e)
2982 if ( this.fireEvent('beforeselect', this) === false) {
2986 this.selectorEl.dom.click();
2990 onFileSelected : function(e)
2994 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2997 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2998 this.selectorEl.dom.value = '';// hopefully reset..
3000 this.fireEvent('uploaded', this, files );
3008 * addCard - add an Attachment to the uploader
3009 * @param data - the data about the image to upload
3013 title : "Title of file",
3014 is_uploaded : false,
3015 src : "http://.....",
3016 srcfile : { the File upload object },
3017 mimetype : file.type,
3020 .. any other data...
3045 * @class Roo.bootstrap.Img
3046 * @extends Roo.bootstrap.Component
3047 * Bootstrap Img class
3048 * @cfg {Boolean} imgResponsive false | true
3049 * @cfg {String} border rounded | circle | thumbnail
3050 * @cfg {String} src image source
3051 * @cfg {String} alt image alternative text
3052 * @cfg {String} href a tag href
3053 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3054 * @cfg {String} xsUrl xs image source
3055 * @cfg {String} smUrl sm image source
3056 * @cfg {String} mdUrl md image source
3057 * @cfg {String} lgUrl lg image source
3058 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3061 * Create a new Input
3062 * @param {Object} config The config object
3065 Roo.bootstrap.Img = function(config){
3066 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3072 * The img click event for the img.
3073 * @param {Roo.EventObject} e
3078 * The when any image loads
3079 * @param {Roo.EventObject} e
3085 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3087 imgResponsive: true,
3096 backgroundContain : false,
3098 getAutoCreate : function()
3100 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3101 return this.createSingleImg();
3106 cls: 'roo-image-responsive-group',
3111 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3113 if(!_this[size + 'Url']){
3119 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3120 html: _this.html || cfg.html,
3121 src: _this[size + 'Url']
3124 img.cls += ' roo-image-responsive-' + size;
3126 var s = ['xs', 'sm', 'md', 'lg'];
3128 s.splice(s.indexOf(size), 1);
3130 Roo.each(s, function(ss){
3131 img.cls += ' hidden-' + ss;
3134 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3135 cfg.cls += ' img-' + _this.border;
3139 cfg.alt = _this.alt;
3152 a.target = _this.target;
3156 cfg.cn.push((_this.href) ? a : img);
3163 createSingleImg : function()
3167 cls: (this.imgResponsive) ? 'img-responsive' : '',
3169 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3172 if (this.backgroundContain) {
3173 cfg.cls += ' background-contain';
3176 cfg.html = this.html || cfg.html;
3178 if (this.backgroundContain) {
3179 cfg.style="background-image: url(" + this.src + ')';
3181 cfg.src = this.src || cfg.src;
3184 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3185 cfg.cls += ' img-' + this.border;
3202 a.target = this.target;
3207 return (this.href) ? a : cfg;
3210 initEvents: function()
3213 this.el.on('click', this.onClick, this);
3215 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3216 this.el.on('load', this.onImageLoad, this);
3218 // not sure if this works.. not tested
3219 this.el.select('img', true).on('load', this.onImageLoad, this);
3224 onClick : function(e)
3226 Roo.log('img onclick');
3227 this.fireEvent('click', this, e);
3229 onImageLoad: function(e)
3231 Roo.log('img load');
3232 this.fireEvent('load', this, e);
3236 * Sets the url of the image - used to update it
3237 * @param {String} url the url of the image
3240 setSrc : function(url)
3244 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3245 if (this.backgroundContain) {
3246 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3248 this.el.dom.src = url;
3253 this.el.select('img', true).first().dom.src = url;
3269 * @class Roo.bootstrap.Link
3270 * @extends Roo.bootstrap.Component
3271 * @children Roo.bootstrap.Component
3272 * Bootstrap Link Class (eg. '<a href>')
3274 * @cfg {String} alt image alternative text
3275 * @cfg {String} href a tag href
3276 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3277 * @cfg {String} html the content of the link.
3278 * @cfg {String} anchor name for the anchor link
3279 * @cfg {String} fa - favicon
3281 * @cfg {Boolean} preventDefault (true | false) default false
3285 * Create a new Input
3286 * @param {Object} config The config object
3289 Roo.bootstrap.Link = function(config){
3290 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3296 * The img click event for the img.
3297 * @param {Roo.EventObject} e
3303 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3307 preventDefault: false,
3313 getAutoCreate : function()
3315 var html = this.html || '';
3317 if (this.fa !== false) {
3318 html = '<i class="fa fa-' + this.fa + '"></i>';
3323 // anchor's do not require html/href...
3324 if (this.anchor === false) {
3326 cfg.href = this.href || '#';
3328 cfg.name = this.anchor;
3329 if (this.html !== false || this.fa !== false) {
3332 if (this.href !== false) {
3333 cfg.href = this.href;
3337 if(this.alt !== false){
3342 if(this.target !== false) {
3343 cfg.target = this.target;
3349 initEvents: function() {
3351 if(!this.href || this.preventDefault){
3352 this.el.on('click', this.onClick, this);
3356 onClick : function(e)
3358 if(this.preventDefault){
3361 //Roo.log('img onclick');
3362 this.fireEvent('click', this, e);
3375 * @class Roo.bootstrap.Header
3376 * @extends Roo.bootstrap.Component
3377 * Bootstrap Header class
3378 * @cfg {String} html content of header
3379 * @cfg {Number} level (1|2|3|4|5|6) default 1
3382 * Create a new Header
3383 * @param {Object} config The config object
3387 Roo.bootstrap.Header = function(config){
3388 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3391 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3399 getAutoCreate : function(){
3404 tag: 'h' + (1 *this.level),
3405 html: this.html || ''
3417 * Ext JS Library 1.1.1
3418 * Copyright(c) 2006-2007, Ext JS, LLC.
3420 * Originally Released Under LGPL - original licence link has changed is not relivant.
3423 * <script type="text/javascript">
3427 * @class Roo.bootstrap.MenuMgr
3428 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431 Roo.bootstrap.MenuMgr = function(){
3432 var menus, active, groups = {}, attached = false, lastShow = new Date();
3434 // private - called when first menu is created
3437 active = new Roo.util.MixedCollection();
3438 Roo.get(document).addKeyListener(27, function(){
3439 if(active.length > 0){
3447 if(active && active.length > 0){
3448 var c = active.clone();
3458 if(active.length < 1){
3459 Roo.get(document).un("mouseup", onMouseDown);
3467 var last = active.last();
3468 lastShow = new Date();
3471 Roo.get(document).on("mouseup", onMouseDown);
3476 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3477 m.parentMenu.activeChild = m;
3478 }else if(last && last.isVisible()){
3479 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3484 function onBeforeHide(m){
3486 m.activeChild.hide();
3488 if(m.autoHideTimer){
3489 clearTimeout(m.autoHideTimer);
3490 delete m.autoHideTimer;
3495 function onBeforeShow(m){
3496 var pm = m.parentMenu;
3497 if(!pm && !m.allowOtherMenus){
3499 }else if(pm && pm.activeChild && active != m){
3500 pm.activeChild.hide();
3504 // private this should really trigger on mouseup..
3505 function onMouseDown(e){
3506 Roo.log("on Mouse Up");
3508 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3509 Roo.log("MenuManager hideAll");
3518 function onBeforeCheck(mi, state){
3520 var g = groups[mi.group];
3521 for(var i = 0, l = g.length; i < l; i++){
3523 g[i].setChecked(false);
3532 * Hides all menus that are currently visible
3534 hideAll : function(){
3539 register : function(menu){
3543 menus[menu.id] = menu;
3544 menu.on("beforehide", onBeforeHide);
3545 menu.on("hide", onHide);
3546 menu.on("beforeshow", onBeforeShow);
3547 menu.on("show", onShow);
3549 if(g && menu.events["checkchange"]){
3553 groups[g].push(menu);
3554 menu.on("checkchange", onCheck);
3559 * Returns a {@link Roo.menu.Menu} object
3560 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3561 * be used to generate and return a new Menu instance.
3563 get : function(menu){
3564 if(typeof menu == "string"){ // menu id
3566 }else if(menu.events){ // menu instance
3569 /*else if(typeof menu.length == 'number'){ // array of menu items?
3570 return new Roo.bootstrap.Menu({items:menu});
3571 }else{ // otherwise, must be a config
3572 return new Roo.bootstrap.Menu(menu);
3579 unregister : function(menu){
3580 delete menus[menu.id];
3581 menu.un("beforehide", onBeforeHide);
3582 menu.un("hide", onHide);
3583 menu.un("beforeshow", onBeforeShow);
3584 menu.un("show", onShow);
3586 if(g && menu.events["checkchange"]){
3587 groups[g].remove(menu);
3588 menu.un("checkchange", onCheck);
3593 registerCheckable : function(menuItem){
3594 var g = menuItem.group;
3599 groups[g].push(menuItem);
3600 menuItem.on("beforecheckchange", onBeforeCheck);
3605 unregisterCheckable : function(menuItem){
3606 var g = menuItem.group;
3608 groups[g].remove(menuItem);
3609 menuItem.un("beforecheckchange", onBeforeCheck);
3621 * @class Roo.bootstrap.Menu
3622 * @extends Roo.bootstrap.Component
3623 * Bootstrap Menu class - container for MenuItems
3624 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625 * @cfg {bool} hidden if the menu should be hidden when rendered.
3626 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3627 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3628 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3629 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3633 * @param {Object} config The config object
3637 Roo.bootstrap.Menu = function(config){
3639 if (config.type == 'treeview') {
3640 // normally menu's are drawn attached to the document to handle layering etc..
3641 // however treeview (used by the docs menu is drawn into the parent element)
3642 this.container_method = 'getChildContainer';
3645 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3646 if (this.registerMenu && this.type != 'treeview') {
3647 Roo.bootstrap.MenuMgr.register(this);
3654 * Fires before this menu is displayed (return false to block)
3655 * @param {Roo.menu.Menu} this
3660 * Fires before this menu is hidden (return false to block)
3661 * @param {Roo.menu.Menu} this
3666 * Fires after this menu is displayed
3667 * @param {Roo.menu.Menu} this
3672 * Fires after this menu is hidden
3673 * @param {Roo.menu.Menu} this
3678 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679 * @param {Roo.menu.Menu} this
3680 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681 * @param {Roo.EventObject} e
3686 * Fires when the mouse is hovering over this menu
3687 * @param {Roo.menu.Menu} this
3688 * @param {Roo.EventObject} e
3689 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3694 * Fires when the mouse exits this menu
3695 * @param {Roo.menu.Menu} this
3696 * @param {Roo.EventObject} e
3697 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3702 * Fires when a menu item contained in this menu is clicked
3703 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704 * @param {Roo.EventObject} e
3708 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3711 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3715 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3718 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3720 registerMenu : true,
3722 menuItems :false, // stores the menu items..
3732 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3734 hideTrigger : false,
3739 getChildContainer : function() {
3743 getAutoCreate : function(){
3745 //if (['right'].indexOf(this.align)!==-1) {
3746 // cfg.cn[1].cls += ' pull-right'
3751 cls : 'dropdown-menu shadow' ,
3752 style : 'z-index:1000'
3756 if (this.type === 'submenu') {
3757 cfg.cls = 'submenu active';
3759 if (this.type === 'treeview') {
3760 cfg.cls = 'treeview-menu';
3765 initEvents : function() {
3767 // Roo.log("ADD event");
3768 // Roo.log(this.triggerEl.dom);
3769 if (this.triggerEl) {
3771 this.triggerEl.on('click', this.onTriggerClick, this);
3773 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3775 if (!this.hideTrigger) {
3776 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777 // dropdown toggle on the 'a' in BS4?
3778 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3780 this.triggerEl.addClass('dropdown-toggle');
3786 this.el.on('touchstart' , this.onTouch, this);
3788 this.el.on('click' , this.onClick, this);
3790 this.el.on("mouseover", this.onMouseOver, this);
3791 this.el.on("mouseout", this.onMouseOut, this);
3795 findTargetItem : function(e)
3797 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3801 //Roo.log(t); Roo.log(t.id);
3803 //Roo.log(this.menuitems);
3804 return this.menuitems.get(t.id);
3806 //return this.items.get(t.menuItemId);
3812 onTouch : function(e)
3814 Roo.log("menu.onTouch");
3815 //e.stopEvent(); this make the user popdown broken
3819 onClick : function(e)
3821 Roo.log("menu.onClick");
3823 var t = this.findTargetItem(e);
3824 if(!t || t.isContainer){
3829 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3830 if(t == this.activeItem && t.shouldDeactivate(e)){
3831 this.activeItem.deactivate();
3832 delete this.activeItem;
3836 this.setActiveItem(t, true);
3844 Roo.log('pass click event');
3848 this.fireEvent("click", this, t, e);
3852 if(!t.href.length || t.href == '#'){
3853 (function() { _this.hide(); }).defer(100);
3858 onMouseOver : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t.canActivate && !t.disabled){
3863 // this.setActiveItem(t, true);
3867 this.fireEvent("mouseover", this, e, t);
3869 isVisible : function(){
3870 return !this.hidden;
3872 onMouseOut : function(e){
3873 var t = this.findTargetItem(e);
3876 // if(t == this.activeItem && t.shouldDeactivate(e)){
3877 // this.activeItem.deactivate();
3878 // delete this.activeItem;
3881 this.fireEvent("mouseout", this, e, t);
3886 * Displays this menu relative to another element
3887 * @param {String/HTMLElement/Roo.Element} element The element to align to
3888 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889 * the element (defaults to this.defaultAlign)
3890 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3892 show : function(el, pos, parentMenu)
3894 if (false === this.fireEvent("beforeshow", this)) {
3895 Roo.log("show canceled");
3898 this.parentMenu = parentMenu;
3902 this.el.addClass('show'); // show otherwise we do not know how big we are..
3904 var xy = this.el.getAlignToXY(el, pos);
3906 // bl-tl << left align below
3907 // tl-bl << left align
3909 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910 // if it goes to far to the right.. -> align left.
3911 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3914 // was left align - go right?
3915 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3918 // goes down the bottom
3919 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3921 var a = this.align.replace('?', '').split('-');
3922 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3926 this.showAt( xy , parentMenu, false);
3929 * Displays this menu at a specific xy position
3930 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3933 showAt : function(xy, parentMenu, /* private: */_e){
3934 this.parentMenu = parentMenu;
3939 this.fireEvent("beforeshow", this);
3940 //xy = this.el.adjustForConstraints(xy);
3944 this.hideMenuItems();
3945 this.hidden = false;
3946 if (this.triggerEl) {
3947 this.triggerEl.addClass('open');
3950 this.el.addClass('show');
3954 // reassign x when hitting right
3956 // reassign y when hitting bottom
3958 // but the list may align on trigger left or trigger top... should it be a properity?
3960 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3965 this.fireEvent("show", this);
3971 this.doFocus.defer(50, this);
3975 doFocus : function(){
3977 this.focusEl.focus();
3982 * Hides this menu and optionally all parent menus
3983 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3985 hide : function(deep)
3987 if (false === this.fireEvent("beforehide", this)) {
3988 Roo.log("hide canceled");
3991 this.hideMenuItems();
3992 if(this.el && this.isVisible()){
3994 if(this.activeItem){
3995 this.activeItem.deactivate();
3996 this.activeItem = null;
3998 if (this.triggerEl) {
3999 this.triggerEl.removeClass('open');
4002 this.el.removeClass('show');
4004 this.fireEvent("hide", this);
4006 if(deep === true && this.parentMenu){
4007 this.parentMenu.hide(true);
4011 onTriggerClick : function(e)
4013 Roo.log('trigger click');
4015 var target = e.getTarget();
4017 Roo.log(target.nodeName.toLowerCase());
4019 if(target.nodeName.toLowerCase() === 'i'){
4025 onTriggerPress : function(e)
4027 Roo.log('trigger press');
4028 //Roo.log(e.getTarget());
4029 // Roo.log(this.triggerEl.dom);
4031 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032 var pel = Roo.get(e.getTarget());
4033 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034 Roo.log('is treeview or dropdown?');
4038 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4042 if (this.isVisible()) {
4048 this.show(this.triggerEl, this.align, false);
4051 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4058 hideMenuItems : function()
4060 Roo.log("hide Menu Items");
4065 this.el.select('.open',true).each(function(aa) {
4067 aa.removeClass('open');
4071 addxtypeChild : function (tree, cntr) {
4072 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4074 this.menuitems.add(comp);
4086 this.getEl().dom.innerHTML = '';
4087 this.menuitems.clear();
4101 * @class Roo.bootstrap.MenuItem
4102 * @extends Roo.bootstrap.Component
4103 * Bootstrap MenuItem class
4104 * @cfg {String} html the menu label
4105 * @cfg {String} href the link
4106 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4107 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4108 * @cfg {Boolean} active used on sidebars to highlight active itesm
4109 * @cfg {String} fa favicon to show on left of menu item.
4110 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4114 * Create a new MenuItem
4115 * @param {Object} config The config object
4119 Roo.bootstrap.MenuItem = function(config){
4120 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4125 * The raw click event for the entire grid.
4126 * @param {Roo.bootstrap.MenuItem} this
4127 * @param {Roo.EventObject} e
4133 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4137 preventDefault: false,
4138 isContainer : false,
4142 getAutoCreate : function(){
4144 if(this.isContainer){
4147 cls: 'dropdown-menu-item '
4157 cls : 'dropdown-item',
4162 if (this.fa !== false) {
4165 cls : 'fa fa-' + this.fa
4174 cls: 'dropdown-menu-item',
4177 if (this.parent().type == 'treeview') {
4178 cfg.cls = 'treeview-menu';
4181 cfg.cls += ' active';
4186 anc.href = this.href || cfg.cn[0].href ;
4187 ctag.html = this.html || cfg.cn[0].html ;
4191 initEvents: function()
4193 if (this.parent().type == 'treeview') {
4194 this.el.select('a').on('click', this.onClick, this);
4198 this.menu.parentType = this.xtype;
4199 this.menu.triggerEl = this.el;
4200 this.menu = this.addxtype(Roo.apply({}, this.menu));
4204 onClick : function(e)
4206 Roo.log('item on click ');
4208 if(this.preventDefault){
4211 //this.parent().hideMenuItems();
4213 this.fireEvent('click', this, e);
4232 * @class Roo.bootstrap.MenuSeparator
4233 * @extends Roo.bootstrap.Component
4234 * Bootstrap MenuSeparator class
4237 * Create a new MenuItem
4238 * @param {Object} config The config object
4242 Roo.bootstrap.MenuSeparator = function(config){
4243 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4246 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4248 getAutoCreate : function(){
4267 * @class Roo.bootstrap.Modal
4268 * @extends Roo.bootstrap.Component
4271 * @children Roo.bootstrap.Component
4272 * Bootstrap Modal class
4273 * @cfg {String} title Title of dialog
4274 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4275 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4276 * @cfg {Boolean} specificTitle default false
4277 * @cfg {Array} buttons Array of buttons or standard button set..
4278 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4279 * @cfg {Boolean} animate default true
4280 * @cfg {Boolean} allow_close default true
4281 * @cfg {Boolean} fitwindow default false
4282 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4283 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4284 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4285 * @cfg {String} size (sm|lg|xl) default empty
4286 * @cfg {Number} max_width set the max width of modal
4287 * @cfg {Boolean} editableTitle can the title be edited
4292 * Create a new Modal Dialog
4293 * @param {Object} config The config object
4296 Roo.bootstrap.Modal = function(config){
4297 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4302 * The raw btnclick event for the button
4303 * @param {Roo.EventObject} e
4308 * Fire when dialog resize
4309 * @param {Roo.bootstrap.Modal} this
4310 * @param {Roo.EventObject} e
4314 * @event titlechanged
4315 * Fire when the editable title has been changed
4316 * @param {Roo.bootstrap.Modal} this
4317 * @param {Roo.EventObject} value
4319 "titlechanged" : true
4322 this.buttons = this.buttons || [];
4325 this.tmpl = Roo.factory(this.tmpl);
4330 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4332 title : 'test dialog',
4342 specificTitle: false,
4344 buttonPosition: 'right',
4366 editableTitle : false,
4368 onRender : function(ct, position)
4370 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4373 var cfg = Roo.apply({}, this.getAutoCreate());
4376 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4378 //if (!cfg.name.length) {
4382 cfg.cls += ' ' + this.cls;
4385 cfg.style = this.style;
4387 this.el = Roo.get(document.body).createChild(cfg, position);
4389 //var type = this.el.dom.type;
4392 if(this.tabIndex !== undefined){
4393 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4396 this.dialogEl = this.el.select('.modal-dialog',true).first();
4397 this.bodyEl = this.el.select('.modal-body',true).first();
4398 this.closeEl = this.el.select('.modal-header .close', true).first();
4399 this.headerEl = this.el.select('.modal-header',true).first();
4400 this.titleEl = this.el.select('.modal-title',true).first();
4401 this.footerEl = this.el.select('.modal-footer',true).first();
4403 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4405 //this.el.addClass("x-dlg-modal");
4407 if (this.buttons.length) {
4408 Roo.each(this.buttons, function(bb) {
4409 var b = Roo.apply({}, bb);
4410 b.xns = b.xns || Roo.bootstrap;
4411 b.xtype = b.xtype || 'Button';
4412 if (typeof(b.listeners) == 'undefined') {
4413 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4416 var btn = Roo.factory(b);
4418 btn.render(this.getButtonContainer());
4422 // render the children.
4425 if(typeof(this.items) != 'undefined'){
4426 var items = this.items;
4429 for(var i =0;i < items.length;i++) {
4430 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4434 this.items = nitems;
4436 // where are these used - they used to be body/close/footer
4440 //this.el.addClass([this.fieldClass, this.cls]);
4444 getAutoCreate : function()
4446 // we will default to modal-body-overflow - might need to remove or make optional later.
4448 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4449 html : this.html || ''
4454 cls : 'modal-title',
4458 if(this.specificTitle){ // WTF is this?
4463 if (this.allow_close && Roo.bootstrap.version == 3) {
4473 if (this.editableTitle) {
4475 cls: 'form-control roo-editable-title d-none',
4481 if (this.allow_close && Roo.bootstrap.version == 4) {
4491 if(this.size.length){
4492 size = 'modal-' + this.size;
4495 var footer = Roo.bootstrap.version == 3 ?
4497 cls : 'modal-footer',
4501 cls: 'btn-' + this.buttonPosition
4506 { // BS4 uses mr-auto on left buttons....
4507 cls : 'modal-footer'
4518 cls: "modal-dialog " + size,
4521 cls : "modal-content",
4524 cls : 'modal-header',
4539 modal.cls += ' fade';
4545 getChildContainer : function() {
4550 getButtonContainer : function() {
4552 return Roo.bootstrap.version == 4 ?
4553 this.el.select('.modal-footer',true).first()
4554 : this.el.select('.modal-footer div',true).first();
4557 initEvents : function()
4559 if (this.allow_close) {
4560 this.closeEl.on('click', this.hide, this);
4562 Roo.EventManager.onWindowResize(this.resize, this, true);
4563 if (this.editableTitle) {
4564 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4565 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4566 this.headerEditEl.on('keyup', function(e) {
4567 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4568 this.toggleHeaderInput(false)
4571 this.headerEditEl.on('blur', function(e) {
4572 this.toggleHeaderInput(false)
4581 this.maskEl.setSize(
4582 Roo.lib.Dom.getViewWidth(true),
4583 Roo.lib.Dom.getViewHeight(true)
4586 if (this.fitwindow) {
4588 this.dialogEl.setStyle( { 'max-width' : '100%' });
4590 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4591 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4596 if(this.max_width !== 0) {
4598 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4601 this.setSize(w, this.height);
4605 if(this.max_height) {
4606 this.setSize(w,Math.min(
4608 Roo.lib.Dom.getViewportHeight(true) - 60
4614 if(!this.fit_content) {
4615 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4619 this.setSize(w, Math.min(
4621 this.headerEl.getHeight() +
4622 this.footerEl.getHeight() +
4623 this.getChildHeight(this.bodyEl.dom.childNodes),
4624 Roo.lib.Dom.getViewportHeight(true) - 60)
4630 setSize : function(w,h)
4641 if (!this.rendered) {
4644 this.toggleHeaderInput(false);
4645 //this.el.setStyle('display', 'block');
4646 this.el.removeClass('hideing');
4647 this.el.dom.style.display='block';
4649 Roo.get(document.body).addClass('modal-open');
4651 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4654 this.el.addClass('show');
4655 this.el.addClass('in');
4658 this.el.addClass('show');
4659 this.el.addClass('in');
4662 // not sure how we can show data in here..
4664 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4667 Roo.get(document.body).addClass("x-body-masked");
4669 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4670 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4671 this.maskEl.dom.style.display = 'block';
4672 this.maskEl.addClass('show');
4677 this.fireEvent('show', this);
4679 // set zindex here - otherwise it appears to be ignored...
4680 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4683 this.items.forEach( function(e) {
4684 e.layout ? e.layout() : false;
4692 if(this.fireEvent("beforehide", this) !== false){
4694 this.maskEl.removeClass('show');
4696 this.maskEl.dom.style.display = '';
4697 Roo.get(document.body).removeClass("x-body-masked");
4698 this.el.removeClass('in');
4699 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701 if(this.animate){ // why
4702 this.el.addClass('hideing');
4703 this.el.removeClass('show');
4705 if (!this.el.hasClass('hideing')) {
4706 return; // it's been shown again...
4709 this.el.dom.style.display='';
4711 Roo.get(document.body).removeClass('modal-open');
4712 this.el.removeClass('hideing');
4716 this.el.removeClass('show');
4717 this.el.dom.style.display='';
4718 Roo.get(document.body).removeClass('modal-open');
4721 this.fireEvent('hide', this);
4724 isVisible : function()
4727 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4731 addButton : function(str, cb)
4735 var b = Roo.apply({}, { html : str } );
4736 b.xns = b.xns || Roo.bootstrap;
4737 b.xtype = b.xtype || 'Button';
4738 if (typeof(b.listeners) == 'undefined') {
4739 b.listeners = { click : cb.createDelegate(this) };
4742 var btn = Roo.factory(b);
4744 btn.render(this.getButtonContainer());
4750 setDefaultButton : function(btn)
4752 //this.el.select('.modal-footer').()
4755 resizeTo: function(w,h)
4757 this.dialogEl.setWidth(w);
4759 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4761 this.bodyEl.setHeight(h - diff);
4763 this.fireEvent('resize', this);
4766 setContentSize : function(w, h)
4770 onButtonClick: function(btn,e)
4773 this.fireEvent('btnclick', btn.name, e);
4776 * Set the title of the Dialog
4777 * @param {String} str new Title
4779 setTitle: function(str) {
4780 this.titleEl.dom.innerHTML = str;
4784 * Set the body of the Dialog
4785 * @param {String} str new Title
4787 setBody: function(str) {
4788 this.bodyEl.dom.innerHTML = str;
4791 * Set the body of the Dialog using the template
4792 * @param {Obj} data - apply this data to the template and replace the body contents.
4794 applyBody: function(obj)
4797 Roo.log("Error - using apply Body without a template");
4800 this.tmpl.overwrite(this.bodyEl, obj);
4803 getChildHeight : function(child_nodes)
4807 child_nodes.length == 0
4812 var child_height = 0;
4814 for(var i = 0; i < child_nodes.length; i++) {
4817 * for modal with tabs...
4818 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820 var layout_childs = child_nodes[i].childNodes;
4822 for(var j = 0; j < layout_childs.length; j++) {
4824 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826 var layout_body_childs = layout_childs[j].childNodes;
4828 for(var k = 0; k < layout_body_childs.length; k++) {
4830 if(layout_body_childs[k].classList.contains('navbar')) {
4831 child_height += layout_body_childs[k].offsetHeight;
4835 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4842 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4857 child_height += child_nodes[i].offsetHeight;
4858 // Roo.log(child_nodes[i].offsetHeight);
4861 return child_height;
4863 toggleHeaderInput : function(is_edit)
4865 if (!this.editableTitle) {
4866 return; // not editable.
4868 if (is_edit && this.is_header_editing) {
4869 return; // already editing..
4873 this.headerEditEl.dom.value = this.title;
4874 this.headerEditEl.removeClass('d-none');
4875 this.headerEditEl.dom.focus();
4876 this.titleEl.addClass('d-none');
4878 this.is_header_editing = true;
4881 // flip back to not editing.
4882 this.title = this.headerEditEl.dom.value;
4883 this.headerEditEl.addClass('d-none');
4884 this.titleEl.removeClass('d-none');
4885 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4886 this.is_header_editing = false;
4887 this.fireEvent('titlechanged', this, this.title);
4896 Roo.apply(Roo.bootstrap.Modal, {
4898 * Button config that displays a single OK button
4907 * Button config that displays Yes and No buttons
4923 * Button config that displays OK and Cancel buttons
4938 * Button config that displays Yes, No and Cancel buttons
4963 * messagebox - can be used as a replace
4967 * @class Roo.MessageBox
4968 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4972 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974 // Prompt for user data:
4975 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977 // process text value...
4981 // Show a dialog using config options:
4983 title:'Save Changes?',
4984 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4985 buttons: Roo.Msg.YESNOCANCEL,
4992 Roo.bootstrap.MessageBox = function(){
4993 var dlg, opt, mask, waitTimer;
4994 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4995 var buttons, activeTextEl, bwidth;
4999 var handleButton = function(button){
5001 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5005 var handleHide = function(){
5007 dlg.el.removeClass(opt.cls);
5010 // Roo.TaskMgr.stop(waitTimer);
5011 // waitTimer = null;
5016 var updateButtons = function(b){
5019 buttons["ok"].hide();
5020 buttons["cancel"].hide();
5021 buttons["yes"].hide();
5022 buttons["no"].hide();
5023 dlg.footerEl.hide();
5027 dlg.footerEl.show();
5028 for(var k in buttons){
5029 if(typeof buttons[k] != "function"){
5032 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5033 width += buttons[k].el.getWidth()+15;
5043 var handleEsc = function(d, k, e){
5044 if(opt && opt.closable !== false){
5054 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5055 * @return {Roo.BasicDialog} The BasicDialog element
5057 getDialog : function(){
5059 dlg = new Roo.bootstrap.Modal( {
5062 //constraintoviewport:false,
5064 //collapsible : false,
5069 //buttonAlign:"center",
5070 closeClick : function(){
5071 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5074 handleButton("cancel");
5079 dlg.on("hide", handleHide);
5081 //dlg.addKeyListener(27, handleEsc);
5083 this.buttons = buttons;
5084 var bt = this.buttonText;
5085 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5086 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5087 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5088 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090 bodyEl = dlg.bodyEl.createChild({
5092 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5093 '<textarea class="roo-mb-textarea"></textarea>' +
5094 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5096 msgEl = bodyEl.dom.firstChild;
5097 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5098 textboxEl.enableDisplayMode();
5099 textboxEl.addKeyListener([10,13], function(){
5100 if(dlg.isVisible() && opt && opt.buttons){
5103 }else if(opt.buttons.yes){
5104 handleButton("yes");
5108 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5109 textareaEl.enableDisplayMode();
5110 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5111 progressEl.enableDisplayMode();
5113 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5114 var pf = progressEl.dom.firstChild;
5116 pp = Roo.get(pf.firstChild);
5117 pp.setHeight(pf.offsetHeight);
5125 * Updates the message box body text
5126 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5127 * the XHTML-compliant non-breaking space character '&#160;')
5128 * @return {Roo.MessageBox} This message box
5130 updateText : function(text)
5132 if(!dlg.isVisible() && !opt.width){
5133 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5134 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136 msgEl.innerHTML = text || ' ';
5138 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5139 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141 Math.min(opt.width || cw , this.maxWidth),
5142 Math.max(opt.minWidth || this.minWidth, bwidth)
5145 activeTextEl.setWidth(w);
5147 if(dlg.isVisible()){
5148 dlg.fixedcenter = false;
5150 // to big, make it scroll. = But as usual stupid IE does not support
5153 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5154 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5155 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157 bodyEl.dom.style.height = '';
5158 bodyEl.dom.style.overflowY = '';
5161 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163 bodyEl.dom.style.overflowX = '';
5166 dlg.setContentSize(w, bodyEl.getHeight());
5167 if(dlg.isVisible()){
5168 dlg.fixedcenter = true;
5174 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5175 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5176 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5177 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5178 * @return {Roo.MessageBox} This message box
5180 updateProgress : function(value, text){
5182 this.updateText(text);
5185 if (pp) { // weird bug on my firefox - for some reason this is not defined
5186 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5187 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5193 * Returns true if the message box is currently displayed
5194 * @return {Boolean} True if the message box is visible, else false
5196 isVisible : function(){
5197 return dlg && dlg.isVisible();
5201 * Hides the message box if it is displayed
5204 if(this.isVisible()){
5210 * Displays a new message box, or reinitializes an existing message box, based on the config options
5211 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5212 * The following config object properties are supported:
5214 Property Type Description
5215 ---------- --------------- ------------------------------------------------------------------------------------
5216 animEl String/Element An id or Element from which the message box should animate as it opens and
5217 closes (defaults to undefined)
5218 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5219 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5220 closable Boolean False to hide the top-right close button (defaults to true). Note that
5221 progress and wait dialogs will ignore this property and always hide the
5222 close button as they can only be closed programmatically.
5223 cls String A custom CSS class to apply to the message box element
5224 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5225 displayed (defaults to 75)
5226 fn Function A callback function to execute after closing the dialog. The arguments to the
5227 function will be btn (the name of the button that was clicked, if applicable,
5228 e.g. "ok"), and text (the value of the active text field, if applicable).
5229 Progress and wait dialogs will ignore this option since they do not respond to
5230 user actions and can only be closed programmatically, so any required function
5231 should be called by the same code after it closes the dialog.
5232 icon String A CSS class that provides a background image to be used as an icon for
5233 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5234 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5235 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5236 modal Boolean False to allow user interaction with the page while the message box is
5237 displayed (defaults to true)
5238 msg String A string that will replace the existing message box body text (defaults
5239 to the XHTML-compliant non-breaking space character ' ')
5240 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5241 progress Boolean True to display a progress bar (defaults to false)
5242 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5243 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5244 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5245 title String The title text
5246 value String The string value to set into the active textbox element if displayed
5247 wait Boolean True to display a progress bar (defaults to false)
5248 width Number The width of the dialog in pixels
5255 msg: 'Please enter your address:',
5257 buttons: Roo.MessageBox.OKCANCEL,
5260 animEl: 'addAddressBtn'
5263 * @param {Object} config Configuration options
5264 * @return {Roo.MessageBox} This message box
5266 show : function(options)
5269 // this causes nightmares if you show one dialog after another
5270 // especially on callbacks..
5272 if(this.isVisible()){
5275 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5276 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5277 Roo.log("New Dialog Message:" + options.msg )
5278 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5279 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5282 var d = this.getDialog();
5284 d.setTitle(opt.title || " ");
5285 d.closeEl.setDisplayed(opt.closable !== false);
5286 activeTextEl = textboxEl;
5287 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5292 textareaEl.setHeight(typeof opt.multiline == "number" ?
5293 opt.multiline : this.defaultTextHeight);
5294 activeTextEl = textareaEl;
5303 progressEl.setDisplayed(opt.progress === true);
5305 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307 this.updateProgress(0);
5308 activeTextEl.dom.value = opt.value || "";
5310 dlg.setDefaultButton(activeTextEl);
5312 var bs = opt.buttons;
5316 }else if(bs && bs.yes){
5317 db = buttons["yes"];
5319 dlg.setDefaultButton(db);
5321 bwidth = updateButtons(opt.buttons);
5322 this.updateText(opt.msg);
5324 d.el.addClass(opt.cls);
5326 d.proxyDrag = opt.proxyDrag === true;
5327 d.modal = opt.modal !== false;
5328 d.mask = opt.modal !== false ? mask : false;
5330 // force it to the end of the z-index stack so it gets a cursor in FF
5331 document.body.appendChild(dlg.el.dom);
5332 d.animateTarget = null;
5333 d.show(options.animEl);
5339 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5340 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5341 * and closing the message box when the process is complete.
5342 * @param {String} title The title bar text
5343 * @param {String} msg The message box body text
5344 * @return {Roo.MessageBox} This message box
5346 progress : function(title, msg){
5353 minWidth: this.minProgressWidth,
5360 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5361 * If a callback function is passed it will be called after the user clicks the button, and the
5362 * id of the button that was clicked will be passed as the only parameter to the callback
5363 * (could also be the top-right close button).
5364 * @param {String} title The title bar text
5365 * @param {String} msg The message box body text
5366 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5367 * @param {Object} scope (optional) The scope of the callback function
5368 * @return {Roo.MessageBox} This message box
5370 alert : function(title, msg, fn, scope)
5385 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5386 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5387 * You are responsible for closing the message box when the process is complete.
5388 * @param {String} msg The message box body text
5389 * @param {String} title (optional) The title bar text
5390 * @return {Roo.MessageBox} This message box
5392 wait : function(msg, title){
5403 waitTimer = Roo.TaskMgr.start({
5405 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5413 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5414 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5415 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5416 * @param {String} title The title bar text
5417 * @param {String} msg The message box body text
5418 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5419 * @param {Object} scope (optional) The scope of the callback function
5420 * @return {Roo.MessageBox} This message box
5422 confirm : function(title, msg, fn, scope){
5426 buttons: this.YESNO,
5435 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5436 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5437 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5438 * (could also be the top-right close button) and the text that was entered will be passed as the two
5439 * parameters to the callback.
5440 * @param {String} title The title bar text
5441 * @param {String} msg The message box body text
5442 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5443 * @param {Object} scope (optional) The scope of the callback function
5444 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5445 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5446 * @return {Roo.MessageBox} This message box
5448 prompt : function(title, msg, fn, scope, multiline){
5452 buttons: this.OKCANCEL,
5457 multiline: multiline,
5464 * Button config that displays a single OK button
5469 * Button config that displays Yes and No buttons
5472 YESNO : {yes:true, no:true},
5474 * Button config that displays OK and Cancel buttons
5477 OKCANCEL : {ok:true, cancel:true},
5479 * Button config that displays Yes, No and Cancel buttons
5482 YESNOCANCEL : {yes:true, no:true, cancel:true},
5485 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5488 defaultTextHeight : 75,
5490 * The maximum width in pixels of the message box (defaults to 600)
5495 * The minimum width in pixels of the message box (defaults to 100)
5500 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5501 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5504 minProgressWidth : 250,
5506 * An object containing the default button text strings that can be overriden for localized language support.
5507 * Supported properties are: ok, cancel, yes and no.
5508 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5521 * Shorthand for {@link Roo.MessageBox}
5523 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5524 Roo.Msg = Roo.Msg || Roo.MessageBox;
5533 * @class Roo.bootstrap.Navbar
5534 * @extends Roo.bootstrap.Component
5535 * Bootstrap Navbar class
5538 * Create a new Navbar
5539 * @param {Object} config The config object
5543 Roo.bootstrap.Navbar = function(config){
5544 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5548 * @event beforetoggle
5549 * Fire before toggle the menu
5550 * @param {Roo.EventObject} e
5552 "beforetoggle" : true
5556 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5565 getAutoCreate : function(){
5568 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5572 initEvents :function ()
5574 //Roo.log(this.el.select('.navbar-toggle',true));
5575 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5582 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5584 var size = this.el.getSize();
5585 this.maskEl.setSize(size.width, size.height);
5586 this.maskEl.enableDisplayMode("block");
5595 getChildContainer : function()
5597 if (this.el && this.el.select('.collapse').getCount()) {
5598 return this.el.select('.collapse',true).first();
5613 onToggle : function()
5616 if(this.fireEvent('beforetoggle', this) === false){
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (!ce.hasClass('show')) {
5631 * Expand the navbar pulldown
5633 expand : function ()
5636 var ce = this.el.select('.navbar-collapse',true).first();
5637 if (ce.hasClass('collapsing')) {
5640 ce.dom.style.height = '';
5642 ce.addClass('in'); // old...
5643 ce.removeClass('collapse');
5644 ce.addClass('show');
5645 var h = ce.getHeight();
5647 ce.removeClass('show');
5648 // at this point we should be able to see it..
5649 ce.addClass('collapsing');
5651 ce.setHeight(0); // resize it ...
5652 ce.on('transitionend', function() {
5653 //Roo.log('done transition');
5654 ce.removeClass('collapsing');
5655 ce.addClass('show');
5656 ce.removeClass('collapse');
5658 ce.dom.style.height = '';
5659 }, this, { single: true} );
5661 ce.dom.scrollTop = 0;
5664 * Collapse the navbar pulldown
5666 collapse : function()
5668 var ce = this.el.select('.navbar-collapse',true).first();
5670 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5671 // it's collapsed or collapsing..
5674 ce.removeClass('in'); // old...
5675 ce.setHeight(ce.getHeight());
5676 ce.removeClass('show');
5677 ce.addClass('collapsing');
5679 ce.on('transitionend', function() {
5680 ce.dom.style.height = '';
5681 ce.removeClass('collapsing');
5682 ce.addClass('collapse');
5683 }, this, { single: true} );
5703 * @class Roo.bootstrap.NavSimplebar
5704 * @extends Roo.bootstrap.Navbar
5705 * Bootstrap Sidebar class
5707 * @cfg {Boolean} inverse is inverted color
5709 * @cfg {String} type (nav | pills | tabs)
5710 * @cfg {Boolean} arrangement stacked | justified
5711 * @cfg {String} align (left | right) alignment
5713 * @cfg {Boolean} main (true|false) main nav bar? default false
5714 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5716 * @cfg {String} tag (header|footer|nav|div) default is nav
5718 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722 * Create a new Sidebar
5723 * @param {Object} config The config object
5727 Roo.bootstrap.NavSimplebar = function(config){
5728 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5731 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5747 getAutoCreate : function(){
5751 tag : this.tag || 'div',
5752 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5754 if (['light','white'].indexOf(this.weight) > -1) {
5755 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5757 cfg.cls += ' bg-' + this.weight;
5760 cfg.cls += ' navbar-inverse';
5764 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5766 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5775 cls: 'nav nav-' + this.xtype,
5781 this.type = this.type || 'nav';
5782 if (['tabs','pills'].indexOf(this.type) != -1) {
5783 cfg.cn[0].cls += ' nav-' + this.type
5787 if (this.type!=='nav') {
5788 Roo.log('nav type must be nav/tabs/pills')
5790 cfg.cn[0].cls += ' navbar-nav'
5796 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5797 cfg.cn[0].cls += ' nav-' + this.arrangement;
5801 if (this.align === 'right') {
5802 cfg.cn[0].cls += ' navbar-right';
5827 * navbar-expand-md fixed-top
5831 * @class Roo.bootstrap.NavHeaderbar
5832 * @extends Roo.bootstrap.NavSimplebar
5833 * Bootstrap Sidebar class
5835 * @cfg {String} brand what is brand
5836 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5837 * @cfg {String} brand_href href of the brand
5838 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5839 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5840 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5841 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5844 * Create a new Sidebar
5845 * @param {Object} config The config object
5849 Roo.bootstrap.NavHeaderbar = function(config){
5850 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5854 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5861 desktopCenter : false,
5864 getAutoCreate : function(){
5867 tag: this.nav || 'nav',
5868 cls: 'navbar navbar-expand-md',
5874 if (this.desktopCenter) {
5875 cn.push({cls : 'container', cn : []});
5883 cls: 'navbar-toggle navbar-toggler',
5884 'data-toggle': 'collapse',
5889 html: 'Toggle navigation'
5893 cls: 'icon-bar navbar-toggler-icon'
5906 cn.push( Roo.bootstrap.version == 4 ? btn : {
5908 cls: 'navbar-header',
5917 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5921 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5923 if (['light','white'].indexOf(this.weight) > -1) {
5924 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5926 cfg.cls += ' bg-' + this.weight;
5929 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5930 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5932 // tag can override this..
5934 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5937 if (this.brand !== '') {
5938 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5939 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5941 href: this.brand_href ? this.brand_href : '#',
5942 cls: 'navbar-brand',
5950 cfg.cls += ' main-nav';
5958 getHeaderChildContainer : function()
5960 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5961 return this.el.select('.navbar-header',true).first();
5964 return this.getChildContainer();
5967 getChildContainer : function()
5970 return this.el.select('.roo-navbar-collapse',true).first();
5975 initEvents : function()
5977 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5979 if (this.autohide) {
5984 Roo.get(document).on('scroll',function(e) {
5985 var ns = Roo.get(document).getScroll().top;
5986 var os = prevScroll;
5990 ft.removeClass('slideDown');
5991 ft.addClass('slideUp');
5994 ft.removeClass('slideUp');
5995 ft.addClass('slideDown');
6016 * @class Roo.bootstrap.NavSidebar
6017 * @extends Roo.bootstrap.Navbar
6018 * Bootstrap Sidebar class
6021 * Create a new Sidebar
6022 * @param {Object} config The config object
6026 Roo.bootstrap.NavSidebar = function(config){
6027 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6030 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6032 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6034 getAutoCreate : function(){
6039 cls: 'sidebar sidebar-nav'
6061 * @class Roo.bootstrap.NavGroup
6062 * @extends Roo.bootstrap.Component
6063 * Bootstrap NavGroup class
6064 * @cfg {String} align (left|right)
6065 * @cfg {Boolean} inverse
6066 * @cfg {String} type (nav|pills|tab) default nav
6067 * @cfg {String} navId - reference Id for navbar.
6068 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6071 * Create a new nav group
6072 * @param {Object} config The config object
6075 Roo.bootstrap.NavGroup = function(config){
6076 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6079 Roo.bootstrap.NavGroup.register(this);
6083 * Fires when the active item changes
6084 * @param {Roo.bootstrap.NavGroup} this
6085 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6086 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6093 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6105 getAutoCreate : function()
6107 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6113 if (Roo.bootstrap.version == 4) {
6114 if (['tabs','pills'].indexOf(this.type) != -1) {
6115 cfg.cls += ' nav-' + this.type;
6117 // trying to remove so header bar can right align top?
6118 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6119 // do not use on header bar...
6120 cfg.cls += ' navbar-nav';
6125 if (['tabs','pills'].indexOf(this.type) != -1) {
6126 cfg.cls += ' nav-' + this.type
6128 if (this.type !== 'nav') {
6129 Roo.log('nav type must be nav/tabs/pills')
6131 cfg.cls += ' navbar-nav'
6135 if (this.parent() && this.parent().sidebar) {
6138 cls: 'dashboard-menu sidebar-menu'
6144 if (this.form === true) {
6147 cls: 'navbar-form form-inline'
6149 //nav navbar-right ml-md-auto
6150 if (this.align === 'right') {
6151 cfg.cls += ' navbar-right ml-md-auto';
6153 cfg.cls += ' navbar-left';
6157 if (this.align === 'right') {
6158 cfg.cls += ' navbar-right ml-md-auto';
6160 cfg.cls += ' mr-auto';
6164 cfg.cls += ' navbar-inverse';
6172 * sets the active Navigation item
6173 * @param {Roo.bootstrap.NavItem} the new current navitem
6175 setActiveItem : function(item)
6178 Roo.each(this.navItems, function(v){
6183 v.setActive(false, true);
6190 item.setActive(true, true);
6191 this.fireEvent('changed', this, item, prev);
6196 * gets the active Navigation item
6197 * @return {Roo.bootstrap.NavItem} the current navitem
6199 getActive : function()
6203 Roo.each(this.navItems, function(v){
6214 indexOfNav : function()
6218 Roo.each(this.navItems, function(v,i){
6229 * adds a Navigation item
6230 * @param {Roo.bootstrap.NavItem} the navitem to add
6232 addItem : function(cfg)
6234 if (this.form && Roo.bootstrap.version == 4) {
6237 var cn = new Roo.bootstrap.NavItem(cfg);
6239 cn.parentId = this.id;
6240 cn.onRender(this.el, null);
6244 * register a Navigation item
6245 * @param {Roo.bootstrap.NavItem} the navitem to add
6247 register : function(item)
6249 this.navItems.push( item);
6250 item.navId = this.navId;
6255 * clear all the Navigation item
6258 clearAll : function()
6261 this.el.dom.innerHTML = '';
6264 getNavItem: function(tabId)
6267 Roo.each(this.navItems, function(e) {
6268 if (e.tabId == tabId) {
6278 setActiveNext : function()
6280 var i = this.indexOfNav(this.getActive());
6281 if (i > this.navItems.length) {
6284 this.setActiveItem(this.navItems[i+1]);
6286 setActivePrev : function()
6288 var i = this.indexOfNav(this.getActive());
6292 this.setActiveItem(this.navItems[i-1]);
6294 clearWasActive : function(except) {
6295 Roo.each(this.navItems, function(e) {
6296 if (e.tabId != except.tabId && e.was_active) {
6297 e.was_active = false;
6304 getWasActive : function ()
6307 Roo.each(this.navItems, function(e) {
6322 Roo.apply(Roo.bootstrap.NavGroup, {
6326 * register a Navigation Group
6327 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6329 register : function(navgrp)
6331 this.groups[navgrp.navId] = navgrp;
6335 * fetch a Navigation Group based on the navigation ID
6336 * @param {string} the navgroup to add
6337 * @returns {Roo.bootstrap.NavGroup} the navgroup
6339 get: function(navId) {
6340 if (typeof(this.groups[navId]) == 'undefined') {
6342 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6344 return this.groups[navId] ;
6359 * @class Roo.bootstrap.NavItem
6360 * @extends Roo.bootstrap.Component
6361 * Bootstrap Navbar.NavItem class
6362 * @cfg {String} href link to
6363 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6364 * @cfg {Boolean} button_outline show and outlined button
6365 * @cfg {String} html content of button
6366 * @cfg {String} badge text inside badge
6367 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6368 * @cfg {String} glyphicon DEPRICATED - use fa
6369 * @cfg {String} icon DEPRICATED - use fa
6370 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6371 * @cfg {Boolean} active Is item active
6372 * @cfg {Boolean} disabled Is item disabled
6373 * @cfg {String} linkcls Link Class
6374 * @cfg {Boolean} preventDefault (true | false) default false
6375 * @cfg {String} tabId the tab that this item activates.
6376 * @cfg {String} tagtype (a|span) render as a href or span?
6377 * @cfg {Boolean} animateRef (true|false) link to element default false
6380 * Create a new Navbar Item
6381 * @param {Object} config The config object
6383 Roo.bootstrap.NavItem = function(config){
6384 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6389 * The raw click event for the entire grid.
6390 * @param {Roo.EventObject} e
6395 * Fires when the active item active state changes
6396 * @param {Roo.bootstrap.NavItem} this
6397 * @param {boolean} state the new state
6403 * Fires when scroll to element
6404 * @param {Roo.bootstrap.NavItem} this
6405 * @param {Object} options
6406 * @param {Roo.EventObject} e
6414 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6423 preventDefault : false,
6431 button_outline : false,
6435 getAutoCreate : function(){
6442 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6445 cfg.cls += ' active' ;
6447 if (this.disabled) {
6448 cfg.cls += ' disabled';
6452 if (this.button_weight.length) {
6453 cfg.tag = this.href ? 'a' : 'button';
6454 cfg.html = this.html || '';
6455 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6457 cfg.href = this.href;
6460 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6462 cfg.cls += " nav-html";
6465 // menu .. should add dropdown-menu class - so no need for carat..
6467 if (this.badge !== '') {
6469 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6478 href : this.href || "#",
6479 html: this.html || '',
6483 if (this.tagtype == 'a') {
6484 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6488 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6489 } else if (this.fa) {
6490 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6491 } else if(this.glyphicon) {
6492 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6494 cfg.cn[0].cls += " nav-html";
6498 cfg.cn[0].html += " <span class='caret'></span>";
6502 if (this.badge !== '') {
6503 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6511 onRender : function(ct, position)
6513 // Roo.log("Call onRender: " + this.xtype);
6514 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6518 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6519 this.navLink = this.el.select('.nav-link',true).first();
6520 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525 initEvents: function()
6527 if (typeof (this.menu) != 'undefined') {
6528 this.menu.parentType = this.xtype;
6529 this.menu.triggerEl = this.el;
6530 this.menu = this.addxtype(Roo.apply({}, this.menu));
6533 this.el.on('click', this.onClick, this);
6535 //if(this.tagtype == 'span'){
6536 // this.el.select('span',true).on('click', this.onClick, this);
6539 // at this point parent should be available..
6540 this.parent().register(this);
6543 onClick : function(e)
6545 if (e.getTarget('.dropdown-menu-item')) {
6546 // did you click on a menu itemm.... - then don't trigger onclick..
6551 this.preventDefault ||
6554 Roo.log("NavItem - prevent Default?");
6558 if (this.disabled) {
6562 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6563 if (tg && tg.transition) {
6564 Roo.log("waiting for the transitionend");
6570 //Roo.log("fire event clicked");
6571 if(this.fireEvent('click', this, e) === false){
6575 if(this.tagtype == 'span'){
6579 //Roo.log(this.href);
6580 var ael = this.el.select('a',true).first();
6583 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6584 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6585 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6586 return; // ignore... - it's a 'hash' to another page.
6588 Roo.log("NavItem - prevent Default?");
6590 this.scrollToElement(e);
6594 var p = this.parent();
6596 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6597 if (typeof(p.setActiveItem) !== 'undefined') {
6598 p.setActiveItem(this);
6602 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6603 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6604 // remove the collapsed menu expand...
6605 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6609 isActive: function () {
6612 setActive : function(state, fire, is_was_active)
6614 if (this.active && !state && this.navId) {
6615 this.was_active = true;
6616 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6618 nv.clearWasActive(this);
6622 this.active = state;
6625 this.el.removeClass('active');
6626 this.navLink ? this.navLink.removeClass('active') : false;
6627 } else if (!this.el.hasClass('active')) {
6629 this.el.addClass('active');
6630 if (Roo.bootstrap.version == 4 && this.navLink ) {
6631 this.navLink.addClass('active');
6636 this.fireEvent('changed', this, state);
6639 // show a panel if it's registered and related..
6641 if (!this.navId || !this.tabId || !state || is_was_active) {
6645 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6649 var pan = tg.getPanelByName(this.tabId);
6653 // if we can not flip to new panel - go back to old nav highlight..
6654 if (false == tg.showPanel(pan)) {
6655 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6657 var onav = nv.getWasActive();
6659 onav.setActive(true, false, true);
6668 // this should not be here...
6669 setDisabled : function(state)
6671 this.disabled = state;
6673 this.el.removeClass('disabled');
6674 } else if (!this.el.hasClass('disabled')) {
6675 this.el.addClass('disabled');
6681 * Fetch the element to display the tooltip on.
6682 * @return {Roo.Element} defaults to this.el
6684 tooltipEl : function()
6686 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6689 scrollToElement : function(e)
6691 var c = document.body;
6694 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6696 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6697 c = document.documentElement;
6700 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706 var o = target.calcOffsetsTo(c);
6713 this.fireEvent('scrollto', this, options, e);
6715 Roo.get(c).scrollTo('top', options.value, true);
6720 * Set the HTML (text content) of the item
6721 * @param {string} html content for the nav item
6723 setHtml : function(html)
6726 this.htmlEl.dom.innerHTML = html;
6738 * <span> icon </span>
6739 * <span> text </span>
6740 * <span>badge </span>
6744 * @class Roo.bootstrap.NavSidebarItem
6745 * @extends Roo.bootstrap.NavItem
6746 * Bootstrap Navbar.NavSidebarItem class
6747 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6748 * {Boolean} open is the menu open
6749 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6750 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6751 * {String} buttonSize (sm|md|lg)the extra classes for the button
6752 * {Boolean} showArrow show arrow next to the text (default true)
6754 * Create a new Navbar Button
6755 * @param {Object} config The config object
6757 Roo.bootstrap.NavSidebarItem = function(config){
6758 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6763 * The raw click event for the entire grid.
6764 * @param {Roo.EventObject} e
6769 * Fires when the active item active state changes
6770 * @param {Roo.bootstrap.NavSidebarItem} this
6771 * @param {boolean} state the new state
6779 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6781 badgeWeight : 'default',
6787 buttonWeight : 'default',
6793 getAutoCreate : function(){
6798 href : this.href || '#',
6804 if(this.buttonView){
6807 href : this.href || '#',
6808 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6821 cfg.cls += ' active';
6824 if (this.disabled) {
6825 cfg.cls += ' disabled';
6828 cfg.cls += ' open x-open';
6831 if (this.glyphicon || this.icon) {
6832 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6833 a.cn.push({ tag : 'i', cls : c }) ;
6836 if(!this.buttonView){
6839 html : this.html || ''
6846 if (this.badge !== '') {
6847 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6853 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6856 a.cls += ' dropdown-toggle treeview' ;
6862 initEvents : function()
6864 if (typeof (this.menu) != 'undefined') {
6865 this.menu.parentType = this.xtype;
6866 this.menu.triggerEl = this.el;
6867 this.menu = this.addxtype(Roo.apply({}, this.menu));
6870 this.el.on('click', this.onClick, this);
6872 if(this.badge !== ''){
6873 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6878 onClick : function(e)
6885 if(this.preventDefault){
6889 this.fireEvent('click', this, e);
6892 disable : function()
6894 this.setDisabled(true);
6899 this.setDisabled(false);
6902 setDisabled : function(state)
6904 if(this.disabled == state){
6908 this.disabled = state;
6911 this.el.addClass('disabled');
6915 this.el.removeClass('disabled');
6920 setActive : function(state)
6922 if(this.active == state){
6926 this.active = state;
6929 this.el.addClass('active');
6933 this.el.removeClass('active');
6938 isActive: function ()
6943 setBadge : function(str)
6949 this.badgeEl.dom.innerHTML = str;
6964 Roo.namespace('Roo.bootstrap.breadcrumb');
6968 * @class Roo.bootstrap.breadcrumb.Nav
6969 * @extends Roo.bootstrap.Component
6970 * Bootstrap Breadcrumb Nav Class
6972 * @children Roo.bootstrap.breadcrumb.Item
6975 * Create a new breadcrumb.Nav
6976 * @param {Object} config The config object
6980 Roo.bootstrap.breadcrumb.Nav = function(config){
6981 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6986 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6988 getAutoCreate : function()
7005 initEvents: function()
7007 this.olEl = this.el.select('ol',true).first();
7009 getChildContainer : function()
7025 * @class Roo.bootstrap.breadcrumb.Nav
7026 * @extends Roo.bootstrap.Component
7027 * Bootstrap Breadcrumb Nav Class
7029 * @children Roo.bootstrap.breadcrumb.Component
7030 * @cfg {String} html the content of the link.
7031 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7032 * @cfg {Boolean} active is it active
7036 * Create a new breadcrumb.Nav
7037 * @param {Object} config The config object
7040 Roo.bootstrap.breadcrumb.Item = function(config){
7041 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7046 * The img click event for the img.
7047 * @param {Roo.EventObject} e
7054 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7059 getAutoCreate : function()
7064 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7066 if (this.href !== false) {
7073 cfg.html = this.html;
7079 initEvents: function()
7082 this.el.select('a', true).first().on('click',this.onClick, this)
7086 onClick : function(e)
7089 this.fireEvent('click',this, e);
7102 * @class Roo.bootstrap.Row
7103 * @extends Roo.bootstrap.Component
7104 * @children Roo.bootstrap.Component
7105 * Bootstrap Row class (contains columns...)
7109 * @param {Object} config The config object
7112 Roo.bootstrap.Row = function(config){
7113 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7116 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7118 getAutoCreate : function(){
7137 * @class Roo.bootstrap.Pagination
7138 * @extends Roo.bootstrap.Component
7139 * Bootstrap Pagination class
7140 * @cfg {String} size xs | sm | md | lg
7141 * @cfg {Boolean} inverse false | true
7144 * Create a new Pagination
7145 * @param {Object} config The config object
7148 Roo.bootstrap.Pagination = function(config){
7149 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7152 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7158 getAutoCreate : function(){
7164 cfg.cls += ' inverse';
7170 cfg.cls += " " + this.cls;
7188 * @class Roo.bootstrap.PaginationItem
7189 * @extends Roo.bootstrap.Component
7190 * Bootstrap PaginationItem class
7191 * @cfg {String} html text
7192 * @cfg {String} href the link
7193 * @cfg {Boolean} preventDefault (true | false) default true
7194 * @cfg {Boolean} active (true | false) default false
7195 * @cfg {Boolean} disabled default false
7199 * Create a new PaginationItem
7200 * @param {Object} config The config object
7204 Roo.bootstrap.PaginationItem = function(config){
7205 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7210 * The raw click event for the entire grid.
7211 * @param {Roo.EventObject} e
7217 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7221 preventDefault: true,
7226 getAutoCreate : function(){
7232 href : this.href ? this.href : '#',
7233 html : this.html ? this.html : ''
7243 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7247 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7253 initEvents: function() {
7255 this.el.on('click', this.onClick, this);
7258 onClick : function(e)
7260 Roo.log('PaginationItem on click ');
7261 if(this.preventDefault){
7269 this.fireEvent('click', this, e);
7285 * @class Roo.bootstrap.Slider
7286 * @extends Roo.bootstrap.Component
7287 * Bootstrap Slider class
7290 * Create a new Slider
7291 * @param {Object} config The config object
7294 Roo.bootstrap.Slider = function(config){
7295 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7298 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7300 getAutoCreate : function(){
7304 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7308 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7320 * Ext JS Library 1.1.1
7321 * Copyright(c) 2006-2007, Ext JS, LLC.
7323 * Originally Released Under LGPL - original licence link has changed is not relivant.
7326 * <script type="text/javascript">
7329 * @extends Roo.dd.DDProxy
7330 * @class Roo.grid.SplitDragZone
7331 * Support for Column Header resizing
7333 * @param {Object} config
7336 // This is a support class used internally by the Grid components
7337 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7339 this.view = grid.getView();
7340 this.proxy = this.view.resizeProxy;
7341 Roo.grid.SplitDragZone.superclass.constructor.call(
7344 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7346 dragElId : Roo.id(this.proxy.dom),
7351 this.setHandleElId(Roo.id(hd));
7352 if (hd2 !== false) {
7353 this.setOuterHandleElId(Roo.id(hd2));
7356 this.scroll = false;
7358 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7359 fly: Roo.Element.fly,
7361 b4StartDrag : function(x, y){
7362 this.view.headersDisabled = true;
7363 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7364 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7366 this.proxy.setHeight(h);
7368 // for old system colWidth really stored the actual width?
7369 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7370 // which in reality did not work.. - it worked only for fixed sizes
7371 // for resizable we need to use actual sizes.
7372 var w = this.cm.getColumnWidth(this.cellIndex);
7373 if (!this.view.mainWrap) {
7375 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7380 // this was w-this.grid.minColumnWidth;
7381 // doesnt really make sense? - w = thie curren width or the rendered one?
7382 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7383 this.resetConstraints();
7384 this.setXConstraint(minw, 1000);
7385 this.setYConstraint(0, 0);
7386 this.minX = x - minw;
7387 this.maxX = x + 1000;
7389 if (!this.view.mainWrap) { // this is Bootstrap code..
7390 this.getDragEl().style.display='block';
7393 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7397 handleMouseDown : function(e){
7398 ev = Roo.EventObject.setEvent(e);
7399 var t = this.fly(ev.getTarget());
7400 if(t.hasClass("x-grid-split")){
7401 this.cellIndex = this.view.getCellIndex(t.dom);
7403 this.cm = this.grid.colModel;
7404 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7405 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7410 endDrag : function(e){
7411 this.view.headersDisabled = false;
7412 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7413 var diff = endX - this.startPos;
7415 var w = this.cm.getColumnWidth(this.cellIndex);
7416 if (!this.view.mainWrap) {
7419 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7422 autoOffset : function(){
7427 * Ext JS Library 1.1.1
7428 * Copyright(c) 2006-2007, Ext JS, LLC.
7430 * Originally Released Under LGPL - original licence link has changed is not relivant.
7433 * <script type="text/javascript">
7437 * @class Roo.grid.AbstractSelectionModel
7438 * @extends Roo.util.Observable
7440 * Abstract base class for grid SelectionModels. It provides the interface that should be
7441 * implemented by descendant classes. This class should not be directly instantiated.
7444 Roo.grid.AbstractSelectionModel = function(){
7445 this.locked = false;
7446 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7449 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7450 /** @ignore Called by the grid automatically. Do not call directly. */
7451 init : function(grid){
7457 * Locks the selections.
7464 * Unlocks the selections.
7466 unlock : function(){
7467 this.locked = false;
7471 * Returns true if the selections are locked.
7474 isLocked : function(){
7479 * Ext JS Library 1.1.1
7480 * Copyright(c) 2006-2007, Ext JS, LLC.
7482 * Originally Released Under LGPL - original licence link has changed is not relivant.
7485 * <script type="text/javascript">
7488 * @extends Roo.grid.AbstractSelectionModel
7489 * @class Roo.grid.RowSelectionModel
7490 * The default SelectionModel used by {@link Roo.grid.Grid}.
7491 * It supports multiple selections and keyboard selection/navigation.
7493 * @param {Object} config
7495 Roo.grid.RowSelectionModel = function(config){
7496 Roo.apply(this, config);
7497 this.selections = new Roo.util.MixedCollection(false, function(o){
7502 this.lastActive = false;
7506 * @event selectionchange
7507 * Fires when the selection changes
7508 * @param {SelectionModel} this
7510 "selectionchange" : true,
7512 * @event afterselectionchange
7513 * Fires after the selection changes (eg. by key press or clicking)
7514 * @param {SelectionModel} this
7516 "afterselectionchange" : true,
7518 * @event beforerowselect
7519 * Fires when a row is selected being selected, return false to cancel.
7520 * @param {SelectionModel} this
7521 * @param {Number} rowIndex The selected index
7522 * @param {Boolean} keepExisting False if other selections will be cleared
7524 "beforerowselect" : true,
7527 * Fires when a row is selected.
7528 * @param {SelectionModel} this
7529 * @param {Number} rowIndex The selected index
7530 * @param {Roo.data.Record} r The record
7534 * @event rowdeselect
7535 * Fires when a row is deselected.
7536 * @param {SelectionModel} this
7537 * @param {Number} rowIndex The selected index
7539 "rowdeselect" : true
7541 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7542 this.locked = false;
7545 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7547 * @cfg {Boolean} singleSelect
7548 * True to allow selection of only one row at a time (defaults to false)
7550 singleSelect : false,
7553 initEvents : function(){
7555 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7556 this.grid.on("mousedown", this.handleMouseDown, this);
7557 }else{ // allow click to work like normal
7558 this.grid.on("rowclick", this.handleDragableRowClick, this);
7560 // bootstrap does not have a view..
7561 var view = this.grid.view ? this.grid.view : this.grid;
7562 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7565 this.selectPrevious(e.shiftKey);
7566 }else if(this.last !== false && this.lastActive !== false){
7567 var last = this.last;
7568 this.selectRange(this.last, this.lastActive-1);
7569 view.focusRow(this.lastActive);
7574 this.selectFirstRow();
7576 this.fireEvent("afterselectionchange", this);
7578 "down" : function(e){
7580 this.selectNext(e.shiftKey);
7581 }else if(this.last !== false && this.lastActive !== false){
7582 var last = this.last;
7583 this.selectRange(this.last, this.lastActive+1);
7584 view.focusRow(this.lastActive);
7589 this.selectFirstRow();
7591 this.fireEvent("afterselectionchange", this);
7597 view.on("refresh", this.onRefresh, this);
7598 view.on("rowupdated", this.onRowUpdated, this);
7599 view.on("rowremoved", this.onRemove, this);
7603 onRefresh : function(){
7604 var ds = this.grid.ds, i, v = this.grid.view;
7605 var s = this.selections;
7607 if((i = ds.indexOfId(r.id)) != -1){
7609 s.add(ds.getAt(i)); // updating the selection relate data
7617 onRemove : function(v, index, r){
7618 this.selections.remove(r);
7622 onRowUpdated : function(v, index, r){
7623 if(this.isSelected(r)){
7624 v.onRowSelect(index);
7630 * @param {Array} records The records to select
7631 * @param {Boolean} keepExisting (optional) True to keep existing selections
7633 selectRecords : function(records, keepExisting){
7635 this.clearSelections();
7637 var ds = this.grid.ds;
7638 for(var i = 0, len = records.length; i < len; i++){
7639 this.selectRow(ds.indexOf(records[i]), true);
7644 * Gets the number of selected rows.
7647 getCount : function(){
7648 return this.selections.length;
7652 * Selects the first row in the grid.
7654 selectFirstRow : function(){
7659 * Select the last row.
7660 * @param {Boolean} keepExisting (optional) True to keep existing selections
7662 selectLastRow : function(keepExisting){
7663 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7667 * Selects the row immediately following the last selected row.
7668 * @param {Boolean} keepExisting (optional) True to keep existing selections
7670 selectNext : function(keepExisting){
7671 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7672 this.selectRow(this.last+1, keepExisting);
7673 var view = this.grid.view ? this.grid.view : this.grid;
7674 view.focusRow(this.last);
7679 * Selects the row that precedes the last selected row.
7680 * @param {Boolean} keepExisting (optional) True to keep existing selections
7682 selectPrevious : function(keepExisting){
7684 this.selectRow(this.last-1, keepExisting);
7685 var view = this.grid.view ? this.grid.view : this.grid;
7686 view.focusRow(this.last);
7691 * Returns the selected records
7692 * @return {Array} Array of selected records
7694 getSelections : function(){
7695 return [].concat(this.selections.items);
7699 * Returns the first selected record.
7702 getSelected : function(){
7703 return this.selections.itemAt(0);
7708 * Clears all selections.
7710 clearSelections : function(fast){
7715 var ds = this.grid.ds;
7716 var s = this.selections;
7718 this.deselectRow(ds.indexOfId(r.id));
7722 this.selections.clear();
7731 selectAll : function(){
7735 this.selections.clear();
7736 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7737 this.selectRow(i, true);
7742 * Returns True if there is a selection.
7745 hasSelection : function(){
7746 return this.selections.length > 0;
7750 * Returns True if the specified row is selected.
7751 * @param {Number/Record} record The record or index of the record to check
7754 isSelected : function(index){
7755 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7756 return (r && this.selections.key(r.id) ? true : false);
7760 * Returns True if the specified record id is selected.
7761 * @param {String} id The id of record to check
7764 isIdSelected : function(id){
7765 return (this.selections.key(id) ? true : false);
7769 handleMouseDown : function(e, t)
7771 var view = this.grid.view ? this.grid.view : this.grid;
7773 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7776 if(e.shiftKey && this.last !== false){
7777 var last = this.last;
7778 this.selectRange(last, rowIndex, e.ctrlKey);
7779 this.last = last; // reset the last
7780 view.focusRow(rowIndex);
7782 var isSelected = this.isSelected(rowIndex);
7783 if(e.button !== 0 && isSelected){
7784 view.focusRow(rowIndex);
7785 }else if(e.ctrlKey && isSelected){
7786 this.deselectRow(rowIndex);
7787 }else if(!isSelected){
7788 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7789 view.focusRow(rowIndex);
7792 this.fireEvent("afterselectionchange", this);
7795 handleDragableRowClick : function(grid, rowIndex, e)
7797 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7798 this.selectRow(rowIndex, false);
7799 var view = this.grid.view ? this.grid.view : this.grid;
7800 view.focusRow(rowIndex);
7801 this.fireEvent("afterselectionchange", this);
7806 * Selects multiple rows.
7807 * @param {Array} rows Array of the indexes of the row to select
7808 * @param {Boolean} keepExisting (optional) True to keep existing selections
7810 selectRows : function(rows, keepExisting){
7812 this.clearSelections();
7814 for(var i = 0, len = rows.length; i < len; i++){
7815 this.selectRow(rows[i], true);
7820 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7821 * @param {Number} startRow The index of the first row in the range
7822 * @param {Number} endRow The index of the last row in the range
7823 * @param {Boolean} keepExisting (optional) True to retain existing selections
7825 selectRange : function(startRow, endRow, keepExisting){
7830 this.clearSelections();
7832 if(startRow <= endRow){
7833 for(var i = startRow; i <= endRow; i++){
7834 this.selectRow(i, true);
7837 for(var i = startRow; i >= endRow; i--){
7838 this.selectRow(i, true);
7844 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7845 * @param {Number} startRow The index of the first row in the range
7846 * @param {Number} endRow The index of the last row in the range
7848 deselectRange : function(startRow, endRow, preventViewNotify){
7852 for(var i = startRow; i <= endRow; i++){
7853 this.deselectRow(i, preventViewNotify);
7859 * @param {Number} row The index of the row to select
7860 * @param {Boolean} keepExisting (optional) True to keep existing selections
7862 selectRow : function(index, keepExisting, preventViewNotify){
7863 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7866 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7867 if(!keepExisting || this.singleSelect){
7868 this.clearSelections();
7870 var r = this.grid.ds.getAt(index);
7871 this.selections.add(r);
7872 this.last = this.lastActive = index;
7873 if(!preventViewNotify){
7874 var view = this.grid.view ? this.grid.view : this.grid;
7875 view.onRowSelect(index);
7877 this.fireEvent("rowselect", this, index, r);
7878 this.fireEvent("selectionchange", this);
7884 * @param {Number} row The index of the row to deselect
7886 deselectRow : function(index, preventViewNotify){
7890 if(this.last == index){
7893 if(this.lastActive == index){
7894 this.lastActive = false;
7896 var r = this.grid.ds.getAt(index);
7897 this.selections.remove(r);
7898 if(!preventViewNotify){
7899 var view = this.grid.view ? this.grid.view : this.grid;
7900 view.onRowDeselect(index);
7902 this.fireEvent("rowdeselect", this, index);
7903 this.fireEvent("selectionchange", this);
7907 restoreLast : function(){
7909 this.last = this._last;
7914 acceptsNav : function(row, col, cm){
7915 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7919 onEditorKey : function(field, e){
7920 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7925 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7927 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7929 }else if(k == e.ENTER && !e.ctrlKey){
7933 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7935 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7937 }else if(k == e.ESC){
7941 g.startEditing(newCell[0], newCell[1]);
7946 * Ext JS Library 1.1.1
7947 * Copyright(c) 2006-2007, Ext JS, LLC.
7949 * Originally Released Under LGPL - original licence link has changed is not relivant.
7952 * <script type="text/javascript">
7957 * @class Roo.grid.ColumnModel
7958 * @extends Roo.util.Observable
7959 * This is the default implementation of a ColumnModel used by the Grid. It defines
7960 * the columns in the grid.
7963 var colModel = new Roo.grid.ColumnModel([
7964 {header: "Ticker", width: 60, sortable: true, locked: true},
7965 {header: "Company Name", width: 150, sortable: true},
7966 {header: "Market Cap.", width: 100, sortable: true},
7967 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7968 {header: "Employees", width: 100, sortable: true, resizable: false}
7973 * The config options listed for this class are options which may appear in each
7974 * individual column definition.
7975 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7977 * @param {Object} config An Array of column config objects. See this class's
7978 * config objects for details.
7980 Roo.grid.ColumnModel = function(config){
7982 * The config passed into the constructor
7984 this.config = []; //config;
7987 // if no id, create one
7988 // if the column does not have a dataIndex mapping,
7989 // map it to the order it is in the config
7990 for(var i = 0, len = config.length; i < len; i++){
7991 this.addColumn(config[i]);
7996 * The width of columns which have no width specified (defaults to 100)
7999 this.defaultWidth = 100;
8002 * Default sortable of columns which have no sortable specified (defaults to false)
8005 this.defaultSortable = false;
8009 * @event widthchange
8010 * Fires when the width of a column changes.
8011 * @param {ColumnModel} this
8012 * @param {Number} columnIndex The column index
8013 * @param {Number} newWidth The new width
8015 "widthchange": true,
8017 * @event headerchange
8018 * Fires when the text of a header changes.
8019 * @param {ColumnModel} this
8020 * @param {Number} columnIndex The column index
8021 * @param {Number} newText The new header text
8023 "headerchange": true,
8025 * @event hiddenchange
8026 * Fires when a column is hidden or "unhidden".
8027 * @param {ColumnModel} this
8028 * @param {Number} columnIndex The column index
8029 * @param {Boolean} hidden true if hidden, false otherwise
8031 "hiddenchange": true,
8033 * @event columnmoved
8034 * Fires when a column is moved.
8035 * @param {ColumnModel} this
8036 * @param {Number} oldIndex
8037 * @param {Number} newIndex
8039 "columnmoved" : true,
8041 * @event columlockchange
8042 * Fires when a column's locked state is changed
8043 * @param {ColumnModel} this
8044 * @param {Number} colIndex
8045 * @param {Boolean} locked true if locked
8047 "columnlockchange" : true
8049 Roo.grid.ColumnModel.superclass.constructor.call(this);
8051 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8053 * @cfg {String} header The header text to display in the Grid view.
8056 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8059 * @cfg {String} smHeader Header at Bootsrap Small width
8062 * @cfg {String} mdHeader Header at Bootsrap Medium width
8065 * @cfg {String} lgHeader Header at Bootsrap Large width
8068 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8071 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8072 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8073 * specified, the column's index is used as an index into the Record's data Array.
8076 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8077 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8080 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8081 * Defaults to the value of the {@link #defaultSortable} property.
8082 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8085 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8088 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8091 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8094 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8097 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8098 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8099 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8100 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8103 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8106 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8109 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8112 * @cfg {String} cursor (Optional)
8115 * @cfg {String} tooltip (Optional)
8118 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8121 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8124 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8127 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8130 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8133 * Returns the id of the column at the specified index.
8134 * @param {Number} index The column index
8135 * @return {String} the id
8137 getColumnId : function(index){
8138 return this.config[index].id;
8142 * Returns the column for a specified id.
8143 * @param {String} id The column id
8144 * @return {Object} the column
8146 getColumnById : function(id){
8147 return this.lookup[id];
8152 * Returns the column Object for a specified dataIndex.
8153 * @param {String} dataIndex The column dataIndex
8154 * @return {Object|Boolean} the column or false if not found
8156 getColumnByDataIndex: function(dataIndex){
8157 var index = this.findColumnIndex(dataIndex);
8158 return index > -1 ? this.config[index] : false;
8162 * Returns the index for a specified column id.
8163 * @param {String} id The column id
8164 * @return {Number} the index, or -1 if not found
8166 getIndexById : function(id){
8167 for(var i = 0, len = this.config.length; i < len; i++){
8168 if(this.config[i].id == id){
8176 * Returns the index for a specified column dataIndex.
8177 * @param {String} dataIndex The column dataIndex
8178 * @return {Number} the index, or -1 if not found
8181 findColumnIndex : function(dataIndex){
8182 for(var i = 0, len = this.config.length; i < len; i++){
8183 if(this.config[i].dataIndex == dataIndex){
8191 moveColumn : function(oldIndex, newIndex){
8192 var c = this.config[oldIndex];
8193 this.config.splice(oldIndex, 1);
8194 this.config.splice(newIndex, 0, c);
8195 this.dataMap = null;
8196 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8199 isLocked : function(colIndex){
8200 return this.config[colIndex].locked === true;
8203 setLocked : function(colIndex, value, suppressEvent){
8204 if(this.isLocked(colIndex) == value){
8207 this.config[colIndex].locked = value;
8209 this.fireEvent("columnlockchange", this, colIndex, value);
8213 getTotalLockedWidth : function(){
8215 for(var i = 0; i < this.config.length; i++){
8216 if(this.isLocked(i) && !this.isHidden(i)){
8217 this.totalWidth += this.getColumnWidth(i);
8223 getLockedCount : function(){
8224 for(var i = 0, len = this.config.length; i < len; i++){
8225 if(!this.isLocked(i)){
8230 return this.config.length;
8234 * Returns the number of columns.
8237 getColumnCount : function(visibleOnly){
8238 if(visibleOnly === true){
8240 for(var i = 0, len = this.config.length; i < len; i++){
8241 if(!this.isHidden(i)){
8247 return this.config.length;
8251 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8252 * @param {Function} fn
8253 * @param {Object} scope (optional)
8254 * @return {Array} result
8256 getColumnsBy : function(fn, scope){
8258 for(var i = 0, len = this.config.length; i < len; i++){
8259 var c = this.config[i];
8260 if(fn.call(scope||this, c, i) === true){
8268 * Returns true if the specified column is sortable.
8269 * @param {Number} col The column index
8272 isSortable : function(col){
8273 if(typeof this.config[col].sortable == "undefined"){
8274 return this.defaultSortable;
8276 return this.config[col].sortable;
8280 * Returns the rendering (formatting) function defined for the column.
8281 * @param {Number} col The column index.
8282 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8284 getRenderer : function(col){
8285 if(!this.config[col].renderer){
8286 return Roo.grid.ColumnModel.defaultRenderer;
8288 return this.config[col].renderer;
8292 * Sets the rendering (formatting) function for a column.
8293 * @param {Number} col The column index
8294 * @param {Function} fn The function to use to process the cell's raw data
8295 * to return HTML markup for the grid view. The render function is called with
8296 * the following parameters:<ul>
8297 * <li>Data value.</li>
8298 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8299 * <li>css A CSS style string to apply to the table cell.</li>
8300 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8301 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8302 * <li>Row index</li>
8303 * <li>Column index</li>
8304 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8306 setRenderer : function(col, fn){
8307 this.config[col].renderer = fn;
8311 * Returns the width for the specified column.
8312 * @param {Number} col The column index
8313 * @param (optional) {String} gridSize bootstrap width size.
8316 getColumnWidth : function(col, gridSize)
8318 var cfg = this.config[col];
8320 if (typeof(gridSize) == 'undefined') {
8321 return cfg.width * 1 || this.defaultWidth;
8323 if (gridSize === false) { // if we set it..
8324 return cfg.width || false;
8326 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8328 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8329 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8332 return cfg[ sizes[i] ];
8339 * Sets the width for a column.
8340 * @param {Number} col The column index
8341 * @param {Number} width The new width
8343 setColumnWidth : function(col, width, suppressEvent){
8344 this.config[col].width = width;
8345 this.totalWidth = null;
8347 this.fireEvent("widthchange", this, col, width);
8352 * Returns the total width of all columns.
8353 * @param {Boolean} includeHidden True to include hidden column widths
8356 getTotalWidth : function(includeHidden){
8357 if(!this.totalWidth){
8358 this.totalWidth = 0;
8359 for(var i = 0, len = this.config.length; i < len; i++){
8360 if(includeHidden || !this.isHidden(i)){
8361 this.totalWidth += this.getColumnWidth(i);
8365 return this.totalWidth;
8369 * Returns the header for the specified column.
8370 * @param {Number} col The column index
8373 getColumnHeader : function(col){
8374 return this.config[col].header;
8378 * Sets the header for a column.
8379 * @param {Number} col The column index
8380 * @param {String} header The new header
8382 setColumnHeader : function(col, header){
8383 this.config[col].header = header;
8384 this.fireEvent("headerchange", this, col, header);
8388 * Returns the tooltip for the specified column.
8389 * @param {Number} col The column index
8392 getColumnTooltip : function(col){
8393 return this.config[col].tooltip;
8396 * Sets the tooltip for a column.
8397 * @param {Number} col The column index
8398 * @param {String} tooltip The new tooltip
8400 setColumnTooltip : function(col, tooltip){
8401 this.config[col].tooltip = tooltip;
8405 * Returns the dataIndex for the specified column.
8406 * @param {Number} col The column index
8409 getDataIndex : function(col){
8410 return this.config[col].dataIndex;
8414 * Sets the dataIndex for a column.
8415 * @param {Number} col The column index
8416 * @param {Number} dataIndex The new dataIndex
8418 setDataIndex : function(col, dataIndex){
8419 this.config[col].dataIndex = dataIndex;
8425 * Returns true if the cell is editable.
8426 * @param {Number} colIndex The column index
8427 * @param {Number} rowIndex The row index - this is nto actually used..?
8430 isCellEditable : function(colIndex, rowIndex){
8431 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8435 * Returns the editor defined for the cell/column.
8436 * return false or null to disable editing.
8437 * @param {Number} colIndex The column index
8438 * @param {Number} rowIndex The row index
8441 getCellEditor : function(colIndex, rowIndex){
8442 return this.config[colIndex].editor;
8446 * Sets if a column is editable.
8447 * @param {Number} col The column index
8448 * @param {Boolean} editable True if the column is editable
8450 setEditable : function(col, editable){
8451 this.config[col].editable = editable;
8456 * Returns true if the column is hidden.
8457 * @param {Number} colIndex The column index
8460 isHidden : function(colIndex){
8461 return this.config[colIndex].hidden;
8466 * Returns true if the column width cannot be changed
8468 isFixed : function(colIndex){
8469 return this.config[colIndex].fixed;
8473 * Returns true if the column can be resized
8476 isResizable : function(colIndex){
8477 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8480 * Sets if a column is hidden.
8481 * @param {Number} colIndex The column index
8482 * @param {Boolean} hidden True if the column is hidden
8484 setHidden : function(colIndex, hidden){
8485 this.config[colIndex].hidden = hidden;
8486 this.totalWidth = null;
8487 this.fireEvent("hiddenchange", this, colIndex, hidden);
8491 * Sets the editor for a column.
8492 * @param {Number} col The column index
8493 * @param {Object} editor The editor object
8495 setEditor : function(col, editor){
8496 this.config[col].editor = editor;
8499 * Add a column (experimental...) - defaults to adding to the end..
8500 * @param {Object} config
8502 addColumn : function(c)
8505 var i = this.config.length;
8508 if(typeof c.dataIndex == "undefined"){
8511 if(typeof c.renderer == "string"){
8512 c.renderer = Roo.util.Format[c.renderer];
8514 if(typeof c.id == "undefined"){
8517 if(c.editor && c.editor.xtype){
8518 c.editor = Roo.factory(c.editor, Roo.grid);
8520 if(c.editor && c.editor.isFormField){
8521 c.editor = new Roo.grid.GridEditor(c.editor);
8523 this.lookup[c.id] = c;
8528 Roo.grid.ColumnModel.defaultRenderer = function(value)
8530 if(typeof value == "object") {
8533 if(typeof value == "string" && value.length < 1){
8537 return String.format("{0}", value);
8540 // Alias for backwards compatibility
8541 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8544 * Ext JS Library 1.1.1
8545 * Copyright(c) 2006-2007, Ext JS, LLC.
8547 * Originally Released Under LGPL - original licence link has changed is not relivant.
8550 * <script type="text/javascript">
8554 * @class Roo.LoadMask
8555 * A simple utility class for generically masking elements while loading data. If the element being masked has
8556 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8557 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8558 * element's UpdateManager load indicator and will be destroyed after the initial load.
8560 * Create a new LoadMask
8561 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8562 * @param {Object} config The config object
8564 Roo.LoadMask = function(el, config){
8565 this.el = Roo.get(el);
8566 Roo.apply(this, config);
8568 this.store.on('beforeload', this.onBeforeLoad, this);
8569 this.store.on('load', this.onLoad, this);
8570 this.store.on('loadexception', this.onLoadException, this);
8571 this.removeMask = false;
8573 var um = this.el.getUpdateManager();
8574 um.showLoadIndicator = false; // disable the default indicator
8575 um.on('beforeupdate', this.onBeforeLoad, this);
8576 um.on('update', this.onLoad, this);
8577 um.on('failure', this.onLoad, this);
8578 this.removeMask = true;
8582 Roo.LoadMask.prototype = {
8584 * @cfg {Boolean} removeMask
8585 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8586 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8591 * The text to display in a centered loading message box (defaults to 'Loading...')
8595 * @cfg {String} msgCls
8596 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8598 msgCls : 'x-mask-loading',
8601 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8607 * Disables the mask to prevent it from being displayed
8609 disable : function(){
8610 this.disabled = true;
8614 * Enables the mask so that it can be displayed
8616 enable : function(){
8617 this.disabled = false;
8620 onLoadException : function()
8624 if (typeof(arguments[3]) != 'undefined') {
8625 Roo.MessageBox.alert("Error loading",arguments[3]);
8629 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8630 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8637 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8642 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8646 onBeforeLoad : function(){
8648 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8653 destroy : function(){
8655 this.store.un('beforeload', this.onBeforeLoad, this);
8656 this.store.un('load', this.onLoad, this);
8657 this.store.un('loadexception', this.onLoadException, this);
8659 var um = this.el.getUpdateManager();
8660 um.un('beforeupdate', this.onBeforeLoad, this);
8661 um.un('update', this.onLoad, this);
8662 um.un('failure', this.onLoad, this);
8666 * @class Roo.bootstrap.Table
8668 * @extends Roo.bootstrap.Component
8669 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8670 * Similar to Roo.grid.Grid
8672 var table = Roo.factory({
8674 xns : Roo.bootstrap,
8675 autoSizeColumns: true,
8682 sortInfo : { direction : 'ASC', field: 'name' },
8684 xtype : 'HttpProxy',
8687 url : 'https://example.com/some.data.url.json'
8690 xtype : 'JsonReader',
8692 fields : [ 'id', 'name', whatever' ],
8699 xtype : 'ColumnModel',
8703 dataIndex : 'is_in_group',
8706 renderer : function(v, x , r) {
8708 return String.format("{0}", v)
8714 xtype : 'RowSelectionModel',
8715 xns : Roo.bootstrap.Table
8716 // you can add listeners to catch selection change here....
8722 grid.render(Roo.get("some-div"));
8725 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8730 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8731 * @cfg {Roo.data.Store} store The data store to use
8732 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8734 * @cfg {String} cls table class
8737 * @cfg {boolean} striped Should the rows be alternative striped
8738 * @cfg {boolean} bordered Add borders to the table
8739 * @cfg {boolean} hover Add hover highlighting
8740 * @cfg {boolean} condensed Format condensed
8741 * @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,
8742 * also adds table-responsive (see bootstrap docs for details)
8743 * @cfg {Boolean} loadMask (true|false) default false
8744 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8745 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8746 * @cfg {Boolean} rowSelection (true|false) default false
8747 * @cfg {Boolean} cellSelection (true|false) default false
8748 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8749 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8750 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8751 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8752 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8753 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8756 * Create a new Table
8757 * @param {Object} config The config object
8760 Roo.bootstrap.Table = function(config)
8762 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8765 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8766 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8767 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8768 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8770 this.view = this; // compat with grid.
8772 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8774 this.sm.grid = this;
8775 this.selModel = Roo.factory(this.sm, Roo.grid);
8776 this.sm = this.selModel;
8777 this.sm.xmodule = this.xmodule || false;
8780 if (this.cm && typeof(this.cm.config) == 'undefined') {
8781 this.colModel = new Roo.grid.ColumnModel(this.cm);
8782 this.cm = this.colModel;
8783 this.cm.xmodule = this.xmodule || false;
8786 this.store= Roo.factory(this.store, Roo.data);
8787 this.ds = this.store;
8788 this.ds.xmodule = this.xmodule || false;
8791 if (this.footer && this.store) {
8792 this.footer.dataSource = this.ds;
8793 this.footer = Roo.factory(this.footer);
8800 * Fires when a cell is clicked
8801 * @param {Roo.bootstrap.Table} this
8802 * @param {Roo.Element} el
8803 * @param {Number} rowIndex
8804 * @param {Number} columnIndex
8805 * @param {Roo.EventObject} e
8809 * @event celldblclick
8810 * Fires when a cell is double clicked
8811 * @param {Roo.bootstrap.Table} this
8812 * @param {Roo.Element} el
8813 * @param {Number} rowIndex
8814 * @param {Number} columnIndex
8815 * @param {Roo.EventObject} e
8817 "celldblclick" : true,
8820 * Fires when a row is clicked
8821 * @param {Roo.bootstrap.Table} this
8822 * @param {Roo.Element} el
8823 * @param {Number} rowIndex
8824 * @param {Roo.EventObject} e
8828 * @event rowdblclick
8829 * Fires when a row is double clicked
8830 * @param {Roo.bootstrap.Table} this
8831 * @param {Roo.Element} el
8832 * @param {Number} rowIndex
8833 * @param {Roo.EventObject} e
8835 "rowdblclick" : true,
8838 * Fires when a mouseover occur
8839 * @param {Roo.bootstrap.Table} this
8840 * @param {Roo.Element} el
8841 * @param {Number} rowIndex
8842 * @param {Number} columnIndex
8843 * @param {Roo.EventObject} e
8848 * Fires when a mouseout occur
8849 * @param {Roo.bootstrap.Table} this
8850 * @param {Roo.Element} el
8851 * @param {Number} rowIndex
8852 * @param {Number} columnIndex
8853 * @param {Roo.EventObject} e
8858 * Fires when a row is rendered, so you can change add a style to it.
8859 * @param {Roo.bootstrap.Table} this
8860 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8864 * @event rowsrendered
8865 * Fires when all the rows have been rendered
8866 * @param {Roo.bootstrap.Table} this
8868 'rowsrendered' : true,
8870 * @event contextmenu
8871 * The raw contextmenu event for the entire grid.
8872 * @param {Roo.EventObject} e
8874 "contextmenu" : true,
8876 * @event rowcontextmenu
8877 * Fires when a row is right clicked
8878 * @param {Roo.bootstrap.Table} this
8879 * @param {Number} rowIndex
8880 * @param {Roo.EventObject} e
8882 "rowcontextmenu" : true,
8884 * @event cellcontextmenu
8885 * Fires when a cell is right clicked
8886 * @param {Roo.bootstrap.Table} this
8887 * @param {Number} rowIndex
8888 * @param {Number} cellIndex
8889 * @param {Roo.EventObject} e
8891 "cellcontextmenu" : true,
8893 * @event headercontextmenu
8894 * Fires when a header is right clicked
8895 * @param {Roo.bootstrap.Table} this
8896 * @param {Number} columnIndex
8897 * @param {Roo.EventObject} e
8899 "headercontextmenu" : true,
8902 * The raw mousedown event for the entire grid.
8903 * @param {Roo.EventObject} e
8910 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8926 enableColumnResize: true,
8928 rowSelection : false,
8929 cellSelection : false,
8932 minColumnWidth : 50,
8934 // Roo.Element - the tbody
8935 bodyEl: false, // <tbody> Roo.Element - thead element
8936 headEl: false, // <thead> Roo.Element - thead element
8937 resizeProxy : false, // proxy element for dragging?
8941 container: false, // used by gridpanel...
8947 auto_hide_footer : false,
8949 view: false, // actually points to this..
8951 getAutoCreate : function()
8953 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8960 // this get's auto added by panel.Grid
8961 if (this.scrollBody) {
8962 cfg.cls += ' table-body-fixed';
8965 cfg.cls += ' table-striped';
8969 cfg.cls += ' table-hover';
8971 if (this.bordered) {
8972 cfg.cls += ' table-bordered';
8974 if (this.condensed) {
8975 cfg.cls += ' table-condensed';
8978 if (this.responsive) {
8979 cfg.cls += ' table-responsive';
8983 cfg.cls+= ' ' +this.cls;
8989 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8992 if(this.store || this.cm){
8993 if(this.headerShow){
8994 cfg.cn.push(this.renderHeader());
8997 cfg.cn.push(this.renderBody());
8999 if(this.footerShow){
9000 cfg.cn.push(this.renderFooter());
9002 // where does this come from?
9003 //cfg.cls+= ' TableGrid';
9006 return { cn : [ cfg ] };
9009 initEvents : function()
9011 if(!this.store || !this.cm){
9014 if (this.selModel) {
9015 this.selModel.initEvents();
9019 //Roo.log('initEvents with ds!!!!');
9021 this.bodyEl = this.el.select('tbody', true).first();
9022 this.headEl = this.el.select('thead', true).first();
9023 this.mainFoot = this.el.select('tfoot', true).first();
9028 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9029 e.on('click', this.sort, this);
9033 // why is this done????? = it breaks dialogs??
9034 //this.parent().el.setStyle('position', 'relative');
9038 this.footer.parentId = this.id;
9039 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9042 this.el.select('tfoot tr td').first().addClass('hide');
9047 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9050 this.store.on('load', this.onLoad, this);
9051 this.store.on('beforeload', this.onBeforeLoad, this);
9052 this.store.on('update', this.onUpdate, this);
9053 this.store.on('add', this.onAdd, this);
9054 this.store.on("clear", this.clear, this);
9056 this.el.on("contextmenu", this.onContextMenu, this);
9059 this.cm.on("headerchange", this.onHeaderChange, this);
9060 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9062 //?? does bodyEl get replaced on render?
9063 this.bodyEl.on("click", this.onClick, this);
9064 this.bodyEl.on("dblclick", this.onDblClick, this);
9065 this.bodyEl.on('scroll', this.onBodyScroll, this);
9067 // guessing mainbody will work - this relays usually caught by selmodel at present.
9068 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9071 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9074 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9075 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9080 // Compatibility with grid - we implement all the view features at present.
9081 getView : function()
9086 initCSS : function()
9090 var cm = this.cm, styles = [];
9091 this.CSS.removeStyleSheet(this.id + '-cssrules');
9092 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9093 // we can honour xs/sm/md/xl as widths...
9094 // we first have to decide what widht we are currently at...
9095 var sz = Roo.getGridSize();
9099 var cols = []; // visable cols.
9101 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9102 var w = cm.getColumnWidth(i, false);
9104 cols.push( { rel : false, abs : 0 });
9108 cols.push( { rel : false, abs : w });
9110 last = i; // not really..
9113 var w = cm.getColumnWidth(i, sz);
9118 cols.push( { rel : w, abs : false });
9121 var avail = this.bodyEl.dom.clientWidth - total_abs;
9123 var unitWidth = Math.floor(avail / total);
9124 var rem = avail - (unitWidth * total);
9126 var hidden, width, pos = 0 , splithide , left;
9127 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9129 hidden = 'display:none;';
9131 width = 'width:0px;';
9133 if(!cm.isHidden(i)){
9137 // we can honour xs/sm/md/xl ?
9138 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9140 hidden = 'display:none;';
9142 // width should return a small number...
9144 w+=rem; // add the remaining with..
9147 left = "left:" + (pos -4) + "px;";
9148 width = "width:" + w+ "px;";
9151 if (this.responsive) {
9154 hidden = cm.isHidden(i) ? 'display:none;' : '';
9155 splithide = 'display: none;';
9158 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9161 splithide = 'display:none;';
9164 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9165 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9170 //Roo.log(styles.join(''));
9171 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9177 onContextMenu : function(e, t)
9179 this.processEvent("contextmenu", e);
9182 processEvent : function(name, e)
9184 if (name != 'touchstart' ) {
9185 this.fireEvent(name, e);
9188 var t = e.getTarget();
9190 var cell = Roo.get(t);
9196 if(cell.findParent('tfoot', false, true)){
9200 if(cell.findParent('thead', false, true)){
9202 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9203 cell = Roo.get(t).findParent('th', false, true);
9205 Roo.log("failed to find th in thead?");
9206 Roo.log(e.getTarget());
9211 var cellIndex = cell.dom.cellIndex;
9213 var ename = name == 'touchstart' ? 'click' : name;
9214 this.fireEvent("header" + ename, this, cellIndex, e);
9219 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9220 cell = Roo.get(t).findParent('td', false, true);
9222 Roo.log("failed to find th in tbody?");
9223 Roo.log(e.getTarget());
9228 var row = cell.findParent('tr', false, true);
9229 var cellIndex = cell.dom.cellIndex;
9230 var rowIndex = row.dom.rowIndex - 1;
9234 this.fireEvent("row" + name, this, rowIndex, e);
9238 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9244 onMouseover : function(e, el)
9246 var cell = Roo.get(el);
9252 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9253 cell = cell.findParent('td', false, true);
9256 var row = cell.findParent('tr', false, true);
9257 var cellIndex = cell.dom.cellIndex;
9258 var rowIndex = row.dom.rowIndex - 1; // start from 0
9260 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9264 onMouseout : function(e, el)
9266 var cell = Roo.get(el);
9272 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9273 cell = cell.findParent('td', false, true);
9276 var row = cell.findParent('tr', false, true);
9277 var cellIndex = cell.dom.cellIndex;
9278 var rowIndex = row.dom.rowIndex - 1; // start from 0
9280 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9284 onClick : function(e, el)
9286 var cell = Roo.get(el);
9288 if(!cell || (!this.cellSelection && !this.rowSelection)){
9292 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9293 cell = cell.findParent('td', false, true);
9296 if(!cell || typeof(cell) == 'undefined'){
9300 var row = cell.findParent('tr', false, true);
9302 if(!row || typeof(row) == 'undefined'){
9306 var cellIndex = cell.dom.cellIndex;
9307 var rowIndex = this.getRowIndex(row);
9309 // why??? - should these not be based on SelectionModel?
9310 //if(this.cellSelection){
9311 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9314 //if(this.rowSelection){
9315 this.fireEvent('rowclick', this, row, rowIndex, e);
9320 onDblClick : function(e,el)
9322 var cell = Roo.get(el);
9324 if(!cell || (!this.cellSelection && !this.rowSelection)){
9328 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9329 cell = cell.findParent('td', false, true);
9332 if(!cell || typeof(cell) == 'undefined'){
9336 var row = cell.findParent('tr', false, true);
9338 if(!row || typeof(row) == 'undefined'){
9342 var cellIndex = cell.dom.cellIndex;
9343 var rowIndex = this.getRowIndex(row);
9345 if(this.cellSelection){
9346 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9349 if(this.rowSelection){
9350 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9353 findRowIndex : function(el)
9355 var cell = Roo.get(el);
9359 var row = cell.findParent('tr', false, true);
9361 if(!row || typeof(row) == 'undefined'){
9364 return this.getRowIndex(row);
9366 sort : function(e,el)
9368 var col = Roo.get(el);
9370 if(!col.hasClass('sortable')){
9374 var sort = col.attr('sort');
9377 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9381 this.store.sortInfo = {field : sort, direction : dir};
9384 Roo.log("calling footer first");
9385 this.footer.onClick('first');
9388 this.store.load({ params : { start : 0 } });
9392 renderHeader : function()
9400 this.totalWidth = 0;
9402 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9404 var config = cm.config[i];
9408 cls : 'x-hcol-' + i,
9411 html: cm.getColumnHeader(i)
9414 var tooltip = cm.getColumnTooltip(i);
9416 c.tooltip = tooltip;
9422 if(typeof(config.sortable) != 'undefined' && config.sortable){
9423 c.cls += ' sortable';
9424 c.html = '<i class="fa"></i>' + c.html;
9427 // could use BS4 hidden-..-down
9429 if(typeof(config.lgHeader) != 'undefined'){
9430 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9433 if(typeof(config.mdHeader) != 'undefined'){
9434 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9437 if(typeof(config.smHeader) != 'undefined'){
9438 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9441 if(typeof(config.xsHeader) != 'undefined'){
9442 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9449 if(typeof(config.tooltip) != 'undefined'){
9450 c.tooltip = config.tooltip;
9453 if(typeof(config.colspan) != 'undefined'){
9454 c.colspan = config.colspan;
9457 // hidden is handled by CSS now
9459 if(typeof(config.dataIndex) != 'undefined'){
9460 c.sort = config.dataIndex;
9465 if(typeof(config.align) != 'undefined' && config.align.length){
9466 c.style += ' text-align:' + config.align + ';';
9469 /* width is done in CSS
9470 *if(typeof(config.width) != 'undefined'){
9471 c.style += ' width:' + config.width + 'px;';
9472 this.totalWidth += config.width;
9474 this.totalWidth += 100; // assume minimum of 100 per column?
9478 if(typeof(config.cls) != 'undefined'){
9479 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9481 // this is the bit that doesnt reall work at all...
9483 if (this.responsive) {
9486 ['xs','sm','md','lg'].map(function(size){
9488 if(typeof(config[size]) == 'undefined'){
9492 if (!config[size]) { // 0 = hidden
9493 // BS 4 '0' is treated as hide that column and below.
9494 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9498 c.cls += ' col-' + size + '-' + config[size] + (
9499 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9507 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9518 renderBody : function()
9528 colspan : this.cm.getColumnCount()
9538 renderFooter : function()
9548 colspan : this.cm.getColumnCount()
9562 // Roo.log('ds onload');
9567 var ds = this.store;
9569 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9570 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9571 if (_this.store.sortInfo) {
9573 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9574 e.select('i', true).addClass(['fa-arrow-up']);
9577 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9578 e.select('i', true).addClass(['fa-arrow-down']);
9583 var tbody = this.bodyEl;
9585 if(ds.getCount() > 0){
9586 ds.data.each(function(d,rowIndex){
9587 var row = this.renderRow(cm, ds, rowIndex);
9589 tbody.createChild(row);
9593 if(row.cellObjects.length){
9594 Roo.each(row.cellObjects, function(r){
9595 _this.renderCellObject(r);
9602 var tfoot = this.el.select('tfoot', true).first();
9604 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9606 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9608 var total = this.ds.getTotalCount();
9610 if(this.footer.pageSize < total){
9611 this.mainFoot.show();
9615 Roo.each(this.el.select('tbody td', true).elements, function(e){
9616 e.on('mouseover', _this.onMouseover, _this);
9619 Roo.each(this.el.select('tbody td', true).elements, function(e){
9620 e.on('mouseout', _this.onMouseout, _this);
9622 this.fireEvent('rowsrendered', this);
9626 this.initCSS(); /// resize cols
9632 onUpdate : function(ds,record)
9634 this.refreshRow(record);
9638 onRemove : function(ds, record, index, isUpdate){
9639 if(isUpdate !== true){
9640 this.fireEvent("beforerowremoved", this, index, record);
9642 var bt = this.bodyEl.dom;
9644 var rows = this.el.select('tbody > tr', true).elements;
9646 if(typeof(rows[index]) != 'undefined'){
9647 bt.removeChild(rows[index].dom);
9650 // if(bt.rows[index]){
9651 // bt.removeChild(bt.rows[index]);
9654 if(isUpdate !== true){
9655 //this.stripeRows(index);
9656 //this.syncRowHeights(index, index);
9658 this.fireEvent("rowremoved", this, index, record);
9662 onAdd : function(ds, records, rowIndex)
9664 //Roo.log('on Add called');
9665 // - note this does not handle multiple adding very well..
9666 var bt = this.bodyEl.dom;
9667 for (var i =0 ; i < records.length;i++) {
9668 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9669 //Roo.log(records[i]);
9670 //Roo.log(this.store.getAt(rowIndex+i));
9671 this.insertRow(this.store, rowIndex + i, false);
9678 refreshRow : function(record){
9679 var ds = this.store, index;
9680 if(typeof record == 'number'){
9682 record = ds.getAt(index);
9684 index = ds.indexOf(record);
9686 return; // should not happen - but seems to
9689 this.insertRow(ds, index, true);
9691 this.onRemove(ds, record, index+1, true);
9693 //this.syncRowHeights(index, index);
9695 this.fireEvent("rowupdated", this, index, record);
9697 // private - called by RowSelection
9698 onRowSelect : function(rowIndex){
9699 var row = this.getRowDom(rowIndex);
9700 row.addClass(['bg-info','info']);
9702 // private - called by RowSelection
9703 onRowDeselect : function(rowIndex)
9708 var row = this.getRowDom(rowIndex);
9709 row.removeClass(['bg-info','info']);
9712 * Focuses the specified row.
9713 * @param {Number} row The row index
9715 focusRow : function(row)
9717 //Roo.log('GridView.focusRow');
9718 var x = this.bodyEl.dom.scrollLeft;
9719 this.focusCell(row, 0, false);
9720 this.bodyEl.dom.scrollLeft = x;
9724 * Focuses the specified cell.
9725 * @param {Number} row The row index
9726 * @param {Number} col The column index
9727 * @param {Boolean} hscroll false to disable horizontal scrolling
9729 focusCell : function(row, col, hscroll)
9731 //Roo.log('GridView.focusCell');
9732 var el = this.ensureVisible(row, col, hscroll);
9733 // not sure what focusEL achives = it's a <a> pos relative
9734 //this.focusEl.alignTo(el, "tl-tl");
9736 // this.focusEl.focus();
9738 // this.focusEl.focus.defer(1, this.focusEl);
9743 * Scrolls the specified cell into view
9744 * @param {Number} row The row index
9745 * @param {Number} col The column index
9746 * @param {Boolean} hscroll false to disable horizontal scrolling
9748 ensureVisible : function(row, col, hscroll)
9750 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9751 //return null; //disable for testing.
9752 if(typeof row != "number"){
9755 if(row < 0 && row >= this.ds.getCount()){
9758 col = (col !== undefined ? col : 0);
9760 while(cm.isHidden(col)){
9764 var el = this.getCellDom(row, col);
9768 var c = this.bodyEl.dom;
9770 var ctop = parseInt(el.offsetTop, 10);
9771 var cleft = parseInt(el.offsetLeft, 10);
9772 var cbot = ctop + el.offsetHeight;
9773 var cright = cleft + el.offsetWidth;
9775 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9776 var ch = 0; //?? header is not withing the area?
9777 var stop = parseInt(c.scrollTop, 10);
9778 var sleft = parseInt(c.scrollLeft, 10);
9779 var sbot = stop + ch;
9780 var sright = sleft + c.clientWidth;
9782 Roo.log('GridView.ensureVisible:' +
9784 ' c.clientHeight:' + c.clientHeight +
9785 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9794 //Roo.log("set scrolltop to ctop DISABLE?");
9795 }else if(cbot > sbot){
9796 //Roo.log("set scrolltop to cbot-ch");
9797 c.scrollTop = cbot-ch;
9800 if(hscroll !== false){
9802 c.scrollLeft = cleft;
9803 }else if(cright > sright){
9804 c.scrollLeft = cright-c.clientWidth;
9812 insertRow : function(dm, rowIndex, isUpdate){
9815 this.fireEvent("beforerowsinserted", this, rowIndex);
9817 //var s = this.getScrollState();
9818 var row = this.renderRow(this.cm, this.store, rowIndex);
9819 // insert before rowIndex..
9820 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9824 if(row.cellObjects.length){
9825 Roo.each(row.cellObjects, function(r){
9826 _this.renderCellObject(r);
9831 this.fireEvent("rowsinserted", this, rowIndex);
9832 //this.syncRowHeights(firstRow, lastRow);
9833 //this.stripeRows(firstRow);
9840 getRowDom : function(rowIndex)
9842 var rows = this.el.select('tbody > tr', true).elements;
9844 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9847 getCellDom : function(rowIndex, colIndex)
9849 var row = this.getRowDom(rowIndex);
9850 if (row === false) {
9853 var cols = row.select('td', true).elements;
9854 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9858 // returns the object tree for a tr..
9861 renderRow : function(cm, ds, rowIndex)
9863 var d = ds.getAt(rowIndex);
9867 cls : 'x-row-' + rowIndex,
9871 var cellObjects = [];
9873 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9874 var config = cm.config[i];
9876 var renderer = cm.getRenderer(i);
9880 if(typeof(renderer) !== 'undefined'){
9881 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9883 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9884 // and are rendered into the cells after the row is rendered - using the id for the element.
9886 if(typeof(value) === 'object'){
9896 rowIndex : rowIndex,
9901 this.fireEvent('rowclass', this, rowcfg);
9905 // this might end up displaying HTML?
9906 // this is too messy... - better to only do it on columsn you know are going to be too long
9907 //tooltip : (typeof(value) === 'object') ? '' : value,
9908 cls : rowcfg.rowClass + ' x-col-' + i,
9910 html: (typeof(value) === 'object') ? '' : value
9917 if(typeof(config.colspan) != 'undefined'){
9918 td.colspan = config.colspan;
9923 if(typeof(config.align) != 'undefined' && config.align.length){
9924 td.style += ' text-align:' + config.align + ';';
9926 if(typeof(config.valign) != 'undefined' && config.valign.length){
9927 td.style += ' vertical-align:' + config.valign + ';';
9930 if(typeof(config.width) != 'undefined'){
9931 td.style += ' width:' + config.width + 'px;';
9935 if(typeof(config.cursor) != 'undefined'){
9936 td.style += ' cursor:' + config.cursor + ';';
9939 if(typeof(config.cls) != 'undefined'){
9940 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9942 if (this.responsive) {
9943 ['xs','sm','md','lg'].map(function(size){
9945 if(typeof(config[size]) == 'undefined'){
9951 if (!config[size]) { // 0 = hidden
9952 // BS 4 '0' is treated as hide that column and below.
9953 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9957 td.cls += ' col-' + size + '-' + config[size] + (
9958 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9968 row.cellObjects = cellObjects;
9976 onBeforeLoad : function()
9985 this.el.select('tbody', true).first().dom.innerHTML = '';
9988 * Show or hide a row.
9989 * @param {Number} rowIndex to show or hide
9990 * @param {Boolean} state hide
9992 setRowVisibility : function(rowIndex, state)
9994 var bt = this.bodyEl.dom;
9996 var rows = this.el.select('tbody > tr', true).elements;
9998 if(typeof(rows[rowIndex]) == 'undefined'){
10001 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10006 getSelectionModel : function(){
10007 if(!this.selModel){
10008 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10010 return this.selModel;
10013 * Render the Roo.bootstrap object from renderder
10015 renderCellObject : function(r)
10019 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10021 var t = r.cfg.render(r.container);
10024 Roo.each(r.cfg.cn, function(c){
10026 container: t.getChildContainer(),
10029 _this.renderCellObject(child);
10034 * get the Row Index from a dom element.
10035 * @param {Roo.Element} row The row to look for
10036 * @returns {Number} the row
10038 getRowIndex : function(row)
10042 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10053 * get the header TH element for columnIndex
10054 * @param {Number} columnIndex
10055 * @returns {Roo.Element}
10057 getHeaderIndex: function(colIndex)
10059 var cols = this.headEl.select('th', true).elements;
10060 return cols[colIndex];
10063 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10064 * @param {domElement} cell to look for
10065 * @returns {Number} the column
10067 getCellIndex : function(cell)
10069 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10071 return parseInt(id[1], 10);
10076 * Returns the grid's underlying element = used by panel.Grid
10077 * @return {Element} The element
10079 getGridEl : function(){
10083 * Forces a resize - used by panel.Grid
10084 * @return {Element} The element
10086 autoSize : function()
10088 //var ctr = Roo.get(this.container.dom.parentElement);
10089 var ctr = Roo.get(this.el.dom);
10091 var thd = this.getGridEl().select('thead',true).first();
10092 var tbd = this.getGridEl().select('tbody', true).first();
10093 var tfd = this.getGridEl().select('tfoot', true).first();
10095 var cw = ctr.getWidth();
10096 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10100 tbd.setWidth(ctr.getWidth());
10101 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10102 // this needs fixing for various usage - currently only hydra job advers I think..
10104 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10106 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10109 cw = Math.max(cw, this.totalWidth);
10110 this.getGridEl().select('tbody tr',true).setWidth(cw);
10113 // resize 'expandable coloumn?
10115 return; // we doe not have a view in this design..
10118 onBodyScroll: function()
10120 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10122 this.headEl.setStyle({
10123 'position' : 'relative',
10124 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10130 var scrollHeight = this.bodyEl.dom.scrollHeight;
10132 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10134 var height = this.bodyEl.getHeight();
10136 if(scrollHeight - height == scrollTop) {
10138 var total = this.ds.getTotalCount();
10140 if(this.footer.cursor + this.footer.pageSize < total){
10142 this.footer.ds.load({
10144 start : this.footer.cursor + this.footer.pageSize,
10145 limit : this.footer.pageSize
10154 onColumnSplitterMoved : function(i, diff)
10156 this.userResized = true;
10158 var cm = this.colModel;
10160 var w = this.getHeaderIndex(i).getWidth() + diff;
10163 cm.setColumnWidth(i, w, true);
10165 //var cid = cm.getColumnId(i); << not used in this version?
10166 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10168 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10169 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10170 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10172 //this.updateSplitters();
10173 //this.layout(); << ??
10174 this.fireEvent("columnresize", i, w);
10176 onHeaderChange : function()
10178 var header = this.renderHeader();
10179 var table = this.el.select('table', true).first();
10181 this.headEl.remove();
10182 this.headEl = table.createChild(header, this.bodyEl, false);
10184 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10185 e.on('click', this.sort, this);
10188 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10189 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10194 onHiddenChange : function(colModel, colIndex, hidden)
10197 this.cm.setHidden()
10198 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10199 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10201 this.CSS.updateRule(thSelector, "display", "");
10202 this.CSS.updateRule(tdSelector, "display", "");
10205 this.CSS.updateRule(thSelector, "display", "none");
10206 this.CSS.updateRule(tdSelector, "display", "none");
10209 // onload calls initCSS()
10210 this.onHeaderChange();
10214 setColumnWidth: function(col_index, width)
10216 // width = "md-2 xs-2..."
10217 if(!this.colModel.config[col_index]) {
10221 var w = width.split(" ");
10223 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10225 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10228 for(var j = 0; j < w.length; j++) {
10234 var size_cls = w[j].split("-");
10236 if(!Number.isInteger(size_cls[1] * 1)) {
10240 if(!this.colModel.config[col_index][size_cls[0]]) {
10244 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10248 h_row[0].classList.replace(
10249 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10250 "col-"+size_cls[0]+"-"+size_cls[1]
10253 for(var i = 0; i < rows.length; i++) {
10255 var size_cls = w[j].split("-");
10257 if(!Number.isInteger(size_cls[1] * 1)) {
10261 if(!this.colModel.config[col_index][size_cls[0]]) {
10265 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10269 rows[i].classList.replace(
10270 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10271 "col-"+size_cls[0]+"-"+size_cls[1]
10275 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10280 // currently only used to find the split on drag..
10281 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10286 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10287 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10296 * @class Roo.bootstrap.TableCell
10297 * @extends Roo.bootstrap.Component
10298 * Bootstrap TableCell class
10299 * @cfg {String} html cell contain text
10300 * @cfg {String} cls cell class
10301 * @cfg {String} tag cell tag (td|th) default td
10302 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10303 * @cfg {String} align Aligns the content in a cell
10304 * @cfg {String} axis Categorizes cells
10305 * @cfg {String} bgcolor Specifies the background color of a cell
10306 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10307 * @cfg {Number} colspan Specifies the number of columns a cell should span
10308 * @cfg {String} headers Specifies one or more header cells a cell is related to
10309 * @cfg {Number} height Sets the height of a cell
10310 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10311 * @cfg {Number} rowspan Sets the number of rows a cell should span
10312 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10313 * @cfg {String} valign Vertical aligns the content in a cell
10314 * @cfg {Number} width Specifies the width of a cell
10317 * Create a new TableCell
10318 * @param {Object} config The config object
10321 Roo.bootstrap.TableCell = function(config){
10322 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10325 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10345 getAutoCreate : function(){
10346 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10353 cfg.tag = this.tag;
10366 cfg.align=this.align
10371 if (this.bgcolor) {
10372 cfg.bgcolor=this.bgcolor
10374 if (this.charoff) {
10375 cfg.charoff=this.charoff
10377 if (this.colspan) {
10378 cfg.colspan=this.colspan
10380 if (this.headers) {
10381 cfg.headers=this.headers
10384 cfg.height=this.height
10387 cfg.nowrap=this.nowrap
10389 if (this.rowspan) {
10390 cfg.rowspan=this.rowspan
10393 cfg.scope=this.scope
10396 cfg.valign=this.valign
10399 cfg.width=this.width
10418 * @class Roo.bootstrap.TableRow
10419 * @extends Roo.bootstrap.Component
10420 * Bootstrap TableRow class
10421 * @cfg {String} cls row class
10422 * @cfg {String} align Aligns the content in a table row
10423 * @cfg {String} bgcolor Specifies a background color for a table row
10424 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10425 * @cfg {String} valign Vertical aligns the content in a table row
10428 * Create a new TableRow
10429 * @param {Object} config The config object
10432 Roo.bootstrap.TableRow = function(config){
10433 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10436 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10444 getAutoCreate : function(){
10445 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10452 cfg.cls = this.cls;
10455 cfg.align = this.align;
10458 cfg.bgcolor = this.bgcolor;
10461 cfg.charoff = this.charoff;
10464 cfg.valign = this.valign;
10482 * @class Roo.bootstrap.TableBody
10483 * @extends Roo.bootstrap.Component
10484 * Bootstrap TableBody class
10485 * @cfg {String} cls element class
10486 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10487 * @cfg {String} align Aligns the content inside the element
10488 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10489 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10492 * Create a new TableBody
10493 * @param {Object} config The config object
10496 Roo.bootstrap.TableBody = function(config){
10497 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10500 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10508 getAutoCreate : function(){
10509 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10519 cfg.tag = this.tag;
10523 cfg.align = this.align;
10526 cfg.charoff = this.charoff;
10529 cfg.valign = this.valign;
10536 // initEvents : function()
10539 // if(!this.store){
10543 // this.store = Roo.factory(this.store, Roo.data);
10544 // this.store.on('load', this.onLoad, this);
10546 // this.store.load();
10550 // onLoad: function ()
10552 // this.fireEvent('load', this);
10562 * Ext JS Library 1.1.1
10563 * Copyright(c) 2006-2007, Ext JS, LLC.
10565 * Originally Released Under LGPL - original licence link has changed is not relivant.
10568 * <script type="text/javascript">
10571 // as we use this in bootstrap.
10572 Roo.namespace('Roo.form');
10574 * @class Roo.form.Action
10575 * Internal Class used to handle form actions
10577 * @param {Roo.form.BasicForm} el The form element or its id
10578 * @param {Object} config Configuration options
10583 // define the action interface
10584 Roo.form.Action = function(form, options){
10586 this.options = options || {};
10589 * Client Validation Failed
10592 Roo.form.Action.CLIENT_INVALID = 'client';
10594 * Server Validation Failed
10597 Roo.form.Action.SERVER_INVALID = 'server';
10599 * Connect to Server Failed
10602 Roo.form.Action.CONNECT_FAILURE = 'connect';
10604 * Reading Data from Server Failed
10607 Roo.form.Action.LOAD_FAILURE = 'load';
10609 Roo.form.Action.prototype = {
10611 failureType : undefined,
10612 response : undefined,
10613 result : undefined,
10615 // interface method
10616 run : function(options){
10620 // interface method
10621 success : function(response){
10625 // interface method
10626 handleResponse : function(response){
10630 // default connection failure
10631 failure : function(response){
10633 this.response = response;
10634 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10635 this.form.afterAction(this, false);
10638 processResponse : function(response){
10639 this.response = response;
10640 if(!response.responseText){
10643 this.result = this.handleResponse(response);
10644 return this.result;
10647 // utility functions used internally
10648 getUrl : function(appendParams){
10649 var url = this.options.url || this.form.url || this.form.el.dom.action;
10651 var p = this.getParams();
10653 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10659 getMethod : function(){
10660 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10663 getParams : function(){
10664 var bp = this.form.baseParams;
10665 var p = this.options.params;
10667 if(typeof p == "object"){
10668 p = Roo.urlEncode(Roo.applyIf(p, bp));
10669 }else if(typeof p == 'string' && bp){
10670 p += '&' + Roo.urlEncode(bp);
10673 p = Roo.urlEncode(bp);
10678 createCallback : function(){
10680 success: this.success,
10681 failure: this.failure,
10683 timeout: (this.form.timeout*1000),
10684 upload: this.form.fileUpload ? this.success : undefined
10689 Roo.form.Action.Submit = function(form, options){
10690 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10693 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10696 haveProgress : false,
10697 uploadComplete : false,
10699 // uploadProgress indicator.
10700 uploadProgress : function()
10702 if (!this.form.progressUrl) {
10706 if (!this.haveProgress) {
10707 Roo.MessageBox.progress("Uploading", "Uploading");
10709 if (this.uploadComplete) {
10710 Roo.MessageBox.hide();
10714 this.haveProgress = true;
10716 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10718 var c = new Roo.data.Connection();
10720 url : this.form.progressUrl,
10725 success : function(req){
10726 //console.log(data);
10730 rdata = Roo.decode(req.responseText)
10732 Roo.log("Invalid data from server..");
10736 if (!rdata || !rdata.success) {
10738 Roo.MessageBox.alert(Roo.encode(rdata));
10741 var data = rdata.data;
10743 if (this.uploadComplete) {
10744 Roo.MessageBox.hide();
10749 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10750 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10753 this.uploadProgress.defer(2000,this);
10756 failure: function(data) {
10757 Roo.log('progress url failed ');
10768 // run get Values on the form, so it syncs any secondary forms.
10769 this.form.getValues();
10771 var o = this.options;
10772 var method = this.getMethod();
10773 var isPost = method == 'POST';
10774 if(o.clientValidation === false || this.form.isValid()){
10776 if (this.form.progressUrl) {
10777 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10778 (new Date() * 1) + '' + Math.random());
10783 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10784 form:this.form.el.dom,
10785 url:this.getUrl(!isPost),
10787 params:isPost ? this.getParams() : null,
10788 isUpload: this.form.fileUpload,
10789 formData : this.form.formData
10792 this.uploadProgress();
10794 }else if (o.clientValidation !== false){ // client validation failed
10795 this.failureType = Roo.form.Action.CLIENT_INVALID;
10796 this.form.afterAction(this, false);
10800 success : function(response)
10802 this.uploadComplete= true;
10803 if (this.haveProgress) {
10804 Roo.MessageBox.hide();
10808 var result = this.processResponse(response);
10809 if(result === true || result.success){
10810 this.form.afterAction(this, true);
10814 this.form.markInvalid(result.errors);
10815 this.failureType = Roo.form.Action.SERVER_INVALID;
10817 this.form.afterAction(this, false);
10819 failure : function(response)
10821 this.uploadComplete= true;
10822 if (this.haveProgress) {
10823 Roo.MessageBox.hide();
10826 this.response = response;
10827 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10828 this.form.afterAction(this, false);
10831 handleResponse : function(response){
10832 if(this.form.errorReader){
10833 var rs = this.form.errorReader.read(response);
10836 for(var i = 0, len = rs.records.length; i < len; i++) {
10837 var r = rs.records[i];
10838 errors[i] = r.data;
10841 if(errors.length < 1){
10845 success : rs.success,
10851 ret = Roo.decode(response.responseText);
10855 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10865 Roo.form.Action.Load = function(form, options){
10866 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10867 this.reader = this.form.reader;
10870 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10875 Roo.Ajax.request(Roo.apply(
10876 this.createCallback(), {
10877 method:this.getMethod(),
10878 url:this.getUrl(false),
10879 params:this.getParams()
10883 success : function(response){
10885 var result = this.processResponse(response);
10886 if(result === true || !result.success || !result.data){
10887 this.failureType = Roo.form.Action.LOAD_FAILURE;
10888 this.form.afterAction(this, false);
10891 this.form.clearInvalid();
10892 this.form.setValues(result.data);
10893 this.form.afterAction(this, true);
10896 handleResponse : function(response){
10897 if(this.form.reader){
10898 var rs = this.form.reader.read(response);
10899 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10901 success : rs.success,
10905 return Roo.decode(response.responseText);
10909 Roo.form.Action.ACTION_TYPES = {
10910 'load' : Roo.form.Action.Load,
10911 'submit' : Roo.form.Action.Submit
10920 * @class Roo.bootstrap.Form
10921 * @extends Roo.bootstrap.Component
10922 * @children Roo.bootstrap.Component
10923 * Bootstrap Form class
10924 * @cfg {String} method GET | POST (default POST)
10925 * @cfg {String} labelAlign top | left (default top)
10926 * @cfg {String} align left | right - for navbars
10927 * @cfg {Boolean} loadMask load mask when submit (default true)
10931 * Create a new Form
10932 * @param {Object} config The config object
10936 Roo.bootstrap.Form = function(config){
10938 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10940 Roo.bootstrap.Form.popover.apply();
10944 * @event clientvalidation
10945 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10946 * @param {Form} this
10947 * @param {Boolean} valid true if the form has passed client-side validation
10949 clientvalidation: true,
10951 * @event beforeaction
10952 * Fires before any action is performed. Return false to cancel the action.
10953 * @param {Form} this
10954 * @param {Action} action The action to be performed
10956 beforeaction: true,
10958 * @event actionfailed
10959 * Fires when an action fails.
10960 * @param {Form} this
10961 * @param {Action} action The action that failed
10963 actionfailed : true,
10965 * @event actioncomplete
10966 * Fires when an action is completed.
10967 * @param {Form} this
10968 * @param {Action} action The action that completed
10970 actioncomplete : true
10974 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10977 * @cfg {String} method
10978 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10982 * @cfg {String} url
10983 * The URL to use for form actions if one isn't supplied in the action options.
10986 * @cfg {Boolean} fileUpload
10987 * Set to true if this form is a file upload.
10991 * @cfg {Object} baseParams
10992 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10996 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11000 * @cfg {Sting} align (left|right) for navbar forms
11005 activeAction : null,
11008 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11009 * element by passing it or its id or mask the form itself by passing in true.
11012 waitMsgTarget : false,
11017 * @cfg {Boolean} errorMask (true|false) default false
11022 * @cfg {Number} maskOffset Default 100
11027 * @cfg {Boolean} maskBody
11031 getAutoCreate : function(){
11035 method : this.method || 'POST',
11036 id : this.id || Roo.id(),
11039 if (this.parent().xtype.match(/^Nav/)) {
11040 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11044 if (this.labelAlign == 'left' ) {
11045 cfg.cls += ' form-horizontal';
11051 initEvents : function()
11053 this.el.on('submit', this.onSubmit, this);
11054 // this was added as random key presses on the form where triggering form submit.
11055 this.el.on('keypress', function(e) {
11056 if (e.getCharCode() != 13) {
11059 // we might need to allow it for textareas.. and some other items.
11060 // check e.getTarget().
11062 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11066 Roo.log("keypress blocked");
11068 e.preventDefault();
11074 onSubmit : function(e){
11079 * Returns true if client-side validation on the form is successful.
11082 isValid : function(){
11083 var items = this.getItems();
11085 var target = false;
11087 items.each(function(f){
11093 Roo.log('invalid field: ' + f.name);
11097 if(!target && f.el.isVisible(true)){
11103 if(this.errorMask && !valid){
11104 Roo.bootstrap.Form.popover.mask(this, target);
11111 * Returns true if any fields in this form have changed since their original load.
11114 isDirty : function(){
11116 var items = this.getItems();
11117 items.each(function(f){
11127 * Performs a predefined action (submit or load) or custom actions you define on this form.
11128 * @param {String} actionName The name of the action type
11129 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11130 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11131 * accept other config options):
11133 Property Type Description
11134 ---------------- --------------- ----------------------------------------------------------------------------------
11135 url String The url for the action (defaults to the form's url)
11136 method String The form method to use (defaults to the form's method, or POST if not defined)
11137 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11138 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11139 validate the form on the client (defaults to false)
11141 * @return {BasicForm} this
11143 doAction : function(action, options){
11144 if(typeof action == 'string'){
11145 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11147 if(this.fireEvent('beforeaction', this, action) !== false){
11148 this.beforeAction(action);
11149 action.run.defer(100, action);
11155 beforeAction : function(action){
11156 var o = action.options;
11161 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11163 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11166 // not really supported yet.. ??
11168 //if(this.waitMsgTarget === true){
11169 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11170 //}else if(this.waitMsgTarget){
11171 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11172 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11174 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11180 afterAction : function(action, success){
11181 this.activeAction = null;
11182 var o = action.options;
11187 Roo.get(document.body).unmask();
11193 //if(this.waitMsgTarget === true){
11194 // this.el.unmask();
11195 //}else if(this.waitMsgTarget){
11196 // this.waitMsgTarget.unmask();
11198 // Roo.MessageBox.updateProgress(1);
11199 // Roo.MessageBox.hide();
11206 Roo.callback(o.success, o.scope, [this, action]);
11207 this.fireEvent('actioncomplete', this, action);
11211 // failure condition..
11212 // we have a scenario where updates need confirming.
11213 // eg. if a locking scenario exists..
11214 // we look for { errors : { needs_confirm : true }} in the response.
11216 (typeof(action.result) != 'undefined') &&
11217 (typeof(action.result.errors) != 'undefined') &&
11218 (typeof(action.result.errors.needs_confirm) != 'undefined')
11221 Roo.log("not supported yet");
11224 Roo.MessageBox.confirm(
11225 "Change requires confirmation",
11226 action.result.errorMsg,
11231 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11241 Roo.callback(o.failure, o.scope, [this, action]);
11242 // show an error message if no failed handler is set..
11243 if (!this.hasListener('actionfailed')) {
11244 Roo.log("need to add dialog support");
11246 Roo.MessageBox.alert("Error",
11247 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11248 action.result.errorMsg :
11249 "Saving Failed, please check your entries or try again"
11254 this.fireEvent('actionfailed', this, action);
11259 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11260 * @param {String} id The value to search for
11263 findField : function(id){
11264 var items = this.getItems();
11265 var field = items.get(id);
11267 items.each(function(f){
11268 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11275 return field || null;
11278 * Mark fields in this form invalid in bulk.
11279 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11280 * @return {BasicForm} this
11282 markInvalid : function(errors){
11283 if(errors instanceof Array){
11284 for(var i = 0, len = errors.length; i < len; i++){
11285 var fieldError = errors[i];
11286 var f = this.findField(fieldError.id);
11288 f.markInvalid(fieldError.msg);
11294 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11295 field.markInvalid(errors[id]);
11299 //Roo.each(this.childForms || [], function (f) {
11300 // f.markInvalid(errors);
11307 * Set values for fields in this form in bulk.
11308 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11309 * @return {BasicForm} this
11311 setValues : function(values){
11312 if(values instanceof Array){ // array of objects
11313 for(var i = 0, len = values.length; i < len; i++){
11315 var f = this.findField(v.id);
11317 f.setValue(v.value);
11318 if(this.trackResetOnLoad){
11319 f.originalValue = f.getValue();
11323 }else{ // object hash
11326 if(typeof values[id] != 'function' && (field = this.findField(id))){
11328 if (field.setFromData &&
11329 field.valueField &&
11330 field.displayField &&
11331 // combos' with local stores can
11332 // be queried via setValue()
11333 // to set their value..
11334 (field.store && !field.store.isLocal)
11338 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11339 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11340 field.setFromData(sd);
11342 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11344 field.setFromData(values);
11347 field.setValue(values[id]);
11351 if(this.trackResetOnLoad){
11352 field.originalValue = field.getValue();
11358 //Roo.each(this.childForms || [], function (f) {
11359 // f.setValues(values);
11366 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11367 * they are returned as an array.
11368 * @param {Boolean} asString
11371 getValues : function(asString){
11372 //if (this.childForms) {
11373 // copy values from the child forms
11374 // Roo.each(this.childForms, function (f) {
11375 // this.setValues(f.getValues());
11381 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11382 if(asString === true){
11385 return Roo.urlDecode(fs);
11389 * Returns the fields in this form as an object with key/value pairs.
11390 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11393 getFieldValues : function(with_hidden)
11395 var items = this.getItems();
11397 items.each(function(f){
11399 if (!f.getName()) {
11403 var v = f.getValue();
11405 if (f.inputType =='radio') {
11406 if (typeof(ret[f.getName()]) == 'undefined') {
11407 ret[f.getName()] = ''; // empty..
11410 if (!f.el.dom.checked) {
11414 v = f.el.dom.value;
11418 if(f.xtype == 'MoneyField'){
11419 ret[f.currencyName] = f.getCurrency();
11422 // not sure if this supported any more..
11423 if ((typeof(v) == 'object') && f.getRawValue) {
11424 v = f.getRawValue() ; // dates..
11426 // combo boxes where name != hiddenName...
11427 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11428 ret[f.name] = f.getRawValue();
11430 ret[f.getName()] = v;
11437 * Clears all invalid messages in this form.
11438 * @return {BasicForm} this
11440 clearInvalid : function(){
11441 var items = this.getItems();
11443 items.each(function(f){
11451 * Resets this form.
11452 * @return {BasicForm} this
11454 reset : function(){
11455 var items = this.getItems();
11456 items.each(function(f){
11460 Roo.each(this.childForms || [], function (f) {
11468 getItems : function()
11470 var r=new Roo.util.MixedCollection(false, function(o){
11471 return o.id || (o.id = Roo.id());
11473 var iter = function(el) {
11480 Roo.each(el.items,function(e) {
11489 hideFields : function(items)
11491 Roo.each(items, function(i){
11493 var f = this.findField(i);
11504 showFields : function(items)
11506 Roo.each(items, function(i){
11508 var f = this.findField(i);
11521 Roo.apply(Roo.bootstrap.Form, {
11537 intervalID : false,
11543 if(this.isApplied){
11548 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11549 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11550 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11551 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11554 this.maskEl.top.enableDisplayMode("block");
11555 this.maskEl.left.enableDisplayMode("block");
11556 this.maskEl.bottom.enableDisplayMode("block");
11557 this.maskEl.right.enableDisplayMode("block");
11559 this.toolTip = new Roo.bootstrap.Tooltip({
11560 cls : 'roo-form-error-popover',
11562 'left' : ['r-l', [-2,0], 'right'],
11563 'right' : ['l-r', [2,0], 'left'],
11564 'bottom' : ['tl-bl', [0,2], 'top'],
11565 'top' : [ 'bl-tl', [0,-2], 'bottom']
11569 this.toolTip.render(Roo.get(document.body));
11571 this.toolTip.el.enableDisplayMode("block");
11573 Roo.get(document.body).on('click', function(){
11577 Roo.get(document.body).on('touchstart', function(){
11581 this.isApplied = true
11584 mask : function(form, target)
11588 this.target = target;
11590 if(!this.form.errorMask || !target.el){
11594 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11596 Roo.log(scrollable);
11598 var ot = this.target.el.calcOffsetsTo(scrollable);
11600 var scrollTo = ot[1] - this.form.maskOffset;
11602 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11604 scrollable.scrollTo('top', scrollTo);
11606 var box = this.target.el.getBox();
11608 var zIndex = Roo.bootstrap.Modal.zIndex++;
11611 this.maskEl.top.setStyle('position', 'absolute');
11612 this.maskEl.top.setStyle('z-index', zIndex);
11613 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11614 this.maskEl.top.setLeft(0);
11615 this.maskEl.top.setTop(0);
11616 this.maskEl.top.show();
11618 this.maskEl.left.setStyle('position', 'absolute');
11619 this.maskEl.left.setStyle('z-index', zIndex);
11620 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11621 this.maskEl.left.setLeft(0);
11622 this.maskEl.left.setTop(box.y - this.padding);
11623 this.maskEl.left.show();
11625 this.maskEl.bottom.setStyle('position', 'absolute');
11626 this.maskEl.bottom.setStyle('z-index', zIndex);
11627 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11628 this.maskEl.bottom.setLeft(0);
11629 this.maskEl.bottom.setTop(box.bottom + this.padding);
11630 this.maskEl.bottom.show();
11632 this.maskEl.right.setStyle('position', 'absolute');
11633 this.maskEl.right.setStyle('z-index', zIndex);
11634 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11635 this.maskEl.right.setLeft(box.right + this.padding);
11636 this.maskEl.right.setTop(box.y - this.padding);
11637 this.maskEl.right.show();
11639 this.toolTip.bindEl = this.target.el;
11641 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11643 var tip = this.target.blankText;
11645 if(this.target.getValue() !== '' ) {
11647 if (this.target.invalidText.length) {
11648 tip = this.target.invalidText;
11649 } else if (this.target.regexText.length){
11650 tip = this.target.regexText;
11654 this.toolTip.show(tip);
11656 this.intervalID = window.setInterval(function() {
11657 Roo.bootstrap.Form.popover.unmask();
11660 window.onwheel = function(){ return false;};
11662 (function(){ this.isMasked = true; }).defer(500, this);
11666 unmask : function()
11668 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11672 this.maskEl.top.setStyle('position', 'absolute');
11673 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11674 this.maskEl.top.hide();
11676 this.maskEl.left.setStyle('position', 'absolute');
11677 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11678 this.maskEl.left.hide();
11680 this.maskEl.bottom.setStyle('position', 'absolute');
11681 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11682 this.maskEl.bottom.hide();
11684 this.maskEl.right.setStyle('position', 'absolute');
11685 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11686 this.maskEl.right.hide();
11688 this.toolTip.hide();
11690 this.toolTip.el.hide();
11692 window.onwheel = function(){ return true;};
11694 if(this.intervalID){
11695 window.clearInterval(this.intervalID);
11696 this.intervalID = false;
11699 this.isMasked = false;
11709 * Ext JS Library 1.1.1
11710 * Copyright(c) 2006-2007, Ext JS, LLC.
11712 * Originally Released Under LGPL - original licence link has changed is not relivant.
11715 * <script type="text/javascript">
11718 * @class Roo.form.VTypes
11719 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11722 Roo.form.VTypes = function(){
11723 // closure these in so they are only created once.
11724 var alpha = /^[a-zA-Z_]+$/;
11725 var alphanum = /^[a-zA-Z0-9_]+$/;
11726 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11727 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11729 // All these messages and functions are configurable
11732 * The function used to validate email addresses
11733 * @param {String} value The email address
11735 'email' : function(v){
11736 return email.test(v);
11739 * The error text to display when the email validation function returns false
11742 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11744 * The keystroke filter mask to be applied on email input
11747 'emailMask' : /[a-z0-9_\.\-@]/i,
11750 * The function used to validate URLs
11751 * @param {String} value The URL
11753 'url' : function(v){
11754 return url.test(v);
11757 * The error text to display when the url validation function returns false
11760 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11763 * The function used to validate alpha values
11764 * @param {String} value The value
11766 'alpha' : function(v){
11767 return alpha.test(v);
11770 * The error text to display when the alpha validation function returns false
11773 'alphaText' : 'This field should only contain letters and _',
11775 * The keystroke filter mask to be applied on alpha input
11778 'alphaMask' : /[a-z_]/i,
11781 * The function used to validate alphanumeric values
11782 * @param {String} value The value
11784 'alphanum' : function(v){
11785 return alphanum.test(v);
11788 * The error text to display when the alphanumeric validation function returns false
11791 'alphanumText' : 'This field should only contain letters, numbers and _',
11793 * The keystroke filter mask to be applied on alphanumeric input
11796 'alphanumMask' : /[a-z0-9_]/i
11806 * @class Roo.bootstrap.Input
11807 * @extends Roo.bootstrap.Component
11808 * Bootstrap Input class
11809 * @cfg {Boolean} disabled is it disabled
11810 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11811 * @cfg {String} name name of the input
11812 * @cfg {string} fieldLabel - the label associated
11813 * @cfg {string} placeholder - placeholder to put in text.
11814 * @cfg {string} before - input group add on before
11815 * @cfg {string} after - input group add on after
11816 * @cfg {string} size - (lg|sm) or leave empty..
11817 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11818 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11819 * @cfg {Number} md colspan out of 12 for computer-sized screens
11820 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11821 * @cfg {string} value default value of the input
11822 * @cfg {Number} labelWidth set the width of label
11823 * @cfg {Number} labellg set the width of label (1-12)
11824 * @cfg {Number} labelmd set the width of label (1-12)
11825 * @cfg {Number} labelsm set the width of label (1-12)
11826 * @cfg {Number} labelxs set the width of label (1-12)
11827 * @cfg {String} labelAlign (top|left)
11828 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11829 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11830 * @cfg {String} indicatorpos (left|right) default left
11831 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11832 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11833 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11835 * @cfg {String} align (left|center|right) Default left
11836 * @cfg {Boolean} forceFeedback (true|false) Default false
11839 * Create a new Input
11840 * @param {Object} config The config object
11843 Roo.bootstrap.Input = function(config){
11845 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11850 * Fires when this field receives input focus.
11851 * @param {Roo.form.Field} this
11856 * Fires when this field loses input focus.
11857 * @param {Roo.form.Field} this
11861 * @event specialkey
11862 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11863 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11864 * @param {Roo.form.Field} this
11865 * @param {Roo.EventObject} e The event object
11870 * Fires just before the field blurs if the field value has changed.
11871 * @param {Roo.form.Field} this
11872 * @param {Mixed} newValue The new value
11873 * @param {Mixed} oldValue The original value
11878 * Fires after the field has been marked as invalid.
11879 * @param {Roo.form.Field} this
11880 * @param {String} msg The validation message
11885 * Fires after the field has been validated with no errors.
11886 * @param {Roo.form.Field} this
11891 * Fires after the key up
11892 * @param {Roo.form.Field} this
11893 * @param {Roo.EventObject} e The event Object
11898 * Fires after the user pastes into input
11899 * @param {Roo.form.Field} this
11900 * @param {Roo.EventObject} e The event Object
11906 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11908 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11909 automatic validation (defaults to "keyup").
11911 validationEvent : "keyup",
11913 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11915 validateOnBlur : true,
11917 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11919 validationDelay : 250,
11921 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11923 focusClass : "x-form-focus", // not needed???
11927 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11929 invalidClass : "has-warning",
11932 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11934 validClass : "has-success",
11937 * @cfg {Boolean} hasFeedback (true|false) default true
11939 hasFeedback : true,
11942 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11944 invalidFeedbackClass : "glyphicon-warning-sign",
11947 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11949 validFeedbackClass : "glyphicon-ok",
11952 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11954 selectOnFocus : false,
11957 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11961 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11966 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11968 disableKeyFilter : false,
11971 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11975 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11979 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11981 blankText : "Please complete this mandatory field",
11984 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11988 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11990 maxLength : Number.MAX_VALUE,
11992 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11994 minLengthText : "The minimum length for this field is {0}",
11996 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11998 maxLengthText : "The maximum length for this field is {0}",
12002 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12003 * If available, this function will be called only after the basic validators all return true, and will be passed the
12004 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12008 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12009 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12010 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12014 * @cfg {String} regexText -- Depricated - use Invalid Text
12019 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12025 autocomplete: false,
12029 inputType : 'text',
12032 placeholder: false,
12037 preventMark: false,
12038 isFormField : true,
12041 labelAlign : false,
12044 formatedValue : false,
12045 forceFeedback : false,
12047 indicatorpos : 'left',
12057 parentLabelAlign : function()
12060 while (parent.parent()) {
12061 parent = parent.parent();
12062 if (typeof(parent.labelAlign) !='undefined') {
12063 return parent.labelAlign;
12070 getAutoCreate : function()
12072 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12078 if(this.inputType != 'hidden'){
12079 cfg.cls = 'form-group' //input-group
12085 type : this.inputType,
12086 value : this.value,
12087 cls : 'form-control',
12088 placeholder : this.placeholder || '',
12089 autocomplete : this.autocomplete || 'new-password'
12091 if (this.inputType == 'file') {
12092 input.style = 'overflow:hidden'; // why not in CSS?
12095 if(this.capture.length){
12096 input.capture = this.capture;
12099 if(this.accept.length){
12100 input.accept = this.accept + "/*";
12104 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12107 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12108 input.maxLength = this.maxLength;
12111 if (this.disabled) {
12112 input.disabled=true;
12115 if (this.readOnly) {
12116 input.readonly=true;
12120 input.name = this.name;
12124 input.cls += ' input-' + this.size;
12128 ['xs','sm','md','lg'].map(function(size){
12129 if (settings[size]) {
12130 cfg.cls += ' col-' + size + '-' + settings[size];
12134 var inputblock = input;
12138 cls: 'glyphicon form-control-feedback'
12141 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12144 cls : 'has-feedback',
12152 if (this.before || this.after) {
12155 cls : 'input-group',
12159 if (this.before && typeof(this.before) == 'string') {
12161 inputblock.cn.push({
12163 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12167 if (this.before && typeof(this.before) == 'object') {
12168 this.before = Roo.factory(this.before);
12170 inputblock.cn.push({
12172 cls : 'roo-input-before input-group-prepend input-group-' +
12173 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12177 inputblock.cn.push(input);
12179 if (this.after && typeof(this.after) == 'string') {
12180 inputblock.cn.push({
12182 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12186 if (this.after && typeof(this.after) == 'object') {
12187 this.after = Roo.factory(this.after);
12189 inputblock.cn.push({
12191 cls : 'roo-input-after input-group-append input-group-' +
12192 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12196 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12197 inputblock.cls += ' has-feedback';
12198 inputblock.cn.push(feedback);
12203 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12204 tooltip : 'This field is required'
12206 if (this.allowBlank ) {
12207 indicator.style = this.allowBlank ? ' display:none' : '';
12209 if (align ==='left' && this.fieldLabel.length) {
12211 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12218 cls : 'control-label col-form-label',
12219 html : this.fieldLabel
12230 var labelCfg = cfg.cn[1];
12231 var contentCfg = cfg.cn[2];
12233 if(this.indicatorpos == 'right'){
12238 cls : 'control-label col-form-label',
12242 html : this.fieldLabel
12256 labelCfg = cfg.cn[0];
12257 contentCfg = cfg.cn[1];
12261 if(this.labelWidth > 12){
12262 labelCfg.style = "width: " + this.labelWidth + 'px';
12265 if(this.labelWidth < 13 && this.labelmd == 0){
12266 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12269 if(this.labellg > 0){
12270 labelCfg.cls += ' col-lg-' + this.labellg;
12271 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12274 if(this.labelmd > 0){
12275 labelCfg.cls += ' col-md-' + this.labelmd;
12276 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12279 if(this.labelsm > 0){
12280 labelCfg.cls += ' col-sm-' + this.labelsm;
12281 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12284 if(this.labelxs > 0){
12285 labelCfg.cls += ' col-xs-' + this.labelxs;
12286 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12290 } else if ( this.fieldLabel.length) {
12297 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12298 tooltip : 'This field is required',
12299 style : this.allowBlank ? ' display:none' : ''
12303 //cls : 'input-group-addon',
12304 html : this.fieldLabel
12312 if(this.indicatorpos == 'right'){
12317 //cls : 'input-group-addon',
12318 html : this.fieldLabel
12323 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12324 tooltip : 'This field is required',
12325 style : this.allowBlank ? ' display:none' : ''
12345 if (this.parentType === 'Navbar' && this.parent().bar) {
12346 cfg.cls += ' navbar-form';
12349 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12350 // on BS4 we do this only if not form
12351 cfg.cls += ' navbar-form';
12359 * return the real input element.
12361 inputEl: function ()
12363 return this.el.select('input.form-control',true).first();
12366 tooltipEl : function()
12368 return this.inputEl();
12371 indicatorEl : function()
12373 if (Roo.bootstrap.version == 4) {
12374 return false; // not enabled in v4 yet.
12377 var indicator = this.el.select('i.roo-required-indicator',true).first();
12387 setDisabled : function(v)
12389 var i = this.inputEl().dom;
12391 i.removeAttribute('disabled');
12395 i.setAttribute('disabled','true');
12397 initEvents : function()
12400 this.inputEl().on("keydown" , this.fireKey, this);
12401 this.inputEl().on("focus", this.onFocus, this);
12402 this.inputEl().on("blur", this.onBlur, this);
12404 this.inputEl().relayEvent('keyup', this);
12405 this.inputEl().relayEvent('paste', this);
12407 this.indicator = this.indicatorEl();
12409 if(this.indicator){
12410 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12413 // reference to original value for reset
12414 this.originalValue = this.getValue();
12415 //Roo.form.TextField.superclass.initEvents.call(this);
12416 if(this.validationEvent == 'keyup'){
12417 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12418 this.inputEl().on('keyup', this.filterValidation, this);
12420 else if(this.validationEvent !== false){
12421 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12424 if(this.selectOnFocus){
12425 this.on("focus", this.preFocus, this);
12428 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12429 this.inputEl().on("keypress", this.filterKeys, this);
12431 this.inputEl().relayEvent('keypress', this);
12434 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12435 this.el.on("click", this.autoSize, this);
12438 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12439 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12442 if (typeof(this.before) == 'object') {
12443 this.before.render(this.el.select('.roo-input-before',true).first());
12445 if (typeof(this.after) == 'object') {
12446 this.after.render(this.el.select('.roo-input-after',true).first());
12449 this.inputEl().on('change', this.onChange, this);
12452 filterValidation : function(e){
12453 if(!e.isNavKeyPress()){
12454 this.validationTask.delay(this.validationDelay);
12458 * Validates the field value
12459 * @return {Boolean} True if the value is valid, else false
12461 validate : function(){
12462 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12463 if(this.disabled || this.validateValue(this.getRawValue())){
12468 this.markInvalid();
12474 * Validates a value according to the field's validation rules and marks the field as invalid
12475 * if the validation fails
12476 * @param {Mixed} value The value to validate
12477 * @return {Boolean} True if the value is valid, else false
12479 validateValue : function(value)
12481 if(this.getVisibilityEl().hasClass('hidden')){
12485 if(value.length < 1) { // if it's blank
12486 if(this.allowBlank){
12492 if(value.length < this.minLength){
12495 if(value.length > this.maxLength){
12499 var vt = Roo.form.VTypes;
12500 if(!vt[this.vtype](value, this)){
12504 if(typeof this.validator == "function"){
12505 var msg = this.validator(value);
12509 if (typeof(msg) == 'string') {
12510 this.invalidText = msg;
12514 if(this.regex && !this.regex.test(value)){
12522 fireKey : function(e){
12523 //Roo.log('field ' + e.getKey());
12524 if(e.isNavKeyPress()){
12525 this.fireEvent("specialkey", this, e);
12528 focus : function (selectText){
12530 this.inputEl().focus();
12531 if(selectText === true){
12532 this.inputEl().dom.select();
12538 onFocus : function(){
12539 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12540 // this.el.addClass(this.focusClass);
12542 if(!this.hasFocus){
12543 this.hasFocus = true;
12544 this.startValue = this.getValue();
12545 this.fireEvent("focus", this);
12549 beforeBlur : Roo.emptyFn,
12553 onBlur : function(){
12555 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12556 //this.el.removeClass(this.focusClass);
12558 this.hasFocus = false;
12559 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12562 var v = this.getValue();
12563 if(String(v) !== String(this.startValue)){
12564 this.fireEvent('change', this, v, this.startValue);
12566 this.fireEvent("blur", this);
12569 onChange : function(e)
12571 var v = this.getValue();
12572 if(String(v) !== String(this.startValue)){
12573 this.fireEvent('change', this, v, this.startValue);
12579 * Resets the current field value to the originally loaded value and clears any validation messages
12581 reset : function(){
12582 this.setValue(this.originalValue);
12586 * Returns the name of the field
12587 * @return {Mixed} name The name field
12589 getName: function(){
12593 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12594 * @return {Mixed} value The field value
12596 getValue : function(){
12598 var v = this.inputEl().getValue();
12603 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12604 * @return {Mixed} value The field value
12606 getRawValue : function(){
12607 var v = this.inputEl().getValue();
12613 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12614 * @param {Mixed} value The value to set
12616 setRawValue : function(v){
12617 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12620 selectText : function(start, end){
12621 var v = this.getRawValue();
12623 start = start === undefined ? 0 : start;
12624 end = end === undefined ? v.length : end;
12625 var d = this.inputEl().dom;
12626 if(d.setSelectionRange){
12627 d.setSelectionRange(start, end);
12628 }else if(d.createTextRange){
12629 var range = d.createTextRange();
12630 range.moveStart("character", start);
12631 range.moveEnd("character", v.length-end);
12638 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12639 * @param {Mixed} value The value to set
12641 setValue : function(v){
12644 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12650 processValue : function(value){
12651 if(this.stripCharsRe){
12652 var newValue = value.replace(this.stripCharsRe, '');
12653 if(newValue !== value){
12654 this.setRawValue(newValue);
12661 preFocus : function(){
12663 if(this.selectOnFocus){
12664 this.inputEl().dom.select();
12667 filterKeys : function(e){
12668 var k = e.getKey();
12669 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12672 var c = e.getCharCode(), cc = String.fromCharCode(c);
12673 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12676 if(!this.maskRe.test(cc)){
12681 * Clear any invalid styles/messages for this field
12683 clearInvalid : function(){
12685 if(!this.el || this.preventMark){ // not rendered
12690 this.el.removeClass([this.invalidClass, 'is-invalid']);
12692 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12694 var feedback = this.el.select('.form-control-feedback', true).first();
12697 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12702 if(this.indicator){
12703 this.indicator.removeClass('visible');
12704 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12707 this.fireEvent('valid', this);
12711 * Mark this field as valid
12713 markValid : function()
12715 if(!this.el || this.preventMark){ // not rendered...
12719 this.el.removeClass([this.invalidClass, this.validClass]);
12720 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12722 var feedback = this.el.select('.form-control-feedback', true).first();
12725 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12728 if(this.indicator){
12729 this.indicator.removeClass('visible');
12730 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12738 if(this.allowBlank && !this.getRawValue().length){
12741 if (Roo.bootstrap.version == 3) {
12742 this.el.addClass(this.validClass);
12744 this.inputEl().addClass('is-valid');
12747 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12749 var feedback = this.el.select('.form-control-feedback', true).first();
12752 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12753 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12758 this.fireEvent('valid', this);
12762 * Mark this field as invalid
12763 * @param {String} msg The validation message
12765 markInvalid : function(msg)
12767 if(!this.el || this.preventMark){ // not rendered
12771 this.el.removeClass([this.invalidClass, this.validClass]);
12772 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12774 var feedback = this.el.select('.form-control-feedback', true).first();
12777 this.el.select('.form-control-feedback', true).first().removeClass(
12778 [this.invalidFeedbackClass, this.validFeedbackClass]);
12785 if(this.allowBlank && !this.getRawValue().length){
12789 if(this.indicator){
12790 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12791 this.indicator.addClass('visible');
12793 if (Roo.bootstrap.version == 3) {
12794 this.el.addClass(this.invalidClass);
12796 this.inputEl().addClass('is-invalid');
12801 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12803 var feedback = this.el.select('.form-control-feedback', true).first();
12806 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12808 if(this.getValue().length || this.forceFeedback){
12809 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12816 this.fireEvent('invalid', this, msg);
12819 SafariOnKeyDown : function(event)
12821 // this is a workaround for a password hang bug on chrome/ webkit.
12822 if (this.inputEl().dom.type != 'password') {
12826 var isSelectAll = false;
12828 if(this.inputEl().dom.selectionEnd > 0){
12829 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12831 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12832 event.preventDefault();
12837 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12839 event.preventDefault();
12840 // this is very hacky as keydown always get's upper case.
12842 var cc = String.fromCharCode(event.getCharCode());
12843 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12847 adjustWidth : function(tag, w){
12848 tag = tag.toLowerCase();
12849 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12850 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12851 if(tag == 'input'){
12854 if(tag == 'textarea'){
12857 }else if(Roo.isOpera){
12858 if(tag == 'input'){
12861 if(tag == 'textarea'){
12869 setFieldLabel : function(v)
12871 if(!this.rendered){
12875 if(this.indicatorEl()){
12876 var ar = this.el.select('label > span',true);
12878 if (ar.elements.length) {
12879 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12880 this.fieldLabel = v;
12884 var br = this.el.select('label',true);
12886 if(br.elements.length) {
12887 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12888 this.fieldLabel = v;
12892 Roo.log('Cannot Found any of label > span || label in input');
12896 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12897 this.fieldLabel = v;
12912 * @class Roo.bootstrap.TextArea
12913 * @extends Roo.bootstrap.Input
12914 * Bootstrap TextArea class
12915 * @cfg {Number} cols Specifies the visible width of a text area
12916 * @cfg {Number} rows Specifies the visible number of lines in a text area
12917 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12918 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12919 * @cfg {string} html text
12922 * Create a new TextArea
12923 * @param {Object} config The config object
12926 Roo.bootstrap.TextArea = function(config){
12927 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12931 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12941 getAutoCreate : function(){
12943 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12949 if(this.inputType != 'hidden'){
12950 cfg.cls = 'form-group' //input-group
12958 value : this.value || '',
12959 html: this.html || '',
12960 cls : 'form-control',
12961 placeholder : this.placeholder || ''
12965 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12966 input.maxLength = this.maxLength;
12970 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12974 input.cols = this.cols;
12977 if (this.readOnly) {
12978 input.readonly = true;
12982 input.name = this.name;
12986 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12990 ['xs','sm','md','lg'].map(function(size){
12991 if (settings[size]) {
12992 cfg.cls += ' col-' + size + '-' + settings[size];
12996 var inputblock = input;
12998 if(this.hasFeedback && !this.allowBlank){
13002 cls: 'glyphicon form-control-feedback'
13006 cls : 'has-feedback',
13015 if (this.before || this.after) {
13018 cls : 'input-group',
13022 inputblock.cn.push({
13024 cls : 'input-group-addon',
13029 inputblock.cn.push(input);
13031 if(this.hasFeedback && !this.allowBlank){
13032 inputblock.cls += ' has-feedback';
13033 inputblock.cn.push(feedback);
13037 inputblock.cn.push({
13039 cls : 'input-group-addon',
13046 if (align ==='left' && this.fieldLabel.length) {
13051 cls : 'control-label',
13052 html : this.fieldLabel
13063 if(this.labelWidth > 12){
13064 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13067 if(this.labelWidth < 13 && this.labelmd == 0){
13068 this.labelmd = this.labelWidth;
13071 if(this.labellg > 0){
13072 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13073 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13076 if(this.labelmd > 0){
13077 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13078 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13081 if(this.labelsm > 0){
13082 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13083 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13086 if(this.labelxs > 0){
13087 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13088 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13091 } else if ( this.fieldLabel.length) {
13096 //cls : 'input-group-addon',
13097 html : this.fieldLabel
13115 if (this.disabled) {
13116 input.disabled=true;
13123 * return the real textarea element.
13125 inputEl: function ()
13127 return this.el.select('textarea.form-control',true).first();
13131 * Clear any invalid styles/messages for this field
13133 clearInvalid : function()
13136 if(!this.el || this.preventMark){ // not rendered
13140 var label = this.el.select('label', true).first();
13141 var icon = this.el.select('i.fa-star', true).first();
13146 this.el.removeClass( this.validClass);
13147 this.inputEl().removeClass('is-invalid');
13149 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13151 var feedback = this.el.select('.form-control-feedback', true).first();
13154 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13159 this.fireEvent('valid', this);
13163 * Mark this field as valid
13165 markValid : function()
13167 if(!this.el || this.preventMark){ // not rendered
13171 this.el.removeClass([this.invalidClass, this.validClass]);
13172 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13174 var feedback = this.el.select('.form-control-feedback', true).first();
13177 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13180 if(this.disabled || this.allowBlank){
13184 var label = this.el.select('label', true).first();
13185 var icon = this.el.select('i.fa-star', true).first();
13190 if (Roo.bootstrap.version == 3) {
13191 this.el.addClass(this.validClass);
13193 this.inputEl().addClass('is-valid');
13197 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13199 var feedback = this.el.select('.form-control-feedback', true).first();
13202 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13203 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13208 this.fireEvent('valid', this);
13212 * Mark this field as invalid
13213 * @param {String} msg The validation message
13215 markInvalid : function(msg)
13217 if(!this.el || this.preventMark){ // not rendered
13221 this.el.removeClass([this.invalidClass, this.validClass]);
13222 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13224 var feedback = this.el.select('.form-control-feedback', true).first();
13227 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13230 if(this.disabled || this.allowBlank){
13234 var label = this.el.select('label', true).first();
13235 var icon = this.el.select('i.fa-star', true).first();
13237 if(!this.getValue().length && label && !icon){
13238 this.el.createChild({
13240 cls : 'text-danger fa fa-lg fa-star',
13241 tooltip : 'This field is required',
13242 style : 'margin-right:5px;'
13246 if (Roo.bootstrap.version == 3) {
13247 this.el.addClass(this.invalidClass);
13249 this.inputEl().addClass('is-invalid');
13252 // fixme ... this may be depricated need to test..
13253 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13255 var feedback = this.el.select('.form-control-feedback', true).first();
13258 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13260 if(this.getValue().length || this.forceFeedback){
13261 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13268 this.fireEvent('invalid', this, msg);
13276 * trigger field - base class for combo..
13281 * @class Roo.bootstrap.TriggerField
13282 * @extends Roo.bootstrap.Input
13283 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13284 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13285 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13286 * for which you can provide a custom implementation. For example:
13288 var trigger = new Roo.bootstrap.TriggerField();
13289 trigger.onTriggerClick = myTriggerFn;
13290 trigger.applyTo('my-field');
13293 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13294 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13295 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13296 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13297 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13300 * Create a new TriggerField.
13301 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13302 * to the base TextField)
13304 Roo.bootstrap.TriggerField = function(config){
13305 this.mimicing = false;
13306 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13309 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13311 * @cfg {String} triggerClass A CSS class to apply to the trigger
13314 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13319 * @cfg {Boolean} removable (true|false) special filter default false
13323 /** @cfg {Boolean} grow @hide */
13324 /** @cfg {Number} growMin @hide */
13325 /** @cfg {Number} growMax @hide */
13331 autoSize: Roo.emptyFn,
13335 deferHeight : true,
13338 actionMode : 'wrap',
13343 getAutoCreate : function(){
13345 var align = this.labelAlign || this.parentLabelAlign();
13350 cls: 'form-group' //input-group
13357 type : this.inputType,
13358 cls : 'form-control',
13359 autocomplete: 'new-password',
13360 placeholder : this.placeholder || ''
13364 input.name = this.name;
13367 input.cls += ' input-' + this.size;
13370 if (this.disabled) {
13371 input.disabled=true;
13374 var inputblock = input;
13376 if(this.hasFeedback && !this.allowBlank){
13380 cls: 'glyphicon form-control-feedback'
13383 if(this.removable && !this.editable ){
13385 cls : 'has-feedback',
13391 cls : 'roo-combo-removable-btn close'
13398 cls : 'has-feedback',
13407 if(this.removable && !this.editable ){
13409 cls : 'roo-removable',
13415 cls : 'roo-combo-removable-btn close'
13422 if (this.before || this.after) {
13425 cls : 'input-group',
13429 inputblock.cn.push({
13431 cls : 'input-group-addon input-group-prepend input-group-text',
13436 inputblock.cn.push(input);
13438 if(this.hasFeedback && !this.allowBlank){
13439 inputblock.cls += ' has-feedback';
13440 inputblock.cn.push(feedback);
13444 inputblock.cn.push({
13446 cls : 'input-group-addon input-group-append input-group-text',
13455 var ibwrap = inputblock;
13460 cls: 'roo-select2-choices',
13464 cls: 'roo-select2-search-field',
13476 cls: 'roo-select2-container input-group',
13481 cls: 'form-hidden-field'
13487 if(!this.multiple && this.showToggleBtn){
13493 if (this.caret != false) {
13496 cls: 'fa fa-' + this.caret
13503 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13505 Roo.bootstrap.version == 3 ? caret : '',
13508 cls: 'combobox-clear',
13522 combobox.cls += ' roo-select2-container-multi';
13526 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13527 tooltip : 'This field is required'
13529 if (Roo.bootstrap.version == 4) {
13532 style : 'display:none'
13537 if (align ==='left' && this.fieldLabel.length) {
13539 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13546 cls : 'control-label',
13547 html : this.fieldLabel
13559 var labelCfg = cfg.cn[1];
13560 var contentCfg = cfg.cn[2];
13562 if(this.indicatorpos == 'right'){
13567 cls : 'control-label',
13571 html : this.fieldLabel
13585 labelCfg = cfg.cn[0];
13586 contentCfg = cfg.cn[1];
13589 if(this.labelWidth > 12){
13590 labelCfg.style = "width: " + this.labelWidth + 'px';
13593 if(this.labelWidth < 13 && this.labelmd == 0){
13594 this.labelmd = this.labelWidth;
13597 if(this.labellg > 0){
13598 labelCfg.cls += ' col-lg-' + this.labellg;
13599 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13602 if(this.labelmd > 0){
13603 labelCfg.cls += ' col-md-' + this.labelmd;
13604 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13607 if(this.labelsm > 0){
13608 labelCfg.cls += ' col-sm-' + this.labelsm;
13609 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13612 if(this.labelxs > 0){
13613 labelCfg.cls += ' col-xs-' + this.labelxs;
13614 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13617 } else if ( this.fieldLabel.length) {
13618 // Roo.log(" label");
13623 //cls : 'input-group-addon',
13624 html : this.fieldLabel
13632 if(this.indicatorpos == 'right'){
13640 html : this.fieldLabel
13654 // Roo.log(" no label && no align");
13661 ['xs','sm','md','lg'].map(function(size){
13662 if (settings[size]) {
13663 cfg.cls += ' col-' + size + '-' + settings[size];
13674 onResize : function(w, h){
13675 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13676 // if(typeof w == 'number'){
13677 // var x = w - this.trigger.getWidth();
13678 // this.inputEl().setWidth(this.adjustWidth('input', x));
13679 // this.trigger.setStyle('left', x+'px');
13684 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13687 getResizeEl : function(){
13688 return this.inputEl();
13692 getPositionEl : function(){
13693 return this.inputEl();
13697 alignErrorIcon : function(){
13698 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13702 initEvents : function(){
13706 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13707 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13708 if(!this.multiple && this.showToggleBtn){
13709 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13710 if(this.hideTrigger){
13711 this.trigger.setDisplayed(false);
13713 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13717 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13720 if(this.removable && !this.editable && !this.tickable){
13721 var close = this.closeTriggerEl();
13724 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13725 close.on('click', this.removeBtnClick, this, close);
13729 //this.trigger.addClassOnOver('x-form-trigger-over');
13730 //this.trigger.addClassOnClick('x-form-trigger-click');
13733 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13737 closeTriggerEl : function()
13739 var close = this.el.select('.roo-combo-removable-btn', true).first();
13740 return close ? close : false;
13743 removeBtnClick : function(e, h, el)
13745 e.preventDefault();
13747 if(this.fireEvent("remove", this) !== false){
13749 this.fireEvent("afterremove", this)
13753 createList : function()
13755 this.list = Roo.get(document.body).createChild({
13756 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13757 cls: 'typeahead typeahead-long dropdown-menu shadow',
13758 style: 'display:none'
13761 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13766 initTrigger : function(){
13771 onDestroy : function(){
13773 this.trigger.removeAllListeners();
13774 // this.trigger.remove();
13777 // this.wrap.remove();
13779 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13783 onFocus : function(){
13784 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13786 if(!this.mimicing){
13787 this.wrap.addClass('x-trigger-wrap-focus');
13788 this.mimicing = true;
13789 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13790 if(this.monitorTab){
13791 this.el.on("keydown", this.checkTab, this);
13798 checkTab : function(e){
13799 if(e.getKey() == e.TAB){
13800 this.triggerBlur();
13805 onBlur : function(){
13810 mimicBlur : function(e, t){
13812 if(!this.wrap.contains(t) && this.validateBlur()){
13813 this.triggerBlur();
13819 triggerBlur : function(){
13820 this.mimicing = false;
13821 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13822 if(this.monitorTab){
13823 this.el.un("keydown", this.checkTab, this);
13825 //this.wrap.removeClass('x-trigger-wrap-focus');
13826 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13830 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13831 validateBlur : function(e, t){
13836 onDisable : function(){
13837 this.inputEl().dom.disabled = true;
13838 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13840 // this.wrap.addClass('x-item-disabled');
13845 onEnable : function(){
13846 this.inputEl().dom.disabled = false;
13847 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13849 // this.el.removeClass('x-item-disabled');
13854 onShow : function(){
13855 var ae = this.getActionEl();
13858 ae.dom.style.display = '';
13859 ae.dom.style.visibility = 'visible';
13865 onHide : function(){
13866 var ae = this.getActionEl();
13867 ae.dom.style.display = 'none';
13871 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13872 * by an implementing function.
13874 * @param {EventObject} e
13876 onTriggerClick : Roo.emptyFn
13884 * @class Roo.bootstrap.CardUploader
13885 * @extends Roo.bootstrap.Button
13886 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13887 * @cfg {Number} errorTimeout default 3000
13888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13889 * @cfg {Array} html The button text.
13893 * Create a new CardUploader
13894 * @param {Object} config The config object
13897 Roo.bootstrap.CardUploader = function(config){
13901 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13904 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13912 * When a image is clicked on - and needs to display a slideshow or similar..
13913 * @param {Roo.bootstrap.Card} this
13914 * @param {Object} The image information data
13920 * When a the download link is clicked
13921 * @param {Roo.bootstrap.Card} this
13922 * @param {Object} The image information data contains
13929 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13932 errorTimeout : 3000,
13936 fileCollection : false,
13939 getAutoCreate : function()
13943 cls :'form-group' ,
13948 //cls : 'input-group-addon',
13949 html : this.fieldLabel
13957 value : this.value,
13958 cls : 'd-none form-control'
13963 multiple : 'multiple',
13965 cls : 'd-none roo-card-upload-selector'
13969 cls : 'roo-card-uploader-button-container w-100 mb-2'
13972 cls : 'card-columns roo-card-uploader-container'
13982 getChildContainer : function() /// what children are added to.
13984 return this.containerEl;
13987 getButtonContainer : function() /// what children are added to.
13989 return this.el.select(".roo-card-uploader-button-container").first();
13992 initEvents : function()
13995 Roo.bootstrap.Input.prototype.initEvents.call(this);
13999 xns: Roo.bootstrap,
14002 container_method : 'getButtonContainer' ,
14003 html : this.html, // fix changable?
14006 'click' : function(btn, e) {
14015 this.urlAPI = (window.createObjectURL && window) ||
14016 (window.URL && URL.revokeObjectURL && URL) ||
14017 (window.webkitURL && webkitURL);
14022 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14024 this.selectorEl.on('change', this.onFileSelected, this);
14027 this.images.forEach(function(img) {
14030 this.images = false;
14032 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14038 onClick : function(e)
14040 e.preventDefault();
14042 this.selectorEl.dom.click();
14046 onFileSelected : function(e)
14048 e.preventDefault();
14050 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14054 Roo.each(this.selectorEl.dom.files, function(file){
14055 this.addFile(file);
14064 addFile : function(file)
14067 if(typeof(file) === 'string'){
14068 throw "Add file by name?"; // should not happen
14072 if(!file || !this.urlAPI){
14082 var url = _this.urlAPI.createObjectURL( file);
14085 id : Roo.bootstrap.CardUploader.ID--,
14086 is_uploaded : false,
14090 mimetype : file.type,
14098 * addCard - add an Attachment to the uploader
14099 * @param data - the data about the image to upload
14103 title : "Title of file",
14104 is_uploaded : false,
14105 src : "http://.....",
14106 srcfile : { the File upload object },
14107 mimetype : file.type,
14110 .. any other data...
14116 addCard : function (data)
14118 // hidden input element?
14119 // if the file is not an image...
14120 //then we need to use something other that and header_image
14125 xns : Roo.bootstrap,
14126 xtype : 'CardFooter',
14129 xns : Roo.bootstrap,
14135 xns : Roo.bootstrap,
14137 html : String.format("<small>{0}</small>", data.title),
14138 cls : 'col-10 text-left',
14143 click : function() {
14145 t.fireEvent( "download", t, data );
14151 xns : Roo.bootstrap,
14153 style: 'max-height: 28px; ',
14159 click : function() {
14160 t.removeCard(data.id)
14172 var cn = this.addxtype(
14175 xns : Roo.bootstrap,
14178 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14179 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14180 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14185 initEvents : function() {
14186 Roo.bootstrap.Card.prototype.initEvents.call(this);
14188 this.imgEl = this.el.select('.card-img-top').first();
14190 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14191 this.imgEl.set({ 'pointer' : 'cursor' });
14194 this.getCardFooter().addClass('p-1');
14201 // dont' really need ot update items.
14202 // this.items.push(cn);
14203 this.fileCollection.add(cn);
14205 if (!data.srcfile) {
14206 this.updateInput();
14211 var reader = new FileReader();
14212 reader.addEventListener("load", function() {
14213 data.srcdata = reader.result;
14216 reader.readAsDataURL(data.srcfile);
14221 removeCard : function(id)
14224 var card = this.fileCollection.get(id);
14225 card.data.is_deleted = 1;
14226 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14227 //this.fileCollection.remove(card);
14228 //this.items = this.items.filter(function(e) { return e != card });
14229 // dont' really need ot update items.
14230 card.el.dom.parentNode.removeChild(card.el.dom);
14231 this.updateInput();
14237 this.fileCollection.each(function(card) {
14238 if (card.el.dom && card.el.dom.parentNode) {
14239 card.el.dom.parentNode.removeChild(card.el.dom);
14242 this.fileCollection.clear();
14243 this.updateInput();
14246 updateInput : function()
14249 this.fileCollection.each(function(e) {
14253 this.inputEl().dom.value = JSON.stringify(data);
14263 Roo.bootstrap.CardUploader.ID = -1;/*
14265 * Ext JS Library 1.1.1
14266 * Copyright(c) 2006-2007, Ext JS, LLC.
14268 * Originally Released Under LGPL - original licence link has changed is not relivant.
14271 * <script type="text/javascript">
14276 * @class Roo.data.SortTypes
14278 * Defines the default sorting (casting?) comparison functions used when sorting data.
14280 Roo.data.SortTypes = {
14282 * Default sort that does nothing
14283 * @param {Mixed} s The value being converted
14284 * @return {Mixed} The comparison value
14286 none : function(s){
14291 * The regular expression used to strip tags
14295 stripTagsRE : /<\/?[^>]+>/gi,
14298 * Strips all HTML tags to sort on text only
14299 * @param {Mixed} s The value being converted
14300 * @return {String} The comparison value
14302 asText : function(s){
14303 return String(s).replace(this.stripTagsRE, "");
14307 * Strips all HTML tags to sort on text only - Case insensitive
14308 * @param {Mixed} s The value being converted
14309 * @return {String} The comparison value
14311 asUCText : function(s){
14312 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14316 * Case insensitive string
14317 * @param {Mixed} s The value being converted
14318 * @return {String} The comparison value
14320 asUCString : function(s) {
14321 return String(s).toUpperCase();
14326 * @param {Mixed} s The value being converted
14327 * @return {Number} The comparison value
14329 asDate : function(s) {
14333 if(s instanceof Date){
14334 return s.getTime();
14336 return Date.parse(String(s));
14341 * @param {Mixed} s The value being converted
14342 * @return {Float} The comparison value
14344 asFloat : function(s) {
14345 var val = parseFloat(String(s).replace(/,/g, ""));
14354 * @param {Mixed} s The value being converted
14355 * @return {Number} The comparison value
14357 asInt : function(s) {
14358 var val = parseInt(String(s).replace(/,/g, ""));
14366 * Ext JS Library 1.1.1
14367 * Copyright(c) 2006-2007, Ext JS, LLC.
14369 * Originally Released Under LGPL - original licence link has changed is not relivant.
14372 * <script type="text/javascript">
14376 * @class Roo.data.Record
14377 * Instances of this class encapsulate both record <em>definition</em> information, and record
14378 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14379 * to access Records cached in an {@link Roo.data.Store} object.<br>
14381 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14382 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14385 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14387 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14388 * {@link #create}. The parameters are the same.
14389 * @param {Array} data An associative Array of data values keyed by the field name.
14390 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14391 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14392 * not specified an integer id is generated.
14394 Roo.data.Record = function(data, id){
14395 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14400 * Generate a constructor for a specific record layout.
14401 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14402 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14403 * Each field definition object may contain the following properties: <ul>
14404 * <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,
14405 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14406 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14407 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14408 * is being used, then this is a string containing the javascript expression to reference the data relative to
14409 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14410 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14411 * this may be omitted.</p></li>
14412 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14413 * <ul><li>auto (Default, implies no conversion)</li>
14418 * <li>date</li></ul></p></li>
14419 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14420 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14421 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14422 * by the Reader into an object that will be stored in the Record. It is passed the
14423 * following parameters:<ul>
14424 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14426 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14428 * <br>usage:<br><pre><code>
14429 var TopicRecord = Roo.data.Record.create(
14430 {name: 'title', mapping: 'topic_title'},
14431 {name: 'author', mapping: 'username'},
14432 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14433 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14434 {name: 'lastPoster', mapping: 'user2'},
14435 {name: 'excerpt', mapping: 'post_text'}
14438 var myNewRecord = new TopicRecord({
14439 title: 'Do my job please',
14442 lastPost: new Date(),
14443 lastPoster: 'Animal',
14444 excerpt: 'No way dude!'
14446 myStore.add(myNewRecord);
14451 Roo.data.Record.create = function(o){
14452 var f = function(){
14453 f.superclass.constructor.apply(this, arguments);
14455 Roo.extend(f, Roo.data.Record);
14456 var p = f.prototype;
14457 p.fields = new Roo.util.MixedCollection(false, function(field){
14460 for(var i = 0, len = o.length; i < len; i++){
14461 p.fields.add(new Roo.data.Field(o[i]));
14463 f.getField = function(name){
14464 return p.fields.get(name);
14469 Roo.data.Record.AUTO_ID = 1000;
14470 Roo.data.Record.EDIT = 'edit';
14471 Roo.data.Record.REJECT = 'reject';
14472 Roo.data.Record.COMMIT = 'commit';
14474 Roo.data.Record.prototype = {
14476 * Readonly flag - true if this record has been modified.
14485 join : function(store){
14486 this.store = store;
14490 * Set the named field to the specified value.
14491 * @param {String} name The name of the field to set.
14492 * @param {Object} value The value to set the field to.
14494 set : function(name, value){
14495 if(this.data[name] == value){
14499 if(!this.modified){
14500 this.modified = {};
14502 if(typeof this.modified[name] == 'undefined'){
14503 this.modified[name] = this.data[name];
14505 this.data[name] = value;
14506 if(!this.editing && this.store){
14507 this.store.afterEdit(this);
14512 * Get the value of the named field.
14513 * @param {String} name The name of the field to get the value of.
14514 * @return {Object} The value of the field.
14516 get : function(name){
14517 return this.data[name];
14521 beginEdit : function(){
14522 this.editing = true;
14523 this.modified = {};
14527 cancelEdit : function(){
14528 this.editing = false;
14529 delete this.modified;
14533 endEdit : function(){
14534 this.editing = false;
14535 if(this.dirty && this.store){
14536 this.store.afterEdit(this);
14541 * Usually called by the {@link Roo.data.Store} which owns the Record.
14542 * Rejects all changes made to the Record since either creation, or the last commit operation.
14543 * Modified fields are reverted to their original values.
14545 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14546 * of reject operations.
14548 reject : function(){
14549 var m = this.modified;
14551 if(typeof m[n] != "function"){
14552 this.data[n] = m[n];
14555 this.dirty = false;
14556 delete this.modified;
14557 this.editing = false;
14559 this.store.afterReject(this);
14564 * Usually called by the {@link Roo.data.Store} which owns the Record.
14565 * Commits all changes made to the Record since either creation, or the last commit operation.
14567 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14568 * of commit operations.
14570 commit : function(){
14571 this.dirty = false;
14572 delete this.modified;
14573 this.editing = false;
14575 this.store.afterCommit(this);
14580 hasError : function(){
14581 return this.error != null;
14585 clearError : function(){
14590 * Creates a copy of this record.
14591 * @param {String} id (optional) A new record id if you don't want to use this record's id
14594 copy : function(newId) {
14595 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14599 * Ext JS Library 1.1.1
14600 * Copyright(c) 2006-2007, Ext JS, LLC.
14602 * Originally Released Under LGPL - original licence link has changed is not relivant.
14605 * <script type="text/javascript">
14611 * @class Roo.data.Store
14612 * @extends Roo.util.Observable
14613 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14614 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14616 * 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
14617 * has no knowledge of the format of the data returned by the Proxy.<br>
14619 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14620 * instances from the data object. These records are cached and made available through accessor functions.
14622 * Creates a new Store.
14623 * @param {Object} config A config object containing the objects needed for the Store to access data,
14624 * and read the data into Records.
14626 Roo.data.Store = function(config){
14627 this.data = new Roo.util.MixedCollection(false);
14628 this.data.getKey = function(o){
14631 this.baseParams = {};
14633 this.paramNames = {
14638 "multisort" : "_multisort"
14641 if(config && config.data){
14642 this.inlineData = config.data;
14643 delete config.data;
14646 Roo.apply(this, config);
14648 if(this.reader){ // reader passed
14649 this.reader = Roo.factory(this.reader, Roo.data);
14650 this.reader.xmodule = this.xmodule || false;
14651 if(!this.recordType){
14652 this.recordType = this.reader.recordType;
14654 if(this.reader.onMetaChange){
14655 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14659 if(this.recordType){
14660 this.fields = this.recordType.prototype.fields;
14662 this.modified = [];
14666 * @event datachanged
14667 * Fires when the data cache has changed, and a widget which is using this Store
14668 * as a Record cache should refresh its view.
14669 * @param {Store} this
14671 datachanged : true,
14673 * @event metachange
14674 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14675 * @param {Store} this
14676 * @param {Object} meta The JSON metadata
14681 * Fires when Records have been added to the Store
14682 * @param {Store} this
14683 * @param {Roo.data.Record[]} records The array of Records added
14684 * @param {Number} index The index at which the record(s) were added
14689 * Fires when a Record has been removed from the Store
14690 * @param {Store} this
14691 * @param {Roo.data.Record} record The Record that was removed
14692 * @param {Number} index The index at which the record was removed
14697 * Fires when a Record has been updated
14698 * @param {Store} this
14699 * @param {Roo.data.Record} record The Record that was updated
14700 * @param {String} operation The update operation being performed. Value may be one of:
14702 Roo.data.Record.EDIT
14703 Roo.data.Record.REJECT
14704 Roo.data.Record.COMMIT
14710 * Fires when the data cache has been cleared.
14711 * @param {Store} this
14715 * @event beforeload
14716 * Fires before a request is made for a new data object. If the beforeload handler returns false
14717 * the load action will be canceled.
14718 * @param {Store} this
14719 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14723 * @event beforeloadadd
14724 * Fires after a new set of Records has been loaded.
14725 * @param {Store} this
14726 * @param {Roo.data.Record[]} records The Records that were loaded
14727 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14729 beforeloadadd : true,
14732 * Fires after a new set of Records has been loaded, before they are added to the store.
14733 * @param {Store} this
14734 * @param {Roo.data.Record[]} records The Records that were loaded
14735 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14736 * @params {Object} return from reader
14740 * @event loadexception
14741 * Fires if an exception occurs in the Proxy during loading.
14742 * Called with the signature of the Proxy's "loadexception" event.
14743 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14746 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14747 * @param {Object} load options
14748 * @param {Object} jsonData from your request (normally this contains the Exception)
14750 loadexception : true
14754 this.proxy = Roo.factory(this.proxy, Roo.data);
14755 this.proxy.xmodule = this.xmodule || false;
14756 this.relayEvents(this.proxy, ["loadexception"]);
14758 this.sortToggle = {};
14759 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14761 Roo.data.Store.superclass.constructor.call(this);
14763 if(this.inlineData){
14764 this.loadData(this.inlineData);
14765 delete this.inlineData;
14769 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14771 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14772 * without a remote query - used by combo/forms at present.
14776 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14779 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14782 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
14783 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14786 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14787 * on any HTTP request
14790 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14793 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14797 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14798 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14800 remoteSort : false,
14803 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14804 * loaded or when a record is removed. (defaults to false).
14806 pruneModifiedRecords : false,
14809 lastOptions : null,
14812 * Add Records to the Store and fires the add event.
14813 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14815 add : function(records){
14816 records = [].concat(records);
14817 for(var i = 0, len = records.length; i < len; i++){
14818 records[i].join(this);
14820 var index = this.data.length;
14821 this.data.addAll(records);
14822 this.fireEvent("add", this, records, index);
14826 * Remove a Record from the Store and fires the remove event.
14827 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14829 remove : function(record){
14830 var index = this.data.indexOf(record);
14831 this.data.removeAt(index);
14833 if(this.pruneModifiedRecords){
14834 this.modified.remove(record);
14836 this.fireEvent("remove", this, record, index);
14840 * Remove all Records from the Store and fires the clear event.
14842 removeAll : function(){
14844 if(this.pruneModifiedRecords){
14845 this.modified = [];
14847 this.fireEvent("clear", this);
14851 * Inserts Records to the Store at the given index and fires the add event.
14852 * @param {Number} index The start index at which to insert the passed Records.
14853 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14855 insert : function(index, records){
14856 records = [].concat(records);
14857 for(var i = 0, len = records.length; i < len; i++){
14858 this.data.insert(index, records[i]);
14859 records[i].join(this);
14861 this.fireEvent("add", this, records, index);
14865 * Get the index within the cache of the passed Record.
14866 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14867 * @return {Number} The index of the passed Record. Returns -1 if not found.
14869 indexOf : function(record){
14870 return this.data.indexOf(record);
14874 * Get the index within the cache of the Record with the passed id.
14875 * @param {String} id The id of the Record to find.
14876 * @return {Number} The index of the Record. Returns -1 if not found.
14878 indexOfId : function(id){
14879 return this.data.indexOfKey(id);
14883 * Get the Record with the specified id.
14884 * @param {String} id The id of the Record to find.
14885 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14887 getById : function(id){
14888 return this.data.key(id);
14892 * Get the Record at the specified index.
14893 * @param {Number} index The index of the Record to find.
14894 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14896 getAt : function(index){
14897 return this.data.itemAt(index);
14901 * Returns a range of Records between specified indices.
14902 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14903 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14904 * @return {Roo.data.Record[]} An array of Records
14906 getRange : function(start, end){
14907 return this.data.getRange(start, end);
14911 storeOptions : function(o){
14912 o = Roo.apply({}, o);
14915 this.lastOptions = o;
14919 * Loads the Record cache from the configured Proxy using the configured Reader.
14921 * If using remote paging, then the first load call must specify the <em>start</em>
14922 * and <em>limit</em> properties in the options.params property to establish the initial
14923 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14925 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14926 * and this call will return before the new data has been loaded. Perform any post-processing
14927 * in a callback function, or in a "load" event handler.</strong>
14929 * @param {Object} options An object containing properties which control loading options:<ul>
14930 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14931 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14932 * passed the following arguments:<ul>
14933 * <li>r : Roo.data.Record[]</li>
14934 * <li>options: Options object from the load call</li>
14935 * <li>success: Boolean success indicator</li></ul></li>
14936 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14937 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14940 load : function(options){
14941 options = options || {};
14942 if(this.fireEvent("beforeload", this, options) !== false){
14943 this.storeOptions(options);
14944 var p = Roo.apply(options.params || {}, this.baseParams);
14945 // if meta was not loaded from remote source.. try requesting it.
14946 if (!this.reader.metaFromRemote) {
14947 p._requestMeta = 1;
14949 if(this.sortInfo && this.remoteSort){
14950 var pn = this.paramNames;
14951 p[pn["sort"]] = this.sortInfo.field;
14952 p[pn["dir"]] = this.sortInfo.direction;
14954 if (this.multiSort) {
14955 var pn = this.paramNames;
14956 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14959 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14964 * Reloads the Record cache from the configured Proxy using the configured Reader and
14965 * the options from the last load operation performed.
14966 * @param {Object} options (optional) An object containing properties which may override the options
14967 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14968 * the most recently used options are reused).
14970 reload : function(options){
14971 this.load(Roo.applyIf(options||{}, this.lastOptions));
14975 // Called as a callback by the Reader during a load operation.
14976 loadRecords : function(o, options, success){
14977 if(!o || success === false){
14978 if(success !== false){
14979 this.fireEvent("load", this, [], options, o);
14981 if(options.callback){
14982 options.callback.call(options.scope || this, [], options, false);
14986 // if data returned failure - throw an exception.
14987 if (o.success === false) {
14988 // show a message if no listener is registered.
14989 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14990 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14992 // loadmask wil be hooked into this..
14993 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14996 var r = o.records, t = o.totalRecords || r.length;
14998 this.fireEvent("beforeloadadd", this, r, options, o);
15000 if(!options || options.add !== true){
15001 if(this.pruneModifiedRecords){
15002 this.modified = [];
15004 for(var i = 0, len = r.length; i < len; i++){
15008 this.data = this.snapshot;
15009 delete this.snapshot;
15012 this.data.addAll(r);
15013 this.totalLength = t;
15015 this.fireEvent("datachanged", this);
15017 this.totalLength = Math.max(t, this.data.length+r.length);
15021 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15023 var e = new Roo.data.Record({});
15025 e.set(this.parent.displayField, this.parent.emptyTitle);
15026 e.set(this.parent.valueField, '');
15031 this.fireEvent("load", this, r, options, o);
15032 if(options.callback){
15033 options.callback.call(options.scope || this, r, options, true);
15039 * Loads data from a passed data block. A Reader which understands the format of the data
15040 * must have been configured in the constructor.
15041 * @param {Object} data The data block from which to read the Records. The format of the data expected
15042 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15043 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15045 loadData : function(o, append){
15046 var r = this.reader.readRecords(o);
15047 this.loadRecords(r, {add: append}, true);
15051 * using 'cn' the nested child reader read the child array into it's child stores.
15052 * @param {Object} rec The record with a 'children array
15054 loadDataFromChildren : function(rec)
15056 this.loadData(this.reader.toLoadData(rec));
15061 * Gets the number of cached records.
15063 * <em>If using paging, this may not be the total size of the dataset. If the data object
15064 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15065 * the data set size</em>
15067 getCount : function(){
15068 return this.data.length || 0;
15072 * Gets the total number of records in the dataset as returned by the server.
15074 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15075 * the dataset size</em>
15077 getTotalCount : function(){
15078 return this.totalLength || 0;
15082 * Returns the sort state of the Store as an object with two properties:
15084 field {String} The name of the field by which the Records are sorted
15085 direction {String} The sort order, "ASC" or "DESC"
15088 getSortState : function(){
15089 return this.sortInfo;
15093 applySort : function(){
15094 if(this.sortInfo && !this.remoteSort){
15095 var s = this.sortInfo, f = s.field;
15096 var st = this.fields.get(f).sortType;
15097 var fn = function(r1, r2){
15098 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15099 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15101 this.data.sort(s.direction, fn);
15102 if(this.snapshot && this.snapshot != this.data){
15103 this.snapshot.sort(s.direction, fn);
15109 * Sets the default sort column and order to be used by the next load operation.
15110 * @param {String} fieldName The name of the field to sort by.
15111 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15113 setDefaultSort : function(field, dir){
15114 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15118 * Sort the Records.
15119 * If remote sorting is used, the sort is performed on the server, and the cache is
15120 * reloaded. If local sorting is used, the cache is sorted internally.
15121 * @param {String} fieldName The name of the field to sort by.
15122 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15124 sort : function(fieldName, dir){
15125 var f = this.fields.get(fieldName);
15127 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15129 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15130 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15135 this.sortToggle[f.name] = dir;
15136 this.sortInfo = {field: f.name, direction: dir};
15137 if(!this.remoteSort){
15139 this.fireEvent("datachanged", this);
15141 this.load(this.lastOptions);
15146 * Calls the specified function for each of the Records in the cache.
15147 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15148 * Returning <em>false</em> aborts and exits the iteration.
15149 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15151 each : function(fn, scope){
15152 this.data.each(fn, scope);
15156 * Gets all records modified since the last commit. Modified records are persisted across load operations
15157 * (e.g., during paging).
15158 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15160 getModifiedRecords : function(){
15161 return this.modified;
15165 createFilterFn : function(property, value, anyMatch){
15166 if(!value.exec){ // not a regex
15167 value = String(value);
15168 if(value.length == 0){
15171 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15173 return function(r){
15174 return value.test(r.data[property]);
15179 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15180 * @param {String} property A field on your records
15181 * @param {Number} start The record index to start at (defaults to 0)
15182 * @param {Number} end The last record index to include (defaults to length - 1)
15183 * @return {Number} The sum
15185 sum : function(property, start, end){
15186 var rs = this.data.items, v = 0;
15187 start = start || 0;
15188 end = (end || end === 0) ? end : rs.length-1;
15190 for(var i = start; i <= end; i++){
15191 v += (rs[i].data[property] || 0);
15197 * Filter the records by a specified property.
15198 * @param {String} field A field on your records
15199 * @param {String/RegExp} value Either a string that the field
15200 * should start with or a RegExp to test against the field
15201 * @param {Boolean} anyMatch True to match any part not just the beginning
15203 filter : function(property, value, anyMatch){
15204 var fn = this.createFilterFn(property, value, anyMatch);
15205 return fn ? this.filterBy(fn) : this.clearFilter();
15209 * Filter by a function. The specified function will be called with each
15210 * record in this data source. If the function returns true the record is included,
15211 * otherwise it is filtered.
15212 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15213 * @param {Object} scope (optional) The scope of the function (defaults to this)
15215 filterBy : function(fn, scope){
15216 this.snapshot = this.snapshot || this.data;
15217 this.data = this.queryBy(fn, scope||this);
15218 this.fireEvent("datachanged", this);
15222 * Query the records by a specified property.
15223 * @param {String} field A field on your records
15224 * @param {String/RegExp} value Either a string that the field
15225 * should start with or a RegExp to test against the field
15226 * @param {Boolean} anyMatch True to match any part not just the beginning
15227 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15229 query : function(property, value, anyMatch){
15230 var fn = this.createFilterFn(property, value, anyMatch);
15231 return fn ? this.queryBy(fn) : this.data.clone();
15235 * Query by a function. The specified function will be called with each
15236 * record in this data source. If the function returns true the record is included
15238 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15239 * @param {Object} scope (optional) The scope of the function (defaults to this)
15240 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15242 queryBy : function(fn, scope){
15243 var data = this.snapshot || this.data;
15244 return data.filterBy(fn, scope||this);
15248 * Collects unique values for a particular dataIndex from this store.
15249 * @param {String} dataIndex The property to collect
15250 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15251 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15252 * @return {Array} An array of the unique values
15254 collect : function(dataIndex, allowNull, bypassFilter){
15255 var d = (bypassFilter === true && this.snapshot) ?
15256 this.snapshot.items : this.data.items;
15257 var v, sv, r = [], l = {};
15258 for(var i = 0, len = d.length; i < len; i++){
15259 v = d[i].data[dataIndex];
15261 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15270 * Revert to a view of the Record cache with no filtering applied.
15271 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15273 clearFilter : function(suppressEvent){
15274 if(this.snapshot && this.snapshot != this.data){
15275 this.data = this.snapshot;
15276 delete this.snapshot;
15277 if(suppressEvent !== true){
15278 this.fireEvent("datachanged", this);
15284 afterEdit : function(record){
15285 if(this.modified.indexOf(record) == -1){
15286 this.modified.push(record);
15288 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15292 afterReject : function(record){
15293 this.modified.remove(record);
15294 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15298 afterCommit : function(record){
15299 this.modified.remove(record);
15300 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15304 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15305 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15307 commitChanges : function(){
15308 var m = this.modified.slice(0);
15309 this.modified = [];
15310 for(var i = 0, len = m.length; i < len; i++){
15316 * Cancel outstanding changes on all changed records.
15318 rejectChanges : function(){
15319 var m = this.modified.slice(0);
15320 this.modified = [];
15321 for(var i = 0, len = m.length; i < len; i++){
15326 onMetaChange : function(meta, rtype, o){
15327 this.recordType = rtype;
15328 this.fields = rtype.prototype.fields;
15329 delete this.snapshot;
15330 this.sortInfo = meta.sortInfo || this.sortInfo;
15331 this.modified = [];
15332 this.fireEvent('metachange', this, this.reader.meta);
15335 moveIndex : function(data, type)
15337 var index = this.indexOf(data);
15339 var newIndex = index + type;
15343 this.insert(newIndex, data);
15348 * Ext JS Library 1.1.1
15349 * Copyright(c) 2006-2007, Ext JS, LLC.
15351 * Originally Released Under LGPL - original licence link has changed is not relivant.
15354 * <script type="text/javascript">
15358 * @class Roo.data.SimpleStore
15359 * @extends Roo.data.Store
15360 * Small helper class to make creating Stores from Array data easier.
15361 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15362 * @cfg {Array} fields An array of field definition objects, or field name strings.
15363 * @cfg {Object} an existing reader (eg. copied from another store)
15364 * @cfg {Array} data The multi-dimensional array of data
15365 * @cfg {Roo.data.DataProxy} proxy [not-required]
15366 * @cfg {Roo.data.Reader} reader [not-required]
15368 * @param {Object} config
15370 Roo.data.SimpleStore = function(config)
15372 Roo.data.SimpleStore.superclass.constructor.call(this, {
15374 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15377 Roo.data.Record.create(config.fields)
15379 proxy : new Roo.data.MemoryProxy(config.data)
15383 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15385 * Ext JS Library 1.1.1
15386 * Copyright(c) 2006-2007, Ext JS, LLC.
15388 * Originally Released Under LGPL - original licence link has changed is not relivant.
15391 * <script type="text/javascript">
15396 * @extends Roo.data.Store
15397 * @class Roo.data.JsonStore
15398 * Small helper class to make creating Stores for JSON data easier. <br/>
15400 var store = new Roo.data.JsonStore({
15401 url: 'get-images.php',
15403 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15406 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15407 * JsonReader and HttpProxy (unless inline data is provided).</b>
15408 * @cfg {Array} fields An array of field definition objects, or field name strings.
15410 * @param {Object} config
15412 Roo.data.JsonStore = function(c){
15413 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15414 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15415 reader: new Roo.data.JsonReader(c, c.fields)
15418 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15420 * Ext JS Library 1.1.1
15421 * Copyright(c) 2006-2007, Ext JS, LLC.
15423 * Originally Released Under LGPL - original licence link has changed is not relivant.
15426 * <script type="text/javascript">
15430 Roo.data.Field = function(config){
15431 if(typeof config == "string"){
15432 config = {name: config};
15434 Roo.apply(this, config);
15437 this.type = "auto";
15440 var st = Roo.data.SortTypes;
15441 // named sortTypes are supported, here we look them up
15442 if(typeof this.sortType == "string"){
15443 this.sortType = st[this.sortType];
15446 // set default sortType for strings and dates
15447 if(!this.sortType){
15450 this.sortType = st.asUCString;
15453 this.sortType = st.asDate;
15456 this.sortType = st.none;
15461 var stripRe = /[\$,%]/g;
15463 // prebuilt conversion function for this field, instead of
15464 // switching every time we're reading a value
15466 var cv, dateFormat = this.dateFormat;
15471 cv = function(v){ return v; };
15474 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15478 return v !== undefined && v !== null && v !== '' ?
15479 parseInt(String(v).replace(stripRe, ""), 10) : '';
15484 return v !== undefined && v !== null && v !== '' ?
15485 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15490 cv = function(v){ return v === true || v === "true" || v == 1; };
15497 if(v instanceof Date){
15501 if(dateFormat == "timestamp"){
15502 return new Date(v*1000);
15504 return Date.parseDate(v, dateFormat);
15506 var parsed = Date.parse(v);
15507 return parsed ? new Date(parsed) : null;
15516 Roo.data.Field.prototype = {
15524 * Ext JS Library 1.1.1
15525 * Copyright(c) 2006-2007, Ext JS, LLC.
15527 * Originally Released Under LGPL - original licence link has changed is not relivant.
15530 * <script type="text/javascript">
15533 // Base class for reading structured data from a data source. This class is intended to be
15534 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15537 * @class Roo.data.DataReader
15539 * Base class for reading structured data from a data source. This class is intended to be
15540 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15543 Roo.data.DataReader = function(meta, recordType){
15547 this.recordType = recordType instanceof Array ?
15548 Roo.data.Record.create(recordType) : recordType;
15551 Roo.data.DataReader.prototype = {
15554 readerType : 'Data',
15556 * Create an empty record
15557 * @param {Object} data (optional) - overlay some values
15558 * @return {Roo.data.Record} record created.
15560 newRow : function(d) {
15562 this.recordType.prototype.fields.each(function(c) {
15564 case 'int' : da[c.name] = 0; break;
15565 case 'date' : da[c.name] = new Date(); break;
15566 case 'float' : da[c.name] = 0.0; break;
15567 case 'boolean' : da[c.name] = false; break;
15568 default : da[c.name] = ""; break;
15572 return new this.recordType(Roo.apply(da, d));
15578 * Ext JS Library 1.1.1
15579 * Copyright(c) 2006-2007, Ext JS, LLC.
15581 * Originally Released Under LGPL - original licence link has changed is not relivant.
15584 * <script type="text/javascript">
15588 * @class Roo.data.DataProxy
15589 * @extends Roo.data.Observable
15591 * This class is an abstract base class for implementations which provide retrieval of
15592 * unformatted data objects.<br>
15594 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15595 * (of the appropriate type which knows how to parse the data object) to provide a block of
15596 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15598 * Custom implementations must implement the load method as described in
15599 * {@link Roo.data.HttpProxy#load}.
15601 Roo.data.DataProxy = function(){
15604 * @event beforeload
15605 * Fires before a network request is made to retrieve a data object.
15606 * @param {Object} This DataProxy object.
15607 * @param {Object} params The params parameter to the load function.
15612 * Fires before the load method's callback is called.
15613 * @param {Object} This DataProxy object.
15614 * @param {Object} o The data object.
15615 * @param {Object} arg The callback argument object passed to the load function.
15619 * @event loadexception
15620 * Fires if an Exception occurs during data retrieval.
15621 * @param {Object} This DataProxy object.
15622 * @param {Object} o The data object.
15623 * @param {Object} arg The callback argument object passed to the load function.
15624 * @param {Object} e The Exception.
15626 loadexception : true
15628 Roo.data.DataProxy.superclass.constructor.call(this);
15631 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15634 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15638 * Ext JS Library 1.1.1
15639 * Copyright(c) 2006-2007, Ext JS, LLC.
15641 * Originally Released Under LGPL - original licence link has changed is not relivant.
15644 * <script type="text/javascript">
15647 * @class Roo.data.MemoryProxy
15648 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15649 * to the Reader when its load method is called.
15651 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15653 Roo.data.MemoryProxy = function(data){
15657 Roo.data.MemoryProxy.superclass.constructor.call(this);
15661 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15664 * Load data from the requested source (in this case an in-memory
15665 * data object passed to the constructor), read the data object into
15666 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15667 * process that block using the passed callback.
15668 * @param {Object} params This parameter is not used by the MemoryProxy class.
15669 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15670 * object into a block of Roo.data.Records.
15671 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15672 * The function must be passed <ul>
15673 * <li>The Record block object</li>
15674 * <li>The "arg" argument from the load function</li>
15675 * <li>A boolean success indicator</li>
15677 * @param {Object} scope The scope in which to call the callback
15678 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15680 load : function(params, reader, callback, scope, arg){
15681 params = params || {};
15684 result = reader.readRecords(params.data ? params.data :this.data);
15686 this.fireEvent("loadexception", this, arg, null, e);
15687 callback.call(scope, null, arg, false);
15690 callback.call(scope, result, arg, true);
15694 update : function(params, records){
15699 * Ext JS Library 1.1.1
15700 * Copyright(c) 2006-2007, Ext JS, LLC.
15702 * Originally Released Under LGPL - original licence link has changed is not relivant.
15705 * <script type="text/javascript">
15708 * @class Roo.data.HttpProxy
15709 * @extends Roo.data.DataProxy
15710 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15711 * configured to reference a certain URL.<br><br>
15713 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15714 * from which the running page was served.<br><br>
15716 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15718 * Be aware that to enable the browser to parse an XML document, the server must set
15719 * the Content-Type header in the HTTP response to "text/xml".
15721 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15722 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15723 * will be used to make the request.
15725 Roo.data.HttpProxy = function(conn){
15726 Roo.data.HttpProxy.superclass.constructor.call(this);
15727 // is conn a conn config or a real conn?
15729 this.useAjax = !conn || !conn.events;
15733 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15734 // thse are take from connection...
15737 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15740 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15741 * extra parameters to each request made by this object. (defaults to undefined)
15744 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15745 * to each request made by this object. (defaults to undefined)
15748 * @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)
15751 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15754 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15760 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15764 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15765 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15766 * a finer-grained basis than the DataProxy events.
15768 getConnection : function(){
15769 return this.useAjax ? Roo.Ajax : this.conn;
15773 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15774 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15775 * process that block using the passed callback.
15776 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15777 * for the request to the remote server.
15778 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15779 * object into a block of Roo.data.Records.
15780 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15781 * The function must be passed <ul>
15782 * <li>The Record block object</li>
15783 * <li>The "arg" argument from the load function</li>
15784 * <li>A boolean success indicator</li>
15786 * @param {Object} scope The scope in which to call the callback
15787 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15789 load : function(params, reader, callback, scope, arg){
15790 if(this.fireEvent("beforeload", this, params) !== false){
15792 params : params || {},
15794 callback : callback,
15799 callback : this.loadResponse,
15803 Roo.applyIf(o, this.conn);
15804 if(this.activeRequest){
15805 Roo.Ajax.abort(this.activeRequest);
15807 this.activeRequest = Roo.Ajax.request(o);
15809 this.conn.request(o);
15812 callback.call(scope||this, null, arg, false);
15817 loadResponse : function(o, success, response){
15818 delete this.activeRequest;
15820 this.fireEvent("loadexception", this, o, response);
15821 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15826 result = o.reader.read(response);
15828 this.fireEvent("loadexception", this, o, response, e);
15829 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15833 this.fireEvent("load", this, o, o.request.arg);
15834 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15838 update : function(dataSet){
15843 updateResponse : function(dataSet){
15848 * Ext JS Library 1.1.1
15849 * Copyright(c) 2006-2007, Ext JS, LLC.
15851 * Originally Released Under LGPL - original licence link has changed is not relivant.
15854 * <script type="text/javascript">
15858 * @class Roo.data.ScriptTagProxy
15859 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15860 * other than the originating domain of the running page.<br><br>
15862 * <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
15863 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15865 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15866 * source code that is used as the source inside a <script> tag.<br><br>
15868 * In order for the browser to process the returned data, the server must wrap the data object
15869 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15870 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15871 * depending on whether the callback name was passed:
15874 boolean scriptTag = false;
15875 String cb = request.getParameter("callback");
15878 response.setContentType("text/javascript");
15880 response.setContentType("application/x-json");
15882 Writer out = response.getWriter();
15884 out.write(cb + "(");
15886 out.print(dataBlock.toJsonString());
15893 * @param {Object} config A configuration object.
15895 Roo.data.ScriptTagProxy = function(config){
15896 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15897 Roo.apply(this, config);
15898 this.head = document.getElementsByTagName("head")[0];
15901 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15903 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15905 * @cfg {String} url The URL from which to request the data object.
15908 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15912 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15913 * the server the name of the callback function set up by the load call to process the returned data object.
15914 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15915 * javascript output which calls this named function passing the data object as its only parameter.
15917 callbackParam : "callback",
15919 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15920 * name to the request.
15925 * Load data from the configured URL, read the data object into
15926 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15927 * process that block using the passed callback.
15928 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15929 * for the request to the remote server.
15930 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15931 * object into a block of Roo.data.Records.
15932 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15933 * The function must be passed <ul>
15934 * <li>The Record block object</li>
15935 * <li>The "arg" argument from the load function</li>
15936 * <li>A boolean success indicator</li>
15938 * @param {Object} scope The scope in which to call the callback
15939 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15941 load : function(params, reader, callback, scope, arg){
15942 if(this.fireEvent("beforeload", this, params) !== false){
15944 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15946 var url = this.url;
15947 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15949 url += "&_dc=" + (new Date().getTime());
15951 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15954 cb : "stcCallback"+transId,
15955 scriptId : "stcScript"+transId,
15959 callback : callback,
15965 window[trans.cb] = function(o){
15966 conn.handleResponse(o, trans);
15969 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15971 if(this.autoAbort !== false){
15975 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15977 var script = document.createElement("script");
15978 script.setAttribute("src", url);
15979 script.setAttribute("type", "text/javascript");
15980 script.setAttribute("id", trans.scriptId);
15981 this.head.appendChild(script);
15983 this.trans = trans;
15985 callback.call(scope||this, null, arg, false);
15990 isLoading : function(){
15991 return this.trans ? true : false;
15995 * Abort the current server request.
15997 abort : function(){
15998 if(this.isLoading()){
15999 this.destroyTrans(this.trans);
16004 destroyTrans : function(trans, isLoaded){
16005 this.head.removeChild(document.getElementById(trans.scriptId));
16006 clearTimeout(trans.timeoutId);
16008 window[trans.cb] = undefined;
16010 delete window[trans.cb];
16013 // if hasn't been loaded, wait for load to remove it to prevent script error
16014 window[trans.cb] = function(){
16015 window[trans.cb] = undefined;
16017 delete window[trans.cb];
16024 handleResponse : function(o, trans){
16025 this.trans = false;
16026 this.destroyTrans(trans, true);
16029 result = trans.reader.readRecords(o);
16031 this.fireEvent("loadexception", this, o, trans.arg, e);
16032 trans.callback.call(trans.scope||window, null, trans.arg, false);
16035 this.fireEvent("load", this, o, trans.arg);
16036 trans.callback.call(trans.scope||window, result, trans.arg, true);
16040 handleFailure : function(trans){
16041 this.trans = false;
16042 this.destroyTrans(trans, false);
16043 this.fireEvent("loadexception", this, null, trans.arg);
16044 trans.callback.call(trans.scope||window, null, trans.arg, false);
16048 * Ext JS Library 1.1.1
16049 * Copyright(c) 2006-2007, Ext JS, LLC.
16051 * Originally Released Under LGPL - original licence link has changed is not relivant.
16054 * <script type="text/javascript">
16058 * @class Roo.data.JsonReader
16059 * @extends Roo.data.DataReader
16060 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16061 * based on mappings in a provided Roo.data.Record constructor.
16063 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16064 * in the reply previously.
16069 var RecordDef = Roo.data.Record.create([
16070 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16071 {name: 'occupation'} // This field will use "occupation" as the mapping.
16073 var myReader = new Roo.data.JsonReader({
16074 totalProperty: "results", // The property which contains the total dataset size (optional)
16075 root: "rows", // The property which contains an Array of row objects
16076 id: "id" // The property within each row object that provides an ID for the record (optional)
16080 * This would consume a JSON file like this:
16082 { 'results': 2, 'rows': [
16083 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16084 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16087 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16088 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16089 * paged from the remote server.
16090 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16091 * @cfg {String} root name of the property which contains the Array of row objects.
16092 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16093 * @cfg {Array} fields Array of field definition objects
16095 * Create a new JsonReader
16096 * @param {Object} meta Metadata configuration options
16097 * @param {Object} recordType Either an Array of field definition objects,
16098 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16100 Roo.data.JsonReader = function(meta, recordType){
16103 // set some defaults:
16104 Roo.applyIf(meta, {
16105 totalProperty: 'total',
16106 successProperty : 'success',
16111 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16113 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16115 readerType : 'Json',
16118 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16119 * Used by Store query builder to append _requestMeta to params.
16122 metaFromRemote : false,
16124 * This method is only used by a DataProxy which has retrieved data from a remote server.
16125 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16126 * @return {Object} data A data block which is used by an Roo.data.Store object as
16127 * a cache of Roo.data.Records.
16129 read : function(response){
16130 var json = response.responseText;
16132 var o = /* eval:var:o */ eval("("+json+")");
16134 throw {message: "JsonReader.read: Json object not found"};
16140 this.metaFromRemote = true;
16141 this.meta = o.metaData;
16142 this.recordType = Roo.data.Record.create(o.metaData.fields);
16143 this.onMetaChange(this.meta, this.recordType, o);
16145 return this.readRecords(o);
16148 // private function a store will implement
16149 onMetaChange : function(meta, recordType, o){
16156 simpleAccess: function(obj, subsc) {
16163 getJsonAccessor: function(){
16165 return function(expr) {
16167 return(re.test(expr))
16168 ? new Function("obj", "return obj." + expr)
16173 return Roo.emptyFn;
16178 * Create a data block containing Roo.data.Records from an XML document.
16179 * @param {Object} o An object which contains an Array of row objects in the property specified
16180 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16181 * which contains the total size of the dataset.
16182 * @return {Object} data A data block which is used by an Roo.data.Store object as
16183 * a cache of Roo.data.Records.
16185 readRecords : function(o){
16187 * After any data loads, the raw JSON data is available for further custom processing.
16191 var s = this.meta, Record = this.recordType,
16192 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16194 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16196 if(s.totalProperty) {
16197 this.getTotal = this.getJsonAccessor(s.totalProperty);
16199 if(s.successProperty) {
16200 this.getSuccess = this.getJsonAccessor(s.successProperty);
16202 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16204 var g = this.getJsonAccessor(s.id);
16205 this.getId = function(rec) {
16207 return (r === undefined || r === "") ? null : r;
16210 this.getId = function(){return null;};
16213 for(var jj = 0; jj < fl; jj++){
16215 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16216 this.ef[jj] = this.getJsonAccessor(map);
16220 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16221 if(s.totalProperty){
16222 var vt = parseInt(this.getTotal(o), 10);
16227 if(s.successProperty){
16228 var vs = this.getSuccess(o);
16229 if(vs === false || vs === 'false'){
16234 for(var i = 0; i < c; i++){
16237 var id = this.getId(n);
16238 for(var j = 0; j < fl; j++){
16240 var v = this.ef[j](n);
16242 Roo.log('missing convert for ' + f.name);
16246 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16248 var record = new Record(values, id);
16250 records[i] = record;
16256 totalRecords : totalRecords
16259 // used when loading children.. @see loadDataFromChildren
16260 toLoadData: function(rec)
16262 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16263 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16264 return { data : data, total : data.length };
16269 * Ext JS Library 1.1.1
16270 * Copyright(c) 2006-2007, Ext JS, LLC.
16272 * Originally Released Under LGPL - original licence link has changed is not relivant.
16275 * <script type="text/javascript">
16279 * @class Roo.data.ArrayReader
16280 * @extends Roo.data.DataReader
16281 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16282 * Each element of that Array represents a row of data fields. The
16283 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16284 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16288 var RecordDef = Roo.data.Record.create([
16289 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16290 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16292 var myReader = new Roo.data.ArrayReader({
16293 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16297 * This would consume an Array like this:
16299 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16303 * Create a new JsonReader
16304 * @param {Object} meta Metadata configuration options.
16305 * @param {Object|Array} recordType Either an Array of field definition objects
16307 * @cfg {Array} fields Array of field definition objects
16308 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16309 * as specified to {@link Roo.data.Record#create},
16310 * or an {@link Roo.data.Record} object
16313 * created using {@link Roo.data.Record#create}.
16315 Roo.data.ArrayReader = function(meta, recordType)
16317 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16320 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16323 * Create a data block containing Roo.data.Records from an XML document.
16324 * @param {Object} o An Array of row objects which represents the dataset.
16325 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16326 * a cache of Roo.data.Records.
16328 readRecords : function(o)
16330 var sid = this.meta ? this.meta.id : null;
16331 var recordType = this.recordType, fields = recordType.prototype.fields;
16334 for(var i = 0; i < root.length; i++){
16337 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16338 for(var j = 0, jlen = fields.length; j < jlen; j++){
16339 var f = fields.items[j];
16340 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16341 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16343 values[f.name] = v;
16345 var record = new recordType(values, id);
16347 records[records.length] = record;
16351 totalRecords : records.length
16354 // used when loading children.. @see loadDataFromChildren
16355 toLoadData: function(rec)
16357 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16358 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16369 * @class Roo.bootstrap.ComboBox
16370 * @extends Roo.bootstrap.TriggerField
16371 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16372 * @cfg {Boolean} append (true|false) default false
16373 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16374 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16375 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16376 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16377 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16378 * @cfg {Boolean} animate default true
16379 * @cfg {Boolean} emptyResultText only for touch device
16380 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16381 * @cfg {String} emptyTitle default ''
16382 * @cfg {Number} width fixed with? experimental
16384 * Create a new ComboBox.
16385 * @param {Object} config Configuration options
16387 Roo.bootstrap.ComboBox = function(config){
16388 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16392 * Fires when the dropdown list is expanded
16393 * @param {Roo.bootstrap.ComboBox} combo This combo box
16398 * Fires when the dropdown list is collapsed
16399 * @param {Roo.bootstrap.ComboBox} combo This combo box
16403 * @event beforeselect
16404 * Fires before a list item is selected. Return false to cancel the selection.
16405 * @param {Roo.bootstrap.ComboBox} combo This combo box
16406 * @param {Roo.data.Record} record The data record returned from the underlying store
16407 * @param {Number} index The index of the selected item in the dropdown list
16409 'beforeselect' : true,
16412 * Fires when a list item is selected
16413 * @param {Roo.bootstrap.ComboBox} combo This combo box
16414 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16415 * @param {Number} index The index of the selected item in the dropdown list
16419 * @event beforequery
16420 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16421 * The event object passed has these properties:
16422 * @param {Roo.bootstrap.ComboBox} combo This combo box
16423 * @param {String} query The query
16424 * @param {Boolean} forceAll true to force "all" query
16425 * @param {Boolean} cancel true to cancel the query
16426 * @param {Object} e The query event object
16428 'beforequery': true,
16431 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16432 * @param {Roo.bootstrap.ComboBox} combo This combo box
16437 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16438 * @param {Roo.bootstrap.ComboBox} combo This combo box
16439 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16444 * Fires when the remove value from the combobox array
16445 * @param {Roo.bootstrap.ComboBox} combo This combo box
16449 * @event afterremove
16450 * Fires when the remove value from the combobox array
16451 * @param {Roo.bootstrap.ComboBox} combo This combo box
16453 'afterremove' : true,
16455 * @event specialfilter
16456 * Fires when specialfilter
16457 * @param {Roo.bootstrap.ComboBox} combo This combo box
16459 'specialfilter' : true,
16462 * Fires when tick the element
16463 * @param {Roo.bootstrap.ComboBox} combo This combo box
16467 * @event touchviewdisplay
16468 * Fires when touch view require special display (default is using displayField)
16469 * @param {Roo.bootstrap.ComboBox} combo This combo box
16470 * @param {Object} cfg set html .
16472 'touchviewdisplay' : true
16477 this.tickItems = [];
16479 this.selectedIndex = -1;
16480 if(this.mode == 'local'){
16481 if(config.queryDelay === undefined){
16482 this.queryDelay = 10;
16484 if(config.minChars === undefined){
16490 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16493 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16494 * rendering into an Roo.Editor, defaults to false)
16497 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16498 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16501 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16504 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16505 * the dropdown list (defaults to undefined, with no header element)
16509 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16513 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16515 listWidth: undefined,
16517 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16518 * mode = 'remote' or 'text' if mode = 'local')
16520 displayField: undefined,
16523 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16524 * mode = 'remote' or 'value' if mode = 'local').
16525 * Note: use of a valueField requires the user make a selection
16526 * in order for a value to be mapped.
16528 valueField: undefined,
16530 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16535 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16536 * field's data value (defaults to the underlying DOM element's name)
16538 hiddenName: undefined,
16540 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16544 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16546 selectedClass: 'active',
16549 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16553 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16554 * anchor positions (defaults to 'tl-bl')
16556 listAlign: 'tl-bl?',
16558 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16562 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16563 * query specified by the allQuery config option (defaults to 'query')
16565 triggerAction: 'query',
16567 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16568 * (defaults to 4, does not apply if editable = false)
16572 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16573 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16577 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16578 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16582 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16583 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16587 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16588 * when editable = true (defaults to false)
16590 selectOnFocus:false,
16592 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16594 queryParam: 'query',
16596 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16597 * when mode = 'remote' (defaults to 'Loading...')
16599 loadingText: 'Loading...',
16601 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16605 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16609 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16610 * traditional select (defaults to true)
16614 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16618 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16622 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16623 * listWidth has a higher value)
16627 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16628 * allow the user to set arbitrary text into the field (defaults to false)
16630 forceSelection:false,
16632 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16633 * if typeAhead = true (defaults to 250)
16635 typeAheadDelay : 250,
16637 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16638 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16640 valueNotFoundText : undefined,
16642 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16644 blockFocus : false,
16647 * @cfg {Boolean} disableClear Disable showing of clear button.
16649 disableClear : false,
16651 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16653 alwaysQuery : false,
16656 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16661 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16663 invalidClass : "has-warning",
16666 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16668 validClass : "has-success",
16671 * @cfg {Boolean} specialFilter (true|false) special filter default false
16673 specialFilter : false,
16676 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16678 mobileTouchView : true,
16681 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16683 useNativeIOS : false,
16686 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16688 mobile_restrict_height : false,
16690 ios_options : false,
16702 btnPosition : 'right',
16703 triggerList : true,
16704 showToggleBtn : true,
16706 emptyResultText: 'Empty',
16707 triggerText : 'Select',
16711 // element that contains real text value.. (when hidden is used..)
16713 getAutoCreate : function()
16718 * Render classic select for iso
16721 if(Roo.isIOS && this.useNativeIOS){
16722 cfg = this.getAutoCreateNativeIOS();
16730 if(Roo.isTouch && this.mobileTouchView){
16731 cfg = this.getAutoCreateTouchView();
16738 if(!this.tickable){
16739 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16744 * ComboBox with tickable selections
16747 var align = this.labelAlign || this.parentLabelAlign();
16750 cls : 'form-group roo-combobox-tickable' //input-group
16753 var btn_text_select = '';
16754 var btn_text_done = '';
16755 var btn_text_cancel = '';
16757 if (this.btn_text_show) {
16758 btn_text_select = 'Select';
16759 btn_text_done = 'Done';
16760 btn_text_cancel = 'Cancel';
16765 cls : 'tickable-buttons',
16770 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16771 //html : this.triggerText
16772 html: btn_text_select
16778 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16780 html: btn_text_done
16786 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16788 html: btn_text_cancel
16794 buttons.cn.unshift({
16796 cls: 'roo-select2-search-field-input'
16802 Roo.each(buttons.cn, function(c){
16804 c.cls += ' btn-' + _this.size;
16807 if (_this.disabled) {
16814 style : 'display: contents',
16819 cls: 'form-hidden-field'
16823 cls: 'roo-select2-choices',
16827 cls: 'roo-select2-search-field',
16838 cls: 'roo-select2-container input-group roo-select2-container-multi',
16844 // cls: 'typeahead typeahead-long dropdown-menu',
16845 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16850 if(this.hasFeedback && !this.allowBlank){
16854 cls: 'glyphicon form-control-feedback'
16857 combobox.cn.push(feedback);
16864 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16865 tooltip : 'This field is required'
16867 if (Roo.bootstrap.version == 4) {
16870 style : 'display:none'
16873 if (align ==='left' && this.fieldLabel.length) {
16875 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16882 cls : 'control-label col-form-label',
16883 html : this.fieldLabel
16895 var labelCfg = cfg.cn[1];
16896 var contentCfg = cfg.cn[2];
16899 if(this.indicatorpos == 'right'){
16905 cls : 'control-label col-form-label',
16909 html : this.fieldLabel
16925 labelCfg = cfg.cn[0];
16926 contentCfg = cfg.cn[1];
16930 if(this.labelWidth > 12){
16931 labelCfg.style = "width: " + this.labelWidth + 'px';
16933 if(this.width * 1 > 0){
16934 contentCfg.style = "width: " + this.width + 'px';
16936 if(this.labelWidth < 13 && this.labelmd == 0){
16937 this.labelmd = this.labelWidth;
16940 if(this.labellg > 0){
16941 labelCfg.cls += ' col-lg-' + this.labellg;
16942 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16945 if(this.labelmd > 0){
16946 labelCfg.cls += ' col-md-' + this.labelmd;
16947 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16950 if(this.labelsm > 0){
16951 labelCfg.cls += ' col-sm-' + this.labelsm;
16952 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16955 if(this.labelxs > 0){
16956 labelCfg.cls += ' col-xs-' + this.labelxs;
16957 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16961 } else if ( this.fieldLabel.length) {
16962 // Roo.log(" label");
16967 //cls : 'input-group-addon',
16968 html : this.fieldLabel
16973 if(this.indicatorpos == 'right'){
16977 //cls : 'input-group-addon',
16978 html : this.fieldLabel
16988 // Roo.log(" no label && no align");
16995 ['xs','sm','md','lg'].map(function(size){
16996 if (settings[size]) {
16997 cfg.cls += ' col-' + size + '-' + settings[size];
17005 _initEventsCalled : false,
17008 initEvents: function()
17010 if (this._initEventsCalled) { // as we call render... prevent looping...
17013 this._initEventsCalled = true;
17016 throw "can not find store for combo";
17019 this.indicator = this.indicatorEl();
17021 this.store = Roo.factory(this.store, Roo.data);
17022 this.store.parent = this;
17024 // if we are building from html. then this element is so complex, that we can not really
17025 // use the rendered HTML.
17026 // so we have to trash and replace the previous code.
17027 if (Roo.XComponent.build_from_html) {
17028 // remove this element....
17029 var e = this.el.dom, k=0;
17030 while (e ) { e = e.previousSibling; ++k;}
17035 this.rendered = false;
17037 this.render(this.parent().getChildContainer(true), k);
17040 if(Roo.isIOS && this.useNativeIOS){
17041 this.initIOSView();
17049 if(Roo.isTouch && this.mobileTouchView){
17050 this.initTouchView();
17055 this.initTickableEvents();
17059 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17061 if(this.hiddenName){
17063 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17065 this.hiddenField.dom.value =
17066 this.hiddenValue !== undefined ? this.hiddenValue :
17067 this.value !== undefined ? this.value : '';
17069 // prevent input submission
17070 this.el.dom.removeAttribute('name');
17071 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17076 // this.el.dom.setAttribute('autocomplete', 'off');
17079 var cls = 'x-combo-list';
17081 //this.list = new Roo.Layer({
17082 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17088 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17089 _this.list.setWidth(lw);
17092 this.list.on('mouseover', this.onViewOver, this);
17093 this.list.on('mousemove', this.onViewMove, this);
17094 this.list.on('scroll', this.onViewScroll, this);
17097 this.list.swallowEvent('mousewheel');
17098 this.assetHeight = 0;
17101 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17102 this.assetHeight += this.header.getHeight();
17105 this.innerList = this.list.createChild({cls:cls+'-inner'});
17106 this.innerList.on('mouseover', this.onViewOver, this);
17107 this.innerList.on('mousemove', this.onViewMove, this);
17108 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17110 if(this.allowBlank && !this.pageSize && !this.disableClear){
17111 this.footer = this.list.createChild({cls:cls+'-ft'});
17112 this.pageTb = new Roo.Toolbar(this.footer);
17116 this.footer = this.list.createChild({cls:cls+'-ft'});
17117 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17118 {pageSize: this.pageSize});
17122 if (this.pageTb && this.allowBlank && !this.disableClear) {
17124 this.pageTb.add(new Roo.Toolbar.Fill(), {
17125 cls: 'x-btn-icon x-btn-clear',
17127 handler: function()
17130 _this.clearValue();
17131 _this.onSelect(false, -1);
17136 this.assetHeight += this.footer.getHeight();
17141 this.tpl = Roo.bootstrap.version == 4 ?
17142 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17143 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17146 this.view = new Roo.View(this.list, this.tpl, {
17147 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17149 //this.view.wrapEl.setDisplayed(false);
17150 this.view.on('click', this.onViewClick, this);
17153 this.store.on('beforeload', this.onBeforeLoad, this);
17154 this.store.on('load', this.onLoad, this);
17155 this.store.on('loadexception', this.onLoadException, this);
17157 if(this.resizable){
17158 this.resizer = new Roo.Resizable(this.list, {
17159 pinned:true, handles:'se'
17161 this.resizer.on('resize', function(r, w, h){
17162 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17163 this.listWidth = w;
17164 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17165 this.restrictHeight();
17167 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17170 if(!this.editable){
17171 this.editable = true;
17172 this.setEditable(false);
17177 if (typeof(this.events.add.listeners) != 'undefined') {
17179 this.addicon = this.wrap.createChild(
17180 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17182 this.addicon.on('click', function(e) {
17183 this.fireEvent('add', this);
17186 if (typeof(this.events.edit.listeners) != 'undefined') {
17188 this.editicon = this.wrap.createChild(
17189 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17190 if (this.addicon) {
17191 this.editicon.setStyle('margin-left', '40px');
17193 this.editicon.on('click', function(e) {
17195 // we fire even if inothing is selected..
17196 this.fireEvent('edit', this, this.lastData );
17202 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17203 "up" : function(e){
17204 this.inKeyMode = true;
17208 "down" : function(e){
17209 if(!this.isExpanded()){
17210 this.onTriggerClick();
17212 this.inKeyMode = true;
17217 "enter" : function(e){
17218 // this.onViewClick();
17222 if(this.fireEvent("specialkey", this, e)){
17223 this.onViewClick(false);
17229 "esc" : function(e){
17233 "tab" : function(e){
17236 if(this.fireEvent("specialkey", this, e)){
17237 this.onViewClick(false);
17245 doRelay : function(foo, bar, hname){
17246 if(hname == 'down' || this.scope.isExpanded()){
17247 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17256 this.queryDelay = Math.max(this.queryDelay || 10,
17257 this.mode == 'local' ? 10 : 250);
17260 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17262 if(this.typeAhead){
17263 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17265 if(this.editable !== false){
17266 this.inputEl().on("keyup", this.onKeyUp, this);
17268 if(this.forceSelection){
17269 this.inputEl().on('blur', this.doForce, this);
17273 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17274 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17278 initTickableEvents: function()
17282 if(this.hiddenName){
17284 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17286 this.hiddenField.dom.value =
17287 this.hiddenValue !== undefined ? this.hiddenValue :
17288 this.value !== undefined ? this.value : '';
17290 // prevent input submission
17291 this.el.dom.removeAttribute('name');
17292 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17297 // this.list = this.el.select('ul.dropdown-menu',true).first();
17299 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17300 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17301 if(this.triggerList){
17302 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17305 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17306 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17308 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17309 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17311 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17312 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17314 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17315 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17316 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17319 this.cancelBtn.hide();
17324 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17325 _this.list.setWidth(lw);
17328 this.list.on('mouseover', this.onViewOver, this);
17329 this.list.on('mousemove', this.onViewMove, this);
17331 this.list.on('scroll', this.onViewScroll, this);
17334 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17335 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17338 this.view = new Roo.View(this.list, this.tpl, {
17343 selectedClass: this.selectedClass
17346 //this.view.wrapEl.setDisplayed(false);
17347 this.view.on('click', this.onViewClick, this);
17351 this.store.on('beforeload', this.onBeforeLoad, this);
17352 this.store.on('load', this.onLoad, this);
17353 this.store.on('loadexception', this.onLoadException, this);
17356 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17357 "up" : function(e){
17358 this.inKeyMode = true;
17362 "down" : function(e){
17363 this.inKeyMode = true;
17367 "enter" : function(e){
17368 if(this.fireEvent("specialkey", this, e)){
17369 this.onViewClick(false);
17375 "esc" : function(e){
17376 this.onTickableFooterButtonClick(e, false, false);
17379 "tab" : function(e){
17380 this.fireEvent("specialkey", this, e);
17382 this.onTickableFooterButtonClick(e, false, false);
17389 doRelay : function(e, fn, key){
17390 if(this.scope.isExpanded()){
17391 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17400 this.queryDelay = Math.max(this.queryDelay || 10,
17401 this.mode == 'local' ? 10 : 250);
17404 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17406 if(this.typeAhead){
17407 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17410 if(this.editable !== false){
17411 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17414 this.indicator = this.indicatorEl();
17416 if(this.indicator){
17417 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17418 this.indicator.hide();
17423 onDestroy : function(){
17425 this.view.setStore(null);
17426 this.view.el.removeAllListeners();
17427 this.view.el.remove();
17428 this.view.purgeListeners();
17431 this.list.dom.innerHTML = '';
17435 this.store.un('beforeload', this.onBeforeLoad, this);
17436 this.store.un('load', this.onLoad, this);
17437 this.store.un('loadexception', this.onLoadException, this);
17439 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17443 fireKey : function(e){
17444 if(e.isNavKeyPress() && !this.list.isVisible()){
17445 this.fireEvent("specialkey", this, e);
17450 onResize: function(w, h)
17454 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17456 // if(typeof w != 'number'){
17457 // // we do not handle it!?!?
17460 // var tw = this.trigger.getWidth();
17461 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17462 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17464 // this.inputEl().setWidth( this.adjustWidth('input', x));
17466 // //this.trigger.setStyle('left', x+'px');
17468 // if(this.list && this.listWidth === undefined){
17469 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17470 // this.list.setWidth(lw);
17471 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17479 * Allow or prevent the user from directly editing the field text. If false is passed,
17480 * the user will only be able to select from the items defined in the dropdown list. This method
17481 * is the runtime equivalent of setting the 'editable' config option at config time.
17482 * @param {Boolean} value True to allow the user to directly edit the field text
17484 setEditable : function(value){
17485 if(value == this.editable){
17488 this.editable = value;
17490 this.inputEl().dom.setAttribute('readOnly', true);
17491 this.inputEl().on('mousedown', this.onTriggerClick, this);
17492 this.inputEl().addClass('x-combo-noedit');
17494 this.inputEl().dom.removeAttribute('readOnly');
17495 this.inputEl().un('mousedown', this.onTriggerClick, this);
17496 this.inputEl().removeClass('x-combo-noedit');
17502 onBeforeLoad : function(combo,opts){
17503 if(!this.hasFocus){
17507 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17509 this.restrictHeight();
17510 this.selectedIndex = -1;
17514 onLoad : function(){
17516 this.hasQuery = false;
17518 if(!this.hasFocus){
17522 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17523 this.loading.hide();
17526 if(this.store.getCount() > 0){
17529 this.restrictHeight();
17530 if(this.lastQuery == this.allQuery){
17531 if(this.editable && !this.tickable){
17532 this.inputEl().dom.select();
17536 !this.selectByValue(this.value, true) &&
17539 !this.store.lastOptions ||
17540 typeof(this.store.lastOptions.add) == 'undefined' ||
17541 this.store.lastOptions.add != true
17544 this.select(0, true);
17547 if(this.autoFocus){
17550 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17551 this.taTask.delay(this.typeAheadDelay);
17555 this.onEmptyResults();
17561 onLoadException : function()
17563 this.hasQuery = false;
17565 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17566 this.loading.hide();
17569 if(this.tickable && this.editable){
17574 // only causes errors at present
17575 //Roo.log(this.store.reader.jsonData);
17576 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17578 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17584 onTypeAhead : function(){
17585 if(this.store.getCount() > 0){
17586 var r = this.store.getAt(0);
17587 var newValue = r.data[this.displayField];
17588 var len = newValue.length;
17589 var selStart = this.getRawValue().length;
17591 if(selStart != len){
17592 this.setRawValue(newValue);
17593 this.selectText(selStart, newValue.length);
17599 onSelect : function(record, index){
17601 if(this.fireEvent('beforeselect', this, record, index) !== false){
17603 this.setFromData(index > -1 ? record.data : false);
17606 this.fireEvent('select', this, record, index);
17611 * Returns the currently selected field value or empty string if no value is set.
17612 * @return {String} value The selected value
17614 getValue : function()
17616 if(Roo.isIOS && this.useNativeIOS){
17617 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17621 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17624 if(this.valueField){
17625 return typeof this.value != 'undefined' ? this.value : '';
17627 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17631 getRawValue : function()
17633 if(Roo.isIOS && this.useNativeIOS){
17634 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17637 var v = this.inputEl().getValue();
17643 * Clears any text/value currently set in the field
17645 clearValue : function(){
17647 if(this.hiddenField){
17648 this.hiddenField.dom.value = '';
17651 this.setRawValue('');
17652 this.lastSelectionText = '';
17653 this.lastData = false;
17655 var close = this.closeTriggerEl();
17666 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17667 * will be displayed in the field. If the value does not match the data value of an existing item,
17668 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17669 * Otherwise the field will be blank (although the value will still be set).
17670 * @param {String} value The value to match
17672 setValue : function(v)
17674 if(Roo.isIOS && this.useNativeIOS){
17675 this.setIOSValue(v);
17685 if(this.valueField){
17686 var r = this.findRecord(this.valueField, v);
17688 text = r.data[this.displayField];
17689 }else if(this.valueNotFoundText !== undefined){
17690 text = this.valueNotFoundText;
17693 this.lastSelectionText = text;
17694 if(this.hiddenField){
17695 this.hiddenField.dom.value = v;
17697 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17700 var close = this.closeTriggerEl();
17703 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17709 * @property {Object} the last set data for the element
17714 * Sets the value of the field based on a object which is related to the record format for the store.
17715 * @param {Object} value the value to set as. or false on reset?
17717 setFromData : function(o){
17724 var dv = ''; // display value
17725 var vv = ''; // value value..
17727 if (this.displayField) {
17728 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17730 // this is an error condition!!!
17731 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17734 if(this.valueField){
17735 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17738 var close = this.closeTriggerEl();
17741 if(dv.length || vv * 1 > 0){
17743 this.blockFocus=true;
17749 if(this.hiddenField){
17750 this.hiddenField.dom.value = vv;
17752 this.lastSelectionText = dv;
17753 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17757 // no hidden field.. - we store the value in 'value', but still display
17758 // display field!!!!
17759 this.lastSelectionText = dv;
17760 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17767 reset : function(){
17768 // overridden so that last data is reset..
17775 this.setValue(this.originalValue);
17776 //this.clearInvalid();
17777 this.lastData = false;
17779 this.view.clearSelections();
17785 findRecord : function(prop, value){
17787 if(this.store.getCount() > 0){
17788 this.store.each(function(r){
17789 if(r.data[prop] == value){
17799 getName: function()
17801 // returns hidden if it's set..
17802 if (!this.rendered) {return ''};
17803 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17807 onViewMove : function(e, t){
17808 this.inKeyMode = false;
17812 onViewOver : function(e, t){
17813 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17816 var item = this.view.findItemFromChild(t);
17819 var index = this.view.indexOf(item);
17820 this.select(index, false);
17825 onViewClick : function(view, doFocus, el, e)
17827 var index = this.view.getSelectedIndexes()[0];
17829 var r = this.store.getAt(index);
17833 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17840 Roo.each(this.tickItems, function(v,k){
17842 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17844 _this.tickItems.splice(k, 1);
17846 if(typeof(e) == 'undefined' && view == false){
17847 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17859 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17860 this.tickItems.push(r.data);
17863 if(typeof(e) == 'undefined' && view == false){
17864 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17871 this.onSelect(r, index);
17873 if(doFocus !== false && !this.blockFocus){
17874 this.inputEl().focus();
17879 restrictHeight : function(){
17880 //this.innerList.dom.style.height = '';
17881 //var inner = this.innerList.dom;
17882 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17883 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17884 //this.list.beginUpdate();
17885 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17886 this.list.alignTo(this.inputEl(), this.listAlign);
17887 this.list.alignTo(this.inputEl(), this.listAlign);
17888 //this.list.endUpdate();
17892 onEmptyResults : function(){
17894 if(this.tickable && this.editable){
17895 this.hasFocus = false;
17896 this.restrictHeight();
17904 * Returns true if the dropdown list is expanded, else false.
17906 isExpanded : function(){
17907 return this.list.isVisible();
17911 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17912 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17913 * @param {String} value The data value of the item to select
17914 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17915 * selected item if it is not currently in view (defaults to true)
17916 * @return {Boolean} True if the value matched an item in the list, else false
17918 selectByValue : function(v, scrollIntoView){
17919 if(v !== undefined && v !== null){
17920 var r = this.findRecord(this.valueField || this.displayField, v);
17922 this.select(this.store.indexOf(r), scrollIntoView);
17930 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17931 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17932 * @param {Number} index The zero-based index of the list item to select
17933 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17934 * selected item if it is not currently in view (defaults to true)
17936 select : function(index, scrollIntoView){
17937 this.selectedIndex = index;
17938 this.view.select(index);
17939 if(scrollIntoView !== false){
17940 var el = this.view.getNode(index);
17942 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17945 this.list.scrollChildIntoView(el, false);
17951 selectNext : function(){
17952 var ct = this.store.getCount();
17954 if(this.selectedIndex == -1){
17956 }else if(this.selectedIndex < ct-1){
17957 this.select(this.selectedIndex+1);
17963 selectPrev : function(){
17964 var ct = this.store.getCount();
17966 if(this.selectedIndex == -1){
17968 }else if(this.selectedIndex != 0){
17969 this.select(this.selectedIndex-1);
17975 onKeyUp : function(e){
17976 if(this.editable !== false && !e.isSpecialKey()){
17977 this.lastKey = e.getKey();
17978 this.dqTask.delay(this.queryDelay);
17983 validateBlur : function(){
17984 return !this.list || !this.list.isVisible();
17988 initQuery : function(){
17990 var v = this.getRawValue();
17992 if(this.tickable && this.editable){
17993 v = this.tickableInputEl().getValue();
18000 doForce : function(){
18001 if(this.inputEl().dom.value.length > 0){
18002 this.inputEl().dom.value =
18003 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18009 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18010 * query allowing the query action to be canceled if needed.
18011 * @param {String} query The SQL query to execute
18012 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18013 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18014 * saved in the current store (defaults to false)
18016 doQuery : function(q, forceAll){
18018 if(q === undefined || q === null){
18023 forceAll: forceAll,
18027 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18032 forceAll = qe.forceAll;
18033 if(forceAll === true || (q.length >= this.minChars)){
18035 this.hasQuery = true;
18037 if(this.lastQuery != q || this.alwaysQuery){
18038 this.lastQuery = q;
18039 if(this.mode == 'local'){
18040 this.selectedIndex = -1;
18042 this.store.clearFilter();
18045 if(this.specialFilter){
18046 this.fireEvent('specialfilter', this);
18051 this.store.filter(this.displayField, q);
18054 this.store.fireEvent("datachanged", this.store);
18061 this.store.baseParams[this.queryParam] = q;
18063 var options = {params : this.getParams(q)};
18066 options.add = true;
18067 options.params.start = this.page * this.pageSize;
18070 this.store.load(options);
18073 * this code will make the page width larger, at the beginning, the list not align correctly,
18074 * we should expand the list on onLoad
18075 * so command out it
18080 this.selectedIndex = -1;
18085 this.loadNext = false;
18089 getParams : function(q){
18091 //p[this.queryParam] = q;
18095 p.limit = this.pageSize;
18101 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18103 collapse : function(){
18104 if(!this.isExpanded()){
18110 this.hasFocus = false;
18114 this.cancelBtn.hide();
18115 this.trigger.show();
18118 this.tickableInputEl().dom.value = '';
18119 this.tickableInputEl().blur();
18124 Roo.get(document).un('mousedown', this.collapseIf, this);
18125 Roo.get(document).un('mousewheel', this.collapseIf, this);
18126 if (!this.editable) {
18127 Roo.get(document).un('keydown', this.listKeyPress, this);
18129 this.fireEvent('collapse', this);
18135 collapseIf : function(e){
18136 var in_combo = e.within(this.el);
18137 var in_list = e.within(this.list);
18138 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18140 if (in_combo || in_list || is_list) {
18141 //e.stopPropagation();
18146 this.onTickableFooterButtonClick(e, false, false);
18154 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18156 expand : function(){
18158 if(this.isExpanded() || !this.hasFocus){
18162 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18163 this.list.setWidth(lw);
18169 this.restrictHeight();
18173 this.tickItems = Roo.apply([], this.item);
18176 this.cancelBtn.show();
18177 this.trigger.hide();
18180 this.tickableInputEl().focus();
18185 Roo.get(document).on('mousedown', this.collapseIf, this);
18186 Roo.get(document).on('mousewheel', this.collapseIf, this);
18187 if (!this.editable) {
18188 Roo.get(document).on('keydown', this.listKeyPress, this);
18191 this.fireEvent('expand', this);
18195 // Implements the default empty TriggerField.onTriggerClick function
18196 onTriggerClick : function(e)
18198 Roo.log('trigger click');
18200 if(this.disabled || !this.triggerList){
18205 this.loadNext = false;
18207 if(this.isExpanded()){
18209 if (!this.blockFocus) {
18210 this.inputEl().focus();
18214 this.hasFocus = true;
18215 if(this.triggerAction == 'all') {
18216 this.doQuery(this.allQuery, true);
18218 this.doQuery(this.getRawValue());
18220 if (!this.blockFocus) {
18221 this.inputEl().focus();
18226 onTickableTriggerClick : function(e)
18233 this.loadNext = false;
18234 this.hasFocus = true;
18236 if(this.triggerAction == 'all') {
18237 this.doQuery(this.allQuery, true);
18239 this.doQuery(this.getRawValue());
18243 onSearchFieldClick : function(e)
18245 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18246 this.onTickableFooterButtonClick(e, false, false);
18250 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18255 this.loadNext = false;
18256 this.hasFocus = true;
18258 if(this.triggerAction == 'all') {
18259 this.doQuery(this.allQuery, true);
18261 this.doQuery(this.getRawValue());
18265 listKeyPress : function(e)
18267 //Roo.log('listkeypress');
18268 // scroll to first matching element based on key pres..
18269 if (e.isSpecialKey()) {
18272 var k = String.fromCharCode(e.getKey()).toUpperCase();
18275 var csel = this.view.getSelectedNodes();
18276 var cselitem = false;
18278 var ix = this.view.indexOf(csel[0]);
18279 cselitem = this.store.getAt(ix);
18280 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18286 this.store.each(function(v) {
18288 // start at existing selection.
18289 if (cselitem.id == v.id) {
18295 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18296 match = this.store.indexOf(v);
18302 if (match === false) {
18303 return true; // no more action?
18306 this.view.select(match);
18307 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18308 sn.scrollIntoView(sn.dom.parentNode, false);
18311 onViewScroll : function(e, t){
18313 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){
18317 this.hasQuery = true;
18319 this.loading = this.list.select('.loading', true).first();
18321 if(this.loading === null){
18322 this.list.createChild({
18324 cls: 'loading roo-select2-more-results roo-select2-active',
18325 html: 'Loading more results...'
18328 this.loading = this.list.select('.loading', true).first();
18330 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18332 this.loading.hide();
18335 this.loading.show();
18340 this.loadNext = true;
18342 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18347 addItem : function(o)
18349 var dv = ''; // display value
18351 if (this.displayField) {
18352 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18354 // this is an error condition!!!
18355 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18362 var choice = this.choices.createChild({
18364 cls: 'roo-select2-search-choice',
18373 cls: 'roo-select2-search-choice-close fa fa-times',
18378 }, this.searchField);
18380 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18382 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18390 this.inputEl().dom.value = '';
18395 onRemoveItem : function(e, _self, o)
18397 e.preventDefault();
18399 this.lastItem = Roo.apply([], this.item);
18401 var index = this.item.indexOf(o.data) * 1;
18404 Roo.log('not this item?!');
18408 this.item.splice(index, 1);
18413 this.fireEvent('remove', this, e);
18419 syncValue : function()
18421 if(!this.item.length){
18428 Roo.each(this.item, function(i){
18429 if(_this.valueField){
18430 value.push(i[_this.valueField]);
18437 this.value = value.join(',');
18439 if(this.hiddenField){
18440 this.hiddenField.dom.value = this.value;
18443 this.store.fireEvent("datachanged", this.store);
18448 clearItem : function()
18450 if(!this.multiple){
18456 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18464 if(this.tickable && !Roo.isTouch){
18465 this.view.refresh();
18469 inputEl: function ()
18471 if(Roo.isIOS && this.useNativeIOS){
18472 return this.el.select('select.roo-ios-select', true).first();
18475 if(Roo.isTouch && this.mobileTouchView){
18476 return this.el.select('input.form-control',true).first();
18480 return this.searchField;
18483 return this.el.select('input.form-control',true).first();
18486 onTickableFooterButtonClick : function(e, btn, el)
18488 e.preventDefault();
18490 this.lastItem = Roo.apply([], this.item);
18492 if(btn && btn.name == 'cancel'){
18493 this.tickItems = Roo.apply([], this.item);
18502 Roo.each(this.tickItems, function(o){
18510 validate : function()
18512 if(this.getVisibilityEl().hasClass('hidden')){
18516 var v = this.getRawValue();
18519 v = this.getValue();
18522 if(this.disabled || this.allowBlank || v.length){
18527 this.markInvalid();
18531 tickableInputEl : function()
18533 if(!this.tickable || !this.editable){
18534 return this.inputEl();
18537 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18541 getAutoCreateTouchView : function()
18546 cls: 'form-group' //input-group
18552 type : this.inputType,
18553 cls : 'form-control x-combo-noedit',
18554 autocomplete: 'new-password',
18555 placeholder : this.placeholder || '',
18560 input.name = this.name;
18564 input.cls += ' input-' + this.size;
18567 if (this.disabled) {
18568 input.disabled = true;
18572 cls : 'roo-combobox-wrap',
18579 inputblock.cls += ' input-group';
18581 inputblock.cn.unshift({
18583 cls : 'input-group-addon input-group-prepend input-group-text',
18588 if(this.removable && !this.multiple){
18589 inputblock.cls += ' roo-removable';
18591 inputblock.cn.push({
18594 cls : 'roo-combo-removable-btn close'
18598 if(this.hasFeedback && !this.allowBlank){
18600 inputblock.cls += ' has-feedback';
18602 inputblock.cn.push({
18604 cls: 'glyphicon form-control-feedback'
18611 inputblock.cls += (this.before) ? '' : ' input-group';
18613 inputblock.cn.push({
18615 cls : 'input-group-addon input-group-append input-group-text',
18621 var ibwrap = inputblock;
18626 cls: 'roo-select2-choices',
18630 cls: 'roo-select2-search-field',
18643 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18648 cls: 'form-hidden-field'
18654 if(!this.multiple && this.showToggleBtn){
18660 if (this.caret != false) {
18663 cls: 'fa fa-' + this.caret
18670 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18672 Roo.bootstrap.version == 3 ? caret : '',
18675 cls: 'combobox-clear',
18689 combobox.cls += ' roo-select2-container-multi';
18692 var required = this.allowBlank ? {
18694 style: 'display: none'
18697 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18698 tooltip : 'This field is required'
18701 var align = this.labelAlign || this.parentLabelAlign();
18703 if (align ==='left' && this.fieldLabel.length) {
18709 cls : 'control-label col-form-label',
18710 html : this.fieldLabel
18714 cls : 'roo-combobox-wrap ',
18721 var labelCfg = cfg.cn[1];
18722 var contentCfg = cfg.cn[2];
18725 if(this.indicatorpos == 'right'){
18730 cls : 'control-label col-form-label',
18734 html : this.fieldLabel
18740 cls : "roo-combobox-wrap ",
18748 labelCfg = cfg.cn[0];
18749 contentCfg = cfg.cn[1];
18754 if(this.labelWidth > 12){
18755 labelCfg.style = "width: " + this.labelWidth + 'px';
18758 if(this.labelWidth < 13 && this.labelmd == 0){
18759 this.labelmd = this.labelWidth;
18762 if(this.labellg > 0){
18763 labelCfg.cls += ' col-lg-' + this.labellg;
18764 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18767 if(this.labelmd > 0){
18768 labelCfg.cls += ' col-md-' + this.labelmd;
18769 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18772 if(this.labelsm > 0){
18773 labelCfg.cls += ' col-sm-' + this.labelsm;
18774 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18777 if(this.labelxs > 0){
18778 labelCfg.cls += ' col-xs-' + this.labelxs;
18779 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18783 } else if ( this.fieldLabel.length) {
18788 cls : 'control-label',
18789 html : this.fieldLabel
18800 if(this.indicatorpos == 'right'){
18804 cls : 'control-label',
18805 html : this.fieldLabel,
18823 var settings = this;
18825 ['xs','sm','md','lg'].map(function(size){
18826 if (settings[size]) {
18827 cfg.cls += ' col-' + size + '-' + settings[size];
18834 initTouchView : function()
18836 this.renderTouchView();
18838 this.touchViewEl.on('scroll', function(){
18839 this.el.dom.scrollTop = 0;
18842 this.originalValue = this.getValue();
18844 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18846 this.inputEl().on("click", this.showTouchView, this);
18847 if (this.triggerEl) {
18848 this.triggerEl.on("click", this.showTouchView, this);
18852 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18853 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18855 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18857 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18858 this.store.on('load', this.onTouchViewLoad, this);
18859 this.store.on('loadexception', this.onTouchViewLoadException, this);
18861 if(this.hiddenName){
18863 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18865 this.hiddenField.dom.value =
18866 this.hiddenValue !== undefined ? this.hiddenValue :
18867 this.value !== undefined ? this.value : '';
18869 this.el.dom.removeAttribute('name');
18870 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18874 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18875 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18878 if(this.removable && !this.multiple){
18879 var close = this.closeTriggerEl();
18881 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18882 close.on('click', this.removeBtnClick, this, close);
18886 * fix the bug in Safari iOS8
18888 this.inputEl().on("focus", function(e){
18889 document.activeElement.blur();
18892 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18899 renderTouchView : function()
18901 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18902 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18905 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18907 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18908 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18909 this.touchViewBodyEl.setStyle('overflow', 'auto');
18911 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18912 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18914 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18915 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18919 showTouchView : function()
18925 this.touchViewHeaderEl.hide();
18927 if(this.modalTitle.length){
18928 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18929 this.touchViewHeaderEl.show();
18932 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18933 this.touchViewEl.show();
18935 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18937 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18938 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18940 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18942 if(this.modalTitle.length){
18943 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18946 this.touchViewBodyEl.setHeight(bodyHeight);
18950 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18952 this.touchViewEl.addClass(['in','show']);
18955 if(this._touchViewMask){
18956 Roo.get(document.body).addClass("x-body-masked");
18957 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18958 this._touchViewMask.setStyle('z-index', 10000);
18959 this._touchViewMask.addClass('show');
18962 this.doTouchViewQuery();
18966 hideTouchView : function()
18968 this.touchViewEl.removeClass(['in','show']);
18972 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18974 this.touchViewEl.setStyle('display', 'none');
18977 if(this._touchViewMask){
18978 this._touchViewMask.removeClass('show');
18979 Roo.get(document.body).removeClass("x-body-masked");
18983 setTouchViewValue : function()
18990 Roo.each(this.tickItems, function(o){
18995 this.hideTouchView();
18998 doTouchViewQuery : function()
19007 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19011 if(!this.alwaysQuery || this.mode == 'local'){
19012 this.onTouchViewLoad();
19019 onTouchViewBeforeLoad : function(combo,opts)
19025 onTouchViewLoad : function()
19027 if(this.store.getCount() < 1){
19028 this.onTouchViewEmptyResults();
19032 this.clearTouchView();
19034 var rawValue = this.getRawValue();
19036 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19038 this.tickItems = [];
19040 this.store.data.each(function(d, rowIndex){
19041 var row = this.touchViewListGroup.createChild(template);
19043 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19044 row.addClass(d.data.cls);
19047 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19050 html : d.data[this.displayField]
19053 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19054 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19057 row.removeClass('selected');
19058 if(!this.multiple && this.valueField &&
19059 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19062 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19063 row.addClass('selected');
19066 if(this.multiple && this.valueField &&
19067 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19071 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19072 this.tickItems.push(d.data);
19075 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19079 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19081 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19083 if(this.modalTitle.length){
19084 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19087 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19089 if(this.mobile_restrict_height && listHeight < bodyHeight){
19090 this.touchViewBodyEl.setHeight(listHeight);
19095 if(firstChecked && listHeight > bodyHeight){
19096 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19101 onTouchViewLoadException : function()
19103 this.hideTouchView();
19106 onTouchViewEmptyResults : function()
19108 this.clearTouchView();
19110 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19112 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19116 clearTouchView : function()
19118 this.touchViewListGroup.dom.innerHTML = '';
19121 onTouchViewClick : function(e, el, o)
19123 e.preventDefault();
19126 var rowIndex = o.rowIndex;
19128 var r = this.store.getAt(rowIndex);
19130 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19132 if(!this.multiple){
19133 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19134 c.dom.removeAttribute('checked');
19137 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19139 this.setFromData(r.data);
19141 var close = this.closeTriggerEl();
19147 this.hideTouchView();
19149 this.fireEvent('select', this, r, rowIndex);
19154 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19155 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19156 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19160 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19161 this.addItem(r.data);
19162 this.tickItems.push(r.data);
19166 getAutoCreateNativeIOS : function()
19169 cls: 'form-group' //input-group,
19174 cls : 'roo-ios-select'
19178 combobox.name = this.name;
19181 if (this.disabled) {
19182 combobox.disabled = true;
19185 var settings = this;
19187 ['xs','sm','md','lg'].map(function(size){
19188 if (settings[size]) {
19189 cfg.cls += ' col-' + size + '-' + settings[size];
19199 initIOSView : function()
19201 this.store.on('load', this.onIOSViewLoad, this);
19206 onIOSViewLoad : function()
19208 if(this.store.getCount() < 1){
19212 this.clearIOSView();
19214 if(this.allowBlank) {
19216 var default_text = '-- SELECT --';
19218 if(this.placeholder.length){
19219 default_text = this.placeholder;
19222 if(this.emptyTitle.length){
19223 default_text += ' - ' + this.emptyTitle + ' -';
19226 var opt = this.inputEl().createChild({
19229 html : default_text
19233 o[this.valueField] = 0;
19234 o[this.displayField] = default_text;
19236 this.ios_options.push({
19243 this.store.data.each(function(d, rowIndex){
19247 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19248 html = d.data[this.displayField];
19253 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19254 value = d.data[this.valueField];
19263 if(this.value == d.data[this.valueField]){
19264 option['selected'] = true;
19267 var opt = this.inputEl().createChild(option);
19269 this.ios_options.push({
19276 this.inputEl().on('change', function(){
19277 this.fireEvent('select', this);
19282 clearIOSView: function()
19284 this.inputEl().dom.innerHTML = '';
19286 this.ios_options = [];
19289 setIOSValue: function(v)
19293 if(!this.ios_options){
19297 Roo.each(this.ios_options, function(opts){
19299 opts.el.dom.removeAttribute('selected');
19301 if(opts.data[this.valueField] != v){
19305 opts.el.dom.setAttribute('selected', true);
19311 * @cfg {Boolean} grow
19315 * @cfg {Number} growMin
19319 * @cfg {Number} growMax
19328 Roo.apply(Roo.bootstrap.ComboBox, {
19332 cls: 'modal-header',
19354 cls: 'list-group-item',
19358 cls: 'roo-combobox-list-group-item-value'
19362 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19376 listItemCheckbox : {
19378 cls: 'list-group-item',
19382 cls: 'roo-combobox-list-group-item-value'
19386 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19402 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19407 cls: 'modal-footer',
19415 cls: 'col-xs-6 text-left',
19418 cls: 'btn btn-danger roo-touch-view-cancel',
19424 cls: 'col-xs-6 text-right',
19427 cls: 'btn btn-success roo-touch-view-ok',
19438 Roo.apply(Roo.bootstrap.ComboBox, {
19440 touchViewTemplate : {
19442 cls: 'modal fade roo-combobox-touch-view',
19446 cls: 'modal-dialog',
19447 style : 'position:fixed', // we have to fix position....
19451 cls: 'modal-content',
19453 Roo.bootstrap.ComboBox.header,
19454 Roo.bootstrap.ComboBox.body,
19455 Roo.bootstrap.ComboBox.footer
19464 * Ext JS Library 1.1.1
19465 * Copyright(c) 2006-2007, Ext JS, LLC.
19467 * Originally Released Under LGPL - original licence link has changed is not relivant.
19470 * <script type="text/javascript">
19475 * @extends Roo.util.Observable
19476 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19477 * This class also supports single and multi selection modes. <br>
19478 * Create a data model bound view:
19480 var store = new Roo.data.Store(...);
19482 var view = new Roo.View({
19484 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19486 singleSelect: true,
19487 selectedClass: "ydataview-selected",
19491 // listen for node click?
19492 view.on("click", function(vw, index, node, e){
19493 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19497 dataModel.load("foobar.xml");
19499 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19501 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19502 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19504 * Note: old style constructor is still suported (container, template, config)
19507 * Create a new View
19508 * @param {Object} config The config object
19511 Roo.View = function(config, depreciated_tpl, depreciated_config){
19513 this.parent = false;
19515 if (typeof(depreciated_tpl) == 'undefined') {
19516 // new way.. - universal constructor.
19517 Roo.apply(this, config);
19518 this.el = Roo.get(this.el);
19521 this.el = Roo.get(config);
19522 this.tpl = depreciated_tpl;
19523 Roo.apply(this, depreciated_config);
19525 this.wrapEl = this.el.wrap().wrap();
19526 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19529 if(typeof(this.tpl) == "string"){
19530 this.tpl = new Roo.Template(this.tpl);
19532 // support xtype ctors..
19533 this.tpl = new Roo.factory(this.tpl, Roo);
19537 this.tpl.compile();
19542 * @event beforeclick
19543 * Fires before a click is processed. Returns false to cancel the default action.
19544 * @param {Roo.View} this
19545 * @param {Number} index The index of the target node
19546 * @param {HTMLElement} node The target node
19547 * @param {Roo.EventObject} e The raw event object
19549 "beforeclick" : true,
19552 * Fires when a template node is clicked.
19553 * @param {Roo.View} this
19554 * @param {Number} index The index of the target node
19555 * @param {HTMLElement} node The target node
19556 * @param {Roo.EventObject} e The raw event object
19561 * Fires when a template node is double clicked.
19562 * @param {Roo.View} this
19563 * @param {Number} index The index of the target node
19564 * @param {HTMLElement} node The target node
19565 * @param {Roo.EventObject} e The raw event object
19569 * @event contextmenu
19570 * Fires when a template node is right clicked.
19571 * @param {Roo.View} this
19572 * @param {Number} index The index of the target node
19573 * @param {HTMLElement} node The target node
19574 * @param {Roo.EventObject} e The raw event object
19576 "contextmenu" : true,
19578 * @event selectionchange
19579 * Fires when the selected nodes change.
19580 * @param {Roo.View} this
19581 * @param {Array} selections Array of the selected nodes
19583 "selectionchange" : true,
19586 * @event beforeselect
19587 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19588 * @param {Roo.View} this
19589 * @param {HTMLElement} node The node to be selected
19590 * @param {Array} selections Array of currently selected nodes
19592 "beforeselect" : true,
19594 * @event preparedata
19595 * Fires on every row to render, to allow you to change the data.
19596 * @param {Roo.View} this
19597 * @param {Object} data to be rendered (change this)
19599 "preparedata" : true
19607 "click": this.onClick,
19608 "dblclick": this.onDblClick,
19609 "contextmenu": this.onContextMenu,
19613 this.selections = [];
19615 this.cmp = new Roo.CompositeElementLite([]);
19617 this.store = Roo.factory(this.store, Roo.data);
19618 this.setStore(this.store, true);
19621 if ( this.footer && this.footer.xtype) {
19623 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19625 this.footer.dataSource = this.store;
19626 this.footer.container = fctr;
19627 this.footer = Roo.factory(this.footer, Roo);
19628 fctr.insertFirst(this.el);
19630 // this is a bit insane - as the paging toolbar seems to detach the el..
19631 // dom.parentNode.parentNode.parentNode
19632 // they get detached?
19636 Roo.View.superclass.constructor.call(this);
19641 Roo.extend(Roo.View, Roo.util.Observable, {
19644 * @cfg {Roo.data.Store} store Data store to load data from.
19649 * @cfg {String|Roo.Element} el The container element.
19654 * @cfg {String|Roo.Template} tpl The template used by this View
19658 * @cfg {String} dataName the named area of the template to use as the data area
19659 * Works with domtemplates roo-name="name"
19663 * @cfg {String} selectedClass The css class to add to selected nodes
19665 selectedClass : "x-view-selected",
19667 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19672 * @cfg {String} text to display on mask (default Loading)
19676 * @cfg {Boolean} multiSelect Allow multiple selection
19678 multiSelect : false,
19680 * @cfg {Boolean} singleSelect Allow single selection
19682 singleSelect: false,
19685 * @cfg {Boolean} toggleSelect - selecting
19687 toggleSelect : false,
19690 * @cfg {Boolean} tickable - selecting
19695 * Returns the element this view is bound to.
19696 * @return {Roo.Element}
19698 getEl : function(){
19699 return this.wrapEl;
19705 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19707 refresh : function(){
19708 //Roo.log('refresh');
19711 // if we are using something like 'domtemplate', then
19712 // the what gets used is:
19713 // t.applySubtemplate(NAME, data, wrapping data..)
19714 // the outer template then get' applied with
19715 // the store 'extra data'
19716 // and the body get's added to the
19717 // roo-name="data" node?
19718 // <span class='roo-tpl-{name}'></span> ?????
19722 this.clearSelections();
19723 this.el.update("");
19725 var records = this.store.getRange();
19726 if(records.length < 1) {
19728 // is this valid?? = should it render a template??
19730 this.el.update(this.emptyText);
19734 if (this.dataName) {
19735 this.el.update(t.apply(this.store.meta)); //????
19736 el = this.el.child('.roo-tpl-' + this.dataName);
19739 for(var i = 0, len = records.length; i < len; i++){
19740 var data = this.prepareData(records[i].data, i, records[i]);
19741 this.fireEvent("preparedata", this, data, i, records[i]);
19743 var d = Roo.apply({}, data);
19746 Roo.apply(d, {'roo-id' : Roo.id()});
19750 Roo.each(this.parent.item, function(item){
19751 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19754 Roo.apply(d, {'roo-data-checked' : 'checked'});
19758 html[html.length] = Roo.util.Format.trim(
19760 t.applySubtemplate(this.dataName, d, this.store.meta) :
19767 el.update(html.join(""));
19768 this.nodes = el.dom.childNodes;
19769 this.updateIndexes(0);
19774 * Function to override to reformat the data that is sent to
19775 * the template for each node.
19776 * DEPRICATED - use the preparedata event handler.
19777 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19778 * a JSON object for an UpdateManager bound view).
19780 prepareData : function(data, index, record)
19782 this.fireEvent("preparedata", this, data, index, record);
19786 onUpdate : function(ds, record){
19787 // Roo.log('on update');
19788 this.clearSelections();
19789 var index = this.store.indexOf(record);
19790 var n = this.nodes[index];
19791 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19792 n.parentNode.removeChild(n);
19793 this.updateIndexes(index, index);
19799 onAdd : function(ds, records, index)
19801 //Roo.log(['on Add', ds, records, index] );
19802 this.clearSelections();
19803 if(this.nodes.length == 0){
19807 var n = this.nodes[index];
19808 for(var i = 0, len = records.length; i < len; i++){
19809 var d = this.prepareData(records[i].data, i, records[i]);
19811 this.tpl.insertBefore(n, d);
19814 this.tpl.append(this.el, d);
19817 this.updateIndexes(index);
19820 onRemove : function(ds, record, index){
19821 // Roo.log('onRemove');
19822 this.clearSelections();
19823 var el = this.dataName ?
19824 this.el.child('.roo-tpl-' + this.dataName) :
19827 el.dom.removeChild(this.nodes[index]);
19828 this.updateIndexes(index);
19832 * Refresh an individual node.
19833 * @param {Number} index
19835 refreshNode : function(index){
19836 this.onUpdate(this.store, this.store.getAt(index));
19839 updateIndexes : function(startIndex, endIndex){
19840 var ns = this.nodes;
19841 startIndex = startIndex || 0;
19842 endIndex = endIndex || ns.length - 1;
19843 for(var i = startIndex; i <= endIndex; i++){
19844 ns[i].nodeIndex = i;
19849 * Changes the data store this view uses and refresh the view.
19850 * @param {Store} store
19852 setStore : function(store, initial){
19853 if(!initial && this.store){
19854 this.store.un("datachanged", this.refresh);
19855 this.store.un("add", this.onAdd);
19856 this.store.un("remove", this.onRemove);
19857 this.store.un("update", this.onUpdate);
19858 this.store.un("clear", this.refresh);
19859 this.store.un("beforeload", this.onBeforeLoad);
19860 this.store.un("load", this.onLoad);
19861 this.store.un("loadexception", this.onLoad);
19865 store.on("datachanged", this.refresh, this);
19866 store.on("add", this.onAdd, this);
19867 store.on("remove", this.onRemove, this);
19868 store.on("update", this.onUpdate, this);
19869 store.on("clear", this.refresh, this);
19870 store.on("beforeload", this.onBeforeLoad, this);
19871 store.on("load", this.onLoad, this);
19872 store.on("loadexception", this.onLoad, this);
19880 * onbeforeLoad - masks the loading area.
19883 onBeforeLoad : function(store,opts)
19885 //Roo.log('onBeforeLoad');
19887 this.el.update("");
19889 this.el.mask(this.mask ? this.mask : "Loading" );
19891 onLoad : function ()
19898 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19899 * @param {HTMLElement} node
19900 * @return {HTMLElement} The template node
19902 findItemFromChild : function(node){
19903 var el = this.dataName ?
19904 this.el.child('.roo-tpl-' + this.dataName,true) :
19907 if(!node || node.parentNode == el){
19910 var p = node.parentNode;
19911 while(p && p != el){
19912 if(p.parentNode == el){
19921 onClick : function(e){
19922 var item = this.findItemFromChild(e.getTarget());
19924 var index = this.indexOf(item);
19925 if(this.onItemClick(item, index, e) !== false){
19926 this.fireEvent("click", this, index, item, e);
19929 this.clearSelections();
19934 onContextMenu : function(e){
19935 var item = this.findItemFromChild(e.getTarget());
19937 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19942 onDblClick : function(e){
19943 var item = this.findItemFromChild(e.getTarget());
19945 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19949 onItemClick : function(item, index, e)
19951 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19954 if (this.toggleSelect) {
19955 var m = this.isSelected(item) ? 'unselect' : 'select';
19958 _t[m](item, true, false);
19961 if(this.multiSelect || this.singleSelect){
19962 if(this.multiSelect && e.shiftKey && this.lastSelection){
19963 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19965 this.select(item, this.multiSelect && e.ctrlKey);
19966 this.lastSelection = item;
19969 if(!this.tickable){
19970 e.preventDefault();
19978 * Get the number of selected nodes.
19981 getSelectionCount : function(){
19982 return this.selections.length;
19986 * Get the currently selected nodes.
19987 * @return {Array} An array of HTMLElements
19989 getSelectedNodes : function(){
19990 return this.selections;
19994 * Get the indexes of the selected nodes.
19997 getSelectedIndexes : function(){
19998 var indexes = [], s = this.selections;
19999 for(var i = 0, len = s.length; i < len; i++){
20000 indexes.push(s[i].nodeIndex);
20006 * Clear all selections
20007 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20009 clearSelections : function(suppressEvent){
20010 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20011 this.cmp.elements = this.selections;
20012 this.cmp.removeClass(this.selectedClass);
20013 this.selections = [];
20014 if(!suppressEvent){
20015 this.fireEvent("selectionchange", this, this.selections);
20021 * Returns true if the passed node is selected
20022 * @param {HTMLElement/Number} node The node or node index
20023 * @return {Boolean}
20025 isSelected : function(node){
20026 var s = this.selections;
20030 node = this.getNode(node);
20031 return s.indexOf(node) !== -1;
20036 * @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
20037 * @param {Boolean} keepExisting (optional) true to keep existing selections
20038 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20040 select : function(nodeInfo, keepExisting, suppressEvent){
20041 if(nodeInfo instanceof Array){
20043 this.clearSelections(true);
20045 for(var i = 0, len = nodeInfo.length; i < len; i++){
20046 this.select(nodeInfo[i], true, true);
20050 var node = this.getNode(nodeInfo);
20051 if(!node || this.isSelected(node)){
20052 return; // already selected.
20055 this.clearSelections(true);
20058 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20059 Roo.fly(node).addClass(this.selectedClass);
20060 this.selections.push(node);
20061 if(!suppressEvent){
20062 this.fireEvent("selectionchange", this, this.selections);
20070 * @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
20071 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20072 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20074 unselect : function(nodeInfo, keepExisting, suppressEvent)
20076 if(nodeInfo instanceof Array){
20077 Roo.each(this.selections, function(s) {
20078 this.unselect(s, nodeInfo);
20082 var node = this.getNode(nodeInfo);
20083 if(!node || !this.isSelected(node)){
20084 //Roo.log("not selected");
20085 return; // not selected.
20089 Roo.each(this.selections, function(s) {
20091 Roo.fly(node).removeClass(this.selectedClass);
20098 this.selections= ns;
20099 this.fireEvent("selectionchange", this, this.selections);
20103 * Gets a template node.
20104 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20105 * @return {HTMLElement} The node or null if it wasn't found
20107 getNode : function(nodeInfo){
20108 if(typeof nodeInfo == "string"){
20109 return document.getElementById(nodeInfo);
20110 }else if(typeof nodeInfo == "number"){
20111 return this.nodes[nodeInfo];
20117 * Gets a range template nodes.
20118 * @param {Number} startIndex
20119 * @param {Number} endIndex
20120 * @return {Array} An array of nodes
20122 getNodes : function(start, end){
20123 var ns = this.nodes;
20124 start = start || 0;
20125 end = typeof end == "undefined" ? ns.length - 1 : end;
20128 for(var i = start; i <= end; i++){
20132 for(var i = start; i >= end; i--){
20140 * Finds the index of the passed node
20141 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20142 * @return {Number} The index of the node or -1
20144 indexOf : function(node){
20145 node = this.getNode(node);
20146 if(typeof node.nodeIndex == "number"){
20147 return node.nodeIndex;
20149 var ns = this.nodes;
20150 for(var i = 0, len = ns.length; i < len; i++){
20161 * based on jquery fullcalendar
20165 Roo.bootstrap = Roo.bootstrap || {};
20167 * @class Roo.bootstrap.Calendar
20168 * @extends Roo.bootstrap.Component
20169 * Bootstrap Calendar class
20170 * @cfg {Boolean} loadMask (true|false) default false
20171 * @cfg {Object} header generate the user specific header of the calendar, default false
20174 * Create a new Container
20175 * @param {Object} config The config object
20180 Roo.bootstrap.Calendar = function(config){
20181 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20185 * Fires when a date is selected
20186 * @param {DatePicker} this
20187 * @param {Date} date The selected date
20191 * @event monthchange
20192 * Fires when the displayed month changes
20193 * @param {DatePicker} this
20194 * @param {Date} date The selected month
20196 'monthchange': true,
20198 * @event evententer
20199 * Fires when mouse over an event
20200 * @param {Calendar} this
20201 * @param {event} Event
20203 'evententer': true,
20205 * @event eventleave
20206 * Fires when the mouse leaves an
20207 * @param {Calendar} this
20210 'eventleave': true,
20212 * @event eventclick
20213 * Fires when the mouse click an
20214 * @param {Calendar} this
20223 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20226 * @cfg {Roo.data.Store} store
20227 * The data source for the calendar
20231 * @cfg {Number} startDay
20232 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20240 getAutoCreate : function(){
20243 var fc_button = function(name, corner, style, content ) {
20244 return Roo.apply({},{
20246 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20248 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20251 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20262 style : 'width:100%',
20269 cls : 'fc-header-left',
20271 fc_button('prev', 'left', 'arrow', '‹' ),
20272 fc_button('next', 'right', 'arrow', '›' ),
20273 { tag: 'span', cls: 'fc-header-space' },
20274 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20282 cls : 'fc-header-center',
20286 cls: 'fc-header-title',
20289 html : 'month / year'
20297 cls : 'fc-header-right',
20299 /* fc_button('month', 'left', '', 'month' ),
20300 fc_button('week', '', '', 'week' ),
20301 fc_button('day', 'right', '', 'day' )
20313 header = this.header;
20316 var cal_heads = function() {
20318 // fixme - handle this.
20320 for (var i =0; i < Date.dayNames.length; i++) {
20321 var d = Date.dayNames[i];
20324 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20325 html : d.substring(0,3)
20329 ret[0].cls += ' fc-first';
20330 ret[6].cls += ' fc-last';
20333 var cal_cell = function(n) {
20336 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20341 cls: 'fc-day-number',
20345 cls: 'fc-day-content',
20349 style: 'position: relative;' // height: 17px;
20361 var cal_rows = function() {
20364 for (var r = 0; r < 6; r++) {
20371 for (var i =0; i < Date.dayNames.length; i++) {
20372 var d = Date.dayNames[i];
20373 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20376 row.cn[0].cls+=' fc-first';
20377 row.cn[0].cn[0].style = 'min-height:90px';
20378 row.cn[6].cls+=' fc-last';
20382 ret[0].cls += ' fc-first';
20383 ret[4].cls += ' fc-prev-last';
20384 ret[5].cls += ' fc-last';
20391 cls: 'fc-border-separate',
20392 style : 'width:100%',
20400 cls : 'fc-first fc-last',
20418 cls : 'fc-content',
20419 style : "position: relative;",
20422 cls : 'fc-view fc-view-month fc-grid',
20423 style : 'position: relative',
20424 unselectable : 'on',
20427 cls : 'fc-event-container',
20428 style : 'position:absolute;z-index:8;top:0;left:0;'
20446 initEvents : function()
20449 throw "can not find store for calendar";
20455 style: "text-align:center",
20459 style: "background-color:white;width:50%;margin:250 auto",
20463 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20474 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20476 var size = this.el.select('.fc-content', true).first().getSize();
20477 this.maskEl.setSize(size.width, size.height);
20478 this.maskEl.enableDisplayMode("block");
20479 if(!this.loadMask){
20480 this.maskEl.hide();
20483 this.store = Roo.factory(this.store, Roo.data);
20484 this.store.on('load', this.onLoad, this);
20485 this.store.on('beforeload', this.onBeforeLoad, this);
20489 this.cells = this.el.select('.fc-day',true);
20490 //Roo.log(this.cells);
20491 this.textNodes = this.el.query('.fc-day-number');
20492 this.cells.addClassOnOver('fc-state-hover');
20494 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20495 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20496 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20497 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20499 this.on('monthchange', this.onMonthChange, this);
20501 this.update(new Date().clearTime());
20504 resize : function() {
20505 var sz = this.el.getSize();
20507 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20508 this.el.select('.fc-day-content div',true).setHeight(34);
20513 showPrevMonth : function(e){
20514 this.update(this.activeDate.add("mo", -1));
20516 showToday : function(e){
20517 this.update(new Date().clearTime());
20520 showNextMonth : function(e){
20521 this.update(this.activeDate.add("mo", 1));
20525 showPrevYear : function(){
20526 this.update(this.activeDate.add("y", -1));
20530 showNextYear : function(){
20531 this.update(this.activeDate.add("y", 1));
20536 update : function(date)
20538 var vd = this.activeDate;
20539 this.activeDate = date;
20540 // if(vd && this.el){
20541 // var t = date.getTime();
20542 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20543 // Roo.log('using add remove');
20545 // this.fireEvent('monthchange', this, date);
20547 // this.cells.removeClass("fc-state-highlight");
20548 // this.cells.each(function(c){
20549 // if(c.dateValue == t){
20550 // c.addClass("fc-state-highlight");
20551 // setTimeout(function(){
20552 // try{c.dom.firstChild.focus();}catch(e){}
20562 var days = date.getDaysInMonth();
20564 var firstOfMonth = date.getFirstDateOfMonth();
20565 var startingPos = firstOfMonth.getDay()-this.startDay;
20567 if(startingPos < this.startDay){
20571 var pm = date.add(Date.MONTH, -1);
20572 var prevStart = pm.getDaysInMonth()-startingPos;
20574 this.cells = this.el.select('.fc-day',true);
20575 this.textNodes = this.el.query('.fc-day-number');
20576 this.cells.addClassOnOver('fc-state-hover');
20578 var cells = this.cells.elements;
20579 var textEls = this.textNodes;
20581 Roo.each(cells, function(cell){
20582 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20585 days += startingPos;
20587 // convert everything to numbers so it's fast
20588 var day = 86400000;
20589 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20592 //Roo.log(prevStart);
20594 var today = new Date().clearTime().getTime();
20595 var sel = date.clearTime().getTime();
20596 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20597 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20598 var ddMatch = this.disabledDatesRE;
20599 var ddText = this.disabledDatesText;
20600 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20601 var ddaysText = this.disabledDaysText;
20602 var format = this.format;
20604 var setCellClass = function(cal, cell){
20608 //Roo.log('set Cell Class');
20610 var t = d.getTime();
20614 cell.dateValue = t;
20616 cell.className += " fc-today";
20617 cell.className += " fc-state-highlight";
20618 cell.title = cal.todayText;
20621 // disable highlight in other month..
20622 //cell.className += " fc-state-highlight";
20627 cell.className = " fc-state-disabled";
20628 cell.title = cal.minText;
20632 cell.className = " fc-state-disabled";
20633 cell.title = cal.maxText;
20637 if(ddays.indexOf(d.getDay()) != -1){
20638 cell.title = ddaysText;
20639 cell.className = " fc-state-disabled";
20642 if(ddMatch && format){
20643 var fvalue = d.dateFormat(format);
20644 if(ddMatch.test(fvalue)){
20645 cell.title = ddText.replace("%0", fvalue);
20646 cell.className = " fc-state-disabled";
20650 if (!cell.initialClassName) {
20651 cell.initialClassName = cell.dom.className;
20654 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20659 for(; i < startingPos; i++) {
20660 textEls[i].innerHTML = (++prevStart);
20661 d.setDate(d.getDate()+1);
20663 cells[i].className = "fc-past fc-other-month";
20664 setCellClass(this, cells[i]);
20669 for(; i < days; i++){
20670 intDay = i - startingPos + 1;
20671 textEls[i].innerHTML = (intDay);
20672 d.setDate(d.getDate()+1);
20674 cells[i].className = ''; // "x-date-active";
20675 setCellClass(this, cells[i]);
20679 for(; i < 42; i++) {
20680 textEls[i].innerHTML = (++extraDays);
20681 d.setDate(d.getDate()+1);
20683 cells[i].className = "fc-future fc-other-month";
20684 setCellClass(this, cells[i]);
20687 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20689 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20691 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20692 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20694 if(totalRows != 6){
20695 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20696 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20699 this.fireEvent('monthchange', this, date);
20703 if(!this.internalRender){
20704 var main = this.el.dom.firstChild;
20705 var w = main.offsetWidth;
20706 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20707 Roo.fly(main).setWidth(w);
20708 this.internalRender = true;
20709 // opera does not respect the auto grow header center column
20710 // then, after it gets a width opera refuses to recalculate
20711 // without a second pass
20712 if(Roo.isOpera && !this.secondPass){
20713 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20714 this.secondPass = true;
20715 this.update.defer(10, this, [date]);
20722 findCell : function(dt) {
20723 dt = dt.clearTime().getTime();
20725 this.cells.each(function(c){
20726 //Roo.log("check " +c.dateValue + '?=' + dt);
20727 if(c.dateValue == dt){
20737 findCells : function(ev) {
20738 var s = ev.start.clone().clearTime().getTime();
20740 var e= ev.end.clone().clearTime().getTime();
20743 this.cells.each(function(c){
20744 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20746 if(c.dateValue > e){
20749 if(c.dateValue < s){
20758 // findBestRow: function(cells)
20762 // for (var i =0 ; i < cells.length;i++) {
20763 // ret = Math.max(cells[i].rows || 0,ret);
20770 addItem : function(ev)
20772 // look for vertical location slot in
20773 var cells = this.findCells(ev);
20775 // ev.row = this.findBestRow(cells);
20777 // work out the location.
20781 for(var i =0; i < cells.length; i++) {
20783 cells[i].row = cells[0].row;
20786 cells[i].row = cells[i].row + 1;
20796 if (crow.start.getY() == cells[i].getY()) {
20798 crow.end = cells[i];
20815 cells[0].events.push(ev);
20817 this.calevents.push(ev);
20820 clearEvents: function() {
20822 if(!this.calevents){
20826 Roo.each(this.cells.elements, function(c){
20832 Roo.each(this.calevents, function(e) {
20833 Roo.each(e.els, function(el) {
20834 el.un('mouseenter' ,this.onEventEnter, this);
20835 el.un('mouseleave' ,this.onEventLeave, this);
20840 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20846 renderEvents: function()
20850 this.cells.each(function(c) {
20859 if(c.row != c.events.length){
20860 r = 4 - (4 - (c.row - c.events.length));
20863 c.events = ev.slice(0, r);
20864 c.more = ev.slice(r);
20866 if(c.more.length && c.more.length == 1){
20867 c.events.push(c.more.pop());
20870 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20874 this.cells.each(function(c) {
20876 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20879 for (var e = 0; e < c.events.length; e++){
20880 var ev = c.events[e];
20881 var rows = ev.rows;
20883 for(var i = 0; i < rows.length; i++) {
20885 // how many rows should it span..
20888 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20889 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20891 unselectable : "on",
20894 cls: 'fc-event-inner',
20898 // cls: 'fc-event-time',
20899 // html : cells.length > 1 ? '' : ev.time
20903 cls: 'fc-event-title',
20904 html : String.format('{0}', ev.title)
20911 cls: 'ui-resizable-handle ui-resizable-e',
20912 html : '  '
20919 cfg.cls += ' fc-event-start';
20921 if ((i+1) == rows.length) {
20922 cfg.cls += ' fc-event-end';
20925 var ctr = _this.el.select('.fc-event-container',true).first();
20926 var cg = ctr.createChild(cfg);
20928 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20929 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20931 var r = (c.more.length) ? 1 : 0;
20932 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20933 cg.setWidth(ebox.right - sbox.x -2);
20935 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20936 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20937 cg.on('click', _this.onEventClick, _this, ev);
20948 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20949 style : 'position: absolute',
20950 unselectable : "on",
20953 cls: 'fc-event-inner',
20957 cls: 'fc-event-title',
20965 cls: 'ui-resizable-handle ui-resizable-e',
20966 html : '  '
20972 var ctr = _this.el.select('.fc-event-container',true).first();
20973 var cg = ctr.createChild(cfg);
20975 var sbox = c.select('.fc-day-content',true).first().getBox();
20976 var ebox = c.select('.fc-day-content',true).first().getBox();
20978 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20979 cg.setWidth(ebox.right - sbox.x -2);
20981 cg.on('click', _this.onMoreEventClick, _this, c.more);
20991 onEventEnter: function (e, el,event,d) {
20992 this.fireEvent('evententer', this, el, event);
20995 onEventLeave: function (e, el,event,d) {
20996 this.fireEvent('eventleave', this, el, event);
20999 onEventClick: function (e, el,event,d) {
21000 this.fireEvent('eventclick', this, el, event);
21003 onMonthChange: function () {
21007 onMoreEventClick: function(e, el, more)
21011 this.calpopover.placement = 'right';
21012 this.calpopover.setTitle('More');
21014 this.calpopover.setContent('');
21016 var ctr = this.calpopover.el.select('.popover-content', true).first();
21018 Roo.each(more, function(m){
21020 cls : 'fc-event-hori fc-event-draggable',
21023 var cg = ctr.createChild(cfg);
21025 cg.on('click', _this.onEventClick, _this, m);
21028 this.calpopover.show(el);
21033 onLoad: function ()
21035 this.calevents = [];
21038 if(this.store.getCount() > 0){
21039 this.store.data.each(function(d){
21042 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21043 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21044 time : d.data.start_time,
21045 title : d.data.title,
21046 description : d.data.description,
21047 venue : d.data.venue
21052 this.renderEvents();
21054 if(this.calevents.length && this.loadMask){
21055 this.maskEl.hide();
21059 onBeforeLoad: function()
21061 this.clearEvents();
21063 this.maskEl.show();
21077 * @class Roo.bootstrap.Popover
21078 * @extends Roo.bootstrap.Component
21080 * @children Roo.bootstrap.Component
21081 * Bootstrap Popover class
21082 * @cfg {String} html contents of the popover (or false to use children..)
21083 * @cfg {String} title of popover (or false to hide)
21084 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21085 * @cfg {String} trigger click || hover (or false to trigger manually)
21086 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21087 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21088 * - if false and it has a 'parent' then it will be automatically added to that element
21089 * - if string - Roo.get will be called
21090 * @cfg {Number} delay - delay before showing
21093 * Create a new Popover
21094 * @param {Object} config The config object
21097 Roo.bootstrap.Popover = function(config){
21098 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21104 * After the popover show
21106 * @param {Roo.bootstrap.Popover} this
21111 * After the popover hide
21113 * @param {Roo.bootstrap.Popover} this
21119 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21124 placement : 'right',
21125 trigger : 'hover', // hover
21131 can_build_overlaid : false,
21133 maskEl : false, // the mask element
21136 alignEl : false, // when show is called with an element - this get's stored.
21138 getChildContainer : function()
21140 return this.contentEl;
21143 getPopoverHeader : function()
21145 this.title = true; // flag not to hide it..
21146 this.headerEl.addClass('p-0');
21147 return this.headerEl
21151 getAutoCreate : function(){
21154 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21155 style: 'display:block',
21161 cls : 'popover-inner ',
21165 cls: 'popover-title popover-header',
21166 html : this.title === false ? '' : this.title
21169 cls : 'popover-content popover-body ' + (this.cls || ''),
21170 html : this.html || ''
21181 * @param {string} the title
21183 setTitle: function(str)
21187 this.headerEl.dom.innerHTML = str;
21192 * @param {string} the body content
21194 setContent: function(str)
21197 if (this.contentEl) {
21198 this.contentEl.dom.innerHTML = str;
21202 // as it get's added to the bottom of the page.
21203 onRender : function(ct, position)
21205 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21210 var cfg = Roo.apply({}, this.getAutoCreate());
21214 cfg.cls += ' ' + this.cls;
21217 cfg.style = this.style;
21219 //Roo.log("adding to ");
21220 this.el = Roo.get(document.body).createChild(cfg, position);
21221 // Roo.log(this.el);
21224 this.contentEl = this.el.select('.popover-content',true).first();
21225 this.headerEl = this.el.select('.popover-title',true).first();
21228 if(typeof(this.items) != 'undefined'){
21229 var items = this.items;
21232 for(var i =0;i < items.length;i++) {
21233 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21237 this.items = nitems;
21239 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21240 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21247 resizeMask : function()
21249 this.maskEl.setSize(
21250 Roo.lib.Dom.getViewWidth(true),
21251 Roo.lib.Dom.getViewHeight(true)
21255 initEvents : function()
21259 Roo.bootstrap.Popover.register(this);
21262 this.arrowEl = this.el.select('.arrow',true).first();
21263 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21264 this.el.enableDisplayMode('block');
21268 if (this.over === false && !this.parent()) {
21271 if (this.triggers === false) {
21276 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21277 var triggers = this.trigger ? this.trigger.split(' ') : [];
21278 Roo.each(triggers, function(trigger) {
21280 if (trigger == 'click') {
21281 on_el.on('click', this.toggle, this);
21282 } else if (trigger != 'manual') {
21283 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21284 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21286 on_el.on(eventIn ,this.enter, this);
21287 on_el.on(eventOut, this.leave, this);
21297 toggle : function () {
21298 this.hoverState == 'in' ? this.leave() : this.enter();
21301 enter : function () {
21303 clearTimeout(this.timeout);
21305 this.hoverState = 'in';
21307 if (!this.delay || !this.delay.show) {
21312 this.timeout = setTimeout(function () {
21313 if (_t.hoverState == 'in') {
21316 }, this.delay.show)
21319 leave : function() {
21320 clearTimeout(this.timeout);
21322 this.hoverState = 'out';
21324 if (!this.delay || !this.delay.hide) {
21329 this.timeout = setTimeout(function () {
21330 if (_t.hoverState == 'out') {
21333 }, this.delay.hide)
21337 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21338 * @param {string} (left|right|top|bottom) position
21340 show : function (on_el, placement)
21342 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21343 on_el = on_el || false; // default to false
21346 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21347 on_el = this.parent().el;
21348 } else if (this.over) {
21349 on_el = Roo.get(this.over);
21354 this.alignEl = Roo.get( on_el );
21357 this.render(document.body);
21363 if (this.title === false) {
21364 this.headerEl.hide();
21369 this.el.dom.style.display = 'block';
21372 if (this.alignEl) {
21373 this.updatePosition(this.placement, true);
21376 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21377 var es = this.el.getSize();
21378 var x = Roo.lib.Dom.getViewWidth()/2;
21379 var y = Roo.lib.Dom.getViewHeight()/2;
21380 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21385 //var arrow = this.el.select('.arrow',true).first();
21386 //arrow.set(align[2],
21388 this.el.addClass('in');
21392 this.hoverState = 'in';
21395 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21396 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21397 this.maskEl.dom.style.display = 'block';
21398 this.maskEl.addClass('show');
21400 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21402 this.fireEvent('show', this);
21406 * fire this manually after loading a grid in the table for example
21407 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21408 * @param {Boolean} try and move it if we cant get right position.
21410 updatePosition : function(placement, try_move)
21412 // allow for calling with no parameters
21413 placement = placement ? placement : this.placement;
21414 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21416 this.el.removeClass([
21417 'fade','top','bottom', 'left', 'right','in',
21418 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21420 this.el.addClass(placement + ' bs-popover-' + placement);
21422 if (!this.alignEl ) {
21426 switch (placement) {
21428 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21429 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21430 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21431 //normal display... or moved up/down.
21432 this.el.setXY(offset);
21433 var xy = this.alignEl.getAnchorXY('tr', false);
21435 this.arrowEl.setXY(xy);
21438 // continue through...
21439 return this.updatePosition('left', false);
21443 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21444 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21445 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21446 //normal display... or moved up/down.
21447 this.el.setXY(offset);
21448 var xy = this.alignEl.getAnchorXY('tl', false);
21449 xy[0]-=10;xy[1]+=5; // << fix me
21450 this.arrowEl.setXY(xy);
21454 return this.updatePosition('right', false);
21457 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21458 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[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('t', false);
21463 xy[1]-=10; // << fix me
21464 this.arrowEl.setXY(xy);
21468 return this.updatePosition('bottom', false);
21471 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21472 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21473 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21474 //normal display... or moved up/down.
21475 this.el.setXY(offset);
21476 var xy = this.alignEl.getAnchorXY('b', false);
21477 xy[1]+=2; // << fix me
21478 this.arrowEl.setXY(xy);
21482 return this.updatePosition('top', false);
21493 this.el.setXY([0,0]);
21494 this.el.removeClass('in');
21496 this.hoverState = null;
21497 this.maskEl.hide(); // always..
21498 this.fireEvent('hide', this);
21504 Roo.apply(Roo.bootstrap.Popover, {
21507 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21508 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21509 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21510 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21515 clickHander : false,
21519 onMouseDown : function(e)
21521 if (this.popups.length && !e.getTarget(".roo-popover")) {
21522 /// what is nothing is showing..
21531 register : function(popup)
21533 if (!Roo.bootstrap.Popover.clickHandler) {
21534 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21536 // hide other popups.
21537 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21538 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21539 this.hideAll(); //<< why?
21540 //this.popups.push(popup);
21542 hideAll : function()
21544 this.popups.forEach(function(p) {
21548 onShow : function() {
21549 Roo.bootstrap.Popover.popups.push(this);
21551 onHide : function() {
21552 Roo.bootstrap.Popover.popups.remove(this);
21558 * Card header - holder for the card header elements.
21563 * @class Roo.bootstrap.PopoverNav
21564 * @extends Roo.bootstrap.NavGroup
21565 * Bootstrap Popover header navigation class
21567 * Create a new Popover Header Navigation
21568 * @param {Object} config The config object
21571 Roo.bootstrap.PopoverNav = function(config){
21572 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21575 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21578 container_method : 'getPopoverHeader'
21596 * @class Roo.bootstrap.Progress
21597 * @extends Roo.bootstrap.Component
21598 * Bootstrap Progress class
21599 * @cfg {Boolean} striped striped of the progress bar
21600 * @cfg {Boolean} active animated of the progress bar
21604 * Create a new Progress
21605 * @param {Object} config The config object
21608 Roo.bootstrap.Progress = function(config){
21609 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21612 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21617 getAutoCreate : function(){
21625 cfg.cls += ' progress-striped';
21629 cfg.cls += ' active';
21648 * @class Roo.bootstrap.ProgressBar
21649 * @extends Roo.bootstrap.Component
21650 * Bootstrap ProgressBar class
21651 * @cfg {Number} aria_valuenow aria-value now
21652 * @cfg {Number} aria_valuemin aria-value min
21653 * @cfg {Number} aria_valuemax aria-value max
21654 * @cfg {String} label label for the progress bar
21655 * @cfg {String} panel (success | info | warning | danger )
21656 * @cfg {String} role role of the progress bar
21657 * @cfg {String} sr_only text
21661 * Create a new ProgressBar
21662 * @param {Object} config The config object
21665 Roo.bootstrap.ProgressBar = function(config){
21666 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21669 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21673 aria_valuemax : 100,
21679 getAutoCreate : function()
21684 cls: 'progress-bar',
21685 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21697 cfg.role = this.role;
21700 if(this.aria_valuenow){
21701 cfg['aria-valuenow'] = this.aria_valuenow;
21704 if(this.aria_valuemin){
21705 cfg['aria-valuemin'] = this.aria_valuemin;
21708 if(this.aria_valuemax){
21709 cfg['aria-valuemax'] = this.aria_valuemax;
21712 if(this.label && !this.sr_only){
21713 cfg.html = this.label;
21717 cfg.cls += ' progress-bar-' + this.panel;
21723 update : function(aria_valuenow)
21725 this.aria_valuenow = aria_valuenow;
21727 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21742 * @class Roo.bootstrap.TabGroup
21743 * @extends Roo.bootstrap.Column
21744 * Bootstrap Column class
21745 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21746 * @cfg {Boolean} carousel true to make the group behave like a carousel
21747 * @cfg {Boolean} bullets show bullets for the panels
21748 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21749 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21750 * @cfg {Boolean} showarrow (true|false) show arrow default true
21753 * Create a new TabGroup
21754 * @param {Object} config The config object
21757 Roo.bootstrap.TabGroup = function(config){
21758 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21760 this.navId = Roo.id();
21763 Roo.bootstrap.TabGroup.register(this);
21767 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21770 transition : false,
21775 slideOnTouch : false,
21778 getAutoCreate : function()
21780 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21782 cfg.cls += ' tab-content';
21784 if (this.carousel) {
21785 cfg.cls += ' carousel slide';
21788 cls : 'carousel-inner',
21792 if(this.bullets && !Roo.isTouch){
21795 cls : 'carousel-bullets',
21799 if(this.bullets_cls){
21800 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21807 cfg.cn[0].cn.push(bullets);
21810 if(this.showarrow){
21811 cfg.cn[0].cn.push({
21813 class : 'carousel-arrow',
21817 class : 'carousel-prev',
21821 class : 'fa fa-chevron-left'
21827 class : 'carousel-next',
21831 class : 'fa fa-chevron-right'
21844 initEvents: function()
21846 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21847 // this.el.on("touchstart", this.onTouchStart, this);
21850 if(this.autoslide){
21853 this.slideFn = window.setInterval(function() {
21854 _this.showPanelNext();
21858 if(this.showarrow){
21859 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21860 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21866 // onTouchStart : function(e, el, o)
21868 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21872 // this.showPanelNext();
21876 getChildContainer : function()
21878 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21882 * register a Navigation item
21883 * @param {Roo.bootstrap.NavItem} the navitem to add
21885 register : function(item)
21887 this.tabs.push( item);
21888 item.navId = this.navId; // not really needed..
21893 getActivePanel : function()
21896 Roo.each(this.tabs, function(t) {
21906 getPanelByName : function(n)
21909 Roo.each(this.tabs, function(t) {
21910 if (t.tabId == n) {
21918 indexOfPanel : function(p)
21921 Roo.each(this.tabs, function(t,i) {
21922 if (t.tabId == p.tabId) {
21931 * show a specific panel
21932 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21933 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21935 showPanel : function (pan)
21937 if(this.transition || typeof(pan) == 'undefined'){
21938 Roo.log("waiting for the transitionend");
21942 if (typeof(pan) == 'number') {
21943 pan = this.tabs[pan];
21946 if (typeof(pan) == 'string') {
21947 pan = this.getPanelByName(pan);
21950 var cur = this.getActivePanel();
21953 Roo.log('pan or acitve pan is undefined');
21957 if (pan.tabId == this.getActivePanel().tabId) {
21961 if (false === cur.fireEvent('beforedeactivate')) {
21965 if(this.bullets > 0 && !Roo.isTouch){
21966 this.setActiveBullet(this.indexOfPanel(pan));
21969 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21971 //class="carousel-item carousel-item-next carousel-item-left"
21973 this.transition = true;
21974 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21975 var lr = dir == 'next' ? 'left' : 'right';
21976 pan.el.addClass(dir); // or prev
21977 pan.el.addClass('carousel-item-' + dir); // or prev
21978 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21979 cur.el.addClass(lr); // or right
21980 pan.el.addClass(lr);
21981 cur.el.addClass('carousel-item-' +lr); // or right
21982 pan.el.addClass('carousel-item-' +lr);
21986 cur.el.on('transitionend', function() {
21987 Roo.log("trans end?");
21989 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21990 pan.setActive(true);
21992 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21993 cur.setActive(false);
21995 _this.transition = false;
21997 }, this, { single: true } );
22002 cur.setActive(false);
22003 pan.setActive(true);
22008 showPanelNext : function()
22010 var i = this.indexOfPanel(this.getActivePanel());
22012 if (i >= this.tabs.length - 1 && !this.autoslide) {
22016 if (i >= this.tabs.length - 1 && this.autoslide) {
22020 this.showPanel(this.tabs[i+1]);
22023 showPanelPrev : function()
22025 var i = this.indexOfPanel(this.getActivePanel());
22027 if (i < 1 && !this.autoslide) {
22031 if (i < 1 && this.autoslide) {
22032 i = this.tabs.length;
22035 this.showPanel(this.tabs[i-1]);
22039 addBullet: function()
22041 if(!this.bullets || Roo.isTouch){
22044 var ctr = this.el.select('.carousel-bullets',true).first();
22045 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22046 var bullet = ctr.createChild({
22047 cls : 'bullet bullet-' + i
22048 },ctr.dom.lastChild);
22053 bullet.on('click', (function(e, el, o, ii, t){
22055 e.preventDefault();
22057 this.showPanel(ii);
22059 if(this.autoslide && this.slideFn){
22060 clearInterval(this.slideFn);
22061 this.slideFn = window.setInterval(function() {
22062 _this.showPanelNext();
22066 }).createDelegate(this, [i, bullet], true));
22071 setActiveBullet : function(i)
22077 Roo.each(this.el.select('.bullet', true).elements, function(el){
22078 el.removeClass('selected');
22081 var bullet = this.el.select('.bullet-' + i, true).first();
22087 bullet.addClass('selected');
22098 Roo.apply(Roo.bootstrap.TabGroup, {
22102 * register a Navigation Group
22103 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22105 register : function(navgrp)
22107 this.groups[navgrp.navId] = navgrp;
22111 * fetch a Navigation Group based on the navigation ID
22112 * if one does not exist , it will get created.
22113 * @param {string} the navgroup to add
22114 * @returns {Roo.bootstrap.NavGroup} the navgroup
22116 get: function(navId) {
22117 if (typeof(this.groups[navId]) == 'undefined') {
22118 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22120 return this.groups[navId] ;
22135 * @class Roo.bootstrap.TabPanel
22136 * @extends Roo.bootstrap.Component
22137 * @children Roo.bootstrap.Component
22138 * Bootstrap TabPanel class
22139 * @cfg {Boolean} active panel active
22140 * @cfg {String} html panel content
22141 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22142 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22143 * @cfg {String} href click to link..
22144 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22148 * Create a new TabPanel
22149 * @param {Object} config The config object
22152 Roo.bootstrap.TabPanel = function(config){
22153 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22157 * Fires when the active status changes
22158 * @param {Roo.bootstrap.TabPanel} this
22159 * @param {Boolean} state the new state
22164 * @event beforedeactivate
22165 * Fires before a tab is de-activated - can be used to do validation on a form.
22166 * @param {Roo.bootstrap.TabPanel} this
22167 * @return {Boolean} false if there is an error
22170 'beforedeactivate': true
22173 this.tabId = this.tabId || Roo.id();
22177 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22184 touchSlide : false,
22185 getAutoCreate : function(){
22190 // item is needed for carousel - not sure if it has any effect otherwise
22191 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22192 html: this.html || ''
22196 cfg.cls += ' active';
22200 cfg.tabId = this.tabId;
22208 initEvents: function()
22210 var p = this.parent();
22212 this.navId = this.navId || p.navId;
22214 if (typeof(this.navId) != 'undefined') {
22215 // not really needed.. but just in case.. parent should be a NavGroup.
22216 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22220 var i = tg.tabs.length - 1;
22222 if(this.active && tg.bullets > 0 && i < tg.bullets){
22223 tg.setActiveBullet(i);
22227 this.el.on('click', this.onClick, this);
22229 if(Roo.isTouch && this.touchSlide){
22230 this.el.on("touchstart", this.onTouchStart, this);
22231 this.el.on("touchmove", this.onTouchMove, this);
22232 this.el.on("touchend", this.onTouchEnd, this);
22237 onRender : function(ct, position)
22239 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22242 setActive : function(state)
22244 Roo.log("panel - set active " + this.tabId + "=" + state);
22246 this.active = state;
22248 this.el.removeClass('active');
22250 } else if (!this.el.hasClass('active')) {
22251 this.el.addClass('active');
22254 this.fireEvent('changed', this, state);
22257 onClick : function(e)
22259 e.preventDefault();
22261 if(!this.href.length){
22265 window.location.href = this.href;
22274 onTouchStart : function(e)
22276 this.swiping = false;
22278 this.startX = e.browserEvent.touches[0].clientX;
22279 this.startY = e.browserEvent.touches[0].clientY;
22282 onTouchMove : function(e)
22284 this.swiping = true;
22286 this.endX = e.browserEvent.touches[0].clientX;
22287 this.endY = e.browserEvent.touches[0].clientY;
22290 onTouchEnd : function(e)
22297 var tabGroup = this.parent();
22299 if(this.endX > this.startX){ // swiping right
22300 tabGroup.showPanelPrev();
22304 if(this.startX > this.endX){ // swiping left
22305 tabGroup.showPanelNext();
22324 * @class Roo.bootstrap.DateField
22325 * @extends Roo.bootstrap.Input
22326 * Bootstrap DateField class
22327 * @cfg {Number} weekStart default 0
22328 * @cfg {String} viewMode default empty, (months|years)
22329 * @cfg {String} minViewMode default empty, (months|years)
22330 * @cfg {Number} startDate default -Infinity
22331 * @cfg {Number} endDate default Infinity
22332 * @cfg {Boolean} todayHighlight default false
22333 * @cfg {Boolean} todayBtn default false
22334 * @cfg {Boolean} calendarWeeks default false
22335 * @cfg {Object} daysOfWeekDisabled default empty
22336 * @cfg {Boolean} singleMode default false (true | false)
22338 * @cfg {Boolean} keyboardNavigation default true
22339 * @cfg {String} language default en
22342 * Create a new DateField
22343 * @param {Object} config The config object
22346 Roo.bootstrap.DateField = function(config){
22347 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22351 * Fires when this field show.
22352 * @param {Roo.bootstrap.DateField} this
22353 * @param {Mixed} date The date value
22358 * Fires when this field hide.
22359 * @param {Roo.bootstrap.DateField} this
22360 * @param {Mixed} date The date value
22365 * Fires when select a date.
22366 * @param {Roo.bootstrap.DateField} this
22367 * @param {Mixed} date The date value
22371 * @event beforeselect
22372 * Fires when before select a date.
22373 * @param {Roo.bootstrap.DateField} this
22374 * @param {Mixed} date The date value
22376 beforeselect : true
22380 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22383 * @cfg {String} format
22384 * The default date format string which can be overriden for localization support. The format must be
22385 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22389 * @cfg {String} altFormats
22390 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22391 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22393 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22401 todayHighlight : false,
22407 keyboardNavigation: true,
22409 calendarWeeks: false,
22411 startDate: -Infinity,
22415 daysOfWeekDisabled: [],
22419 singleMode : false,
22421 UTCDate: function()
22423 return new Date(Date.UTC.apply(Date, arguments));
22426 UTCToday: function()
22428 var today = new Date();
22429 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22432 getDate: function() {
22433 var d = this.getUTCDate();
22434 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22437 getUTCDate: function() {
22441 setDate: function(d) {
22442 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22445 setUTCDate: function(d) {
22447 this.setValue(this.formatDate(this.date));
22450 onRender: function(ct, position)
22453 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22455 this.language = this.language || 'en';
22456 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22457 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22459 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22460 this.format = this.format || 'm/d/y';
22461 this.isInline = false;
22462 this.isInput = true;
22463 this.component = this.el.select('.add-on', true).first() || false;
22464 this.component = (this.component && this.component.length === 0) ? false : this.component;
22465 this.hasInput = this.component && this.inputEl().length;
22467 if (typeof(this.minViewMode === 'string')) {
22468 switch (this.minViewMode) {
22470 this.minViewMode = 1;
22473 this.minViewMode = 2;
22476 this.minViewMode = 0;
22481 if (typeof(this.viewMode === 'string')) {
22482 switch (this.viewMode) {
22495 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22497 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22499 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22501 this.picker().on('mousedown', this.onMousedown, this);
22502 this.picker().on('click', this.onClick, this);
22504 this.picker().addClass('datepicker-dropdown');
22506 this.startViewMode = this.viewMode;
22508 if(this.singleMode){
22509 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22510 v.setVisibilityMode(Roo.Element.DISPLAY);
22514 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22515 v.setStyle('width', '189px');
22519 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22520 if(!this.calendarWeeks){
22525 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22526 v.attr('colspan', function(i, val){
22527 return parseInt(val) + 1;
22532 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22534 this.setStartDate(this.startDate);
22535 this.setEndDate(this.endDate);
22537 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22544 if(this.isInline) {
22549 picker : function()
22551 return this.pickerEl;
22552 // return this.el.select('.datepicker', true).first();
22555 fillDow: function()
22557 var dowCnt = this.weekStart;
22566 if(this.calendarWeeks){
22574 while (dowCnt < this.weekStart + 7) {
22578 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22582 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22585 fillMonths: function()
22588 var months = this.picker().select('>.datepicker-months td', true).first();
22590 months.dom.innerHTML = '';
22596 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22599 months.createChild(month);
22606 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;
22608 if (this.date < this.startDate) {
22609 this.viewDate = new Date(this.startDate);
22610 } else if (this.date > this.endDate) {
22611 this.viewDate = new Date(this.endDate);
22613 this.viewDate = new Date(this.date);
22621 var d = new Date(this.viewDate),
22622 year = d.getUTCFullYear(),
22623 month = d.getUTCMonth(),
22624 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22625 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22626 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22627 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22628 currentDate = this.date && this.date.valueOf(),
22629 today = this.UTCToday();
22631 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22633 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22635 // this.picker.select('>tfoot th.today').
22636 // .text(dates[this.language].today)
22637 // .toggle(this.todayBtn !== false);
22639 this.updateNavArrows();
22642 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22644 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22646 prevMonth.setUTCDate(day);
22648 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22650 var nextMonth = new Date(prevMonth);
22652 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22654 nextMonth = nextMonth.valueOf();
22656 var fillMonths = false;
22658 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22660 while(prevMonth.valueOf() <= nextMonth) {
22663 if (prevMonth.getUTCDay() === this.weekStart) {
22665 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22673 if(this.calendarWeeks){
22674 // ISO 8601: First week contains first thursday.
22675 // ISO also states week starts on Monday, but we can be more abstract here.
22677 // Start of current week: based on weekstart/current date
22678 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22679 // Thursday of this week
22680 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22681 // First Thursday of year, year from thursday
22682 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22683 // Calendar week: ms between thursdays, div ms per day, div 7 days
22684 calWeek = (th - yth) / 864e5 / 7 + 1;
22686 fillMonths.cn.push({
22694 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22696 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22699 if (this.todayHighlight &&
22700 prevMonth.getUTCFullYear() == today.getFullYear() &&
22701 prevMonth.getUTCMonth() == today.getMonth() &&
22702 prevMonth.getUTCDate() == today.getDate()) {
22703 clsName += ' today';
22706 if (currentDate && prevMonth.valueOf() === currentDate) {
22707 clsName += ' active';
22710 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22711 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22712 clsName += ' disabled';
22715 fillMonths.cn.push({
22717 cls: 'day ' + clsName,
22718 html: prevMonth.getDate()
22721 prevMonth.setDate(prevMonth.getDate()+1);
22724 var currentYear = this.date && this.date.getUTCFullYear();
22725 var currentMonth = this.date && this.date.getUTCMonth();
22727 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22729 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22730 v.removeClass('active');
22732 if(currentYear === year && k === currentMonth){
22733 v.addClass('active');
22736 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22737 v.addClass('disabled');
22743 year = parseInt(year/10, 10) * 10;
22745 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22747 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22750 for (var i = -1; i < 11; i++) {
22751 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22753 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22761 showMode: function(dir)
22764 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22767 Roo.each(this.picker().select('>div',true).elements, function(v){
22768 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22771 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22776 if(this.isInline) {
22780 this.picker().removeClass(['bottom', 'top']);
22782 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22784 * place to the top of element!
22788 this.picker().addClass('top');
22789 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22794 this.picker().addClass('bottom');
22796 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22799 parseDate : function(value)
22801 if(!value || value instanceof Date){
22804 var v = Date.parseDate(value, this.format);
22805 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22806 v = Date.parseDate(value, 'Y-m-d');
22808 if(!v && this.altFormats){
22809 if(!this.altFormatsArray){
22810 this.altFormatsArray = this.altFormats.split("|");
22812 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22813 v = Date.parseDate(value, this.altFormatsArray[i]);
22819 formatDate : function(date, fmt)
22821 return (!date || !(date instanceof Date)) ?
22822 date : date.dateFormat(fmt || this.format);
22825 onFocus : function()
22827 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22831 onBlur : function()
22833 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22835 var d = this.inputEl().getValue();
22842 showPopup : function()
22844 this.picker().show();
22848 this.fireEvent('showpopup', this, this.date);
22851 hidePopup : function()
22853 if(this.isInline) {
22856 this.picker().hide();
22857 this.viewMode = this.startViewMode;
22860 this.fireEvent('hidepopup', this, this.date);
22864 onMousedown: function(e)
22866 e.stopPropagation();
22867 e.preventDefault();
22872 Roo.bootstrap.DateField.superclass.keyup.call(this);
22876 setValue: function(v)
22878 if(this.fireEvent('beforeselect', this, v) !== false){
22879 var d = new Date(this.parseDate(v) ).clearTime();
22881 if(isNaN(d.getTime())){
22882 this.date = this.viewDate = '';
22883 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22887 v = this.formatDate(d);
22889 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22891 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22895 this.fireEvent('select', this, this.date);
22899 getValue: function()
22901 return this.formatDate(this.date);
22904 fireKey: function(e)
22906 if (!this.picker().isVisible()){
22907 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22913 var dateChanged = false,
22915 newDate, newViewDate;
22920 e.preventDefault();
22924 if (!this.keyboardNavigation) {
22927 dir = e.keyCode == 37 ? -1 : 1;
22930 newDate = this.moveYear(this.date, dir);
22931 newViewDate = this.moveYear(this.viewDate, dir);
22932 } else if (e.shiftKey){
22933 newDate = this.moveMonth(this.date, dir);
22934 newViewDate = this.moveMonth(this.viewDate, dir);
22936 newDate = new Date(this.date);
22937 newDate.setUTCDate(this.date.getUTCDate() + dir);
22938 newViewDate = new Date(this.viewDate);
22939 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22941 if (this.dateWithinRange(newDate)){
22942 this.date = newDate;
22943 this.viewDate = newViewDate;
22944 this.setValue(this.formatDate(this.date));
22946 e.preventDefault();
22947 dateChanged = true;
22952 if (!this.keyboardNavigation) {
22955 dir = e.keyCode == 38 ? -1 : 1;
22957 newDate = this.moveYear(this.date, dir);
22958 newViewDate = this.moveYear(this.viewDate, dir);
22959 } else if (e.shiftKey){
22960 newDate = this.moveMonth(this.date, dir);
22961 newViewDate = this.moveMonth(this.viewDate, dir);
22963 newDate = new Date(this.date);
22964 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22965 newViewDate = new Date(this.viewDate);
22966 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22968 if (this.dateWithinRange(newDate)){
22969 this.date = newDate;
22970 this.viewDate = newViewDate;
22971 this.setValue(this.formatDate(this.date));
22973 e.preventDefault();
22974 dateChanged = true;
22978 this.setValue(this.formatDate(this.date));
22980 e.preventDefault();
22983 this.setValue(this.formatDate(this.date));
22997 onClick: function(e)
22999 e.stopPropagation();
23000 e.preventDefault();
23002 var target = e.getTarget();
23004 if(target.nodeName.toLowerCase() === 'i'){
23005 target = Roo.get(target).dom.parentNode;
23008 var nodeName = target.nodeName;
23009 var className = target.className;
23010 var html = target.innerHTML;
23011 //Roo.log(nodeName);
23013 switch(nodeName.toLowerCase()) {
23015 switch(className) {
23021 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23022 switch(this.viewMode){
23024 this.viewDate = this.moveMonth(this.viewDate, dir);
23028 this.viewDate = this.moveYear(this.viewDate, dir);
23034 var date = new Date();
23035 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23037 this.setValue(this.formatDate(this.date));
23044 if (className.indexOf('disabled') < 0) {
23045 if (!this.viewDate) {
23046 this.viewDate = new Date();
23048 this.viewDate.setUTCDate(1);
23049 if (className.indexOf('month') > -1) {
23050 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23052 var year = parseInt(html, 10) || 0;
23053 this.viewDate.setUTCFullYear(year);
23057 if(this.singleMode){
23058 this.setValue(this.formatDate(this.viewDate));
23069 //Roo.log(className);
23070 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23071 var day = parseInt(html, 10) || 1;
23072 var year = (this.viewDate || new Date()).getUTCFullYear(),
23073 month = (this.viewDate || new Date()).getUTCMonth();
23075 if (className.indexOf('old') > -1) {
23082 } else if (className.indexOf('new') > -1) {
23090 //Roo.log([year,month,day]);
23091 this.date = this.UTCDate(year, month, day,0,0,0,0);
23092 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23094 //Roo.log(this.formatDate(this.date));
23095 this.setValue(this.formatDate(this.date));
23102 setStartDate: function(startDate)
23104 this.startDate = startDate || -Infinity;
23105 if (this.startDate !== -Infinity) {
23106 this.startDate = this.parseDate(this.startDate);
23109 this.updateNavArrows();
23112 setEndDate: function(endDate)
23114 this.endDate = endDate || Infinity;
23115 if (this.endDate !== Infinity) {
23116 this.endDate = this.parseDate(this.endDate);
23119 this.updateNavArrows();
23122 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23124 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23125 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23126 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23128 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23129 return parseInt(d, 10);
23132 this.updateNavArrows();
23135 updateNavArrows: function()
23137 if(this.singleMode){
23141 var d = new Date(this.viewDate),
23142 year = d.getUTCFullYear(),
23143 month = d.getUTCMonth();
23145 Roo.each(this.picker().select('.prev', true).elements, function(v){
23147 switch (this.viewMode) {
23150 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23156 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23163 Roo.each(this.picker().select('.next', true).elements, function(v){
23165 switch (this.viewMode) {
23168 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23174 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23182 moveMonth: function(date, dir)
23187 var new_date = new Date(date.valueOf()),
23188 day = new_date.getUTCDate(),
23189 month = new_date.getUTCMonth(),
23190 mag = Math.abs(dir),
23192 dir = dir > 0 ? 1 : -1;
23195 // If going back one month, make sure month is not current month
23196 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23198 return new_date.getUTCMonth() == month;
23200 // If going forward one month, make sure month is as expected
23201 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23203 return new_date.getUTCMonth() != new_month;
23205 new_month = month + dir;
23206 new_date.setUTCMonth(new_month);
23207 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23208 if (new_month < 0 || new_month > 11) {
23209 new_month = (new_month + 12) % 12;
23212 // For magnitudes >1, move one month at a time...
23213 for (var i=0; i<mag; i++) {
23214 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23215 new_date = this.moveMonth(new_date, dir);
23217 // ...then reset the day, keeping it in the new month
23218 new_month = new_date.getUTCMonth();
23219 new_date.setUTCDate(day);
23221 return new_month != new_date.getUTCMonth();
23224 // Common date-resetting loop -- if date is beyond end of month, make it
23227 new_date.setUTCDate(--day);
23228 new_date.setUTCMonth(new_month);
23233 moveYear: function(date, dir)
23235 return this.moveMonth(date, dir*12);
23238 dateWithinRange: function(date)
23240 return date >= this.startDate && date <= this.endDate;
23246 this.picker().remove();
23249 validateValue : function(value)
23251 if(this.getVisibilityEl().hasClass('hidden')){
23255 if(value.length < 1) {
23256 if(this.allowBlank){
23262 if(value.length < this.minLength){
23265 if(value.length > this.maxLength){
23269 var vt = Roo.form.VTypes;
23270 if(!vt[this.vtype](value, this)){
23274 if(typeof this.validator == "function"){
23275 var msg = this.validator(value);
23281 if(this.regex && !this.regex.test(value)){
23285 if(typeof(this.parseDate(value)) == 'undefined'){
23289 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23293 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23303 this.date = this.viewDate = '';
23305 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23310 Roo.apply(Roo.bootstrap.DateField, {
23321 html: '<i class="fa fa-arrow-left"/>'
23331 html: '<i class="fa fa-arrow-right"/>'
23373 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23374 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23375 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23376 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23377 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23390 navFnc: 'FullYear',
23395 navFnc: 'FullYear',
23400 Roo.apply(Roo.bootstrap.DateField, {
23404 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23408 cls: 'datepicker-days',
23412 cls: 'table-condensed',
23414 Roo.bootstrap.DateField.head,
23418 Roo.bootstrap.DateField.footer
23425 cls: 'datepicker-months',
23429 cls: 'table-condensed',
23431 Roo.bootstrap.DateField.head,
23432 Roo.bootstrap.DateField.content,
23433 Roo.bootstrap.DateField.footer
23440 cls: 'datepicker-years',
23444 cls: 'table-condensed',
23446 Roo.bootstrap.DateField.head,
23447 Roo.bootstrap.DateField.content,
23448 Roo.bootstrap.DateField.footer
23467 * @class Roo.bootstrap.TimeField
23468 * @extends Roo.bootstrap.Input
23469 * Bootstrap DateField class
23473 * Create a new TimeField
23474 * @param {Object} config The config object
23477 Roo.bootstrap.TimeField = function(config){
23478 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23482 * Fires when this field show.
23483 * @param {Roo.bootstrap.DateField} thisthis
23484 * @param {Mixed} date The date value
23489 * Fires when this field hide.
23490 * @param {Roo.bootstrap.DateField} this
23491 * @param {Mixed} date The date value
23496 * Fires when select a date.
23497 * @param {Roo.bootstrap.DateField} this
23498 * @param {Mixed} date The date value
23504 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23507 * @cfg {String} format
23508 * The default time format string which can be overriden for localization support. The format must be
23509 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23513 getAutoCreate : function()
23515 this.after = '<i class="fa far fa-clock"></i>';
23516 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23520 onRender: function(ct, position)
23523 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23525 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23527 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23529 this.pop = this.picker().select('>.datepicker-time',true).first();
23530 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23532 this.picker().on('mousedown', this.onMousedown, this);
23533 this.picker().on('click', this.onClick, this);
23535 this.picker().addClass('datepicker-dropdown');
23540 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23541 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23542 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23543 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23544 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23545 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23549 fireKey: function(e){
23550 if (!this.picker().isVisible()){
23551 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23557 e.preventDefault();
23565 this.onTogglePeriod();
23568 this.onIncrementMinutes();
23571 this.onDecrementMinutes();
23580 onClick: function(e) {
23581 e.stopPropagation();
23582 e.preventDefault();
23585 picker : function()
23587 return this.pickerEl;
23590 fillTime: function()
23592 var time = this.pop.select('tbody', true).first();
23594 time.dom.innerHTML = '';
23609 cls: 'hours-up fa fas fa-chevron-up'
23629 cls: 'minutes-up fa fas fa-chevron-up'
23650 cls: 'timepicker-hour',
23665 cls: 'timepicker-minute',
23680 cls: 'btn btn-primary period',
23702 cls: 'hours-down fa fas fa-chevron-down'
23722 cls: 'minutes-down fa fas fa-chevron-down'
23740 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23747 var hours = this.time.getHours();
23748 var minutes = this.time.getMinutes();
23761 hours = hours - 12;
23765 hours = '0' + hours;
23769 minutes = '0' + minutes;
23772 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23773 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23774 this.pop.select('button', true).first().dom.innerHTML = period;
23780 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23782 var cls = ['bottom'];
23784 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23791 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23795 //this.picker().setXY(20000,20000);
23796 this.picker().addClass(cls.join('-'));
23800 Roo.each(cls, function(c){
23805 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23806 //_this.picker().setTop(_this.inputEl().getHeight());
23810 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23812 //_this.picker().setTop(0 - _this.picker().getHeight());
23817 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23821 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23829 onFocus : function()
23831 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23835 onBlur : function()
23837 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23843 this.picker().show();
23848 this.fireEvent('show', this, this.date);
23853 this.picker().hide();
23856 this.fireEvent('hide', this, this.date);
23859 setTime : function()
23862 this.setValue(this.time.format(this.format));
23864 this.fireEvent('select', this, this.date);
23869 onMousedown: function(e){
23870 e.stopPropagation();
23871 e.preventDefault();
23874 onIncrementHours: function()
23876 Roo.log('onIncrementHours');
23877 this.time = this.time.add(Date.HOUR, 1);
23882 onDecrementHours: function()
23884 Roo.log('onDecrementHours');
23885 this.time = this.time.add(Date.HOUR, -1);
23889 onIncrementMinutes: function()
23891 Roo.log('onIncrementMinutes');
23892 this.time = this.time.add(Date.MINUTE, 1);
23896 onDecrementMinutes: function()
23898 Roo.log('onDecrementMinutes');
23899 this.time = this.time.add(Date.MINUTE, -1);
23903 onTogglePeriod: function()
23905 Roo.log('onTogglePeriod');
23906 this.time = this.time.add(Date.HOUR, 12);
23914 Roo.apply(Roo.bootstrap.TimeField, {
23918 cls: 'datepicker dropdown-menu',
23922 cls: 'datepicker-time',
23926 cls: 'table-condensed',
23955 cls: 'btn btn-info ok',
23983 * @class Roo.bootstrap.MonthField
23984 * @extends Roo.bootstrap.Input
23985 * Bootstrap MonthField class
23987 * @cfg {String} language default en
23990 * Create a new MonthField
23991 * @param {Object} config The config object
23994 Roo.bootstrap.MonthField = function(config){
23995 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24000 * Fires when this field show.
24001 * @param {Roo.bootstrap.MonthField} this
24002 * @param {Mixed} date The date value
24007 * Fires when this field hide.
24008 * @param {Roo.bootstrap.MonthField} this
24009 * @param {Mixed} date The date value
24014 * Fires when select a date.
24015 * @param {Roo.bootstrap.MonthField} this
24016 * @param {String} oldvalue The old value
24017 * @param {String} newvalue The new value
24023 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
24025 onRender: function(ct, position)
24028 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24030 this.language = this.language || 'en';
24031 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24032 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24034 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24035 this.isInline = false;
24036 this.isInput = true;
24037 this.component = this.el.select('.add-on', true).first() || false;
24038 this.component = (this.component && this.component.length === 0) ? false : this.component;
24039 this.hasInput = this.component && this.inputEL().length;
24041 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24043 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24045 this.picker().on('mousedown', this.onMousedown, this);
24046 this.picker().on('click', this.onClick, this);
24048 this.picker().addClass('datepicker-dropdown');
24050 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24051 v.setStyle('width', '189px');
24058 if(this.isInline) {
24064 setValue: function(v, suppressEvent)
24066 var o = this.getValue();
24068 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24072 if(suppressEvent !== true){
24073 this.fireEvent('select', this, o, v);
24078 getValue: function()
24083 onClick: function(e)
24085 e.stopPropagation();
24086 e.preventDefault();
24088 var target = e.getTarget();
24090 if(target.nodeName.toLowerCase() === 'i'){
24091 target = Roo.get(target).dom.parentNode;
24094 var nodeName = target.nodeName;
24095 var className = target.className;
24096 var html = target.innerHTML;
24098 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24102 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24104 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24110 picker : function()
24112 return this.pickerEl;
24115 fillMonths: function()
24118 var months = this.picker().select('>.datepicker-months td', true).first();
24120 months.dom.innerHTML = '';
24126 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24129 months.createChild(month);
24138 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24139 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24142 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24143 e.removeClass('active');
24145 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24146 e.addClass('active');
24153 if(this.isInline) {
24157 this.picker().removeClass(['bottom', 'top']);
24159 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24161 * place to the top of element!
24165 this.picker().addClass('top');
24166 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24171 this.picker().addClass('bottom');
24173 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24176 onFocus : function()
24178 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24182 onBlur : function()
24184 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24186 var d = this.inputEl().getValue();
24195 this.picker().show();
24196 this.picker().select('>.datepicker-months', true).first().show();
24200 this.fireEvent('show', this, this.date);
24205 if(this.isInline) {
24208 this.picker().hide();
24209 this.fireEvent('hide', this, this.date);
24213 onMousedown: function(e)
24215 e.stopPropagation();
24216 e.preventDefault();
24221 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24225 fireKey: function(e)
24227 if (!this.picker().isVisible()){
24228 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24239 e.preventDefault();
24243 dir = e.keyCode == 37 ? -1 : 1;
24245 this.vIndex = this.vIndex + dir;
24247 if(this.vIndex < 0){
24251 if(this.vIndex > 11){
24255 if(isNaN(this.vIndex)){
24259 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24265 dir = e.keyCode == 38 ? -1 : 1;
24267 this.vIndex = this.vIndex + dir * 4;
24269 if(this.vIndex < 0){
24273 if(this.vIndex > 11){
24277 if(isNaN(this.vIndex)){
24281 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24286 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24287 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24291 e.preventDefault();
24294 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24295 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24311 this.picker().remove();
24316 Roo.apply(Roo.bootstrap.MonthField, {
24335 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24336 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24341 Roo.apply(Roo.bootstrap.MonthField, {
24345 cls: 'datepicker dropdown-menu roo-dynamic',
24349 cls: 'datepicker-months',
24353 cls: 'table-condensed',
24355 Roo.bootstrap.DateField.content
24375 * @class Roo.bootstrap.CheckBox
24376 * @extends Roo.bootstrap.Input
24377 * Bootstrap CheckBox class
24379 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24380 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24381 * @cfg {String} boxLabel The text that appears beside the checkbox
24382 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24383 * @cfg {Boolean} checked initnal the element
24384 * @cfg {Boolean} inline inline the element (default false)
24385 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24386 * @cfg {String} tooltip label tooltip
24389 * Create a new CheckBox
24390 * @param {Object} config The config object
24393 Roo.bootstrap.CheckBox = function(config){
24394 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24399 * Fires when the element is checked or unchecked.
24400 * @param {Roo.bootstrap.CheckBox} this This input
24401 * @param {Boolean} checked The new checked value
24406 * Fires when the element is click.
24407 * @param {Roo.bootstrap.CheckBox} this This input
24414 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24416 inputType: 'checkbox',
24425 // checkbox success does not make any sense really..
24430 getAutoCreate : function()
24432 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24438 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24441 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24447 type : this.inputType,
24448 value : this.inputValue,
24449 cls : 'roo-' + this.inputType, //'form-box',
24450 placeholder : this.placeholder || ''
24454 if(this.inputType != 'radio'){
24458 cls : 'roo-hidden-value',
24459 value : this.checked ? this.inputValue : this.valueOff
24464 if (this.weight) { // Validity check?
24465 cfg.cls += " " + this.inputType + "-" + this.weight;
24468 if (this.disabled) {
24469 input.disabled=true;
24473 input.checked = this.checked;
24478 input.name = this.name;
24480 if(this.inputType != 'radio'){
24481 hidden.name = this.name;
24482 input.name = '_hidden_' + this.name;
24487 input.cls += ' input-' + this.size;
24492 ['xs','sm','md','lg'].map(function(size){
24493 if (settings[size]) {
24494 cfg.cls += ' col-' + size + '-' + settings[size];
24498 var inputblock = input;
24500 if (this.before || this.after) {
24503 cls : 'input-group',
24508 inputblock.cn.push({
24510 cls : 'input-group-addon',
24515 inputblock.cn.push(input);
24517 if(this.inputType != 'radio'){
24518 inputblock.cn.push(hidden);
24522 inputblock.cn.push({
24524 cls : 'input-group-addon',
24530 var boxLabelCfg = false;
24536 //'for': id, // box label is handled by onclick - so no for...
24538 html: this.boxLabel
24541 boxLabelCfg.tooltip = this.tooltip;
24547 if (align ==='left' && this.fieldLabel.length) {
24548 // Roo.log("left and has label");
24553 cls : 'control-label',
24554 html : this.fieldLabel
24565 cfg.cn[1].cn.push(boxLabelCfg);
24568 if(this.labelWidth > 12){
24569 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24572 if(this.labelWidth < 13 && this.labelmd == 0){
24573 this.labelmd = this.labelWidth;
24576 if(this.labellg > 0){
24577 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24578 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24581 if(this.labelmd > 0){
24582 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24583 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24586 if(this.labelsm > 0){
24587 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24588 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24591 if(this.labelxs > 0){
24592 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24593 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24596 } else if ( this.fieldLabel.length) {
24597 // Roo.log(" label");
24601 tag: this.boxLabel ? 'span' : 'label',
24603 cls: 'control-label box-input-label',
24604 //cls : 'input-group-addon',
24605 html : this.fieldLabel
24612 cfg.cn.push(boxLabelCfg);
24617 // Roo.log(" no label && no align");
24618 cfg.cn = [ inputblock ] ;
24620 cfg.cn.push(boxLabelCfg);
24628 if(this.inputType != 'radio'){
24629 cfg.cn.push(hidden);
24637 * return the real input element.
24639 inputEl: function ()
24641 return this.el.select('input.roo-' + this.inputType,true).first();
24643 hiddenEl: function ()
24645 return this.el.select('input.roo-hidden-value',true).first();
24648 labelEl: function()
24650 return this.el.select('label.control-label',true).first();
24652 /* depricated... */
24656 return this.labelEl();
24659 boxLabelEl: function()
24661 return this.el.select('label.box-label',true).first();
24664 initEvents : function()
24666 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24668 this.inputEl().on('click', this.onClick, this);
24670 if (this.boxLabel) {
24671 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24674 this.startValue = this.getValue();
24677 Roo.bootstrap.CheckBox.register(this);
24681 onClick : function(e)
24683 if(this.fireEvent('click', this, e) !== false){
24684 this.setChecked(!this.checked);
24689 setChecked : function(state,suppressEvent)
24691 this.startValue = this.getValue();
24693 if(this.inputType == 'radio'){
24695 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24696 e.dom.checked = false;
24699 this.inputEl().dom.checked = true;
24701 this.inputEl().dom.value = this.inputValue;
24703 if(suppressEvent !== true){
24704 this.fireEvent('check', this, true);
24712 this.checked = state;
24714 this.inputEl().dom.checked = state;
24717 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24719 if(suppressEvent !== true){
24720 this.fireEvent('check', this, state);
24726 getValue : function()
24728 if(this.inputType == 'radio'){
24729 return this.getGroupValue();
24732 return this.hiddenEl().dom.value;
24736 getGroupValue : function()
24738 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24742 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24745 setValue : function(v,suppressEvent)
24747 if(this.inputType == 'radio'){
24748 this.setGroupValue(v, suppressEvent);
24752 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24757 setGroupValue : function(v, suppressEvent)
24759 this.startValue = this.getValue();
24761 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24762 e.dom.checked = false;
24764 if(e.dom.value == v){
24765 e.dom.checked = true;
24769 if(suppressEvent !== true){
24770 this.fireEvent('check', this, true);
24778 validate : function()
24780 if(this.getVisibilityEl().hasClass('hidden')){
24786 (this.inputType == 'radio' && this.validateRadio()) ||
24787 (this.inputType == 'checkbox' && this.validateCheckbox())
24793 this.markInvalid();
24797 validateRadio : function()
24799 if(this.getVisibilityEl().hasClass('hidden')){
24803 if(this.allowBlank){
24809 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24810 if(!e.dom.checked){
24822 validateCheckbox : function()
24825 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24826 //return (this.getValue() == this.inputValue) ? true : false;
24829 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24837 for(var i in group){
24838 if(group[i].el.isVisible(true)){
24846 for(var i in group){
24851 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24858 * Mark this field as valid
24860 markValid : function()
24864 this.fireEvent('valid', this);
24866 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24869 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24876 if(this.inputType == 'radio'){
24877 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24878 var fg = e.findParent('.form-group', false, true);
24879 if (Roo.bootstrap.version == 3) {
24880 fg.removeClass([_this.invalidClass, _this.validClass]);
24881 fg.addClass(_this.validClass);
24883 fg.removeClass(['is-valid', 'is-invalid']);
24884 fg.addClass('is-valid');
24892 var fg = this.el.findParent('.form-group', false, true);
24893 if (Roo.bootstrap.version == 3) {
24894 fg.removeClass([this.invalidClass, this.validClass]);
24895 fg.addClass(this.validClass);
24897 fg.removeClass(['is-valid', 'is-invalid']);
24898 fg.addClass('is-valid');
24903 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24909 for(var i in group){
24910 var fg = group[i].el.findParent('.form-group', false, true);
24911 if (Roo.bootstrap.version == 3) {
24912 fg.removeClass([this.invalidClass, this.validClass]);
24913 fg.addClass(this.validClass);
24915 fg.removeClass(['is-valid', 'is-invalid']);
24916 fg.addClass('is-valid');
24922 * Mark this field as invalid
24923 * @param {String} msg The validation message
24925 markInvalid : function(msg)
24927 if(this.allowBlank){
24933 this.fireEvent('invalid', this, msg);
24935 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24938 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24942 label.markInvalid();
24945 if(this.inputType == 'radio'){
24947 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24948 var fg = e.findParent('.form-group', false, true);
24949 if (Roo.bootstrap.version == 3) {
24950 fg.removeClass([_this.invalidClass, _this.validClass]);
24951 fg.addClass(_this.invalidClass);
24953 fg.removeClass(['is-invalid', 'is-valid']);
24954 fg.addClass('is-invalid');
24962 var fg = this.el.findParent('.form-group', false, true);
24963 if (Roo.bootstrap.version == 3) {
24964 fg.removeClass([_this.invalidClass, _this.validClass]);
24965 fg.addClass(_this.invalidClass);
24967 fg.removeClass(['is-invalid', 'is-valid']);
24968 fg.addClass('is-invalid');
24973 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24979 for(var i in group){
24980 var fg = group[i].el.findParent('.form-group', false, true);
24981 if (Roo.bootstrap.version == 3) {
24982 fg.removeClass([_this.invalidClass, _this.validClass]);
24983 fg.addClass(_this.invalidClass);
24985 fg.removeClass(['is-invalid', 'is-valid']);
24986 fg.addClass('is-invalid');
24992 clearInvalid : function()
24994 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24996 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24998 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25000 if (label && label.iconEl) {
25001 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25002 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25006 disable : function()
25008 if(this.inputType != 'radio'){
25009 Roo.bootstrap.CheckBox.superclass.disable.call(this);
25016 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25017 _this.getActionEl().addClass(this.disabledClass);
25018 e.dom.disabled = true;
25022 this.disabled = true;
25023 this.fireEvent("disable", this);
25027 enable : function()
25029 if(this.inputType != 'radio'){
25030 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25037 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25038 _this.getActionEl().removeClass(this.disabledClass);
25039 e.dom.disabled = false;
25043 this.disabled = false;
25044 this.fireEvent("enable", this);
25048 setBoxLabel : function(v)
25053 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25059 Roo.apply(Roo.bootstrap.CheckBox, {
25064 * register a CheckBox Group
25065 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25067 register : function(checkbox)
25069 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25070 this.groups[checkbox.groupId] = {};
25073 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25077 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25081 * fetch a CheckBox Group based on the group ID
25082 * @param {string} the group ID
25083 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25085 get: function(groupId) {
25086 if (typeof(this.groups[groupId]) == 'undefined') {
25090 return this.groups[groupId] ;
25103 * @class Roo.bootstrap.Radio
25104 * @extends Roo.bootstrap.Component
25105 * Bootstrap Radio class
25106 * @cfg {String} boxLabel - the label associated
25107 * @cfg {String} value - the value of radio
25110 * Create a new Radio
25111 * @param {Object} config The config object
25113 Roo.bootstrap.Radio = function(config){
25114 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25118 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25124 getAutoCreate : function()
25128 cls : 'form-group radio',
25133 html : this.boxLabel
25141 initEvents : function()
25143 this.parent().register(this);
25145 this.el.on('click', this.onClick, this);
25149 onClick : function(e)
25151 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25152 this.setChecked(true);
25156 setChecked : function(state, suppressEvent)
25158 this.parent().setValue(this.value, suppressEvent);
25162 setBoxLabel : function(v)
25167 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25182 * @class Roo.bootstrap.SecurePass
25183 * @extends Roo.bootstrap.Input
25184 * Bootstrap SecurePass class
25188 * Create a new SecurePass
25189 * @param {Object} config The config object
25192 Roo.bootstrap.SecurePass = function (config) {
25193 // these go here, so the translation tool can replace them..
25195 PwdEmpty: "Please type a password, and then retype it to confirm.",
25196 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25197 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25198 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25199 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25200 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25201 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25202 TooWeak: "Your password is Too Weak."
25204 this.meterLabel = "Password strength:";
25205 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25206 this.meterClass = [
25207 "roo-password-meter-tooweak",
25208 "roo-password-meter-weak",
25209 "roo-password-meter-medium",
25210 "roo-password-meter-strong",
25211 "roo-password-meter-grey"
25216 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25219 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25221 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25223 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25224 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25225 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25226 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25227 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25228 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25229 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25239 * @cfg {String/Object} Label for the strength meter (defaults to
25240 * 'Password strength:')
25245 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25246 * ['Weak', 'Medium', 'Strong'])
25249 pwdStrengths: false,
25262 initEvents: function ()
25264 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25266 if (this.el.is('input[type=password]') && Roo.isSafari) {
25267 this.el.on('keydown', this.SafariOnKeyDown, this);
25270 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25273 onRender: function (ct, position)
25275 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25276 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25277 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25279 this.trigger.createChild({
25284 cls: 'roo-password-meter-grey col-xs-12',
25287 //width: this.meterWidth + 'px'
25291 cls: 'roo-password-meter-text'
25297 if (this.hideTrigger) {
25298 this.trigger.setDisplayed(false);
25300 this.setSize(this.width || '', this.height || '');
25303 onDestroy: function ()
25305 if (this.trigger) {
25306 this.trigger.removeAllListeners();
25307 this.trigger.remove();
25310 this.wrap.remove();
25312 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25315 checkStrength: function ()
25317 var pwd = this.inputEl().getValue();
25318 if (pwd == this._lastPwd) {
25323 if (this.ClientSideStrongPassword(pwd)) {
25325 } else if (this.ClientSideMediumPassword(pwd)) {
25327 } else if (this.ClientSideWeakPassword(pwd)) {
25333 Roo.log('strength1: ' + strength);
25335 //var pm = this.trigger.child('div/div/div').dom;
25336 var pm = this.trigger.child('div/div');
25337 pm.removeClass(this.meterClass);
25338 pm.addClass(this.meterClass[strength]);
25341 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25343 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25345 this._lastPwd = pwd;
25349 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25351 this._lastPwd = '';
25353 var pm = this.trigger.child('div/div');
25354 pm.removeClass(this.meterClass);
25355 pm.addClass('roo-password-meter-grey');
25358 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25361 this.inputEl().dom.type='password';
25364 validateValue: function (value)
25366 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25369 if (value.length == 0) {
25370 if (this.allowBlank) {
25371 this.clearInvalid();
25375 this.markInvalid(this.errors.PwdEmpty);
25376 this.errorMsg = this.errors.PwdEmpty;
25384 if (!value.match(/[\x21-\x7e]+/)) {
25385 this.markInvalid(this.errors.PwdBadChar);
25386 this.errorMsg = this.errors.PwdBadChar;
25389 if (value.length < 6) {
25390 this.markInvalid(this.errors.PwdShort);
25391 this.errorMsg = this.errors.PwdShort;
25394 if (value.length > 16) {
25395 this.markInvalid(this.errors.PwdLong);
25396 this.errorMsg = this.errors.PwdLong;
25400 if (this.ClientSideStrongPassword(value)) {
25402 } else if (this.ClientSideMediumPassword(value)) {
25404 } else if (this.ClientSideWeakPassword(value)) {
25411 if (strength < 2) {
25412 //this.markInvalid(this.errors.TooWeak);
25413 this.errorMsg = this.errors.TooWeak;
25418 console.log('strength2: ' + strength);
25420 //var pm = this.trigger.child('div/div/div').dom;
25422 var pm = this.trigger.child('div/div');
25423 pm.removeClass(this.meterClass);
25424 pm.addClass(this.meterClass[strength]);
25426 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25428 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25430 this.errorMsg = '';
25434 CharacterSetChecks: function (type)
25437 this.fResult = false;
25440 isctype: function (character, type)
25443 case this.kCapitalLetter:
25444 if (character >= 'A' && character <= 'Z') {
25449 case this.kSmallLetter:
25450 if (character >= 'a' && character <= 'z') {
25456 if (character >= '0' && character <= '9') {
25461 case this.kPunctuation:
25462 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25473 IsLongEnough: function (pwd, size)
25475 return !(pwd == null || isNaN(size) || pwd.length < size);
25478 SpansEnoughCharacterSets: function (word, nb)
25480 if (!this.IsLongEnough(word, nb))
25485 var characterSetChecks = new Array(
25486 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25487 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25490 for (var index = 0; index < word.length; ++index) {
25491 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25492 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25493 characterSetChecks[nCharSet].fResult = true;
25500 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25501 if (characterSetChecks[nCharSet].fResult) {
25506 if (nCharSets < nb) {
25512 ClientSideStrongPassword: function (pwd)
25514 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25517 ClientSideMediumPassword: function (pwd)
25519 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25522 ClientSideWeakPassword: function (pwd)
25524 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25527 })//<script type="text/javascript">
25530 * Based Ext JS Library 1.1.1
25531 * Copyright(c) 2006-2007, Ext JS, LLC.
25537 * @class Roo.HtmlEditorCore
25538 * @extends Roo.Component
25539 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25541 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25544 Roo.HtmlEditorCore = function(config){
25547 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25552 * @event initialize
25553 * Fires when the editor is fully initialized (including the iframe)
25554 * @param {Roo.HtmlEditorCore} this
25559 * Fires when the editor is first receives the focus. Any insertion must wait
25560 * until after this event.
25561 * @param {Roo.HtmlEditorCore} this
25565 * @event beforesync
25566 * Fires before the textarea is updated with content from the editor iframe. Return false
25567 * to cancel the sync.
25568 * @param {Roo.HtmlEditorCore} this
25569 * @param {String} html
25573 * @event beforepush
25574 * Fires before the iframe editor is updated with content from the textarea. Return false
25575 * to cancel the push.
25576 * @param {Roo.HtmlEditorCore} this
25577 * @param {String} html
25582 * Fires when the textarea is updated with content from the editor iframe.
25583 * @param {Roo.HtmlEditorCore} this
25584 * @param {String} html
25589 * Fires when the iframe editor is updated with content from the textarea.
25590 * @param {Roo.HtmlEditorCore} this
25591 * @param {String} html
25596 * @event editorevent
25597 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25598 * @param {Roo.HtmlEditorCore} this
25604 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25606 // defaults : white / black...
25607 this.applyBlacklists();
25614 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25618 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25624 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25629 * @cfg {Number} height (in pixels)
25633 * @cfg {Number} width (in pixels)
25638 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25641 stylesheets: false,
25644 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25646 allowComments: false,
25650 // private properties
25651 validationEvent : false,
25653 initialized : false,
25655 sourceEditMode : false,
25656 onFocus : Roo.emptyFn,
25658 hideMode:'offsets',
25662 // blacklist + whitelisted elements..
25669 * Protected method that will not generally be called directly. It
25670 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25671 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25673 getDocMarkup : function(){
25677 // inherit styels from page...??
25678 if (this.stylesheets === false) {
25680 Roo.get(document.head).select('style').each(function(node) {
25681 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25684 Roo.get(document.head).select('link').each(function(node) {
25685 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25688 } else if (!this.stylesheets.length) {
25690 st = '<style type="text/css">' +
25691 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25694 for (var i in this.stylesheets) {
25695 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25700 st += '<style type="text/css">' +
25701 'IMG { cursor: pointer } ' +
25704 var cls = 'roo-htmleditor-body';
25706 if(this.bodyCls.length){
25707 cls += ' ' + this.bodyCls;
25710 return '<html><head>' + st +
25711 //<style type="text/css">' +
25712 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25714 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25718 onRender : function(ct, position)
25721 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25722 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25725 this.el.dom.style.border = '0 none';
25726 this.el.dom.setAttribute('tabIndex', -1);
25727 this.el.addClass('x-hidden hide');
25731 if(Roo.isIE){ // fix IE 1px bogus margin
25732 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25736 this.frameId = Roo.id();
25740 var iframe = this.owner.wrap.createChild({
25742 cls: 'form-control', // bootstrap..
25744 name: this.frameId,
25745 frameBorder : 'no',
25746 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25751 this.iframe = iframe.dom;
25753 this.assignDocWin();
25755 this.doc.designMode = 'on';
25758 this.doc.write(this.getDocMarkup());
25762 var task = { // must defer to wait for browser to be ready
25764 //console.log("run task?" + this.doc.readyState);
25765 this.assignDocWin();
25766 if(this.doc.body || this.doc.readyState == 'complete'){
25768 this.doc.designMode="on";
25772 Roo.TaskMgr.stop(task);
25773 this.initEditor.defer(10, this);
25780 Roo.TaskMgr.start(task);
25785 onResize : function(w, h)
25787 Roo.log('resize: ' +w + ',' + h );
25788 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25792 if(typeof w == 'number'){
25794 this.iframe.style.width = w + 'px';
25796 if(typeof h == 'number'){
25798 this.iframe.style.height = h + 'px';
25800 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25807 * Toggles the editor between standard and source edit mode.
25808 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25810 toggleSourceEdit : function(sourceEditMode){
25812 this.sourceEditMode = sourceEditMode === true;
25814 if(this.sourceEditMode){
25816 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25819 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25820 //this.iframe.className = '';
25823 //this.setSize(this.owner.wrap.getSize());
25824 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25831 * Protected method that will not generally be called directly. If you need/want
25832 * custom HTML cleanup, this is the method you should override.
25833 * @param {String} html The HTML to be cleaned
25834 * return {String} The cleaned HTML
25836 cleanHtml : function(html){
25837 html = String(html);
25838 if(html.length > 5){
25839 if(Roo.isSafari){ // strip safari nonsense
25840 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25843 if(html == ' '){
25850 * HTML Editor -> Textarea
25851 * Protected method that will not generally be called directly. Syncs the contents
25852 * of the editor iframe with the textarea.
25854 syncValue : function(){
25855 if(this.initialized){
25856 var bd = (this.doc.body || this.doc.documentElement);
25857 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25858 var html = bd.innerHTML;
25860 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25861 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25863 html = '<div style="'+m[0]+'">' + html + '</div>';
25866 html = this.cleanHtml(html);
25867 // fix up the special chars.. normaly like back quotes in word...
25868 // however we do not want to do this with chinese..
25869 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25871 var cc = match.charCodeAt();
25873 // Get the character value, handling surrogate pairs
25874 if (match.length == 2) {
25875 // It's a surrogate pair, calculate the Unicode code point
25876 var high = match.charCodeAt(0) - 0xD800;
25877 var low = match.charCodeAt(1) - 0xDC00;
25878 cc = (high * 0x400) + low + 0x10000;
25880 (cc >= 0x4E00 && cc < 0xA000 ) ||
25881 (cc >= 0x3400 && cc < 0x4E00 ) ||
25882 (cc >= 0xf900 && cc < 0xfb00 )
25887 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25888 return "&#" + cc + ";";
25895 if(this.owner.fireEvent('beforesync', this, html) !== false){
25896 this.el.dom.value = html;
25897 this.owner.fireEvent('sync', this, html);
25903 * Protected method that will not generally be called directly. Pushes the value of the textarea
25904 * into the iframe editor.
25906 pushValue : function(){
25907 if(this.initialized){
25908 var v = this.el.dom.value.trim();
25910 // if(v.length < 1){
25914 if(this.owner.fireEvent('beforepush', this, v) !== false){
25915 var d = (this.doc.body || this.doc.documentElement);
25917 this.cleanUpPaste();
25918 this.el.dom.value = d.innerHTML;
25919 this.owner.fireEvent('push', this, v);
25925 deferFocus : function(){
25926 this.focus.defer(10, this);
25930 focus : function(){
25931 if(this.win && !this.sourceEditMode){
25938 assignDocWin: function()
25940 var iframe = this.iframe;
25943 this.doc = iframe.contentWindow.document;
25944 this.win = iframe.contentWindow;
25946 // if (!Roo.get(this.frameId)) {
25949 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25950 // this.win = Roo.get(this.frameId).dom.contentWindow;
25952 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25956 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25957 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25962 initEditor : function(){
25963 //console.log("INIT EDITOR");
25964 this.assignDocWin();
25968 this.doc.designMode="on";
25970 this.doc.write(this.getDocMarkup());
25973 var dbody = (this.doc.body || this.doc.documentElement);
25974 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25975 // this copies styles from the containing element into thsi one..
25976 // not sure why we need all of this..
25977 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25979 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25980 //ss['background-attachment'] = 'fixed'; // w3c
25981 dbody.bgProperties = 'fixed'; // ie
25982 //Roo.DomHelper.applyStyles(dbody, ss);
25983 Roo.EventManager.on(this.doc, {
25984 //'mousedown': this.onEditorEvent,
25985 'mouseup': this.onEditorEvent,
25986 'dblclick': this.onEditorEvent,
25987 'click': this.onEditorEvent,
25988 'keyup': this.onEditorEvent,
25993 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25995 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25996 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25998 this.initialized = true;
26000 this.owner.fireEvent('initialize', this);
26005 onDestroy : function(){
26011 //for (var i =0; i < this.toolbars.length;i++) {
26012 // // fixme - ask toolbars for heights?
26013 // this.toolbars[i].onDestroy();
26016 //this.wrap.dom.innerHTML = '';
26017 //this.wrap.remove();
26022 onFirstFocus : function(){
26024 this.assignDocWin();
26027 this.activated = true;
26030 if(Roo.isGecko){ // prevent silly gecko errors
26032 var s = this.win.getSelection();
26033 if(!s.focusNode || s.focusNode.nodeType != 3){
26034 var r = s.getRangeAt(0);
26035 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26040 this.execCmd('useCSS', true);
26041 this.execCmd('styleWithCSS', false);
26044 this.owner.fireEvent('activate', this);
26048 adjustFont: function(btn){
26049 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26050 //if(Roo.isSafari){ // safari
26053 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26054 if(Roo.isSafari){ // safari
26055 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26056 v = (v < 10) ? 10 : v;
26057 v = (v > 48) ? 48 : v;
26058 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26063 v = Math.max(1, v+adjust);
26065 this.execCmd('FontSize', v );
26068 onEditorEvent : function(e)
26070 this.owner.fireEvent('editorevent', this, e);
26071 // this.updateToolbar();
26072 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26075 insertTag : function(tg)
26077 // could be a bit smarter... -> wrap the current selected tRoo..
26078 if (tg.toLowerCase() == 'span' ||
26079 tg.toLowerCase() == 'code' ||
26080 tg.toLowerCase() == 'sup' ||
26081 tg.toLowerCase() == 'sub'
26084 range = this.createRange(this.getSelection());
26085 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26086 wrappingNode.appendChild(range.extractContents());
26087 range.insertNode(wrappingNode);
26094 this.execCmd("formatblock", tg);
26098 insertText : function(txt)
26102 var range = this.createRange();
26103 range.deleteContents();
26104 //alert(Sender.getAttribute('label'));
26106 range.insertNode(this.doc.createTextNode(txt));
26112 * Executes a Midas editor command on the editor document and performs necessary focus and
26113 * toolbar updates. <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 relayCmd : function(cmd, value){
26119 this.execCmd(cmd, value);
26120 this.owner.fireEvent('editorevent', this);
26121 //this.updateToolbar();
26122 this.owner.deferFocus();
26126 * Executes a Midas editor command directly on the editor document.
26127 * For visual commands, you should use {@link #relayCmd} instead.
26128 * <b>This should only be called after the editor is initialized.</b>
26129 * @param {String} cmd The Midas command
26130 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26132 execCmd : function(cmd, value){
26133 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26140 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26142 * @param {String} text | dom node..
26144 insertAtCursor : function(text)
26147 if(!this.activated){
26153 var r = this.doc.selection.createRange();
26164 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26168 // from jquery ui (MIT licenced)
26170 var win = this.win;
26172 if (win.getSelection && win.getSelection().getRangeAt) {
26173 range = win.getSelection().getRangeAt(0);
26174 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26175 range.insertNode(node);
26176 } else if (win.document.selection && win.document.selection.createRange) {
26177 // no firefox support
26178 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26179 win.document.selection.createRange().pasteHTML(txt);
26181 // no firefox support
26182 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26183 this.execCmd('InsertHTML', txt);
26192 mozKeyPress : function(e){
26194 var c = e.getCharCode(), cmd;
26197 c = String.fromCharCode(c).toLowerCase();
26211 this.cleanUpPaste.defer(100, this);
26219 e.preventDefault();
26227 fixKeys : function(){ // load time branching for fastest keydown performance
26229 return function(e){
26230 var k = e.getKey(), r;
26233 r = this.doc.selection.createRange();
26236 r.pasteHTML('    ');
26243 r = this.doc.selection.createRange();
26245 var target = r.parentElement();
26246 if(!target || target.tagName.toLowerCase() != 'li'){
26248 r.pasteHTML('<br />');
26254 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26255 this.cleanUpPaste.defer(100, this);
26261 }else if(Roo.isOpera){
26262 return function(e){
26263 var k = e.getKey();
26267 this.execCmd('InsertHTML','    ');
26270 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26271 this.cleanUpPaste.defer(100, this);
26276 }else if(Roo.isSafari){
26277 return function(e){
26278 var k = e.getKey();
26282 this.execCmd('InsertText','\t');
26286 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26287 this.cleanUpPaste.defer(100, this);
26295 getAllAncestors: function()
26297 var p = this.getSelectedNode();
26300 a.push(p); // push blank onto stack..
26301 p = this.getParentElement();
26305 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26309 a.push(this.doc.body);
26313 lastSelNode : false,
26316 getSelection : function()
26318 this.assignDocWin();
26319 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26322 getSelectedNode: function()
26324 // this may only work on Gecko!!!
26326 // should we cache this!!!!
26331 var range = this.createRange(this.getSelection()).cloneRange();
26334 var parent = range.parentElement();
26336 var testRange = range.duplicate();
26337 testRange.moveToElementText(parent);
26338 if (testRange.inRange(range)) {
26341 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26344 parent = parent.parentElement;
26349 // is ancestor a text element.
26350 var ac = range.commonAncestorContainer;
26351 if (ac.nodeType == 3) {
26352 ac = ac.parentNode;
26355 var ar = ac.childNodes;
26358 var other_nodes = [];
26359 var has_other_nodes = false;
26360 for (var i=0;i<ar.length;i++) {
26361 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26364 // fullly contained node.
26366 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26371 // probably selected..
26372 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26373 other_nodes.push(ar[i]);
26377 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26382 has_other_nodes = true;
26384 if (!nodes.length && other_nodes.length) {
26385 nodes= other_nodes;
26387 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26393 createRange: function(sel)
26395 // this has strange effects when using with
26396 // top toolbar - not sure if it's a great idea.
26397 //this.editor.contentWindow.focus();
26398 if (typeof sel != "undefined") {
26400 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26402 return this.doc.createRange();
26405 return this.doc.createRange();
26408 getParentElement: function()
26411 this.assignDocWin();
26412 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26414 var range = this.createRange(sel);
26417 var p = range.commonAncestorContainer;
26418 while (p.nodeType == 3) { // text node
26429 * Range intersection.. the hard stuff...
26433 * [ -- selected range --- ]
26437 * if end is before start or hits it. fail.
26438 * if start is after end or hits it fail.
26440 * if either hits (but other is outside. - then it's not
26446 // @see http://www.thismuchiknow.co.uk/?p=64.
26447 rangeIntersectsNode : function(range, node)
26449 var nodeRange = node.ownerDocument.createRange();
26451 nodeRange.selectNode(node);
26453 nodeRange.selectNodeContents(node);
26456 var rangeStartRange = range.cloneRange();
26457 rangeStartRange.collapse(true);
26459 var rangeEndRange = range.cloneRange();
26460 rangeEndRange.collapse(false);
26462 var nodeStartRange = nodeRange.cloneRange();
26463 nodeStartRange.collapse(true);
26465 var nodeEndRange = nodeRange.cloneRange();
26466 nodeEndRange.collapse(false);
26468 return rangeStartRange.compareBoundaryPoints(
26469 Range.START_TO_START, nodeEndRange) == -1 &&
26470 rangeEndRange.compareBoundaryPoints(
26471 Range.START_TO_START, nodeStartRange) == 1;
26475 rangeCompareNode : function(range, node)
26477 var nodeRange = node.ownerDocument.createRange();
26479 nodeRange.selectNode(node);
26481 nodeRange.selectNodeContents(node);
26485 range.collapse(true);
26487 nodeRange.collapse(true);
26489 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26490 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26492 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26494 var nodeIsBefore = ss == 1;
26495 var nodeIsAfter = ee == -1;
26497 if (nodeIsBefore && nodeIsAfter) {
26500 if (!nodeIsBefore && nodeIsAfter) {
26501 return 1; //right trailed.
26504 if (nodeIsBefore && !nodeIsAfter) {
26505 return 2; // left trailed.
26511 // private? - in a new class?
26512 cleanUpPaste : function()
26514 // cleans up the whole document..
26515 Roo.log('cleanuppaste');
26517 this.cleanUpChildren(this.doc.body);
26518 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26519 if (clean != this.doc.body.innerHTML) {
26520 this.doc.body.innerHTML = clean;
26525 cleanWordChars : function(input) {// change the chars to hex code
26526 var he = Roo.HtmlEditorCore;
26528 var output = input;
26529 Roo.each(he.swapCodes, function(sw) {
26530 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26532 output = output.replace(swapper, sw[1]);
26539 cleanUpChildren : function (n)
26541 if (!n.childNodes.length) {
26544 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26545 this.cleanUpChild(n.childNodes[i]);
26552 cleanUpChild : function (node)
26555 //console.log(node);
26556 if (node.nodeName == "#text") {
26557 // clean up silly Windows -- stuff?
26560 if (node.nodeName == "#comment") {
26561 if (!this.allowComments) {
26562 node.parentNode.removeChild(node);
26564 // clean up silly Windows -- stuff?
26567 var lcname = node.tagName.toLowerCase();
26568 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26569 // whitelist of tags..
26571 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26573 node.parentNode.removeChild(node);
26578 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26580 // spans with no attributes - just remove them..
26581 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26582 remove_keep_children = true;
26585 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26586 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26588 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26589 // remove_keep_children = true;
26592 if (remove_keep_children) {
26593 this.cleanUpChildren(node);
26594 // inserts everything just before this node...
26595 while (node.childNodes.length) {
26596 var cn = node.childNodes[0];
26597 node.removeChild(cn);
26598 node.parentNode.insertBefore(cn, node);
26600 node.parentNode.removeChild(node);
26604 if (!node.attributes || !node.attributes.length) {
26609 this.cleanUpChildren(node);
26613 function cleanAttr(n,v)
26616 if (v.match(/^\./) || v.match(/^\//)) {
26619 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26622 if (v.match(/^#/)) {
26625 if (v.match(/^\{/)) { // allow template editing.
26628 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26629 node.removeAttribute(n);
26633 var cwhite = this.cwhite;
26634 var cblack = this.cblack;
26636 function cleanStyle(n,v)
26638 if (v.match(/expression/)) { //XSS?? should we even bother..
26639 node.removeAttribute(n);
26643 var parts = v.split(/;/);
26646 Roo.each(parts, function(p) {
26647 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26651 var l = p.split(':').shift().replace(/\s+/g,'');
26652 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26654 if ( cwhite.length && cblack.indexOf(l) > -1) {
26655 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26656 //node.removeAttribute(n);
26660 // only allow 'c whitelisted system attributes'
26661 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26662 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26663 //node.removeAttribute(n);
26673 if (clean.length) {
26674 node.setAttribute(n, clean.join(';'));
26676 node.removeAttribute(n);
26682 for (var i = node.attributes.length-1; i > -1 ; i--) {
26683 var a = node.attributes[i];
26686 if (a.name.toLowerCase().substr(0,2)=='on') {
26687 node.removeAttribute(a.name);
26690 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26691 node.removeAttribute(a.name);
26694 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26695 cleanAttr(a.name,a.value); // fixme..
26698 if (a.name == 'style') {
26699 cleanStyle(a.name,a.value);
26702 /// clean up MS crap..
26703 // tecnically this should be a list of valid class'es..
26706 if (a.name == 'class') {
26707 if (a.value.match(/^Mso/)) {
26708 node.removeAttribute('class');
26711 if (a.value.match(/^body$/)) {
26712 node.removeAttribute('class');
26723 this.cleanUpChildren(node);
26729 * Clean up MS wordisms...
26731 cleanWord : function(node)
26734 this.cleanWord(this.doc.body);
26739 node.nodeName == 'SPAN' &&
26740 !node.hasAttributes() &&
26741 node.childNodes.length == 1 &&
26742 node.firstChild.nodeName == "#text"
26744 var textNode = node.firstChild;
26745 node.removeChild(textNode);
26746 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26747 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26749 node.parentNode.insertBefore(textNode, node);
26750 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26751 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26753 node.parentNode.removeChild(node);
26756 if (node.nodeName == "#text") {
26757 // clean up silly Windows -- stuff?
26760 if (node.nodeName == "#comment") {
26761 node.parentNode.removeChild(node);
26762 // clean up silly Windows -- stuff?
26766 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26767 node.parentNode.removeChild(node);
26770 //Roo.log(node.tagName);
26771 // remove - but keep children..
26772 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26773 //Roo.log('-- removed');
26774 while (node.childNodes.length) {
26775 var cn = node.childNodes[0];
26776 node.removeChild(cn);
26777 node.parentNode.insertBefore(cn, node);
26778 // move node to parent - and clean it..
26779 this.cleanWord(cn);
26781 node.parentNode.removeChild(node);
26782 /// no need to iterate chidlren = it's got none..
26783 //this.iterateChildren(node, this.cleanWord);
26787 if (node.className.length) {
26789 var cn = node.className.split(/\W+/);
26791 Roo.each(cn, function(cls) {
26792 if (cls.match(/Mso[a-zA-Z]+/)) {
26797 node.className = cna.length ? cna.join(' ') : '';
26799 node.removeAttribute("class");
26803 if (node.hasAttribute("lang")) {
26804 node.removeAttribute("lang");
26807 if (node.hasAttribute("style")) {
26809 var styles = node.getAttribute("style").split(";");
26811 Roo.each(styles, function(s) {
26812 if (!s.match(/:/)) {
26815 var kv = s.split(":");
26816 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26819 // what ever is left... we allow.
26822 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26823 if (!nstyle.length) {
26824 node.removeAttribute('style');
26827 this.iterateChildren(node, this.cleanWord);
26833 * iterateChildren of a Node, calling fn each time, using this as the scole..
26834 * @param {DomNode} node node to iterate children of.
26835 * @param {Function} fn method of this class to call on each item.
26837 iterateChildren : function(node, fn)
26839 if (!node.childNodes.length) {
26842 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26843 fn.call(this, node.childNodes[i])
26849 * cleanTableWidths.
26851 * Quite often pasting from word etc.. results in tables with column and widths.
26852 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26855 cleanTableWidths : function(node)
26860 this.cleanTableWidths(this.doc.body);
26865 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26868 Roo.log(node.tagName);
26869 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26870 this.iterateChildren(node, this.cleanTableWidths);
26873 if (node.hasAttribute('width')) {
26874 node.removeAttribute('width');
26878 if (node.hasAttribute("style")) {
26881 var styles = node.getAttribute("style").split(";");
26883 Roo.each(styles, function(s) {
26884 if (!s.match(/:/)) {
26887 var kv = s.split(":");
26888 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26891 // what ever is left... we allow.
26894 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26895 if (!nstyle.length) {
26896 node.removeAttribute('style');
26900 this.iterateChildren(node, this.cleanTableWidths);
26908 domToHTML : function(currentElement, depth, nopadtext) {
26910 depth = depth || 0;
26911 nopadtext = nopadtext || false;
26913 if (!currentElement) {
26914 return this.domToHTML(this.doc.body);
26917 //Roo.log(currentElement);
26919 var allText = false;
26920 var nodeName = currentElement.nodeName;
26921 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26923 if (nodeName == '#text') {
26925 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26930 if (nodeName != 'BODY') {
26933 // Prints the node tagName, such as <A>, <IMG>, etc
26936 for(i = 0; i < currentElement.attributes.length;i++) {
26938 var aname = currentElement.attributes.item(i).name;
26939 if (!currentElement.attributes.item(i).value.length) {
26942 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26945 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26954 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26957 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26962 // Traverse the tree
26964 var currentElementChild = currentElement.childNodes.item(i);
26965 var allText = true;
26966 var innerHTML = '';
26968 while (currentElementChild) {
26969 // Formatting code (indent the tree so it looks nice on the screen)
26970 var nopad = nopadtext;
26971 if (lastnode == 'SPAN') {
26975 if (currentElementChild.nodeName == '#text') {
26976 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26977 toadd = nopadtext ? toadd : toadd.trim();
26978 if (!nopad && toadd.length > 80) {
26979 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26981 innerHTML += toadd;
26984 currentElementChild = currentElement.childNodes.item(i);
26990 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26992 // Recursively traverse the tree structure of the child node
26993 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26994 lastnode = currentElementChild.nodeName;
26996 currentElementChild=currentElement.childNodes.item(i);
27002 // The remaining code is mostly for formatting the tree
27003 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27008 ret+= "</"+tagName+">";
27014 applyBlacklists : function()
27016 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27017 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27021 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27022 if (b.indexOf(tag) > -1) {
27025 this.white.push(tag);
27029 Roo.each(w, function(tag) {
27030 if (b.indexOf(tag) > -1) {
27033 if (this.white.indexOf(tag) > -1) {
27036 this.white.push(tag);
27041 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27042 if (w.indexOf(tag) > -1) {
27045 this.black.push(tag);
27049 Roo.each(b, function(tag) {
27050 if (w.indexOf(tag) > -1) {
27053 if (this.black.indexOf(tag) > -1) {
27056 this.black.push(tag);
27061 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27062 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27066 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27067 if (b.indexOf(tag) > -1) {
27070 this.cwhite.push(tag);
27074 Roo.each(w, function(tag) {
27075 if (b.indexOf(tag) > -1) {
27078 if (this.cwhite.indexOf(tag) > -1) {
27081 this.cwhite.push(tag);
27086 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27087 if (w.indexOf(tag) > -1) {
27090 this.cblack.push(tag);
27094 Roo.each(b, function(tag) {
27095 if (w.indexOf(tag) > -1) {
27098 if (this.cblack.indexOf(tag) > -1) {
27101 this.cblack.push(tag);
27106 setStylesheets : function(stylesheets)
27108 if(typeof(stylesheets) == 'string'){
27109 Roo.get(this.iframe.contentDocument.head).createChild({
27111 rel : 'stylesheet',
27120 Roo.each(stylesheets, function(s) {
27125 Roo.get(_this.iframe.contentDocument.head).createChild({
27127 rel : 'stylesheet',
27136 removeStylesheets : function()
27140 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27145 setStyle : function(style)
27147 Roo.get(this.iframe.contentDocument.head).createChild({
27156 // hide stuff that is not compatible
27170 * @event specialkey
27174 * @cfg {String} fieldClass @hide
27177 * @cfg {String} focusClass @hide
27180 * @cfg {String} autoCreate @hide
27183 * @cfg {String} inputType @hide
27186 * @cfg {String} invalidClass @hide
27189 * @cfg {String} invalidText @hide
27192 * @cfg {String} msgFx @hide
27195 * @cfg {String} validateOnBlur @hide
27199 Roo.HtmlEditorCore.white = [
27200 'area', 'br', 'img', 'input', 'hr', 'wbr',
27202 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27203 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27204 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27205 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27206 'table', 'ul', 'xmp',
27208 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27211 'dir', 'menu', 'ol', 'ul', 'dl',
27217 Roo.HtmlEditorCore.black = [
27218 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27220 'base', 'basefont', 'bgsound', 'blink', 'body',
27221 'frame', 'frameset', 'head', 'html', 'ilayer',
27222 'iframe', 'layer', 'link', 'meta', 'object',
27223 'script', 'style' ,'title', 'xml' // clean later..
27225 Roo.HtmlEditorCore.clean = [
27226 'script', 'style', 'title', 'xml'
27228 Roo.HtmlEditorCore.remove = [
27233 Roo.HtmlEditorCore.ablack = [
27237 Roo.HtmlEditorCore.aclean = [
27238 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27242 Roo.HtmlEditorCore.pwhite= [
27243 'http', 'https', 'mailto'
27246 // white listed style attributes.
27247 Roo.HtmlEditorCore.cwhite= [
27248 // 'text-align', /// default is to allow most things..
27254 // black listed style attributes.
27255 Roo.HtmlEditorCore.cblack= [
27256 // 'font-size' -- this can be set by the project
27260 Roo.HtmlEditorCore.swapCodes =[
27261 [ 8211, "–" ],
27262 [ 8212, "—" ],
27279 * @class Roo.bootstrap.HtmlEditor
27280 * @extends Roo.bootstrap.TextArea
27281 * Bootstrap HtmlEditor class
27284 * Create a new HtmlEditor
27285 * @param {Object} config The config object
27288 Roo.bootstrap.HtmlEditor = function(config){
27289 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27290 if (!this.toolbars) {
27291 this.toolbars = [];
27294 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27297 * @event initialize
27298 * Fires when the editor is fully initialized (including the iframe)
27299 * @param {HtmlEditor} this
27304 * Fires when the editor is first receives the focus. Any insertion must wait
27305 * until after this event.
27306 * @param {HtmlEditor} this
27310 * @event beforesync
27311 * Fires before the textarea is updated with content from the editor iframe. Return false
27312 * to cancel the sync.
27313 * @param {HtmlEditor} this
27314 * @param {String} html
27318 * @event beforepush
27319 * Fires before the iframe editor is updated with content from the textarea. Return false
27320 * to cancel the push.
27321 * @param {HtmlEditor} this
27322 * @param {String} html
27327 * Fires when the textarea is updated with content from the editor iframe.
27328 * @param {HtmlEditor} this
27329 * @param {String} html
27334 * Fires when the iframe editor is updated with content from the textarea.
27335 * @param {HtmlEditor} this
27336 * @param {String} html
27340 * @event editmodechange
27341 * Fires when the editor switches edit modes
27342 * @param {HtmlEditor} this
27343 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27345 editmodechange: true,
27347 * @event editorevent
27348 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27349 * @param {HtmlEditor} this
27353 * @event firstfocus
27354 * Fires when on first focus - needed by toolbars..
27355 * @param {HtmlEditor} this
27360 * Auto save the htmlEditor value as a file into Events
27361 * @param {HtmlEditor} this
27365 * @event savedpreview
27366 * preview the saved version of htmlEditor
27367 * @param {HtmlEditor} this
27374 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27378 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27383 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27388 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27393 * @cfg {Number} height (in pixels)
27397 * @cfg {Number} width (in pixels)
27402 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27405 stylesheets: false,
27410 // private properties
27411 validationEvent : false,
27413 initialized : false,
27416 onFocus : Roo.emptyFn,
27418 hideMode:'offsets',
27420 tbContainer : false,
27424 toolbarContainer :function() {
27425 return this.wrap.select('.x-html-editor-tb',true).first();
27429 * Protected method that will not generally be called directly. It
27430 * is called when the editor creates its toolbar. Override this method if you need to
27431 * add custom toolbar buttons.
27432 * @param {HtmlEditor} editor
27434 createToolbar : function(){
27435 Roo.log('renewing');
27436 Roo.log("create toolbars");
27438 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27439 this.toolbars[0].render(this.toolbarContainer());
27443 // if (!editor.toolbars || !editor.toolbars.length) {
27444 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27447 // for (var i =0 ; i < editor.toolbars.length;i++) {
27448 // editor.toolbars[i] = Roo.factory(
27449 // typeof(editor.toolbars[i]) == 'string' ?
27450 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27451 // Roo.bootstrap.HtmlEditor);
27452 // editor.toolbars[i].init(editor);
27458 onRender : function(ct, position)
27460 // Roo.log("Call onRender: " + this.xtype);
27462 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27464 this.wrap = this.inputEl().wrap({
27465 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27468 this.editorcore.onRender(ct, position);
27470 if (this.resizable) {
27471 this.resizeEl = new Roo.Resizable(this.wrap, {
27475 minHeight : this.height,
27476 height: this.height,
27477 handles : this.resizable,
27480 resize : function(r, w, h) {
27481 _t.onResize(w,h); // -something
27487 this.createToolbar(this);
27490 if(!this.width && this.resizable){
27491 this.setSize(this.wrap.getSize());
27493 if (this.resizeEl) {
27494 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27495 // should trigger onReize..
27501 onResize : function(w, h)
27503 Roo.log('resize: ' +w + ',' + h );
27504 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27508 if(this.inputEl() ){
27509 if(typeof w == 'number'){
27510 var aw = w - this.wrap.getFrameWidth('lr');
27511 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27514 if(typeof h == 'number'){
27515 var tbh = -11; // fixme it needs to tool bar size!
27516 for (var i =0; i < this.toolbars.length;i++) {
27517 // fixme - ask toolbars for heights?
27518 tbh += this.toolbars[i].el.getHeight();
27519 //if (this.toolbars[i].footer) {
27520 // tbh += this.toolbars[i].footer.el.getHeight();
27528 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27529 ah -= 5; // knock a few pixes off for look..
27530 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27534 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27535 this.editorcore.onResize(ew,eh);
27540 * Toggles the editor between standard and source edit mode.
27541 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27543 toggleSourceEdit : function(sourceEditMode)
27545 this.editorcore.toggleSourceEdit(sourceEditMode);
27547 if(this.editorcore.sourceEditMode){
27548 Roo.log('editor - showing textarea');
27551 // Roo.log(this.syncValue());
27553 this.inputEl().removeClass(['hide', 'x-hidden']);
27554 this.inputEl().dom.removeAttribute('tabIndex');
27555 this.inputEl().focus();
27557 Roo.log('editor - hiding textarea');
27559 // Roo.log(this.pushValue());
27562 this.inputEl().addClass(['hide', 'x-hidden']);
27563 this.inputEl().dom.setAttribute('tabIndex', -1);
27564 //this.deferFocus();
27567 if(this.resizable){
27568 this.setSize(this.wrap.getSize());
27571 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27574 // private (for BoxComponent)
27575 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27577 // private (for BoxComponent)
27578 getResizeEl : function(){
27582 // private (for BoxComponent)
27583 getPositionEl : function(){
27588 initEvents : function(){
27589 this.originalValue = this.getValue();
27593 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27596 // markInvalid : Roo.emptyFn,
27598 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27601 // clearInvalid : Roo.emptyFn,
27603 setValue : function(v){
27604 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27605 this.editorcore.pushValue();
27610 deferFocus : function(){
27611 this.focus.defer(10, this);
27615 focus : function(){
27616 this.editorcore.focus();
27622 onDestroy : function(){
27628 for (var i =0; i < this.toolbars.length;i++) {
27629 // fixme - ask toolbars for heights?
27630 this.toolbars[i].onDestroy();
27633 this.wrap.dom.innerHTML = '';
27634 this.wrap.remove();
27639 onFirstFocus : function(){
27640 //Roo.log("onFirstFocus");
27641 this.editorcore.onFirstFocus();
27642 for (var i =0; i < this.toolbars.length;i++) {
27643 this.toolbars[i].onFirstFocus();
27649 syncValue : function()
27651 this.editorcore.syncValue();
27654 pushValue : function()
27656 this.editorcore.pushValue();
27660 // hide stuff that is not compatible
27674 * @event specialkey
27678 * @cfg {String} fieldClass @hide
27681 * @cfg {String} focusClass @hide
27684 * @cfg {String} autoCreate @hide
27687 * @cfg {String} inputType @hide
27691 * @cfg {String} invalidText @hide
27694 * @cfg {String} msgFx @hide
27697 * @cfg {String} validateOnBlur @hide
27706 Roo.namespace('Roo.bootstrap.htmleditor');
27708 * @class Roo.bootstrap.HtmlEditorToolbar1
27714 new Roo.bootstrap.HtmlEditor({
27717 new Roo.bootstrap.HtmlEditorToolbar1({
27718 disable : { fonts: 1 , format: 1, ..., ... , ...],
27724 * @cfg {Object} disable List of elements to disable..
27725 * @cfg {Array} btns List of additional buttons.
27729 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27732 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27735 Roo.apply(this, config);
27737 // default disabled, based on 'good practice'..
27738 this.disable = this.disable || {};
27739 Roo.applyIf(this.disable, {
27742 specialElements : true
27744 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27746 this.editor = config.editor;
27747 this.editorcore = config.editor.editorcore;
27749 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27751 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27752 // dont call parent... till later.
27754 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27759 editorcore : false,
27764 "h1","h2","h3","h4","h5","h6",
27766 "abbr", "acronym", "address", "cite", "samp", "var",
27770 onRender : function(ct, position)
27772 // Roo.log("Call onRender: " + this.xtype);
27774 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27776 this.el.dom.style.marginBottom = '0';
27778 var editorcore = this.editorcore;
27779 var editor= this.editor;
27782 var btn = function(id,cmd , toggle, handler, html){
27784 var event = toggle ? 'toggle' : 'click';
27789 xns: Roo.bootstrap,
27793 enableToggle:toggle !== false,
27795 pressed : toggle ? false : null,
27798 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27799 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27805 // var cb_box = function...
27810 xns: Roo.bootstrap,
27815 xns: Roo.bootstrap,
27819 Roo.each(this.formats, function(f) {
27820 style.menu.items.push({
27822 xns: Roo.bootstrap,
27823 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27828 editorcore.insertTag(this.tagname);
27835 children.push(style);
27837 btn('bold',false,true);
27838 btn('italic',false,true);
27839 btn('align-left', 'justifyleft',true);
27840 btn('align-center', 'justifycenter',true);
27841 btn('align-right' , 'justifyright',true);
27842 btn('link', false, false, function(btn) {
27843 //Roo.log("create link?");
27844 var url = prompt(this.createLinkText, this.defaultLinkValue);
27845 if(url && url != 'http:/'+'/'){
27846 this.editorcore.relayCmd('createlink', url);
27849 btn('list','insertunorderedlist',true);
27850 btn('pencil', false,true, function(btn){
27852 this.toggleSourceEdit(btn.pressed);
27855 if (this.editor.btns.length > 0) {
27856 for (var i = 0; i<this.editor.btns.length; i++) {
27857 children.push(this.editor.btns[i]);
27865 xns: Roo.bootstrap,
27870 xns: Roo.bootstrap,
27875 cog.menu.items.push({
27877 xns: Roo.bootstrap,
27878 html : Clean styles,
27883 editorcore.insertTag(this.tagname);
27892 this.xtype = 'NavSimplebar';
27894 for(var i=0;i< children.length;i++) {
27896 this.buttons.add(this.addxtypeChild(children[i]));
27900 editor.on('editorevent', this.updateToolbar, this);
27902 onBtnClick : function(id)
27904 this.editorcore.relayCmd(id);
27905 this.editorcore.focus();
27909 * Protected method that will not generally be called directly. It triggers
27910 * a toolbar update by reading the markup state of the current selection in the editor.
27912 updateToolbar: function(){
27914 if(!this.editorcore.activated){
27915 this.editor.onFirstFocus(); // is this neeed?
27919 var btns = this.buttons;
27920 var doc = this.editorcore.doc;
27921 btns.get('bold').setActive(doc.queryCommandState('bold'));
27922 btns.get('italic').setActive(doc.queryCommandState('italic'));
27923 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27925 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27926 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27927 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27929 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27930 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27933 var ans = this.editorcore.getAllAncestors();
27934 if (this.formatCombo) {
27937 var store = this.formatCombo.store;
27938 this.formatCombo.setValue("");
27939 for (var i =0; i < ans.length;i++) {
27940 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27942 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27950 // hides menus... - so this cant be on a menu...
27951 Roo.bootstrap.MenuMgr.hideAll();
27953 Roo.bootstrap.MenuMgr.hideAll();
27954 //this.editorsyncValue();
27956 onFirstFocus: function() {
27957 this.buttons.each(function(item){
27961 toggleSourceEdit : function(sourceEditMode){
27964 if(sourceEditMode){
27965 Roo.log("disabling buttons");
27966 this.buttons.each( function(item){
27967 if(item.cmd != 'pencil'){
27973 Roo.log("enabling buttons");
27974 if(this.editorcore.initialized){
27975 this.buttons.each( function(item){
27981 Roo.log("calling toggole on editor");
27982 // tell the editor that it's been pressed..
27983 this.editor.toggleSourceEdit(sourceEditMode);
27997 * @class Roo.bootstrap.Markdown
27998 * @extends Roo.bootstrap.TextArea
27999 * Bootstrap Showdown editable area
28000 * @cfg {string} content
28003 * Create a new Showdown
28006 Roo.bootstrap.Markdown = function(config){
28007 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28011 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
28015 initEvents : function()
28018 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28019 this.markdownEl = this.el.createChild({
28020 cls : 'roo-markdown-area'
28022 this.inputEl().addClass('d-none');
28023 if (this.getValue() == '') {
28024 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28027 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28029 this.markdownEl.on('click', this.toggleTextEdit, this);
28030 this.on('blur', this.toggleTextEdit, this);
28031 this.on('specialkey', this.resizeTextArea, this);
28034 toggleTextEdit : function()
28036 var sh = this.markdownEl.getHeight();
28037 this.inputEl().addClass('d-none');
28038 this.markdownEl.addClass('d-none');
28039 if (!this.editing) {
28041 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28042 this.inputEl().removeClass('d-none');
28043 this.inputEl().focus();
28044 this.editing = true;
28047 // show showdown...
28048 this.updateMarkdown();
28049 this.markdownEl.removeClass('d-none');
28050 this.editing = false;
28053 updateMarkdown : function()
28055 if (this.getValue() == '') {
28056 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28060 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28063 resizeTextArea: function () {
28066 Roo.log([sh, this.getValue().split("\n").length * 30]);
28067 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28069 setValue : function(val)
28071 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28072 if (!this.editing) {
28073 this.updateMarkdown();
28079 if (!this.editing) {
28080 this.toggleTextEdit();
28088 * Ext JS Library 1.1.1
28089 * Copyright(c) 2006-2007, Ext JS, LLC.
28091 * Originally Released Under LGPL - original licence link has changed is not relivant.
28094 * <script type="text/javascript">
28098 * @class Roo.bootstrap.PagingToolbar
28099 * @extends Roo.bootstrap.NavSimplebar
28100 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28102 * Create a new PagingToolbar
28103 * @param {Object} config The config object
28104 * @param {Roo.data.Store} store
28106 Roo.bootstrap.PagingToolbar = function(config)
28108 // old args format still supported... - xtype is prefered..
28109 // created from xtype...
28111 this.ds = config.dataSource;
28113 if (config.store && !this.ds) {
28114 this.store= Roo.factory(config.store, Roo.data);
28115 this.ds = this.store;
28116 this.ds.xmodule = this.xmodule || false;
28119 this.toolbarItems = [];
28120 if (config.items) {
28121 this.toolbarItems = config.items;
28124 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28129 this.bind(this.ds);
28132 if (Roo.bootstrap.version == 4) {
28133 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28135 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28140 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28142 * @cfg {Roo.data.Store} dataSource
28143 * The underlying data store providing the paged data
28146 * @cfg {String/HTMLElement/Element} container
28147 * container The id or element that will contain the toolbar
28150 * @cfg {Boolean} displayInfo
28151 * True to display the displayMsg (defaults to false)
28154 * @cfg {Number} pageSize
28155 * The number of records to display per page (defaults to 20)
28159 * @cfg {String} displayMsg
28160 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28162 displayMsg : 'Displaying {0} - {1} of {2}',
28164 * @cfg {String} emptyMsg
28165 * The message to display when no records are found (defaults to "No data to display")
28167 emptyMsg : 'No data to display',
28169 * Customizable piece of the default paging text (defaults to "Page")
28172 beforePageText : "Page",
28174 * Customizable piece of the default paging text (defaults to "of %0")
28177 afterPageText : "of {0}",
28179 * Customizable piece of the default paging text (defaults to "First Page")
28182 firstText : "First Page",
28184 * Customizable piece of the default paging text (defaults to "Previous Page")
28187 prevText : "Previous Page",
28189 * Customizable piece of the default paging text (defaults to "Next Page")
28192 nextText : "Next Page",
28194 * Customizable piece of the default paging text (defaults to "Last Page")
28197 lastText : "Last Page",
28199 * Customizable piece of the default paging text (defaults to "Refresh")
28202 refreshText : "Refresh",
28206 onRender : function(ct, position)
28208 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28209 this.navgroup.parentId = this.id;
28210 this.navgroup.onRender(this.el, null);
28211 // add the buttons to the navgroup
28213 if(this.displayInfo){
28214 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28215 this.displayEl = this.el.select('.x-paging-info', true).first();
28216 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28217 // this.displayEl = navel.el.select('span',true).first();
28223 Roo.each(_this.buttons, function(e){ // this might need to use render????
28224 Roo.factory(e).render(_this.el);
28228 Roo.each(_this.toolbarItems, function(e) {
28229 _this.navgroup.addItem(e);
28233 this.first = this.navgroup.addItem({
28234 tooltip: this.firstText,
28235 cls: "prev btn-outline-secondary",
28236 html : ' <i class="fa fa-step-backward"></i>',
28238 preventDefault: true,
28239 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28242 this.prev = this.navgroup.addItem({
28243 tooltip: this.prevText,
28244 cls: "prev btn-outline-secondary",
28245 html : ' <i class="fa fa-backward"></i>',
28247 preventDefault: true,
28248 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28250 //this.addSeparator();
28253 var field = this.navgroup.addItem( {
28255 cls : 'x-paging-position btn-outline-secondary',
28257 html : this.beforePageText +
28258 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28259 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28262 this.field = field.el.select('input', true).first();
28263 this.field.on("keydown", this.onPagingKeydown, this);
28264 this.field.on("focus", function(){this.dom.select();});
28267 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28268 //this.field.setHeight(18);
28269 //this.addSeparator();
28270 this.next = this.navgroup.addItem({
28271 tooltip: this.nextText,
28272 cls: "next btn-outline-secondary",
28273 html : ' <i class="fa fa-forward"></i>',
28275 preventDefault: true,
28276 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28278 this.last = this.navgroup.addItem({
28279 tooltip: this.lastText,
28280 html : ' <i class="fa fa-step-forward"></i>',
28281 cls: "next btn-outline-secondary",
28283 preventDefault: true,
28284 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28286 //this.addSeparator();
28287 this.loading = this.navgroup.addItem({
28288 tooltip: this.refreshText,
28289 cls: "btn-outline-secondary",
28290 html : ' <i class="fa fa-refresh"></i>',
28291 preventDefault: true,
28292 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28298 updateInfo : function(){
28299 if(this.displayEl){
28300 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28301 var msg = count == 0 ?
28305 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28307 this.displayEl.update(msg);
28312 onLoad : function(ds, r, o)
28314 this.cursor = o.params && o.params.start ? o.params.start : 0;
28316 var d = this.getPageData(),
28321 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28322 this.field.dom.value = ap;
28323 this.first.setDisabled(ap == 1);
28324 this.prev.setDisabled(ap == 1);
28325 this.next.setDisabled(ap == ps);
28326 this.last.setDisabled(ap == ps);
28327 this.loading.enable();
28332 getPageData : function(){
28333 var total = this.ds.getTotalCount();
28336 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28337 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28342 onLoadError : function(){
28343 this.loading.enable();
28347 onPagingKeydown : function(e){
28348 var k = e.getKey();
28349 var d = this.getPageData();
28351 var v = this.field.dom.value, pageNum;
28352 if(!v || isNaN(pageNum = parseInt(v, 10))){
28353 this.field.dom.value = d.activePage;
28356 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28357 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28360 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))
28362 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28363 this.field.dom.value = pageNum;
28364 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28367 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28369 var v = this.field.dom.value, pageNum;
28370 var increment = (e.shiftKey) ? 10 : 1;
28371 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28374 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28375 this.field.dom.value = d.activePage;
28378 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28380 this.field.dom.value = parseInt(v, 10) + increment;
28381 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28382 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28389 beforeLoad : function(){
28391 this.loading.disable();
28396 onClick : function(which){
28405 ds.load({params:{start: 0, limit: this.pageSize}});
28408 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28411 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28414 var total = ds.getTotalCount();
28415 var extra = total % this.pageSize;
28416 var lastStart = extra ? (total - extra) : total-this.pageSize;
28417 ds.load({params:{start: lastStart, limit: this.pageSize}});
28420 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28426 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28427 * @param {Roo.data.Store} store The data store to unbind
28429 unbind : function(ds){
28430 ds.un("beforeload", this.beforeLoad, this);
28431 ds.un("load", this.onLoad, this);
28432 ds.un("loadexception", this.onLoadError, this);
28433 ds.un("remove", this.updateInfo, this);
28434 ds.un("add", this.updateInfo, this);
28435 this.ds = undefined;
28439 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28440 * @param {Roo.data.Store} store The data store to bind
28442 bind : function(ds){
28443 ds.on("beforeload", this.beforeLoad, this);
28444 ds.on("load", this.onLoad, this);
28445 ds.on("loadexception", this.onLoadError, this);
28446 ds.on("remove", this.updateInfo, this);
28447 ds.on("add", this.updateInfo, this);
28458 * @class Roo.bootstrap.MessageBar
28459 * @extends Roo.bootstrap.Component
28460 * Bootstrap MessageBar class
28461 * @cfg {String} html contents of the MessageBar
28462 * @cfg {String} weight (info | success | warning | danger) default info
28463 * @cfg {String} beforeClass insert the bar before the given class
28464 * @cfg {Boolean} closable (true | false) default false
28465 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28468 * Create a new Element
28469 * @param {Object} config The config object
28472 Roo.bootstrap.MessageBar = function(config){
28473 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28476 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28482 beforeClass: 'bootstrap-sticky-wrap',
28484 getAutoCreate : function(){
28488 cls: 'alert alert-dismissable alert-' + this.weight,
28493 html: this.html || ''
28499 cfg.cls += ' alert-messages-fixed';
28513 onRender : function(ct, position)
28515 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28518 var cfg = Roo.apply({}, this.getAutoCreate());
28522 cfg.cls += ' ' + this.cls;
28525 cfg.style = this.style;
28527 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28529 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28532 this.el.select('>button.close').on('click', this.hide, this);
28538 if (!this.rendered) {
28544 this.fireEvent('show', this);
28550 if (!this.rendered) {
28556 this.fireEvent('hide', this);
28559 update : function()
28561 // var e = this.el.dom.firstChild;
28563 // if(this.closable){
28564 // e = e.nextSibling;
28567 // e.data = this.html || '';
28569 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28585 * @class Roo.bootstrap.Graph
28586 * @extends Roo.bootstrap.Component
28587 * Bootstrap Graph class
28591 @cfg {String} graphtype bar | vbar | pie
28592 @cfg {number} g_x coodinator | centre x (pie)
28593 @cfg {number} g_y coodinator | centre y (pie)
28594 @cfg {number} g_r radius (pie)
28595 @cfg {number} g_height height of the chart (respected by all elements in the set)
28596 @cfg {number} g_width width of the chart (respected by all elements in the set)
28597 @cfg {Object} title The title of the chart
28600 -opts (object) options for the chart
28602 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28603 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28605 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.
28606 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28608 o stretch (boolean)
28610 -opts (object) options for the pie
28613 o startAngle (number)
28614 o endAngle (number)
28618 * Create a new Input
28619 * @param {Object} config The config object
28622 Roo.bootstrap.Graph = function(config){
28623 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28629 * The img click event for the img.
28630 * @param {Roo.EventObject} e
28636 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28647 //g_colors: this.colors,
28654 getAutoCreate : function(){
28665 onRender : function(ct,position){
28668 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28670 if (typeof(Raphael) == 'undefined') {
28671 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28675 this.raphael = Raphael(this.el.dom);
28677 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28678 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28679 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28680 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28682 r.text(160, 10, "Single Series Chart").attr(txtattr);
28683 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28684 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28685 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28687 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28688 r.barchart(330, 10, 300, 220, data1);
28689 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28690 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28693 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28694 // r.barchart(30, 30, 560, 250, xdata, {
28695 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28696 // axis : "0 0 1 1",
28697 // axisxlabels : xdata
28698 // //yvalues : cols,
28701 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28703 // this.load(null,xdata,{
28704 // axis : "0 0 1 1",
28705 // axisxlabels : xdata
28710 load : function(graphtype,xdata,opts)
28712 this.raphael.clear();
28714 graphtype = this.graphtype;
28719 var r = this.raphael,
28720 fin = function () {
28721 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28723 fout = function () {
28724 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28726 pfin = function() {
28727 this.sector.stop();
28728 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28731 this.label[0].stop();
28732 this.label[0].attr({ r: 7.5 });
28733 this.label[1].attr({ "font-weight": 800 });
28736 pfout = function() {
28737 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28740 this.label[0].animate({ r: 5 }, 500, "bounce");
28741 this.label[1].attr({ "font-weight": 400 });
28747 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28750 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28753 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28754 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28756 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28763 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28768 setTitle: function(o)
28773 initEvents: function() {
28776 this.el.on('click', this.onClick, this);
28780 onClick : function(e)
28782 Roo.log('img onclick');
28783 this.fireEvent('click', this, e);
28795 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28798 * @class Roo.bootstrap.dash.NumberBox
28799 * @extends Roo.bootstrap.Component
28800 * Bootstrap NumberBox class
28801 * @cfg {String} headline Box headline
28802 * @cfg {String} content Box content
28803 * @cfg {String} icon Box icon
28804 * @cfg {String} footer Footer text
28805 * @cfg {String} fhref Footer href
28808 * Create a new NumberBox
28809 * @param {Object} config The config object
28813 Roo.bootstrap.dash.NumberBox = function(config){
28814 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28818 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28827 getAutoCreate : function(){
28831 cls : 'small-box ',
28839 cls : 'roo-headline',
28840 html : this.headline
28844 cls : 'roo-content',
28845 html : this.content
28859 cls : 'ion ' + this.icon
28868 cls : 'small-box-footer',
28869 href : this.fhref || '#',
28873 cfg.cn.push(footer);
28880 onRender : function(ct,position){
28881 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28888 setHeadline: function (value)
28890 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28893 setFooter: function (value, href)
28895 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28898 this.el.select('a.small-box-footer',true).first().attr('href', href);
28903 setContent: function (value)
28905 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28908 initEvents: function()
28922 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28925 * @class Roo.bootstrap.dash.TabBox
28926 * @extends Roo.bootstrap.Component
28927 * Bootstrap TabBox class
28928 * @cfg {String} title Title of the TabBox
28929 * @cfg {String} icon Icon of the TabBox
28930 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28931 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28934 * Create a new TabBox
28935 * @param {Object} config The config object
28939 Roo.bootstrap.dash.TabBox = function(config){
28940 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28945 * When a pane is added
28946 * @param {Roo.bootstrap.dash.TabPane} pane
28950 * @event activatepane
28951 * When a pane is activated
28952 * @param {Roo.bootstrap.dash.TabPane} pane
28954 "activatepane" : true
28962 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28967 tabScrollable : false,
28969 getChildContainer : function()
28971 return this.el.select('.tab-content', true).first();
28974 getAutoCreate : function(){
28978 cls: 'pull-left header',
28986 cls: 'fa ' + this.icon
28992 cls: 'nav nav-tabs pull-right',
28998 if(this.tabScrollable){
29005 cls: 'nav nav-tabs pull-right',
29016 cls: 'nav-tabs-custom',
29021 cls: 'tab-content no-padding',
29029 initEvents : function()
29031 //Roo.log('add add pane handler');
29032 this.on('addpane', this.onAddPane, this);
29035 * Updates the box title
29036 * @param {String} html to set the title to.
29038 setTitle : function(value)
29040 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29042 onAddPane : function(pane)
29044 this.panes.push(pane);
29045 //Roo.log('addpane');
29047 // tabs are rendere left to right..
29048 if(!this.showtabs){
29052 var ctr = this.el.select('.nav-tabs', true).first();
29055 var existing = ctr.select('.nav-tab',true);
29056 var qty = existing.getCount();;
29059 var tab = ctr.createChild({
29061 cls : 'nav-tab' + (qty ? '' : ' active'),
29069 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29072 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29074 pane.el.addClass('active');
29079 onTabClick : function(ev,un,ob,pane)
29081 //Roo.log('tab - prev default');
29082 ev.preventDefault();
29085 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29086 pane.tab.addClass('active');
29087 //Roo.log(pane.title);
29088 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29089 // technically we should have a deactivate event.. but maybe add later.
29090 // and it should not de-activate the selected tab...
29091 this.fireEvent('activatepane', pane);
29092 pane.el.addClass('active');
29093 pane.fireEvent('activate');
29098 getActivePane : function()
29101 Roo.each(this.panes, function(p) {
29102 if(p.el.hasClass('active')){
29123 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29125 * @class Roo.bootstrap.TabPane
29126 * @extends Roo.bootstrap.Component
29127 * Bootstrap TabPane class
29128 * @cfg {Boolean} active (false | true) Default false
29129 * @cfg {String} title title of panel
29133 * Create a new TabPane
29134 * @param {Object} config The config object
29137 Roo.bootstrap.dash.TabPane = function(config){
29138 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29144 * When a pane is activated
29145 * @param {Roo.bootstrap.dash.TabPane} pane
29152 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29157 // the tabBox that this is attached to.
29160 getAutoCreate : function()
29168 cfg.cls += ' active';
29173 initEvents : function()
29175 //Roo.log('trigger add pane handler');
29176 this.parent().fireEvent('addpane', this)
29180 * Updates the tab title
29181 * @param {String} html to set the title to.
29183 setTitle: function(str)
29189 this.tab.select('a', true).first().dom.innerHTML = str;
29206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29209 * @class Roo.bootstrap.menu.Menu
29210 * @extends Roo.bootstrap.Component
29211 * Bootstrap Menu class - container for Menu
29212 * @cfg {String} html Text of the menu
29213 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29214 * @cfg {String} icon Font awesome icon
29215 * @cfg {String} pos Menu align to (top | bottom) default bottom
29219 * Create a new Menu
29220 * @param {Object} config The config object
29224 Roo.bootstrap.menu.Menu = function(config){
29225 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29229 * @event beforeshow
29230 * Fires before this menu is displayed
29231 * @param {Roo.bootstrap.menu.Menu} this
29235 * @event beforehide
29236 * Fires before this menu is hidden
29237 * @param {Roo.bootstrap.menu.Menu} this
29242 * Fires after this menu is displayed
29243 * @param {Roo.bootstrap.menu.Menu} this
29248 * Fires after this menu is hidden
29249 * @param {Roo.bootstrap.menu.Menu} this
29254 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29255 * @param {Roo.bootstrap.menu.Menu} this
29256 * @param {Roo.EventObject} e
29263 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29267 weight : 'default',
29272 getChildContainer : function() {
29273 if(this.isSubMenu){
29277 return this.el.select('ul.dropdown-menu', true).first();
29280 getAutoCreate : function()
29285 cls : 'roo-menu-text',
29293 cls : 'fa ' + this.icon
29304 cls : 'dropdown-button btn btn-' + this.weight,
29309 cls : 'dropdown-toggle btn btn-' + this.weight,
29319 cls : 'dropdown-menu'
29325 if(this.pos == 'top'){
29326 cfg.cls += ' dropup';
29329 if(this.isSubMenu){
29332 cls : 'dropdown-menu'
29339 onRender : function(ct, position)
29341 this.isSubMenu = ct.hasClass('dropdown-submenu');
29343 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29346 initEvents : function()
29348 if(this.isSubMenu){
29352 this.hidden = true;
29354 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29355 this.triggerEl.on('click', this.onTriggerPress, this);
29357 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29358 this.buttonEl.on('click', this.onClick, this);
29364 if(this.isSubMenu){
29368 return this.el.select('ul.dropdown-menu', true).first();
29371 onClick : function(e)
29373 this.fireEvent("click", this, e);
29376 onTriggerPress : function(e)
29378 if (this.isVisible()) {
29385 isVisible : function(){
29386 return !this.hidden;
29391 this.fireEvent("beforeshow", this);
29393 this.hidden = false;
29394 this.el.addClass('open');
29396 Roo.get(document).on("mouseup", this.onMouseUp, this);
29398 this.fireEvent("show", this);
29405 this.fireEvent("beforehide", this);
29407 this.hidden = true;
29408 this.el.removeClass('open');
29410 Roo.get(document).un("mouseup", this.onMouseUp);
29412 this.fireEvent("hide", this);
29415 onMouseUp : function()
29429 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29432 * @class Roo.bootstrap.menu.Item
29433 * @extends Roo.bootstrap.Component
29434 * Bootstrap MenuItem class
29435 * @cfg {Boolean} submenu (true | false) default false
29436 * @cfg {String} html text of the item
29437 * @cfg {String} href the link
29438 * @cfg {Boolean} disable (true | false) default false
29439 * @cfg {Boolean} preventDefault (true | false) default true
29440 * @cfg {String} icon Font awesome icon
29441 * @cfg {String} pos Submenu align to (left | right) default right
29445 * Create a new Item
29446 * @param {Object} config The config object
29450 Roo.bootstrap.menu.Item = function(config){
29451 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29455 * Fires when the mouse is hovering over this menu
29456 * @param {Roo.bootstrap.menu.Item} this
29457 * @param {Roo.EventObject} e
29462 * Fires when the mouse exits this menu
29463 * @param {Roo.bootstrap.menu.Item} this
29464 * @param {Roo.EventObject} e
29470 * The raw click event for the entire grid.
29471 * @param {Roo.EventObject} e
29477 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29482 preventDefault: true,
29487 getAutoCreate : function()
29492 cls : 'roo-menu-item-text',
29500 cls : 'fa ' + this.icon
29509 href : this.href || '#',
29516 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29520 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29522 if(this.pos == 'left'){
29523 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29530 initEvents : function()
29532 this.el.on('mouseover', this.onMouseOver, this);
29533 this.el.on('mouseout', this.onMouseOut, this);
29535 this.el.select('a', true).first().on('click', this.onClick, this);
29539 onClick : function(e)
29541 if(this.preventDefault){
29542 e.preventDefault();
29545 this.fireEvent("click", this, e);
29548 onMouseOver : function(e)
29550 if(this.submenu && this.pos == 'left'){
29551 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29554 this.fireEvent("mouseover", this, e);
29557 onMouseOut : function(e)
29559 this.fireEvent("mouseout", this, e);
29571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29574 * @class Roo.bootstrap.menu.Separator
29575 * @extends Roo.bootstrap.Component
29576 * Bootstrap Separator class
29579 * Create a new Separator
29580 * @param {Object} config The config object
29584 Roo.bootstrap.menu.Separator = function(config){
29585 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29588 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29590 getAutoCreate : function(){
29593 cls: 'dropdown-divider divider'
29611 * @class Roo.bootstrap.Tooltip
29612 * Bootstrap Tooltip class
29613 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29614 * to determine which dom element triggers the tooltip.
29616 * It needs to add support for additional attributes like tooltip-position
29619 * Create a new Toolti
29620 * @param {Object} config The config object
29623 Roo.bootstrap.Tooltip = function(config){
29624 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29626 this.alignment = Roo.bootstrap.Tooltip.alignment;
29628 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29629 this.alignment = config.alignment;
29634 Roo.apply(Roo.bootstrap.Tooltip, {
29636 * @function init initialize tooltip monitoring.
29640 currentTip : false,
29641 currentRegion : false,
29647 Roo.get(document).on('mouseover', this.enter ,this);
29648 Roo.get(document).on('mouseout', this.leave, this);
29651 this.currentTip = new Roo.bootstrap.Tooltip();
29654 enter : function(ev)
29656 var dom = ev.getTarget();
29658 //Roo.log(['enter',dom]);
29659 var el = Roo.fly(dom);
29660 if (this.currentEl) {
29662 //Roo.log(this.currentEl);
29663 //Roo.log(this.currentEl.contains(dom));
29664 if (this.currentEl == el) {
29667 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29673 if (this.currentTip.el) {
29674 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29678 if(!el || el.dom == document){
29684 if (!el.attr('tooltip')) {
29685 pel = el.findParent("[tooltip]");
29687 bindEl = Roo.get(pel);
29693 // you can not look for children, as if el is the body.. then everythign is the child..
29694 if (!pel && !el.attr('tooltip')) { //
29695 if (!el.select("[tooltip]").elements.length) {
29698 // is the mouse over this child...?
29699 bindEl = el.select("[tooltip]").first();
29700 var xy = ev.getXY();
29701 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29702 //Roo.log("not in region.");
29705 //Roo.log("child element over..");
29708 this.currentEl = el;
29709 this.currentTip.bind(bindEl);
29710 this.currentRegion = Roo.lib.Region.getRegion(dom);
29711 this.currentTip.enter();
29714 leave : function(ev)
29716 var dom = ev.getTarget();
29717 //Roo.log(['leave',dom]);
29718 if (!this.currentEl) {
29723 if (dom != this.currentEl.dom) {
29726 var xy = ev.getXY();
29727 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29730 // only activate leave if mouse cursor is outside... bounding box..
29735 if (this.currentTip) {
29736 this.currentTip.leave();
29738 //Roo.log('clear currentEl');
29739 this.currentEl = false;
29744 'left' : ['r-l', [-2,0], 'right'],
29745 'right' : ['l-r', [2,0], 'left'],
29746 'bottom' : ['t-b', [0,2], 'top'],
29747 'top' : [ 'b-t', [0,-2], 'bottom']
29753 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29758 delay : null, // can be { show : 300 , hide: 500}
29762 hoverState : null, //???
29764 placement : 'bottom',
29768 getAutoCreate : function(){
29775 cls : 'tooltip-arrow arrow'
29778 cls : 'tooltip-inner'
29785 bind : function(el)
29790 initEvents : function()
29792 this.arrowEl = this.el.select('.arrow', true).first();
29793 this.innerEl = this.el.select('.tooltip-inner', true).first();
29796 enter : function () {
29798 if (this.timeout != null) {
29799 clearTimeout(this.timeout);
29802 this.hoverState = 'in';
29803 //Roo.log("enter - show");
29804 if (!this.delay || !this.delay.show) {
29809 this.timeout = setTimeout(function () {
29810 if (_t.hoverState == 'in') {
29813 }, this.delay.show);
29817 clearTimeout(this.timeout);
29819 this.hoverState = 'out';
29820 if (!this.delay || !this.delay.hide) {
29826 this.timeout = setTimeout(function () {
29827 //Roo.log("leave - timeout");
29829 if (_t.hoverState == 'out') {
29831 Roo.bootstrap.Tooltip.currentEl = false;
29836 show : function (msg)
29839 this.render(document.body);
29842 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29844 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29846 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29848 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29849 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29851 var placement = typeof this.placement == 'function' ?
29852 this.placement.call(this, this.el, on_el) :
29855 var autoToken = /\s?auto?\s?/i;
29856 var autoPlace = autoToken.test(placement);
29858 placement = placement.replace(autoToken, '') || 'top';
29862 //this.el.setXY([0,0]);
29864 //this.el.dom.style.display='block';
29866 //this.el.appendTo(on_el);
29868 var p = this.getPosition();
29869 var box = this.el.getBox();
29875 var align = this.alignment[placement];
29877 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29879 if(placement == 'top' || placement == 'bottom'){
29881 placement = 'right';
29884 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29885 placement = 'left';
29888 var scroll = Roo.select('body', true).first().getScroll();
29890 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29894 align = this.alignment[placement];
29896 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29900 var elems = document.getElementsByTagName('div');
29901 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29902 for (var i = 0; i < elems.length; i++) {
29903 var zindex = Number.parseInt(
29904 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29907 if (zindex > highest) {
29914 this.el.dom.style.zIndex = highest;
29916 this.el.alignTo(this.bindEl, align[0],align[1]);
29917 //var arrow = this.el.select('.arrow',true).first();
29918 //arrow.set(align[2],
29920 this.el.addClass(placement);
29921 this.el.addClass("bs-tooltip-"+ placement);
29923 this.el.addClass('in fade show');
29925 this.hoverState = null;
29927 if (this.el.hasClass('fade')) {
29942 //this.el.setXY([0,0]);
29943 this.el.removeClass(['show', 'in']);
29959 * @class Roo.bootstrap.LocationPicker
29960 * @extends Roo.bootstrap.Component
29961 * Bootstrap LocationPicker class
29962 * @cfg {Number} latitude Position when init default 0
29963 * @cfg {Number} longitude Position when init default 0
29964 * @cfg {Number} zoom default 15
29965 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29966 * @cfg {Boolean} mapTypeControl default false
29967 * @cfg {Boolean} disableDoubleClickZoom default false
29968 * @cfg {Boolean} scrollwheel default true
29969 * @cfg {Boolean} streetViewControl default false
29970 * @cfg {Number} radius default 0
29971 * @cfg {String} locationName
29972 * @cfg {Boolean} draggable default true
29973 * @cfg {Boolean} enableAutocomplete default false
29974 * @cfg {Boolean} enableReverseGeocode default true
29975 * @cfg {String} markerTitle
29978 * Create a new LocationPicker
29979 * @param {Object} config The config object
29983 Roo.bootstrap.LocationPicker = function(config){
29985 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29990 * Fires when the picker initialized.
29991 * @param {Roo.bootstrap.LocationPicker} this
29992 * @param {Google Location} location
29996 * @event positionchanged
29997 * Fires when the picker position changed.
29998 * @param {Roo.bootstrap.LocationPicker} this
29999 * @param {Google Location} location
30001 positionchanged : true,
30004 * Fires when the map resize.
30005 * @param {Roo.bootstrap.LocationPicker} this
30010 * Fires when the map show.
30011 * @param {Roo.bootstrap.LocationPicker} this
30016 * Fires when the map hide.
30017 * @param {Roo.bootstrap.LocationPicker} this
30022 * Fires when click the map.
30023 * @param {Roo.bootstrap.LocationPicker} this
30024 * @param {Map event} e
30028 * @event mapRightClick
30029 * Fires when right click the map.
30030 * @param {Roo.bootstrap.LocationPicker} this
30031 * @param {Map event} e
30033 mapRightClick : true,
30035 * @event markerClick
30036 * Fires when click the marker.
30037 * @param {Roo.bootstrap.LocationPicker} this
30038 * @param {Map event} e
30040 markerClick : true,
30042 * @event markerRightClick
30043 * Fires when right click the marker.
30044 * @param {Roo.bootstrap.LocationPicker} this
30045 * @param {Map event} e
30047 markerRightClick : true,
30049 * @event OverlayViewDraw
30050 * Fires when OverlayView Draw
30051 * @param {Roo.bootstrap.LocationPicker} this
30053 OverlayViewDraw : true,
30055 * @event OverlayViewOnAdd
30056 * Fires when OverlayView Draw
30057 * @param {Roo.bootstrap.LocationPicker} this
30059 OverlayViewOnAdd : true,
30061 * @event OverlayViewOnRemove
30062 * Fires when OverlayView Draw
30063 * @param {Roo.bootstrap.LocationPicker} this
30065 OverlayViewOnRemove : true,
30067 * @event OverlayViewShow
30068 * Fires when OverlayView Draw
30069 * @param {Roo.bootstrap.LocationPicker} this
30070 * @param {Pixel} cpx
30072 OverlayViewShow : true,
30074 * @event OverlayViewHide
30075 * Fires when OverlayView Draw
30076 * @param {Roo.bootstrap.LocationPicker} this
30078 OverlayViewHide : true,
30080 * @event loadexception
30081 * Fires when load google lib failed.
30082 * @param {Roo.bootstrap.LocationPicker} this
30084 loadexception : true
30089 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30091 gMapContext: false,
30097 mapTypeControl: false,
30098 disableDoubleClickZoom: false,
30100 streetViewControl: false,
30104 enableAutocomplete: false,
30105 enableReverseGeocode: true,
30108 getAutoCreate: function()
30113 cls: 'roo-location-picker'
30119 initEvents: function(ct, position)
30121 if(!this.el.getWidth() || this.isApplied()){
30125 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30130 initial: function()
30132 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30133 this.fireEvent('loadexception', this);
30137 if(!this.mapTypeId){
30138 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30141 this.gMapContext = this.GMapContext();
30143 this.initOverlayView();
30145 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30149 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30150 _this.setPosition(_this.gMapContext.marker.position);
30153 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30154 _this.fireEvent('mapClick', this, event);
30158 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30159 _this.fireEvent('mapRightClick', this, event);
30163 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30164 _this.fireEvent('markerClick', this, event);
30168 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30169 _this.fireEvent('markerRightClick', this, event);
30173 this.setPosition(this.gMapContext.location);
30175 this.fireEvent('initial', this, this.gMapContext.location);
30178 initOverlayView: function()
30182 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30186 _this.fireEvent('OverlayViewDraw', _this);
30191 _this.fireEvent('OverlayViewOnAdd', _this);
30194 onRemove: function()
30196 _this.fireEvent('OverlayViewOnRemove', _this);
30199 show: function(cpx)
30201 _this.fireEvent('OverlayViewShow', _this, cpx);
30206 _this.fireEvent('OverlayViewHide', _this);
30212 fromLatLngToContainerPixel: function(event)
30214 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30217 isApplied: function()
30219 return this.getGmapContext() == false ? false : true;
30222 getGmapContext: function()
30224 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30227 GMapContext: function()
30229 var position = new google.maps.LatLng(this.latitude, this.longitude);
30231 var _map = new google.maps.Map(this.el.dom, {
30234 mapTypeId: this.mapTypeId,
30235 mapTypeControl: this.mapTypeControl,
30236 disableDoubleClickZoom: this.disableDoubleClickZoom,
30237 scrollwheel: this.scrollwheel,
30238 streetViewControl: this.streetViewControl,
30239 locationName: this.locationName,
30240 draggable: this.draggable,
30241 enableAutocomplete: this.enableAutocomplete,
30242 enableReverseGeocode: this.enableReverseGeocode
30245 var _marker = new google.maps.Marker({
30246 position: position,
30248 title: this.markerTitle,
30249 draggable: this.draggable
30256 location: position,
30257 radius: this.radius,
30258 locationName: this.locationName,
30259 addressComponents: {
30260 formatted_address: null,
30261 addressLine1: null,
30262 addressLine2: null,
30264 streetNumber: null,
30268 stateOrProvince: null
30271 domContainer: this.el.dom,
30272 geodecoder: new google.maps.Geocoder()
30276 drawCircle: function(center, radius, options)
30278 if (this.gMapContext.circle != null) {
30279 this.gMapContext.circle.setMap(null);
30283 options = Roo.apply({}, options, {
30284 strokeColor: "#0000FF",
30285 strokeOpacity: .35,
30287 fillColor: "#0000FF",
30291 options.map = this.gMapContext.map;
30292 options.radius = radius;
30293 options.center = center;
30294 this.gMapContext.circle = new google.maps.Circle(options);
30295 return this.gMapContext.circle;
30301 setPosition: function(location)
30303 this.gMapContext.location = location;
30304 this.gMapContext.marker.setPosition(location);
30305 this.gMapContext.map.panTo(location);
30306 this.drawCircle(location, this.gMapContext.radius, {});
30310 if (this.gMapContext.settings.enableReverseGeocode) {
30311 this.gMapContext.geodecoder.geocode({
30312 latLng: this.gMapContext.location
30313 }, function(results, status) {
30315 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30316 _this.gMapContext.locationName = results[0].formatted_address;
30317 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30319 _this.fireEvent('positionchanged', this, location);
30326 this.fireEvent('positionchanged', this, location);
30331 google.maps.event.trigger(this.gMapContext.map, "resize");
30333 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30335 this.fireEvent('resize', this);
30338 setPositionByLatLng: function(latitude, longitude)
30340 this.setPosition(new google.maps.LatLng(latitude, longitude));
30343 getCurrentPosition: function()
30346 latitude: this.gMapContext.location.lat(),
30347 longitude: this.gMapContext.location.lng()
30351 getAddressName: function()
30353 return this.gMapContext.locationName;
30356 getAddressComponents: function()
30358 return this.gMapContext.addressComponents;
30361 address_component_from_google_geocode: function(address_components)
30365 for (var i = 0; i < address_components.length; i++) {
30366 var component = address_components[i];
30367 if (component.types.indexOf("postal_code") >= 0) {
30368 result.postalCode = component.short_name;
30369 } else if (component.types.indexOf("street_number") >= 0) {
30370 result.streetNumber = component.short_name;
30371 } else if (component.types.indexOf("route") >= 0) {
30372 result.streetName = component.short_name;
30373 } else if (component.types.indexOf("neighborhood") >= 0) {
30374 result.city = component.short_name;
30375 } else if (component.types.indexOf("locality") >= 0) {
30376 result.city = component.short_name;
30377 } else if (component.types.indexOf("sublocality") >= 0) {
30378 result.district = component.short_name;
30379 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30380 result.stateOrProvince = component.short_name;
30381 } else if (component.types.indexOf("country") >= 0) {
30382 result.country = component.short_name;
30386 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30387 result.addressLine2 = "";
30391 setZoomLevel: function(zoom)
30393 this.gMapContext.map.setZoom(zoom);
30406 this.fireEvent('show', this);
30417 this.fireEvent('hide', this);
30422 Roo.apply(Roo.bootstrap.LocationPicker, {
30424 OverlayView : function(map, options)
30426 options = options || {};
30433 * @class Roo.bootstrap.Alert
30434 * @extends Roo.bootstrap.Component
30435 * Bootstrap Alert class - shows an alert area box
30437 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30438 Enter a valid email address
30441 * @cfg {String} title The title of alert
30442 * @cfg {String} html The content of alert
30443 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30444 * @cfg {String} fa font-awesomeicon
30445 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30446 * @cfg {Boolean} close true to show a x closer
30450 * Create a new alert
30451 * @param {Object} config The config object
30455 Roo.bootstrap.Alert = function(config){
30456 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30460 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30466 faicon: false, // BC
30470 getAutoCreate : function()
30482 style : this.close ? '' : 'display:none'
30486 cls : 'roo-alert-icon'
30491 cls : 'roo-alert-title',
30496 cls : 'roo-alert-text',
30503 cfg.cn[0].cls += ' fa ' + this.faicon;
30506 cfg.cn[0].cls += ' fa ' + this.fa;
30510 cfg.cls += ' alert-' + this.weight;
30516 initEvents: function()
30518 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30519 this.titleEl = this.el.select('.roo-alert-title',true).first();
30520 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30521 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30522 if (this.seconds > 0) {
30523 this.hide.defer(this.seconds, this);
30527 * Set the Title Message HTML
30528 * @param {String} html
30530 setTitle : function(str)
30532 this.titleEl.dom.innerHTML = str;
30536 * Set the Body Message HTML
30537 * @param {String} html
30539 setHtml : function(str)
30541 this.htmlEl.dom.innerHTML = str;
30544 * Set the Weight of the alert
30545 * @param {String} (success|info|warning|danger) weight
30548 setWeight : function(weight)
30551 this.el.removeClass('alert-' + this.weight);
30554 this.weight = weight;
30556 this.el.addClass('alert-' + this.weight);
30559 * Set the Icon of the alert
30560 * @param {String} see fontawsome names (name without the 'fa-' bit)
30562 setIcon : function(icon)
30565 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30568 this.faicon = icon;
30570 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30595 * @class Roo.bootstrap.UploadCropbox
30596 * @extends Roo.bootstrap.Component
30597 * Bootstrap UploadCropbox class
30598 * @cfg {String} emptyText show when image has been loaded
30599 * @cfg {String} rotateNotify show when image too small to rotate
30600 * @cfg {Number} errorTimeout default 3000
30601 * @cfg {Number} minWidth default 300
30602 * @cfg {Number} minHeight default 300
30603 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30604 * @cfg {Boolean} isDocument (true|false) default false
30605 * @cfg {String} url action url
30606 * @cfg {String} paramName default 'imageUpload'
30607 * @cfg {String} method default POST
30608 * @cfg {Boolean} loadMask (true|false) default true
30609 * @cfg {Boolean} loadingText default 'Loading...'
30612 * Create a new UploadCropbox
30613 * @param {Object} config The config object
30616 Roo.bootstrap.UploadCropbox = function(config){
30617 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30621 * @event beforeselectfile
30622 * Fire before select file
30623 * @param {Roo.bootstrap.UploadCropbox} this
30625 "beforeselectfile" : true,
30628 * Fire after initEvent
30629 * @param {Roo.bootstrap.UploadCropbox} this
30634 * Fire after initEvent
30635 * @param {Roo.bootstrap.UploadCropbox} this
30636 * @param {String} data
30641 * Fire when preparing the file data
30642 * @param {Roo.bootstrap.UploadCropbox} this
30643 * @param {Object} file
30648 * Fire when get exception
30649 * @param {Roo.bootstrap.UploadCropbox} this
30650 * @param {XMLHttpRequest} xhr
30652 "exception" : true,
30654 * @event beforeloadcanvas
30655 * Fire before load the canvas
30656 * @param {Roo.bootstrap.UploadCropbox} this
30657 * @param {String} src
30659 "beforeloadcanvas" : true,
30662 * Fire when trash image
30663 * @param {Roo.bootstrap.UploadCropbox} this
30668 * Fire when download the image
30669 * @param {Roo.bootstrap.UploadCropbox} this
30673 * @event footerbuttonclick
30674 * Fire when footerbuttonclick
30675 * @param {Roo.bootstrap.UploadCropbox} this
30676 * @param {String} type
30678 "footerbuttonclick" : true,
30682 * @param {Roo.bootstrap.UploadCropbox} this
30687 * Fire when rotate the image
30688 * @param {Roo.bootstrap.UploadCropbox} this
30689 * @param {String} pos
30694 * Fire when inspect the file
30695 * @param {Roo.bootstrap.UploadCropbox} this
30696 * @param {Object} file
30701 * Fire when xhr upload the file
30702 * @param {Roo.bootstrap.UploadCropbox} this
30703 * @param {Object} data
30708 * Fire when arrange the file data
30709 * @param {Roo.bootstrap.UploadCropbox} this
30710 * @param {Object} formData
30715 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30718 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30720 emptyText : 'Click to upload image',
30721 rotateNotify : 'Image is too small to rotate',
30722 errorTimeout : 3000,
30736 cropType : 'image/jpeg',
30738 canvasLoaded : false,
30739 isDocument : false,
30741 paramName : 'imageUpload',
30743 loadingText : 'Loading...',
30746 getAutoCreate : function()
30750 cls : 'roo-upload-cropbox',
30754 cls : 'roo-upload-cropbox-selector',
30759 cls : 'roo-upload-cropbox-body',
30760 style : 'cursor:pointer',
30764 cls : 'roo-upload-cropbox-preview'
30768 cls : 'roo-upload-cropbox-thumb'
30772 cls : 'roo-upload-cropbox-empty-notify',
30773 html : this.emptyText
30777 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30778 html : this.rotateNotify
30784 cls : 'roo-upload-cropbox-footer',
30787 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30797 onRender : function(ct, position)
30799 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30801 if (this.buttons.length) {
30803 Roo.each(this.buttons, function(bb) {
30805 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30807 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30813 this.maskEl = this.el;
30817 initEvents : function()
30819 this.urlAPI = (window.createObjectURL && window) ||
30820 (window.URL && URL.revokeObjectURL && URL) ||
30821 (window.webkitURL && webkitURL);
30823 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30824 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30827 this.selectorEl.hide();
30829 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30830 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30832 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30833 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30834 this.thumbEl.hide();
30836 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30837 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30839 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30840 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30841 this.errorEl.hide();
30843 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30844 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30845 this.footerEl.hide();
30847 this.setThumbBoxSize();
30853 this.fireEvent('initial', this);
30860 window.addEventListener("resize", function() { _this.resize(); } );
30862 this.bodyEl.on('click', this.beforeSelectFile, this);
30865 this.bodyEl.on('touchstart', this.onTouchStart, this);
30866 this.bodyEl.on('touchmove', this.onTouchMove, this);
30867 this.bodyEl.on('touchend', this.onTouchEnd, this);
30871 this.bodyEl.on('mousedown', this.onMouseDown, this);
30872 this.bodyEl.on('mousemove', this.onMouseMove, this);
30873 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30874 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30875 Roo.get(document).on('mouseup', this.onMouseUp, this);
30878 this.selectorEl.on('change', this.onFileSelected, this);
30884 this.baseScale = 1;
30886 this.baseRotate = 1;
30887 this.dragable = false;
30888 this.pinching = false;
30891 this.cropData = false;
30892 this.notifyEl.dom.innerHTML = this.emptyText;
30894 this.selectorEl.dom.value = '';
30898 resize : function()
30900 if(this.fireEvent('resize', this) != false){
30901 this.setThumbBoxPosition();
30902 this.setCanvasPosition();
30906 onFooterButtonClick : function(e, el, o, type)
30909 case 'rotate-left' :
30910 this.onRotateLeft(e);
30912 case 'rotate-right' :
30913 this.onRotateRight(e);
30916 this.beforeSelectFile(e);
30931 this.fireEvent('footerbuttonclick', this, type);
30934 beforeSelectFile : function(e)
30936 e.preventDefault();
30938 if(this.fireEvent('beforeselectfile', this) != false){
30939 this.selectorEl.dom.click();
30943 onFileSelected : function(e)
30945 e.preventDefault();
30947 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30951 var file = this.selectorEl.dom.files[0];
30953 if(this.fireEvent('inspect', this, file) != false){
30954 this.prepare(file);
30959 trash : function(e)
30961 this.fireEvent('trash', this);
30964 download : function(e)
30966 this.fireEvent('download', this);
30969 loadCanvas : function(src)
30971 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30975 this.imageEl = document.createElement('img');
30979 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30981 this.imageEl.src = src;
30985 onLoadCanvas : function()
30987 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30988 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30990 this.bodyEl.un('click', this.beforeSelectFile, this);
30992 this.notifyEl.hide();
30993 this.thumbEl.show();
30994 this.footerEl.show();
30996 this.baseRotateLevel();
30998 if(this.isDocument){
30999 this.setThumbBoxSize();
31002 this.setThumbBoxPosition();
31004 this.baseScaleLevel();
31010 this.canvasLoaded = true;
31013 this.maskEl.unmask();
31018 setCanvasPosition : function()
31020 if(!this.canvasEl){
31024 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31025 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31027 this.previewEl.setLeft(pw);
31028 this.previewEl.setTop(ph);
31032 onMouseDown : function(e)
31036 this.dragable = true;
31037 this.pinching = false;
31039 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31040 this.dragable = false;
31044 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31045 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31049 onMouseMove : function(e)
31053 if(!this.canvasLoaded){
31057 if (!this.dragable){
31061 var minX = Math.ceil(this.thumbEl.getLeft(true));
31062 var minY = Math.ceil(this.thumbEl.getTop(true));
31064 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31065 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31067 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31068 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31070 x = x - this.mouseX;
31071 y = y - this.mouseY;
31073 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31074 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31076 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31077 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31079 this.previewEl.setLeft(bgX);
31080 this.previewEl.setTop(bgY);
31082 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31083 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31086 onMouseUp : function(e)
31090 this.dragable = false;
31093 onMouseWheel : function(e)
31097 this.startScale = this.scale;
31099 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31101 if(!this.zoomable()){
31102 this.scale = this.startScale;
31111 zoomable : function()
31113 var minScale = this.thumbEl.getWidth() / this.minWidth;
31115 if(this.minWidth < this.minHeight){
31116 minScale = this.thumbEl.getHeight() / this.minHeight;
31119 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31120 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31124 (this.rotate == 0 || this.rotate == 180) &&
31126 width > this.imageEl.OriginWidth ||
31127 height > this.imageEl.OriginHeight ||
31128 (width < this.minWidth && height < this.minHeight)
31136 (this.rotate == 90 || this.rotate == 270) &&
31138 width > this.imageEl.OriginWidth ||
31139 height > this.imageEl.OriginHeight ||
31140 (width < this.minHeight && height < this.minWidth)
31147 !this.isDocument &&
31148 (this.rotate == 0 || this.rotate == 180) &&
31150 width < this.minWidth ||
31151 width > this.imageEl.OriginWidth ||
31152 height < this.minHeight ||
31153 height > this.imageEl.OriginHeight
31160 !this.isDocument &&
31161 (this.rotate == 90 || this.rotate == 270) &&
31163 width < this.minHeight ||
31164 width > this.imageEl.OriginWidth ||
31165 height < this.minWidth ||
31166 height > this.imageEl.OriginHeight
31176 onRotateLeft : function(e)
31178 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31180 var minScale = this.thumbEl.getWidth() / this.minWidth;
31182 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31183 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31185 this.startScale = this.scale;
31187 while (this.getScaleLevel() < minScale){
31189 this.scale = this.scale + 1;
31191 if(!this.zoomable()){
31196 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31197 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31202 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31209 this.scale = this.startScale;
31211 this.onRotateFail();
31216 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31218 if(this.isDocument){
31219 this.setThumbBoxSize();
31220 this.setThumbBoxPosition();
31221 this.setCanvasPosition();
31226 this.fireEvent('rotate', this, 'left');
31230 onRotateRight : function(e)
31232 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31234 var minScale = this.thumbEl.getWidth() / this.minWidth;
31236 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31237 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31239 this.startScale = this.scale;
31241 while (this.getScaleLevel() < minScale){
31243 this.scale = this.scale + 1;
31245 if(!this.zoomable()){
31250 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31251 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31256 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31263 this.scale = this.startScale;
31265 this.onRotateFail();
31270 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31272 if(this.isDocument){
31273 this.setThumbBoxSize();
31274 this.setThumbBoxPosition();
31275 this.setCanvasPosition();
31280 this.fireEvent('rotate', this, 'right');
31283 onRotateFail : function()
31285 this.errorEl.show(true);
31289 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31294 this.previewEl.dom.innerHTML = '';
31296 var canvasEl = document.createElement("canvas");
31298 var contextEl = canvasEl.getContext("2d");
31300 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31301 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31302 var center = this.imageEl.OriginWidth / 2;
31304 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31305 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31306 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31307 center = this.imageEl.OriginHeight / 2;
31310 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31312 contextEl.translate(center, center);
31313 contextEl.rotate(this.rotate * Math.PI / 180);
31315 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31317 this.canvasEl = document.createElement("canvas");
31319 this.contextEl = this.canvasEl.getContext("2d");
31321 switch (this.rotate) {
31324 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31325 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31327 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31332 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31333 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31335 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31336 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31340 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31345 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31346 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31348 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31349 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31353 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);
31358 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31359 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31361 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31362 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31366 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);
31373 this.previewEl.appendChild(this.canvasEl);
31375 this.setCanvasPosition();
31380 if(!this.canvasLoaded){
31384 var imageCanvas = document.createElement("canvas");
31386 var imageContext = imageCanvas.getContext("2d");
31388 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31389 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31391 var center = imageCanvas.width / 2;
31393 imageContext.translate(center, center);
31395 imageContext.rotate(this.rotate * Math.PI / 180);
31397 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31399 var canvas = document.createElement("canvas");
31401 var context = canvas.getContext("2d");
31403 canvas.width = this.minWidth;
31404 canvas.height = this.minHeight;
31406 switch (this.rotate) {
31409 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31410 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31412 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31413 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31415 var targetWidth = this.minWidth - 2 * x;
31416 var targetHeight = this.minHeight - 2 * y;
31420 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31421 scale = targetWidth / width;
31424 if(x > 0 && y == 0){
31425 scale = targetHeight / height;
31428 if(x > 0 && y > 0){
31429 scale = targetWidth / width;
31431 if(width < height){
31432 scale = targetHeight / height;
31436 context.scale(scale, scale);
31438 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31439 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31441 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31442 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31444 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31449 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31450 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31452 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31453 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31455 var targetWidth = this.minWidth - 2 * x;
31456 var targetHeight = this.minHeight - 2 * y;
31460 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31461 scale = targetWidth / width;
31464 if(x > 0 && y == 0){
31465 scale = targetHeight / height;
31468 if(x > 0 && y > 0){
31469 scale = targetWidth / width;
31471 if(width < height){
31472 scale = targetHeight / height;
31476 context.scale(scale, scale);
31478 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31479 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31481 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31482 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31484 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31486 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31491 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31492 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31494 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31495 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31497 var targetWidth = this.minWidth - 2 * x;
31498 var targetHeight = this.minHeight - 2 * y;
31502 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31503 scale = targetWidth / width;
31506 if(x > 0 && y == 0){
31507 scale = targetHeight / height;
31510 if(x > 0 && y > 0){
31511 scale = targetWidth / width;
31513 if(width < height){
31514 scale = targetHeight / height;
31518 context.scale(scale, scale);
31520 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31521 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31523 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31524 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31526 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31527 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31529 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31534 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31535 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31537 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31538 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31540 var targetWidth = this.minWidth - 2 * x;
31541 var targetHeight = this.minHeight - 2 * y;
31545 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31546 scale = targetWidth / width;
31549 if(x > 0 && y == 0){
31550 scale = targetHeight / height;
31553 if(x > 0 && y > 0){
31554 scale = targetWidth / width;
31556 if(width < height){
31557 scale = targetHeight / height;
31561 context.scale(scale, scale);
31563 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31564 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31566 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31567 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31569 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31571 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31578 this.cropData = canvas.toDataURL(this.cropType);
31580 if(this.fireEvent('crop', this, this.cropData) !== false){
31581 this.process(this.file, this.cropData);
31588 setThumbBoxSize : function()
31592 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31593 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31594 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31596 this.minWidth = width;
31597 this.minHeight = height;
31599 if(this.rotate == 90 || this.rotate == 270){
31600 this.minWidth = height;
31601 this.minHeight = width;
31606 width = Math.ceil(this.minWidth * height / this.minHeight);
31608 if(this.minWidth > this.minHeight){
31610 height = Math.ceil(this.minHeight * width / this.minWidth);
31613 this.thumbEl.setStyle({
31614 width : width + 'px',
31615 height : height + 'px'
31622 setThumbBoxPosition : function()
31624 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31625 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31627 this.thumbEl.setLeft(x);
31628 this.thumbEl.setTop(y);
31632 baseRotateLevel : function()
31634 this.baseRotate = 1;
31637 typeof(this.exif) != 'undefined' &&
31638 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31639 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31641 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31644 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31648 baseScaleLevel : function()
31652 if(this.isDocument){
31654 if(this.baseRotate == 6 || this.baseRotate == 8){
31656 height = this.thumbEl.getHeight();
31657 this.baseScale = height / this.imageEl.OriginWidth;
31659 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31660 width = this.thumbEl.getWidth();
31661 this.baseScale = width / this.imageEl.OriginHeight;
31667 height = this.thumbEl.getHeight();
31668 this.baseScale = height / this.imageEl.OriginHeight;
31670 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31671 width = this.thumbEl.getWidth();
31672 this.baseScale = width / this.imageEl.OriginWidth;
31678 if(this.baseRotate == 6 || this.baseRotate == 8){
31680 width = this.thumbEl.getHeight();
31681 this.baseScale = width / this.imageEl.OriginHeight;
31683 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31684 height = this.thumbEl.getWidth();
31685 this.baseScale = height / this.imageEl.OriginHeight;
31688 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31689 height = this.thumbEl.getWidth();
31690 this.baseScale = height / this.imageEl.OriginHeight;
31692 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31693 width = this.thumbEl.getHeight();
31694 this.baseScale = width / this.imageEl.OriginWidth;
31701 width = this.thumbEl.getWidth();
31702 this.baseScale = width / this.imageEl.OriginWidth;
31704 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31705 height = this.thumbEl.getHeight();
31706 this.baseScale = height / this.imageEl.OriginHeight;
31709 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31711 height = this.thumbEl.getHeight();
31712 this.baseScale = height / this.imageEl.OriginHeight;
31714 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31715 width = this.thumbEl.getWidth();
31716 this.baseScale = width / this.imageEl.OriginWidth;
31724 getScaleLevel : function()
31726 return this.baseScale * Math.pow(1.1, this.scale);
31729 onTouchStart : function(e)
31731 if(!this.canvasLoaded){
31732 this.beforeSelectFile(e);
31736 var touches = e.browserEvent.touches;
31742 if(touches.length == 1){
31743 this.onMouseDown(e);
31747 if(touches.length != 2){
31753 for(var i = 0, finger; finger = touches[i]; i++){
31754 coords.push(finger.pageX, finger.pageY);
31757 var x = Math.pow(coords[0] - coords[2], 2);
31758 var y = Math.pow(coords[1] - coords[3], 2);
31760 this.startDistance = Math.sqrt(x + y);
31762 this.startScale = this.scale;
31764 this.pinching = true;
31765 this.dragable = false;
31769 onTouchMove : function(e)
31771 if(!this.pinching && !this.dragable){
31775 var touches = e.browserEvent.touches;
31782 this.onMouseMove(e);
31788 for(var i = 0, finger; finger = touches[i]; i++){
31789 coords.push(finger.pageX, finger.pageY);
31792 var x = Math.pow(coords[0] - coords[2], 2);
31793 var y = Math.pow(coords[1] - coords[3], 2);
31795 this.endDistance = Math.sqrt(x + y);
31797 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31799 if(!this.zoomable()){
31800 this.scale = this.startScale;
31808 onTouchEnd : function(e)
31810 this.pinching = false;
31811 this.dragable = false;
31815 process : function(file, crop)
31818 this.maskEl.mask(this.loadingText);
31821 this.xhr = new XMLHttpRequest();
31823 file.xhr = this.xhr;
31825 this.xhr.open(this.method, this.url, true);
31828 "Accept": "application/json",
31829 "Cache-Control": "no-cache",
31830 "X-Requested-With": "XMLHttpRequest"
31833 for (var headerName in headers) {
31834 var headerValue = headers[headerName];
31836 this.xhr.setRequestHeader(headerName, headerValue);
31842 this.xhr.onload = function()
31844 _this.xhrOnLoad(_this.xhr);
31847 this.xhr.onerror = function()
31849 _this.xhrOnError(_this.xhr);
31852 var formData = new FormData();
31854 formData.append('returnHTML', 'NO');
31857 formData.append('crop', crop);
31860 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31861 formData.append(this.paramName, file, file.name);
31864 if(typeof(file.filename) != 'undefined'){
31865 formData.append('filename', file.filename);
31868 if(typeof(file.mimetype) != 'undefined'){
31869 formData.append('mimetype', file.mimetype);
31872 if(this.fireEvent('arrange', this, formData) != false){
31873 this.xhr.send(formData);
31877 xhrOnLoad : function(xhr)
31880 this.maskEl.unmask();
31883 if (xhr.readyState !== 4) {
31884 this.fireEvent('exception', this, xhr);
31888 var response = Roo.decode(xhr.responseText);
31890 if(!response.success){
31891 this.fireEvent('exception', this, xhr);
31895 var response = Roo.decode(xhr.responseText);
31897 this.fireEvent('upload', this, response);
31901 xhrOnError : function()
31904 this.maskEl.unmask();
31907 Roo.log('xhr on error');
31909 var response = Roo.decode(xhr.responseText);
31915 prepare : function(file)
31918 this.maskEl.mask(this.loadingText);
31924 if(typeof(file) === 'string'){
31925 this.loadCanvas(file);
31929 if(!file || !this.urlAPI){
31934 this.cropType = file.type;
31938 if(this.fireEvent('prepare', this, this.file) != false){
31940 var reader = new FileReader();
31942 reader.onload = function (e) {
31943 if (e.target.error) {
31944 Roo.log(e.target.error);
31948 var buffer = e.target.result,
31949 dataView = new DataView(buffer),
31951 maxOffset = dataView.byteLength - 4,
31955 if (dataView.getUint16(0) === 0xffd8) {
31956 while (offset < maxOffset) {
31957 markerBytes = dataView.getUint16(offset);
31959 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31960 markerLength = dataView.getUint16(offset + 2) + 2;
31961 if (offset + markerLength > dataView.byteLength) {
31962 Roo.log('Invalid meta data: Invalid segment size.');
31966 if(markerBytes == 0xffe1){
31967 _this.parseExifData(
31974 offset += markerLength;
31984 var url = _this.urlAPI.createObjectURL(_this.file);
31986 _this.loadCanvas(url);
31991 reader.readAsArrayBuffer(this.file);
31997 parseExifData : function(dataView, offset, length)
31999 var tiffOffset = offset + 10,
32003 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32004 // No Exif data, might be XMP data instead
32008 // Check for the ASCII code for "Exif" (0x45786966):
32009 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32010 // No Exif data, might be XMP data instead
32013 if (tiffOffset + 8 > dataView.byteLength) {
32014 Roo.log('Invalid Exif data: Invalid segment size.');
32017 // Check for the two null bytes:
32018 if (dataView.getUint16(offset + 8) !== 0x0000) {
32019 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32022 // Check the byte alignment:
32023 switch (dataView.getUint16(tiffOffset)) {
32025 littleEndian = true;
32028 littleEndian = false;
32031 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32034 // Check for the TIFF tag marker (0x002A):
32035 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32036 Roo.log('Invalid Exif data: Missing TIFF marker.');
32039 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32040 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32042 this.parseExifTags(
32045 tiffOffset + dirOffset,
32050 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32055 if (dirOffset + 6 > dataView.byteLength) {
32056 Roo.log('Invalid Exif data: Invalid directory offset.');
32059 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32060 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32061 if (dirEndOffset + 4 > dataView.byteLength) {
32062 Roo.log('Invalid Exif data: Invalid directory size.');
32065 for (i = 0; i < tagsNumber; i += 1) {
32069 dirOffset + 2 + 12 * i, // tag offset
32073 // Return the offset to the next directory:
32074 return dataView.getUint32(dirEndOffset, littleEndian);
32077 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32079 var tag = dataView.getUint16(offset, littleEndian);
32081 this.exif[tag] = this.getExifValue(
32085 dataView.getUint16(offset + 2, littleEndian), // tag type
32086 dataView.getUint32(offset + 4, littleEndian), // tag length
32091 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32093 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32102 Roo.log('Invalid Exif data: Invalid tag type.');
32106 tagSize = tagType.size * length;
32107 // Determine if the value is contained in the dataOffset bytes,
32108 // or if the value at the dataOffset is a pointer to the actual data:
32109 dataOffset = tagSize > 4 ?
32110 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32111 if (dataOffset + tagSize > dataView.byteLength) {
32112 Roo.log('Invalid Exif data: Invalid data offset.');
32115 if (length === 1) {
32116 return tagType.getValue(dataView, dataOffset, littleEndian);
32119 for (i = 0; i < length; i += 1) {
32120 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32123 if (tagType.ascii) {
32125 // Concatenate the chars:
32126 for (i = 0; i < values.length; i += 1) {
32128 // Ignore the terminating NULL byte(s):
32129 if (c === '\u0000') {
32141 Roo.apply(Roo.bootstrap.UploadCropbox, {
32143 'Orientation': 0x0112
32147 1: 0, //'top-left',
32149 3: 180, //'bottom-right',
32150 // 4: 'bottom-left',
32152 6: 90, //'right-top',
32153 // 7: 'right-bottom',
32154 8: 270 //'left-bottom'
32158 // byte, 8-bit unsigned int:
32160 getValue: function (dataView, dataOffset) {
32161 return dataView.getUint8(dataOffset);
32165 // ascii, 8-bit byte:
32167 getValue: function (dataView, dataOffset) {
32168 return String.fromCharCode(dataView.getUint8(dataOffset));
32173 // short, 16 bit int:
32175 getValue: function (dataView, dataOffset, littleEndian) {
32176 return dataView.getUint16(dataOffset, littleEndian);
32180 // long, 32 bit int:
32182 getValue: function (dataView, dataOffset, littleEndian) {
32183 return dataView.getUint32(dataOffset, littleEndian);
32187 // rational = two long values, first is numerator, second is denominator:
32189 getValue: function (dataView, dataOffset, littleEndian) {
32190 return dataView.getUint32(dataOffset, littleEndian) /
32191 dataView.getUint32(dataOffset + 4, littleEndian);
32195 // slong, 32 bit signed int:
32197 getValue: function (dataView, dataOffset, littleEndian) {
32198 return dataView.getInt32(dataOffset, littleEndian);
32202 // srational, two slongs, first is numerator, second is denominator:
32204 getValue: function (dataView, dataOffset, littleEndian) {
32205 return dataView.getInt32(dataOffset, littleEndian) /
32206 dataView.getInt32(dataOffset + 4, littleEndian);
32216 cls : 'btn-group roo-upload-cropbox-rotate-left',
32217 action : 'rotate-left',
32221 cls : 'btn btn-default',
32222 html : '<i class="fa fa-undo"></i>'
32228 cls : 'btn-group roo-upload-cropbox-picture',
32229 action : 'picture',
32233 cls : 'btn btn-default',
32234 html : '<i class="fa fa-picture-o"></i>'
32240 cls : 'btn-group roo-upload-cropbox-rotate-right',
32241 action : 'rotate-right',
32245 cls : 'btn btn-default',
32246 html : '<i class="fa fa-repeat"></i>'
32254 cls : 'btn-group roo-upload-cropbox-rotate-left',
32255 action : 'rotate-left',
32259 cls : 'btn btn-default',
32260 html : '<i class="fa fa-undo"></i>'
32266 cls : 'btn-group roo-upload-cropbox-download',
32267 action : 'download',
32271 cls : 'btn btn-default',
32272 html : '<i class="fa fa-download"></i>'
32278 cls : 'btn-group roo-upload-cropbox-crop',
32283 cls : 'btn btn-default',
32284 html : '<i class="fa fa-crop"></i>'
32290 cls : 'btn-group roo-upload-cropbox-trash',
32295 cls : 'btn btn-default',
32296 html : '<i class="fa fa-trash"></i>'
32302 cls : 'btn-group roo-upload-cropbox-rotate-right',
32303 action : 'rotate-right',
32307 cls : 'btn btn-default',
32308 html : '<i class="fa fa-repeat"></i>'
32316 cls : 'btn-group roo-upload-cropbox-rotate-left',
32317 action : 'rotate-left',
32321 cls : 'btn btn-default',
32322 html : '<i class="fa fa-undo"></i>'
32328 cls : 'btn-group roo-upload-cropbox-rotate-right',
32329 action : 'rotate-right',
32333 cls : 'btn btn-default',
32334 html : '<i class="fa fa-repeat"></i>'
32347 * @class Roo.bootstrap.DocumentManager
32348 * @extends Roo.bootstrap.Component
32349 * Bootstrap DocumentManager class
32350 * @cfg {String} paramName default 'imageUpload'
32351 * @cfg {String} toolTipName default 'filename'
32352 * @cfg {String} method default POST
32353 * @cfg {String} url action url
32354 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32355 * @cfg {Boolean} multiple multiple upload default true
32356 * @cfg {Number} thumbSize default 300
32357 * @cfg {String} fieldLabel
32358 * @cfg {Number} labelWidth default 4
32359 * @cfg {String} labelAlign (left|top) default left
32360 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32361 * @cfg {Number} labellg set the width of label (1-12)
32362 * @cfg {Number} labelmd set the width of label (1-12)
32363 * @cfg {Number} labelsm set the width of label (1-12)
32364 * @cfg {Number} labelxs set the width of label (1-12)
32367 * Create a new DocumentManager
32368 * @param {Object} config The config object
32371 Roo.bootstrap.DocumentManager = function(config){
32372 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32375 this.delegates = [];
32380 * Fire when initial the DocumentManager
32381 * @param {Roo.bootstrap.DocumentManager} this
32386 * inspect selected file
32387 * @param {Roo.bootstrap.DocumentManager} this
32388 * @param {File} file
32393 * Fire when xhr load exception
32394 * @param {Roo.bootstrap.DocumentManager} this
32395 * @param {XMLHttpRequest} xhr
32397 "exception" : true,
32399 * @event afterupload
32400 * Fire when xhr load exception
32401 * @param {Roo.bootstrap.DocumentManager} this
32402 * @param {XMLHttpRequest} xhr
32404 "afterupload" : true,
32407 * prepare the form data
32408 * @param {Roo.bootstrap.DocumentManager} this
32409 * @param {Object} formData
32414 * Fire when remove the file
32415 * @param {Roo.bootstrap.DocumentManager} this
32416 * @param {Object} file
32421 * Fire after refresh the file
32422 * @param {Roo.bootstrap.DocumentManager} this
32427 * Fire after click the image
32428 * @param {Roo.bootstrap.DocumentManager} this
32429 * @param {Object} file
32434 * Fire when upload a image and editable set to true
32435 * @param {Roo.bootstrap.DocumentManager} this
32436 * @param {Object} file
32440 * @event beforeselectfile
32441 * Fire before select file
32442 * @param {Roo.bootstrap.DocumentManager} this
32444 "beforeselectfile" : true,
32447 * Fire before process file
32448 * @param {Roo.bootstrap.DocumentManager} this
32449 * @param {Object} file
32453 * @event previewrendered
32454 * Fire when preview rendered
32455 * @param {Roo.bootstrap.DocumentManager} this
32456 * @param {Object} file
32458 "previewrendered" : true,
32461 "previewResize" : true
32466 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32475 paramName : 'imageUpload',
32476 toolTipName : 'filename',
32479 labelAlign : 'left',
32489 getAutoCreate : function()
32491 var managerWidget = {
32493 cls : 'roo-document-manager',
32497 cls : 'roo-document-manager-selector',
32502 cls : 'roo-document-manager-uploader',
32506 cls : 'roo-document-manager-upload-btn',
32507 html : '<i class="fa fa-plus"></i>'
32518 cls : 'column col-md-12',
32523 if(this.fieldLabel.length){
32528 cls : 'column col-md-12',
32529 html : this.fieldLabel
32533 cls : 'column col-md-12',
32538 if(this.labelAlign == 'left'){
32543 html : this.fieldLabel
32552 if(this.labelWidth > 12){
32553 content[0].style = "width: " + this.labelWidth + 'px';
32556 if(this.labelWidth < 13 && this.labelmd == 0){
32557 this.labelmd = this.labelWidth;
32560 if(this.labellg > 0){
32561 content[0].cls += ' col-lg-' + this.labellg;
32562 content[1].cls += ' col-lg-' + (12 - this.labellg);
32565 if(this.labelmd > 0){
32566 content[0].cls += ' col-md-' + this.labelmd;
32567 content[1].cls += ' col-md-' + (12 - this.labelmd);
32570 if(this.labelsm > 0){
32571 content[0].cls += ' col-sm-' + this.labelsm;
32572 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32575 if(this.labelxs > 0){
32576 content[0].cls += ' col-xs-' + this.labelxs;
32577 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32585 cls : 'row clearfix',
32593 initEvents : function()
32595 this.managerEl = this.el.select('.roo-document-manager', true).first();
32596 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32598 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32599 this.selectorEl.hide();
32602 this.selectorEl.attr('multiple', 'multiple');
32605 this.selectorEl.on('change', this.onFileSelected, this);
32607 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32608 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32610 this.uploader.on('click', this.onUploaderClick, this);
32612 this.renderProgressDialog();
32616 window.addEventListener("resize", function() { _this.refresh(); } );
32618 this.fireEvent('initial', this);
32621 renderProgressDialog : function()
32625 this.progressDialog = new Roo.bootstrap.Modal({
32626 cls : 'roo-document-manager-progress-dialog',
32627 allow_close : false,
32638 btnclick : function() {
32639 _this.uploadCancel();
32645 this.progressDialog.render(Roo.get(document.body));
32647 this.progress = new Roo.bootstrap.Progress({
32648 cls : 'roo-document-manager-progress',
32653 this.progress.render(this.progressDialog.getChildContainer());
32655 this.progressBar = new Roo.bootstrap.ProgressBar({
32656 cls : 'roo-document-manager-progress-bar',
32659 aria_valuemax : 12,
32663 this.progressBar.render(this.progress.getChildContainer());
32666 onUploaderClick : function(e)
32668 e.preventDefault();
32670 if(this.fireEvent('beforeselectfile', this) != false){
32671 this.selectorEl.dom.click();
32676 onFileSelected : function(e)
32678 e.preventDefault();
32680 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32684 Roo.each(this.selectorEl.dom.files, function(file){
32685 if(this.fireEvent('inspect', this, file) != false){
32686 this.files.push(file);
32696 this.selectorEl.dom.value = '';
32698 if(!this.files || !this.files.length){
32702 if(this.boxes > 0 && this.files.length > this.boxes){
32703 this.files = this.files.slice(0, this.boxes);
32706 this.uploader.show();
32708 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32709 this.uploader.hide();
32718 Roo.each(this.files, function(file){
32720 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32721 var f = this.renderPreview(file);
32726 if(file.type.indexOf('image') != -1){
32727 this.delegates.push(
32729 _this.process(file);
32730 }).createDelegate(this)
32738 _this.process(file);
32739 }).createDelegate(this)
32744 this.files = files;
32746 this.delegates = this.delegates.concat(docs);
32748 if(!this.delegates.length){
32753 this.progressBar.aria_valuemax = this.delegates.length;
32760 arrange : function()
32762 if(!this.delegates.length){
32763 this.progressDialog.hide();
32768 var delegate = this.delegates.shift();
32770 this.progressDialog.show();
32772 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32774 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32779 refresh : function()
32781 this.uploader.show();
32783 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32784 this.uploader.hide();
32787 Roo.isTouch ? this.closable(false) : this.closable(true);
32789 this.fireEvent('refresh', this);
32792 onRemove : function(e, el, o)
32794 e.preventDefault();
32796 this.fireEvent('remove', this, o);
32800 remove : function(o)
32804 Roo.each(this.files, function(file){
32805 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32814 this.files = files;
32821 Roo.each(this.files, function(file){
32826 file.target.remove();
32835 onClick : function(e, el, o)
32837 e.preventDefault();
32839 this.fireEvent('click', this, o);
32843 closable : function(closable)
32845 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32847 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32859 xhrOnLoad : function(xhr)
32861 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32865 if (xhr.readyState !== 4) {
32867 this.fireEvent('exception', this, xhr);
32871 var response = Roo.decode(xhr.responseText);
32873 if(!response.success){
32875 this.fireEvent('exception', this, xhr);
32879 var file = this.renderPreview(response.data);
32881 this.files.push(file);
32885 this.fireEvent('afterupload', this, xhr);
32889 xhrOnError : function(xhr)
32891 Roo.log('xhr on error');
32893 var response = Roo.decode(xhr.responseText);
32900 process : function(file)
32902 if(this.fireEvent('process', this, file) !== false){
32903 if(this.editable && file.type.indexOf('image') != -1){
32904 this.fireEvent('edit', this, file);
32908 this.uploadStart(file, false);
32915 uploadStart : function(file, crop)
32917 this.xhr = new XMLHttpRequest();
32919 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32924 file.xhr = this.xhr;
32926 this.managerEl.createChild({
32928 cls : 'roo-document-manager-loading',
32932 tooltip : file.name,
32933 cls : 'roo-document-manager-thumb',
32934 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32940 this.xhr.open(this.method, this.url, true);
32943 "Accept": "application/json",
32944 "Cache-Control": "no-cache",
32945 "X-Requested-With": "XMLHttpRequest"
32948 for (var headerName in headers) {
32949 var headerValue = headers[headerName];
32951 this.xhr.setRequestHeader(headerName, headerValue);
32957 this.xhr.onload = function()
32959 _this.xhrOnLoad(_this.xhr);
32962 this.xhr.onerror = function()
32964 _this.xhrOnError(_this.xhr);
32967 var formData = new FormData();
32969 formData.append('returnHTML', 'NO');
32972 formData.append('crop', crop);
32975 formData.append(this.paramName, file, file.name);
32982 if(this.fireEvent('prepare', this, formData, options) != false){
32984 if(options.manually){
32988 this.xhr.send(formData);
32992 this.uploadCancel();
32995 uploadCancel : function()
33001 this.delegates = [];
33003 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33010 renderPreview : function(file)
33012 if(typeof(file.target) != 'undefined' && file.target){
33016 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33018 var previewEl = this.managerEl.createChild({
33020 cls : 'roo-document-manager-preview',
33024 tooltip : file[this.toolTipName],
33025 cls : 'roo-document-manager-thumb',
33026 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33031 html : '<i class="fa fa-times-circle"></i>'
33036 var close = previewEl.select('button.close', true).first();
33038 close.on('click', this.onRemove, this, file);
33040 file.target = previewEl;
33042 var image = previewEl.select('img', true).first();
33046 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33048 image.on('click', this.onClick, this, file);
33050 this.fireEvent('previewrendered', this, file);
33056 onPreviewLoad : function(file, image)
33058 if(typeof(file.target) == 'undefined' || !file.target){
33062 var width = image.dom.naturalWidth || image.dom.width;
33063 var height = image.dom.naturalHeight || image.dom.height;
33065 if(!this.previewResize) {
33069 if(width > height){
33070 file.target.addClass('wide');
33074 file.target.addClass('tall');
33079 uploadFromSource : function(file, crop)
33081 this.xhr = new XMLHttpRequest();
33083 this.managerEl.createChild({
33085 cls : 'roo-document-manager-loading',
33089 tooltip : file.name,
33090 cls : 'roo-document-manager-thumb',
33091 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33097 this.xhr.open(this.method, this.url, true);
33100 "Accept": "application/json",
33101 "Cache-Control": "no-cache",
33102 "X-Requested-With": "XMLHttpRequest"
33105 for (var headerName in headers) {
33106 var headerValue = headers[headerName];
33108 this.xhr.setRequestHeader(headerName, headerValue);
33114 this.xhr.onload = function()
33116 _this.xhrOnLoad(_this.xhr);
33119 this.xhr.onerror = function()
33121 _this.xhrOnError(_this.xhr);
33124 var formData = new FormData();
33126 formData.append('returnHTML', 'NO');
33128 formData.append('crop', crop);
33130 if(typeof(file.filename) != 'undefined'){
33131 formData.append('filename', file.filename);
33134 if(typeof(file.mimetype) != 'undefined'){
33135 formData.append('mimetype', file.mimetype);
33140 if(this.fireEvent('prepare', this, formData) != false){
33141 this.xhr.send(formData);
33151 * @class Roo.bootstrap.DocumentViewer
33152 * @extends Roo.bootstrap.Component
33153 * Bootstrap DocumentViewer class
33154 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33155 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33158 * Create a new DocumentViewer
33159 * @param {Object} config The config object
33162 Roo.bootstrap.DocumentViewer = function(config){
33163 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33168 * Fire after initEvent
33169 * @param {Roo.bootstrap.DocumentViewer} this
33175 * @param {Roo.bootstrap.DocumentViewer} this
33180 * Fire after download button
33181 * @param {Roo.bootstrap.DocumentViewer} this
33186 * Fire after trash button
33187 * @param {Roo.bootstrap.DocumentViewer} this
33194 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33196 showDownload : true,
33200 getAutoCreate : function()
33204 cls : 'roo-document-viewer',
33208 cls : 'roo-document-viewer-body',
33212 cls : 'roo-document-viewer-thumb',
33216 cls : 'roo-document-viewer-image'
33224 cls : 'roo-document-viewer-footer',
33227 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33231 cls : 'btn-group roo-document-viewer-download',
33235 cls : 'btn btn-default',
33236 html : '<i class="fa fa-download"></i>'
33242 cls : 'btn-group roo-document-viewer-trash',
33246 cls : 'btn btn-default',
33247 html : '<i class="fa fa-trash"></i>'
33260 initEvents : function()
33262 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33263 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33265 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33266 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33268 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33269 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33271 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33272 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33274 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33275 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33277 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33278 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33280 this.bodyEl.on('click', this.onClick, this);
33281 this.downloadBtn.on('click', this.onDownload, this);
33282 this.trashBtn.on('click', this.onTrash, this);
33284 this.downloadBtn.hide();
33285 this.trashBtn.hide();
33287 if(this.showDownload){
33288 this.downloadBtn.show();
33291 if(this.showTrash){
33292 this.trashBtn.show();
33295 if(!this.showDownload && !this.showTrash) {
33296 this.footerEl.hide();
33301 initial : function()
33303 this.fireEvent('initial', this);
33307 onClick : function(e)
33309 e.preventDefault();
33311 this.fireEvent('click', this);
33314 onDownload : function(e)
33316 e.preventDefault();
33318 this.fireEvent('download', this);
33321 onTrash : function(e)
33323 e.preventDefault();
33325 this.fireEvent('trash', this);
33337 * @class Roo.bootstrap.NavProgressBar
33338 * @extends Roo.bootstrap.Component
33339 * Bootstrap NavProgressBar class
33342 * Create a new nav progress bar
33343 * @param {Object} config The config object
33346 Roo.bootstrap.NavProgressBar = function(config){
33347 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33349 this.bullets = this.bullets || [];
33351 // Roo.bootstrap.NavProgressBar.register(this);
33355 * Fires when the active item changes
33356 * @param {Roo.bootstrap.NavProgressBar} this
33357 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33358 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33365 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33370 getAutoCreate : function()
33372 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33376 cls : 'roo-navigation-bar-group',
33380 cls : 'roo-navigation-top-bar'
33384 cls : 'roo-navigation-bullets-bar',
33388 cls : 'roo-navigation-bar'
33395 cls : 'roo-navigation-bottom-bar'
33405 initEvents: function()
33410 onRender : function(ct, position)
33412 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33414 if(this.bullets.length){
33415 Roo.each(this.bullets, function(b){
33424 addItem : function(cfg)
33426 var item = new Roo.bootstrap.NavProgressItem(cfg);
33428 item.parentId = this.id;
33429 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33432 var top = new Roo.bootstrap.Element({
33434 cls : 'roo-navigation-bar-text'
33437 var bottom = new Roo.bootstrap.Element({
33439 cls : 'roo-navigation-bar-text'
33442 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33443 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33445 var topText = new Roo.bootstrap.Element({
33447 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33450 var bottomText = new Roo.bootstrap.Element({
33452 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33455 topText.onRender(top.el, null);
33456 bottomText.onRender(bottom.el, null);
33459 item.bottomEl = bottom;
33462 this.barItems.push(item);
33467 getActive : function()
33469 var active = false;
33471 Roo.each(this.barItems, function(v){
33473 if (!v.isActive()) {
33485 setActiveItem : function(item)
33489 Roo.each(this.barItems, function(v){
33490 if (v.rid == item.rid) {
33494 if (v.isActive()) {
33495 v.setActive(false);
33500 item.setActive(true);
33502 this.fireEvent('changed', this, item, prev);
33505 getBarItem: function(rid)
33509 Roo.each(this.barItems, function(e) {
33510 if (e.rid != rid) {
33521 indexOfItem : function(item)
33525 Roo.each(this.barItems, function(v, i){
33527 if (v.rid != item.rid) {
33538 setActiveNext : function()
33540 var i = this.indexOfItem(this.getActive());
33542 if (i > this.barItems.length) {
33546 this.setActiveItem(this.barItems[i+1]);
33549 setActivePrev : function()
33551 var i = this.indexOfItem(this.getActive());
33557 this.setActiveItem(this.barItems[i-1]);
33560 format : function()
33562 if(!this.barItems.length){
33566 var width = 100 / this.barItems.length;
33568 Roo.each(this.barItems, function(i){
33569 i.el.setStyle('width', width + '%');
33570 i.topEl.el.setStyle('width', width + '%');
33571 i.bottomEl.el.setStyle('width', width + '%');
33580 * Nav Progress Item
33585 * @class Roo.bootstrap.NavProgressItem
33586 * @extends Roo.bootstrap.Component
33587 * Bootstrap NavProgressItem class
33588 * @cfg {String} rid the reference id
33589 * @cfg {Boolean} active (true|false) Is item active default false
33590 * @cfg {Boolean} disabled (true|false) Is item active default false
33591 * @cfg {String} html
33592 * @cfg {String} position (top|bottom) text position default bottom
33593 * @cfg {String} icon show icon instead of number
33596 * Create a new NavProgressItem
33597 * @param {Object} config The config object
33599 Roo.bootstrap.NavProgressItem = function(config){
33600 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33605 * The raw click event for the entire grid.
33606 * @param {Roo.bootstrap.NavProgressItem} this
33607 * @param {Roo.EventObject} e
33614 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33620 position : 'bottom',
33623 getAutoCreate : function()
33625 var iconCls = 'roo-navigation-bar-item-icon';
33627 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33631 cls: 'roo-navigation-bar-item',
33641 cfg.cls += ' active';
33644 cfg.cls += ' disabled';
33650 disable : function()
33652 this.setDisabled(true);
33655 enable : function()
33657 this.setDisabled(false);
33660 initEvents: function()
33662 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33664 this.iconEl.on('click', this.onClick, this);
33667 onClick : function(e)
33669 e.preventDefault();
33675 if(this.fireEvent('click', this, e) === false){
33679 this.parent().setActiveItem(this);
33682 isActive: function ()
33684 return this.active;
33687 setActive : function(state)
33689 if(this.active == state){
33693 this.active = state;
33696 this.el.addClass('active');
33700 this.el.removeClass('active');
33705 setDisabled : function(state)
33707 if(this.disabled == state){
33711 this.disabled = state;
33714 this.el.addClass('disabled');
33718 this.el.removeClass('disabled');
33721 tooltipEl : function()
33723 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33736 * @class Roo.bootstrap.FieldLabel
33737 * @extends Roo.bootstrap.Component
33738 * Bootstrap FieldLabel class
33739 * @cfg {String} html contents of the element
33740 * @cfg {String} tag tag of the element default label
33741 * @cfg {String} cls class of the element
33742 * @cfg {String} target label target
33743 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33744 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33745 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33746 * @cfg {String} iconTooltip default "This field is required"
33747 * @cfg {String} indicatorpos (left|right) default left
33750 * Create a new FieldLabel
33751 * @param {Object} config The config object
33754 Roo.bootstrap.FieldLabel = function(config){
33755 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33760 * Fires after the field has been marked as invalid.
33761 * @param {Roo.form.FieldLabel} this
33762 * @param {String} msg The validation message
33767 * Fires after the field has been validated with no errors.
33768 * @param {Roo.form.FieldLabel} this
33774 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33781 invalidClass : 'has-warning',
33782 validClass : 'has-success',
33783 iconTooltip : 'This field is required',
33784 indicatorpos : 'left',
33786 getAutoCreate : function(){
33789 if (!this.allowBlank) {
33795 cls : 'roo-bootstrap-field-label ' + this.cls,
33800 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33801 tooltip : this.iconTooltip
33810 if(this.indicatorpos == 'right'){
33813 cls : 'roo-bootstrap-field-label ' + this.cls,
33822 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33823 tooltip : this.iconTooltip
33832 initEvents: function()
33834 Roo.bootstrap.Element.superclass.initEvents.call(this);
33836 this.indicator = this.indicatorEl();
33838 if(this.indicator){
33839 this.indicator.removeClass('visible');
33840 this.indicator.addClass('invisible');
33843 Roo.bootstrap.FieldLabel.register(this);
33846 indicatorEl : function()
33848 var indicator = this.el.select('i.roo-required-indicator',true).first();
33859 * Mark this field as valid
33861 markValid : function()
33863 if(this.indicator){
33864 this.indicator.removeClass('visible');
33865 this.indicator.addClass('invisible');
33867 if (Roo.bootstrap.version == 3) {
33868 this.el.removeClass(this.invalidClass);
33869 this.el.addClass(this.validClass);
33871 this.el.removeClass('is-invalid');
33872 this.el.addClass('is-valid');
33876 this.fireEvent('valid', this);
33880 * Mark this field as invalid
33881 * @param {String} msg The validation message
33883 markInvalid : function(msg)
33885 if(this.indicator){
33886 this.indicator.removeClass('invisible');
33887 this.indicator.addClass('visible');
33889 if (Roo.bootstrap.version == 3) {
33890 this.el.removeClass(this.validClass);
33891 this.el.addClass(this.invalidClass);
33893 this.el.removeClass('is-valid');
33894 this.el.addClass('is-invalid');
33898 this.fireEvent('invalid', this, msg);
33904 Roo.apply(Roo.bootstrap.FieldLabel, {
33909 * register a FieldLabel Group
33910 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33912 register : function(label)
33914 if(this.groups.hasOwnProperty(label.target)){
33918 this.groups[label.target] = label;
33922 * fetch a FieldLabel Group based on the target
33923 * @param {string} target
33924 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33926 get: function(target) {
33927 if (typeof(this.groups[target]) == 'undefined') {
33931 return this.groups[target] ;
33940 * page DateSplitField.
33946 * @class Roo.bootstrap.DateSplitField
33947 * @extends Roo.bootstrap.Component
33948 * Bootstrap DateSplitField class
33949 * @cfg {string} fieldLabel - the label associated
33950 * @cfg {Number} labelWidth set the width of label (0-12)
33951 * @cfg {String} labelAlign (top|left)
33952 * @cfg {Boolean} dayAllowBlank (true|false) default false
33953 * @cfg {Boolean} monthAllowBlank (true|false) default false
33954 * @cfg {Boolean} yearAllowBlank (true|false) default false
33955 * @cfg {string} dayPlaceholder
33956 * @cfg {string} monthPlaceholder
33957 * @cfg {string} yearPlaceholder
33958 * @cfg {string} dayFormat default 'd'
33959 * @cfg {string} monthFormat default 'm'
33960 * @cfg {string} yearFormat default 'Y'
33961 * @cfg {Number} labellg set the width of label (1-12)
33962 * @cfg {Number} labelmd set the width of label (1-12)
33963 * @cfg {Number} labelsm set the width of label (1-12)
33964 * @cfg {Number} labelxs set the width of label (1-12)
33968 * Create a new DateSplitField
33969 * @param {Object} config The config object
33972 Roo.bootstrap.DateSplitField = function(config){
33973 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33979 * getting the data of years
33980 * @param {Roo.bootstrap.DateSplitField} this
33981 * @param {Object} years
33986 * getting the data of days
33987 * @param {Roo.bootstrap.DateSplitField} this
33988 * @param {Object} days
33993 * Fires after the field has been marked as invalid.
33994 * @param {Roo.form.Field} this
33995 * @param {String} msg The validation message
34000 * Fires after the field has been validated with no errors.
34001 * @param {Roo.form.Field} this
34007 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
34010 labelAlign : 'top',
34012 dayAllowBlank : false,
34013 monthAllowBlank : false,
34014 yearAllowBlank : false,
34015 dayPlaceholder : '',
34016 monthPlaceholder : '',
34017 yearPlaceholder : '',
34021 isFormField : true,
34027 getAutoCreate : function()
34031 cls : 'row roo-date-split-field-group',
34036 cls : 'form-hidden-field roo-date-split-field-group-value',
34042 var labelCls = 'col-md-12';
34043 var contentCls = 'col-md-4';
34045 if(this.fieldLabel){
34049 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34053 html : this.fieldLabel
34058 if(this.labelAlign == 'left'){
34060 if(this.labelWidth > 12){
34061 label.style = "width: " + this.labelWidth + 'px';
34064 if(this.labelWidth < 13 && this.labelmd == 0){
34065 this.labelmd = this.labelWidth;
34068 if(this.labellg > 0){
34069 labelCls = ' col-lg-' + this.labellg;
34070 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34073 if(this.labelmd > 0){
34074 labelCls = ' col-md-' + this.labelmd;
34075 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34078 if(this.labelsm > 0){
34079 labelCls = ' col-sm-' + this.labelsm;
34080 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34083 if(this.labelxs > 0){
34084 labelCls = ' col-xs-' + this.labelxs;
34085 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34089 label.cls += ' ' + labelCls;
34091 cfg.cn.push(label);
34094 Roo.each(['day', 'month', 'year'], function(t){
34097 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34104 inputEl: function ()
34106 return this.el.select('.roo-date-split-field-group-value', true).first();
34109 onRender : function(ct, position)
34113 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34115 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34117 this.dayField = new Roo.bootstrap.ComboBox({
34118 allowBlank : this.dayAllowBlank,
34119 alwaysQuery : true,
34120 displayField : 'value',
34123 forceSelection : true,
34125 placeholder : this.dayPlaceholder,
34126 selectOnFocus : true,
34127 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34128 triggerAction : 'all',
34130 valueField : 'value',
34131 store : new Roo.data.SimpleStore({
34132 data : (function() {
34134 _this.fireEvent('days', _this, days);
34137 fields : [ 'value' ]
34140 select : function (_self, record, index)
34142 _this.setValue(_this.getValue());
34147 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34149 this.monthField = new Roo.bootstrap.MonthField({
34150 after : '<i class=\"fa fa-calendar\"></i>',
34151 allowBlank : this.monthAllowBlank,
34152 placeholder : this.monthPlaceholder,
34155 render : function (_self)
34157 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34158 e.preventDefault();
34162 select : function (_self, oldvalue, newvalue)
34164 _this.setValue(_this.getValue());
34169 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34171 this.yearField = new Roo.bootstrap.ComboBox({
34172 allowBlank : this.yearAllowBlank,
34173 alwaysQuery : true,
34174 displayField : 'value',
34177 forceSelection : true,
34179 placeholder : this.yearPlaceholder,
34180 selectOnFocus : true,
34181 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34182 triggerAction : 'all',
34184 valueField : 'value',
34185 store : new Roo.data.SimpleStore({
34186 data : (function() {
34188 _this.fireEvent('years', _this, years);
34191 fields : [ 'value' ]
34194 select : function (_self, record, index)
34196 _this.setValue(_this.getValue());
34201 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34204 setValue : function(v, format)
34206 this.inputEl.dom.value = v;
34208 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34210 var d = Date.parseDate(v, f);
34217 this.setDay(d.format(this.dayFormat));
34218 this.setMonth(d.format(this.monthFormat));
34219 this.setYear(d.format(this.yearFormat));
34226 setDay : function(v)
34228 this.dayField.setValue(v);
34229 this.inputEl.dom.value = this.getValue();
34234 setMonth : function(v)
34236 this.monthField.setValue(v, true);
34237 this.inputEl.dom.value = this.getValue();
34242 setYear : function(v)
34244 this.yearField.setValue(v);
34245 this.inputEl.dom.value = this.getValue();
34250 getDay : function()
34252 return this.dayField.getValue();
34255 getMonth : function()
34257 return this.monthField.getValue();
34260 getYear : function()
34262 return this.yearField.getValue();
34265 getValue : function()
34267 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34269 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34279 this.inputEl.dom.value = '';
34284 validate : function()
34286 var d = this.dayField.validate();
34287 var m = this.monthField.validate();
34288 var y = this.yearField.validate();
34293 (!this.dayAllowBlank && !d) ||
34294 (!this.monthAllowBlank && !m) ||
34295 (!this.yearAllowBlank && !y)
34300 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34309 this.markInvalid();
34314 markValid : function()
34317 var label = this.el.select('label', true).first();
34318 var icon = this.el.select('i.fa-star', true).first();
34324 this.fireEvent('valid', this);
34328 * Mark this field as invalid
34329 * @param {String} msg The validation message
34331 markInvalid : function(msg)
34334 var label = this.el.select('label', true).first();
34335 var icon = this.el.select('i.fa-star', true).first();
34337 if(label && !icon){
34338 this.el.select('.roo-date-split-field-label', true).createChild({
34340 cls : 'text-danger fa fa-lg fa-star',
34341 tooltip : 'This field is required',
34342 style : 'margin-right:5px;'
34346 this.fireEvent('invalid', this, msg);
34349 clearInvalid : function()
34351 var label = this.el.select('label', true).first();
34352 var icon = this.el.select('i.fa-star', true).first();
34358 this.fireEvent('valid', this);
34361 getName: function()
34371 * http://masonry.desandro.com
34373 * The idea is to render all the bricks based on vertical width...
34375 * The original code extends 'outlayer' - we might need to use that....
34381 * @class Roo.bootstrap.LayoutMasonry
34382 * @extends Roo.bootstrap.Component
34383 * Bootstrap Layout Masonry class
34386 * Create a new Element
34387 * @param {Object} config The config object
34390 Roo.bootstrap.LayoutMasonry = function(config){
34392 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34396 Roo.bootstrap.LayoutMasonry.register(this);
34402 * Fire after layout the items
34403 * @param {Roo.bootstrap.LayoutMasonry} this
34404 * @param {Roo.EventObject} e
34411 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34414 * @cfg {Boolean} isLayoutInstant = no animation?
34416 isLayoutInstant : false, // needed?
34419 * @cfg {Number} boxWidth width of the columns
34424 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34429 * @cfg {Number} padWidth padding below box..
34434 * @cfg {Number} gutter gutter width..
34439 * @cfg {Number} maxCols maximum number of columns
34445 * @cfg {Boolean} isAutoInitial defalut true
34447 isAutoInitial : true,
34452 * @cfg {Boolean} isHorizontal defalut false
34454 isHorizontal : false,
34456 currentSize : null,
34462 bricks: null, //CompositeElement
34466 _isLayoutInited : false,
34468 // isAlternative : false, // only use for vertical layout...
34471 * @cfg {Number} alternativePadWidth padding below box..
34473 alternativePadWidth : 50,
34475 selectedBrick : [],
34477 getAutoCreate : function(){
34479 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34483 cls: 'blog-masonary-wrapper ' + this.cls,
34485 cls : 'mas-boxes masonary'
34492 getChildContainer: function( )
34494 if (this.boxesEl) {
34495 return this.boxesEl;
34498 this.boxesEl = this.el.select('.mas-boxes').first();
34500 return this.boxesEl;
34504 initEvents : function()
34508 if(this.isAutoInitial){
34509 Roo.log('hook children rendered');
34510 this.on('childrenrendered', function() {
34511 Roo.log('children rendered');
34517 initial : function()
34519 this.selectedBrick = [];
34521 this.currentSize = this.el.getBox(true);
34523 Roo.EventManager.onWindowResize(this.resize, this);
34525 if(!this.isAutoInitial){
34533 //this.layout.defer(500,this);
34537 resize : function()
34539 var cs = this.el.getBox(true);
34542 this.currentSize.width == cs.width &&
34543 this.currentSize.x == cs.x &&
34544 this.currentSize.height == cs.height &&
34545 this.currentSize.y == cs.y
34547 Roo.log("no change in with or X or Y");
34551 this.currentSize = cs;
34557 layout : function()
34559 this._resetLayout();
34561 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34563 this.layoutItems( isInstant );
34565 this._isLayoutInited = true;
34567 this.fireEvent('layout', this);
34571 _resetLayout : function()
34573 if(this.isHorizontal){
34574 this.horizontalMeasureColumns();
34578 this.verticalMeasureColumns();
34582 verticalMeasureColumns : function()
34584 this.getContainerWidth();
34586 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34587 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34591 var boxWidth = this.boxWidth + this.padWidth;
34593 if(this.containerWidth < this.boxWidth){
34594 boxWidth = this.containerWidth
34597 var containerWidth = this.containerWidth;
34599 var cols = Math.floor(containerWidth / boxWidth);
34601 this.cols = Math.max( cols, 1 );
34603 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34605 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34607 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34609 this.colWidth = boxWidth + avail - this.padWidth;
34611 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34612 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34615 horizontalMeasureColumns : function()
34617 this.getContainerWidth();
34619 var boxWidth = this.boxWidth;
34621 if(this.containerWidth < boxWidth){
34622 boxWidth = this.containerWidth;
34625 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34627 this.el.setHeight(boxWidth);
34631 getContainerWidth : function()
34633 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34636 layoutItems : function( isInstant )
34638 Roo.log(this.bricks);
34640 var items = Roo.apply([], this.bricks);
34642 if(this.isHorizontal){
34643 this._horizontalLayoutItems( items , isInstant );
34647 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34648 // this._verticalAlternativeLayoutItems( items , isInstant );
34652 this._verticalLayoutItems( items , isInstant );
34656 _verticalLayoutItems : function ( items , isInstant)
34658 if ( !items || !items.length ) {
34663 ['xs', 'xs', 'xs', 'tall'],
34664 ['xs', 'xs', 'tall'],
34665 ['xs', 'xs', 'sm'],
34666 ['xs', 'xs', 'xs'],
34672 ['sm', 'xs', 'xs'],
34676 ['tall', 'xs', 'xs', 'xs'],
34677 ['tall', 'xs', 'xs'],
34689 Roo.each(items, function(item, k){
34691 switch (item.size) {
34692 // these layouts take up a full box,
34703 boxes.push([item]);
34726 var filterPattern = function(box, length)
34734 var pattern = box.slice(0, length);
34738 Roo.each(pattern, function(i){
34739 format.push(i.size);
34742 Roo.each(standard, function(s){
34744 if(String(s) != String(format)){
34753 if(!match && length == 1){
34758 filterPattern(box, length - 1);
34762 queue.push(pattern);
34764 box = box.slice(length, box.length);
34766 filterPattern(box, 4);
34772 Roo.each(boxes, function(box, k){
34778 if(box.length == 1){
34783 filterPattern(box, 4);
34787 this._processVerticalLayoutQueue( queue, isInstant );
34791 // _verticalAlternativeLayoutItems : function( items , isInstant )
34793 // if ( !items || !items.length ) {
34797 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34801 _horizontalLayoutItems : function ( items , isInstant)
34803 if ( !items || !items.length || items.length < 3) {
34809 var eItems = items.slice(0, 3);
34811 items = items.slice(3, items.length);
34814 ['xs', 'xs', 'xs', 'wide'],
34815 ['xs', 'xs', 'wide'],
34816 ['xs', 'xs', 'sm'],
34817 ['xs', 'xs', 'xs'],
34823 ['sm', 'xs', 'xs'],
34827 ['wide', 'xs', 'xs', 'xs'],
34828 ['wide', 'xs', 'xs'],
34841 Roo.each(items, function(item, k){
34843 switch (item.size) {
34854 boxes.push([item]);
34878 var filterPattern = function(box, length)
34886 var pattern = box.slice(0, length);
34890 Roo.each(pattern, function(i){
34891 format.push(i.size);
34894 Roo.each(standard, function(s){
34896 if(String(s) != String(format)){
34905 if(!match && length == 1){
34910 filterPattern(box, length - 1);
34914 queue.push(pattern);
34916 box = box.slice(length, box.length);
34918 filterPattern(box, 4);
34924 Roo.each(boxes, function(box, k){
34930 if(box.length == 1){
34935 filterPattern(box, 4);
34942 var pos = this.el.getBox(true);
34946 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34948 var hit_end = false;
34950 Roo.each(queue, function(box){
34954 Roo.each(box, function(b){
34956 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34966 Roo.each(box, function(b){
34968 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34971 mx = Math.max(mx, b.x);
34975 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34979 Roo.each(box, function(b){
34981 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34995 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34998 /** Sets position of item in DOM
34999 * @param {Element} item
35000 * @param {Number} x - horizontal position
35001 * @param {Number} y - vertical position
35002 * @param {Boolean} isInstant - disables transitions
35004 _processVerticalLayoutQueue : function( queue, isInstant )
35006 var pos = this.el.getBox(true);
35011 for (var i = 0; i < this.cols; i++){
35015 Roo.each(queue, function(box, k){
35017 var col = k % this.cols;
35019 Roo.each(box, function(b,kk){
35021 b.el.position('absolute');
35023 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35024 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35026 if(b.size == 'md-left' || b.size == 'md-right'){
35027 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35028 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35031 b.el.setWidth(width);
35032 b.el.setHeight(height);
35034 b.el.select('iframe',true).setSize(width,height);
35038 for (var i = 0; i < this.cols; i++){
35040 if(maxY[i] < maxY[col]){
35045 col = Math.min(col, i);
35049 x = pos.x + col * (this.colWidth + this.padWidth);
35053 var positions = [];
35055 switch (box.length){
35057 positions = this.getVerticalOneBoxColPositions(x, y, box);
35060 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35063 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35066 positions = this.getVerticalFourBoxColPositions(x, y, box);
35072 Roo.each(box, function(b,kk){
35074 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35076 var sz = b.el.getSize();
35078 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35086 for (var i = 0; i < this.cols; i++){
35087 mY = Math.max(mY, maxY[i]);
35090 this.el.setHeight(mY - pos.y);
35094 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35096 // var pos = this.el.getBox(true);
35099 // var maxX = pos.right;
35101 // var maxHeight = 0;
35103 // Roo.each(items, function(item, k){
35107 // item.el.position('absolute');
35109 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35111 // item.el.setWidth(width);
35113 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35115 // item.el.setHeight(height);
35118 // item.el.setXY([x, y], isInstant ? false : true);
35120 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35123 // y = y + height + this.alternativePadWidth;
35125 // maxHeight = maxHeight + height + this.alternativePadWidth;
35129 // this.el.setHeight(maxHeight);
35133 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35135 var pos = this.el.getBox(true);
35140 var maxX = pos.right;
35142 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35144 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35146 Roo.each(queue, function(box, k){
35148 Roo.each(box, function(b, kk){
35150 b.el.position('absolute');
35152 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35153 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35155 if(b.size == 'md-left' || b.size == 'md-right'){
35156 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35157 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35160 b.el.setWidth(width);
35161 b.el.setHeight(height);
35169 var positions = [];
35171 switch (box.length){
35173 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35176 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35179 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35182 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35188 Roo.each(box, function(b,kk){
35190 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35192 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35200 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35202 Roo.each(eItems, function(b,k){
35204 b.size = (k == 0) ? 'sm' : 'xs';
35205 b.x = (k == 0) ? 2 : 1;
35206 b.y = (k == 0) ? 2 : 1;
35208 b.el.position('absolute');
35210 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35212 b.el.setWidth(width);
35214 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35216 b.el.setHeight(height);
35220 var positions = [];
35223 x : maxX - this.unitWidth * 2 - this.gutter,
35228 x : maxX - this.unitWidth,
35229 y : minY + (this.unitWidth + this.gutter) * 2
35233 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35237 Roo.each(eItems, function(b,k){
35239 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35245 getVerticalOneBoxColPositions : function(x, y, box)
35249 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35251 if(box[0].size == 'md-left'){
35255 if(box[0].size == 'md-right'){
35260 x : x + (this.unitWidth + this.gutter) * rand,
35267 getVerticalTwoBoxColPositions : function(x, y, box)
35271 if(box[0].size == 'xs'){
35275 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35279 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35293 x : x + (this.unitWidth + this.gutter) * 2,
35294 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35301 getVerticalThreeBoxColPositions : function(x, y, box)
35305 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35313 x : x + (this.unitWidth + this.gutter) * 1,
35318 x : x + (this.unitWidth + this.gutter) * 2,
35326 if(box[0].size == 'xs' && box[1].size == 'xs'){
35335 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35339 x : x + (this.unitWidth + this.gutter) * 1,
35353 x : x + (this.unitWidth + this.gutter) * 2,
35358 x : x + (this.unitWidth + this.gutter) * 2,
35359 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35366 getVerticalFourBoxColPositions : function(x, y, box)
35370 if(box[0].size == 'xs'){
35379 y : y + (this.unitHeight + this.gutter) * 1
35384 y : y + (this.unitHeight + this.gutter) * 2
35388 x : x + (this.unitWidth + this.gutter) * 1,
35402 x : x + (this.unitWidth + this.gutter) * 2,
35407 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35408 y : y + (this.unitHeight + this.gutter) * 1
35412 x : x + (this.unitWidth + this.gutter) * 2,
35413 y : y + (this.unitWidth + this.gutter) * 2
35420 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35424 if(box[0].size == 'md-left'){
35426 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35433 if(box[0].size == 'md-right'){
35435 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35436 y : minY + (this.unitWidth + this.gutter) * 1
35442 var rand = Math.floor(Math.random() * (4 - box[0].y));
35445 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35446 y : minY + (this.unitWidth + this.gutter) * rand
35453 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35457 if(box[0].size == 'xs'){
35460 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35465 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35466 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35474 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35479 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35480 y : minY + (this.unitWidth + this.gutter) * 2
35487 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35491 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35494 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35499 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35500 y : minY + (this.unitWidth + this.gutter) * 1
35504 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35505 y : minY + (this.unitWidth + this.gutter) * 2
35512 if(box[0].size == 'xs' && box[1].size == 'xs'){
35515 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35520 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35525 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35526 y : minY + (this.unitWidth + this.gutter) * 1
35534 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35539 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35540 y : minY + (this.unitWidth + this.gutter) * 2
35544 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35545 y : minY + (this.unitWidth + this.gutter) * 2
35552 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35556 if(box[0].size == 'xs'){
35559 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35564 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35569 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),
35574 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35575 y : minY + (this.unitWidth + this.gutter) * 1
35583 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35588 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35589 y : minY + (this.unitWidth + this.gutter) * 2
35593 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35594 y : minY + (this.unitWidth + this.gutter) * 2
35598 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),
35599 y : minY + (this.unitWidth + this.gutter) * 2
35607 * remove a Masonry Brick
35608 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35610 removeBrick : function(brick_id)
35616 for (var i = 0; i<this.bricks.length; i++) {
35617 if (this.bricks[i].id == brick_id) {
35618 this.bricks.splice(i,1);
35619 this.el.dom.removeChild(Roo.get(brick_id).dom);
35626 * adds a Masonry Brick
35627 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35629 addBrick : function(cfg)
35631 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35632 //this.register(cn);
35633 cn.parentId = this.id;
35634 cn.render(this.el);
35639 * register a Masonry Brick
35640 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35643 register : function(brick)
35645 this.bricks.push(brick);
35646 brick.masonryId = this.id;
35650 * clear all the Masonry Brick
35652 clearAll : function()
35655 //this.getChildContainer().dom.innerHTML = "";
35656 this.el.dom.innerHTML = '';
35659 getSelected : function()
35661 if (!this.selectedBrick) {
35665 return this.selectedBrick;
35669 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35673 * register a Masonry Layout
35674 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35677 register : function(layout)
35679 this.groups[layout.id] = layout;
35682 * fetch a Masonry Layout based on the masonry layout ID
35683 * @param {string} the masonry layout to add
35684 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35687 get: function(layout_id) {
35688 if (typeof(this.groups[layout_id]) == 'undefined') {
35691 return this.groups[layout_id] ;
35703 * http://masonry.desandro.com
35705 * The idea is to render all the bricks based on vertical width...
35707 * The original code extends 'outlayer' - we might need to use that....
35713 * @class Roo.bootstrap.LayoutMasonryAuto
35714 * @extends Roo.bootstrap.Component
35715 * Bootstrap Layout Masonry class
35718 * Create a new Element
35719 * @param {Object} config The config object
35722 Roo.bootstrap.LayoutMasonryAuto = function(config){
35723 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35726 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35729 * @cfg {Boolean} isFitWidth - resize the width..
35731 isFitWidth : false, // options..
35733 * @cfg {Boolean} isOriginLeft = left align?
35735 isOriginLeft : true,
35737 * @cfg {Boolean} isOriginTop = top align?
35739 isOriginTop : false,
35741 * @cfg {Boolean} isLayoutInstant = no animation?
35743 isLayoutInstant : false, // needed?
35745 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35747 isResizingContainer : true,
35749 * @cfg {Number} columnWidth width of the columns
35755 * @cfg {Number} maxCols maximum number of columns
35760 * @cfg {Number} padHeight padding below box..
35766 * @cfg {Boolean} isAutoInitial defalut true
35769 isAutoInitial : true,
35775 initialColumnWidth : 0,
35776 currentSize : null,
35778 colYs : null, // array.
35785 bricks: null, //CompositeElement
35786 cols : 0, // array?
35787 // element : null, // wrapped now this.el
35788 _isLayoutInited : null,
35791 getAutoCreate : function(){
35795 cls: 'blog-masonary-wrapper ' + this.cls,
35797 cls : 'mas-boxes masonary'
35804 getChildContainer: function( )
35806 if (this.boxesEl) {
35807 return this.boxesEl;
35810 this.boxesEl = this.el.select('.mas-boxes').first();
35812 return this.boxesEl;
35816 initEvents : function()
35820 if(this.isAutoInitial){
35821 Roo.log('hook children rendered');
35822 this.on('childrenrendered', function() {
35823 Roo.log('children rendered');
35830 initial : function()
35832 this.reloadItems();
35834 this.currentSize = this.el.getBox(true);
35836 /// was window resize... - let's see if this works..
35837 Roo.EventManager.onWindowResize(this.resize, this);
35839 if(!this.isAutoInitial){
35844 this.layout.defer(500,this);
35847 reloadItems: function()
35849 this.bricks = this.el.select('.masonry-brick', true);
35851 this.bricks.each(function(b) {
35852 //Roo.log(b.getSize());
35853 if (!b.attr('originalwidth')) {
35854 b.attr('originalwidth', b.getSize().width);
35859 Roo.log(this.bricks.elements.length);
35862 resize : function()
35865 var cs = this.el.getBox(true);
35867 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35868 Roo.log("no change in with or X");
35871 this.currentSize = cs;
35875 layout : function()
35878 this._resetLayout();
35879 //this._manageStamps();
35881 // don't animate first layout
35882 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35883 this.layoutItems( isInstant );
35885 // flag for initalized
35886 this._isLayoutInited = true;
35889 layoutItems : function( isInstant )
35891 //var items = this._getItemsForLayout( this.items );
35892 // original code supports filtering layout items.. we just ignore it..
35894 this._layoutItems( this.bricks , isInstant );
35896 this._postLayout();
35898 _layoutItems : function ( items , isInstant)
35900 //this.fireEvent( 'layout', this, items );
35903 if ( !items || !items.elements.length ) {
35904 // no items, emit event with empty array
35909 items.each(function(item) {
35910 Roo.log("layout item");
35912 // get x/y object from method
35913 var position = this._getItemLayoutPosition( item );
35915 position.item = item;
35916 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35917 queue.push( position );
35920 this._processLayoutQueue( queue );
35922 /** Sets position of item in DOM
35923 * @param {Element} item
35924 * @param {Number} x - horizontal position
35925 * @param {Number} y - vertical position
35926 * @param {Boolean} isInstant - disables transitions
35928 _processLayoutQueue : function( queue )
35930 for ( var i=0, len = queue.length; i < len; i++ ) {
35931 var obj = queue[i];
35932 obj.item.position('absolute');
35933 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35939 * Any logic you want to do after each layout,
35940 * i.e. size the container
35942 _postLayout : function()
35944 this.resizeContainer();
35947 resizeContainer : function()
35949 if ( !this.isResizingContainer ) {
35952 var size = this._getContainerSize();
35954 this.el.setSize(size.width,size.height);
35955 this.boxesEl.setSize(size.width,size.height);
35961 _resetLayout : function()
35963 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35964 this.colWidth = this.el.getWidth();
35965 //this.gutter = this.el.getWidth();
35967 this.measureColumns();
35973 this.colYs.push( 0 );
35979 measureColumns : function()
35981 this.getContainerWidth();
35982 // if columnWidth is 0, default to outerWidth of first item
35983 if ( !this.columnWidth ) {
35984 var firstItem = this.bricks.first();
35985 Roo.log(firstItem);
35986 this.columnWidth = this.containerWidth;
35987 if (firstItem && firstItem.attr('originalwidth') ) {
35988 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35990 // columnWidth fall back to item of first element
35991 Roo.log("set column width?");
35992 this.initialColumnWidth = this.columnWidth ;
35994 // if first elem has no width, default to size of container
35999 if (this.initialColumnWidth) {
36000 this.columnWidth = this.initialColumnWidth;
36005 // column width is fixed at the top - however if container width get's smaller we should
36008 // this bit calcs how man columns..
36010 var columnWidth = this.columnWidth += this.gutter;
36012 // calculate columns
36013 var containerWidth = this.containerWidth + this.gutter;
36015 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36016 // fix rounding errors, typically with gutters
36017 var excess = columnWidth - containerWidth % columnWidth;
36020 // if overshoot is less than a pixel, round up, otherwise floor it
36021 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36022 cols = Math[ mathMethod ]( cols );
36023 this.cols = Math.max( cols, 1 );
36024 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36026 // padding positioning..
36027 var totalColWidth = this.cols * this.columnWidth;
36028 var padavail = this.containerWidth - totalColWidth;
36029 // so for 2 columns - we need 3 'pads'
36031 var padNeeded = (1+this.cols) * this.padWidth;
36033 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36035 this.columnWidth += padExtra
36036 //this.padWidth = Math.floor(padavail / ( this.cols));
36038 // adjust colum width so that padding is fixed??
36040 // we have 3 columns ... total = width * 3
36041 // we have X left over... that should be used by
36043 //if (this.expandC) {
36051 getContainerWidth : function()
36053 /* // container is parent if fit width
36054 var container = this.isFitWidth ? this.element.parentNode : this.element;
36055 // check that this.size and size are there
36056 // IE8 triggers resize on body size change, so they might not be
36058 var size = getSize( container ); //FIXME
36059 this.containerWidth = size && size.innerWidth; //FIXME
36062 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36066 _getItemLayoutPosition : function( item ) // what is item?
36068 // we resize the item to our columnWidth..
36070 item.setWidth(this.columnWidth);
36071 item.autoBoxAdjust = false;
36073 var sz = item.getSize();
36075 // how many columns does this brick span
36076 var remainder = this.containerWidth % this.columnWidth;
36078 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36079 // round if off by 1 pixel, otherwise use ceil
36080 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36081 colSpan = Math.min( colSpan, this.cols );
36083 // normally this should be '1' as we dont' currently allow multi width columns..
36085 var colGroup = this._getColGroup( colSpan );
36086 // get the minimum Y value from the columns
36087 var minimumY = Math.min.apply( Math, colGroup );
36088 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36090 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36092 // position the brick
36094 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36095 y: this.currentSize.y + minimumY + this.padHeight
36099 // apply setHeight to necessary columns
36100 var setHeight = minimumY + sz.height + this.padHeight;
36101 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36103 var setSpan = this.cols + 1 - colGroup.length;
36104 for ( var i = 0; i < setSpan; i++ ) {
36105 this.colYs[ shortColIndex + i ] = setHeight ;
36112 * @param {Number} colSpan - number of columns the element spans
36113 * @returns {Array} colGroup
36115 _getColGroup : function( colSpan )
36117 if ( colSpan < 2 ) {
36118 // if brick spans only one column, use all the column Ys
36123 // how many different places could this brick fit horizontally
36124 var groupCount = this.cols + 1 - colSpan;
36125 // for each group potential horizontal position
36126 for ( var i = 0; i < groupCount; i++ ) {
36127 // make an array of colY values for that one group
36128 var groupColYs = this.colYs.slice( i, i + colSpan );
36129 // and get the max value of the array
36130 colGroup[i] = Math.max.apply( Math, groupColYs );
36135 _manageStamp : function( stamp )
36137 var stampSize = stamp.getSize();
36138 var offset = stamp.getBox();
36139 // get the columns that this stamp affects
36140 var firstX = this.isOriginLeft ? offset.x : offset.right;
36141 var lastX = firstX + stampSize.width;
36142 var firstCol = Math.floor( firstX / this.columnWidth );
36143 firstCol = Math.max( 0, firstCol );
36145 var lastCol = Math.floor( lastX / this.columnWidth );
36146 // lastCol should not go over if multiple of columnWidth #425
36147 lastCol -= lastX % this.columnWidth ? 0 : 1;
36148 lastCol = Math.min( this.cols - 1, lastCol );
36150 // set colYs to bottom of the stamp
36151 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36154 for ( var i = firstCol; i <= lastCol; i++ ) {
36155 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36160 _getContainerSize : function()
36162 this.maxY = Math.max.apply( Math, this.colYs );
36167 if ( this.isFitWidth ) {
36168 size.width = this._getContainerFitWidth();
36174 _getContainerFitWidth : function()
36176 var unusedCols = 0;
36177 // count unused columns
36180 if ( this.colYs[i] !== 0 ) {
36185 // fit container to columns that have been used
36186 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36189 needsResizeLayout : function()
36191 var previousWidth = this.containerWidth;
36192 this.getContainerWidth();
36193 return previousWidth !== this.containerWidth;
36208 * @class Roo.bootstrap.MasonryBrick
36209 * @extends Roo.bootstrap.Component
36210 * Bootstrap MasonryBrick class
36213 * Create a new MasonryBrick
36214 * @param {Object} config The config object
36217 Roo.bootstrap.MasonryBrick = function(config){
36219 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36221 Roo.bootstrap.MasonryBrick.register(this);
36227 * When a MasonryBrick is clcik
36228 * @param {Roo.bootstrap.MasonryBrick} this
36229 * @param {Roo.EventObject} e
36235 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36238 * @cfg {String} title
36242 * @cfg {String} html
36246 * @cfg {String} bgimage
36250 * @cfg {String} videourl
36254 * @cfg {String} cls
36258 * @cfg {String} href
36262 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36267 * @cfg {String} placetitle (center|bottom)
36272 * @cfg {Boolean} isFitContainer defalut true
36274 isFitContainer : true,
36277 * @cfg {Boolean} preventDefault defalut false
36279 preventDefault : false,
36282 * @cfg {Boolean} inverse defalut false
36284 maskInverse : false,
36286 getAutoCreate : function()
36288 if(!this.isFitContainer){
36289 return this.getSplitAutoCreate();
36292 var cls = 'masonry-brick masonry-brick-full';
36294 if(this.href.length){
36295 cls += ' masonry-brick-link';
36298 if(this.bgimage.length){
36299 cls += ' masonry-brick-image';
36302 if(this.maskInverse){
36303 cls += ' mask-inverse';
36306 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36307 cls += ' enable-mask';
36311 cls += ' masonry-' + this.size + '-brick';
36314 if(this.placetitle.length){
36316 switch (this.placetitle) {
36318 cls += ' masonry-center-title';
36321 cls += ' masonry-bottom-title';
36328 if(!this.html.length && !this.bgimage.length){
36329 cls += ' masonry-center-title';
36332 if(!this.html.length && this.bgimage.length){
36333 cls += ' masonry-bottom-title';
36338 cls += ' ' + this.cls;
36342 tag: (this.href.length) ? 'a' : 'div',
36347 cls: 'masonry-brick-mask'
36351 cls: 'masonry-brick-paragraph',
36357 if(this.href.length){
36358 cfg.href = this.href;
36361 var cn = cfg.cn[1].cn;
36363 if(this.title.length){
36366 cls: 'masonry-brick-title',
36371 if(this.html.length){
36374 cls: 'masonry-brick-text',
36379 if (!this.title.length && !this.html.length) {
36380 cfg.cn[1].cls += ' hide';
36383 if(this.bgimage.length){
36386 cls: 'masonry-brick-image-view',
36391 if(this.videourl.length){
36392 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36393 // youtube support only?
36396 cls: 'masonry-brick-image-view',
36399 allowfullscreen : true
36407 getSplitAutoCreate : function()
36409 var cls = 'masonry-brick masonry-brick-split';
36411 if(this.href.length){
36412 cls += ' masonry-brick-link';
36415 if(this.bgimage.length){
36416 cls += ' masonry-brick-image';
36420 cls += ' masonry-' + this.size + '-brick';
36423 switch (this.placetitle) {
36425 cls += ' masonry-center-title';
36428 cls += ' masonry-bottom-title';
36431 if(!this.bgimage.length){
36432 cls += ' masonry-center-title';
36435 if(this.bgimage.length){
36436 cls += ' masonry-bottom-title';
36442 cls += ' ' + this.cls;
36446 tag: (this.href.length) ? 'a' : 'div',
36451 cls: 'masonry-brick-split-head',
36455 cls: 'masonry-brick-paragraph',
36462 cls: 'masonry-brick-split-body',
36468 if(this.href.length){
36469 cfg.href = this.href;
36472 if(this.title.length){
36473 cfg.cn[0].cn[0].cn.push({
36475 cls: 'masonry-brick-title',
36480 if(this.html.length){
36481 cfg.cn[1].cn.push({
36483 cls: 'masonry-brick-text',
36488 if(this.bgimage.length){
36489 cfg.cn[0].cn.push({
36491 cls: 'masonry-brick-image-view',
36496 if(this.videourl.length){
36497 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36498 // youtube support only?
36499 cfg.cn[0].cn.cn.push({
36501 cls: 'masonry-brick-image-view',
36504 allowfullscreen : true
36511 initEvents: function()
36513 switch (this.size) {
36546 this.el.on('touchstart', this.onTouchStart, this);
36547 this.el.on('touchmove', this.onTouchMove, this);
36548 this.el.on('touchend', this.onTouchEnd, this);
36549 this.el.on('contextmenu', this.onContextMenu, this);
36551 this.el.on('mouseenter' ,this.enter, this);
36552 this.el.on('mouseleave', this.leave, this);
36553 this.el.on('click', this.onClick, this);
36556 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36557 this.parent().bricks.push(this);
36562 onClick: function(e, el)
36564 var time = this.endTimer - this.startTimer;
36565 // Roo.log(e.preventDefault());
36568 e.preventDefault();
36573 if(!this.preventDefault){
36577 e.preventDefault();
36579 if (this.activeClass != '') {
36580 this.selectBrick();
36583 this.fireEvent('click', this, e);
36586 enter: function(e, el)
36588 e.preventDefault();
36590 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36594 if(this.bgimage.length && this.html.length){
36595 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36599 leave: function(e, el)
36601 e.preventDefault();
36603 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36607 if(this.bgimage.length && this.html.length){
36608 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36612 onTouchStart: function(e, el)
36614 // e.preventDefault();
36616 this.touchmoved = false;
36618 if(!this.isFitContainer){
36622 if(!this.bgimage.length || !this.html.length){
36626 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36628 this.timer = new Date().getTime();
36632 onTouchMove: function(e, el)
36634 this.touchmoved = true;
36637 onContextMenu : function(e,el)
36639 e.preventDefault();
36640 e.stopPropagation();
36644 onTouchEnd: function(e, el)
36646 // e.preventDefault();
36648 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36655 if(!this.bgimage.length || !this.html.length){
36657 if(this.href.length){
36658 window.location.href = this.href;
36664 if(!this.isFitContainer){
36668 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36670 window.location.href = this.href;
36673 //selection on single brick only
36674 selectBrick : function() {
36676 if (!this.parentId) {
36680 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36681 var index = m.selectedBrick.indexOf(this.id);
36684 m.selectedBrick.splice(index,1);
36685 this.el.removeClass(this.activeClass);
36689 for(var i = 0; i < m.selectedBrick.length; i++) {
36690 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36691 b.el.removeClass(b.activeClass);
36694 m.selectedBrick = [];
36696 m.selectedBrick.push(this.id);
36697 this.el.addClass(this.activeClass);
36701 isSelected : function(){
36702 return this.el.hasClass(this.activeClass);
36707 Roo.apply(Roo.bootstrap.MasonryBrick, {
36710 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36712 * register a Masonry Brick
36713 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36716 register : function(brick)
36718 //this.groups[brick.id] = brick;
36719 this.groups.add(brick.id, brick);
36722 * fetch a masonry brick based on the masonry brick ID
36723 * @param {string} the masonry brick to add
36724 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36727 get: function(brick_id)
36729 // if (typeof(this.groups[brick_id]) == 'undefined') {
36732 // return this.groups[brick_id] ;
36734 if(this.groups.key(brick_id)) {
36735 return this.groups.key(brick_id);
36753 * @class Roo.bootstrap.Brick
36754 * @extends Roo.bootstrap.Component
36755 * Bootstrap Brick class
36758 * Create a new Brick
36759 * @param {Object} config The config object
36762 Roo.bootstrap.Brick = function(config){
36763 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36769 * When a Brick is click
36770 * @param {Roo.bootstrap.Brick} this
36771 * @param {Roo.EventObject} e
36777 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36780 * @cfg {String} title
36784 * @cfg {String} html
36788 * @cfg {String} bgimage
36792 * @cfg {String} cls
36796 * @cfg {String} href
36800 * @cfg {String} video
36804 * @cfg {Boolean} square
36808 getAutoCreate : function()
36810 var cls = 'roo-brick';
36812 if(this.href.length){
36813 cls += ' roo-brick-link';
36816 if(this.bgimage.length){
36817 cls += ' roo-brick-image';
36820 if(!this.html.length && !this.bgimage.length){
36821 cls += ' roo-brick-center-title';
36824 if(!this.html.length && this.bgimage.length){
36825 cls += ' roo-brick-bottom-title';
36829 cls += ' ' + this.cls;
36833 tag: (this.href.length) ? 'a' : 'div',
36838 cls: 'roo-brick-paragraph',
36844 if(this.href.length){
36845 cfg.href = this.href;
36848 var cn = cfg.cn[0].cn;
36850 if(this.title.length){
36853 cls: 'roo-brick-title',
36858 if(this.html.length){
36861 cls: 'roo-brick-text',
36868 if(this.bgimage.length){
36871 cls: 'roo-brick-image-view',
36879 initEvents: function()
36881 if(this.title.length || this.html.length){
36882 this.el.on('mouseenter' ,this.enter, this);
36883 this.el.on('mouseleave', this.leave, this);
36886 Roo.EventManager.onWindowResize(this.resize, this);
36888 if(this.bgimage.length){
36889 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36890 this.imageEl.on('load', this.onImageLoad, this);
36897 onImageLoad : function()
36902 resize : function()
36904 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36906 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36908 if(this.bgimage.length){
36909 var image = this.el.select('.roo-brick-image-view', true).first();
36911 image.setWidth(paragraph.getWidth());
36914 image.setHeight(paragraph.getWidth());
36917 this.el.setHeight(image.getHeight());
36918 paragraph.setHeight(image.getHeight());
36924 enter: function(e, el)
36926 e.preventDefault();
36928 if(this.bgimage.length){
36929 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36930 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36934 leave: function(e, el)
36936 e.preventDefault();
36938 if(this.bgimage.length){
36939 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36940 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36955 * @class Roo.bootstrap.NumberField
36956 * @extends Roo.bootstrap.Input
36957 * Bootstrap NumberField class
36963 * Create a new NumberField
36964 * @param {Object} config The config object
36967 Roo.bootstrap.NumberField = function(config){
36968 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36971 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36974 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36976 allowDecimals : true,
36978 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36980 decimalSeparator : ".",
36982 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36984 decimalPrecision : 2,
36986 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36988 allowNegative : true,
36991 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36995 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36997 minValue : Number.NEGATIVE_INFINITY,
36999 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37001 maxValue : Number.MAX_VALUE,
37003 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37005 minText : "The minimum value for this field is {0}",
37007 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37009 maxText : "The maximum value for this field is {0}",
37011 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
37012 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37014 nanText : "{0} is not a valid number",
37016 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37018 thousandsDelimiter : false,
37020 * @cfg {String} valueAlign alignment of value
37022 valueAlign : "left",
37024 getAutoCreate : function()
37026 var hiddenInput = {
37030 cls: 'hidden-number-input'
37034 hiddenInput.name = this.name;
37039 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37041 this.name = hiddenInput.name;
37043 if(cfg.cn.length > 0) {
37044 cfg.cn.push(hiddenInput);
37051 initEvents : function()
37053 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37055 var allowed = "0123456789";
37057 if(this.allowDecimals){
37058 allowed += this.decimalSeparator;
37061 if(this.allowNegative){
37065 if(this.thousandsDelimiter) {
37069 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37071 var keyPress = function(e){
37073 var k = e.getKey();
37075 var c = e.getCharCode();
37078 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37079 allowed.indexOf(String.fromCharCode(c)) === -1
37085 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37089 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37094 this.el.on("keypress", keyPress, this);
37097 validateValue : function(value)
37100 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37104 var num = this.parseValue(value);
37107 this.markInvalid(String.format(this.nanText, value));
37111 if(num < this.minValue){
37112 this.markInvalid(String.format(this.minText, this.minValue));
37116 if(num > this.maxValue){
37117 this.markInvalid(String.format(this.maxText, this.maxValue));
37124 getValue : function()
37126 var v = this.hiddenEl().getValue();
37128 return this.fixPrecision(this.parseValue(v));
37131 parseValue : function(value)
37133 if(this.thousandsDelimiter) {
37135 r = new RegExp(",", "g");
37136 value = value.replace(r, "");
37139 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37140 return isNaN(value) ? '' : value;
37143 fixPrecision : function(value)
37145 if(this.thousandsDelimiter) {
37147 r = new RegExp(",", "g");
37148 value = value.replace(r, "");
37151 var nan = isNaN(value);
37153 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37154 return nan ? '' : value;
37156 return parseFloat(value).toFixed(this.decimalPrecision);
37159 setValue : function(v)
37161 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37167 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37169 this.inputEl().dom.value = (v == '') ? '' :
37170 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37172 if(!this.allowZero && v === '0') {
37173 this.hiddenEl().dom.value = '';
37174 this.inputEl().dom.value = '';
37181 decimalPrecisionFcn : function(v)
37183 return Math.floor(v);
37186 beforeBlur : function()
37188 var v = this.parseValue(this.getRawValue());
37190 if(v || v === 0 || v === ''){
37195 hiddenEl : function()
37197 return this.el.select('input.hidden-number-input',true).first();
37209 * @class Roo.bootstrap.DocumentSlider
37210 * @extends Roo.bootstrap.Component
37211 * Bootstrap DocumentSlider class
37214 * Create a new DocumentViewer
37215 * @param {Object} config The config object
37218 Roo.bootstrap.DocumentSlider = function(config){
37219 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37226 * Fire after initEvent
37227 * @param {Roo.bootstrap.DocumentSlider} this
37232 * Fire after update
37233 * @param {Roo.bootstrap.DocumentSlider} this
37239 * @param {Roo.bootstrap.DocumentSlider} this
37245 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37251 getAutoCreate : function()
37255 cls : 'roo-document-slider',
37259 cls : 'roo-document-slider-header',
37263 cls : 'roo-document-slider-header-title'
37269 cls : 'roo-document-slider-body',
37273 cls : 'roo-document-slider-prev',
37277 cls : 'fa fa-chevron-left'
37283 cls : 'roo-document-slider-thumb',
37287 cls : 'roo-document-slider-image'
37293 cls : 'roo-document-slider-next',
37297 cls : 'fa fa-chevron-right'
37309 initEvents : function()
37311 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37312 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37314 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37315 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37317 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37318 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37320 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37321 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37323 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37324 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37326 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37327 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37329 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37330 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37332 this.thumbEl.on('click', this.onClick, this);
37334 this.prevIndicator.on('click', this.prev, this);
37336 this.nextIndicator.on('click', this.next, this);
37340 initial : function()
37342 if(this.files.length){
37343 this.indicator = 1;
37347 this.fireEvent('initial', this);
37350 update : function()
37352 this.imageEl.attr('src', this.files[this.indicator - 1]);
37354 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37356 this.prevIndicator.show();
37358 if(this.indicator == 1){
37359 this.prevIndicator.hide();
37362 this.nextIndicator.show();
37364 if(this.indicator == this.files.length){
37365 this.nextIndicator.hide();
37368 this.thumbEl.scrollTo('top');
37370 this.fireEvent('update', this);
37373 onClick : function(e)
37375 e.preventDefault();
37377 this.fireEvent('click', this);
37382 e.preventDefault();
37384 this.indicator = Math.max(1, this.indicator - 1);
37391 e.preventDefault();
37393 this.indicator = Math.min(this.files.length, this.indicator + 1);
37407 * @class Roo.bootstrap.RadioSet
37408 * @extends Roo.bootstrap.Input
37409 * Bootstrap RadioSet class
37410 * @cfg {String} indicatorpos (left|right) default left
37411 * @cfg {Boolean} inline (true|false) inline the element (default true)
37412 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37414 * Create a new RadioSet
37415 * @param {Object} config The config object
37418 Roo.bootstrap.RadioSet = function(config){
37420 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37424 Roo.bootstrap.RadioSet.register(this);
37429 * Fires when the element is checked or unchecked.
37430 * @param {Roo.bootstrap.RadioSet} this This radio
37431 * @param {Roo.bootstrap.Radio} item The checked item
37436 * Fires when the element is click.
37437 * @param {Roo.bootstrap.RadioSet} this This radio set
37438 * @param {Roo.bootstrap.Radio} item The checked item
37439 * @param {Roo.EventObject} e The event object
37446 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37454 indicatorpos : 'left',
37456 getAutoCreate : function()
37460 cls : 'roo-radio-set-label',
37464 html : this.fieldLabel
37468 if (Roo.bootstrap.version == 3) {
37471 if(this.indicatorpos == 'left'){
37474 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37475 tooltip : 'This field is required'
37480 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37481 tooltip : 'This field is required'
37487 cls : 'roo-radio-set-items'
37490 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37492 if (align === 'left' && this.fieldLabel.length) {
37495 cls : "roo-radio-set-right",
37501 if(this.labelWidth > 12){
37502 label.style = "width: " + this.labelWidth + 'px';
37505 if(this.labelWidth < 13 && this.labelmd == 0){
37506 this.labelmd = this.labelWidth;
37509 if(this.labellg > 0){
37510 label.cls += ' col-lg-' + this.labellg;
37511 items.cls += ' col-lg-' + (12 - this.labellg);
37514 if(this.labelmd > 0){
37515 label.cls += ' col-md-' + this.labelmd;
37516 items.cls += ' col-md-' + (12 - this.labelmd);
37519 if(this.labelsm > 0){
37520 label.cls += ' col-sm-' + this.labelsm;
37521 items.cls += ' col-sm-' + (12 - this.labelsm);
37524 if(this.labelxs > 0){
37525 label.cls += ' col-xs-' + this.labelxs;
37526 items.cls += ' col-xs-' + (12 - this.labelxs);
37532 cls : 'roo-radio-set',
37536 cls : 'roo-radio-set-input',
37539 value : this.value ? this.value : ''
37546 if(this.weight.length){
37547 cfg.cls += ' roo-radio-' + this.weight;
37551 cfg.cls += ' roo-radio-set-inline';
37555 ['xs','sm','md','lg'].map(function(size){
37556 if (settings[size]) {
37557 cfg.cls += ' col-' + size + '-' + settings[size];
37565 initEvents : function()
37567 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37568 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37570 if(!this.fieldLabel.length){
37571 this.labelEl.hide();
37574 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37575 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37577 this.indicator = this.indicatorEl();
37579 if(this.indicator){
37580 this.indicator.addClass('invisible');
37583 this.originalValue = this.getValue();
37587 inputEl: function ()
37589 return this.el.select('.roo-radio-set-input', true).first();
37592 getChildContainer : function()
37594 return this.itemsEl;
37597 register : function(item)
37599 this.radioes.push(item);
37603 validate : function()
37605 if(this.getVisibilityEl().hasClass('hidden')){
37611 Roo.each(this.radioes, function(i){
37620 if(this.allowBlank) {
37624 if(this.disabled || valid){
37629 this.markInvalid();
37634 markValid : function()
37636 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37637 this.indicatorEl().removeClass('visible');
37638 this.indicatorEl().addClass('invisible');
37642 if (Roo.bootstrap.version == 3) {
37643 this.el.removeClass([this.invalidClass, this.validClass]);
37644 this.el.addClass(this.validClass);
37646 this.el.removeClass(['is-invalid','is-valid']);
37647 this.el.addClass(['is-valid']);
37649 this.fireEvent('valid', this);
37652 markInvalid : function(msg)
37654 if(this.allowBlank || this.disabled){
37658 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37659 this.indicatorEl().removeClass('invisible');
37660 this.indicatorEl().addClass('visible');
37662 if (Roo.bootstrap.version == 3) {
37663 this.el.removeClass([this.invalidClass, this.validClass]);
37664 this.el.addClass(this.invalidClass);
37666 this.el.removeClass(['is-invalid','is-valid']);
37667 this.el.addClass(['is-invalid']);
37670 this.fireEvent('invalid', this, msg);
37674 setValue : function(v, suppressEvent)
37676 if(this.value === v){
37683 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37686 Roo.each(this.radioes, function(i){
37688 i.el.removeClass('checked');
37691 Roo.each(this.radioes, function(i){
37693 if(i.value === v || i.value.toString() === v.toString()){
37695 i.el.addClass('checked');
37697 if(suppressEvent !== true){
37698 this.fireEvent('check', this, i);
37709 clearInvalid : function(){
37711 if(!this.el || this.preventMark){
37715 this.el.removeClass([this.invalidClass]);
37717 this.fireEvent('valid', this);
37722 Roo.apply(Roo.bootstrap.RadioSet, {
37726 register : function(set)
37728 this.groups[set.name] = set;
37731 get: function(name)
37733 if (typeof(this.groups[name]) == 'undefined') {
37737 return this.groups[name] ;
37743 * Ext JS Library 1.1.1
37744 * Copyright(c) 2006-2007, Ext JS, LLC.
37746 * Originally Released Under LGPL - original licence link has changed is not relivant.
37749 * <script type="text/javascript">
37754 * @class Roo.bootstrap.SplitBar
37755 * @extends Roo.util.Observable
37756 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37760 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37761 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37762 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37763 split.minSize = 100;
37764 split.maxSize = 600;
37765 split.animate = true;
37766 split.on('moved', splitterMoved);
37769 * Create a new SplitBar
37770 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37771 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37772 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37773 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37774 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37775 position of the SplitBar).
37777 Roo.bootstrap.SplitBar = function(cfg){
37782 // dragElement : elm
37783 // resizingElement: el,
37785 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37786 // placement : Roo.bootstrap.SplitBar.LEFT ,
37787 // existingProxy ???
37790 this.el = Roo.get(cfg.dragElement, true);
37791 this.el.dom.unselectable = "on";
37793 this.resizingEl = Roo.get(cfg.resizingElement, true);
37797 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37798 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37801 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37804 * The minimum size of the resizing element. (Defaults to 0)
37810 * The maximum size of the resizing element. (Defaults to 2000)
37813 this.maxSize = 2000;
37816 * Whether to animate the transition to the new size
37819 this.animate = false;
37822 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37825 this.useShim = false;
37830 if(!cfg.existingProxy){
37832 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37834 this.proxy = Roo.get(cfg.existingProxy).dom;
37837 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37840 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37843 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37846 this.dragSpecs = {};
37849 * @private The adapter to use to positon and resize elements
37851 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37852 this.adapter.init(this);
37854 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37856 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37857 this.el.addClass("roo-splitbar-h");
37860 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37861 this.el.addClass("roo-splitbar-v");
37867 * Fires when the splitter is moved (alias for {@link #event-moved})
37868 * @param {Roo.bootstrap.SplitBar} this
37869 * @param {Number} newSize the new width or height
37874 * Fires when the splitter is moved
37875 * @param {Roo.bootstrap.SplitBar} this
37876 * @param {Number} newSize the new width or height
37880 * @event beforeresize
37881 * Fires before the splitter is dragged
37882 * @param {Roo.bootstrap.SplitBar} this
37884 "beforeresize" : true,
37886 "beforeapply" : true
37889 Roo.util.Observable.call(this);
37892 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37893 onStartProxyDrag : function(x, y){
37894 this.fireEvent("beforeresize", this);
37896 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37898 o.enableDisplayMode("block");
37899 // all splitbars share the same overlay
37900 Roo.bootstrap.SplitBar.prototype.overlay = o;
37902 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37903 this.overlay.show();
37904 Roo.get(this.proxy).setDisplayed("block");
37905 var size = this.adapter.getElementSize(this);
37906 this.activeMinSize = this.getMinimumSize();;
37907 this.activeMaxSize = this.getMaximumSize();;
37908 var c1 = size - this.activeMinSize;
37909 var c2 = Math.max(this.activeMaxSize - size, 0);
37910 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37911 this.dd.resetConstraints();
37912 this.dd.setXConstraint(
37913 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37914 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37916 this.dd.setYConstraint(0, 0);
37918 this.dd.resetConstraints();
37919 this.dd.setXConstraint(0, 0);
37920 this.dd.setYConstraint(
37921 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37922 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37925 this.dragSpecs.startSize = size;
37926 this.dragSpecs.startPoint = [x, y];
37927 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37931 * @private Called after the drag operation by the DDProxy
37933 onEndProxyDrag : function(e){
37934 Roo.get(this.proxy).setDisplayed(false);
37935 var endPoint = Roo.lib.Event.getXY(e);
37937 this.overlay.hide();
37940 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37941 newSize = this.dragSpecs.startSize +
37942 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37943 endPoint[0] - this.dragSpecs.startPoint[0] :
37944 this.dragSpecs.startPoint[0] - endPoint[0]
37947 newSize = this.dragSpecs.startSize +
37948 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37949 endPoint[1] - this.dragSpecs.startPoint[1] :
37950 this.dragSpecs.startPoint[1] - endPoint[1]
37953 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37954 if(newSize != this.dragSpecs.startSize){
37955 if(this.fireEvent('beforeapply', this, newSize) !== false){
37956 this.adapter.setElementSize(this, newSize);
37957 this.fireEvent("moved", this, newSize);
37958 this.fireEvent("resize", this, newSize);
37964 * Get the adapter this SplitBar uses
37965 * @return The adapter object
37967 getAdapter : function(){
37968 return this.adapter;
37972 * Set the adapter this SplitBar uses
37973 * @param {Object} adapter A SplitBar adapter object
37975 setAdapter : function(adapter){
37976 this.adapter = adapter;
37977 this.adapter.init(this);
37981 * Gets the minimum size for the resizing element
37982 * @return {Number} The minimum size
37984 getMinimumSize : function(){
37985 return this.minSize;
37989 * Sets the minimum size for the resizing element
37990 * @param {Number} minSize The minimum size
37992 setMinimumSize : function(minSize){
37993 this.minSize = minSize;
37997 * Gets the maximum size for the resizing element
37998 * @return {Number} The maximum size
38000 getMaximumSize : function(){
38001 return this.maxSize;
38005 * Sets the maximum size for the resizing element
38006 * @param {Number} maxSize The maximum size
38008 setMaximumSize : function(maxSize){
38009 this.maxSize = maxSize;
38013 * Sets the initialize size for the resizing element
38014 * @param {Number} size The initial size
38016 setCurrentSize : function(size){
38017 var oldAnimate = this.animate;
38018 this.animate = false;
38019 this.adapter.setElementSize(this, size);
38020 this.animate = oldAnimate;
38024 * Destroy this splitbar.
38025 * @param {Boolean} removeEl True to remove the element
38027 destroy : function(removeEl){
38029 this.shim.remove();
38032 this.proxy.parentNode.removeChild(this.proxy);
38040 * @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.
38042 Roo.bootstrap.SplitBar.createProxy = function(dir){
38043 var proxy = new Roo.Element(document.createElement("div"));
38044 proxy.unselectable();
38045 var cls = 'roo-splitbar-proxy';
38046 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38047 document.body.appendChild(proxy.dom);
38052 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38053 * Default Adapter. It assumes the splitter and resizing element are not positioned
38054 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38056 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38059 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38060 // do nothing for now
38061 init : function(s){
38065 * Called before drag operations to get the current size of the resizing element.
38066 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38068 getElementSize : function(s){
38069 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38070 return s.resizingEl.getWidth();
38072 return s.resizingEl.getHeight();
38077 * Called after drag operations to set the size of the resizing element.
38078 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38079 * @param {Number} newSize The new size to set
38080 * @param {Function} onComplete A function to be invoked when resizing is complete
38082 setElementSize : function(s, newSize, onComplete){
38083 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38085 s.resizingEl.setWidth(newSize);
38087 onComplete(s, newSize);
38090 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38095 s.resizingEl.setHeight(newSize);
38097 onComplete(s, newSize);
38100 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38107 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38108 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38109 * Adapter that moves the splitter element to align with the resized sizing element.
38110 * Used with an absolute positioned SplitBar.
38111 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38112 * document.body, make sure you assign an id to the body element.
38114 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38115 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38116 this.container = Roo.get(container);
38119 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38120 init : function(s){
38121 this.basic.init(s);
38124 getElementSize : function(s){
38125 return this.basic.getElementSize(s);
38128 setElementSize : function(s, newSize, onComplete){
38129 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38132 moveSplitter : function(s){
38133 var yes = Roo.bootstrap.SplitBar;
38134 switch(s.placement){
38136 s.el.setX(s.resizingEl.getRight());
38139 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38142 s.el.setY(s.resizingEl.getBottom());
38145 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38152 * Orientation constant - Create a vertical SplitBar
38156 Roo.bootstrap.SplitBar.VERTICAL = 1;
38159 * Orientation constant - Create a horizontal SplitBar
38163 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38166 * Placement constant - The resizing element is to the left of the splitter element
38170 Roo.bootstrap.SplitBar.LEFT = 1;
38173 * Placement constant - The resizing element is to the right of the splitter element
38177 Roo.bootstrap.SplitBar.RIGHT = 2;
38180 * Placement constant - The resizing element is positioned above the splitter element
38184 Roo.bootstrap.SplitBar.TOP = 3;
38187 * Placement constant - The resizing element is positioned under splitter element
38191 Roo.bootstrap.SplitBar.BOTTOM = 4;
38192 Roo.namespace("Roo.bootstrap.layout");/*
38194 * Ext JS Library 1.1.1
38195 * Copyright(c) 2006-2007, Ext JS, LLC.
38197 * Originally Released Under LGPL - original licence link has changed is not relivant.
38200 * <script type="text/javascript">
38204 * @class Roo.bootstrap.layout.Manager
38205 * @extends Roo.bootstrap.Component
38206 * Base class for layout managers.
38208 Roo.bootstrap.layout.Manager = function(config)
38210 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38216 /** false to disable window resize monitoring @type Boolean */
38217 this.monitorWindowResize = true;
38222 * Fires when a layout is performed.
38223 * @param {Roo.LayoutManager} this
38227 * @event regionresized
38228 * Fires when the user resizes a region.
38229 * @param {Roo.LayoutRegion} region The resized region
38230 * @param {Number} newSize The new size (width for east/west, height for north/south)
38232 "regionresized" : true,
38234 * @event regioncollapsed
38235 * Fires when a region is collapsed.
38236 * @param {Roo.LayoutRegion} region The collapsed region
38238 "regioncollapsed" : true,
38240 * @event regionexpanded
38241 * Fires when a region is expanded.
38242 * @param {Roo.LayoutRegion} region The expanded region
38244 "regionexpanded" : true
38246 this.updating = false;
38249 this.el = Roo.get(config.el);
38255 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38260 monitorWindowResize : true,
38266 onRender : function(ct, position)
38269 this.el = Roo.get(ct);
38272 //this.fireEvent('render',this);
38276 initEvents: function()
38280 // ie scrollbar fix
38281 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38282 document.body.scroll = "no";
38283 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38284 this.el.position('relative');
38286 this.id = this.el.id;
38287 this.el.addClass("roo-layout-container");
38288 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38289 if(this.el.dom != document.body ) {
38290 this.el.on('resize', this.layout,this);
38291 this.el.on('show', this.layout,this);
38297 * Returns true if this layout is currently being updated
38298 * @return {Boolean}
38300 isUpdating : function(){
38301 return this.updating;
38305 * Suspend the LayoutManager from doing auto-layouts while
38306 * making multiple add or remove calls
38308 beginUpdate : function(){
38309 this.updating = true;
38313 * Restore auto-layouts and optionally disable the manager from performing a layout
38314 * @param {Boolean} noLayout true to disable a layout update
38316 endUpdate : function(noLayout){
38317 this.updating = false;
38323 layout: function(){
38327 onRegionResized : function(region, newSize){
38328 this.fireEvent("regionresized", region, newSize);
38332 onRegionCollapsed : function(region){
38333 this.fireEvent("regioncollapsed", region);
38336 onRegionExpanded : function(region){
38337 this.fireEvent("regionexpanded", region);
38341 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38342 * performs box-model adjustments.
38343 * @return {Object} The size as an object {width: (the width), height: (the height)}
38345 getViewSize : function()
38348 if(this.el.dom != document.body){
38349 size = this.el.getSize();
38351 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38353 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38354 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38359 * Returns the Element this layout is bound to.
38360 * @return {Roo.Element}
38362 getEl : function(){
38367 * Returns the specified region.
38368 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38369 * @return {Roo.LayoutRegion}
38371 getRegion : function(target){
38372 return this.regions[target.toLowerCase()];
38375 onWindowResize : function(){
38376 if(this.monitorWindowResize){
38383 * Ext JS Library 1.1.1
38384 * Copyright(c) 2006-2007, Ext JS, LLC.
38386 * Originally Released Under LGPL - original licence link has changed is not relivant.
38389 * <script type="text/javascript">
38392 * @class Roo.bootstrap.layout.Border
38393 * @extends Roo.bootstrap.layout.Manager
38395 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38396 * please see: examples/bootstrap/nested.html<br><br>
38398 <b>The container the layout is rendered into can be either the body element or any other element.
38399 If it is not the body element, the container needs to either be an absolute positioned element,
38400 or you will need to add "position:relative" to the css of the container. You will also need to specify
38401 the container size if it is not the body element.</b>
38404 * Create a new Border
38405 * @param {Object} config Configuration options
38407 Roo.bootstrap.layout.Border = function(config){
38408 config = config || {};
38409 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38413 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38414 if(config[region]){
38415 config[region].region = region;
38416 this.addRegion(config[region]);
38422 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38424 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38426 parent : false, // this might point to a 'nest' or a ???
38429 * Creates and adds a new region if it doesn't already exist.
38430 * @param {String} target The target region key (north, south, east, west or center).
38431 * @param {Object} config The regions config object
38432 * @return {BorderLayoutRegion} The new region
38434 addRegion : function(config)
38436 if(!this.regions[config.region]){
38437 var r = this.factory(config);
38438 this.bindRegion(r);
38440 return this.regions[config.region];
38444 bindRegion : function(r){
38445 this.regions[r.config.region] = r;
38447 r.on("visibilitychange", this.layout, this);
38448 r.on("paneladded", this.layout, this);
38449 r.on("panelremoved", this.layout, this);
38450 r.on("invalidated", this.layout, this);
38451 r.on("resized", this.onRegionResized, this);
38452 r.on("collapsed", this.onRegionCollapsed, this);
38453 r.on("expanded", this.onRegionExpanded, this);
38457 * Performs a layout update.
38459 layout : function()
38461 if(this.updating) {
38465 // render all the rebions if they have not been done alreayd?
38466 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38467 if(this.regions[region] && !this.regions[region].bodyEl){
38468 this.regions[region].onRender(this.el)
38472 var size = this.getViewSize();
38473 var w = size.width;
38474 var h = size.height;
38479 //var x = 0, y = 0;
38481 var rs = this.regions;
38482 var north = rs["north"];
38483 var south = rs["south"];
38484 var west = rs["west"];
38485 var east = rs["east"];
38486 var center = rs["center"];
38487 //if(this.hideOnLayout){ // not supported anymore
38488 //c.el.setStyle("display", "none");
38490 if(north && north.isVisible()){
38491 var b = north.getBox();
38492 var m = north.getMargins();
38493 b.width = w - (m.left+m.right);
38496 centerY = b.height + b.y + m.bottom;
38497 centerH -= centerY;
38498 north.updateBox(this.safeBox(b));
38500 if(south && south.isVisible()){
38501 var b = south.getBox();
38502 var m = south.getMargins();
38503 b.width = w - (m.left+m.right);
38505 var totalHeight = (b.height + m.top + m.bottom);
38506 b.y = h - totalHeight + m.top;
38507 centerH -= totalHeight;
38508 south.updateBox(this.safeBox(b));
38510 if(west && west.isVisible()){
38511 var b = west.getBox();
38512 var m = west.getMargins();
38513 b.height = centerH - (m.top+m.bottom);
38515 b.y = centerY + m.top;
38516 var totalWidth = (b.width + m.left + m.right);
38517 centerX += totalWidth;
38518 centerW -= totalWidth;
38519 west.updateBox(this.safeBox(b));
38521 if(east && east.isVisible()){
38522 var b = east.getBox();
38523 var m = east.getMargins();
38524 b.height = centerH - (m.top+m.bottom);
38525 var totalWidth = (b.width + m.left + m.right);
38526 b.x = w - totalWidth + m.left;
38527 b.y = centerY + m.top;
38528 centerW -= totalWidth;
38529 east.updateBox(this.safeBox(b));
38532 var m = center.getMargins();
38534 x: centerX + m.left,
38535 y: centerY + m.top,
38536 width: centerW - (m.left+m.right),
38537 height: centerH - (m.top+m.bottom)
38539 //if(this.hideOnLayout){
38540 //center.el.setStyle("display", "block");
38542 center.updateBox(this.safeBox(centerBox));
38545 this.fireEvent("layout", this);
38549 safeBox : function(box){
38550 box.width = Math.max(0, box.width);
38551 box.height = Math.max(0, box.height);
38556 * Adds a ContentPanel (or subclass) to this layout.
38557 * @param {String} target The target region key (north, south, east, west or center).
38558 * @param {Roo.ContentPanel} panel The panel to add
38559 * @return {Roo.ContentPanel} The added panel
38561 add : function(target, panel){
38563 target = target.toLowerCase();
38564 return this.regions[target].add(panel);
38568 * Remove a ContentPanel (or subclass) to this layout.
38569 * @param {String} target The target region key (north, south, east, west or center).
38570 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38571 * @return {Roo.ContentPanel} The removed panel
38573 remove : function(target, panel){
38574 target = target.toLowerCase();
38575 return this.regions[target].remove(panel);
38579 * Searches all regions for a panel with the specified id
38580 * @param {String} panelId
38581 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38583 findPanel : function(panelId){
38584 var rs = this.regions;
38585 for(var target in rs){
38586 if(typeof rs[target] != "function"){
38587 var p = rs[target].getPanel(panelId);
38597 * Searches all regions for a panel with the specified id and activates (shows) it.
38598 * @param {String/ContentPanel} panelId The panels id or the panel itself
38599 * @return {Roo.ContentPanel} The shown panel or null
38601 showPanel : function(panelId) {
38602 var rs = this.regions;
38603 for(var target in rs){
38604 var r = rs[target];
38605 if(typeof r != "function"){
38606 if(r.hasPanel(panelId)){
38607 return r.showPanel(panelId);
38615 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38616 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38619 restoreState : function(provider){
38621 provider = Roo.state.Manager;
38623 var sm = new Roo.LayoutStateManager();
38624 sm.init(this, provider);
38630 * Adds a xtype elements to the layout.
38634 xtype : 'ContentPanel',
38641 xtype : 'NestedLayoutPanel',
38647 items : [ ... list of content panels or nested layout panels.. ]
38651 * @param {Object} cfg Xtype definition of item to add.
38653 addxtype : function(cfg)
38655 // basically accepts a pannel...
38656 // can accept a layout region..!?!?
38657 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38660 // theory? children can only be panels??
38662 //if (!cfg.xtype.match(/Panel$/)) {
38667 if (typeof(cfg.region) == 'undefined') {
38668 Roo.log("Failed to add Panel, region was not set");
38672 var region = cfg.region;
38678 xitems = cfg.items;
38683 if ( region == 'center') {
38684 Roo.log("Center: " + cfg.title);
38690 case 'Content': // ContentPanel (el, cfg)
38691 case 'Scroll': // ContentPanel (el, cfg)
38693 cfg.autoCreate = cfg.autoCreate || true;
38694 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38696 // var el = this.el.createChild();
38697 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38700 this.add(region, ret);
38704 case 'TreePanel': // our new panel!
38705 cfg.el = this.el.createChild();
38706 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38707 this.add(region, ret);
38712 // create a new Layout (which is a Border Layout...
38714 var clayout = cfg.layout;
38715 clayout.el = this.el.createChild();
38716 clayout.items = clayout.items || [];
38720 // replace this exitems with the clayout ones..
38721 xitems = clayout.items;
38723 // force background off if it's in center...
38724 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38725 cfg.background = false;
38727 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38730 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38731 //console.log('adding nested layout panel ' + cfg.toSource());
38732 this.add(region, ret);
38733 nb = {}; /// find first...
38738 // needs grid and region
38740 //var el = this.getRegion(region).el.createChild();
38742 *var el = this.el.createChild();
38743 // create the grid first...
38744 cfg.grid.container = el;
38745 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38748 if (region == 'center' && this.active ) {
38749 cfg.background = false;
38752 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38754 this.add(region, ret);
38756 if (cfg.background) {
38757 // render grid on panel activation (if panel background)
38758 ret.on('activate', function(gp) {
38759 if (!gp.grid.rendered) {
38760 // gp.grid.render(el);
38764 // cfg.grid.render(el);
38770 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38771 // it was the old xcomponent building that caused this before.
38772 // espeically if border is the top element in the tree.
38782 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38784 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38785 this.add(region, ret);
38789 throw "Can not add '" + cfg.xtype + "' to Border";
38795 this.beginUpdate();
38799 Roo.each(xitems, function(i) {
38800 region = nb && i.region ? i.region : false;
38802 var add = ret.addxtype(i);
38805 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38806 if (!i.background) {
38807 abn[region] = nb[region] ;
38814 // make the last non-background panel active..
38815 //if (nb) { Roo.log(abn); }
38818 for(var r in abn) {
38819 region = this.getRegion(r);
38821 // tried using nb[r], but it does not work..
38823 region.showPanel(abn[r]);
38834 factory : function(cfg)
38837 var validRegions = Roo.bootstrap.layout.Border.regions;
38839 var target = cfg.region;
38842 var r = Roo.bootstrap.layout;
38846 return new r.North(cfg);
38848 return new r.South(cfg);
38850 return new r.East(cfg);
38852 return new r.West(cfg);
38854 return new r.Center(cfg);
38856 throw 'Layout region "'+target+'" not supported.';
38863 * Ext JS Library 1.1.1
38864 * Copyright(c) 2006-2007, Ext JS, LLC.
38866 * Originally Released Under LGPL - original licence link has changed is not relivant.
38869 * <script type="text/javascript">
38873 * @class Roo.bootstrap.layout.Basic
38874 * @extends Roo.util.Observable
38875 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38876 * and does not have a titlebar, tabs or any other features. All it does is size and position
38877 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38878 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38879 * @cfg {string} region the region that it inhabits..
38880 * @cfg {bool} skipConfig skip config?
38884 Roo.bootstrap.layout.Basic = function(config){
38886 this.mgr = config.mgr;
38888 this.position = config.region;
38890 var skipConfig = config.skipConfig;
38894 * @scope Roo.BasicLayoutRegion
38898 * @event beforeremove
38899 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38900 * @param {Roo.LayoutRegion} this
38901 * @param {Roo.ContentPanel} panel The panel
38902 * @param {Object} e The cancel event object
38904 "beforeremove" : true,
38906 * @event invalidated
38907 * Fires when the layout for this region is changed.
38908 * @param {Roo.LayoutRegion} this
38910 "invalidated" : true,
38912 * @event visibilitychange
38913 * Fires when this region is shown or hidden
38914 * @param {Roo.LayoutRegion} this
38915 * @param {Boolean} visibility true or false
38917 "visibilitychange" : true,
38919 * @event paneladded
38920 * Fires when a panel is added.
38921 * @param {Roo.LayoutRegion} this
38922 * @param {Roo.ContentPanel} panel The panel
38924 "paneladded" : true,
38926 * @event panelremoved
38927 * Fires when a panel is removed.
38928 * @param {Roo.LayoutRegion} this
38929 * @param {Roo.ContentPanel} panel The panel
38931 "panelremoved" : true,
38933 * @event beforecollapse
38934 * Fires when this region before collapse.
38935 * @param {Roo.LayoutRegion} this
38937 "beforecollapse" : true,
38940 * Fires when this region is collapsed.
38941 * @param {Roo.LayoutRegion} this
38943 "collapsed" : true,
38946 * Fires when this region is expanded.
38947 * @param {Roo.LayoutRegion} this
38952 * Fires when this region is slid into view.
38953 * @param {Roo.LayoutRegion} this
38955 "slideshow" : true,
38958 * Fires when this region slides out of view.
38959 * @param {Roo.LayoutRegion} this
38961 "slidehide" : true,
38963 * @event panelactivated
38964 * Fires when a panel is activated.
38965 * @param {Roo.LayoutRegion} this
38966 * @param {Roo.ContentPanel} panel The activated panel
38968 "panelactivated" : true,
38971 * Fires when the user resizes this region.
38972 * @param {Roo.LayoutRegion} this
38973 * @param {Number} newSize The new size (width for east/west, height for north/south)
38977 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38978 this.panels = new Roo.util.MixedCollection();
38979 this.panels.getKey = this.getPanelId.createDelegate(this);
38981 this.activePanel = null;
38982 // ensure listeners are added...
38984 if (config.listeners || config.events) {
38985 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38986 listeners : config.listeners || {},
38987 events : config.events || {}
38991 if(skipConfig !== true){
38992 this.applyConfig(config);
38996 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38998 getPanelId : function(p){
39002 applyConfig : function(config){
39003 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39004 this.config = config;
39009 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
39010 * the width, for horizontal (north, south) the height.
39011 * @param {Number} newSize The new width or height
39013 resizeTo : function(newSize){
39014 var el = this.el ? this.el :
39015 (this.activePanel ? this.activePanel.getEl() : null);
39017 switch(this.position){
39020 el.setWidth(newSize);
39021 this.fireEvent("resized", this, newSize);
39025 el.setHeight(newSize);
39026 this.fireEvent("resized", this, newSize);
39032 getBox : function(){
39033 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39036 getMargins : function(){
39037 return this.margins;
39040 updateBox : function(box){
39042 var el = this.activePanel.getEl();
39043 el.dom.style.left = box.x + "px";
39044 el.dom.style.top = box.y + "px";
39045 this.activePanel.setSize(box.width, box.height);
39049 * Returns the container element for this region.
39050 * @return {Roo.Element}
39052 getEl : function(){
39053 return this.activePanel;
39057 * Returns true if this region is currently visible.
39058 * @return {Boolean}
39060 isVisible : function(){
39061 return this.activePanel ? true : false;
39064 setActivePanel : function(panel){
39065 panel = this.getPanel(panel);
39066 if(this.activePanel && this.activePanel != panel){
39067 this.activePanel.setActiveState(false);
39068 this.activePanel.getEl().setLeftTop(-10000,-10000);
39070 this.activePanel = panel;
39071 panel.setActiveState(true);
39073 panel.setSize(this.box.width, this.box.height);
39075 this.fireEvent("panelactivated", this, panel);
39076 this.fireEvent("invalidated");
39080 * Show the specified panel.
39081 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39082 * @return {Roo.ContentPanel} The shown panel or null
39084 showPanel : function(panel){
39085 panel = this.getPanel(panel);
39087 this.setActivePanel(panel);
39093 * Get the active panel for this region.
39094 * @return {Roo.ContentPanel} The active panel or null
39096 getActivePanel : function(){
39097 return this.activePanel;
39101 * Add the passed ContentPanel(s)
39102 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39103 * @return {Roo.ContentPanel} The panel added (if only one was added)
39105 add : function(panel){
39106 if(arguments.length > 1){
39107 for(var i = 0, len = arguments.length; i < len; i++) {
39108 this.add(arguments[i]);
39112 if(this.hasPanel(panel)){
39113 this.showPanel(panel);
39116 var el = panel.getEl();
39117 if(el.dom.parentNode != this.mgr.el.dom){
39118 this.mgr.el.dom.appendChild(el.dom);
39120 if(panel.setRegion){
39121 panel.setRegion(this);
39123 this.panels.add(panel);
39124 el.setStyle("position", "absolute");
39125 if(!panel.background){
39126 this.setActivePanel(panel);
39127 if(this.config.initialSize && this.panels.getCount()==1){
39128 this.resizeTo(this.config.initialSize);
39131 this.fireEvent("paneladded", this, panel);
39136 * Returns true if the panel is in this region.
39137 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39138 * @return {Boolean}
39140 hasPanel : function(panel){
39141 if(typeof panel == "object"){ // must be panel obj
39142 panel = panel.getId();
39144 return this.getPanel(panel) ? true : false;
39148 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39149 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39150 * @param {Boolean} preservePanel Overrides the config preservePanel option
39151 * @return {Roo.ContentPanel} The panel that was removed
39153 remove : function(panel, preservePanel){
39154 panel = this.getPanel(panel);
39159 this.fireEvent("beforeremove", this, panel, e);
39160 if(e.cancel === true){
39163 var panelId = panel.getId();
39164 this.panels.removeKey(panelId);
39169 * Returns the panel specified or null if it's not in this region.
39170 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39171 * @return {Roo.ContentPanel}
39173 getPanel : function(id){
39174 if(typeof id == "object"){ // must be panel obj
39177 return this.panels.get(id);
39181 * Returns this regions position (north/south/east/west/center).
39184 getPosition: function(){
39185 return this.position;
39189 * Ext JS Library 1.1.1
39190 * Copyright(c) 2006-2007, Ext JS, LLC.
39192 * Originally Released Under LGPL - original licence link has changed is not relivant.
39195 * <script type="text/javascript">
39199 * @class Roo.bootstrap.layout.Region
39200 * @extends Roo.bootstrap.layout.Basic
39201 * This class represents a region in a layout manager.
39203 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39204 * @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})
39205 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39206 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39207 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39208 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39209 * @cfg {String} title The title for the region (overrides panel titles)
39210 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39211 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39212 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39213 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39214 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39215 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39216 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39217 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39218 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39219 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39221 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39222 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39223 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39224 * @cfg {Number} width For East/West panels
39225 * @cfg {Number} height For North/South panels
39226 * @cfg {Boolean} split To show the splitter
39227 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39229 * @cfg {string} cls Extra CSS classes to add to region
39231 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39232 * @cfg {string} region the region that it inhabits..
39235 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39236 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39238 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39239 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39240 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39242 Roo.bootstrap.layout.Region = function(config)
39244 this.applyConfig(config);
39246 var mgr = config.mgr;
39247 var pos = config.region;
39248 config.skipConfig = true;
39249 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39252 this.onRender(mgr.el);
39255 this.visible = true;
39256 this.collapsed = false;
39257 this.unrendered_panels = [];
39260 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39262 position: '', // set by wrapper (eg. north/south etc..)
39263 unrendered_panels : null, // unrendered panels.
39265 tabPosition : false,
39267 mgr: false, // points to 'Border'
39270 createBody : function(){
39271 /** This region's body element
39272 * @type Roo.Element */
39273 this.bodyEl = this.el.createChild({
39275 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39279 onRender: function(ctr, pos)
39281 var dh = Roo.DomHelper;
39282 /** This region's container element
39283 * @type Roo.Element */
39284 this.el = dh.append(ctr.dom, {
39286 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39288 /** This region's title element
39289 * @type Roo.Element */
39291 this.titleEl = dh.append(this.el.dom, {
39293 unselectable: "on",
39294 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39296 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39297 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39301 this.titleEl.enableDisplayMode();
39302 /** This region's title text element
39303 * @type HTMLElement */
39304 this.titleTextEl = this.titleEl.dom.firstChild;
39305 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39307 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39308 this.closeBtn.enableDisplayMode();
39309 this.closeBtn.on("click", this.closeClicked, this);
39310 this.closeBtn.hide();
39312 this.createBody(this.config);
39313 if(this.config.hideWhenEmpty){
39315 this.on("paneladded", this.validateVisibility, this);
39316 this.on("panelremoved", this.validateVisibility, this);
39318 if(this.autoScroll){
39319 this.bodyEl.setStyle("overflow", "auto");
39321 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39323 //if(c.titlebar !== false){
39324 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39325 this.titleEl.hide();
39327 this.titleEl.show();
39328 if(this.config.title){
39329 this.titleTextEl.innerHTML = this.config.title;
39333 if(this.config.collapsed){
39334 this.collapse(true);
39336 if(this.config.hidden){
39340 if (this.unrendered_panels && this.unrendered_panels.length) {
39341 for (var i =0;i< this.unrendered_panels.length; i++) {
39342 this.add(this.unrendered_panels[i]);
39344 this.unrendered_panels = null;
39350 applyConfig : function(c)
39353 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39354 var dh = Roo.DomHelper;
39355 if(c.titlebar !== false){
39356 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39357 this.collapseBtn.on("click", this.collapse, this);
39358 this.collapseBtn.enableDisplayMode();
39360 if(c.showPin === true || this.showPin){
39361 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39362 this.stickBtn.enableDisplayMode();
39363 this.stickBtn.on("click", this.expand, this);
39364 this.stickBtn.hide();
39369 /** This region's collapsed element
39370 * @type Roo.Element */
39373 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39374 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39377 if(c.floatable !== false){
39378 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39379 this.collapsedEl.on("click", this.collapseClick, this);
39382 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39383 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39384 id: "message", unselectable: "on", style:{"float":"left"}});
39385 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39387 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39388 this.expandBtn.on("click", this.expand, this);
39392 if(this.collapseBtn){
39393 this.collapseBtn.setVisible(c.collapsible == true);
39396 this.cmargins = c.cmargins || this.cmargins ||
39397 (this.position == "west" || this.position == "east" ?
39398 {top: 0, left: 2, right:2, bottom: 0} :
39399 {top: 2, left: 0, right:0, bottom: 2});
39401 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39404 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39406 this.autoScroll = c.autoScroll || false;
39411 this.duration = c.duration || .30;
39412 this.slideDuration = c.slideDuration || .45;
39417 * Returns true if this region is currently visible.
39418 * @return {Boolean}
39420 isVisible : function(){
39421 return this.visible;
39425 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39426 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39428 //setCollapsedTitle : function(title){
39429 // title = title || " ";
39430 // if(this.collapsedTitleTextEl){
39431 // this.collapsedTitleTextEl.innerHTML = title;
39435 getBox : function(){
39437 // if(!this.collapsed){
39438 b = this.el.getBox(false, true);
39440 // b = this.collapsedEl.getBox(false, true);
39445 getMargins : function(){
39446 return this.margins;
39447 //return this.collapsed ? this.cmargins : this.margins;
39450 highlight : function(){
39451 this.el.addClass("x-layout-panel-dragover");
39454 unhighlight : function(){
39455 this.el.removeClass("x-layout-panel-dragover");
39458 updateBox : function(box)
39460 if (!this.bodyEl) {
39461 return; // not rendered yet..
39465 if(!this.collapsed){
39466 this.el.dom.style.left = box.x + "px";
39467 this.el.dom.style.top = box.y + "px";
39468 this.updateBody(box.width, box.height);
39470 this.collapsedEl.dom.style.left = box.x + "px";
39471 this.collapsedEl.dom.style.top = box.y + "px";
39472 this.collapsedEl.setSize(box.width, box.height);
39475 this.tabs.autoSizeTabs();
39479 updateBody : function(w, h)
39482 this.el.setWidth(w);
39483 w -= this.el.getBorderWidth("rl");
39484 if(this.config.adjustments){
39485 w += this.config.adjustments[0];
39488 if(h !== null && h > 0){
39489 this.el.setHeight(h);
39490 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39491 h -= this.el.getBorderWidth("tb");
39492 if(this.config.adjustments){
39493 h += this.config.adjustments[1];
39495 this.bodyEl.setHeight(h);
39497 h = this.tabs.syncHeight(h);
39500 if(this.panelSize){
39501 w = w !== null ? w : this.panelSize.width;
39502 h = h !== null ? h : this.panelSize.height;
39504 if(this.activePanel){
39505 var el = this.activePanel.getEl();
39506 w = w !== null ? w : el.getWidth();
39507 h = h !== null ? h : el.getHeight();
39508 this.panelSize = {width: w, height: h};
39509 this.activePanel.setSize(w, h);
39511 if(Roo.isIE && this.tabs){
39512 this.tabs.el.repaint();
39517 * Returns the container element for this region.
39518 * @return {Roo.Element}
39520 getEl : function(){
39525 * Hides this region.
39528 //if(!this.collapsed){
39529 this.el.dom.style.left = "-2000px";
39532 // this.collapsedEl.dom.style.left = "-2000px";
39533 // this.collapsedEl.hide();
39535 this.visible = false;
39536 this.fireEvent("visibilitychange", this, false);
39540 * Shows this region if it was previously hidden.
39543 //if(!this.collapsed){
39546 // this.collapsedEl.show();
39548 this.visible = true;
39549 this.fireEvent("visibilitychange", this, true);
39552 closeClicked : function(){
39553 if(this.activePanel){
39554 this.remove(this.activePanel);
39558 collapseClick : function(e){
39560 e.stopPropagation();
39563 e.stopPropagation();
39569 * Collapses this region.
39570 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39573 collapse : function(skipAnim, skipCheck = false){
39574 if(this.collapsed) {
39578 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39580 this.collapsed = true;
39582 this.split.el.hide();
39584 if(this.config.animate && skipAnim !== true){
39585 this.fireEvent("invalidated", this);
39586 this.animateCollapse();
39588 this.el.setLocation(-20000,-20000);
39590 this.collapsedEl.show();
39591 this.fireEvent("collapsed", this);
39592 this.fireEvent("invalidated", this);
39598 animateCollapse : function(){
39603 * Expands this region if it was previously collapsed.
39604 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39605 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39608 expand : function(e, skipAnim){
39610 e.stopPropagation();
39612 if(!this.collapsed || this.el.hasActiveFx()) {
39616 this.afterSlideIn();
39619 this.collapsed = false;
39620 if(this.config.animate && skipAnim !== true){
39621 this.animateExpand();
39625 this.split.el.show();
39627 this.collapsedEl.setLocation(-2000,-2000);
39628 this.collapsedEl.hide();
39629 this.fireEvent("invalidated", this);
39630 this.fireEvent("expanded", this);
39634 animateExpand : function(){
39638 initTabs : function()
39640 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39642 var ts = new Roo.bootstrap.panel.Tabs({
39643 el: this.bodyEl.dom,
39645 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39646 disableTooltips: this.config.disableTabTips,
39647 toolbar : this.config.toolbar
39650 if(this.config.hideTabs){
39651 ts.stripWrap.setDisplayed(false);
39654 ts.resizeTabs = this.config.resizeTabs === true;
39655 ts.minTabWidth = this.config.minTabWidth || 40;
39656 ts.maxTabWidth = this.config.maxTabWidth || 250;
39657 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39658 ts.monitorResize = false;
39659 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39660 ts.bodyEl.addClass('roo-layout-tabs-body');
39661 this.panels.each(this.initPanelAsTab, this);
39664 initPanelAsTab : function(panel){
39665 var ti = this.tabs.addTab(
39669 this.config.closeOnTab && panel.isClosable(),
39672 if(panel.tabTip !== undefined){
39673 ti.setTooltip(panel.tabTip);
39675 ti.on("activate", function(){
39676 this.setActivePanel(panel);
39679 if(this.config.closeOnTab){
39680 ti.on("beforeclose", function(t, e){
39682 this.remove(panel);
39686 panel.tabItem = ti;
39691 updatePanelTitle : function(panel, title)
39693 if(this.activePanel == panel){
39694 this.updateTitle(title);
39697 var ti = this.tabs.getTab(panel.getEl().id);
39699 if(panel.tabTip !== undefined){
39700 ti.setTooltip(panel.tabTip);
39705 updateTitle : function(title){
39706 if(this.titleTextEl && !this.config.title){
39707 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39711 setActivePanel : function(panel)
39713 panel = this.getPanel(panel);
39714 if(this.activePanel && this.activePanel != panel){
39715 if(this.activePanel.setActiveState(false) === false){
39719 this.activePanel = panel;
39720 panel.setActiveState(true);
39721 if(this.panelSize){
39722 panel.setSize(this.panelSize.width, this.panelSize.height);
39725 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39727 this.updateTitle(panel.getTitle());
39729 this.fireEvent("invalidated", this);
39731 this.fireEvent("panelactivated", this, panel);
39735 * Shows the specified panel.
39736 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39737 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39739 showPanel : function(panel)
39741 panel = this.getPanel(panel);
39744 var tab = this.tabs.getTab(panel.getEl().id);
39745 if(tab.isHidden()){
39746 this.tabs.unhideTab(tab.id);
39750 this.setActivePanel(panel);
39757 * Get the active panel for this region.
39758 * @return {Roo.ContentPanel} The active panel or null
39760 getActivePanel : function(){
39761 return this.activePanel;
39764 validateVisibility : function(){
39765 if(this.panels.getCount() < 1){
39766 this.updateTitle(" ");
39767 this.closeBtn.hide();
39770 if(!this.isVisible()){
39777 * Adds the passed ContentPanel(s) to this region.
39778 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39779 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39781 add : function(panel)
39783 if(arguments.length > 1){
39784 for(var i = 0, len = arguments.length; i < len; i++) {
39785 this.add(arguments[i]);
39790 // if we have not been rendered yet, then we can not really do much of this..
39791 if (!this.bodyEl) {
39792 this.unrendered_panels.push(panel);
39799 if(this.hasPanel(panel)){
39800 this.showPanel(panel);
39803 panel.setRegion(this);
39804 this.panels.add(panel);
39805 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39806 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39807 // and hide them... ???
39808 this.bodyEl.dom.appendChild(panel.getEl().dom);
39809 if(panel.background !== true){
39810 this.setActivePanel(panel);
39812 this.fireEvent("paneladded", this, panel);
39819 this.initPanelAsTab(panel);
39823 if(panel.background !== true){
39824 this.tabs.activate(panel.getEl().id);
39826 this.fireEvent("paneladded", this, panel);
39831 * Hides the tab for the specified panel.
39832 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39834 hidePanel : function(panel){
39835 if(this.tabs && (panel = this.getPanel(panel))){
39836 this.tabs.hideTab(panel.getEl().id);
39841 * Unhides the tab for a previously hidden panel.
39842 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39844 unhidePanel : function(panel){
39845 if(this.tabs && (panel = this.getPanel(panel))){
39846 this.tabs.unhideTab(panel.getEl().id);
39850 clearPanels : function(){
39851 while(this.panels.getCount() > 0){
39852 this.remove(this.panels.first());
39857 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39858 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39859 * @param {Boolean} preservePanel Overrides the config preservePanel option
39860 * @return {Roo.ContentPanel} The panel that was removed
39862 remove : function(panel, preservePanel)
39864 panel = this.getPanel(panel);
39869 this.fireEvent("beforeremove", this, panel, e);
39870 if(e.cancel === true){
39873 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39874 var panelId = panel.getId();
39875 this.panels.removeKey(panelId);
39877 document.body.appendChild(panel.getEl().dom);
39880 this.tabs.removeTab(panel.getEl().id);
39881 }else if (!preservePanel){
39882 this.bodyEl.dom.removeChild(panel.getEl().dom);
39884 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39885 var p = this.panels.first();
39886 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39887 tempEl.appendChild(p.getEl().dom);
39888 this.bodyEl.update("");
39889 this.bodyEl.dom.appendChild(p.getEl().dom);
39891 this.updateTitle(p.getTitle());
39893 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39894 this.setActivePanel(p);
39896 panel.setRegion(null);
39897 if(this.activePanel == panel){
39898 this.activePanel = null;
39900 if(this.config.autoDestroy !== false && preservePanel !== true){
39901 try{panel.destroy();}catch(e){}
39903 this.fireEvent("panelremoved", this, panel);
39908 * Returns the TabPanel component used by this region
39909 * @return {Roo.TabPanel}
39911 getTabs : function(){
39915 createTool : function(parentEl, className){
39916 var btn = Roo.DomHelper.append(parentEl, {
39918 cls: "x-layout-tools-button",
39921 cls: "roo-layout-tools-button-inner " + className,
39925 btn.addClassOnOver("roo-layout-tools-button-over");
39930 * Ext JS Library 1.1.1
39931 * Copyright(c) 2006-2007, Ext JS, LLC.
39933 * Originally Released Under LGPL - original licence link has changed is not relivant.
39936 * <script type="text/javascript">
39942 * @class Roo.SplitLayoutRegion
39943 * @extends Roo.LayoutRegion
39944 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39946 Roo.bootstrap.layout.Split = function(config){
39947 this.cursor = config.cursor;
39948 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39951 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39953 splitTip : "Drag to resize.",
39954 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39955 useSplitTips : false,
39957 applyConfig : function(config){
39958 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39961 onRender : function(ctr,pos) {
39963 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39964 if(!this.config.split){
39969 var splitEl = Roo.DomHelper.append(ctr.dom, {
39971 id: this.el.id + "-split",
39972 cls: "roo-layout-split roo-layout-split-"+this.position,
39975 /** The SplitBar for this region
39976 * @type Roo.SplitBar */
39977 // does not exist yet...
39978 Roo.log([this.position, this.orientation]);
39980 this.split = new Roo.bootstrap.SplitBar({
39981 dragElement : splitEl,
39982 resizingElement: this.el,
39983 orientation : this.orientation
39986 this.split.on("moved", this.onSplitMove, this);
39987 this.split.useShim = this.config.useShim === true;
39988 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39989 if(this.useSplitTips){
39990 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39992 //if(config.collapsible){
39993 // this.split.el.on("dblclick", this.collapse, this);
39996 if(typeof this.config.minSize != "undefined"){
39997 this.split.minSize = this.config.minSize;
39999 if(typeof this.config.maxSize != "undefined"){
40000 this.split.maxSize = this.config.maxSize;
40002 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
40003 this.hideSplitter();
40008 getHMaxSize : function(){
40009 var cmax = this.config.maxSize || 10000;
40010 var center = this.mgr.getRegion("center");
40011 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40014 getVMaxSize : function(){
40015 var cmax = this.config.maxSize || 10000;
40016 var center = this.mgr.getRegion("center");
40017 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40020 onSplitMove : function(split, newSize){
40021 this.fireEvent("resized", this, newSize);
40025 * Returns the {@link Roo.SplitBar} for this region.
40026 * @return {Roo.SplitBar}
40028 getSplitBar : function(){
40033 this.hideSplitter();
40034 Roo.bootstrap.layout.Split.superclass.hide.call(this);
40037 hideSplitter : function(){
40039 this.split.el.setLocation(-2000,-2000);
40040 this.split.el.hide();
40046 this.split.el.show();
40048 Roo.bootstrap.layout.Split.superclass.show.call(this);
40051 beforeSlide: function(){
40052 if(Roo.isGecko){// firefox overflow auto bug workaround
40053 this.bodyEl.clip();
40055 this.tabs.bodyEl.clip();
40057 if(this.activePanel){
40058 this.activePanel.getEl().clip();
40060 if(this.activePanel.beforeSlide){
40061 this.activePanel.beforeSlide();
40067 afterSlide : function(){
40068 if(Roo.isGecko){// firefox overflow auto bug workaround
40069 this.bodyEl.unclip();
40071 this.tabs.bodyEl.unclip();
40073 if(this.activePanel){
40074 this.activePanel.getEl().unclip();
40075 if(this.activePanel.afterSlide){
40076 this.activePanel.afterSlide();
40082 initAutoHide : function(){
40083 if(this.autoHide !== false){
40084 if(!this.autoHideHd){
40085 var st = new Roo.util.DelayedTask(this.slideIn, this);
40086 this.autoHideHd = {
40087 "mouseout": function(e){
40088 if(!e.within(this.el, true)){
40092 "mouseover" : function(e){
40098 this.el.on(this.autoHideHd);
40102 clearAutoHide : function(){
40103 if(this.autoHide !== false){
40104 this.el.un("mouseout", this.autoHideHd.mouseout);
40105 this.el.un("mouseover", this.autoHideHd.mouseover);
40109 clearMonitor : function(){
40110 Roo.get(document).un("click", this.slideInIf, this);
40113 // these names are backwards but not changed for compat
40114 slideOut : function(){
40115 if(this.isSlid || this.el.hasActiveFx()){
40118 this.isSlid = true;
40119 if(this.collapseBtn){
40120 this.collapseBtn.hide();
40122 this.closeBtnState = this.closeBtn.getStyle('display');
40123 this.closeBtn.hide();
40125 this.stickBtn.show();
40128 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40129 this.beforeSlide();
40130 this.el.setStyle("z-index", 10001);
40131 this.el.slideIn(this.getSlideAnchor(), {
40132 callback: function(){
40134 this.initAutoHide();
40135 Roo.get(document).on("click", this.slideInIf, this);
40136 this.fireEvent("slideshow", this);
40143 afterSlideIn : function(){
40144 this.clearAutoHide();
40145 this.isSlid = false;
40146 this.clearMonitor();
40147 this.el.setStyle("z-index", "");
40148 if(this.collapseBtn){
40149 this.collapseBtn.show();
40151 this.closeBtn.setStyle('display', this.closeBtnState);
40153 this.stickBtn.hide();
40155 this.fireEvent("slidehide", this);
40158 slideIn : function(cb){
40159 if(!this.isSlid || this.el.hasActiveFx()){
40163 this.isSlid = false;
40164 this.beforeSlide();
40165 this.el.slideOut(this.getSlideAnchor(), {
40166 callback: function(){
40167 this.el.setLeftTop(-10000, -10000);
40169 this.afterSlideIn();
40177 slideInIf : function(e){
40178 if(!e.within(this.el)){
40183 animateCollapse : function(){
40184 this.beforeSlide();
40185 this.el.setStyle("z-index", 20000);
40186 var anchor = this.getSlideAnchor();
40187 this.el.slideOut(anchor, {
40188 callback : function(){
40189 this.el.setStyle("z-index", "");
40190 this.collapsedEl.slideIn(anchor, {duration:.3});
40192 this.el.setLocation(-10000,-10000);
40194 this.fireEvent("collapsed", this);
40201 animateExpand : function(){
40202 this.beforeSlide();
40203 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40204 this.el.setStyle("z-index", 20000);
40205 this.collapsedEl.hide({
40208 this.el.slideIn(this.getSlideAnchor(), {
40209 callback : function(){
40210 this.el.setStyle("z-index", "");
40213 this.split.el.show();
40215 this.fireEvent("invalidated", this);
40216 this.fireEvent("expanded", this);
40244 getAnchor : function(){
40245 return this.anchors[this.position];
40248 getCollapseAnchor : function(){
40249 return this.canchors[this.position];
40252 getSlideAnchor : function(){
40253 return this.sanchors[this.position];
40256 getAlignAdj : function(){
40257 var cm = this.cmargins;
40258 switch(this.position){
40274 getExpandAdj : function(){
40275 var c = this.collapsedEl, cm = this.cmargins;
40276 switch(this.position){
40278 return [-(cm.right+c.getWidth()+cm.left), 0];
40281 return [cm.right+c.getWidth()+cm.left, 0];
40284 return [0, -(cm.top+cm.bottom+c.getHeight())];
40287 return [0, cm.top+cm.bottom+c.getHeight()];
40293 * Ext JS Library 1.1.1
40294 * Copyright(c) 2006-2007, Ext JS, LLC.
40296 * Originally Released Under LGPL - original licence link has changed is not relivant.
40299 * <script type="text/javascript">
40302 * These classes are private internal classes
40304 Roo.bootstrap.layout.Center = function(config){
40305 config.region = "center";
40306 Roo.bootstrap.layout.Region.call(this, config);
40307 this.visible = true;
40308 this.minWidth = config.minWidth || 20;
40309 this.minHeight = config.minHeight || 20;
40312 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40314 // center panel can't be hidden
40318 // center panel can't be hidden
40321 getMinWidth: function(){
40322 return this.minWidth;
40325 getMinHeight: function(){
40326 return this.minHeight;
40340 Roo.bootstrap.layout.North = function(config)
40342 config.region = 'north';
40343 config.cursor = 'n-resize';
40345 Roo.bootstrap.layout.Split.call(this, config);
40349 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40350 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40351 this.split.el.addClass("roo-layout-split-v");
40353 //var size = config.initialSize || config.height;
40354 //if(this.el && typeof size != "undefined"){
40355 // this.el.setHeight(size);
40358 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40360 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40363 onRender : function(ctr, pos)
40365 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40366 var size = this.config.initialSize || this.config.height;
40367 if(this.el && typeof size != "undefined"){
40368 this.el.setHeight(size);
40373 getBox : function(){
40374 if(this.collapsed){
40375 return this.collapsedEl.getBox();
40377 var box = this.el.getBox();
40379 box.height += this.split.el.getHeight();
40384 updateBox : function(box){
40385 if(this.split && !this.collapsed){
40386 box.height -= this.split.el.getHeight();
40387 this.split.el.setLeft(box.x);
40388 this.split.el.setTop(box.y+box.height);
40389 this.split.el.setWidth(box.width);
40391 if(this.collapsed){
40392 this.updateBody(box.width, null);
40394 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40402 Roo.bootstrap.layout.South = function(config){
40403 config.region = 'south';
40404 config.cursor = 's-resize';
40405 Roo.bootstrap.layout.Split.call(this, config);
40407 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40408 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40409 this.split.el.addClass("roo-layout-split-v");
40414 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40415 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40417 onRender : function(ctr, pos)
40419 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40420 var size = this.config.initialSize || this.config.height;
40421 if(this.el && typeof size != "undefined"){
40422 this.el.setHeight(size);
40427 getBox : function(){
40428 if(this.collapsed){
40429 return this.collapsedEl.getBox();
40431 var box = this.el.getBox();
40433 var sh = this.split.el.getHeight();
40440 updateBox : function(box){
40441 if(this.split && !this.collapsed){
40442 var sh = this.split.el.getHeight();
40445 this.split.el.setLeft(box.x);
40446 this.split.el.setTop(box.y-sh);
40447 this.split.el.setWidth(box.width);
40449 if(this.collapsed){
40450 this.updateBody(box.width, null);
40452 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40456 Roo.bootstrap.layout.East = function(config){
40457 config.region = "east";
40458 config.cursor = "e-resize";
40459 Roo.bootstrap.layout.Split.call(this, config);
40461 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40462 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40463 this.split.el.addClass("roo-layout-split-h");
40467 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40468 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40470 onRender : function(ctr, pos)
40472 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40473 var size = this.config.initialSize || this.config.width;
40474 if(this.el && typeof size != "undefined"){
40475 this.el.setWidth(size);
40480 getBox : function(){
40481 if(this.collapsed){
40482 return this.collapsedEl.getBox();
40484 var box = this.el.getBox();
40486 var sw = this.split.el.getWidth();
40493 updateBox : function(box){
40494 if(this.split && !this.collapsed){
40495 var sw = this.split.el.getWidth();
40497 this.split.el.setLeft(box.x);
40498 this.split.el.setTop(box.y);
40499 this.split.el.setHeight(box.height);
40502 if(this.collapsed){
40503 this.updateBody(null, box.height);
40505 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40509 Roo.bootstrap.layout.West = function(config){
40510 config.region = "west";
40511 config.cursor = "w-resize";
40513 Roo.bootstrap.layout.Split.call(this, config);
40515 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40516 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40517 this.split.el.addClass("roo-layout-split-h");
40521 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40522 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40524 onRender: function(ctr, pos)
40526 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40527 var size = this.config.initialSize || this.config.width;
40528 if(typeof size != "undefined"){
40529 this.el.setWidth(size);
40533 getBox : function(){
40534 if(this.collapsed){
40535 return this.collapsedEl.getBox();
40537 var box = this.el.getBox();
40538 if (box.width == 0) {
40539 box.width = this.config.width; // kludge?
40542 box.width += this.split.el.getWidth();
40547 updateBox : function(box){
40548 if(this.split && !this.collapsed){
40549 var sw = this.split.el.getWidth();
40551 this.split.el.setLeft(box.x+box.width);
40552 this.split.el.setTop(box.y);
40553 this.split.el.setHeight(box.height);
40555 if(this.collapsed){
40556 this.updateBody(null, box.height);
40558 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40560 });Roo.namespace("Roo.bootstrap.panel");/*
40562 * Ext JS Library 1.1.1
40563 * Copyright(c) 2006-2007, Ext JS, LLC.
40565 * Originally Released Under LGPL - original licence link has changed is not relivant.
40568 * <script type="text/javascript">
40571 * @class Roo.bootstrap.paenl.Content
40572 * @extends Roo.util.Observable
40574 * @children Roo.bootstrap.Component
40575 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40576 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40577 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40578 * @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
40579 * @cfg {Boolean} closable True if the panel can be closed/removed
40580 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40581 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40582 * @cfg {Toolbar} toolbar A toolbar for this panel
40583 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40584 * @cfg {String} title The title for this panel
40585 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40586 * @cfg {String} url Calls {@link #setUrl} with this value
40587 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40588 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40589 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40590 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40591 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40592 * @cfg {Boolean} badges render the badges
40593 * @cfg {String} cls extra classes to use
40594 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40597 * Create a new ContentPanel.
40598 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40599 * @param {String/Object} config A string to set only the title or a config object
40600 * @param {String} content (optional) Set the HTML content for this panel
40601 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40603 Roo.bootstrap.panel.Content = function( config){
40605 this.tpl = config.tpl || false;
40607 var el = config.el;
40608 var content = config.content;
40610 if(config.autoCreate){ // xtype is available if this is called from factory
40613 this.el = Roo.get(el);
40614 if(!this.el && config && config.autoCreate){
40615 if(typeof config.autoCreate == "object"){
40616 if(!config.autoCreate.id){
40617 config.autoCreate.id = config.id||el;
40619 this.el = Roo.DomHelper.append(document.body,
40620 config.autoCreate, true);
40624 cls: (config.cls || '') +
40625 (config.background ? ' bg-' + config.background : '') +
40626 " roo-layout-inactive-content",
40629 if (config.iframe) {
40633 style : 'border: 0px',
40634 src : 'about:blank'
40640 elcfg.html = config.html;
40644 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40645 if (config.iframe) {
40646 this.iframeEl = this.el.select('iframe',true).first();
40651 this.closable = false;
40652 this.loaded = false;
40653 this.active = false;
40656 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40658 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40660 this.wrapEl = this.el; //this.el.wrap();
40662 if (config.toolbar.items) {
40663 ti = config.toolbar.items ;
40664 delete config.toolbar.items ;
40668 this.toolbar.render(this.wrapEl, 'before');
40669 for(var i =0;i < ti.length;i++) {
40670 // Roo.log(['add child', items[i]]);
40671 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40673 this.toolbar.items = nitems;
40674 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40675 delete config.toolbar;
40679 // xtype created footer. - not sure if will work as we normally have to render first..
40680 if (this.footer && !this.footer.el && this.footer.xtype) {
40681 if (!this.wrapEl) {
40682 this.wrapEl = this.el.wrap();
40685 this.footer.container = this.wrapEl.createChild();
40687 this.footer = Roo.factory(this.footer, Roo);
40692 if(typeof config == "string"){
40693 this.title = config;
40695 Roo.apply(this, config);
40699 this.resizeEl = Roo.get(this.resizeEl, true);
40701 this.resizeEl = this.el;
40703 // handle view.xtype
40711 * Fires when this panel is activated.
40712 * @param {Roo.ContentPanel} this
40716 * @event deactivate
40717 * Fires when this panel is activated.
40718 * @param {Roo.ContentPanel} this
40720 "deactivate" : true,
40724 * Fires when this panel is resized if fitToFrame is true.
40725 * @param {Roo.ContentPanel} this
40726 * @param {Number} width The width after any component adjustments
40727 * @param {Number} height The height after any component adjustments
40733 * Fires when this tab is created
40734 * @param {Roo.ContentPanel} this
40740 * Fires when this content is scrolled
40741 * @param {Roo.ContentPanel} this
40742 * @param {Event} scrollEvent
40753 if(this.autoScroll && !this.iframe){
40754 this.resizeEl.setStyle("overflow", "auto");
40755 this.resizeEl.on('scroll', this.onScroll, this);
40757 // fix randome scrolling
40758 //this.el.on('scroll', function() {
40759 // Roo.log('fix random scolling');
40760 // this.scrollTo('top',0);
40763 content = content || this.content;
40765 this.setContent(content);
40767 if(config && config.url){
40768 this.setUrl(this.url, this.params, this.loadOnce);
40773 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40775 if (this.view && typeof(this.view.xtype) != 'undefined') {
40776 this.view.el = this.el.appendChild(document.createElement("div"));
40777 this.view = Roo.factory(this.view);
40778 this.view.render && this.view.render(false, '');
40782 this.fireEvent('render', this);
40785 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40795 /* Resize Element - use this to work out scroll etc. */
40798 setRegion : function(region){
40799 this.region = region;
40800 this.setActiveClass(region && !this.background);
40804 setActiveClass: function(state)
40807 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40808 this.el.setStyle('position','relative');
40810 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40811 this.el.setStyle('position', 'absolute');
40816 * Returns the toolbar for this Panel if one was configured.
40817 * @return {Roo.Toolbar}
40819 getToolbar : function(){
40820 return this.toolbar;
40823 setActiveState : function(active)
40825 this.active = active;
40826 this.setActiveClass(active);
40828 if(this.fireEvent("deactivate", this) === false){
40833 this.fireEvent("activate", this);
40837 * Updates this panel's element (not for iframe)
40838 * @param {String} content The new content
40839 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40841 setContent : function(content, loadScripts){
40846 this.el.update(content, loadScripts);
40849 ignoreResize : function(w, h){
40850 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40853 this.lastSize = {width: w, height: h};
40858 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40859 * @return {Roo.UpdateManager} The UpdateManager
40861 getUpdateManager : function(){
40865 return this.el.getUpdateManager();
40868 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40869 * Does not work with IFRAME contents
40870 * @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:
40873 url: "your-url.php",
40874 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40875 callback: yourFunction,
40876 scope: yourObject, //(optional scope)
40879 text: "Loading...",
40885 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40886 * 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.
40887 * @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}
40888 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40889 * @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.
40890 * @return {Roo.ContentPanel} this
40898 var um = this.el.getUpdateManager();
40899 um.update.apply(um, arguments);
40905 * 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.
40906 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40907 * @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)
40908 * @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)
40909 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40911 setUrl : function(url, params, loadOnce){
40913 this.iframeEl.dom.src = url;
40917 if(this.refreshDelegate){
40918 this.removeListener("activate", this.refreshDelegate);
40920 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40921 this.on("activate", this.refreshDelegate);
40922 return this.el.getUpdateManager();
40925 _handleRefresh : function(url, params, loadOnce){
40926 if(!loadOnce || !this.loaded){
40927 var updater = this.el.getUpdateManager();
40928 updater.update(url, params, this._setLoaded.createDelegate(this));
40932 _setLoaded : function(){
40933 this.loaded = true;
40937 * Returns this panel's id
40940 getId : function(){
40945 * Returns this panel's element - used by regiosn to add.
40946 * @return {Roo.Element}
40948 getEl : function(){
40949 return this.wrapEl || this.el;
40954 adjustForComponents : function(width, height)
40956 //Roo.log('adjustForComponents ');
40957 if(this.resizeEl != this.el){
40958 width -= this.el.getFrameWidth('lr');
40959 height -= this.el.getFrameWidth('tb');
40962 var te = this.toolbar.getEl();
40963 te.setWidth(width);
40964 height -= te.getHeight();
40967 var te = this.footer.getEl();
40968 te.setWidth(width);
40969 height -= te.getHeight();
40973 if(this.adjustments){
40974 width += this.adjustments[0];
40975 height += this.adjustments[1];
40977 return {"width": width, "height": height};
40980 setSize : function(width, height){
40981 if(this.fitToFrame && !this.ignoreResize(width, height)){
40982 if(this.fitContainer && this.resizeEl != this.el){
40983 this.el.setSize(width, height);
40985 var size = this.adjustForComponents(width, height);
40987 this.iframeEl.setSize(width,height);
40990 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40991 this.fireEvent('resize', this, size.width, size.height);
40998 * Returns this panel's title
41001 getTitle : function(){
41003 if (typeof(this.title) != 'object') {
41008 for (var k in this.title) {
41009 if (!this.title.hasOwnProperty(k)) {
41013 if (k.indexOf('-') >= 0) {
41014 var s = k.split('-');
41015 for (var i = 0; i<s.length; i++) {
41016 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41019 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41026 * Set this panel's title
41027 * @param {String} title
41029 setTitle : function(title){
41030 this.title = title;
41032 this.region.updatePanelTitle(this, title);
41037 * Returns true is this panel was configured to be closable
41038 * @return {Boolean}
41040 isClosable : function(){
41041 return this.closable;
41044 beforeSlide : function(){
41046 this.resizeEl.clip();
41049 afterSlide : function(){
41051 this.resizeEl.unclip();
41055 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41056 * Will fail silently if the {@link #setUrl} method has not been called.
41057 * This does not activate the panel, just updates its content.
41059 refresh : function(){
41060 if(this.refreshDelegate){
41061 this.loaded = false;
41062 this.refreshDelegate();
41067 * Destroys this panel
41069 destroy : function(){
41070 this.el.removeAllListeners();
41071 var tempEl = document.createElement("span");
41072 tempEl.appendChild(this.el.dom);
41073 tempEl.innerHTML = "";
41079 * form - if the content panel contains a form - this is a reference to it.
41080 * @type {Roo.form.Form}
41084 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41085 * This contains a reference to it.
41091 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41101 * @param {Object} cfg Xtype definition of item to add.
41105 getChildContainer: function () {
41106 return this.getEl();
41110 onScroll : function(e)
41112 this.fireEvent('scroll', this, e);
41117 var ret = new Roo.factory(cfg);
41122 if (cfg.xtype.match(/^Form$/)) {
41125 //if (this.footer) {
41126 // el = this.footer.container.insertSibling(false, 'before');
41128 el = this.el.createChild();
41131 this.form = new Roo.form.Form(cfg);
41134 if ( this.form.allItems.length) {
41135 this.form.render(el.dom);
41139 // should only have one of theses..
41140 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41141 // views.. should not be just added - used named prop 'view''
41143 cfg.el = this.el.appendChild(document.createElement("div"));
41146 var ret = new Roo.factory(cfg);
41148 ret.render && ret.render(false, ''); // render blank..
41158 * @class Roo.bootstrap.panel.Grid
41159 * @extends Roo.bootstrap.panel.Content
41161 * Create a new GridPanel.
41162 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41163 * @param {Object} config A the config object
41169 Roo.bootstrap.panel.Grid = function(config)
41173 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41174 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41176 config.el = this.wrapper;
41177 //this.el = this.wrapper;
41179 if (config.container) {
41180 // ctor'ed from a Border/panel.grid
41183 this.wrapper.setStyle("overflow", "hidden");
41184 this.wrapper.addClass('roo-grid-container');
41189 if(config.toolbar){
41190 var tool_el = this.wrapper.createChild();
41191 this.toolbar = Roo.factory(config.toolbar);
41193 if (config.toolbar.items) {
41194 ti = config.toolbar.items ;
41195 delete config.toolbar.items ;
41199 this.toolbar.render(tool_el);
41200 for(var i =0;i < ti.length;i++) {
41201 // Roo.log(['add child', items[i]]);
41202 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41204 this.toolbar.items = nitems;
41206 delete config.toolbar;
41209 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41210 config.grid.scrollBody = true;;
41211 config.grid.monitorWindowResize = false; // turn off autosizing
41212 config.grid.autoHeight = false;
41213 config.grid.autoWidth = false;
41215 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41217 if (config.background) {
41218 // render grid on panel activation (if panel background)
41219 this.on('activate', function(gp) {
41220 if (!gp.grid.rendered) {
41221 gp.grid.render(this.wrapper);
41222 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41227 this.grid.render(this.wrapper);
41228 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41231 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41232 // ??? needed ??? config.el = this.wrapper;
41237 // xtype created footer. - not sure if will work as we normally have to render first..
41238 if (this.footer && !this.footer.el && this.footer.xtype) {
41240 var ctr = this.grid.getView().getFooterPanel(true);
41241 this.footer.dataSource = this.grid.dataSource;
41242 this.footer = Roo.factory(this.footer, Roo);
41243 this.footer.render(ctr);
41253 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41254 getId : function(){
41255 return this.grid.id;
41259 * Returns the grid for this panel
41260 * @return {Roo.bootstrap.Table}
41262 getGrid : function(){
41266 setSize : function(width, height){
41267 if(!this.ignoreResize(width, height)){
41268 var grid = this.grid;
41269 var size = this.adjustForComponents(width, height);
41270 // tfoot is not a footer?
41273 var gridel = grid.getGridEl();
41274 gridel.setSize(size.width, size.height);
41276 var tbd = grid.getGridEl().select('tbody', true).first();
41277 var thd = grid.getGridEl().select('thead',true).first();
41278 var tbf= grid.getGridEl().select('tfoot', true).first();
41281 size.height -= tbf.getHeight();
41284 size.height -= thd.getHeight();
41287 tbd.setSize(size.width, size.height );
41288 // this is for the account management tab -seems to work there.
41289 var thd = grid.getGridEl().select('thead',true).first();
41291 // tbd.setSize(size.width, size.height - thd.getHeight());
41300 beforeSlide : function(){
41301 this.grid.getView().scroller.clip();
41304 afterSlide : function(){
41305 this.grid.getView().scroller.unclip();
41308 destroy : function(){
41309 this.grid.destroy();
41311 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41316 * @class Roo.bootstrap.panel.Nest
41317 * @extends Roo.bootstrap.panel.Content
41319 * Create a new Panel, that can contain a layout.Border.
41322 * @param {Roo.BorderLayout} layout The layout for this panel
41323 * @param {String/Object} config A string to set only the title or a config object
41325 Roo.bootstrap.panel.Nest = function(config)
41327 // construct with only one argument..
41328 /* FIXME - implement nicer consturctors
41329 if (layout.layout) {
41331 layout = config.layout;
41332 delete config.layout;
41334 if (layout.xtype && !layout.getEl) {
41335 // then layout needs constructing..
41336 layout = Roo.factory(layout, Roo);
41340 config.el = config.layout.getEl();
41342 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41344 config.layout.monitorWindowResize = false; // turn off autosizing
41345 this.layout = config.layout;
41346 this.layout.getEl().addClass("roo-layout-nested-layout");
41347 this.layout.parent = this;
41354 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41356 setSize : function(width, height){
41357 if(!this.ignoreResize(width, height)){
41358 var size = this.adjustForComponents(width, height);
41359 var el = this.layout.getEl();
41360 if (size.height < 1) {
41361 el.setWidth(size.width);
41363 el.setSize(size.width, size.height);
41365 var touch = el.dom.offsetWidth;
41366 this.layout.layout();
41367 // ie requires a double layout on the first pass
41368 if(Roo.isIE && !this.initialized){
41369 this.initialized = true;
41370 this.layout.layout();
41375 // activate all subpanels if not currently active..
41377 setActiveState : function(active){
41378 this.active = active;
41379 this.setActiveClass(active);
41382 this.fireEvent("deactivate", this);
41386 this.fireEvent("activate", this);
41387 // not sure if this should happen before or after..
41388 if (!this.layout) {
41389 return; // should not happen..
41392 for (var r in this.layout.regions) {
41393 reg = this.layout.getRegion(r);
41394 if (reg.getActivePanel()) {
41395 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41396 reg.setActivePanel(reg.getActivePanel());
41399 if (!reg.panels.length) {
41402 reg.showPanel(reg.getPanel(0));
41411 * Returns the nested BorderLayout for this panel
41412 * @return {Roo.BorderLayout}
41414 getLayout : function(){
41415 return this.layout;
41419 * Adds a xtype elements to the layout of the nested panel
41423 xtype : 'ContentPanel',
41430 xtype : 'NestedLayoutPanel',
41436 items : [ ... list of content panels or nested layout panels.. ]
41440 * @param {Object} cfg Xtype definition of item to add.
41442 addxtype : function(cfg) {
41443 return this.layout.addxtype(cfg);
41448 * Ext JS Library 1.1.1
41449 * Copyright(c) 2006-2007, Ext JS, LLC.
41451 * Originally Released Under LGPL - original licence link has changed is not relivant.
41454 * <script type="text/javascript">
41457 * @class Roo.TabPanel
41458 * @extends Roo.util.Observable
41459 * A lightweight tab container.
41463 // basic tabs 1, built from existing content
41464 var tabs = new Roo.TabPanel("tabs1");
41465 tabs.addTab("script", "View Script");
41466 tabs.addTab("markup", "View Markup");
41467 tabs.activate("script");
41469 // more advanced tabs, built from javascript
41470 var jtabs = new Roo.TabPanel("jtabs");
41471 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41473 // set up the UpdateManager
41474 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41475 var updater = tab2.getUpdateManager();
41476 updater.setDefaultUrl("ajax1.htm");
41477 tab2.on('activate', updater.refresh, updater, true);
41479 // Use setUrl for Ajax loading
41480 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41481 tab3.setUrl("ajax2.htm", null, true);
41484 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41487 jtabs.activate("jtabs-1");
41490 * Create a new TabPanel.
41491 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41492 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41494 Roo.bootstrap.panel.Tabs = function(config){
41496 * The container element for this TabPanel.
41497 * @type Roo.Element
41499 this.el = Roo.get(config.el);
41502 if(typeof config == "boolean"){
41503 this.tabPosition = config ? "bottom" : "top";
41505 Roo.apply(this, config);
41509 if(this.tabPosition == "bottom"){
41510 // if tabs are at the bottom = create the body first.
41511 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41512 this.el.addClass("roo-tabs-bottom");
41514 // next create the tabs holders
41516 if (this.tabPosition == "west"){
41518 var reg = this.region; // fake it..
41520 if (!reg.mgr.parent) {
41523 reg = reg.mgr.parent.region;
41525 Roo.log("got nest?");
41527 if (reg.mgr.getRegion('west')) {
41528 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41529 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41530 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41531 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41532 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41540 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41541 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41542 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41543 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41548 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41551 // finally - if tabs are at the top, then create the body last..
41552 if(this.tabPosition != "bottom"){
41553 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41554 * @type Roo.Element
41556 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41557 this.el.addClass("roo-tabs-top");
41561 this.bodyEl.setStyle("position", "relative");
41563 this.active = null;
41564 this.activateDelegate = this.activate.createDelegate(this);
41569 * Fires when the active tab changes
41570 * @param {Roo.TabPanel} this
41571 * @param {Roo.TabPanelItem} activePanel The new active tab
41575 * @event beforetabchange
41576 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41577 * @param {Roo.TabPanel} this
41578 * @param {Object} e Set cancel to true on this object to cancel the tab change
41579 * @param {Roo.TabPanelItem} tab The tab being changed to
41581 "beforetabchange" : true
41584 Roo.EventManager.onWindowResize(this.onResize, this);
41585 this.cpad = this.el.getPadding("lr");
41586 this.hiddenCount = 0;
41589 // toolbar on the tabbar support...
41590 if (this.toolbar) {
41591 alert("no toolbar support yet");
41592 this.toolbar = false;
41594 var tcfg = this.toolbar;
41595 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41596 this.toolbar = new Roo.Toolbar(tcfg);
41597 if (Roo.isSafari) {
41598 var tbl = tcfg.container.child('table', true);
41599 tbl.setAttribute('width', '100%');
41607 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41610 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41612 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41614 tabPosition : "top",
41616 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41618 currentTabWidth : 0,
41620 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41624 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41628 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41630 preferredTabWidth : 175,
41632 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41634 resizeTabs : false,
41636 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41638 monitorResize : true,
41640 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41642 toolbar : false, // set by caller..
41644 region : false, /// set by caller
41646 disableTooltips : true, // not used yet...
41649 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41650 * @param {String} id The id of the div to use <b>or create</b>
41651 * @param {String} text The text for the tab
41652 * @param {String} content (optional) Content to put in the TabPanelItem body
41653 * @param {Boolean} closable (optional) True to create a close icon on the tab
41654 * @return {Roo.TabPanelItem} The created TabPanelItem
41656 addTab : function(id, text, content, closable, tpl)
41658 var item = new Roo.bootstrap.panel.TabItem({
41662 closable : closable,
41665 this.addTabItem(item);
41667 item.setContent(content);
41673 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41674 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41675 * @return {Roo.TabPanelItem}
41677 getTab : function(id){
41678 return this.items[id];
41682 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41683 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41685 hideTab : function(id){
41686 var t = this.items[id];
41689 this.hiddenCount++;
41690 this.autoSizeTabs();
41695 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41696 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41698 unhideTab : function(id){
41699 var t = this.items[id];
41701 t.setHidden(false);
41702 this.hiddenCount--;
41703 this.autoSizeTabs();
41708 * Adds an existing {@link Roo.TabPanelItem}.
41709 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41711 addTabItem : function(item)
41713 this.items[item.id] = item;
41714 this.items.push(item);
41715 this.autoSizeTabs();
41716 // if(this.resizeTabs){
41717 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41718 // this.autoSizeTabs();
41720 // item.autoSize();
41725 * Removes a {@link Roo.TabPanelItem}.
41726 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41728 removeTab : function(id){
41729 var items = this.items;
41730 var tab = items[id];
41731 if(!tab) { return; }
41732 var index = items.indexOf(tab);
41733 if(this.active == tab && items.length > 1){
41734 var newTab = this.getNextAvailable(index);
41739 this.stripEl.dom.removeChild(tab.pnode.dom);
41740 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41741 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41743 items.splice(index, 1);
41744 delete this.items[tab.id];
41745 tab.fireEvent("close", tab);
41746 tab.purgeListeners();
41747 this.autoSizeTabs();
41750 getNextAvailable : function(start){
41751 var items = this.items;
41753 // look for a next tab that will slide over to
41754 // replace the one being removed
41755 while(index < items.length){
41756 var item = items[++index];
41757 if(item && !item.isHidden()){
41761 // if one isn't found select the previous tab (on the left)
41764 var item = items[--index];
41765 if(item && !item.isHidden()){
41773 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41774 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41776 disableTab : function(id){
41777 var tab = this.items[id];
41778 if(tab && this.active != tab){
41784 * Enables a {@link Roo.TabPanelItem} that is disabled.
41785 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41787 enableTab : function(id){
41788 var tab = this.items[id];
41793 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41794 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41795 * @return {Roo.TabPanelItem} The TabPanelItem.
41797 activate : function(id)
41799 //Roo.log('activite:' + id);
41801 var tab = this.items[id];
41805 if(tab == this.active || tab.disabled){
41809 this.fireEvent("beforetabchange", this, e, tab);
41810 if(e.cancel !== true && !tab.disabled){
41812 this.active.hide();
41814 this.active = this.items[id];
41815 this.active.show();
41816 this.fireEvent("tabchange", this, this.active);
41822 * Gets the active {@link Roo.TabPanelItem}.
41823 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41825 getActiveTab : function(){
41826 return this.active;
41830 * Updates the tab body element to fit the height of the container element
41831 * for overflow scrolling
41832 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41834 syncHeight : function(targetHeight){
41835 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41836 var bm = this.bodyEl.getMargins();
41837 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41838 this.bodyEl.setHeight(newHeight);
41842 onResize : function(){
41843 if(this.monitorResize){
41844 this.autoSizeTabs();
41849 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41851 beginUpdate : function(){
41852 this.updating = true;
41856 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41858 endUpdate : function(){
41859 this.updating = false;
41860 this.autoSizeTabs();
41864 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41866 autoSizeTabs : function()
41868 var count = this.items.length;
41869 var vcount = count - this.hiddenCount;
41872 this.stripEl.hide();
41874 this.stripEl.show();
41877 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41882 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41883 var availWidth = Math.floor(w / vcount);
41884 var b = this.stripBody;
41885 if(b.getWidth() > w){
41886 var tabs = this.items;
41887 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41888 if(availWidth < this.minTabWidth){
41889 /*if(!this.sleft){ // incomplete scrolling code
41890 this.createScrollButtons();
41893 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41896 if(this.currentTabWidth < this.preferredTabWidth){
41897 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41903 * Returns the number of tabs in this TabPanel.
41906 getCount : function(){
41907 return this.items.length;
41911 * Resizes all the tabs to the passed width
41912 * @param {Number} The new width
41914 setTabWidth : function(width){
41915 this.currentTabWidth = width;
41916 for(var i = 0, len = this.items.length; i < len; i++) {
41917 if(!this.items[i].isHidden()) {
41918 this.items[i].setWidth(width);
41924 * Destroys this TabPanel
41925 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41927 destroy : function(removeEl){
41928 Roo.EventManager.removeResizeListener(this.onResize, this);
41929 for(var i = 0, len = this.items.length; i < len; i++){
41930 this.items[i].purgeListeners();
41932 if(removeEl === true){
41933 this.el.update("");
41938 createStrip : function(container)
41940 var strip = document.createElement("nav");
41941 strip.className = Roo.bootstrap.version == 4 ?
41942 "navbar-light bg-light" :
41943 "navbar navbar-default"; //"x-tabs-wrap";
41944 container.appendChild(strip);
41948 createStripList : function(strip)
41950 // div wrapper for retard IE
41951 // returns the "tr" element.
41952 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41953 //'<div class="x-tabs-strip-wrap">'+
41954 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41955 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41956 return strip.firstChild; //.firstChild.firstChild.firstChild;
41958 createBody : function(container)
41960 var body = document.createElement("div");
41961 Roo.id(body, "tab-body");
41962 //Roo.fly(body).addClass("x-tabs-body");
41963 Roo.fly(body).addClass("tab-content");
41964 container.appendChild(body);
41967 createItemBody :function(bodyEl, id){
41968 var body = Roo.getDom(id);
41970 body = document.createElement("div");
41973 //Roo.fly(body).addClass("x-tabs-item-body");
41974 Roo.fly(body).addClass("tab-pane");
41975 bodyEl.insertBefore(body, bodyEl.firstChild);
41979 createStripElements : function(stripEl, text, closable, tpl)
41981 var td = document.createElement("li"); // was td..
41982 td.className = 'nav-item';
41984 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41987 stripEl.appendChild(td);
41989 td.className = "x-tabs-closable";
41990 if(!this.closeTpl){
41991 this.closeTpl = new Roo.Template(
41992 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41993 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41994 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41997 var el = this.closeTpl.overwrite(td, {"text": text});
41998 var close = el.getElementsByTagName("div")[0];
41999 var inner = el.getElementsByTagName("em")[0];
42000 return {"el": el, "close": close, "inner": inner};
42003 // not sure what this is..
42004 // if(!this.tabTpl){
42005 //this.tabTpl = new Roo.Template(
42006 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42007 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42009 // this.tabTpl = new Roo.Template(
42010 // '<a href="#">' +
42011 // '<span unselectable="on"' +
42012 // (this.disableTooltips ? '' : ' title="{text}"') +
42013 // ' >{text}</span></a>'
42019 var template = tpl || this.tabTpl || false;
42022 template = new Roo.Template(
42023 Roo.bootstrap.version == 4 ?
42025 '<a class="nav-link" href="#" unselectable="on"' +
42026 (this.disableTooltips ? '' : ' title="{text}"') +
42029 '<a class="nav-link" href="#">' +
42030 '<span unselectable="on"' +
42031 (this.disableTooltips ? '' : ' title="{text}"') +
42032 ' >{text}</span></a>'
42037 switch (typeof(template)) {
42041 template = new Roo.Template(template);
42047 var el = template.overwrite(td, {"text": text});
42049 var inner = el.getElementsByTagName("span")[0];
42051 return {"el": el, "inner": inner};
42059 * @class Roo.TabPanelItem
42060 * @extends Roo.util.Observable
42061 * Represents an individual item (tab plus body) in a TabPanel.
42062 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42063 * @param {String} id The id of this TabPanelItem
42064 * @param {String} text The text for the tab of this TabPanelItem
42065 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42067 Roo.bootstrap.panel.TabItem = function(config){
42069 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42070 * @type Roo.TabPanel
42072 this.tabPanel = config.panel;
42074 * The id for this TabPanelItem
42077 this.id = config.id;
42079 this.disabled = false;
42081 this.text = config.text;
42083 this.loaded = false;
42084 this.closable = config.closable;
42087 * The body element for this TabPanelItem.
42088 * @type Roo.Element
42090 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42091 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42092 this.bodyEl.setStyle("display", "block");
42093 this.bodyEl.setStyle("zoom", "1");
42094 //this.hideAction();
42096 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42098 this.el = Roo.get(els.el);
42099 this.inner = Roo.get(els.inner, true);
42100 this.textEl = Roo.bootstrap.version == 4 ?
42101 this.el : Roo.get(this.el.dom.firstChild, true);
42103 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42104 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42107 // this.el.on("mousedown", this.onTabMouseDown, this);
42108 this.el.on("click", this.onTabClick, this);
42110 if(config.closable){
42111 var c = Roo.get(els.close, true);
42112 c.dom.title = this.closeText;
42113 c.addClassOnOver("close-over");
42114 c.on("click", this.closeClick, this);
42120 * Fires when this tab becomes the active tab.
42121 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42122 * @param {Roo.TabPanelItem} this
42126 * @event beforeclose
42127 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42128 * @param {Roo.TabPanelItem} this
42129 * @param {Object} e Set cancel to true on this object to cancel the close.
42131 "beforeclose": true,
42134 * Fires when this tab is closed.
42135 * @param {Roo.TabPanelItem} this
42139 * @event deactivate
42140 * Fires when this tab is no longer the active tab.
42141 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42142 * @param {Roo.TabPanelItem} this
42144 "deactivate" : true
42146 this.hidden = false;
42148 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42151 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42153 purgeListeners : function(){
42154 Roo.util.Observable.prototype.purgeListeners.call(this);
42155 this.el.removeAllListeners();
42158 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42161 this.status_node.addClass("active");
42164 this.tabPanel.stripWrap.repaint();
42166 this.fireEvent("activate", this.tabPanel, this);
42170 * Returns true if this tab is the active tab.
42171 * @return {Boolean}
42173 isActive : function(){
42174 return this.tabPanel.getActiveTab() == this;
42178 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42181 this.status_node.removeClass("active");
42183 this.fireEvent("deactivate", this.tabPanel, this);
42186 hideAction : function(){
42187 this.bodyEl.hide();
42188 this.bodyEl.setStyle("position", "absolute");
42189 this.bodyEl.setLeft("-20000px");
42190 this.bodyEl.setTop("-20000px");
42193 showAction : function(){
42194 this.bodyEl.setStyle("position", "relative");
42195 this.bodyEl.setTop("");
42196 this.bodyEl.setLeft("");
42197 this.bodyEl.show();
42201 * Set the tooltip for the tab.
42202 * @param {String} tooltip The tab's tooltip
42204 setTooltip : function(text){
42205 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42206 this.textEl.dom.qtip = text;
42207 this.textEl.dom.removeAttribute('title');
42209 this.textEl.dom.title = text;
42213 onTabClick : function(e){
42214 e.preventDefault();
42215 this.tabPanel.activate(this.id);
42218 onTabMouseDown : function(e){
42219 e.preventDefault();
42220 this.tabPanel.activate(this.id);
42223 getWidth : function(){
42224 return this.inner.getWidth();
42227 setWidth : function(width){
42228 var iwidth = width - this.linode.getPadding("lr");
42229 this.inner.setWidth(iwidth);
42230 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42231 this.linode.setWidth(width);
42235 * Show or hide the tab
42236 * @param {Boolean} hidden True to hide or false to show.
42238 setHidden : function(hidden){
42239 this.hidden = hidden;
42240 this.linode.setStyle("display", hidden ? "none" : "");
42244 * Returns true if this tab is "hidden"
42245 * @return {Boolean}
42247 isHidden : function(){
42248 return this.hidden;
42252 * Returns the text for this tab
42255 getText : function(){
42259 autoSize : function(){
42260 //this.el.beginMeasure();
42261 this.textEl.setWidth(1);
42263 * #2804 [new] Tabs in Roojs
42264 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42266 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42267 //this.el.endMeasure();
42271 * Sets the text for the tab (Note: this also sets the tooltip text)
42272 * @param {String} text The tab's text and tooltip
42274 setText : function(text){
42276 this.textEl.update(text);
42277 this.setTooltip(text);
42278 //if(!this.tabPanel.resizeTabs){
42279 // this.autoSize();
42283 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42285 activate : function(){
42286 this.tabPanel.activate(this.id);
42290 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42292 disable : function(){
42293 if(this.tabPanel.active != this){
42294 this.disabled = true;
42295 this.status_node.addClass("disabled");
42300 * Enables this TabPanelItem if it was previously disabled.
42302 enable : function(){
42303 this.disabled = false;
42304 this.status_node.removeClass("disabled");
42308 * Sets the content for this TabPanelItem.
42309 * @param {String} content The content
42310 * @param {Boolean} loadScripts true to look for and load scripts
42312 setContent : function(content, loadScripts){
42313 this.bodyEl.update(content, loadScripts);
42317 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42318 * @return {Roo.UpdateManager} The UpdateManager
42320 getUpdateManager : function(){
42321 return this.bodyEl.getUpdateManager();
42325 * Set a URL to be used to load the content for this TabPanelItem.
42326 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42327 * @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)
42328 * @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)
42329 * @return {Roo.UpdateManager} The UpdateManager
42331 setUrl : function(url, params, loadOnce){
42332 if(this.refreshDelegate){
42333 this.un('activate', this.refreshDelegate);
42335 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42336 this.on("activate", this.refreshDelegate);
42337 return this.bodyEl.getUpdateManager();
42341 _handleRefresh : function(url, params, loadOnce){
42342 if(!loadOnce || !this.loaded){
42343 var updater = this.bodyEl.getUpdateManager();
42344 updater.update(url, params, this._setLoaded.createDelegate(this));
42349 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42350 * Will fail silently if the setUrl method has not been called.
42351 * This does not activate the panel, just updates its content.
42353 refresh : function(){
42354 if(this.refreshDelegate){
42355 this.loaded = false;
42356 this.refreshDelegate();
42361 _setLoaded : function(){
42362 this.loaded = true;
42366 closeClick : function(e){
42369 this.fireEvent("beforeclose", this, o);
42370 if(o.cancel !== true){
42371 this.tabPanel.removeTab(this.id);
42375 * The text displayed in the tooltip for the close icon.
42378 closeText : "Close this tab"
42381 * This script refer to:
42382 * Title: International Telephone Input
42383 * Author: Jack O'Connor
42384 * Code version: v12.1.12
42385 * Availability: https://github.com/jackocnr/intl-tel-input.git
42388 Roo.bootstrap.PhoneInputData = function() {
42391 "Afghanistan (افغانستان)",
42396 "Albania (Shqipëri)",
42401 "Algeria (الجزائر)",
42426 "Antigua and Barbuda",
42436 "Armenia (Հայաստան)",
42452 "Austria (Österreich)",
42457 "Azerbaijan (Azərbaycan)",
42467 "Bahrain (البحرين)",
42472 "Bangladesh (বাংলাদেশ)",
42482 "Belarus (Беларусь)",
42487 "Belgium (België)",
42517 "Bosnia and Herzegovina (Босна и Херцеговина)",
42532 "British Indian Ocean Territory",
42537 "British Virgin Islands",
42547 "Bulgaria (България)",
42557 "Burundi (Uburundi)",
42562 "Cambodia (កម្ពុជា)",
42567 "Cameroon (Cameroun)",
42576 ["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"]
42579 "Cape Verde (Kabu Verdi)",
42584 "Caribbean Netherlands",
42595 "Central African Republic (République centrafricaine)",
42615 "Christmas Island",
42621 "Cocos (Keeling) Islands",
42632 "Comoros (جزر القمر)",
42637 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42642 "Congo (Republic) (Congo-Brazzaville)",
42662 "Croatia (Hrvatska)",
42683 "Czech Republic (Česká republika)",
42688 "Denmark (Danmark)",
42703 "Dominican Republic (República Dominicana)",
42707 ["809", "829", "849"]
42725 "Equatorial Guinea (Guinea Ecuatorial)",
42745 "Falkland Islands (Islas Malvinas)",
42750 "Faroe Islands (Føroyar)",
42771 "French Guiana (Guyane française)",
42776 "French Polynesia (Polynésie française)",
42791 "Georgia (საქართველო)",
42796 "Germany (Deutschland)",
42816 "Greenland (Kalaallit Nunaat)",
42853 "Guinea-Bissau (Guiné Bissau)",
42878 "Hungary (Magyarország)",
42883 "Iceland (Ísland)",
42903 "Iraq (العراق)",
42919 "Israel (ישראל)",
42946 "Jordan (الأردن)",
42951 "Kazakhstan (Казахстан)",
42972 "Kuwait (الكويت)",
42977 "Kyrgyzstan (Кыргызстан)",
42987 "Latvia (Latvija)",
42992 "Lebanon (لبنان)",
43007 "Libya (ليبيا)",
43017 "Lithuania (Lietuva)",
43032 "Macedonia (FYROM) (Македонија)",
43037 "Madagascar (Madagasikara)",
43067 "Marshall Islands",
43077 "Mauritania (موريتانيا)",
43082 "Mauritius (Moris)",
43103 "Moldova (Republica Moldova)",
43113 "Mongolia (Монгол)",
43118 "Montenegro (Crna Gora)",
43128 "Morocco (المغرب)",
43134 "Mozambique (Moçambique)",
43139 "Myanmar (Burma) (မြန်မာ)",
43144 "Namibia (Namibië)",
43159 "Netherlands (Nederland)",
43164 "New Caledonia (Nouvelle-Calédonie)",
43199 "North Korea (조선 민주주의 인민 공화국)",
43204 "Northern Mariana Islands",
43220 "Pakistan (پاکستان)",
43230 "Palestine (فلسطين)",
43240 "Papua New Guinea",
43282 "Réunion (La Réunion)",
43288 "Romania (România)",
43304 "Saint Barthélemy",
43315 "Saint Kitts and Nevis",
43325 "Saint Martin (Saint-Martin (partie française))",
43331 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43336 "Saint Vincent and the Grenadines",
43351 "São Tomé and Príncipe (São Tomé e Príncipe)",
43356 "Saudi Arabia (المملكة العربية السعودية)",
43361 "Senegal (Sénégal)",
43391 "Slovakia (Slovensko)",
43396 "Slovenia (Slovenija)",
43406 "Somalia (Soomaaliya)",
43416 "South Korea (대한민국)",
43421 "South Sudan (جنوب السودان)",
43431 "Sri Lanka (ශ්රී ලංකාව)",
43436 "Sudan (السودان)",
43446 "Svalbard and Jan Mayen",
43457 "Sweden (Sverige)",
43462 "Switzerland (Schweiz)",
43467 "Syria (سوريا)",
43512 "Trinidad and Tobago",
43517 "Tunisia (تونس)",
43522 "Turkey (Türkiye)",
43532 "Turks and Caicos Islands",
43542 "U.S. Virgin Islands",
43552 "Ukraine (Україна)",
43557 "United Arab Emirates (الإمارات العربية المتحدة)",
43579 "Uzbekistan (Oʻzbekiston)",
43589 "Vatican City (Città del Vaticano)",
43600 "Vietnam (Việt Nam)",
43605 "Wallis and Futuna (Wallis-et-Futuna)",
43610 "Western Sahara (الصحراء الغربية)",
43616 "Yemen (اليمن)",
43640 * This script refer to:
43641 * Title: International Telephone Input
43642 * Author: Jack O'Connor
43643 * Code version: v12.1.12
43644 * Availability: https://github.com/jackocnr/intl-tel-input.git
43648 * @class Roo.bootstrap.PhoneInput
43649 * @extends Roo.bootstrap.TriggerField
43650 * An input with International dial-code selection
43652 * @cfg {String} defaultDialCode default '+852'
43653 * @cfg {Array} preferedCountries default []
43656 * Create a new PhoneInput.
43657 * @param {Object} config Configuration options
43660 Roo.bootstrap.PhoneInput = function(config) {
43661 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43664 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43666 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43668 listWidth: undefined,
43670 selectedClass: 'active',
43672 invalidClass : "has-warning",
43674 validClass: 'has-success',
43676 allowed: '0123456789',
43681 * @cfg {String} defaultDialCode The default dial code when initializing the input
43683 defaultDialCode: '+852',
43686 * @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
43688 preferedCountries: false,
43690 getAutoCreate : function()
43692 var data = Roo.bootstrap.PhoneInputData();
43693 var align = this.labelAlign || this.parentLabelAlign();
43696 this.allCountries = [];
43697 this.dialCodeMapping = [];
43699 for (var i = 0; i < data.length; i++) {
43701 this.allCountries[i] = {
43705 priority: c[3] || 0,
43706 areaCodes: c[4] || null
43708 this.dialCodeMapping[c[2]] = {
43711 priority: c[3] || 0,
43712 areaCodes: c[4] || null
43724 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43725 maxlength: this.max_length,
43726 cls : 'form-control tel-input',
43727 autocomplete: 'new-password'
43730 var hiddenInput = {
43733 cls: 'hidden-tel-input'
43737 hiddenInput.name = this.name;
43740 if (this.disabled) {
43741 input.disabled = true;
43744 var flag_container = {
43761 cls: this.hasFeedback ? 'has-feedback' : '',
43767 cls: 'dial-code-holder',
43774 cls: 'roo-select2-container input-group',
43781 if (this.fieldLabel.length) {
43784 tooltip: 'This field is required'
43790 cls: 'control-label',
43796 html: this.fieldLabel
43799 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43805 if(this.indicatorpos == 'right') {
43806 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43813 if(align == 'left') {
43821 if(this.labelWidth > 12){
43822 label.style = "width: " + this.labelWidth + 'px';
43824 if(this.labelWidth < 13 && this.labelmd == 0){
43825 this.labelmd = this.labelWidth;
43827 if(this.labellg > 0){
43828 label.cls += ' col-lg-' + this.labellg;
43829 input.cls += ' col-lg-' + (12 - this.labellg);
43831 if(this.labelmd > 0){
43832 label.cls += ' col-md-' + this.labelmd;
43833 container.cls += ' col-md-' + (12 - this.labelmd);
43835 if(this.labelsm > 0){
43836 label.cls += ' col-sm-' + this.labelsm;
43837 container.cls += ' col-sm-' + (12 - this.labelsm);
43839 if(this.labelxs > 0){
43840 label.cls += ' col-xs-' + this.labelxs;
43841 container.cls += ' col-xs-' + (12 - this.labelxs);
43851 var settings = this;
43853 ['xs','sm','md','lg'].map(function(size){
43854 if (settings[size]) {
43855 cfg.cls += ' col-' + size + '-' + settings[size];
43859 this.store = new Roo.data.Store({
43860 proxy : new Roo.data.MemoryProxy({}),
43861 reader : new Roo.data.JsonReader({
43872 'name' : 'dialCode',
43876 'name' : 'priority',
43880 'name' : 'areaCodes',
43887 if(!this.preferedCountries) {
43888 this.preferedCountries = [
43895 var p = this.preferedCountries.reverse();
43898 for (var i = 0; i < p.length; i++) {
43899 for (var j = 0; j < this.allCountries.length; j++) {
43900 if(this.allCountries[j].iso2 == p[i]) {
43901 var t = this.allCountries[j];
43902 this.allCountries.splice(j,1);
43903 this.allCountries.unshift(t);
43909 this.store.proxy.data = {
43911 data: this.allCountries
43917 initEvents : function()
43920 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43922 this.indicator = this.indicatorEl();
43923 this.flag = this.flagEl();
43924 this.dialCodeHolder = this.dialCodeHolderEl();
43926 this.trigger = this.el.select('div.flag-box',true).first();
43927 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43932 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43933 _this.list.setWidth(lw);
43936 this.list.on('mouseover', this.onViewOver, this);
43937 this.list.on('mousemove', this.onViewMove, this);
43938 this.inputEl().on("keyup", this.onKeyUp, this);
43939 this.inputEl().on("keypress", this.onKeyPress, this);
43941 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43943 this.view = new Roo.View(this.list, this.tpl, {
43944 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43947 this.view.on('click', this.onViewClick, this);
43948 this.setValue(this.defaultDialCode);
43951 onTriggerClick : function(e)
43953 Roo.log('trigger click');
43958 if(this.isExpanded()){
43960 this.hasFocus = false;
43962 this.store.load({});
43963 this.hasFocus = true;
43968 isExpanded : function()
43970 return this.list.isVisible();
43973 collapse : function()
43975 if(!this.isExpanded()){
43979 Roo.get(document).un('mousedown', this.collapseIf, this);
43980 Roo.get(document).un('mousewheel', this.collapseIf, this);
43981 this.fireEvent('collapse', this);
43985 expand : function()
43989 if(this.isExpanded() || !this.hasFocus){
43993 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43994 this.list.setWidth(lw);
43997 this.restrictHeight();
43999 Roo.get(document).on('mousedown', this.collapseIf, this);
44000 Roo.get(document).on('mousewheel', this.collapseIf, this);
44002 this.fireEvent('expand', this);
44005 restrictHeight : function()
44007 this.list.alignTo(this.inputEl(), this.listAlign);
44008 this.list.alignTo(this.inputEl(), this.listAlign);
44011 onViewOver : function(e, t)
44013 if(this.inKeyMode){
44016 var item = this.view.findItemFromChild(t);
44019 var index = this.view.indexOf(item);
44020 this.select(index, false);
44025 onViewClick : function(view, doFocus, el, e)
44027 var index = this.view.getSelectedIndexes()[0];
44029 var r = this.store.getAt(index);
44032 this.onSelect(r, index);
44034 if(doFocus !== false && !this.blockFocus){
44035 this.inputEl().focus();
44039 onViewMove : function(e, t)
44041 this.inKeyMode = false;
44044 select : function(index, scrollIntoView)
44046 this.selectedIndex = index;
44047 this.view.select(index);
44048 if(scrollIntoView !== false){
44049 var el = this.view.getNode(index);
44051 this.list.scrollChildIntoView(el, false);
44056 createList : function()
44058 this.list = Roo.get(document.body).createChild({
44060 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44061 style: 'display:none'
44064 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44067 collapseIf : function(e)
44069 var in_combo = e.within(this.el);
44070 var in_list = e.within(this.list);
44071 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44073 if (in_combo || in_list || is_list) {
44079 onSelect : function(record, index)
44081 if(this.fireEvent('beforeselect', this, record, index) !== false){
44083 this.setFlagClass(record.data.iso2);
44084 this.setDialCode(record.data.dialCode);
44085 this.hasFocus = false;
44087 this.fireEvent('select', this, record, index);
44091 flagEl : function()
44093 var flag = this.el.select('div.flag',true).first();
44100 dialCodeHolderEl : function()
44102 var d = this.el.select('input.dial-code-holder',true).first();
44109 setDialCode : function(v)
44111 this.dialCodeHolder.dom.value = '+'+v;
44114 setFlagClass : function(n)
44116 this.flag.dom.className = 'flag '+n;
44119 getValue : function()
44121 var v = this.inputEl().getValue();
44122 if(this.dialCodeHolder) {
44123 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44128 setValue : function(v)
44130 var d = this.getDialCode(v);
44132 //invalid dial code
44133 if(v.length == 0 || !d || d.length == 0) {
44135 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44136 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44142 this.setFlagClass(this.dialCodeMapping[d].iso2);
44143 this.setDialCode(d);
44144 this.inputEl().dom.value = v.replace('+'+d,'');
44145 this.hiddenEl().dom.value = this.getValue();
44150 getDialCode : function(v)
44154 if (v.length == 0) {
44155 return this.dialCodeHolder.dom.value;
44159 if (v.charAt(0) != "+") {
44162 var numericChars = "";
44163 for (var i = 1; i < v.length; i++) {
44164 var c = v.charAt(i);
44167 if (this.dialCodeMapping[numericChars]) {
44168 dialCode = v.substr(1, i);
44170 if (numericChars.length == 4) {
44180 this.setValue(this.defaultDialCode);
44184 hiddenEl : function()
44186 return this.el.select('input.hidden-tel-input',true).first();
44189 // after setting val
44190 onKeyUp : function(e){
44191 this.setValue(this.getValue());
44194 onKeyPress : function(e){
44195 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44202 * @class Roo.bootstrap.MoneyField
44203 * @extends Roo.bootstrap.ComboBox
44204 * Bootstrap MoneyField class
44207 * Create a new MoneyField.
44208 * @param {Object} config Configuration options
44211 Roo.bootstrap.MoneyField = function(config) {
44213 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44217 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44220 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44222 allowDecimals : true,
44224 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44226 decimalSeparator : ".",
44228 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44230 decimalPrecision : 0,
44232 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44234 allowNegative : true,
44236 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44240 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44242 minValue : Number.NEGATIVE_INFINITY,
44244 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44246 maxValue : Number.MAX_VALUE,
44248 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44250 minText : "The minimum value for this field is {0}",
44252 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44254 maxText : "The maximum value for this field is {0}",
44256 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44257 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44259 nanText : "{0} is not a valid number",
44261 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44265 * @cfg {String} defaults currency of the MoneyField
44266 * value should be in lkey
44268 defaultCurrency : false,
44270 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44272 thousandsDelimiter : false,
44274 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44285 getAutoCreate : function()
44287 var align = this.labelAlign || this.parentLabelAlign();
44299 cls : 'form-control roo-money-amount-input',
44300 autocomplete: 'new-password'
44303 var hiddenInput = {
44307 cls: 'hidden-number-input'
44310 if(this.max_length) {
44311 input.maxlength = this.max_length;
44315 hiddenInput.name = this.name;
44318 if (this.disabled) {
44319 input.disabled = true;
44322 var clg = 12 - this.inputlg;
44323 var cmd = 12 - this.inputmd;
44324 var csm = 12 - this.inputsm;
44325 var cxs = 12 - this.inputxs;
44329 cls : 'row roo-money-field',
44333 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44337 cls: 'roo-select2-container input-group',
44341 cls : 'form-control roo-money-currency-input',
44342 autocomplete: 'new-password',
44344 name : this.currencyName
44348 cls : 'input-group-addon',
44362 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44366 cls: this.hasFeedback ? 'has-feedback' : '',
44377 if (this.fieldLabel.length) {
44380 tooltip: 'This field is required'
44386 cls: 'control-label',
44392 html: this.fieldLabel
44395 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44401 if(this.indicatorpos == 'right') {
44402 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44409 if(align == 'left') {
44417 if(this.labelWidth > 12){
44418 label.style = "width: " + this.labelWidth + 'px';
44420 if(this.labelWidth < 13 && this.labelmd == 0){
44421 this.labelmd = this.labelWidth;
44423 if(this.labellg > 0){
44424 label.cls += ' col-lg-' + this.labellg;
44425 input.cls += ' col-lg-' + (12 - this.labellg);
44427 if(this.labelmd > 0){
44428 label.cls += ' col-md-' + this.labelmd;
44429 container.cls += ' col-md-' + (12 - this.labelmd);
44431 if(this.labelsm > 0){
44432 label.cls += ' col-sm-' + this.labelsm;
44433 container.cls += ' col-sm-' + (12 - this.labelsm);
44435 if(this.labelxs > 0){
44436 label.cls += ' col-xs-' + this.labelxs;
44437 container.cls += ' col-xs-' + (12 - this.labelxs);
44448 var settings = this;
44450 ['xs','sm','md','lg'].map(function(size){
44451 if (settings[size]) {
44452 cfg.cls += ' col-' + size + '-' + settings[size];
44459 initEvents : function()
44461 this.indicator = this.indicatorEl();
44463 this.initCurrencyEvent();
44465 this.initNumberEvent();
44468 initCurrencyEvent : function()
44471 throw "can not find store for combo";
44474 this.store = Roo.factory(this.store, Roo.data);
44475 this.store.parent = this;
44479 this.triggerEl = this.el.select('.input-group-addon', true).first();
44481 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44486 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44487 _this.list.setWidth(lw);
44490 this.list.on('mouseover', this.onViewOver, this);
44491 this.list.on('mousemove', this.onViewMove, this);
44492 this.list.on('scroll', this.onViewScroll, this);
44495 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44498 this.view = new Roo.View(this.list, this.tpl, {
44499 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44502 this.view.on('click', this.onViewClick, this);
44504 this.store.on('beforeload', this.onBeforeLoad, this);
44505 this.store.on('load', this.onLoad, this);
44506 this.store.on('loadexception', this.onLoadException, this);
44508 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44509 "up" : function(e){
44510 this.inKeyMode = true;
44514 "down" : function(e){
44515 if(!this.isExpanded()){
44516 this.onTriggerClick();
44518 this.inKeyMode = true;
44523 "enter" : function(e){
44526 if(this.fireEvent("specialkey", this, e)){
44527 this.onViewClick(false);
44533 "esc" : function(e){
44537 "tab" : function(e){
44540 if(this.fireEvent("specialkey", this, e)){
44541 this.onViewClick(false);
44549 doRelay : function(foo, bar, hname){
44550 if(hname == 'down' || this.scope.isExpanded()){
44551 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44559 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44563 initNumberEvent : function(e)
44565 this.inputEl().on("keydown" , this.fireKey, this);
44566 this.inputEl().on("focus", this.onFocus, this);
44567 this.inputEl().on("blur", this.onBlur, this);
44569 this.inputEl().relayEvent('keyup', this);
44571 if(this.indicator){
44572 this.indicator.addClass('invisible');
44575 this.originalValue = this.getValue();
44577 if(this.validationEvent == 'keyup'){
44578 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44579 this.inputEl().on('keyup', this.filterValidation, this);
44581 else if(this.validationEvent !== false){
44582 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44585 if(this.selectOnFocus){
44586 this.on("focus", this.preFocus, this);
44589 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44590 this.inputEl().on("keypress", this.filterKeys, this);
44592 this.inputEl().relayEvent('keypress', this);
44595 var allowed = "0123456789";
44597 if(this.allowDecimals){
44598 allowed += this.decimalSeparator;
44601 if(this.allowNegative){
44605 if(this.thousandsDelimiter) {
44609 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44611 var keyPress = function(e){
44613 var k = e.getKey();
44615 var c = e.getCharCode();
44618 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44619 allowed.indexOf(String.fromCharCode(c)) === -1
44625 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44629 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44634 this.inputEl().on("keypress", keyPress, this);
44638 onTriggerClick : function(e)
44645 this.loadNext = false;
44647 if(this.isExpanded()){
44652 this.hasFocus = true;
44654 if(this.triggerAction == 'all') {
44655 this.doQuery(this.allQuery, true);
44659 this.doQuery(this.getRawValue());
44662 getCurrency : function()
44664 var v = this.currencyEl().getValue();
44669 restrictHeight : function()
44671 this.list.alignTo(this.currencyEl(), this.listAlign);
44672 this.list.alignTo(this.currencyEl(), this.listAlign);
44675 onViewClick : function(view, doFocus, el, e)
44677 var index = this.view.getSelectedIndexes()[0];
44679 var r = this.store.getAt(index);
44682 this.onSelect(r, index);
44686 onSelect : function(record, index){
44688 if(this.fireEvent('beforeselect', this, record, index) !== false){
44690 this.setFromCurrencyData(index > -1 ? record.data : false);
44694 this.fireEvent('select', this, record, index);
44698 setFromCurrencyData : function(o)
44702 this.lastCurrency = o;
44704 if (this.currencyField) {
44705 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44707 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44710 this.lastSelectionText = currency;
44712 //setting default currency
44713 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44714 this.setCurrency(this.defaultCurrency);
44718 this.setCurrency(currency);
44721 setFromData : function(o)
44725 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44727 this.setFromCurrencyData(c);
44732 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44734 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44737 this.setValue(value);
44741 setCurrency : function(v)
44743 this.currencyValue = v;
44746 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44751 setValue : function(v)
44753 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44759 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44761 this.inputEl().dom.value = (v == '') ? '' :
44762 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44764 if(!this.allowZero && v === '0') {
44765 this.hiddenEl().dom.value = '';
44766 this.inputEl().dom.value = '';
44773 getRawValue : function()
44775 var v = this.inputEl().getValue();
44780 getValue : function()
44782 return this.fixPrecision(this.parseValue(this.getRawValue()));
44785 parseValue : function(value)
44787 if(this.thousandsDelimiter) {
44789 r = new RegExp(",", "g");
44790 value = value.replace(r, "");
44793 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44794 return isNaN(value) ? '' : value;
44798 fixPrecision : function(value)
44800 if(this.thousandsDelimiter) {
44802 r = new RegExp(",", "g");
44803 value = value.replace(r, "");
44806 var nan = isNaN(value);
44808 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44809 return nan ? '' : value;
44811 return parseFloat(value).toFixed(this.decimalPrecision);
44814 decimalPrecisionFcn : function(v)
44816 return Math.floor(v);
44819 validateValue : function(value)
44821 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44825 var num = this.parseValue(value);
44828 this.markInvalid(String.format(this.nanText, value));
44832 if(num < this.minValue){
44833 this.markInvalid(String.format(this.minText, this.minValue));
44837 if(num > this.maxValue){
44838 this.markInvalid(String.format(this.maxText, this.maxValue));
44845 validate : function()
44847 if(this.disabled || this.allowBlank){
44852 var currency = this.getCurrency();
44854 if(this.validateValue(this.getRawValue()) && currency.length){
44859 this.markInvalid();
44863 getName: function()
44868 beforeBlur : function()
44874 var v = this.parseValue(this.getRawValue());
44881 onBlur : function()
44885 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44886 //this.el.removeClass(this.focusClass);
44889 this.hasFocus = false;
44891 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44895 var v = this.getValue();
44897 if(String(v) !== String(this.startValue)){
44898 this.fireEvent('change', this, v, this.startValue);
44901 this.fireEvent("blur", this);
44904 inputEl : function()
44906 return this.el.select('.roo-money-amount-input', true).first();
44909 currencyEl : function()
44911 return this.el.select('.roo-money-currency-input', true).first();
44914 hiddenEl : function()
44916 return this.el.select('input.hidden-number-input',true).first();
44920 * @class Roo.bootstrap.BezierSignature
44921 * @extends Roo.bootstrap.Component
44922 * Bootstrap BezierSignature class
44923 * This script refer to:
44924 * Title: Signature Pad
44926 * Availability: https://github.com/szimek/signature_pad
44929 * Create a new BezierSignature
44930 * @param {Object} config The config object
44933 Roo.bootstrap.BezierSignature = function(config){
44934 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44940 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44947 mouse_btn_down: true,
44950 * @cfg {int} canvas height
44952 canvas_height: '200px',
44955 * @cfg {float|function} Radius of a single dot.
44960 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44965 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44970 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44975 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44980 * @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.
44982 bg_color: 'rgba(0, 0, 0, 0)',
44985 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44987 dot_color: 'black',
44990 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44992 velocity_filter_weight: 0.7,
44995 * @cfg {function} Callback when stroke begin.
45000 * @cfg {function} Callback when stroke end.
45004 getAutoCreate : function()
45006 var cls = 'roo-signature column';
45009 cls += ' ' + this.cls;
45019 for(var i = 0; i < col_sizes.length; i++) {
45020 if(this[col_sizes[i]]) {
45021 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45031 cls: 'roo-signature-body',
45035 cls: 'roo-signature-body-canvas',
45036 height: this.canvas_height,
45037 width: this.canvas_width
45044 style: 'display: none'
45052 initEvents: function()
45054 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45056 var canvas = this.canvasEl();
45058 // mouse && touch event swapping...
45059 canvas.dom.style.touchAction = 'none';
45060 canvas.dom.style.msTouchAction = 'none';
45062 this.mouse_btn_down = false;
45063 canvas.on('mousedown', this._handleMouseDown, this);
45064 canvas.on('mousemove', this._handleMouseMove, this);
45065 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45067 if (window.PointerEvent) {
45068 canvas.on('pointerdown', this._handleMouseDown, this);
45069 canvas.on('pointermove', this._handleMouseMove, this);
45070 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45073 if ('ontouchstart' in window) {
45074 canvas.on('touchstart', this._handleTouchStart, this);
45075 canvas.on('touchmove', this._handleTouchMove, this);
45076 canvas.on('touchend', this._handleTouchEnd, this);
45079 Roo.EventManager.onWindowResize(this.resize, this, true);
45081 // file input event
45082 this.fileEl().on('change', this.uploadImage, this);
45089 resize: function(){
45091 var canvas = this.canvasEl().dom;
45092 var ctx = this.canvasElCtx();
45093 var img_data = false;
45095 if(canvas.width > 0) {
45096 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45098 // setting canvas width will clean img data
45101 var style = window.getComputedStyle ?
45102 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45104 var padding_left = parseInt(style.paddingLeft) || 0;
45105 var padding_right = parseInt(style.paddingRight) || 0;
45107 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45110 ctx.putImageData(img_data, 0, 0);
45114 _handleMouseDown: function(e)
45116 if (e.browserEvent.which === 1) {
45117 this.mouse_btn_down = true;
45118 this.strokeBegin(e);
45122 _handleMouseMove: function (e)
45124 if (this.mouse_btn_down) {
45125 this.strokeMoveUpdate(e);
45129 _handleMouseUp: function (e)
45131 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45132 this.mouse_btn_down = false;
45137 _handleTouchStart: function (e) {
45139 e.preventDefault();
45140 if (e.browserEvent.targetTouches.length === 1) {
45141 // var touch = e.browserEvent.changedTouches[0];
45142 // this.strokeBegin(touch);
45144 this.strokeBegin(e); // assume e catching the correct xy...
45148 _handleTouchMove: function (e) {
45149 e.preventDefault();
45150 // var touch = event.targetTouches[0];
45151 // _this._strokeMoveUpdate(touch);
45152 this.strokeMoveUpdate(e);
45155 _handleTouchEnd: function (e) {
45156 var wasCanvasTouched = e.target === this.canvasEl().dom;
45157 if (wasCanvasTouched) {
45158 e.preventDefault();
45159 // var touch = event.changedTouches[0];
45160 // _this._strokeEnd(touch);
45165 reset: function () {
45166 this._lastPoints = [];
45167 this._lastVelocity = 0;
45168 this._lastWidth = (this.min_width + this.max_width) / 2;
45169 this.canvasElCtx().fillStyle = this.dot_color;
45172 strokeMoveUpdate: function(e)
45174 this.strokeUpdate(e);
45176 if (this.throttle) {
45177 this.throttleStroke(this.strokeUpdate, this.throttle);
45180 this.strokeUpdate(e);
45184 strokeBegin: function(e)
45186 var newPointGroup = {
45187 color: this.dot_color,
45191 if (typeof this.onBegin === 'function') {
45195 this.curve_data.push(newPointGroup);
45197 this.strokeUpdate(e);
45200 strokeUpdate: function(e)
45202 var rect = this.canvasEl().dom.getBoundingClientRect();
45203 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45204 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45205 var lastPoints = lastPointGroup.points;
45206 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45207 var isLastPointTooClose = lastPoint
45208 ? point.distanceTo(lastPoint) <= this.min_distance
45210 var color = lastPointGroup.color;
45211 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45212 var curve = this.addPoint(point);
45214 this.drawDot({color: color, point: point});
45217 this.drawCurve({color: color, curve: curve});
45227 strokeEnd: function(e)
45229 this.strokeUpdate(e);
45230 if (typeof this.onEnd === 'function') {
45235 addPoint: function (point) {
45236 var _lastPoints = this._lastPoints;
45237 _lastPoints.push(point);
45238 if (_lastPoints.length > 2) {
45239 if (_lastPoints.length === 3) {
45240 _lastPoints.unshift(_lastPoints[0]);
45242 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45243 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45244 _lastPoints.shift();
45250 calculateCurveWidths: function (startPoint, endPoint) {
45251 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45252 (1 - this.velocity_filter_weight) * this._lastVelocity;
45254 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45257 start: this._lastWidth
45260 this._lastVelocity = velocity;
45261 this._lastWidth = newWidth;
45265 drawDot: function (_a) {
45266 var color = _a.color, point = _a.point;
45267 var ctx = this.canvasElCtx();
45268 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45270 this.drawCurveSegment(point.x, point.y, width);
45272 ctx.fillStyle = color;
45276 drawCurve: function (_a) {
45277 var color = _a.color, curve = _a.curve;
45278 var ctx = this.canvasElCtx();
45279 var widthDelta = curve.endWidth - curve.startWidth;
45280 var drawSteps = Math.floor(curve.length()) * 2;
45282 ctx.fillStyle = color;
45283 for (var i = 0; i < drawSteps; i += 1) {
45284 var t = i / drawSteps;
45290 var x = uuu * curve.startPoint.x;
45291 x += 3 * uu * t * curve.control1.x;
45292 x += 3 * u * tt * curve.control2.x;
45293 x += ttt * curve.endPoint.x;
45294 var y = uuu * curve.startPoint.y;
45295 y += 3 * uu * t * curve.control1.y;
45296 y += 3 * u * tt * curve.control2.y;
45297 y += ttt * curve.endPoint.y;
45298 var width = curve.startWidth + ttt * widthDelta;
45299 this.drawCurveSegment(x, y, width);
45305 drawCurveSegment: function (x, y, width) {
45306 var ctx = this.canvasElCtx();
45308 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45309 this.is_empty = false;
45314 var ctx = this.canvasElCtx();
45315 var canvas = this.canvasEl().dom;
45316 ctx.fillStyle = this.bg_color;
45317 ctx.clearRect(0, 0, canvas.width, canvas.height);
45318 ctx.fillRect(0, 0, canvas.width, canvas.height);
45319 this.curve_data = [];
45321 this.is_empty = true;
45326 return this.el.select('input',true).first();
45329 canvasEl: function()
45331 return this.el.select('canvas',true).first();
45334 canvasElCtx: function()
45336 return this.el.select('canvas',true).first().dom.getContext('2d');
45339 getImage: function(type)
45341 if(this.is_empty) {
45346 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45349 drawFromImage: function(img_src)
45351 var img = new Image();
45353 img.onload = function(){
45354 this.canvasElCtx().drawImage(img, 0, 0);
45359 this.is_empty = false;
45362 selectImage: function()
45364 this.fileEl().dom.click();
45367 uploadImage: function(e)
45369 var reader = new FileReader();
45371 reader.onload = function(e){
45372 var img = new Image();
45373 img.onload = function(){
45375 this.canvasElCtx().drawImage(img, 0, 0);
45377 img.src = e.target.result;
45380 reader.readAsDataURL(e.target.files[0]);
45383 // Bezier Point Constructor
45384 Point: (function () {
45385 function Point(x, y, time) {
45388 this.time = time || Date.now();
45390 Point.prototype.distanceTo = function (start) {
45391 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45393 Point.prototype.equals = function (other) {
45394 return this.x === other.x && this.y === other.y && this.time === other.time;
45396 Point.prototype.velocityFrom = function (start) {
45397 return this.time !== start.time
45398 ? this.distanceTo(start) / (this.time - start.time)
45405 // Bezier Constructor
45406 Bezier: (function () {
45407 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45408 this.startPoint = startPoint;
45409 this.control2 = control2;
45410 this.control1 = control1;
45411 this.endPoint = endPoint;
45412 this.startWidth = startWidth;
45413 this.endWidth = endWidth;
45415 Bezier.fromPoints = function (points, widths, scope) {
45416 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45417 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45418 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45420 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45421 var dx1 = s1.x - s2.x;
45422 var dy1 = s1.y - s2.y;
45423 var dx2 = s2.x - s3.x;
45424 var dy2 = s2.y - s3.y;
45425 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45426 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45427 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45428 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45429 var dxm = m1.x - m2.x;
45430 var dym = m1.y - m2.y;
45431 var k = l2 / (l1 + l2);
45432 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45433 var tx = s2.x - cm.x;
45434 var ty = s2.y - cm.y;
45436 c1: new scope.Point(m1.x + tx, m1.y + ty),
45437 c2: new scope.Point(m2.x + tx, m2.y + ty)
45440 Bezier.prototype.length = function () {
45445 for (var i = 0; i <= steps; i += 1) {
45447 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45448 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45450 var xdiff = cx - px;
45451 var ydiff = cy - py;
45452 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45459 Bezier.prototype.point = function (t, start, c1, c2, end) {
45460 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45461 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45462 + (3.0 * c2 * (1.0 - t) * t * t)
45463 + (end * t * t * t);
45468 throttleStroke: function(fn, wait) {
45469 if (wait === void 0) { wait = 250; }
45471 var timeout = null;
45475 var later = function () {
45476 previous = Date.now();
45478 result = fn.apply(storedContext, storedArgs);
45480 storedContext = null;
45484 return function wrapper() {
45486 for (var _i = 0; _i < arguments.length; _i++) {
45487 args[_i] = arguments[_i];
45489 var now = Date.now();
45490 var remaining = wait - (now - previous);
45491 storedContext = this;
45493 if (remaining <= 0 || remaining > wait) {
45495 clearTimeout(timeout);
45499 result = fn.apply(storedContext, storedArgs);
45501 storedContext = null;
45505 else if (!timeout) {
45506 timeout = window.setTimeout(later, remaining);