1+ // Configuration constants
2+ const CONFIG = {
3+ DEFAULT_LANGUAGE : 'en-us' ,
4+ DOMAIN : 'learn.microsoft.com' ,
5+ URL_PATTERN : / h t t p s : \/ \/ l e a r n \. m i c r o s o f t \. c o m \/ ( [ ^ \/ ] + ) \/ / ,
6+ DEBUG_PARAM : 'jp-learn-microsoft-com-update-checker-debug' ,
7+ SELECTORS : {
8+ DATE_ELEMENT : 'local-time' ,
9+ THEME_BUTTON : 'button[data-theme-to][aria-pressed="true"]' ,
10+ } ,
11+ STYLES : {
12+ ALERT : {
13+ margin : '5px' ,
14+ padding : '10px' ,
15+ } ,
16+ INFO : {
17+ marginTop : '0' ,
18+ marginLeft : '3px' ,
19+ } ,
20+ } ,
21+ CLASSES : {
22+ ALERT : 'alert is-primary' ,
23+ THEMES : {
24+ dark : 'text-color-dark' ,
25+ 'high-contrast' : 'text-color-high-contrast' ,
26+ light : 'text-color-light' ,
27+ default : 'text-color' ,
28+ } ,
29+ } ,
30+ TIME_CONSTANTS : {
31+ MILLISECONDS_IN_MINUTE : 1000 * 60 ,
32+ MILLISECONDS_IN_HOUR : 1000 * 60 * 60 ,
33+ MILLISECONDS_IN_DAY : 1000 * 60 * 60 * 24 ,
34+ MILLISECONDS_IN_YEAR : 1000 * 60 * 60 * 24 * 365 ,
35+ } ,
36+ } ;
37+
138// languageLabels is a dictionary that maps message from language codes to the corresponding language
239// Default languageLabels is 'last updated on'
340// Add more languageLabels as needed
@@ -21,31 +58,78 @@ const timeAgoLabels = {
2158 // 'fr-fr': { years: 'il y a ans', days: 'il y a jours', hours: 'il y a heures', minutes: 'il y a minutes', justNow: 'à l\'instant' },
2259} ;
2360
61+ // Helper functions
62+ const getLanguageFromUrl = ( url ) => {
63+ const match = url . match ( CONFIG . URL_PATTERN ) ;
64+ return match ? match [ 1 ] : null ;
65+ } ;
66+
67+ const getEnglishUrl = ( url , currentLang ) => {
68+ return url . replace ( `/${ currentLang } /` , `/${ CONFIG . DEFAULT_LANGUAGE } /` ) ;
69+ } ;
70+
71+ const calculateTimeAgo = ( timeDifference , currentLang ) => {
72+ const { MILLISECONDS_IN_YEAR , MILLISECONDS_IN_DAY , MILLISECONDS_IN_HOUR , MILLISECONDS_IN_MINUTE } = CONFIG . TIME_CONSTANTS ;
73+
74+ const years = Math . floor ( timeDifference / MILLISECONDS_IN_YEAR ) ;
75+ const days = Math . floor ( ( timeDifference % MILLISECONDS_IN_YEAR ) / MILLISECONDS_IN_DAY ) ;
76+ const hours = Math . floor ( ( timeDifference % MILLISECONDS_IN_DAY ) / MILLISECONDS_IN_HOUR ) ;
77+ const minutes = Math . floor ( ( timeDifference % MILLISECONDS_IN_HOUR ) / MILLISECONDS_IN_MINUTE ) ;
78+
79+ const labels = timeAgoLabels [ currentLang ] || {
80+ years : 'years ago' ,
81+ days : 'days ago' ,
82+ hours : 'hours ago' ,
83+ minutes : 'minutes ago' ,
84+ justNow : 'just now'
85+ } ;
86+
87+ if ( years > 0 ) {
88+ return ` ${ years } ${ labels . years } ` ;
89+ } else if ( days > 0 ) {
90+ return ` ${ days } ${ labels . days } ` ;
91+ } else if ( hours > 0 ) {
92+ return ` ${ hours } ${ labels . hours } ` ;
93+ } else if ( minutes > 0 ) {
94+ return ` ${ minutes } ${ labels . minutes } ` ;
95+ } else {
96+ return labels . justNow ;
97+ }
98+ } ;
99+
100+ const getTextColorClass = ( theme ) => {
101+ return CONFIG . CLASSES . THEMES [ theme ] || CONFIG . CLASSES . THEMES . default ;
102+ } ;
103+
104+ const applyStyles = ( element , styles ) => {
105+ Object . entries ( styles ) . forEach ( ( [ key , value ] ) => {
106+ element . style [ key ] = value ;
107+ } ) ;
108+ } ;
109+
24110( async ( ) => {
25111 // Get current URL
26112 const currentUrl = window . location . href ;
27113
28114 // Use a regular expression to extract the language code
29- const languageCodeMatch = currentUrl . match ( / h t t p s : \/ \/ l e a r n \. m i c r o s o f t \. c o m \/ ( [ ^ \/ ] + ) \/ / ) ;
30- const currentLang = languageCodeMatch ? languageCodeMatch [ 1 ] : null ;
115+ const currentLang = getLanguageFromUrl ( currentUrl ) ;
31116 if ( ! currentLang ) return ;
32117
33118 // Check if the page(https://learn.microsoft.com/en-us) is in en-us, if so, return
34- const lang = 'en-us' ;
35- if ( currentLang === lang ) return ;
119+ if ( currentLang === CONFIG . DEFAULT_LANGUAGE ) return ;
36120
37- const debug = new URLSearchParams ( window . location . search ) . get ( "jp-learn-microsoft-com-update-checker-debug" ) ;
121+ const debug = new URLSearchParams ( window . location . search ) . get ( CONFIG . DEBUG_PARAM ) ;
38122
39123 // Get local-time tag in current page
40- const dataArticleDateElement = document . querySelector ( 'local-time' ) ;
124+ const dataArticleDateElement = document . querySelector ( CONFIG . SELECTORS . DATE_ELEMENT ) ;
41125 if ( ! dataArticleDateElement ) return ;
42126
43127 // Parse article date
44128 const articleDateStr = dataArticleDateElement . getAttribute ( "datetime" ) ;
45129 const articleDate = new Date ( articleDateStr ) ;
46130
47131 // Translate URL to English
48- const englishUrl = currentUrl . replace ( `/ ${ currentLang } /` , "/en-us/" ) ;
132+ const englishUrl = getEnglishUrl ( currentUrl , currentLang ) ;
49133
50134 try {
51135 // Get English page and parse update date
@@ -56,7 +140,7 @@ const timeAgoLabels = {
56140 const parser = new DOMParser ( ) ;
57141 const doc = parser . parseFromString ( data , "text/html" ) ;
58142
59- const englishDateStr = doc . querySelector ( 'local-time' ) ?. getAttribute ( "datetime" ) ;
143+ const englishDateStr = doc . querySelector ( CONFIG . SELECTORS . DATE_ELEMENT ) ?. getAttribute ( "datetime" ) ;
60144 if ( ! englishDateStr ) return ;
61145 const englishDate = new Date ( englishDateStr ) ;
62146
@@ -66,48 +150,21 @@ const timeAgoLabels = {
66150 const timeDifference = currentDate - englishDate ;
67151
68152 // Create a new paragraph element to display the update information
69- let timeAgo ;
70- const years = Math . floor ( timeDifference / ( 1000 * 60 * 60 * 24 * 365 ) ) ;
71- const days = Math . floor ( ( timeDifference % ( 1000 * 60 * 60 * 24 * 365 ) ) / ( 1000 * 60 * 60 * 24 ) ) ;
72- const hours = Math . floor ( ( timeDifference % ( 1000 * 60 * 60 * 24 ) ) / ( 1000 * 60 * 60 ) ) ;
73- const minutes = Math . floor ( ( timeDifference % ( 1000 * 60 * 60 ) ) / ( 1000 * 60 ) ) ;
74-
75- const labels = timeAgoLabels [ currentLang ] || {
76- years : 'years ago' ,
77- days : 'days ago' ,
78- hours : 'hours ago' ,
79- minutes : 'minutes ago' ,
80- justNow : 'just now'
81- } ;
82-
83- if ( years > 0 ) {
84- timeAgo = ` ${ years } ${ labels . years } ` ;
85- } else if ( days > 0 ) {
86- timeAgo = ` ${ days } ${ labels . days } ` ;
87- } else if ( hours > 0 ) {
88- timeAgo = ` ${ hours } ${ labels . hours } ` ;
89- } else if ( minutes > 0 ) {
90- timeAgo = ` ${ minutes } ${ labels . minutes } ` ;
91- } else {
92- timeAgo = labels . justNow ;
93- }
94- let timeAgoStr = ` (${ timeAgo } )` ;
153+ const timeAgo = calculateTimeAgo ( timeDifference , currentLang ) ;
154+ const timeAgoStr = ` (${ timeAgo } )` ;
95155
96156 const updateInfo = document . createElement ( "p" ) ;
97157 dataArticleDateElement . parentElement . appendChild ( updateInfo ) ;
98158
99159 const updateClass = ( ) => {
100160 // if theme is selected, apply appropriate text color based on theme
101- const textColorClass = ( ( theme ) => {
102- if ( theme === "dark" ) return "text-color-dark" ;
103- if ( theme === "high-contrast" ) return "text-color-high-contrast" ;
104- if ( theme === "light" ) return "text-color-light" ;
105- return "text-color" ;
106- } ) ( document . querySelector ( 'button[data-theme-to][aria-pressed="true"]' ) . getAttribute ( "data-theme-to" ) ) ;
161+ const themeButton = document . querySelector ( CONFIG . SELECTORS . THEME_BUTTON ) ;
162+ const theme = themeButton . getAttribute ( "data-theme-to" ) ;
163+ const textColorClass = getTextColorClass ( theme ) ;
107164 console . log ( "textColorClass:" , textColorClass ) ;
108165
109166 // Add icon to update info
110- informationIcon = "" ;
167+ let informationIcon = "" ;
111168
112169 console . log ( "English date:" , englishDate ) ;
113170 console . log ( "Article date:" , articleDate ) ;
@@ -116,14 +173,12 @@ const timeAgoLabels = {
116173 // Compare English date and Article date
117174 if ( englishDate > articleDate || debug === "true" ) {
118175 // Display alert if English page is updated
119- updateInfo . className = "alert is-primary" ; // <class="alert is-primary"> is defined in CSS
120- updateInfo . style . margin = "5px" ;
121- updateInfo . style . padding = "10px" ;
176+ updateInfo . className = CONFIG . CLASSES . ALERT ;
177+ applyStyles ( updateInfo , CONFIG . STYLES . ALERT ) ;
122178 informationIcon = `<span class="icon"><span class="docon docon-status-error-outline" aria-hidden="true" style="margin: 0px"></span></span>` ;
123179 } else {
124180 // Display info if English page is not updated
125- updateInfo . style . marginTop = "0" ; // <p> default margin-top is 1rem
126- updateInfo . style . marginLeft = "3px" ; // <p> default margin-left is 0
181+ applyStyles ( updateInfo , CONFIG . STYLES . INFO ) ;
127182 updateInfo . className = textColorClass ; // Apply appropriate text color based on theme
128183 }
129184
@@ -135,7 +190,7 @@ const timeAgoLabels = {
135190 }
136191 updateClass ( ) ;
137192 const observer = new MutationObserver ( updateClass ) ;
138- observer . observe ( document . querySelector ( 'button[data-theme-to][aria-pressed="true"]' ) , { attributes : true } ) ;
193+ observer . observe ( document . querySelector ( CONFIG . SELECTORS . THEME_BUTTON ) , { attributes : true } ) ;
139194 } catch ( error ) {
140195 console . error ( "Error fetching English page:" , error ) ;
141196 }
0 commit comments