/** * * Ext.ux.EventZone Extension Class for Ext 3.x Library * * @author Nigel White * * @license Ext.ux.EventZone is licensed under the terms of * the Open Source LGPL 3.0 license. Commercial use is permitted to the extent * that the code/component(s) do NOT become part of another Open Source or Commercially * licensed development library or toolkit without explicit permission. * * License details: http://www.gnu.org/licenses/lgpl.html * * @class Ext.ux.EventZone *
This class implements a "virtual element" at a relative size and position * within an existing element. It provides mouse events from a zone of an element of * defined dimensions.
*The zone is defined using top
, right
, bottom
,
* left
, width
and height
options which specify
* the bounds of the zone in a similar manner to the CSS style properties of those names.
This class encapsulates an absolute area of the document bounded by a list of points.
* @constructor * Create a new Polygon * @param {Object} points An Array of[n,n]
point specification Arrays, or
* an Array of Ext.lib.Points, or an HtmlElement, or an Ext.lib.Region.
*/
Ext.lib.Polygon = Ext.extend(Ext.lib.Region, {
constructor: function(points) {
var i, l, el;
if (l = points.length) {
if (points[0].x) {
for (i = 0; i < l; i++) {
points[i] = [ points[i].x, points[i].y ];
}
}
this.points = points;
} else {
if (el = Ext.get(points)) {
points = Ext.lib.Region.getRegion(el.dom);
}
if (points instanceof Ext.lib.Region) {
this.points = [
[points.left, points.top],
[points.right, points.top],
[points.right, points.bottom],
[points.left, points.bottom]
];
}
}
},
/**
* Returns a new Polygon translated by the specified X
and Y
increments.
* @param xDelta {Number} The X
translation increment.
* @param xDelta {Number} The Y
translation increment.
* @return {Polygon} The resulting Polygon.
*/
translate: function(xDelta, yDelta) {
var r = [], p = this.points, l = p.length, i;
for (i = 0; i < l; i++) {
r[i] = [ p[i][0] + xDelta, p[i][1] + yDelta ];
}
return new Ext.lib.Polygon(r);
},
/**
* Returns the area of this Polygon.
*/
getArea: function() {
var p = this.points, l = p.length, area = 0, i, j = 0;
for (i = 0; i < l; i++) {
j++;
if (j == l) {
j = 0;
}
area += (p[i][0] + p[j][0]) * (p[i][1] - p[j][1]);
}
return area * 0.5;
},
/**
* Returns true
if this Polygon contains the specified point. Thanks
* to http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html for the algorithm.
* @param pt {Point|Number} Either an Ext.lib.Point object, or the X
coordinate to test.
* @param py {Number} Optional. If the first parameter was an X
coordinate, this is the Y
coordinate.
*/
contains: function(pt, py) {
var f = (arguments.length == 1),
testX = f ? pt.x : pt,
testY = f ? pt.y : py,
p = this.points,
nvert = p.length,
j = nvert - 1,
i, j, c = false;
for (i = 0; i < nvert; j = i++) {
if ( ((p[i][1] > testY) != (p[j][1] > testY)) &&
(testX < (p[j][0]-p[i][0]) * (testY-p[i][1]) / (p[j][1]-p[i][1]) + p[i][0])) {
c = !c;
}
}
return c;
}
});
/**
* @class Ext.Resizable
* @extends Ext.util.Observable
* This is an override of Ext.Resizable to make usage of the Ex.ux.EventZone
* Applies virtual drag handles to an element to make it resizable.
*Here is the list of valid resize handles:
*Value Description ------ ------------------- 'n' north 's' south 'e' east 'w' west 'nw' northwest 'sw' southwest 'se' southeast 'ne' northeast 'all' all*
Here's an example showing the creation of a typical Resizable:
*
var resizer = new Ext.Resizable('element-id', {
handles: 'all',
minWidth: 200,
minHeight: 100,
maxWidth: 500,
maxHeight: 400,
pinned: true
});
resizer.on('resize', myHandler);
* To hide a particular handle, set its display to none in CSS, or through script:
* resizer.east.setDisplayed(false);
{@link #dynamic}==true
). Defaults to 0.
*/
heightIncrement : 0,
/**
* @cfg {Number} widthIncrement The increment to snap the width resize in pixels
* (only applies if {@link #dynamic}==true
). Defaults to 0.
*/
widthIncrement : 0,
/**
* @cfg {Number} minHeight The minimum height for the element (defaults to 5)
*/
minHeight : 5,
/**
* @cfg {Number} minWidth The minimum width for the element (defaults to 5)
*/
minWidth : 5,
/**
* @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
*/
maxHeight : 10000,
/**
* @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
*/
maxWidth : 10000,
/**
* @cfg {Number} minX The minimum x for the element (defaults to 0)
*/
minX: 0,
/**
* @cfg {Number} minY The minimum x for the element (defaults to 0)
*/
minY: 0,
/**
* @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
* user mouses over the resizable borders. This is only applied at config time. (defaults to false)
*/
pinned : false,
/**
* @cfg {Boolean} preserveRatio True to preserve the original ratio between height
* and width during resize (defaults to false)
*/
preserveRatio : false,
/**
* @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region
*/
/**
* Perform a manual resize and fires the 'resize' event.
* @param {Number} width
* @param {Number} height
*/
resizeTo : function(width, height){
this.el.setSize(width, height);
this.fireEvent('resize', this, width, height, null);
},
// private
startSizing : function(e, handle){
this.fireEvent('beforeresize', this, e);
if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
e.stopEvent();
Ext.getDoc().on({
scope: this,
mousemove: this.onMouseMove,
mouseup: {
fn: this.onMouseUp,
single: true,
scope: this
}
});
Ext.getBody().addClass('ux-resizable-handle-' + handle.position);
this.resizing = true;
this.startBox = this.el.getBox();
this.startPoint = e.getXY();
this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
(this.startBox.y + this.startBox.height) - this.startPoint[1]];
if(this.constrainTo) {
var ct = Ext.get(this.constrainTo);
this.resizeRegion = ct.getRegion().adjust(
ct.getFrameWidth('t'),
ct.getFrameWidth('l'),
-ct.getFrameWidth('b'),
-ct.getFrameWidth('r')
);
}
this.proxy.setStyle('visibility', 'hidden'); // workaround display none
this.proxy.show();
this.proxy.setBox(this.startBox);
if(!this.dynamic){
this.proxy.setStyle('visibility', 'visible');
}
}
},
// private
onMouseDown : function(handle, e){
if(this.enabled && !this.activeHandle){
e.stopEvent();
this.activeHandle = handle;
this.startSizing(e, handle);
}
},
// private
onMouseUp : function(e){
Ext.getBody().removeClass('ux-resizable-handle-' + this.activeHandle.position)
.un('mousemove', this.onMouseMove, this);
var size = this.resizeElement();
this.resizing = false;
this.handleOut(this.activeHandle);
this.proxy.hide();
this.fireEvent('resize', this, size.width, size.height, e);
this.activeHandle = null;
},
// private
snap : function(value, inc, min){
if(!inc || !value){
return value;
}
var newValue = value;
var m = value % inc;
if(m > 0){
if(m > (inc/2)){
newValue = value + (inc-m);
}else{
newValue = value - m;
}
}
return Math.max(min, newValue);
},
/**
* Performs resizing of the associated Element. This method is called internally by this * class, and should not be called by user code.
*If a Resizable is being used to resize an Element which encapsulates a more complex UI * component such as a Panel, this method may be overridden by specifying an implementation * as a config option to provide appropriate behaviour at the end of the resize operation on * mouseup, for example resizing the Panel, and relaying the Panel's content.
*The new area to be resized to is available by examining the state of the {@link #proxy} * Element. Example:
new Ext.Panel({
title: 'Resize me',
x: 100,
y: 100,
renderTo: Ext.getBody(),
floating: true,
frame: true,
width: 400,
height: 200,
listeners: {
render: function(p) {
new Ext.Resizable(p.getEl(), {
handles: 'all',
pinned: true,
transparent: true,
resizeElement: function() {
var box = this.proxy.getBox();
p.updateBox(box);
if (p.layout) {
p.doLayout();
}
return box;
}
});
}
}
}).show();
*/
resizeElement : function(){
var box = this.proxy.getBox();
if(this.updateBox){
this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
}else{
this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
}
if(!this.dynamic){
this.proxy.hide();
}
return box;
},
// private
constrain : function(v, diff, m, mx){
if(v - diff < m){
diff = v - m;
}else if(v - diff > mx){
diff = v - mx;
}
return diff;
},
// private
onMouseMove : function(e){
if(this.enabled && this.activeHandle){
try{// try catch so if something goes wrong the user doesn't get hung
if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
return;
}
//var curXY = this.startPoint;
var curSize = this.curSize || this.startBox,
x = this.startBox.x, y = this.startBox.y,
ox = x,
oy = y,
w = curSize.width,
h = curSize.height,
ow = w,
oh = h,
mw = this.minWidth,
mh = this.minHeight,
mxw = this.maxWidth,
mxh = this.maxHeight,
wi = this.widthIncrement,
hi = this.heightIncrement,
eventXY = e.getXY(),
diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),
diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),
pos = this.activeHandle.position,
tw,
th;
switch(pos){
case 'east':
w += diffX;
w = Math.min(Math.max(mw, w), mxw);
break;
case 'south':
h += diffY;
h = Math.min(Math.max(mh, h), mxh);
break;
case 'southeast':
w += diffX;
h += diffY;
w = Math.min(Math.max(mw, w), mxw);
h = Math.min(Math.max(mh, h), mxh);
break;
case 'north':
diffY = this.constrain(h, diffY, mh, mxh);
y += diffY;
h -= diffY;
break;
case 'west':
diffX = this.constrain(w, diffX, mw, mxw);
x += diffX;
w -= diffX;
break;
case 'northeast':
w += diffX;
w = Math.min(Math.max(mw, w), mxw);
diffY = this.constrain(h, diffY, mh, mxh);
y += diffY;
h -= diffY;
break;
case 'northwest':
diffX = this.constrain(w, diffX, mw, mxw);
diffY = this.constrain(h, diffY, mh, mxh);
y += diffY;
h -= diffY;
x += diffX;
w -= diffX;
break;
case 'southwest':
diffX = this.constrain(w, diffX, mw, mxw);
h += diffY;
h = Math.min(Math.max(mh, h), mxh);
x += diffX;
w -= diffX;
break;
}
var sw = this.snap(w, wi, mw);
var sh = this.snap(h, hi, mh);
if(sw != w || sh != h){
switch(pos){
case 'northeast':
y -= sh - h;
break;
case 'north':
y -= sh - h;
break;
case 'southwest':
x -= sw - w;
break;
case 'west':
x -= sw - w;
break;
case 'northwest':
x -= sw - w;
y -= sh - h;
break;
}
w = sw;
h = sh;
}
if(this.preserveRatio){
switch(pos){
case 'southeast':
case 'east':
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
w = ow * (h/oh);
break;
case 'south':
w = ow * (h/oh);
w = Math.min(Math.max(mw, w), mxw);
h = oh * (w/ow);
break;
case 'northeast':
w = ow * (h/oh);
w = Math.min(Math.max(mw, w), mxw);
h = oh * (w/ow);
break;
case 'north':
tw = w;
w = ow * (h/oh);
w = Math.min(Math.max(mw, w), mxw);
h = oh * (w/ow);
x += (tw - w) / 2;
break;
case 'southwest':
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
tw = w;
w = ow * (h/oh);
x += tw - w;
break;
case 'west':
th = h;
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
y += (th - h) / 2;
tw = w;
w = ow * (h/oh);
x += tw - w;
break;
case 'northwest':
tw = w;
th = h;
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
w = ow * (h/oh);
y += th - h;
x += tw - w;
break;
}
}
this.proxy.setBounds(x, y, w, h);
if(this.dynamic){
this.resizeElement();
}
}catch(ex){}
}
},
// private
handleOver : function(handle){
if(this.enabled){
Ext.getBody().addClass('ux-resizable-handle-' + handle.position);
}
},
// private
handleOut : function(handle){
if(!this.resizing){
Ext.getBody().removeClass('ux-resizable-handle-' + handle.position);
}
},
/**
* Returns the element this component is bound to.
* @return {Ext.Element}
*/
getEl : function(){
return this.el;
},
/**
* Destroys this resizable. If the element was wrapped and
* removeEl is not true then the element remains.
* @param {Boolean} removeEl (optional) true to remove the element from the DOM
*/
destroy : function(removeEl){
Ext.destroy(this.dd, this.proxy);
this.proxy = null;
var ps = Ext.Resizable.positions;
for(var k in ps){
if(typeof ps[k] != 'function' && this[ps[k]]){
this[ps[k]].destroy();
}
}
if(removeEl){
this.el.update('');
Ext.destroy(this.el);
this.el = null;
}
this.purgeListeners();
},
syncHandleHeight : function(){
var h = this.el.getHeight(true);
if(this.west.el){
this.west.el.setHeight(h);
}
if(this.east.el){
this.east.el.setHeight(h);
}
}
});
// private
// hash to map config positions to true positions
Ext.Resizable.positions = {
n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'
};
Ext.Resizable.cfg = {
north: {left: 7, right: -7, height: 7},
south: {left: 7, right: -7, top: -7},
east: {top: 7, bottom: -7, left: -7},
west: {top: 7, bottom: -7, width: 7},
southeast: {top: -7, left: -7},
southwest: {top: -7, width: 7},
northwest: {height: 7, width: 7},
northeast: {left: -7, height: 7}
};
// private
Ext.Resizable.Handle = function(rz, pos){
this.position = pos;
this.rz = rz;
var cfg = Ext.Resizable.cfg[pos] || Ext.Resizable.cfg[Ext.Resizable.positions[pos]];
this.ez = new Ext.ux.EventZone(Ext.apply({
position: pos,
el: rz.el
}, cfg));
this.ez.on({
mousedown: this.onMouseDown,
mouseenter: this.onMouseOver,
mouseleave: this.onMouseOut,
scope: this
});
};
// private
Ext.Resizable.Handle.prototype = {
cursor: 'move',
// private
afterResize : function(rz){
// do nothing
},
// private
onMouseDown : function(e){
this.rz.onMouseDown(this, e);
},
// private
onMouseOver : function(e){
this.rz.handleOver(this, e);
},
// private
onMouseOut : function(e){
this.rz.handleOut(this, e);
},
// private
destroy : function(){
Ext.destroy(this.el);
this.el = null;
}
};
/**
*
* Ext.ux.elasticTextArea Extension Class for Ext 3.x Library
*
* @author Steffen Kamper
*
* @license Ext.ux.elasticTextArea is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*
*/
Ext.ux.elasticTextArea = function(){
var defaultConfig = function(){
return {
minHeight : 0
,maxHeight : 0
,growBy: 12
}
}
var processOptions = function(config){
var o = defaultConfig();
var options = {};
Ext.apply(options, config, o);
return options ;
}
return {
div : null
,applyTo: function(elementId, options){
var el = Ext.get(elementId);
var width = el.getWidth();
var height = el.getHeight();
var styles = el.getStyles('padding-top', 'padding-bottom', 'padding-left', 'padding-right', 'line-height', 'font-size', 'font-family', 'font-weight');
if(! this.div){
var options = processOptions(options);
this.div = Ext.DomHelper.append(Ext.getBody() || document.body, {
'id':elementId + '-preview-div'
,'tag' : 'div'
,'background': 'red'
,'style' : 'position: absolute; top: -100000px; left: -100000px;'
}, true)
Ext.DomHelper.applyStyles(this.div, styles);
el.on('keyup', function() {
this.applyTo(elementId, options);
}, this);
}
this.div.setWidth(parseInt(el.getStyle('width')));
//replace \n with