\n \n \n \n \n \n \n >\n );\n};\n","import React from 'react';\nimport { ContentBase } from '../animation/ContentBase';\nimport { Column, ImageColumn } from '../Column';\nimport { Logo } from '../branding/Logo';\nimport { PeopleHeadline } from '../text/PeopleHeadline';\nimport { PurpleQuarterCirclePeople } from '../branding/PurpleQuarterCirclePeople';\nimport { PurpleRectanglePeople } from '../branding/PurpleRectanglePeople';\nimport { SectionHeading } from '../text/SectionHeading';\nimport { Bio } from '../ui/Bio';\nimport { getBios } from '../../services/content';\n\nexport const People = ({ x }) => (\n \n \n x.set(0)} src=\"Telstra_Purple_Logo_RGB_White.png\" alt=\"Telstra Purple Logo\" />\n \n \n People giving purpose to technology\n \n \n \n \n \n \n Meet the Purple Peeps\n {getBios().map((bioData, index) => (\n \n ))}\n \n \n);\n","import styled from 'styled-components';\n\nexport const PurposeHeadline = styled.article`\n font-size: 3rem;\n margin: 4rem 4rem 0;\n width: 70%;\n max-width: 75rem;\n`;\n","import styled from 'styled-components';\n\nexport const TelstraPurpleIntroductionText = styled.article`\n font-size: 1.5rem;\n margin: 1rem 4rem 0 4rem;\n width: 70%;\n max-width: 75rem;\n`;\n","// YouTube API reference is here: https://developers.google.com/youtube/iframe_api_reference\n\n// Map of { id: { videoId, player } }; id is the DOM ID where the player is loaded; videoId is the video ID; player is the `YT.Player` object\nconst players = {};\n\n// Map of { id: videoId }; id is the DOM ID to load the video into; videoId is the video to load\nconst pendingPlayers = {};\n\nexport const loadYouTubeVideo = (id, videoId) => {\n if (!window.YT || !window.YT.Player) {\n // The API isn't loaded yet, so queue the player for creation later\n pendingPlayers[id] = videoId;\n return;\n }\n\n // Check whether a video is already loaded in this element\n if (players[id]) {\n // If the video loaded is the same as the video being loaded, then we're done\n if (players[id].videoId === videoId) {\n return;\n }\n\n // The YT API doesn't give a `restore()` or similar function, so we have to clean up ourselves\n // The new `div` is created to be the container for the next video\n const container = document.getElementById(id);\n if (container) {\n const replacement = document.createElement('div');\n replacement.id = id;\n container.parentElement.insertBefore(replacement, container);\n container.remove();\n }\n }\n\n // The YouTube API is unreliable, it seems; I tried several different (better) workarounds but this `setTimeout` was\n // the only thing that would reliably get the video to load on page load\n setTimeout(() => {\n players[id] = {\n videoId,\n player: new window.YT.Player(id, {\n videoId,\n width: '100%',\n height: '100%',\n playerVars: {\n controls: 0,\n // Does not disable related videos, but sticks to the same channel.\n // https://developers.google.com/youtube/player_parameters.html?playerVersion=HTML5#rel\n rel: 0\n }\n })\n };\n }, 0);\n};\n\n// This function should be called once only\nexport const initialiseYouTubeAPI = () => {\n const tag = document.createElement('script');\n tag.src = 'https://www.youtube.com/iframe_api';\n const firstScriptTag = document.getElementsByTagName('script')[0];\n firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);\n window.onYouTubeIframeAPIReady = () => {\n // If any videos attempted to load before the YT API is ready, load them again now\n Object.keys(pendingPlayers).forEach(id => {\n loadYouTubeVideo(id, pendingPlayers[id]);\n delete pendingPlayers[id];\n });\n };\n};\n\nexport const isYouTubeVideoPlaying = id =>\n players[id] && typeof players[id].player.getPlayerState === 'function' && players[id].player.getPlayerState() === 1;\n\nexport const playYouTubeVideo = id =>\n players[id] && typeof players[id].player.playVideo === 'function' && players[id].player.playVideo();\n\nexport const pauseYouTubeVideo = id =>\n players[id] && typeof players[id].player.pauseVideo === 'function' && players[id].player.pauseVideo();\n\nexport const seekYouTubeVideo = (id, time = 0) =>\n players[id] && typeof players[id].player.seekTo === 'function' && players[id].player.seekTo(time);\n","import React, { useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport styled from 'styled-components';\nimport {\n isYouTubeVideoPlaying,\n loadYouTubeVideo,\n pauseYouTubeVideo,\n playYouTubeVideo,\n seekYouTubeVideo\n} from '../../services/youtube';\nimport { sendAnalyticsEvent } from '../../services/analytics';\nimport { EditToggle } from '../admin/EditToggle';\nimport { EditableText } from '../admin/EditableText';\nimport { saveContent, updatePurposeVideo } from '../../services/content';\nimport { ProgressOverlay } from '../admin/ProgressOverlay';\nimport { isAuthenticated } from '../../services/auth';\n\nconst YouTubeVideoContainer = styled.div`\n position: relative;\n width: 100%;\n height: 100%;\n`;\n\nconst VideoEditControlsContainer = styled.div`\n position: absolute;\n top: -2rem;\n right: 0;\n text-align: right;\n z-index: 5;\n\n & button {\n font-size: large;\n }\n`;\n\nconst VideoIdContainer = styled.div`\n display: ${({ editing }) => (editing ? 'inline-block' : 'none')};\n margin-right: 1rem;\n`;\n\nconst PlayerContainer = styled.div`\n z-index: 4;\n max-width: 75rem;\n`;\n\nconst Label = styled.label`\n margin-right: 2rem;\n white-space: nowrap;\n`;\n\nconst dataBeforeEditing = {};\n\nconst videoIdTransformPatterns = [\n {\n pattern: /^https?:\\/\\/(?:www\\.)?youtube\\.com\\/watch\\?(?:.*&)?v=(.+)(?:&.*)?$/,\n replacement: '$1'\n },\n {\n pattern: /^https?:\\/\\/youtu\\.be\\/(.+)$/,\n replacement: '$1'\n }\n];\n\nexport const YouTubeVideo = ({ id, data, x }) => {\n const [videoId, setVideoId] = useState(data.videoId);\n const [editing, setEditing] = useState(false);\n const [busy, setBusy] = useState(false);\n const { region } = useParams();\n\n const setNormalisedVideoIdOrUrl = input =>\n setVideoId(\n videoIdTransformPatterns.reduce(\n (result, { pattern, replacement }) => (pattern.test(result) ? result.replace(pattern, replacement) : result),\n input\n )\n );\n\n const edit = () => {\n setEditing(true);\n Object.assign(dataBeforeEditing, data);\n };\n\n const save = async () => {\n if (isAuthenticated()) {\n setBusy(true);\n updatePurposeVideo(videoId);\n await saveContent(region);\n loadYouTubeVideo(id, videoId);\n setEditing(false);\n setBusy(false);\n }\n };\n\n const cancel = () => {\n setVideoId(dataBeforeEditing.videoId);\n setEditing(false);\n };\n\n loadYouTubeVideo(id, videoId);\n\n useEffect(\n () =>\n x.onChange(() => {\n const videoCanPlay = x.get() >= window.innerWidth / 2;\n const isPlaying = isYouTubeVideoPlaying(id);\n if (videoCanPlay && !isPlaying) {\n seekYouTubeVideo(id);\n playYouTubeVideo(id);\n sendAnalyticsEvent({ category: 'Video', action: videoId });\n } else if (!videoCanPlay && isPlaying) {\n pauseYouTubeVideo(id);\n }\n }),\n [x, id, videoId]\n );\n\n return (\n \n {isAuthenticated() && (\n \n {busy && }\n {editing && }\n \n \n \n \n \n )}\n \n \n );\n};\n","import styled from 'styled-components';\nimport { circleWidth } from '../../constants';\n\nexport const PurpleHalfCirclePurpose = styled.div`\n z-index: 2;\n background-color: ${({ theme }) => theme.primaryBlue};\n width: ${circleWidth}rem;\n height: ${circleWidth * 2}rem;\n border-bottom-right-radius: ${circleWidth * 2}rem;\n border-top-right-radius: ${circleWidth * 2}rem;\n margin-left: ${circleWidth / 2}rem;\n`;\n","import styled from 'styled-components';\nimport { circleWidth } from '../../constants';\n\nexport const PurpleRectanglePurpose = styled.div`\n z-index: 2;\n width: ${circleWidth / 2}rem;\n height: calc(100% - ${circleWidth * 2}rem);\n background-color: ${({ theme }) => theme.primaryRed};\n`;\n","import React from 'react';\nimport styled from 'styled-components';\nimport { ContentBase } from '../animation/ContentBase';\nimport { Column } from '../Column';\nimport { Logo } from '../branding/Logo';\nimport { PurposeHeadline } from '../text/PurposeHeadline';\nimport { TelstraPurpleIntroductionText } from '../text/TelstraPurpleIntroductionText';\nimport { YouTubeVideo } from '../ui/YouTubeVideo';\nimport { PurpleHalfCirclePurpose } from '../branding/PurpleHalfCirclePurpose';\nimport { PurpleRectanglePurpose } from '../branding/PurpleRectanglePurpose';\nimport { getPurposeVideo } from '../../services/content';\nimport { purposeVideoElementId } from '../../constants';\n\nconst PurposeVideoContainer = styled.div`\n flex: 1;\n margin: 4rem;\n width: 70%;\n`;\n\nexport const Purpose = ({ x }) => (\n \n \n x.set(0)} />\n \n \n We’re a collective of passionate people who give purpose to technology.\n \n Telstra Purple is a newly aggregated entity of 8 technology consulting organisations. The core of everything we\n do is deliberately purposeful - we listen, we learn, we solve problems, we design, we share. We're Telstra\n Purple.\n \n \n \n \n \n \n \n \n \n \n);\n","import React, { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useMotionValue } from 'framer-motion';\nimport { ContentCover } from './animation/ContentCover';\nimport { MovingArrowContainer } from './ui/MovingArrowContainer';\nimport { DragLine } from './ui/DragLine';\nimport { People } from './content/People';\nimport { Purpose } from './content/Purpose';\nimport { ProgressOverlay } from './admin/ProgressOverlay';\nimport { sendAnalyticsPageView } from '../services/analytics';\nimport { purposeVideoElementId } from '../constants';\nimport { isYouTubeVideoPlaying } from '../services/youtube';\nimport { initialiseInteractionTimeout, interactionOccurred } from '../services/interaction';\nimport { loadContent } from '../services/content';\n\nexport const Presentation = () => {\n const [loading, setLoading] = useState(true);\n const x = useMotionValue(0);\n const { region } = useParams();\n\n loadContent(region).then(() => setLoading(false));\n\n sendAnalyticsPageView();\n\n // Reset the slider if the window is resized\n window.addEventListener('resize', () => x.set(0));\n\n // Reset the slider after a period of non-interaction, ready for the next viewer\n initialiseInteractionTimeout(\n () => isYouTubeVideoPlaying(purposeVideoElementId),\n () => x.set(0)\n );\n\n // Moving the slider `x` position is an interaction\n x.onChange(interactionOccurred);\n\n return loading ? (\n \n ) : (\n <>\n \n \n \n \n \n \n >\n );\n};\n","import React from 'react';\nimport { Link } from 'react-router-dom';\nimport styled from 'styled-components';\nimport { PurpleHalfCirclePurpose } from './branding/PurpleHalfCirclePurpose';\nimport { PurpleRectanglePurpose } from './branding/PurpleRectanglePurpose';\nimport { Column } from './Column';\nimport { Logo } from './branding/Logo';\n\n// Hardcoded list, it's only for this component\nconst regions = [\n {\n path: '/new-south-wales',\n label: 'New South Wales'\n },\n {\n path: '/queensland',\n label: 'Queensland'\n },\n {\n path: '/singapore',\n label: 'Singapore'\n },\n {\n path: '/united-kingdom',\n label: 'United Kingdom'\n },\n {\n path: '/victoria',\n label: 'Victoria'\n },\n {\n path: '/western-australia',\n label: 'Western Australia'\n }\n];\n\nconst Container = styled.div`\n position: absolute;\n display: flex;\n justify-content: flex-start;\n align-items: flex-start;\n width: 100%;\n height: 100%;\n color: ${({ theme }) => theme.secondaryWhite};\n`;\n\nconst Heading = styled.h1`\n font-size: 2rem;\n font-weight: normal;\n`;\n\nconst Inner = styled.div`\n padding: 0.5rem 1.5rem;\n border: 2px solid ${({ theme }) => theme.secondaryWhite};\n border-radius: 8px;\n`;\n\nconst InnerContainer = styled.div`\n display: flex;\n width: 100%;\n height: 100%;\n flex-direction: column;\n align-items: center;\n align-content: center;\n justify-items: center;\n justify-content: center;\n`;\n\nconst Paragraph = styled.p`\n color: ${({ theme }) => theme.secondaryWhite};\n`;\n\nconst List = styled.ul`\n text-align: center;\n list-style: none;\n padding: 0;\n`;\n\nconst RegionLink = styled(Link)`\n color: ${({ theme }) => theme.secondaryWhite};\n font-size: x-large;\n`;\n\nexport const RegionList = () => (\n \n \n \n \n \n \n Meet Telstra Purple\n \n Please select a region to display the relevant presentation:\n \n {regions.map(({ path, label }) => (\n
\n {label}\n
\n ))}\n \n \n \n \n \n \n \n \n \n);\n","import React from 'react';\nimport styled from 'styled-components';\nimport { AuthenticationState } from 'react-aad-msal';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { toast } from 'react-toastify';\nimport { isAuthenticationEnabled } from '../../services/flags';\nimport { Spin } from '../animation/Spin';\n\nconst Container = styled.div`\n position: absolute;\n bottom: 0.5rem;\n right: 0.5rem;\n z-index: 6;\n`;\n\nconst Button = styled.button`\n color: ${({ theme }) => theme.secondaryWhite};\n background: rgba(0, 0, 0, 0.25);\n padding: 2px 4px;\n border: none;\n cursor: pointer;\n`;\n\nconst Icon = styled(FontAwesomeIcon)`\n margin-right: 4px;\n`;\n\nexport const AuthenticationControl = ({ login, logout, authenticationState, accountInfo, error }) => {\n if (!isAuthenticationEnabled()) {\n return null;\n }\n\n if (error) {\n toast.error(`Authentication error: ${error.errorMessage}`);\n }\n\n const isInProgress = authenticationState === AuthenticationState.InProgress;\n\n return (\n \n {authenticationState === AuthenticationState.Authenticated ? (\n \n ) : (\n \n )}\n \n );\n};\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.1/8 is considered localhost for IPv4.\n window.location.hostname.match(/^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)\n);\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n // eslint-disable-next-line no-param-reassign\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n // eslint-disable-next-line no-console\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n // eslint-disable-next-line no-console\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n // eslint-disable-next-line no-console\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl)\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n // eslint-disable-next-line no-console\n console.log('No internet connection found. App is running in offline mode.');\n });\n}\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n // eslint-disable-next-line no-console\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import { toast } from 'react-toastify';\n\nexport const initialiseToast = () =>\n toast.configure({\n position: toast.POSITION.TOP_CENTER,\n autoClose: 10000\n });\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport { config } from 'dotenv';\nimport 'react-toastify/dist/ReactToastify.css';\nimport './index.css';\nimport { App } from './components/App';\nimport * as serviceWorker from './serviceWorker';\nimport { initialiseToast } from './services/toast';\nimport { initialiseGoogleAnalytics } from './services/analytics';\nimport { initialiseYouTubeAPI } from './services/youtube';\nimport { initialiseIcons } from './services/icons';\n\nconfig();\n\ninitialiseToast();\ninitialiseGoogleAnalytics();\ninitialiseIcons();\ninitialiseYouTubeAPI();\n\nReactDOM.render(, document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change unregister() to register() below.\n// Note this comes with some pitfalls. Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n","import { library } from '@fortawesome/fontawesome-svg-core';\nimport {\n faEdit,\n faTimes,\n faTimesCircle,\n faUpload,\n faImage,\n faSignInAlt,\n faSignOutAlt,\n faSpinner\n} from '@fortawesome/free-solid-svg-icons';\n\nexport const initialiseIcons = () =>\n library.add(faTimes, faTimesCircle, faUpload, faEdit, faImage, faSignInAlt, faSignOutAlt, faSpinner);\n","import React from 'react';\nimport { BrowserRouter, Route, Switch } from 'react-router-dom';\nimport { AzureAD } from 'react-aad-msal';\nimport { ThemeProvider } from 'styled-components';\nimport { telstraTheme } from '../theme';\nimport { authProvider } from '../services/auth';\nimport { Presentation } from './Presentation';\nimport { RegionList } from './RegionList';\nimport { AuthenticationControl } from './admin/AuthenticationControl';\n\nexport const App = () => (\n \n {authProviderProps => (\n \n \n \n \n \n \n \n \n \n \n \n \n \n )}\n \n);\n"],"sourceRoot":""}