Kevnz
7/19/2013 - 2:24 AM

ios-linkfix.js

/*global YUI*/
/**
 * Prevents iOS from showing the URL bar for cancelled links
 * 
 * @module ios-linkfix
 * @requires base-build, event-touch, node-base, node-event-delegate, plugin
 */
YUI.add("ios-linkfix", function (Y, NAME) {
  var HREF = "href",
    DATA = "data-" + NAME,
    SELECTOR = "selector",
    CLICK = "click",
    HOST = "host",
    TOUCHSTART = "touchstart",
    TOUCHEND = "touchend",
    PLACEHOLDER = "#linkfix",
    ROOT = ".",
    isIOS = Y.UA.ios,
    ATTRS = {};

  ATTRS[SELECTOR] = { value: ROOT, writeOnce: true };
  /**
   * Plugin for fixing ugly cancelled link behavior on iOS
   *
   * @class IOSLinkFix
   * @extends Plugin.Base
   * @constructor
   */
  Y.IOSLinkFix = Y.Base.create(NAME, Y.Plugin.Base, [], {
    initializer: function () {
      var host = this.get(HOST),
        selector = this.get(SELECTOR),
        isHost = (ROOT === selector);
      if (!isIOS) {
        return;
      }
      if (isHost) {
        this._hideHref(host);
        this._ev = [
          host.on(TOUCHSTART, this._onTouchStart),
          host.on(TOUCHEND, this._onTouchEnd),
          host.on(CLICK, this._onClick)
        ];
      } else {
        host.all(selector).each(this._hideHref);
        this._ev = [
          host.delegate(TOUCHSTART, this._onTouchStart, selector),
          host.delegate(TOUCHEND, this._onTouchEnd, selector),
          host.delegate(CLICK, this._onClick, selector)
        ];
      }
    },
    _onTouchStart: function (e) {
      var link = e.currentTarget;
      link.setAttribute(HREF, link.getAttribute(DATA));
    },
    _onTouchEnd: function (e) {
      var link = e.currentTarget;
      link.setAttribute(HREF, PLACEHOLDER);
    },
    _onClick: function (e) {
      console.log(e);
      e.preventDefault();
    },
    _hideHref: function (el) {
      el.
        setAttribute(DATA, el.getAttribute(HREF)).
        setAttribute(HREF, PLACEHOLDER);
    },
    _cleanup: function (el) {
      el.
        setAttribute(HREF, el.setAttribute(DATA)).
        removeAttribute(DATA);
    },
    destructor: function () {
      var selector = this.get(SELECTOR),
        host = this.get(HOST),
        isHost = (ROOT === selector);
      if (!isIOS) {
        return;
      }
      if (isHost) {
        this._cleanup(host);
      } else {
        host.all(selector).each(this._cleanup);
      }
      Y.Array.each(this._ev, function (ev) {
        ev.detach();
      });
    }
  }, {
    /**
    Returns the original href attribute of the link for elements using IOSLinkFix,
    regardless of whether they're iOS or not.

    @method getHREFAttribute
    @for IOSLinkFix
    @static
    @param {Node} link A Node wrapper around an HTMLAnchorElement.
    @returns {String} The original href attribute
    **/
    getHREFAttribute: function (link) {
      return link.getAttribute(isIOS && link.hasAttribute(DATA) ? DATA : HREF);
    },
    /**
    Returns the original href attribute of the link for elements using IOSLinkFix,
    regardless of whether they're iOS or not.

    @method getHREF
    @for IOSLinkFix
    @static
    @param {Node} link A Node wrapper around an HTMLAnchorElement.
    @returns {String} The fully qualified URL of the anchor
    **/
    getHREF: function (link) {
      var href = link.get(HREF);
      if (isIOS && link.hasAttribute(DATA)) {
        href = href.replace(PLACEHOLDER, link.getAttribute(DATA));
      }
      return href;
    },
    NS: "linkfix",
    /**
      Selector to search for prevented links. If "." will listen on
      the current node.
      @attribute selector
      @default "."
      @type String
      @writeOnce
    **/
    ATTRS: ATTRS
  });

}, "3.5.1", {
  requires: ["base-build", "event-touch", "node-base", "node-event-delegate", "plugin"]
});