(function() { // check if elementFromPageExists (function() { // document body has to exists for this test if (!document.body ) { setTimeout(arguments.callee, 1) return; } var div = document.createElement('div') document.body.appendChild(div); Syn.helpers.extend(div.style, { width: "100px", height: "10000px", backgroundColor: "blue", position: "absolute", top: "10px", left: "0px", zIndex: 19999 }); document.body.scrollTop = 11; if (!document.elementFromPoint ) { return; } var el = document.elementFromPoint(3, 1) if ( el == div ) { Syn.support.elementFromClient = true; } else { Syn.support.elementFromPage = true; } document.body.removeChild(div); document.body.scrollTop = 0; })(); //gets an element from a point var elementFromPoint = function( point, element ) { var clientX = point.clientX, clientY = point.clientY, win = Syn.helpers.getWindow(element), el; if ( Syn.support.elementFromPage ) { var off = Syn.helpers.scrollOffset(win); clientX = clientX + off.left; //convert to pageX clientY = clientY + off.top; //convert to pageY } el = win.document.elementFromPoint ? win.document.elementFromPoint(clientX, clientY) : element; if ( el === win.document.documentElement && (point.clientY < 0 || point.clientX < 0) ) { return element; } else { return el; } }, //creates an event at a certain point createEventAtPoint = function( event, point, element ) { var el = elementFromPoint(point, element) Syn.trigger(event, point, el || element) return el; }, // creates a mousemove event, but first triggering mouseout / mouseover if appropriate mouseMove = function( point, element, last ) { var el = elementFromPoint(point, element) if ( last != el && el && last ) { var options = Syn.helpers.extend({}, point); options.relatedTarget = el; Syn.trigger("mouseout", options, last); options.relatedTarget = last; Syn.trigger("mouseover", options, el); } Syn.trigger("mousemove", point, el || element) return el; }, // start and end are in clientX, clientY startMove = function( start, end, duration, element, callback ) { var startTime = new Date(), distX = end.clientX - start.clientX, distY = end.clientY - start.clientY, win = Syn.helpers.getWindow(element), current = elementFromPoint(start, element), cursor = win.document.createElement('div'), calls = 0; move = function() { //get what fraction we are at var now = new Date(), scrollOffset = Syn.helpers.scrollOffset(win), fraction = (calls == 0 ? 0 : now - startTime) / duration, options = { clientX: distX * fraction + start.clientX, clientY: distY * fraction + start.clientY }; calls++; if ( fraction < 1 ) { Syn.helpers.extend(cursor.style, { left: (options.clientX + scrollOffset.left + 2) + "px", top: (options.clientY + scrollOffset.top + 2) + "px" }) current = mouseMove(options, element, current) setTimeout(arguments.callee, 15) } else { current = mouseMove(end, element, current); win.document.body.removeChild(cursor) callback(); } } Syn.helpers.extend(cursor.style, { height: "5px", width: "5px", backgroundColor: "red", position: "absolute", zIndex: 19999, fontSize: "1px" }) win.document.body.appendChild(cursor) move(); }, startDrag = function( start, end, duration, element, callback ) { createEventAtPoint("mousedown", start, element); startMove(start, end, duration, element, function() { createEventAtPoint("mouseup", end, element); callback(); }) }, center = function( el ) { var j = Syn.jquery()(el), o = j.offset(); return { pageX: o.left + (j.outerWidth() / 2), pageY: o.top + (j.outerHeight() / 2) } }, convertOption = function( option, win, from ) { var page = /(\d+)[x ](\d+)/, client = /(\d+)X(\d+)/, relative = /([+-]\d+)[xX ]([+-]\d+)/ //check relative "+22x-44" if ( typeof option == 'string' && relative.test(option) && from ) { var cent = center(from), parts = option.match(relative); option = { pageX: cent.pageX + parseInt(parts[1]), pageY: cent.pageY + parseInt(parts[2]) } } if ( typeof option == 'string' && page.test(option) ) { var parts = option.match(page) option = { pageX: parseInt(parts[1]), pageY: parseInt(parts[2]) } } if ( typeof option == 'string' && client.test(option) ) { var parts = option.match(client) option = { clientX: parseInt(parts[1]), clientY: parseInt(parts[2]) } } if ( typeof option == 'string' ) { option = Syn.jquery()(option, win.document)[0]; } if ( option.nodeName ) { option = center(option) } if ( option.pageX ) { var off = Syn.helpers.scrollOffset(win); option = { clientX: option.pageX - off.left, clientY: option.pageY - off.top } } return option; }, // if the client chords are not going to be visible ... scroll the page so they will be ... adjust = function(from, to, win){ if(from.clientY < 0){ var off = Syn.helpers.scrollOffset(win); var dimensions = Syn.helpers.scrollDimensions(win), top = off.top + (from.clientY) - 100, diff = top - off.top // first, lets see if we can scroll 100 px if( top > 0){ } else { top =0; diff = -off.top; } from.clientY = from.clientY - diff; to.clientY = to.clientY - diff; Syn.helpers.scrollOffset(win,{top: top, left: off.left}); //throw "out of bounds!" } } /** * @add Syn prototype */ Syn.helpers.extend(Syn.init.prototype, { /** * @function move * Moves the cursor from one point to another. * * ### Quick Example * * The following moves the cursor from (0,0) in * the window to (100,100) in 1 second. * * Syn.move( * { * from: {clientX: 0, clientY: 0}, * to: {clientX: 100, clientY: 100}, * duration: 1000 * }, * document.document) * * ## Options * * There are many ways to configure the endpoints of the move. * * ### PageX and PageY * * If you pass pageX or pageY, these will get converted * to client coordinates. * * Syn.move( * { * from: {pageX: 0, pageY: 0}, * to: {pageX: 100, pageY: 100} * }, * document.document) * * ### String Coordinates * * You can set the pageX and pageY as strings like: * * Syn.move( * { * from: "0x0", * to: "100x100" * }, * document.document) * * ### Element Coordinates * * If jQuery is present, you can pass an element as the from or to option * and the coordinate will be set as the center of the element. * Syn.move( * { * from: $(".recipe")[0], * to: $("#trash")[0] * }, * document.document) * * ### Query Strings * * If jQuery is present, you can pass a query string as the from or to option. * * Syn.move( * { * from: ".recipe", * to: "#trash" * }, * document.document) * * ### No From * * If you don't provide a from, the element argument passed to Syn is used. * * Syn.move( * { to: "#trash" }, * 'myrecipe') * * ### Relative * * You can move the drag relative to the center of the from element. * * Syn.move("+20 +30", "myrecipe"); * * @param {Object} options options to configure the drag * @param {HTMLElement} from the element to move * @param {Function} callback a callback that happens after the drag motion has completed */ _move: function( options, from, callback ) { //need to convert if elements var win = Syn.helpers.getWindow(from), fro = convertOption(options.from || from, win, from), to = convertOption(options.to || options, win, from); options.adjust !== false && adjust(fro, to, win); startMove(fro, to, options.duration || 500, from, callback); }, /** * @function drag * Creates a mousedown and drags from one point to another. * Check out [Syn.prototype.move move] for API details. * * @param {Object} options * @param {Object} from * @param {Object} callback */ _drag: function( options, from, callback ) { //need to convert if elements var win = Syn.helpers.getWindow(from), fro = convertOption(options.from || from, win, from), to = convertOption(options.to || options, win, from); options.adjust !== false && adjust(fro, to, win); startDrag(fro, to, options.duration || 500, from, callback); } }) })()