/[webpac2]/Webpacus/root/js/dragdrop.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /Webpacus/root/js/dragdrop.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 83 - (show annotations)
Mon Nov 21 17:46:27 2005 UTC (18 years, 6 months ago) by dpavlin
File MIME type: text/cpp
File size: 18499 byte(s)
Import of web font-end for WebPAC v2 called WebPACus based on Catalyst
1 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 //
3 // Element.Class part Copyright (c) 2005 by Rick Olson
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 Element.Class = {
25 // Element.toggleClass(element, className) toggles the class being on/off
26 // Element.toggleClass(element, className1, className2) toggles between both classes,
27 // defaulting to className1 if neither exist
28 toggle: function(element, className) {
29 if(Element.Class.has(element, className)) {
30 Element.Class.remove(element, className);
31 if(arguments.length == 3) Element.Class.add(element, arguments[2]);
32 } else {
33 Element.Class.add(element, className);
34 if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
35 }
36 },
37
38 // gets space-delimited classnames of an element as an array
39 get: function(element) {
40 element = $(element);
41 return element.className.split(' ');
42 },
43
44 // functions adapted from original functions by Gavin Kistner
45 remove: function(element) {
46 element = $(element);
47 var regEx;
48 for(var i = 1; i < arguments.length; i++) {
49 regEx = new RegExp("^" + arguments[i] + "\\b\\s*|\\s*\\b" + arguments[i] + "\\b", 'g');
50 element.className = element.className.replace(regEx, '')
51 }
52 },
53
54 add: function(element) {
55 element = $(element);
56 for(var i = 1; i < arguments.length; i++) {
57 Element.Class.remove(element, arguments[i]);
58 element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
59 }
60 },
61
62 // returns true if all given classes exist in said element
63 has: function(element) {
64 element = $(element);
65 if(!element || !element.className) return false;
66 var regEx;
67 for(var i = 1; i < arguments.length; i++) {
68 regEx = new RegExp("\\b" + arguments[i] + "\\b");
69 if(!regEx.test(element.className)) return false;
70 }
71 return true;
72 },
73
74 // expects arrays of strings and/or strings as optional paramters
75 // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
76 has_any: function(element) {
77 element = $(element);
78 if(!element || !element.className) return false;
79 var regEx;
80 for(var i = 1; i < arguments.length; i++) {
81 if((typeof arguments[i] == 'object') &&
82 (arguments[i].constructor == Array)) {
83 for(var j = 0; j < arguments[i].length; j++) {
84 regEx = new RegExp("\\b" + arguments[i][j] + "\\b");
85 if(regEx.test(element.className)) return true;
86 }
87 } else {
88 regEx = new RegExp("\\b" + arguments[i] + "\\b");
89 if(regEx.test(element.className)) return true;
90 }
91 }
92 return false;
93 },
94
95 childrenWith: function(element, className) {
96 var children = $(element).getElementsByTagName('*');
97 var elements = new Array();
98
99 for (var i = 0; i < children.length; i++) {
100 if (Element.Class.has(children[i], className)) {
101 elements.push(children[i]);
102 break;
103 }
104 }
105
106 return elements;
107 }
108 }
109
110 /*--------------------------------------------------------------------------*/
111
112 var Droppables = {
113 drops: false,
114
115 remove: function(element) {
116 for(var i = 0; i < this.drops.length; i++)
117 if(this.drops[i].element == element)
118 this.drops.splice(i,1);
119 },
120
121 add: function(element) {
122 var element = $(element);
123 var options = Object.extend({
124 greedy: true,
125 hoverclass: null
126 }, arguments[1] || {});
127
128 // cache containers
129 if(options.containment) {
130 options._containers = new Array();
131 var containment = options.containment;
132 if((typeof containment == 'object') &&
133 (containment.constructor == Array)) {
134 for(var i=0; i<containment.length; i++)
135 options._containers.push($(containment[i]));
136 } else {
137 options._containers.push($(containment));
138 }
139 options._containers_length =
140 options._containers.length-1;
141 }
142
143 Element.makePositioned(element); // fix IE
144
145 options.element = element;
146
147 // activate the droppable
148 if(!this.drops) this.drops = [];
149 this.drops.push(options);
150 },
151
152 is_contained: function(element, drop) {
153 var containers = drop._containers;
154 var parentNode = element.parentNode;
155 var i = drop._containers_length;
156 do { if(parentNode==containers[i]) return true; } while (i--);
157 return false;
158 },
159
160 is_affected: function(pX, pY, element, drop) {
161 return (
162 (drop.element!=element) &&
163 ((!drop._containers) ||
164 this.is_contained(element, drop)) &&
165 ((!drop.accept) ||
166 (Element.Class.has_any(element, drop.accept))) &&
167 Position.within(drop.element, pX, pY) );
168 },
169
170 deactivate: function(drop) {
171 Element.Class.remove(drop.element, drop.hoverclass);
172 this.last_active = null;
173 },
174
175 activate: function(drop) {
176 if(this.last_active) this.deactivate(this.last_active);
177 if(drop.hoverclass) {
178 Element.Class.add(drop.element, drop.hoverclass);
179 this.last_active = drop;
180 }
181 },
182
183 show: function(event, element) {
184 if(!this.drops) return;
185 var pX = Event.pointerX(event);
186 var pY = Event.pointerY(event);
187 Position.prepare();
188
189 var i = this.drops.length-1; do {
190 var drop = this.drops[i];
191 if(this.is_affected(pX, pY, element, drop)) {
192 if(drop.onHover)
193 drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
194 if(drop.greedy) {
195 this.activate(drop);
196 return;
197 }
198 }
199 } while (i--);
200 },
201
202 fire: function(event, element) {
203 if(!this.last_active) return;
204 Position.prepare();
205
206 if (this.is_affected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
207 if (this.last_active.onDrop)
208 this.last_active.onDrop(element, this.last_active);
209
210 },
211
212 reset: function() {
213 if(this.last_active)
214 this.deactivate(this.last_active);
215 }
216 }
217
218 Draggables = {
219 observers: new Array(),
220 addObserver: function(observer) {
221 this.observers.push(observer);
222 },
223 removeObserver: function(element) { // element instead of obsever fixes mem leaks
224 for(var i = 0; i < this.observers.length; i++)
225 if(this.observers[i].element && (this.observers[i].element == element))
226 this.observers.splice(i,1);
227 },
228 notify: function(eventName, draggable) { // 'onStart', 'onEnd'
229 for(var i = 0; i < this.observers.length; i++)
230 this.observers[i][eventName](draggable);
231 }
232 }
233
234 /*--------------------------------------------------------------------------*/
235
236 Draggable = Class.create();
237 Draggable.prototype = {
238 initialize: function(element) {
239 var options = Object.extend({
240 handle: false,
241 starteffect: function(element) {
242 new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
243 },
244 reverteffect: function(element, top_offset, left_offset) {
245 new Effect.MoveBy(element, -top_offset, -left_offset, {duration:0.4});
246 },
247 endeffect: function(element) {
248 new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
249 },
250 zindex: 1000,
251 revert: false
252 }, arguments[1] || {});
253
254 this.element = $(element);
255 this.handle = options.handle ? $(options.handle) : this.element;
256
257 Element.makePositioned(this.element); // fix IE
258
259 this.offsetX = 0;
260 this.offsetY = 0;
261 this.originalLeft = this.currentLeft();
262 this.originalTop = this.currentTop();
263 this.originalX = this.element.offsetLeft;
264 this.originalY = this.element.offsetTop;
265 this.originalZ = parseInt(this.element.style.zIndex || "0");
266
267 this.options = options;
268
269 this.active = false;
270 this.dragging = false;
271
272 this.eventMouseDown = this.startDrag.bindAsEventListener(this);
273 this.eventMouseUp = this.endDrag.bindAsEventListener(this);
274 this.eventMouseMove = this.update.bindAsEventListener(this);
275 this.eventKeypress = this.keyPress.bindAsEventListener(this);
276
277 Event.observe(this.handle, "mousedown", this.eventMouseDown);
278 Event.observe(document, "mouseup", this.eventMouseUp);
279 Event.observe(document, "mousemove", this.eventMouseMove);
280 Event.observe(document, "keypress", this.eventKeypress);
281 },
282 destroy: function() {
283 Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
284 Event.stopObserving(document, "mouseup", this.eventMouseUp);
285 Event.stopObserving(document, "mousemove", this.eventMouseMove);
286 Event.stopObserving(document, "keypress", this.eventKeypress);
287 },
288 currentLeft: function() {
289 return parseInt(this.element.style.left || '0');
290 },
291 currentTop: function() {
292 return parseInt(this.element.style.top || '0')
293 },
294 startDrag: function(event) {
295 if(Event.isLeftClick(event)) {
296 this.active = true;
297
298 var style = this.element.style;
299 this.originalY = this.element.offsetTop - this.currentTop() - this.originalTop;
300 this.originalX = this.element.offsetLeft - this.currentLeft() - this.originalLeft;
301 this.offsetY = event.clientY - this.originalY - this.originalTop;
302 this.offsetX = event.clientX - this.originalX - this.originalLeft;
303
304 Event.stop(event);
305 }
306 },
307 finishDrag: function(event, success) {
308 this.active = false;
309 this.dragging = false;
310
311 if(success) Droppables.fire(event, this.element);
312 Draggables.notify('onEnd', this);
313
314 var revert = this.options.revert;
315 if(revert && typeof revert == 'function') revert = revert(this.element);
316
317 if(revert && this.options.reverteffect) {
318 this.options.reverteffect(this.element,
319 this.currentTop()-this.originalTop,
320 this.currentLeft()-this.originalLeft);
321 } else {
322 this.originalLeft = this.currentLeft();
323 this.originalTop = this.currentTop();
324 }
325
326 this.element.style.zIndex = this.originalZ;
327
328 if(this.options.endeffect)
329 this.options.endeffect(this.element);
330
331 Droppables.reset();
332 },
333 keyPress: function(event) {
334 if(this.active) {
335 if(event.keyCode==Event.KEY_ESC) {
336 this.finishDrag(event, false);
337 Event.stop(event);
338 }
339 }
340 },
341 endDrag: function(event) {
342 if(this.active && this.dragging) {
343 this.finishDrag(event, true);
344 Event.stop(event);
345 }
346 this.active = false;
347 this.dragging = false;
348 },
349 draw: function(event) {
350 var style = this.element.style;
351 this.originalX = this.element.offsetLeft - this.currentLeft() - this.originalLeft;
352 this.originalY = this.element.offsetTop - this.currentTop() - this.originalTop;
353 if((!this.options.constraint) || (this.options.constraint=='horizontal'))
354 style.left = ((event.clientX - this.originalX) - this.offsetX) + "px";
355 if((!this.options.constraint) || (this.options.constraint=='vertical'))
356 style.top = ((event.clientY - this.originalY) - this.offsetY) + "px";
357 if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
358 },
359 update: function(event) {
360 if(this.active) {
361 if(!this.dragging) {
362 var style = this.element.style;
363 this.dragging = true;
364 if(style.position=="") style.position = "relative";
365 style.zIndex = this.options.zindex;
366 Draggables.notify('onStart', this);
367 if(this.options.starteffect) this.options.starteffect(this.element);
368 }
369
370 Droppables.show(event, this.element);
371 this.draw(event);
372 if(this.options.change) this.options.change(this);
373
374 // fix AppleWebKit rendering
375 if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
376
377 Event.stop(event);
378 }
379 }
380 }
381
382 /*--------------------------------------------------------------------------*/
383
384 SortableObserver = Class.create();
385 SortableObserver.prototype = {
386 initialize: function(element, observer) {
387 this.element = $(element);
388 this.observer = observer;
389 this.lastValue = Sortable.serialize(this.element);
390 },
391 onStart: function() {
392 this.lastValue = Sortable.serialize(this.element);
393 },
394 onEnd: function() {
395 if(this.lastValue != Sortable.serialize(this.element))
396 this.observer(this.element)
397 }
398 }
399
400 Sortable = {
401 sortables: new Array(),
402 options: function(element){
403 var element = $(element);
404 for(var i=0;i<this.sortables.length;i++)
405 if(this.sortables[i].element == element)
406 return this.sortables[i];
407 return null;
408 },
409 destroy: function(element){
410 var element = $(element);
411 for(var i=0;i<this.sortables.length;i++) {
412 if(this.sortables[i].element == element) {
413 var s = this.sortables[i];
414 Draggables.removeObserver(s.element);
415 for(var j=0;j<s.droppables.length;j++)
416 Droppables.remove(s.droppables[j]);
417 for(var j=0;j<s.draggables.length;j++)
418 s.draggables[j].destroy();
419 this.sortables.splice(i,1);
420 }
421 }
422 },
423 create: function(element) {
424 var element = $(element);
425 var options = Object.extend({
426 element: element,
427 tag: 'li', // assumes li children, override with tag: 'tagname'
428 overlap: 'vertical', // one of 'vertical', 'horizontal'
429 constraint: 'vertical', // one of 'vertical', 'horizontal', false
430 containment: element, // also takes array of elements (or id's); or false
431 handle: false, // or a CSS class
432 only: false,
433 hoverclass: null,
434 onChange: function() {},
435 onUpdate: function() {}
436 }, arguments[1] || {});
437
438 // clear any old sortable with same element
439 this.destroy(element);
440
441 // build options for the draggables
442 var options_for_draggable = {
443 revert: true,
444 constraint: options.constraint,
445 handle: handle };
446 if(options.starteffect)
447 options_for_draggable.starteffect = options.starteffect;
448 if(options.reverteffect)
449 options_for_draggable.reverteffect = options.reverteffect;
450 if(options.endeffect)
451 options_for_draggable.endeffect = options.endeffect;
452 if(options.zindex)
453 options_for_draggable.zindex = options.zindex;
454
455 // build options for the droppables
456 var options_for_droppable = {
457 overlap: options.overlap,
458 containment: options.containment,
459 hoverclass: options.hoverclass,
460 onHover: function(element, dropon, overlap) {
461 if(overlap>0.5) {
462 if(dropon.previousSibling != element) {
463 var oldParentNode = element.parentNode;
464 element.style.visibility = "hidden"; // fix gecko rendering
465 dropon.parentNode.insertBefore(element, dropon);
466 if(dropon.parentNode!=oldParentNode && oldParentNode.sortable)
467 oldParentNode.sortable.onChange(element);
468 if(dropon.parentNode.sortable)
469 dropon.parentNode.sortable.onChange(element);
470 }
471 } else {
472 var nextElement = dropon.nextSibling || null;
473 if(nextElement != element) {
474 var oldParentNode = element.parentNode;
475 element.style.visibility = "hidden"; // fix gecko rendering
476 dropon.parentNode.insertBefore(element, nextElement);
477 if(dropon.parentNode!=oldParentNode && oldParentNode.sortable)
478 oldParentNode.sortable.onChange(element);
479 if(dropon.parentNode.sortable)
480 dropon.parentNode.sortable.onChange(element);
481 }
482 }
483 }
484 }
485
486 // fix for gecko engine
487 Element.cleanWhitespace(element);
488
489 options.draggables = [];
490 options.droppables = [];
491
492 // make it so
493 var elements = element.childNodes;
494 for (var i = 0; i < elements.length; i++)
495 if(elements[i].tagName && elements[i].tagName==options.tag.toUpperCase() &&
496 (!options.only || (Element.Class.has(elements[i], options.only)))) {
497
498 // handles are per-draggable
499 var handle = options.handle ?
500 Element.Class.childrenWith(elements[i], options.handle)[0] : elements[i];
501
502 options.draggables.push(new Draggable(elements[i], Object.extend(options_for_draggable, { handle: handle })));
503
504 Droppables.add(elements[i], options_for_droppable);
505 options.droppables.push(elements[i]);
506
507 }
508
509 // keep reference
510 this.sortables.push(options);
511
512 // for onupdate
513 Draggables.addObserver(new SortableObserver(element, options.onUpdate));
514
515 },
516 serialize: function(element) {
517 var element = $(element);
518 var sortableOptions = this.options(element);
519 var options = Object.extend({
520 tag: sortableOptions.tag,
521 only: sortableOptions.only,
522 name: element.id
523 }, arguments[1] || {});
524
525 var items = $(element).childNodes;
526 var queryComponents = new Array();
527
528 for(var i=0; i<items.length; i++)
529 if(items[i].tagName && items[i].tagName==options.tag.toUpperCase() &&
530 (!options.only || (Element.Class.has(items[i], options.only))))
531 queryComponents.push(
532 encodeURIComponent(options.name) + "[]=" +
533 encodeURIComponent(items[i].id.split("_")[1]));
534
535 return queryComponents.join("&");
536 }
537 }

Properties

Name Value
svn:mime-type text/cpp

  ViewVC Help
Powered by ViewVC 1.1.26