diff options
Diffstat (limited to 'packages/gitbook-core/src/components/PJAXWrapper.js')
-rw-r--r-- | packages/gitbook-core/src/components/PJAXWrapper.js | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/packages/gitbook-core/src/components/PJAXWrapper.js b/packages/gitbook-core/src/components/PJAXWrapper.js new file mode 100644 index 0000000..f35a554 --- /dev/null +++ b/packages/gitbook-core/src/components/PJAXWrapper.js @@ -0,0 +1,112 @@ +const React = require('react'); +const ReactRedux = require('react-redux'); +const navigation = require('../actions/navigation'); + +/** + * Check if an element is inside a link + * @param {DOMElement} el + * @param {String} name + * @return {DOMElement|undefined + */ +function findParentByTagName(el, name) { + while (el && el !== document) { + if (el.tagName && el.tagName.toUpperCase() === name) { + return el; + } + el = el.parentNode; + } + + return false; +} + +/** + * Internal: Return the `href` component of given URL object with the hash + * portion removed. + * + * @param {Location|HTMLAnchorElement} location + * @return {String} + */ +function stripHash(location) { + return location.href.replace(/#.*/, ''); +} + +/** + * Test if a click event should be handled, + * return the new url if it's a normal lcick + */ +function getHrefForEvent(event) { + const link = findParentByTagName(event.target, 'A'); + + if (!link) + return; + + // Middle click, cmd click, and ctrl click should open + // links in a new tab as normal. + if (event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) + return; + + // Ignore cross origin links + if (location.protocol !== link.protocol || location.hostname !== link.hostname) + return; + + // Ignore case when a hash is being tacked on the current URL + if (link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location)) + return; + + // Ignore event with default prevented + if (event.defaultPrevented) + return; + + // Explicitly ignored + if (link.getAttribute('data-nopjax')) + return; + + return link.href; +} + +/* + Wrapper to bind all navigation events to fetch pages. + */ + +const PJAXWrapper = React.createClass({ + propTypes: { + children: React.PropTypes.node, + dispatch: React.PropTypes.func + }, + + onClick(event) { + const { dispatch } = this.props; + const href = getHrefForEvent(event); + + if (!href) { + return; + } + + event.preventDefault(); + dispatch(navigation.fetchPage(href)); + + }, + + onPopState(event) { + const { dispatch } = this.props; + event.preventDefault(); + + dispatch(navigation.fetchPage(location.href, { replace: true })); + }, + + componentDidMount() { + document.addEventListener('click', this.onClick, false); + window.addEventListener('popstate', this.onPopState, false); + }, + + componentWillUnmount() { + document.removeEventListener('click', this.onClick, false); + window.removeEventListener('popstate', this.onPopState, false); + }, + + render() { + return React.Children.only(this.props.children); + } +}); + +module.exports = ReactRedux.connect()(PJAXWrapper); |