static/js/dnd.js
//--
// Copyright (c) 2012, Net-ng.
// All rights reserved.
//
// This software is licensed under the BSD License, as described in
// the file LICENSE.txt, which you should have received as part of
// this distribution.
//--
// TODO: move all column counter related stuff to extension
(function () {
'use strict';
var Y = YAHOO.util,
Dom = Y.Dom,
Event = Y.Event,
ECN = Dom.getElementsByClassName,
NS = YAHOO.namespace('kansha'),
DDM = YAHOO.util.DDM;
/**
* Helper log function
*/
var log = function (args) {
var s = '', i;
for (i in args) {
s = s + args[i] + ' ';
}
console.log(s);
};
/**
* A DnD proxy that scrolls his container
* Sources:
* - http://new.davglass.com/files/yui/dd15/
* - yui dragdrop.js source
*/
var ScrollProxy = function (id, sGroup, config) {
ScrollProxy.superclass.constructor.apply(this, arguments);
// Can drag using A elements
this.invalidHandleTypes.A = false;
// Disable drag for textarea and input elements
this.invalidHandleTypes.TEXTAREA = 'TEXTAREA';
this.invalidHandleTypes.INPUT = 'INPUT';
// Element used for scrolling
this.scrollParent = Dom.get(config.scrollParent);
// Attributes used to determine the drag orientation
this.lastX = 0;
this.goingRight = false;
this.lastY = 0;
this.goingDown = false;
this.scrollInt = null;
};
YAHOO.extend(ScrollProxy, YAHOO.util.DDProxy, {
/**
* Auto-scroll the lists div if the dragged object has been moved
* beyond the visible viewport boundary.
* @method autoScroll
* @param {int} x the drag element's x position
* @param {int} y the drag element's y position
* @param {int} h the height of the drag element
* @param {int} w the width of the drag element
*/
autoScroll: function (x, y, h, w) {
// How many pixels to scroll per autoscroll op. This helps to
// reduce clunky scrolling. IE is more sensitive about this ...
// it needs this value to be higher.
var scrAmt = (document.all) ? 80 : 30;
// How close to the edge the cursor must be before we scroll
var thresh = 50;
// Horizontal scrolling is done on the scrollParent element
// (not the window)
// Right marker coordinates
var rightMarker = Dom.getRegion(this.scrollParent).right - thresh;
// Left marker coordinates
var leftMarker = Dom.getRegion(this.scrollParent).left + thresh;
// Current scroll left
var sl = this.scrollParent.scrollLeft;
// Scroll left
YAHOO.log('autoScroll: ' + sl + '/' + x + '/' + leftMarker);
if (sl > 0 && x < leftMarker) {
this.scrollParent.scrollLeft = sl - scrAmt;
}
// Scroll right
if (x + w > rightMarker) {
this.scrollParent.scrollLeft = sl + scrAmt;
}
// Vertical scrolling
// For mobile, scroll the client window
if (YAHOO.kansha.app.isMobile()) {
// Current scroll top
var st = this.DDM.getScrollTop();
// Top marker coordinates
var topMarker = st + thresh;
// Bottom marker coordinates
var bottomMarker = st + this.DDM.getClientHeight() - thresh;
// Scroll top
if (st > 0 && y < topMarker) {
window.scrollTo(this.scrollParent.scrollLeft, st - scrAmt);
}
// Scroll bottom
if (y + h > bottomMarker) {
window.scrollTo(this.scrollParent.scrollLeft, st + scrAmt);
}
}
DDM.refreshCache();
},
/**
* Update drag orientation
*/
onDrag: function (e) {
var c = Event.getXY(e);
this.goingRight = (c[0] >= this.lastX);
this.goingDown = (c[1] >= this.lastY);
this.lastX = c[0];
this.lastY = c[1];
}
});
NS.dnd = {
listMarker: undefined,
cardMarker: undefined,
init: function () {
var target = new YAHOO.util.DDTarget(Dom.get('list-target'), 'list'),
listBody;
NS.dnd.listMarker = document.createElement('div');
Dom.addClass(NS.dnd.listMarker, 'list-marker');
NS.dnd.cardMarker = document.createElement('div');
Dom.addClass(NS.dnd.cardMarker, 'card-marker');
listBody = ECN('list-body', 'div', list_id);
alert(listBody);
target = YAHOO.util.DDTarget(listBody);
},
initConstraints: function (drag) {
//Get Region wrapper
var region = Dom.getRegion('viewport-wrapper');
//Clear old constraints
drag.clearConstraints();
//Get the element we are working on
var el = drag.getEl();
//Get the xy position of it
var xy = Dom.getXY(el);
//Get the height
var height = parseInt(Dom.getStyle(el, 'height'), 10);
//Set top to y minus top
var top = xy[1] - region.top;
//Set bottom to bottom minus y minus height
var bottom = region.bottom - xy[1] - height - 20;
//Set the constraints based on the above calculations
drag.setYConstraint(top, bottom);
//resetConstraints must be called if you manually
//reposition a dd element.
drag.resetConstraints();
},
/**
* Initialize DnD List
*/
initList: function (list_id) {
var drag = new ScrollProxy(list_id, 'list', {
dragElId: 'dnd-frame',
scrollParent: 'lists'
});
var handle = ECN('list-header', 'div', list_id).pop();
drag.setHandleElId(Dom.getAttribute(handle, 'id'));
drag.b4StartDrag = function (x, y) {
this.showFrame(x, y);
if (YAHOO.kansha.app.isDesktop()) {
this.clearConstraints();
// this.setYConstraint(0, 0);
} else {
this.clearConstraints();
this.setXConstraint(0, 0);
}
this.resetConstraints();
YAHOO.kansha.app.hideOverlay();
};
drag.startDrag = function (x, y) {
var src = this.getEl(),
dragEl = this.getDragEl(),
region = Dom.getRegion(src);
if (YAHOO.kansha.app.isDesktop()) {
Dom.setAttribute(dragEl, 'class', 'list list-proxy');
Dom.setStyle(dragEl, 'height', dragEl.clientHeight - 4 + 'px');
Dom.setStyle(dragEl, 'width', dragEl.clientWidth - 4 + 'px');
Dom.setStyle(NS.dnd.listMarker, 'height', region.height - 2 + 'px');
Dom.setStyle(NS.dnd.listMarker, 'width', region.width + -2 + 'px');
} else {
Dom.setAttribute(dragEl, 'class', 'list reduced');
Dom.setStyle(dragEl, 'height', 'auto');
Dom.setStyle(dragEl, 'width', '100%');
//define list marker style
Dom.setStyle(NS.dnd.listMarker, 'height', '30px');
Dom.setStyle(NS.dnd.listMarker, 'width', '99%');
//reduce all other column
Dom.setAttribute(ECN('list'), 'class', 'span-auto list reduced');
}
dragEl.innerHTML = src.innerHTML;
src.parentNode.replaceChild(NS.dnd.listMarker, src);
};
drag.onDragEnter = function (e, id) {
var func = null;
if (YAHOO.kansha.app.isDesktop()) {// Column mode
func = (this.goingRight ? Dom.insertAfter : Dom.insertBefore);
} else {// Vertical mode
func = (this.goingDown ? Dom.insertAfter : Dom.insertBefore);
}
if (func) {
func(NS.dnd.listMarker, id);
DDM.refreshCache();
// Important !
}
};
drag.endDrag = function (e, id) {
var clone = Dom.get(list_id);
if (clone) {
clone.parentNode.removeChild(clone);
}
var dragEl = this.getDragEl();
Dom.setAttribute(dragEl, 'class', '');
NS.dnd.listMarker.parentNode.replaceChild(this.getEl(), NS.dnd.listMarker);
Dom.batch(Dom.getChildren(dragEl), function (c) {
dragEl.removeChild(c);
});
if (!YAHOO.kansha.app.isDesktop()) {
Dom.setAttribute(ECN('list'), 'class', 'span-auto list');
}
Dom.setXY(dragEl, [0, 0]);
var lists = Selector.query('.list');
var index = 0;
for (index = 0; index < lists.length; index++) {
if (Dom.getAttribute(lists[index], 'id') === list_id) {
break;
}
}
_send_column_position({'list': list_id, 'index': index});
};
},
initTargetCard: function (targetId) {
var card = new YAHOO.util.DDTarget(targetId, 'card');
},
initCard: function (card) {
var drag = new ScrollProxy(card, 'card', {
dragElId: 'dnd-frame',
scrollParent: 'lists'
});
drag.b4StartDrag = function (x, y) {
this.showFrame(x, y);
if (!YAHOO.kansha.app.isDesktop()) {
this.clearConstraints();
this.setXConstraint(0, 0);
} else {
NS.dnd.initConstraints(this);
}
this.container = Dom.getAncestorByClassName(this.getEl(), 'list-body');
this.origin = Dom.getAncestorByClassName(this.getEl(), 'list');
YAHOO.kansha.app.hideOverlay();
};
drag.startDrag = function (x, y) {
var src = this.getEl(),
dragEl = this.getDragEl(),
region = Dom.getRegion(src);
dragEl.innerHTML = src.innerHTML;
Dom.addClass(dragEl, 'card-dragging');
if (YAHOO.kansha.app.isDesktop()) {
Dom.setStyle(NS.dnd.cardMarker, 'height', region.height + 'px');
} else {
Dom.setStyle(NS.dnd.cardMarker, 'height', '30px');
}
src.parentNode.replaceChild(NS.dnd.cardMarker, src);
};
drag.endDrag = function (e, id) {
var el = this.getEl(),
dragEl = this.getDragEl(),
region = Dom.getRegion(NS.dnd.cardMarker);
var self = this;
var origCard = Selector.query('.card', this.getEl(), true);
var clone = Dom.get(Dom.getAttribute(origCard, 'id'));
Dom.setStyle(dragEl, 'visibility', '');
var a = new YAHOO.util.Motion(dragEl, {
points: {
to: [region.x, region.y]
}
}, 0.15);
a.onComplete.subscribe(function () {
NS.dnd.cardMarker.parentNode.replaceChild(el, NS.dnd.cardMarker);
Dom.batch(Dom.getChildren(dragEl), function (c) {
dragEl.removeChild(c);
});
Dom.setStyle(this.getEl(), 'visibility', 'hidden');
Dom.setXY(dragEl, [0, 0]);
// Reset drag frame position
var cardId = Dom.getAttribute(Selector.query('.card', card, true), 'id');
var dest = Dom.getAncestorByClassName(card, 'list');
var cards = Selector.query('.card', dest);
var index = 0;
for (index = 0; index < cards.length; index++) {
if (Dom.getAttribute(cards[index], 'id') === cardId) {
break;
}
}
if (clone) {
clone.parentNode.removeChild(clone);
}
var dest_id = Dom.getAttribute(dest, 'id'),
orig_id = Dom.getAttribute(self.origin, 'id');
var data = {dest: dest_id,
orig: orig_id,
card: cardId, index: index};
if (dest === self.origin) {
_send_card_position(data);
} else {
var send_positions = function () {
_send_card_position(data);
};
var refresh_dest = function() {
var header_refresh_dest = Dom.get(dest_id + '_refresh');
header_refresh_dest.click();
};
var refresh_orig = function() {
var header_refresh_orig = Dom.get(orig_id + '_refresh');
header_refresh_orig.click();
};
nagare_chained_calls([send_positions, refresh_dest, refresh_orig]);
}
});
if (this.scrollInt) {
clearInterval(this.scrollInt);
}
a.animate();
Dom.removeClass(Dom.getElementsByClassName('list'), 'max-weight-highlight');
};
drag.onDrag = function (e) {
// copy onDrag basic instruction, is it possible to call the super method ?
var c = Event.getXY(e);
this.goingRight = (c[0] > this.lastX);
this.goingDown = (c[1] > this.lastY + 3) || (c[1] >= this.lastY - 3) && this.goingDown;
this.lastX = c[0];
this.lastY = c[1];
var y = Event.getPageY(e),
scrollTop = false,
xy = Dom.getXY(this.container),
topMarker = xy[1] + 50,
bottomMarker = xy[1] + this.container.clientHeight - 50;
if (this.goingDown) {
if (y > bottomMarker) {
scrollTop = this.container.scrollTop + 20;
}
} else {
if (y < topMarker) {
scrollTop = this.container.scrollTop - 20;
}
}
this.scrollTo(scrollTop);
};
drag.onDragEnter = function (e, id) {
var destEl = Dom.get(id),
list = Dom.getAncestorByClassName(id, 'list'),
limit = parseInt(localStorage[list.id], 10),
cards = ECN('card', null, list);
if (Dom.hasClass(destEl, 'list-body')) {
if (cards.length < limit || limit === 0) {
DDM.refreshCache();
this.container = Dom.get(id);
} else {
Dom.addClass(list, 'max-weight-highlight');
}
} else {
this.container = Dom.getAncestorByClassName(id, 'list-body');
}
};
drag.onDragOut = function(e, id) {
var srcEl = Dom.get(id),
list = Dom.getAncestorByClassName(id, 'list');
if (Dom.hasClass(srcEl, 'list-body')) {
Dom.removeClass(list, 'max-weight-highlight');
}
};
drag.onDragOver = function (e, id) {
var destEl = Dom.get(id),
list = Dom.getAncestorByClassName(id, 'list'),
limit = parseInt(localStorage[list.id], 10),
cards = ECN('card', null, list),
canAdd = (cards.length < limit || limit == 0);
if (canAdd) {
if (Dom.hasClass(destEl, 'card-dnd-wrapper')) { // We are hovering a card, add marker depending on move direction
if (this.goingDown) {
// insert above
Dom.insertAfter(NS.dnd.cardMarker, id);
} else {
// insert below
Dom.insertBefore(NS.dnd.cardMarker, id);
}
} else if (Dom.hasClass(destEl, 'list-body')) { // Hovering a list, add marker in it if not already present
var findCardMarker = function(elem) { return Dom.hasClass(elem, 'card-marker'); },
isAlreadyPlaced = Dom.getChildrenBy(destEl, findCardMarker).length != 0;
if (!isAlreadyPlaced) {
destEl.appendChild(NS.dnd.cardMarker);
}
}
DDM.refreshCache();
}
};
drag.scrollTo = function (scrollTop) {
// first step
// TODO: Use clearInterval method for continue scrolling
// when cursor doesn't move
this.currentScrollTop = scrollTop;
if (this.scrollInt) {
clearInterval(this.scrollInt);
}
if (scrollTop) {
var self = this;
this.scrollInt = setInterval(function () {
if ((self.currentScrollTop < 0) ||
(self.currentScrollTop > self.container.scrollHeight)) {
clearInterval(self.scrollInt);
}
self.container.scrollTop = self.currentScrollTop;
DDM.refreshCache();
if (self.goingDown) {
self.currentScrollTop += 20;
} else {
self.currentScrollTop -= 20;
}
}, 10);
}
};
}
};
}());
YAHOO.util.Event.onDOMReady(YAHOO.kansha.dnd.init);