| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- /* @flow */
- import type Router from '../index'
- import { History } from './base'
- import { cleanPath } from '../util/path'
- import { getLocation } from './html5'
- import { setupScroll, handleScroll } from '../util/scroll'
- import { pushState, replaceState, supportsPushState } from '../util/push-state'
- export class HashHistory extends History {
- constructor (router: Router, base: ?string, fallback: boolean) {
- super(router, base)
- // check history fallback deeplinking
- if (fallback && checkFallback(this.base)) {
- return
- }
- ensureSlash()
- }
- // this is delayed until the app mounts
- // to avoid the hashchange listener being fired too early
- setupListeners () {
- if (this.listeners.length > 0) {
- return
- }
- const router = this.router
- const expectScroll = router.options.scrollBehavior
- const supportsScroll = supportsPushState && expectScroll
- if (supportsScroll) {
- this.listeners.push(setupScroll())
- }
- const handleRoutingEvent = () => {
- const current = this.current
- if (!ensureSlash()) {
- return
- }
- this.transitionTo(getHash(), route => {
- if (supportsScroll) {
- handleScroll(this.router, route, current, true)
- }
- if (!supportsPushState) {
- replaceHash(route.fullPath)
- }
- })
- }
- const eventType = supportsPushState ? 'popstate' : 'hashchange'
- window.addEventListener(
- eventType,
- handleRoutingEvent
- )
- this.listeners.push(() => {
- window.removeEventListener(eventType, handleRoutingEvent)
- })
- }
- push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
- const { current: fromRoute } = this
- this.transitionTo(
- location,
- route => {
- pushHash(route.fullPath)
- handleScroll(this.router, route, fromRoute, false)
- onComplete && onComplete(route)
- },
- onAbort
- )
- }
- replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
- const { current: fromRoute } = this
- this.transitionTo(
- location,
- route => {
- replaceHash(route.fullPath)
- handleScroll(this.router, route, fromRoute, false)
- onComplete && onComplete(route)
- },
- onAbort
- )
- }
- go (n: number) {
- window.history.go(n)
- }
- ensureURL (push?: boolean) {
- const current = this.current.fullPath
- if (getHash() !== current) {
- push ? pushHash(current) : replaceHash(current)
- }
- }
- getCurrentLocation () {
- return getHash()
- }
- }
- function checkFallback (base) {
- const location = getLocation(base)
- if (!/^\/#/.test(location)) {
- window.location.replace(cleanPath(base + '/#' + location))
- return true
- }
- }
- function ensureSlash (): boolean {
- const path = getHash()
- if (path.charAt(0) === '/') {
- return true
- }
- replaceHash('/' + path)
- return false
- }
- export function getHash (): string {
- // We can't use window.location.hash here because it's not
- // consistent across browsers - Firefox will pre-decode it!
- let href = window.location.href
- const index = href.indexOf('#')
- // empty path
- if (index < 0) return ''
- href = href.slice(index + 1)
- return href
- }
- function getUrl (path) {
- const href = window.location.href
- const i = href.indexOf('#')
- const base = i >= 0 ? href.slice(0, i) : href
- return `${base}#${path}`
- }
- function pushHash (path) {
- if (supportsPushState) {
- pushState(getUrl(path))
- } else {
- window.location.hash = path
- }
- }
- function replaceHash (path) {
- if (supportsPushState) {
- replaceState(getUrl(path))
- } else {
- window.location.replace(getUrl(path))
- }
- }
|