Skip to content

Commit

Permalink
Merge pull request #1 from spilpind/intial_logic
Browse files Browse the repository at this point in the history
Intial logic
  • Loading branch information
Anigif authored Apr 30, 2021
2 parents 993af3c + cb66e5b commit dccaecc
Show file tree
Hide file tree
Showing 41 changed files with 1,897 additions and 1 deletion.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#Taken from here: https://github.com/Kotlin/kmm-sample/blob/master/.gitignore

*.iml
.gradle/
build/
.idea/
.DS_STORE
local.properties
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 woar.it
Copyright (c) 2021 Dansk Pind Union (spilpind.dk)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
11 changes: 11 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
allprojects {
repositories {
mavenCentral()
maven { setUrl("https://kotlin.bintray.com/kotlinx/") }
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-js-wrappers") }
}
}

plugins {
kotlin("multiplatform") version "1.4.32" apply false
}
20 changes: 20 additions & 0 deletions chrome-extension/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
kotlin("js")
}

dependencies {
implementation(project(":common"))

implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
}

kotlin {
js(LEGACY) {
browser{
commonWebpackConfig{
outputFileName = "stick-calendar.js"
}
}
binaries.executable()
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added chrome-extension/releases/0_1_0.zip
Binary file not shown.
194 changes: 194 additions & 0 deletions chrome-extension/src/main/kotlin/DomDateReplacer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import dk.spilpind.stickcalendar.StickCalendar.toStickDate
import kotlinx.datetime.*
import org.w3c.dom.*

/**
* Replaces all gregorian dates of [document] by with their corresponding stick date. Any dates added later to the
* document are also replaced, this is done by the observing relevant mutations
*/
class DomDateReplacer(private val document: Document) {

private val mutationObserverOptions = MutationObserverInit(
subtree = true,
childList = true,
attributes = true,
attributeFilter = arrayOf("style"),
attributeOldValue = true,
characterData = true,
)

private val mutationObserver = MutationObserver { mutations, _ ->
mutations.forEach { mutationRecord ->
handleMutation(mutationRecord)
}
}

init {
mutationObserver.observe(document, mutationObserverOptions)

replaceDatesInTree(document)
}

private fun handleMutation(mutationRecord: MutationRecord) {
if (mutationRecord.type == "childlist") {
val nodes = mutationRecord.addedNodes
for (index in 0.until(nodes.length)) {
nodes[index]?.let { node ->
replaceDatesInTree(node)
}
}

return
}

val element = mutationRecord.target as? HTMLElement
if (element?.style?.display == "none") {
return
}

replaceDatesInTree(mutationRecord.target)
}

private fun replaceDatesInTree(root: Node) {
val treeWalker = document.createTreeWalker(root, whatToShow = NodeFilter.SHOW_TEXT) { node ->
if (node.isChildOfMetaTag) {
NodeFilter.FILTER_REJECT
} else {
NodeFilter.FILTER_ACCEPT
}
}

var currentNode: Node? = treeWalker.currentNode
while (currentNode != null) {
replaceDates(currentNode)
currentNode = treeWalker.nextNode()
}
}

private fun replaceDates(node: Node) {
if (node.nodeType != Node.TEXT_NODE || node.isChildOfMetaTag) {
return
}

val text = node.nodeValue ?: return

val newText = text.replaceExtendedDates().replaceSimpleDates()

if (text != newText) {
node.nodeValue = newText
}
}

private fun String.replaceExtendedDates(): String {
val regex = Regex(
"([0-9]+)\\. (januar|jan\\.?|februar|feb\\.?|marts|mar\\.?|april|apr\\.?|maj|juni|jun\\.?|juli|jul\\.?|august|aug\\.?|september|sept\\.?|sep\\.?|oktober|okt\\.?|november|nov\\.?|december|dec\\.?)(?: ([0-9]+))?",
RegexOption.IGNORE_CASE
)

return regex.replace(this) { matchResult ->
val dayOfMonth = matchResult.groupValues.getOrNull(1)?.toIntOrNull() ?: return@replace matchResult.value

val month = matchResult.groupValues.getOrNull(2).let { month ->
when (month?.removeSuffix(".")) {
"jan", "januar" -> Month.JANUARY
"feb", "februar" -> Month.FEBRUARY
"mar", "marts" -> Month.MARCH
"apr", "april" -> Month.APRIL
"maj" -> Month.MAY
"jun", "juni" -> Month.JUNE
"jul", "juli" -> Month.JULY
"aug", "august" -> Month.AUGUST
"sep", "sept", "september" -> Month.SEPTEMBER
"okt", "oktober" -> Month.OCTOBER
"nov", "november" -> Month.NOVEMBER
"dec", "december" -> Month.DECEMBER
else -> {
return@replace matchResult.value
}
}
}

val year = matchResult.groupValues.getOrNull(3)?.toIntOrNull().let { year ->
when {
year == null -> null
year < 100 -> year + 2000
else -> year
}
}

try {
val today = Clock.System.todayAt(TimeZone.currentSystemDefault())

val stickDate = LocalDate(
year = year ?: today.year,
month = month,
dayOfMonth = dayOfMonth
).toStickDate()

if (year == null && stickDate.year == today.toStickDate().year) {
// We just assume a date without a year is for the current year and if that's the same for the stick
// date there's no reason to include the stick date year
stickDate.toExtendedDayString()
} else {
stickDate.toExtendedFullString()
}
} catch (exception: IllegalArgumentException) {
matchResult.value
}
}
}

private fun String.replaceSimpleDates(): String {
val regex = Regex(
"(?:(d\\.|den) )?([0-9]+)[.\\-/]([0-9]+)(?:[.\\-/ ]([0-9]+))?",
RegexOption.IGNORE_CASE
)

return regex.replace(this) { matchResult ->
val prefix = matchResult.groupValues.getOrNull(1)

val datesParts = matchResult.groupValues.subList(2, matchResult.groupValues.size).map { datePart ->
datePart.toIntOrNull() ?: return@replace matchResult.value
}

val monthNumber = datesParts[1]
val (dayOfMonth, year) = when {
datesParts.size < 2 -> Pair(datesParts[1], Clock.System.todayAt(TimeZone.currentSystemDefault()).year)
datesParts.size > 3 -> return@replace matchResult.value
datesParts[0] > 31 -> Pair(datesParts[2], datesParts[0])
else -> Pair(datesParts[0], datesParts[2])
}.let { (dayOfMonth, year) ->
if (year < 100) {
Pair(dayOfMonth, year + 2000)
} else {
Pair(dayOfMonth, year)
}
}

try {
val localDate = LocalDate(
year = year,
monthNumber = monthNumber,
dayOfMonth = dayOfMonth
)

"${
if (prefix != null) {
"$prefix "
} else {
""
}
}${localDate.toStickDate().toSimplifiedString()}"
} catch (exception: IllegalArgumentException) {
matchResult.value
}
}
}

private val Node.isChildOfMetaTag: Boolean
get() = when (parentNode?.nodeName?.toLowerCase()) {
"script" -> true
"style" -> true
else -> false
}
}
5 changes: 5 additions & 0 deletions chrome-extension/src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import kotlinx.browser.document

fun main() {
DomDateReplacer(document = document)
}
8 changes: 8 additions & 0 deletions chrome-extension/src/main/resources/_locales/da/messages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"application_name": {
"message": "Pindsekalenderen"
},
"application_description": {
"message": "Erstatter gregorianske datoer (som 29. marts 2021 og 21/3-21) på alle hjemmesider til deres repræsentation i pindsekalenderen."
}
}
10 changes: 10 additions & 0 deletions chrome-extension/src/main/resources/_locales/en/messages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"application_name": {
"message": "Stick Calendar",
"description": "The title of the application, displayed in the web store."
},
"application_description": {
"message": "Replaces gregorian calendar dates (29/3-2021) in all websites to dates of the stick calendar. Mostly just makes sense in Danish.",
"description": "The description of the application, displayed in the web store."
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions chrome-extension/src/main/resources/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "__MSG_application_name__",
"description": "__MSG_application_description__",
"version": "0.1.0",
"manifest_version": 3,
"default_locale": "en",
"icons": {
"16": "icons/dpu16.png",
"32": "icons/dpu32.png",
"48": "icons/dpu48.png",
"72": "icons/dpu72.png",
"128": "icons/dpu128.png",
"256": "icons/dpu256.png"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"exclude_matches": [
"http://*.spilpind.dk/*",
"https://*.spilpind.dk/*"
],
"run_at": "document_idle",
"js": [
"stick-calendar.js"
]
}
]
}
70 changes: 70 additions & 0 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
plugins {
kotlin("multiplatform")
}

kotlin {
jvm {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
testRuns["test"].executionTask.configure {
useJUnit()
}
}

js(LEGACY) {
browser {
commonWebpackConfig {
cssSupport.enabled = true
}

testTask {
useKarma {
useChromeHeadless()
}
}
}
binaries.executable()
}

val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}

sourceSets {

@Suppress("UNUSED_VARIABLE")
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
}
}

@Suppress("UNUSED_VARIABLE")
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}

@Suppress("UNUSED_VARIABLE")
val jvmTest by getting {
dependencies {
implementation(kotlin("test-junit"))
}
}

@Suppress("UNUSED_VARIABLE")
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
}
}
Loading

0 comments on commit dccaecc

Please sign in to comment.