Align event names with HTML5 DOM event names #105

Closed
JamesMGreene opened this Issue Feb 22, 2013 · 9 comments

Comments

Projects
None yet
1 participant
Owner

JamesMGreene commented Feb 22, 2013

Consider aligning event names with existing IE/HTML5 DOM event names, specifically:

  • onDataRequested → onBeforeCopy and/or onCopy
  • onComplete → "onAfterCopy" (this event is not actually present in any existing DOM APIs 😕)

SemVer-wise, we could either change the names with a major version rev or else alias them in a minor version rev.

Thoughts, @jonrohan?

Owner

JamesMGreene commented May 7, 2013

Any opinion, @jonrohan?

Owner

JamesMGreene commented May 17, 2013

These are now being standardized: http://www.w3.org/TR/clipboard-apis/

Owner

JamesMGreene commented Jul 14, 2013

The old jquery.zclip plugin [which wrapped ZeroClipboard] offered beforeCopy and afterCopy events like this.

Owner

JamesMGreene commented Jul 14, 2013

If we are trying to best align the event names with the HTML5 Clipboard API, then we would want to use onCopy instead of onBeforeCopy. onBeforeCopy is intended for updating DOM/UI components whereas onCopy is where they recommend setting the outgoing clipboard data.

That said, we're already looking at adding an onAfterCopy event, which does not exist in the HTML5 Clipboard API, so we're already breaking the mold there. To me, onCopy sounds like an event that would occur after the clipboard injection has occurred but that is not actually the case.

To avoid confusion, I'd recommend we go with onBeforeCopy and onAfterCopy like zclip had done.

Owner

JamesMGreene commented Jul 14, 2013

@jonrohan Thoughts?

Owner

JamesMGreene commented Jul 14, 2013

On second thought, following the HTML5 Clipboard API in terms of handler invocation would also be somewhat limiting as the handler wouldn't have access to the current client (ZeroClipboard instance). As such, we might want to either:

  1. leave both the old and new event names; or
  2. use a single set of event names but control their execution environments via a config option (e.g. emulateHtml5: true) defaulted to false.
Owner

JamesMGreene commented Jul 14, 2013

Actually, the HTML5 Clipboard API spec doesn't comment on the context of this, therefore it would just be equal to the client after all. This is different than our current context, though, in which we supply the glued element as this and supply the client as an argument.

Owner

JamesMGreene commented Jul 15, 2013

Here's an example for the beforeCopy/copy event(s), which would replace the current dataRequested event.

New code inside of ZeroClipboard.js:

var ZeroClipboardDataTransferItem = function(data, type) {
  this.kind = 'string';
  this.type = type;
  this.getAsString = function(callback) {
    if (typeof callback === 'function') {
      window.setTimeout(function() { callback(data); }, 0);
    }
  };
};
ZeroClipboardDataTransferItem.prototype.getAsFile = function() {
  return null;
};

var ZeroClipboardEvent = function(client, element, lock) {
  // TODO: This `CustomEvent` code will not work in IE or PhantomJS 1.x, and maybe not
  //       in JSDOM either but similar mechanisms are available.
  var evt = new CustomEvent('copy', { bubbles: false, cancelable: true, detail: undefined });

  evt.target = element;
  evt.currentTarget = client;
  evt.eventPhase = 2;  // Event.AT_TARGET

  // For maintaining private data
  var dataMap = {},
       mapPrefix = 'type_',
       _isImmediatePropagationStopped = false;

  evt.isImmediatePropagationStopped = function() {
    return _isImmediatePropagationStopped;
  };

  evt.stopImmediatePropagation = function() {
    _isImmediatePropagationStopped = true;
  };

  //
  // ClipboardEvent custom properties and methods
  //

  // Override the `evt.preventDefault` method to actually write to the clipboard
  evt.preventDefault = function() {
    if (dataMap.hasOwnProperty(mapPrefix + 'text/plain')) {
      client.setText(dataMap[mapPrefix + 'text/plain']);
    }
    // TODO: Add support for all text-based content types in the future
  };

  // Duck-typing `DataTransfer`
  // http://www.w3.org/TR/html5/editing.html#datatransfer
  evt.clipboardData = {

    // http://www.w3.org/TR/html5/editing.html#dom-datatransfer-cleardata
    clearData: function() {
      dataMap = {};
      evt.clipboardData.types.length = 0;
      evt.clipboardData.items.clear();
    },

    // http://www.w3.org/TR/html5/editing.html#dom-datatransfer-setdata
    setData: function(type, data) {
      if (!dataMap.hasOwnProperty(mapPrefix + type)) {
        var index = evt.clipboardData.types.length;
        dataMap[mapPrefix + type] = index;
        evt.clipboardData.types[index] = type;
      }
      evt.clipboardData.items[index] = new ZeroClipboardDataTransferItem(data, type);
    },

    // http://www.w3.org/TR/html5/editing.html#dom-datatransfer-getdata
    getData: function(type) {
      return undefined;  // Permission denied during a 'copy' event
    },

    // Array of Strings
    // Should be read-only but that makes no sense for ZeroClipboard
    // http://www.w3.org/TR/html5/editing.html#dom-datatransfer-types
    // TODO: Add support for all text-based content types in the future:
    //         ['text/plain', 'text/html', 'application/rtf']
    types: [],

    // Array of [ZeroClipboard]DataTransferItem objects
    // Duck-typing `DataTransferItemList` isn't worth it for this one
    // Should be read-only but that makes no sense for ZeroClipboard
    // http://www.w3.org/TR/html5/editing.html#dom-datatransfer-items
    // http://www.w3.org/TR/html5/editing.html#datatransferitemlist
    // http://www.w3.org/TR/html5/editing.html#datatransferitem
    items: [];
  };

  return evt;
};

// To dispatch the event
var evt = new ZeroClipboardEvent(client, element);

// TODO: This `forEach` won't work in IE<9
client.handlers['copy'].forEach(function(fn) {
  if (!evt.isImmediatePropagationStopped()) {
    // NOTE: We currently pass `element` as the context (`this`) rather than `client` as below
    //       but `client` will be the correct choice if following the HTML5 Clipboard API model.
    fn.call(client, evt);
  }
});

Usage:

(function() {
  var textMsg = 'ZeroClipboard injected this plain text!';
  var htmlMsg = '<b>ZeroClipboard</b> injected <i>this</i> HTML!';
  var richTextMsg = '{\\rtf1\\ansi\\n\n{\\b ZeroClipboard} injected {\\i this} RichText (RTF)!\n}';

  var clip = new ZeroClipboard($('.clipboard'));

  // Could be `.on('copy', fn)` or `.on('beforecopy', fn)` and still align with the HTML5
  // Clipboard API. Technically speaking, `copy` would be more correct; I personally find it
  // more confusing, though.
  clip.on('copy', function(e) {
    // `this` === `clip`
    // `e.target` === glued element
    // `e.currentTarget` === `clip`/`this`

    // All of this callback code is aligned with the HTML5 Clipboard API
    if (e.clipboardData) {
      e.clipboardData.clearData();
      e.clipboardData.setData('text/plain', textMsg);

      // TODO: Add support for all text-based content types in the future
      e.clipboardData.setData('text/html', htmlMsg);
      e.clipboardData.setData('application/rtf', richTextMsg);

      // Wacky: Must prevent default in order to get this copied into the clipboard
      e.preventDefault();
    }
  });

})();
Owner

JamesMGreene commented Aug 10, 2013

Also, we could even get our HTML5-like implementation even closer: if the "copy" event handler does not call evt.preventDefault(), the defined default behavior is to copy the user's active text selection, if any. From the spec:

Its default action is to place the selected data on the clipboard.

Grabbing the selection is very easy to do using the long-standing TextRange/Range and Selection APIs. Been there, done that. 👍

JamesMGreene changed the title from Consider aligning event names with HTML5 DOM event names to Align event names with HTML5 DOM event names Mar 21, 2014

JamesMGreene self-assigned this Mar 21, 2014

@JamesMGreene JamesMGreene added a commit to JamesMGreene/zeroclipboard that referenced this issue Mar 26, 2014

@JamesMGreene JamesMGreene Event callback model mimics DOM Event API model.
Also changed all event names.

Fixes #286.
Fixes #285.
Fixes #105.
d5c30b6

@JamesMGreene JamesMGreene added a commit to JamesMGreene/zeroclipboard that referenced this issue Mar 26, 2014

@JamesMGreene JamesMGreene Event callback model mimics DOM Event API model.
Also changed all event names.

Fixes #286.
Fixes #285.
Fixes #105.
4bca90b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment