Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support computers with touch screen #215

Merged
merged 18 commits into from
Jul 25, 2024
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [ 17, 'latest' ]
node: [ 21 ]

steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules/
.DS_Store
storybook-static
coverage/
cypress/screenshots
5 changes: 4 additions & 1 deletion cypress/e2e/active.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ describe( 'Check the Hover and Click Event', () => {

it( 'Check the Preview by hovering over the Span and then leaving', () => {

preview.getPreviewSpan().first().trigger( 'mouseenter', 'right' )
preview.getPreviewSpan().first().trigger( 'pointerenter', {
position: 'right',
pointerType: 'mouse'
} )

preview.checkPreview()

Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/mobile-gallery.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe( 'Check the Gallery Pages in Mobile View', () => {

it( 'Check the Gallery Pages Movement by Swiping', () => {
// Open the Preview
preview.getPreviewSpan().first().click()
preview.getPreviewSpan().first().trigger( 'pointerenter', { pointerType: 'touch' } )
const i = 0
// Check if the Images exist
preview.getBodyGalleryImages().its( 'length' ).then( () => {
Expand Down
8 changes: 4 additions & 4 deletions cypress/e2e/mobile-layout.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe( 'Check the different Layout in Mobile Wikipedia Preview ', () => {

it( 'Check the Wikipedia Preview in Standard Layout', () => {
// Opens Mobile Preview
preview.getPreviewSpan().eq( 0 ).click()
preview.getPreviewSpan().eq( 0 ).trigger( 'pointerenter', { pointerType: 'touch' } )
// Checks the preview
preview.checkMobilePreview()
preview.checkPreview()
Expand All @@ -23,7 +23,7 @@ describe( 'Check the different Layout in Mobile Wikipedia Preview ', () => {
it.skip( 'Check the Wikipedia Preview in Offline Layout', () => {
goOffline()
// Opens Mobile Preview
preview.getPreviewSpan().first().click()
preview.getPreviewSpan().first().trigger( 'pointerenter', { pointerType: 'touch' } )
// Checks the preview
preview.checkMobilePreview()
preview.checkPreviewOffline()
Expand All @@ -35,7 +35,7 @@ describe( 'Check the different Layout in Mobile Wikipedia Preview ', () => {

it( 'Check the Wikipedia Preview in Error Layout', () => {
// Opens Mobile Preview
preview.getPreviewSpan().eq( 1 ).click()
preview.getPreviewSpan().eq( 1 ).trigger( 'pointerenter', { pointerType: 'touch' } )
// Checks the preview
preview.checkMobilePreview()
preview.checkPreviewError()
Expand All @@ -46,7 +46,7 @@ describe( 'Check the different Layout in Mobile Wikipedia Preview ', () => {

it( 'Check the Wikipedia Preview in Disambiguation Layout', () => {
// Opens Mobile Preview
preview.getPreviewSpan().eq( 2 ).click()
preview.getPreviewSpan().eq( 2 ).trigger( 'pointerenter', { pointerType: 'touch' } )
// Checks the preview
preview.checkMobilePreview()
preview.checkPreview()
Expand Down
8 changes: 4 additions & 4 deletions src/event.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isTouch, isVerticallyScrollable } from './utils'
import { isMobile, isVerticallyScrollable } from './utils'
import { showFullscreenGallery } from './gallery/fullscreen'

export const customEvents = ( popup ) => {
Expand Down Expand Up @@ -84,13 +84,13 @@ export const customEvents = ( popup ) => {
} )
}

if ( isTouch ) {
if ( element.component.closeBtn ) {
addEventListener( element.component.closeBtn, 'click', popup.hide )
}

if ( isTouch ) {
if ( isMobile ) {
const darkScreen = document.querySelector( '.wp-dark-screen' )
addEventListener( darkScreen, 'click', popup.hide, true )
addEventListener( darkScreen, 'pointerup', popup.hide, true )
} else {
addEventListener( element, 'mouseleave', onMouseLeave )
addEventListener( element.currentTargetElement, 'mouseleave', onMouseLeave )
Expand Down
64 changes: 40 additions & 24 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { createPopup } from './popup'
import { createTouchPopup } from './touchPopup'
import { renderPreview, renderLoading, renderError, renderDisambiguation, renderOffline } from './preview'
import {
getWikipediaAttrFromUrl, buildWikipediaUrl, isTouch, getDir, isOnline,
version, getAnalyticsQueryParam, getElement
getWikipediaAttrFromUrl, buildWikipediaUrl, getDir, isOnline,
version, getAnalyticsQueryParam, getElement, isMobile
} from './utils'

const invokeCallback = ( events, name, params ) => {
Expand All @@ -23,7 +23,7 @@ const invokeCallback = ( events, name, params ) => {
// getPreviewHtml is meant to be used by the Wordpress plugin only
const getPreviewHtml = ( title, lang, callback ) => {
requestPagePreviewWithMedia( lang, title, ( data ) => {
callback( renderPreview( lang, data, isTouch ) )
callback( renderPreview( lang, data, isMobile ) )
} )
}

Expand Down Expand Up @@ -74,7 +74,7 @@ function init( {
} ) {
popupContainer = getElement( popupContainer ) || document.body
const globalLang = lang
const popup = isTouch ?
const popup = isMobile ?
createTouchPopup( popupContainer ) :
createPopup( popupContainer )
const popupEvents = customEvents( popup )
Expand All @@ -85,6 +85,7 @@ function init( {

const showPopup = ( e, refresh = false ) => {
e.preventDefault()
e.stopPropagation()

const popupId = Date.now()
const { currentTarget } = refresh ? last : e
Expand All @@ -107,7 +108,7 @@ function init( {
popup.loading = true
popup.dir = dir
popup.show(
renderLoading( isTouch, localLang, dir, currentColorScheme ),
renderLoading( isMobile, localLang, dir, currentColorScheme ),
currentTarget,
pointerPosition
)
Expand All @@ -123,18 +124,18 @@ function init( {
popup.title = title
if ( data.type === 'standard' ) {
popup.show(
renderPreview( localLang, data, isTouch, currentColorScheme ),
renderPreview( localLang, data, isMobile, currentColorScheme ),
currentTarget,
pointerPosition
)
popup.media = data.media
invokeCallback( events, 'onShow', [ title, localLang, 'standard' ] )
} else if ( data.type === 'disambiguation' ) {
const content = data.extractHtml ?
renderPreview( localLang, data, isTouch, currentColorScheme ) :
renderPreview( localLang, data, isMobile, currentColorScheme ) :
// fallback message when no extract is found on disambiguation page
renderDisambiguation(
isTouch,
isMobile,
localLang,
data.title,
data.dir,
Expand All @@ -150,14 +151,14 @@ function init( {
} else {
if ( isOnline() ) {
popup.show(
renderError( isTouch, localLang, title, dir, currentColorScheme ),
renderError( isMobile, localLang, title, dir, currentColorScheme ),
currentTarget,
pointerPosition
)
invokeCallback( events, 'onShow', [ title, localLang, 'error' ] )
} else {
popup.show(
renderOffline( isTouch, localLang, dir, currentColorScheme ),
renderOffline( isMobile, localLang, dir, currentColorScheme ),
currentTarget,
pointerPosition
)
Expand Down Expand Up @@ -189,18 +190,34 @@ function init( {
} )
}

popup.subscribe( popupEvents )
const onPointerEnter = ( pointerEvent ) => {
showPopup( pointerEvent )
}

const registerPreviewEvents = ( node ) => {
node.addEventListener( 'pointerenter', onPointerEnter )
}

const preventTapFromNavigatingLink = ( node ) => {
// The click event still receives a MouseEvent instead of the newer PointerEvent
// in some browsers so we have to grab the pointerType from the preceding pointerdown event.
let currentPointerType = null
node.addEventListener( 'pointerdown', ( e ) => {
currentPointerType = e.pointerType
} )
node.addEventListener( 'click', ( e ) => {
if ( currentPointerType === 'touch' ) {
e.preventDefault()
e.stopPropagation()
}
} )
}

forEachRoot( root, ( localRoot ) => {
Array.prototype.forEach.call(
localRoot.querySelectorAll( selector ),
( node ) => {
if ( isTouch ) {
node.addEventListener( 'click', showPopup )
} else {
node.addEventListener( 'mouseenter', showPopup )
}

registerPreviewEvents( node )
foundSelectorLinks.push( {
text: node.textContent,
title: node.getAttribute( 'data-wp-title' ) || node.textContent,
Expand All @@ -219,11 +236,8 @@ function init( {
if ( matches ) {
node.setAttribute( 'data-wp-title', matches.title )
node.setAttribute( 'data-wp-lang', matches.lang )
if ( isTouch ) {
node.addEventListener( 'click', showPopup )
} else {
node.addEventListener( 'mouseenter', showPopup )
}
registerPreviewEvents( node )
preventTapFromNavigatingLink( node )

foundDetectLinks.push( {
text: node.textContent,
Expand All @@ -236,18 +250,20 @@ function init( {
} )
}

popup.subscribe( popupEvents )

if ( debug ) {
/* eslint-disable no-console */
console.group( 'Wikipedia Preview [debug mode]' )
console.group( `Searching for "${ selector }" inside ${ root }, Total links found: ${ foundSelectorLinks.length }` )
foundSelectorLinks.forEach( ( link, index ) => {
console.log( index + 1, `${ link.text } -> ${ decodeURI( buildWikipediaUrl( link.lang, link.title, isTouch, false ) ) }` )
console.log( index + 1, `${ link.text } -> ${ decodeURI( buildWikipediaUrl( link.lang, link.title, isMobile, false ) ) }` )
} )
console.groupEnd()
if ( detectLinks ) {
console.group( `Searching for links to Wikipedia, Total links found: ${ foundDetectLinks.length }` )
foundDetectLinks.forEach( ( link, index ) => {
console.log( index + 1, `${ link.text } -> ${ decodeURI( buildWikipediaUrl( link.lang, link.title, isTouch, false ) ) }` )
console.log( index + 1, `${ link.text } -> ${ decodeURI( buildWikipediaUrl( link.lang, link.title, isMobile, false ) ) }` )
} )
console.groupEnd()
}
Expand Down
40 changes: 20 additions & 20 deletions src/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { buildWikipediaUrl, getLinkIconSvg } from './utils'
import { getGallery } from './gallery'
import '../style/preview.less'

const getPreviewHeader = ( lang, isTouch, imageUrl = '', media = [] ) => {
const getPreviewHeader = ( lang, imageUrl = '', media = [] ) => {
const showThumbnail = imageUrl !== '' && media.length > 0 && media.length < 3
const thumbnail = imageUrl || media[ 0 ] && media[ 0 ].thumb
return `
<div class= "wikipediapreview-header ${ showThumbnail ? '' : 'wikipediapreview-header-no-thumb' }">
${ showThumbnail ? `<div class="wikipediapreview-header-image" style="${ `background-image:url('${ thumbnail }');background-size:cover;` }"></div>` : '' }
<div class="wikipediapreview-header-wordmark wikipediapreview-header-wordmark-${ lang }"></div>
${ isTouch ? '<div class="wikipediapreview-header-closebtn"></div>' : '' }
<div class="wikipediapreview-header-closebtn"></div>
</div>
`.trim()
}
Expand All @@ -29,31 +29,31 @@ const getPreviewBody = ( type, message, cta ) => {
`.trim()
}

const getReadOnWikiCta = ( lang, title, isTouch ) => {
return `<a href="${ buildWikipediaUrl( lang, title, isTouch ) }" target="_blank" class="wikipediapreview-footer-link-cta">${ msg( lang, 'read-on-wiki' ) }</a>`
const getReadOnWikiCta = ( lang, title, isMobile ) => {
return `<a href="${ buildWikipediaUrl( lang, title, isMobile ) }" target="_blank" class="wikipediapreview-footer-link-cta">${ msg( lang, 'read-on-wiki' ) }</a>`
}

const render = (
lang, isTouch, dir, headerContent, bodyContent, prefersColorScheme
lang, isMobile, dir, headerContent, bodyContent, prefersColorScheme
) => {
const colorScheme = prefersColorScheme === 'detect' ? '' : `wikipediapreview-${ prefersColorScheme }-theme`
return `
<div class="wikipediapreview ${ isTouch ? 'mobile' : '' } ${ colorScheme }" lang="${ lang }" dir="${ dir }">
<div class="wikipediapreview ${ isMobile ? 'mobile' : '' } ${ colorScheme }" lang="${ lang }" dir="${ dir }">
${ headerContent }
${ bodyContent }
</div>
`.trim()
}

const renderPreview = ( lang, data, isTouch, prefersColorScheme ) => {
const renderPreview = ( lang, data, isMobile, prefersColorScheme ) => {
const imageUrl = data.imgUrl,
bodyContent = `
${ getGallery( data.media ) }
<div class="wikipediapreview-body">
${ data.extractHtml }
<div class="wikipediapreview-footer">
<div class="wikipediapreview-footer-link">
<a href="${ buildWikipediaUrl( lang, data.title, isTouch ) }"
<a href="${ buildWikipediaUrl( lang, data.title, isMobile ) }"
class="wikipediapreview-footer-link-cta" target="_blank"
>
${ msg( lang, 'read-more' ) }
Expand All @@ -67,15 +67,15 @@ const renderPreview = ( lang, data, isTouch, prefersColorScheme ) => {

return render(
lang,
isTouch,
isMobile,
data.dir,
getPreviewHeader( lang, isTouch, imageUrl, data.media ),
getPreviewHeader( lang, imageUrl, data.media ),
bodyContent,
prefersColorScheme
)
}

const renderLoading = ( isTouch, lang, dir, prefersColorScheme ) => {
const renderLoading = ( isMobile, lang, dir, prefersColorScheme ) => {
const bodyContent = `
<div class="wikipediapreview-body wikipediapreview-body-loading">
<div class="wikipediapreview-body-loading-line larger"></div>
Expand All @@ -92,28 +92,28 @@ const renderLoading = ( isTouch, lang, dir, prefersColorScheme ) => {
<div class="wikipediapreview-footer-loading"></div>
`.trim()

return render( lang, isTouch, dir, getPreviewHeader( lang, isTouch ), bodyContent, prefersColorScheme )
return render( lang, isMobile, dir, getPreviewHeader( lang ), bodyContent, prefersColorScheme )
}

const renderError = ( isTouch, lang, title, dir, prefersColorScheme ) => {
const renderError = ( isMobile, lang, title, dir, prefersColorScheme ) => {
const message = `<span>${ msg( lang, 'preview-error-message' ) }</span>`
const cta = getReadOnWikiCta( lang, title, isTouch )
const cta = getReadOnWikiCta( lang, title, isMobile )

return render( lang, isTouch, dir, getPreviewHeader( lang, isTouch ), getPreviewBody( 'error', message, cta ), prefersColorScheme )
return render( lang, isMobile, dir, getPreviewHeader( lang ), getPreviewBody( 'error', message, cta ), prefersColorScheme )
}

const renderDisambiguation = ( isTouch, lang, title, dir, prefersColorScheme ) => {
const renderDisambiguation = ( isMobile, lang, title, dir, prefersColorScheme ) => {
const message = `<span>${ msg( lang, 'preview-disambiguation-message', title ) }</span>`
const cta = getReadOnWikiCta( lang, title, isTouch )
const cta = getReadOnWikiCta( lang, title, isMobile )

return render( lang, isTouch, dir, getPreviewHeader( lang, isTouch ), getPreviewBody( 'disambiguation', message, cta ), prefersColorScheme )
return render( lang, isMobile, dir, getPreviewHeader( lang ), getPreviewBody( 'disambiguation', message, cta ), prefersColorScheme )
}

const renderOffline = ( isTouch, lang, dir, prefersColorScheme ) => {
const renderOffline = ( isMobile, lang, dir, prefersColorScheme ) => {
const message = `<span>${ msg( lang, 'preview-offline-message' ) }</span>`
const cta = `<a>${ msg( lang, 'preview-offline-cta' ) }</a>`

return render( lang, isTouch, dir, getPreviewHeader( lang, isTouch ), getPreviewBody( 'offline', message, cta ), prefersColorScheme )
return render( lang, isMobile, dir, getPreviewHeader( lang ), getPreviewBody( 'offline', message, cta ), prefersColorScheme )
}

export { renderPreview, renderLoading, renderError, renderDisambiguation, renderOffline }
13 changes: 10 additions & 3 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
const regexList = [
// https://zh.wikipedia.org/wiki/前岐镇"
// https://en.wikipedia.org/wiki/Cat#Section
/^https?:\/\/([\w-]{2,}\.)?(m\.)?wikipedia\.org\/wiki\/([^?]+)/,

Check warning on line 24 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

Unsafe Regular Expression

Check warning on line 24 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

Unsafe Regular Expression

Check warning on line 24 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

Unsafe Regular Expression
// https://en.wikipedia.org/w/index.php?title=Cat
// https://zh.wikipedia.org/w/index.php?title=太阳帆&action=purge
/^https?:\/\/([\w-]{2,}\.)?(m\.)?wikipedia\.org\/w\/index.php\?title=([^&]+)/

Check warning on line 27 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

Unsafe Regular Expression

Check warning on line 27 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

Unsafe Regular Expression

Check warning on line 27 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

Unsafe Regular Expression
]

for ( let i = 0; i < regexList.length; i++ ) {
Expand All @@ -41,8 +41,15 @@
return null
}

const isTouch = 'ontouchstart' in window || ( navigator.maxTouchPoints > 0 ) ||
( navigator.msMaxTouchPoints > 0 )
/**
* Check if the device is a mobile device
*/
const isMobile = !!window.matchMedia( '( max-width: 768px )' ).matches

/**
* Check if the device is a touch device
*/
const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0

const isOnline = () => window.navigator.onLine

Expand Down Expand Up @@ -132,5 +139,5 @@
export {
getWikipediaAttrFromUrl, isTouch, isOnline, getDir, buildMwApiUrl,
convertUrlToMobile, strip, sanitizeHTML, getDeviceSize, getAnalyticsQueryParam,
buildWikipediaUrl, version, logError, getElement, getLinkIconSvg, isVerticallyScrollable
buildWikipediaUrl, version, logError, getElement, getLinkIconSvg, isVerticallyScrollable, isMobile

Check warning on line 142 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

This line has a length of 102. Maximum allowed is 100

Check warning on line 142 in src/utils.js

View workflow job for this annotation

GitHub Actions / build (21)

This line has a length of 102. Maximum allowed is 100
}
Loading
Loading