2
// script.aculo.us Resizables.js
4
// Copyright(c) 2007 - Orr Siloni, Comet Information Systems http://www.comet.co.il/en/
6
// Resizable.js is freely distributable under the terms of an MIT-style license.
7
// For details, see the script.aculo.us web site: http://script.aculo.us/
13
register: function(resizable) {
14
if(this.instances.length == 0) {
15
this.eventMouseUp = this.endResize.bindAsEventListener(this);
16
this.eventMouseMove = this.updateResize.bindAsEventListener(this);
18
Event.observe(document, "mouseup", this.eventMouseUp);
19
Event.observe(document, "mousemove", this.eventMouseMove);
21
this.instances.push(resizable);
24
unregister: function(resizable) {
25
this.instances = this.instances.reject(function(d) { return d==resizable });
26
if(this.instances.length == 0) {
27
Event.stopObserving(document, "mouseup", this.eventMouseUp);
28
Event.stopObserving(document, "mousemove", this.eventMouseMove);
32
activate: function(resizable) {
33
if(resizable.options.delay) {
34
this._timeout = setTimeout(function() {
35
Resizables._timeout = null;
36
Resizables.activeResizable = resizable;
37
}.bind(this), resizable.options.delay);
39
this.activeResizable = resizable;
43
deactivate: function() {
44
this.activeResizable = null;
47
updateResize: function(event) {
48
if(!this.activeResizable) return;
49
var pointer = [Event.pointerX(event), Event.pointerY(event)];
50
// Mozilla-based browsers fire successive mousemove events with
51
// the same coordinates, prevent needless redrawing (moz bug?)
52
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
53
this._lastPointer = pointer;
55
this.activeResizable.updateResize(event, pointer);
58
endResize: function(event) {
60
clearTimeout(this._timeout);
63
if(!this.activeResizable) return;
64
this._lastPointer = null;
65
this.activeResizable.endResize(event);
66
this.activeResizable = null;
69
addObserver: function(observer) {
70
this.observers.push(observer);
71
this._cacheObserverCallbacks();
74
removeObserver: function(element) { // element instead of observer fixes mem leaks
75
this.observers = this.observers.reject( function(o) { return o.element==element });
76
this._cacheObserverCallbacks();
79
notify: function(eventName, resizable, event) { // 'onStart', 'onEnd', 'onResize'
80
if(this[eventName+'Count'] > 0)
81
this.observers.each( function(o) {
82
if(o[eventName]) o[eventName](eventName, resizable, event);
84
if(resizable.options[eventName]) resizable.options[eventName](resizable, event);
87
_cacheObserverCallbacks: function() {
88
['onStart','onEnd','onResize'].each( function(eventName) {
89
Resizables[eventName+'Count'] = Resizables.observers.select(
90
function(o) { return o[eventName]; }
96
var Resizable = Class.create();
97
Resizable._resizing = {};
99
Resizable.prototype = {
100
initialize: function(element){
103
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
111
this.element = $(element);
113
var options = Object.extend(defaults, arguments[1] || {});
114
if(options.handle && typeof options.handle == 'string')
115
this.handle = $(options.handle);
116
if(!this.handle) this.handle = this.element;
118
this.options = options;
119
this.dragging = false;
121
this.eventMouseDown = this.initResize.bindAsEventListener(this);
122
Event.observe(this.handle, "mousedown", this.eventMouseDown);
124
Resizables.register(this);
127
destroy: function() {
128
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
131
currentDelta: function() {
133
parseInt(Element.getStyle(this.element,'width') || '0'),
134
parseInt(Element.getStyle(this.element,'height') || '0')]);
137
initResize: function(event) {
138
if(typeof Resizable._resizing[this.element] != 'undefined' &&
139
Resizable._resizing[this.element]) return;
140
if(Event.isLeftClick(event)) {
141
// abort on form elements, fixes a Firefox issue
142
var src = Event.element(event);
143
if((tag_name = src.tagName.toUpperCase()) && (
144
tag_name=='INPUT' || tag_name=='SELECT' || tag_name=='OPTION' ||
145
tag_name=='BUTTON' || tag_name=='TEXTAREA')) return;
147
this.pointer = [Event.pointerX(event), Event.pointerY(event)];
148
this.size = [parseInt(this.element.getStyle('width')) || 0, parseInt(this.element.getStyle('height')) || 0];
150
Resizables.activate(this);
155
startResize: function(event) {
156
this.resizing = true;
157
if(this.options.zindex) {
158
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
159
this.element.style.zIndex = this.options.zindex;
161
Resizables.notify('onStart', this, event);
162
Resizable._resizing[this.element] = true;
165
updateResize: function(event, pointer) {
166
if(!this.resizing) this.startResize(event);
168
Resizables.notify('onResize', this, event);
171
if(this.options.change) this.options.change(this);
173
// fix AppleWebKit rendering
174
if(Prototype.Browser.WebKit) window.scrollBy(0,0);
178
finishResize: function(event, success) {
179
this.resizing = false;
180
Resizables.notify('onEnd', this, event);
181
if(this.options.zindex) this.element.style.zIndex = this.originalZ;
182
Resizable._resizing[this.element] = false;
183
Resizables.deactivate(this);
186
endResize: function(event) {
187
if(!this.resizing) return;
188
this.finishResize(event, true);
192
draw: function(point) {
193
var p = [0,1].map(function(i){
194
return (this.size[i] + point[i] - this.pointer[i]);
197
if(this.options.snap) {
198
if(typeof this.options.snap == 'function') {
199
p = this.options.snap(p[0],p[1],this);
201
if(this.options.snap instanceof Array) {
202
p = p.map( function(v, i) {
203
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
205
p = p.map( function(v) {
206
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
210
if (this.options.minWidth && p[0] <= this.options.minWidth) p[0] = this.options.minWidth;
211
if (this.options.maxWidth && p[0] >= this.options.maxWidth) p[0] = this.options.maxWidth;
212
if (this.options.minHeight && p[1] <= this.options.minHeight) p[1] = this.options.minHeight;
213
if (this.options.maxHeight && p[1] >= this.options.maxHeight) p[1] = this.options.maxHeight;
215
var style = this.element.style;
216
if((!this.options.constraint) || (this.options.constraint=='horizontal')){
217
style.width = p[0] + "px";
219
if((!this.options.constraint) || (this.options.constraint=='vertical')){
220
style.height = p[1] + "px";
223
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering