Skip to content
This repository has been archived by the owner on Feb 16, 2021. It is now read-only.

Align event names with HTML5 DOM event names #105

Closed
JamesMGreene opened this issue Feb 22, 2013 · 9 comments · Fixed by #371
Closed

Align event names with HTML5 DOM event names #105

JamesMGreene opened this issue Feb 22, 2013 · 9 comments · Fixed by #371

Comments

@JamesMGreene
Copy link
Member

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?

@JamesMGreene
Copy link
Member Author

Any opinion, @jonrohan?

@JamesMGreene
Copy link
Member Author

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

@JamesMGreene
Copy link
Member Author

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

@JamesMGreene
Copy link
Member Author

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.

@JamesMGreene
Copy link
Member Author

@jonrohan Thoughts?

@JamesMGreene
Copy link
Member Author

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.

@JamesMGreene
Copy link
Member Author

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.

@JamesMGreene
Copy link
Member Author

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();
    }
  });

})();

@JamesMGreene
Copy link
Member Author

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 JamesMGreene changed the title Consider aligning event names with HTML5 DOM event names Align event names with HTML5 DOM event names Mar 21, 2014
@JamesMGreene JamesMGreene self-assigned this Mar 21, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant