Skip to content

Instantly share code, notes, and snippets.

@rcorreia
Last active February 16, 2025 12:05
Show Gist options
  • Select an option

  • Save rcorreia/2362544 to your computer and use it in GitHub Desktop.

Select an option

Save rcorreia/2362544 to your computer and use it in GitHub Desktop.

Revisions

  1. rcorreia revised this gist Oct 8, 2013. 1 changed file with 53 additions and 286 deletions.
    339 changes: 53 additions & 286 deletions drag_and_drop_helper.js
    Original file line number Diff line number Diff line change
    @@ -1,286 +1,53 @@
    /*
    * jquery.simulate - simulate browser mouse and keyboard events
    *
    * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
    * Dual licensed under the MIT or GPL Version 2 licenses.
    * http://jquery.org/license
    */

    ;(function( $ ) {

    var rkeyEvent = /^key/,
    rmouseEvent = /^(?:mouse|contextmenu)|click*/,
    rdndEvent = /^drag*|drop*/;

    $.fn.simulate = function( type, options ) {
    return this.each(function() {
    new $.simulate( this, type, options );
    });
    };

    $.simulate = function( elem, type, options ) {
    var method = $.camelCase( "simulate-" + type );

    this.target = elem;
    this.options = options;

    if ( this[ method ] ) {
    this[ method ]();
    } else {
    this.simulateEvent( elem, type, options );
    }
    };

    $.extend( $.simulate.prototype, {
    simulateEvent: function( elem, type, options ) {
    var event = this.createEvent( type, options );
    this.dispatchEvent( elem, type, event, options );
    if(type == "dragstart" && options.dropTarget){
    this._performDND(event, options);
    }
    },

    _performDND: function(event, options){
    var dropEvent = this.createEvent("drop", {});
    dropEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], "drop", dropEvent, {});
    },

    createEvent: function( type, options ) {
    if ( rkeyEvent.test( type ) ) {
    return this.keyEvent( type, options );
    }
    if(rmouseEvent.test(type)){
    return this.mouseEvent( type, options );
    }
    if(rdndEvent.test(type)){
    return this.dndEvent( type, options );
    }
    },

    dndEvent: function(type, options, dataTransfer){
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent(type, true, true, null);
    event.dataTransfer = {
    data: {
    },
    setData: function(type, val){
    this.data[type] = val;
    },
    getData: function(type){
    return this.data[type];
    }
    };
    return event;
    },

    mouseEvent: function( type, options ) {
    var event, eventDoc, doc, body;
    options = $.extend({
    bubbles: true,
    cancelable: (type !== "mousemove"),
    view: window,
    detail: 0,
    screenX: 0,
    screenY: 0,
    // TODO: default clientX/Y to a position within the target element
    clientX: 1,
    clientY: 1,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    button: 0,
    relatedTarget: undefined
    }, options );

    if ( document.createEvent ) {
    event = document.createEvent( "MouseEvents" );
    event.initMouseEvent( type, options.bubbles, options.cancelable,
    options.view, options.detail,
    options.screenX, options.screenY, options.clientX, options.clientY,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.button, options.relatedTarget || document.body.parentNode );
    // IE 9+ creates events with pageX and pageY set to 0.
    // Trying to modify the properties throws an error,
    // so we define getters to return the correct values.
    if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) {
    eventDoc = event.relatedTarget.ownerDocument || document;
    doc = eventDoc.documentElement;
    body = eventDoc.body;

    Object.defineProperty( event, "pageX", {
    get: function() {
    return options.clientX +
    ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
    ( doc && doc.clientLeft || body && body.clientLeft || 0 );
    }
    });
    Object.defineProperty( event, "pageY", {
    get: function() {
    return options.clientY +
    ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
    ( doc && doc.clientTop || body && body.clientTop || 0 );
    }
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    // TODO: what is this mapping for?
    event.button = { 0:1, 1:4, 2:2 }[ event.button ] || event.button;
    }

    return event;
    },

    keyEvent: function( type, options ) {
    var event;
    options = $.extend({
    bubbles: true,
    cancelable: true,
    view: window,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    keyCode: 0,
    charCode: undefined
    }, options );

    if ( document.createEvent ) {
    try {
    event = document.createEvent( "KeyEvents" );
    event.initKeyEvent( type, options.bubbles, options.cancelable, options.view,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.keyCode, options.charCode );
    // TODO: what is this supporting?
    } catch( err ) {
    event = document.createEvent( "Events" );
    event.initEvent( type, options.bubbles, options.cancelable );
    $.extend( event, {
    view: options.view,
    ctrlKey: options.ctrlKey,
    altKey: options.altKey,
    shiftKey: options.shiftKey,
    metaKey: options.metaKey,
    keyCode: options.keyCode,
    charCode: options.charCode
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    }

    // TODO: can we hook into core's logic?
    if ( $.browser.msie || $.browser.opera ) {
    // TODO: is charCode ever <0 ? Can we just use charCode || keyCode?
    event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode;
    event.charCode = undefined;
    }

    return event;
    },

    // TODO: does this need type? Can't we just check event.type?
    dispatchEvent: function( elem, type, event ) {
    if ( elem.dispatchEvent ) {
    elem.dispatchEvent( event );
    } else if ( elem.fireEvent ) {
    elem.fireEvent( "on" + type, event );
    }
    },

    simulateFocus: function() {
    var focusinEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "focus", trigger );
    element[ 0 ].focus();

    if ( !triggered ) {
    focusinEvent = $.Event( "focusin" );
    focusinEvent.preventDefault();
    element.trigger( focusinEvent );
    element.triggerHandler( "focus" );
    }
    element.unbind( "focus", trigger );
    },

    simulateBlur: function() {
    var focusoutEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "blur", trigger );
    element[ 0 ].blur();

    // blur events are async in IE
    setTimeout(function() {
    // IE won't let the blur occur if the window is inactive
    if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) {
    element[ 0 ].ownerDocument.body.focus();
    }

    // Firefox won't trigger events if the window is inactive
    // IE doesn't trigger events if we had to manually focus the body
    if ( !triggered ) {
    focusoutEvent = $.Event( "focusout" );
    focusoutEvent.preventDefault();
    element.trigger( focusoutEvent );
    element.triggerHandler( "blur" );
    }
    element.unbind( "blur", trigger );
    }, 1 );
    }
    });



    /** complex events **/

    function findCenter( elem ) {
    var offset,
    document = $( elem.ownerDocument );
    elem = $( elem );
    offset = elem.offset();

    return {
    x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(),
    y: offset.top + elem.outerHeight() / 2 - document.scrollTop()
    };
    }

    $.extend( $.simulate.prototype, {
    simulateDrag: function() {
    var target = this.target,
    options = this.options,
    center = findCenter( target ),
    x = Math.floor( center.x ),
    y = Math.floor( center.y ),
    dx = options.dx || 0,
    dy = options.dy || 0,
    target = this.target,
    coord = { clientX: x, clientY: y };
    this.simulateEvent( target, "dragstart", coord );
    coord = { clientX: x + 1, clientY: y + 1 };
    this.simulateEvent( document, "mousemove", coord );
    coord = { clientX: x + dx, clientY: y + dy };
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( target, "mouseup", coord );
    this.simulateEvent( target, "click", coord );
    }
    });

    })( jQuery );
    (function( $ ) {
    $.fn.simulateDragDrop = function(options) {
    return this.each(function() {
    new $.simulateDragDrop(this, options);
    });
    };
    $.simulateDragDrop = function(elem, options) {
    this.options = options;
    this.simulateEvent(elem, options);
    };
    $.extend($.simulateDragDrop.prototype, {
    simulateEvent: function(elem, options) {
    /*Simulating drag start*/
    var type = 'dragstart';
    var event = this.createEvent(type);
    this.dispatchEvent(elem, type, event);

    /*Simulating drop*/
    type = 'drop';
    var dropEvent = this.createEvent(type, {});
    dropEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], type, dropEvent);

    /*Simulating drag end*/
    type = 'dragend';
    var dragEndEvent = this.createEvent(type, {});
    dragEndEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent(elem, type, dragEndEvent);
    },
    createEvent: function(type) {
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent(type, true, true, null);
    event.dataTransfer = {
    data: {
    },
    setData: function(type, val){
    this.data[type] = val;
    },
    getData: function(type){
    return this.data[type];
    }
    };
    return event;
    },
    dispatchEvent: function(elem, type, event) {
    if(elem.dispatchEvent) {
    elem.dispatchEvent(event);
    }else if( elem.fireEvent ) {
    elem.fireEvent("on"+type, event);
    }
    }
    });
    })(jQuery);
  2. Ryan Thomás Correia Ortega renamed this gist Apr 16, 2012. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. Ryan Thomás Correia Ortega revised this gist Apr 13, 2012. 1 changed file with 286 additions and 53 deletions.
    339 changes: 286 additions & 53 deletions JQuerySimulate.js
    Original file line number Diff line number Diff line change
    @@ -1,53 +1,286 @@
    (function( $ ) {
    $.fn.simulateDragDrop = function(options) {
    return this.each(function() {
    new $.simulateDragDrop(this, options);
    });
    };
    $.simulateDragDrop = function(elem, options) {
    this.options = options;
    this.simulateEvent(elem, options);
    };
    $.extend($.simulateDragDrop.prototype, {
    simulateEvent: function(elem, options) {
    /*Simulating drag start*/
    var type = 'dragstart';
    var event = this.createEvent(type);
    this.dispatchEvent(elem, type, event);

    /*Simulating drop*/
    type = 'drop';
    var dropEvent = this.createEvent(type, {});
    dropEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], type, dropEvent);

    /*Simulating drag end*/
    type = 'dragend';
    var dragEndEvent = this.createEvent(type, {});
    dragEndEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent(elem, type, dragEndEvent);
    },
    createEvent: function(type) {
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent(type, true, true, null);
    event.dataTransfer = {
    data: {
    },
    setData: function(type, val){
    this.data[type] = val;
    },
    getData: function(type){
    return this.data[type];
    }
    };
    return event;
    },
    dispatchEvent: function(elem, type, event) {
    if(elem.dispatchEvent) {
    elem.dispatchEvent(event);
    }else if( elem.fireEvent ) {
    elem.fireEvent("on"+type, event);
    }
    }
    });
    })(jQuery);
    /*
    * jquery.simulate - simulate browser mouse and keyboard events
    *
    * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
    * Dual licensed under the MIT or GPL Version 2 licenses.
    * http://jquery.org/license
    */

    ;(function( $ ) {

    var rkeyEvent = /^key/,
    rmouseEvent = /^(?:mouse|contextmenu)|click*/,
    rdndEvent = /^drag*|drop*/;

    $.fn.simulate = function( type, options ) {
    return this.each(function() {
    new $.simulate( this, type, options );
    });
    };

    $.simulate = function( elem, type, options ) {
    var method = $.camelCase( "simulate-" + type );

    this.target = elem;
    this.options = options;

    if ( this[ method ] ) {
    this[ method ]();
    } else {
    this.simulateEvent( elem, type, options );
    }
    };

    $.extend( $.simulate.prototype, {
    simulateEvent: function( elem, type, options ) {
    var event = this.createEvent( type, options );
    this.dispatchEvent( elem, type, event, options );
    if(type == "dragstart" && options.dropTarget){
    this._performDND(event, options);
    }
    },

    _performDND: function(event, options){
    var dropEvent = this.createEvent("drop", {});
    dropEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], "drop", dropEvent, {});
    },

    createEvent: function( type, options ) {
    if ( rkeyEvent.test( type ) ) {
    return this.keyEvent( type, options );
    }
    if(rmouseEvent.test(type)){
    return this.mouseEvent( type, options );
    }
    if(rdndEvent.test(type)){
    return this.dndEvent( type, options );
    }
    },

    dndEvent: function(type, options, dataTransfer){
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent(type, true, true, null);
    event.dataTransfer = {
    data: {
    },
    setData: function(type, val){
    this.data[type] = val;
    },
    getData: function(type){
    return this.data[type];
    }
    };
    return event;
    },

    mouseEvent: function( type, options ) {
    var event, eventDoc, doc, body;
    options = $.extend({
    bubbles: true,
    cancelable: (type !== "mousemove"),
    view: window,
    detail: 0,
    screenX: 0,
    screenY: 0,
    // TODO: default clientX/Y to a position within the target element
    clientX: 1,
    clientY: 1,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    button: 0,
    relatedTarget: undefined
    }, options );

    if ( document.createEvent ) {
    event = document.createEvent( "MouseEvents" );
    event.initMouseEvent( type, options.bubbles, options.cancelable,
    options.view, options.detail,
    options.screenX, options.screenY, options.clientX, options.clientY,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.button, options.relatedTarget || document.body.parentNode );
    // IE 9+ creates events with pageX and pageY set to 0.
    // Trying to modify the properties throws an error,
    // so we define getters to return the correct values.
    if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) {
    eventDoc = event.relatedTarget.ownerDocument || document;
    doc = eventDoc.documentElement;
    body = eventDoc.body;

    Object.defineProperty( event, "pageX", {
    get: function() {
    return options.clientX +
    ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
    ( doc && doc.clientLeft || body && body.clientLeft || 0 );
    }
    });
    Object.defineProperty( event, "pageY", {
    get: function() {
    return options.clientY +
    ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
    ( doc && doc.clientTop || body && body.clientTop || 0 );
    }
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    // TODO: what is this mapping for?
    event.button = { 0:1, 1:4, 2:2 }[ event.button ] || event.button;
    }

    return event;
    },

    keyEvent: function( type, options ) {
    var event;
    options = $.extend({
    bubbles: true,
    cancelable: true,
    view: window,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    keyCode: 0,
    charCode: undefined
    }, options );

    if ( document.createEvent ) {
    try {
    event = document.createEvent( "KeyEvents" );
    event.initKeyEvent( type, options.bubbles, options.cancelable, options.view,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.keyCode, options.charCode );
    // TODO: what is this supporting?
    } catch( err ) {
    event = document.createEvent( "Events" );
    event.initEvent( type, options.bubbles, options.cancelable );
    $.extend( event, {
    view: options.view,
    ctrlKey: options.ctrlKey,
    altKey: options.altKey,
    shiftKey: options.shiftKey,
    metaKey: options.metaKey,
    keyCode: options.keyCode,
    charCode: options.charCode
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    }

    // TODO: can we hook into core's logic?
    if ( $.browser.msie || $.browser.opera ) {
    // TODO: is charCode ever <0 ? Can we just use charCode || keyCode?
    event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode;
    event.charCode = undefined;
    }

    return event;
    },

    // TODO: does this need type? Can't we just check event.type?
    dispatchEvent: function( elem, type, event ) {
    if ( elem.dispatchEvent ) {
    elem.dispatchEvent( event );
    } else if ( elem.fireEvent ) {
    elem.fireEvent( "on" + type, event );
    }
    },

    simulateFocus: function() {
    var focusinEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "focus", trigger );
    element[ 0 ].focus();

    if ( !triggered ) {
    focusinEvent = $.Event( "focusin" );
    focusinEvent.preventDefault();
    element.trigger( focusinEvent );
    element.triggerHandler( "focus" );
    }
    element.unbind( "focus", trigger );
    },

    simulateBlur: function() {
    var focusoutEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "blur", trigger );
    element[ 0 ].blur();

    // blur events are async in IE
    setTimeout(function() {
    // IE won't let the blur occur if the window is inactive
    if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) {
    element[ 0 ].ownerDocument.body.focus();
    }

    // Firefox won't trigger events if the window is inactive
    // IE doesn't trigger events if we had to manually focus the body
    if ( !triggered ) {
    focusoutEvent = $.Event( "focusout" );
    focusoutEvent.preventDefault();
    element.trigger( focusoutEvent );
    element.triggerHandler( "blur" );
    }
    element.unbind( "blur", trigger );
    }, 1 );
    }
    });



    /** complex events **/

    function findCenter( elem ) {
    var offset,
    document = $( elem.ownerDocument );
    elem = $( elem );
    offset = elem.offset();

    return {
    x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(),
    y: offset.top + elem.outerHeight() / 2 - document.scrollTop()
    };
    }

    $.extend( $.simulate.prototype, {
    simulateDrag: function() {
    var target = this.target,
    options = this.options,
    center = findCenter( target ),
    x = Math.floor( center.x ),
    y = Math.floor( center.y ),
    dx = options.dx || 0,
    dy = options.dy || 0,
    target = this.target,
    coord = { clientX: x, clientY: y };
    this.simulateEvent( target, "dragstart", coord );
    coord = { clientX: x + 1, clientY: y + 1 };
    this.simulateEvent( document, "mousemove", coord );
    coord = { clientX: x + dx, clientY: y + dy };
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( target, "mouseup", coord );
    this.simulateEvent( target, "click", coord );
    }
    });

    })( jQuery );
  4. Ryan Thomás Correia Ortega revised this gist Apr 13, 2012. No changes.
  5. Ryan Thomás Correia Ortega revised this gist Apr 13, 2012. 1 changed file with 53 additions and 286 deletions.
    339 changes: 53 additions & 286 deletions JQuerySimulate.js
    Original file line number Diff line number Diff line change
    @@ -1,286 +1,53 @@
    /*
    * jquery.simulate - simulate browser mouse and keyboard events
    *
    * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
    * Dual licensed under the MIT or GPL Version 2 licenses.
    * http://jquery.org/license
    */

    ;(function( $ ) {

    var rkeyEvent = /^key/,
    rmouseEvent = /^(?:mouse|contextmenu)|click*/,
    rdndEvent = /^drag*|drop*/;

    $.fn.simulate = function( type, options ) {
    return this.each(function() {
    new $.simulate( this, type, options );
    });
    };

    $.simulate = function( elem, type, options ) {
    var method = $.camelCase( "simulate-" + type );

    this.target = elem;
    this.options = options;

    if ( this[ method ] ) {
    this[ method ]();
    } else {
    this.simulateEvent( elem, type, options );
    }
    };

    $.extend( $.simulate.prototype, {
    simulateEvent: function( elem, type, options ) {
    var event = this.createEvent( type, options );
    this.dispatchEvent( elem, type, event, options );
    if(type == "dragstart" && options.dropTarget){
    this._performDND(event, options);
    }
    },

    _performDND: function(event, options){
    var dropEvent = this.createEvent("drop", {});
    dropEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], "drop", dropEvent, {});
    },

    createEvent: function( type, options ) {
    if ( rkeyEvent.test( type ) ) {
    return this.keyEvent( type, options );
    }
    if(rmouseEvent.test(type)){
    return this.mouseEvent( type, options );
    }
    if(rdndEvent.test(type)){
    return this.dndEvent( type, options );
    }
    },

    dndEvent: function(type, options, dataTransfer){
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent(type, true, true, null);
    event.dataTransfer = {
    data: {
    },
    setData: function(type, val){
    this.data[type] = val;
    },
    getData: function(type){
    return this.data[type];
    }
    };
    return event;
    },

    mouseEvent: function( type, options ) {
    var event, eventDoc, doc, body;
    options = $.extend({
    bubbles: true,
    cancelable: (type !== "mousemove"),
    view: window,
    detail: 0,
    screenX: 0,
    screenY: 0,
    // TODO: default clientX/Y to a position within the target element
    clientX: 1,
    clientY: 1,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    button: 0,
    relatedTarget: undefined
    }, options );

    if ( document.createEvent ) {
    event = document.createEvent( "MouseEvents" );
    event.initMouseEvent( type, options.bubbles, options.cancelable,
    options.view, options.detail,
    options.screenX, options.screenY, options.clientX, options.clientY,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.button, options.relatedTarget || document.body.parentNode );
    // IE 9+ creates events with pageX and pageY set to 0.
    // Trying to modify the properties throws an error,
    // so we define getters to return the correct values.
    if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) {
    eventDoc = event.relatedTarget.ownerDocument || document;
    doc = eventDoc.documentElement;
    body = eventDoc.body;

    Object.defineProperty( event, "pageX", {
    get: function() {
    return options.clientX +
    ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
    ( doc && doc.clientLeft || body && body.clientLeft || 0 );
    }
    });
    Object.defineProperty( event, "pageY", {
    get: function() {
    return options.clientY +
    ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
    ( doc && doc.clientTop || body && body.clientTop || 0 );
    }
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    // TODO: what is this mapping for?
    event.button = { 0:1, 1:4, 2:2 }[ event.button ] || event.button;
    }

    return event;
    },

    keyEvent: function( type, options ) {
    var event;
    options = $.extend({
    bubbles: true,
    cancelable: true,
    view: window,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    keyCode: 0,
    charCode: undefined
    }, options );

    if ( document.createEvent ) {
    try {
    event = document.createEvent( "KeyEvents" );
    event.initKeyEvent( type, options.bubbles, options.cancelable, options.view,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.keyCode, options.charCode );
    // TODO: what is this supporting?
    } catch( err ) {
    event = document.createEvent( "Events" );
    event.initEvent( type, options.bubbles, options.cancelable );
    $.extend( event, {
    view: options.view,
    ctrlKey: options.ctrlKey,
    altKey: options.altKey,
    shiftKey: options.shiftKey,
    metaKey: options.metaKey,
    keyCode: options.keyCode,
    charCode: options.charCode
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    }

    // TODO: can we hook into core's logic?
    if ( $.browser.msie || $.browser.opera ) {
    // TODO: is charCode ever <0 ? Can we just use charCode || keyCode?
    event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode;
    event.charCode = undefined;
    }

    return event;
    },

    // TODO: does this need type? Can't we just check event.type?
    dispatchEvent: function( elem, type, event ) {
    if ( elem.dispatchEvent ) {
    elem.dispatchEvent( event );
    } else if ( elem.fireEvent ) {
    elem.fireEvent( "on" + type, event );
    }
    },

    simulateFocus: function() {
    var focusinEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "focus", trigger );
    element[ 0 ].focus();

    if ( !triggered ) {
    focusinEvent = $.Event( "focusin" );
    focusinEvent.preventDefault();
    element.trigger( focusinEvent );
    element.triggerHandler( "focus" );
    }
    element.unbind( "focus", trigger );
    },

    simulateBlur: function() {
    var focusoutEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "blur", trigger );
    element[ 0 ].blur();

    // blur events are async in IE
    setTimeout(function() {
    // IE won't let the blur occur if the window is inactive
    if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) {
    element[ 0 ].ownerDocument.body.focus();
    }

    // Firefox won't trigger events if the window is inactive
    // IE doesn't trigger events if we had to manually focus the body
    if ( !triggered ) {
    focusoutEvent = $.Event( "focusout" );
    focusoutEvent.preventDefault();
    element.trigger( focusoutEvent );
    element.triggerHandler( "blur" );
    }
    element.unbind( "blur", trigger );
    }, 1 );
    }
    });



    /** complex events **/

    function findCenter( elem ) {
    var offset,
    document = $( elem.ownerDocument );
    elem = $( elem );
    offset = elem.offset();

    return {
    x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(),
    y: offset.top + elem.outerHeight() / 2 - document.scrollTop()
    };
    }

    $.extend( $.simulate.prototype, {
    simulateDrag: function() {
    var target = this.target,
    options = this.options,
    center = findCenter( target ),
    x = Math.floor( center.x ),
    y = Math.floor( center.y ),
    dx = options.dx || 0,
    dy = options.dy || 0,
    target = this.target,
    coord = { clientX: x, clientY: y };
    this.simulateEvent( target, "dragstart", coord );
    coord = { clientX: x + 1, clientY: y + 1 };
    this.simulateEvent( document, "mousemove", coord );
    coord = { clientX: x + dx, clientY: y + dy };
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( target, "mouseup", coord );
    this.simulateEvent( target, "click", coord );
    }
    });

    })( jQuery );
    (function( $ ) {
    $.fn.simulateDragDrop = function(options) {
    return this.each(function() {
    new $.simulateDragDrop(this, options);
    });
    };
    $.simulateDragDrop = function(elem, options) {
    this.options = options;
    this.simulateEvent(elem, options);
    };
    $.extend($.simulateDragDrop.prototype, {
    simulateEvent: function(elem, options) {
    /*Simulating drag start*/
    var type = 'dragstart';
    var event = this.createEvent(type);
    this.dispatchEvent(elem, type, event);

    /*Simulating drop*/
    type = 'drop';
    var dropEvent = this.createEvent(type, {});
    dropEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], type, dropEvent);

    /*Simulating drag end*/
    type = 'dragend';
    var dragEndEvent = this.createEvent(type, {});
    dragEndEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent(elem, type, dragEndEvent);
    },
    createEvent: function(type) {
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent(type, true, true, null);
    event.dataTransfer = {
    data: {
    },
    setData: function(type, val){
    this.data[type] = val;
    },
    getData: function(type){
    return this.data[type];
    }
    };
    return event;
    },
    dispatchEvent: function(elem, type, event) {
    if(elem.dispatchEvent) {
    elem.dispatchEvent(event);
    }else if( elem.fireEvent ) {
    elem.fireEvent("on"+type, event);
    }
    }
    });
    })(jQuery);
  6. Ryan Thomás Correia Ortega created this gist Apr 11, 2012.
    286 changes: 286 additions & 0 deletions JQuerySimulate.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,286 @@
    /*
    * jquery.simulate - simulate browser mouse and keyboard events
    *
    * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
    * Dual licensed under the MIT or GPL Version 2 licenses.
    * http://jquery.org/license
    */

    ;(function( $ ) {

    var rkeyEvent = /^key/,
    rmouseEvent = /^(?:mouse|contextmenu)|click*/,
    rdndEvent = /^drag*|drop*/;

    $.fn.simulate = function( type, options ) {
    return this.each(function() {
    new $.simulate( this, type, options );
    });
    };

    $.simulate = function( elem, type, options ) {
    var method = $.camelCase( "simulate-" + type );

    this.target = elem;
    this.options = options;

    if ( this[ method ] ) {
    this[ method ]();
    } else {
    this.simulateEvent( elem, type, options );
    }
    };

    $.extend( $.simulate.prototype, {
    simulateEvent: function( elem, type, options ) {
    var event = this.createEvent( type, options );
    this.dispatchEvent( elem, type, event, options );
    if(type == "dragstart" && options.dropTarget){
    this._performDND(event, options);
    }
    },

    _performDND: function(event, options){
    var dropEvent = this.createEvent("drop", {});
    dropEvent.dataTransfer = event.dataTransfer;
    this.dispatchEvent($(options.dropTarget)[0], "drop", dropEvent, {});
    },

    createEvent: function( type, options ) {
    if ( rkeyEvent.test( type ) ) {
    return this.keyEvent( type, options );
    }
    if(rmouseEvent.test(type)){
    return this.mouseEvent( type, options );
    }
    if(rdndEvent.test(type)){
    return this.dndEvent( type, options );
    }
    },

    dndEvent: function(type, options, dataTransfer){
    var event = document.createEvent("CustomEvent");
    event.initCustomEvent(type, true, true, null);
    event.dataTransfer = {
    data: {
    },
    setData: function(type, val){
    this.data[type] = val;
    },
    getData: function(type){
    return this.data[type];
    }
    };
    return event;
    },

    mouseEvent: function( type, options ) {
    var event, eventDoc, doc, body;
    options = $.extend({
    bubbles: true,
    cancelable: (type !== "mousemove"),
    view: window,
    detail: 0,
    screenX: 0,
    screenY: 0,
    // TODO: default clientX/Y to a position within the target element
    clientX: 1,
    clientY: 1,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    button: 0,
    relatedTarget: undefined
    }, options );

    if ( document.createEvent ) {
    event = document.createEvent( "MouseEvents" );
    event.initMouseEvent( type, options.bubbles, options.cancelable,
    options.view, options.detail,
    options.screenX, options.screenY, options.clientX, options.clientY,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.button, options.relatedTarget || document.body.parentNode );
    // IE 9+ creates events with pageX and pageY set to 0.
    // Trying to modify the properties throws an error,
    // so we define getters to return the correct values.
    if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) {
    eventDoc = event.relatedTarget.ownerDocument || document;
    doc = eventDoc.documentElement;
    body = eventDoc.body;

    Object.defineProperty( event, "pageX", {
    get: function() {
    return options.clientX +
    ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
    ( doc && doc.clientLeft || body && body.clientLeft || 0 );
    }
    });
    Object.defineProperty( event, "pageY", {
    get: function() {
    return options.clientY +
    ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
    ( doc && doc.clientTop || body && body.clientTop || 0 );
    }
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    // TODO: what is this mapping for?
    event.button = { 0:1, 1:4, 2:2 }[ event.button ] || event.button;
    }

    return event;
    },

    keyEvent: function( type, options ) {
    var event;
    options = $.extend({
    bubbles: true,
    cancelable: true,
    view: window,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    keyCode: 0,
    charCode: undefined
    }, options );

    if ( document.createEvent ) {
    try {
    event = document.createEvent( "KeyEvents" );
    event.initKeyEvent( type, options.bubbles, options.cancelable, options.view,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
    options.keyCode, options.charCode );
    // TODO: what is this supporting?
    } catch( err ) {
    event = document.createEvent( "Events" );
    event.initEvent( type, options.bubbles, options.cancelable );
    $.extend( event, {
    view: options.view,
    ctrlKey: options.ctrlKey,
    altKey: options.altKey,
    shiftKey: options.shiftKey,
    metaKey: options.metaKey,
    keyCode: options.keyCode,
    charCode: options.charCode
    });
    }
    } else if ( document.createEventObject ) {
    event = document.createEventObject();
    $.extend( event, options );
    }

    // TODO: can we hook into core's logic?
    if ( $.browser.msie || $.browser.opera ) {
    // TODO: is charCode ever <0 ? Can we just use charCode || keyCode?
    event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode;
    event.charCode = undefined;
    }

    return event;
    },

    // TODO: does this need type? Can't we just check event.type?
    dispatchEvent: function( elem, type, event ) {
    if ( elem.dispatchEvent ) {
    elem.dispatchEvent( event );
    } else if ( elem.fireEvent ) {
    elem.fireEvent( "on" + type, event );
    }
    },

    simulateFocus: function() {
    var focusinEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "focus", trigger );
    element[ 0 ].focus();

    if ( !triggered ) {
    focusinEvent = $.Event( "focusin" );
    focusinEvent.preventDefault();
    element.trigger( focusinEvent );
    element.triggerHandler( "focus" );
    }
    element.unbind( "focus", trigger );
    },

    simulateBlur: function() {
    var focusoutEvent,
    triggered = false,
    element = $( this.target );

    function trigger() {
    triggered = true;
    }

    element.bind( "blur", trigger );
    element[ 0 ].blur();

    // blur events are async in IE
    setTimeout(function() {
    // IE won't let the blur occur if the window is inactive
    if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) {
    element[ 0 ].ownerDocument.body.focus();
    }

    // Firefox won't trigger events if the window is inactive
    // IE doesn't trigger events if we had to manually focus the body
    if ( !triggered ) {
    focusoutEvent = $.Event( "focusout" );
    focusoutEvent.preventDefault();
    element.trigger( focusoutEvent );
    element.triggerHandler( "blur" );
    }
    element.unbind( "blur", trigger );
    }, 1 );
    }
    });



    /** complex events **/

    function findCenter( elem ) {
    var offset,
    document = $( elem.ownerDocument );
    elem = $( elem );
    offset = elem.offset();

    return {
    x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(),
    y: offset.top + elem.outerHeight() / 2 - document.scrollTop()
    };
    }

    $.extend( $.simulate.prototype, {
    simulateDrag: function() {
    var target = this.target,
    options = this.options,
    center = findCenter( target ),
    x = Math.floor( center.x ),
    y = Math.floor( center.y ),
    dx = options.dx || 0,
    dy = options.dy || 0,
    target = this.target,
    coord = { clientX: x, clientY: y };
    this.simulateEvent( target, "dragstart", coord );
    coord = { clientX: x + 1, clientY: y + 1 };
    this.simulateEvent( document, "mousemove", coord );
    coord = { clientX: x + dx, clientY: y + dy };
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( document, "mousemove", coord );
    this.simulateEvent( target, "mouseup", coord );
    this.simulateEvent( target, "click", coord );
    }
    });

    })( jQuery );