| 525 | | SortableObserver = function (element, observer) { |
|---|
| 526 | | this.__init__(element, observer); |
|---|
| 527 | | }; |
|---|
| 528 | | |
|---|
| 529 | | SortableObserver.prototype = { |
|---|
| 530 | | __init__: function (element, observer) { |
|---|
| 531 | | this.element = MochiKit.DOM.getElement(element); |
|---|
| 532 | | this.observer = observer; |
|---|
| 533 | | this.lastValue = Sortable.serialize(this.element); |
|---|
| 534 | | }, |
|---|
| 535 | | |
|---|
| 536 | | onStart: function () { |
|---|
| 537 | | this.lastValue = Sortable.serialize(this.element); |
|---|
| 538 | | }, |
|---|
| 539 | | |
|---|
| 540 | | onEnd: function () { |
|---|
| 541 | | Sortable.unmark(); |
|---|
| 542 | | if (this.lastValue != Sortable.serialize(this.element)) { |
|---|
| 543 | | this.observer(this.element) |
|---|
| 544 | | } |
|---|
| 545 | | } |
|---|
| 546 | | }; |
|---|
| 547 | | |
|---|
| 548 | | var Sortable = { |
|---|
| 549 | | sortables: new Array(), |
|---|
| 550 | | |
|---|
| 551 | | options: function (element){ |
|---|
| 552 | | element = MochiKit.DOM.getElement(element); |
|---|
| 553 | | var result; |
|---|
| 554 | | MochiKit.Iter.forEach(this.sortables, function (s) { |
|---|
| 555 | | if (s.element == element) { |
|---|
| 556 | | result = s; |
|---|
| 557 | | throw MochiKit.Iter.StopIteration; |
|---|
| 558 | | } |
|---|
| 559 | | }); |
|---|
| 560 | | return result; |
|---|
| 561 | | }, |
|---|
| 562 | | |
|---|
| 563 | | destroy: function (element){ |
|---|
| 564 | | element = MochiKit.DOM.getElement(element); |
|---|
| 565 | | MochiKit.Iter.forEach(MochiKit.Iter.ifilter(function (s) { |
|---|
| 566 | | return s.element == element; |
|---|
| 567 | | }, this.sortables), function (s) { |
|---|
| 568 | | DragAndDrop.Draggables.removeObserver(s.element); |
|---|
| 569 | | MochiKit.Iter.forEach(s.droppables, function (d) { |
|---|
| 570 | | DragAndDrop.Droppables.remove(d); |
|---|
| 571 | | }); |
|---|
| 572 | | s.draggables.invoke('destroy'); |
|---|
| 573 | | }); |
|---|
| 574 | | this.sortables = MochiKit.Base.filter(function (s) { |
|---|
| 575 | | return s.element != element; |
|---|
| 576 | | }, this.sortables); |
|---|
| 577 | | }, |
|---|
| 578 | | |
|---|
| 579 | | create: function (element, options) { |
|---|
| 580 | | element = MochiKit.DOM.getElement(element); |
|---|
| 581 | | options = MochiKit.Base.update({ |
|---|
| 582 | | element: element, |
|---|
| 583 | | tag: 'li', // assumes li children, override with tag: 'tagname' |
|---|
| 584 | | dropOnEmpty: false, |
|---|
| 585 | | tree: false, // fixme: unimplemented |
|---|
| 586 | | overlap: 'vertical', // one of 'vertical', 'horizontal' |
|---|
| 587 | | constraint: 'vertical', // one of 'vertical', 'horizontal', false |
|---|
| 588 | | // also takes array of elements (or ids); or false |
|---|
| 589 | | containment: element, |
|---|
| 590 | | handle: false, // or a CSS class |
|---|
| 591 | | only: false, |
|---|
| 592 | | hoverclass: null, |
|---|
| 593 | | ghosting: false, |
|---|
| 594 | | format: null, |
|---|
| 595 | | onChange: MochiKit.Base.emptyFunction, |
|---|
| 596 | | onUpdate: MochiKit.Base.emptyFunction |
|---|
| 597 | | }, options); |
|---|
| 598 | | |
|---|
| 599 | | // clear any old sortable with same element |
|---|
| 600 | | this.destroy(element); |
|---|
| 601 | | |
|---|
| 602 | | // build options for the draggables |
|---|
| 603 | | var options_for_draggable = { |
|---|
| 604 | | revert: true, |
|---|
| 605 | | ghosting: options.ghosting, |
|---|
| 606 | | constraint: options.constraint, |
|---|
| 607 | | handle: options.handle |
|---|
| 608 | | }; |
|---|
| 609 | | |
|---|
| 610 | | if (options.starteffect) { |
|---|
| 611 | | options_for_draggable.starteffect = options.starteffect; |
|---|
| 612 | | } |
|---|
| 613 | | |
|---|
| 614 | | if (options.reverteffect) { |
|---|
| 615 | | options_for_draggable.reverteffect = options.reverteffect; |
|---|
| 616 | | } else if (options.ghosting) { |
|---|
| 617 | | options_for_draggable.reverteffect = function (element) { |
|---|
| 618 | | element.style.top = 0; |
|---|
| 619 | | element.style.left = 0; |
|---|
| 620 | | }; |
|---|
| 621 | | } |
|---|
| 622 | | |
|---|
| 623 | | if (options.endeffect) { |
|---|
| 624 | | options_for_draggable.endeffect = options.endeffect; |
|---|
| 625 | | } |
|---|
| 626 | | |
|---|
| 627 | | if (options.zindex) { |
|---|
| 628 | | options_for_draggable.zindex = options.zindex; |
|---|
| 629 | | } |
|---|
| 630 | | |
|---|
| 631 | | // build options for the droppables |
|---|
| 632 | | var options_for_droppable = { |
|---|
| 633 | | overlap: options.overlap, |
|---|
| 634 | | containment: options.containment, |
|---|
| 635 | | hoverclass: options.hoverclass, |
|---|
| 636 | | onHover: Sortable.onHover, |
|---|
| 637 | | greedy: !options.dropOnEmpty |
|---|
| 638 | | } |
|---|
| 639 | | |
|---|
| 640 | | // fix for gecko engine |
|---|
| 641 | | MochiKit.DOM.cleanWhitespace(element); |
|---|
| 642 | | |
|---|
| 643 | | options.draggables = []; |
|---|
| 644 | | options.droppables = []; |
|---|
| 645 | | |
|---|
| 646 | | // make it so |
|---|
| 647 | | |
|---|
| 648 | | // drop on empty handling |
|---|
| 649 | | if (options.dropOnEmpty) { |
|---|
| 650 | | new DragAndDrop.Droppable(element, |
|---|
| 651 | | {containment: options.containment, |
|---|
| 652 | | onHover: Sortable.onEmptyHover, |
|---|
| 653 | | greedy: false}); |
|---|
| 654 | | options.droppables.push(element); |
|---|
| 655 | | } |
|---|
| 656 | | MochiKit.Iter.forEach((this.findElements(element, options) || []), |
|---|
| 657 | | function (e) { |
|---|
| 658 | | // handles are per-draggable |
|---|
| 659 | | var handle = options.handle ? |
|---|
| 660 | | MochiKit.DOM.getElementsByTagAndClassName(null, |
|---|
| 661 | | options.handle, e)[0] : e; |
|---|
| 662 | | options.draggables.push( |
|---|
| 663 | | new DragAndDrop.Draggable(e, |
|---|
| 664 | | MochiKit.Base.update(options_for_draggable, |
|---|
| 665 | | {handle: handle}))); |
|---|
| 666 | | new DragAndDrop.Droppable(e, options_for_droppable); |
|---|
| 667 | | options.droppables.push(e); |
|---|
| 668 | | }); |
|---|
| 669 | | |
|---|
| 670 | | // keep reference |
|---|
| 671 | | this.sortables.push(options); |
|---|
| 672 | | |
|---|
| 673 | | // for onupdate |
|---|
| 674 | | DragAndDrop.Draggables.addObserver( |
|---|
| 675 | | new SortableObserver(element, options.onUpdate)); |
|---|
| 676 | | }, |
|---|
| 677 | | |
|---|
| 678 | | // return all suitable-for-sortable elements in a guaranteed order |
|---|
| 679 | | findElements: function (element, options) { |
|---|
| 680 | | if (!element.hasChildNodes()) { |
|---|
| 681 | | return null; |
|---|
| 682 | | } |
|---|
| 683 | | var elements = []; |
|---|
| 684 | | MochiKit.Iter.forEach(element.childNodes, function (e) { |
|---|
| 685 | | if (e.tagName && |
|---|
| 686 | | e.tagName.toUpperCase() == options.tag.toUpperCase() && |
|---|
| 687 | | (!options.only || |
|---|
| 688 | | (MochiKit.DOM.hasElementClass(e, options.only)))) { |
|---|
| 689 | | elements.push(e); |
|---|
| 690 | | } |
|---|
| 691 | | if (options.tree) { |
|---|
| 692 | | var grandchildren = this.findElements(e, options); |
|---|
| 693 | | if (grandchildren) { |
|---|
| 694 | | elements.push(grandchildren); |
|---|
| 695 | | } |
|---|
| 696 | | } |
|---|
| 697 | | }); |
|---|
| 698 | | |
|---|
| 699 | | return (elements.length > 0 ? MochiKit.Iter.flatten(elements) : null); |
|---|
| 700 | | }, |
|---|
| 701 | | |
|---|
| 702 | | onHover: function (element, dropon, overlap) { |
|---|
| 703 | | if (overlap > 0.5) { |
|---|
| 704 | | Sortable.mark(dropon, 'before'); |
|---|
| 705 | | if (dropon.previousSibling != element) { |
|---|
| 706 | | var oldParentNode = element.parentNode; |
|---|
| 707 | | element.style.visibility = 'hidden'; // fix gecko rendering |
|---|
| 708 | | dropon.parentNode.insertBefore(element, dropon); |
|---|
| 709 | | if (dropon.parentNode != oldParentNode) { |
|---|
| 710 | | Sortable.options(oldParentNode).onChange(element); |
|---|
| 711 | | } |
|---|
| 712 | | Sortable.options(dropon.parentNode).onChange(element); |
|---|
| 713 | | } |
|---|
| 714 | | } else { |
|---|
| 715 | | Sortable.mark(dropon, 'after'); |
|---|
| 716 | | var nextElement = dropon.nextSibling || null; |
|---|
| 717 | | if (nextElement != element) { |
|---|
| 718 | | var oldParentNode = element.parentNode; |
|---|
| 719 | | element.style.visibility = 'hidden'; // fix gecko rendering |
|---|
| 720 | | dropon.parentNode.insertBefore(element, nextElement); |
|---|
| 721 | | if (dropon.parentNode != oldParentNode) { |
|---|
| 722 | | Sortable.options(oldParentNode).onChange(element); |
|---|
| 723 | | } |
|---|
| 724 | | Sortable.options(dropon.parentNode).onChange(element); |
|---|
| 725 | | } |
|---|
| 726 | | } |
|---|
| 727 | | }, |
|---|
| 728 | | |
|---|
| 729 | | onEmptyHover: function (element, dropon) { |
|---|
| 730 | | if (element.parentNode != dropon) { |
|---|
| 731 | | var oldParentNode = element.parentNode; |
|---|
| 732 | | dropon.appendChild(element); |
|---|
| 733 | | Sortable.options(oldParentNode).onChange(element); |
|---|
| 734 | | Sortable.options(dropon).onChange(element); |
|---|
| 735 | | } |
|---|
| 736 | | }, |
|---|
| 737 | | |
|---|
| 738 | | unmark: function () { |
|---|
| 739 | | if (Sortable._marker) { |
|---|
| 740 | | MochiKit.DOM.hideElement(Sortable._marker); |
|---|
| 741 | | } |
|---|
| 742 | | }, |
|---|
| 743 | | |
|---|
| 744 | | mark: function (dropon, position) { |
|---|
| 745 | | // mark on ghosting only |
|---|
| 746 | | var sortable = Sortable.options(dropon.parentNode); |
|---|
| 747 | | if (sortable && !sortable.ghosting) { |
|---|
| 748 | | return; |
|---|
| 749 | | } |
|---|
| 750 | | |
|---|
| 751 | | if (!Sortable._marker) { |
|---|
| 752 | | Sortable._marker = MochiKit.DOM.getElement('dropmarker') || |
|---|
| 753 | | document.createElement('DIV'); |
|---|
| 754 | | MochiKit.DOM.hideElement(Sortable._marker); |
|---|
| 755 | | MochiKit.DOM.addElementClass(Sortable._marker, 'dropmarker'); |
|---|
| 756 | | Sortable._marker.style.position = 'absolute'; |
|---|
| 757 | | document.getElementsByTagName('body').item(0).appendChild( |
|---|
| 758 | | Sortable._marker); |
|---|
| 759 | | } |
|---|
| 760 | | var offsets = MochiKit.Position.cumulativeOffset(dropon); |
|---|
| 761 | | Sortable._marker.style.left = offsets[0] + 'px'; |
|---|
| 762 | | Sortable._marker.style.top = offsets[1] + 'px'; |
|---|
| 763 | | |
|---|
| 764 | | if (position == 'after') { |
|---|
| 765 | | if (sortable.overlap == 'horizontal') { |
|---|
| 766 | | Sortable._marker.style.left = (offsets[0] + |
|---|
| 767 | | dropon.clientWidth) + 'px'; |
|---|
| 768 | | } else { |
|---|
| 769 | | Sortable._marker.style.top = (offsets[1] + |
|---|
| 770 | | dropon.clientHeight) + 'px'; |
|---|
| 771 | | } |
|---|
| 772 | | } |
|---|
| 773 | | MochiKit.DOM.showElement(Sortable._marker); |
|---|
| 774 | | }, |
|---|
| 775 | | |
|---|
| 776 | | serialize: function (element, options) { |
|---|
| 777 | | element = MochiKit.DOM.getElement(element); |
|---|
| 778 | | var sortableOptions = this.options(element); |
|---|
| 779 | | options = MochiKit.Base.update( |
|---|
| 780 | | { |
|---|
| 781 | | tag: sortableOptions.tag, |
|---|
| 782 | | only: sortableOptions.only, |
|---|
| 783 | | name: element.id, |
|---|
| 784 | | format: sortableOptions.format || /^[^_]*_(.*)$/ |
|---|
| 785 | | }, options || {}); |
|---|
| 786 | | return MochiKit.Base.map(function (item) { |
|---|
| 787 | | return (encodeURIComponent(options.name) + '[]=' + |
|---|
| 788 | | encodeURIComponent(item.id.match(options.format) ? |
|---|
| 789 | | item.id.match(options.format)[1] : '')); |
|---|
| 790 | | }, MochiKit.DOM.getElement( |
|---|
| 791 | | this.findElements(element, options) || [])).join('&'); |
|---|
| 792 | | } |
|---|
| 793 | | }; |
|---|
| 794 | | |
|---|