joseph / Monocle

A silky, tactile browser-based ebook JavaScript library.
http://monocle.inventivelabs.com.au
MIT License
743 stars 200 forks source link

JavaScript URL's in Links #214

Closed scottfr closed 10 years ago

scottfr commented 10 years ago

Javascript URL's (test) in links should be handled differently by deconstructHref.

Currently monocle opens a new page and then runs the javascript on that page. This is almost certainly what is not intended.

The javascript should be run within the current component iframe.

joseph commented 10 years ago

Yes, that makes sense. Happy to take a PR, else I'll add it to my list.

scottfr commented 10 years ago

Sorry, no pull request but this is what I came up with my own copy. It's just about 6 lines of code added to two functions marked. The two additions are marked by "// SFR XXX".

API.fitMask = function (link, mask) {
    var hrefObject = deconstructHref(link);
    var rdr = stencil.properties.reader;
    var evtData = { href: hrefObject, link: link, mask: mask }

    if (hrefObject.internal) {
      mask.setAttribute('href', 'javascript:"Skip to chapter"');
      mask.onclick = function (evt) {
        if (rdr.dispatchEvent('monocle:link:internal', evtData, true)) {
          rdr.skipToChapter(hrefObject.internal);
        }
        evt.preventDefault();
        return false;
      }
    }else if(hrefObject.passThrough){ //SFR XXX
        mask.onclick = function (evt) {
           link.click();
           evt.preventDefault();
           return true;
        }
        return; // We don't want to override the link click event
    }else {
      mask.setAttribute('href', hrefObject.external);
      mask.setAttribute('target', '_blank');
      mask.onclick = function (evt) {
        return rdr.dispatchEvent('monocle:link:external', evtData, true);
      }
    }

    link.onclick = function (evt) {
      evt.preventDefault();
      return false;
    }
  }

  // Returns an object with either:
  //
  // - an 'external' property -- an absolute URL with a protocol,
  // host & etc, which should be treated as an external resource (eg,
  // open in new window)
  //
  //   OR
  //
  // - an 'internal' property -- a relative URL (with optional hash anchor),
  //  that is treated as a link to component in the book
  //
  // A weird but useful property of <a> tags is that while
  // link.getAttribute('href') will return the actual string value of the
  // attribute (eg, 'foo.html'), link.href will return the absolute URL (eg,
  // 'http://example.com/monocles/foo.html').
  //
  function deconstructHref(elem) {

    if(elem.getAttribute("href").toLowerCase().indexOf("javascript:") == 0){//SFR XXX
        return {passThrough: true};
    }

    var loc = document.location;
    var origin = loc.protocol+'//'+loc.host;
    var href = elem.href;
    var path = href.substring(origin.length);
    var ext = { external: href };

    // Anchor tags with 'target' attributes are always external URLs.
    if (elem.getAttribute('target')) {
      return ext;
    }
    // URLs with a different protocol or domain are always external.
    //console.log("Domain test: %s <=> %s", origin, href);
    if (href.indexOf(origin) != 0) {
      return ext;
    }

    // If it is in a sub-path of the current path, it's internal.
    var topPath = loc.pathname.replace(/[^\/]*\.[^\/]+$/,'');
    if (topPath[topPath.length - 1] != '/') {
      topPath += '/';
    }
    //console.log("Sub-path test: %s <=> %s", topPath, path);
    if (path.indexOf(topPath) == 0) {
      return { internal: path.substring(topPath.length) }
    }

    // If it's a root-relative URL and it's in our list of component ids,
    // it's internal.
    var cmptIds = stencil.properties.reader.getBook().properties.componentIds;
    for (var i = 0, ii = cmptIds.length; i < ii; ++i) {
      //console.log("Component test: %s <=> %s", cmptIds[i], path);
      if (path.indexOf(cmptIds[i]) == 0) {
        return { internal: path }
      }
    }

    // Otherwise it's external.
    return ext;
  }

  return API;
}
joseph commented 10 years ago

Thanks for this — I've tweaked your suggestion and committed it to master.