<template> <v-app id="app"> <header> <h1><a href="/">Ordbøkene <span class="beta">(BETA)</span></a></h1> <p class="about-link"><a href="#">OM ORDBØKENE</a></p> <p class="sub-title"><a href="/">Bokmålsordboka | Nynorskordboka – rett norsk</a></p> </header> <main :class="(article.error || article.lemmas.length || search_results.length || waiting) ? '' : 'welcome '"> <div class="search_container"> <Autocomplete @submit="select_result" :endpoint="api_pref"> </Autocomplete> <div class="lang_select_container"> <select class="lang_select" name="lang" v-model="lang"> <option value="bob,nob">Begge ordbøker </option> <option value="bob">Bokmål</option> <option value="nob">Nynorsk</option> </select> </div> </div> <div id="spinner"> <v-progress-circular indeterminate color="rgb(188, 71, 123)" size="120" v-show="waiting"></v-progress-circular> </div> <SearchResults :hits="search_results" :lang="lang" @article-click="article_link_click" v-show="! waiting" /> <div id="single_article_container"> <Article :key="article_key" :article="article" @article-click="article_link_click" /> </div> <div class="welcome" v-show="! (article.error || article.lemmas.length || search_results.length || waiting)"> <div class="monthly"> <h2>Månedens ord</h2> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> <div id="word-of-month-bm"> <Article :article="monthly_bm" @article-click="article_link_click" /> </div> <div id="word-of-month-nn"> <Article :article="monthly_nn" @article-click="article_link_click" /> </div> </div> </div> </main> <footer> <div> <img id="srlogo" src="./assets/Sprakradet_logo_neg.png" alt=""> </div> <div> <img id="uiblogo" src="./assets/uib-logo.svg" alt=""> </div> <div>Bokmålsordboka og Nynorskordboka viser skrivemåte og bøying i tråd med norsk rettskriving. Språkrådet og Universitetet i Bergen står bak ordbøkene.</div> </footer> </v-app> </template> <script> import axios from "axios" import entities from './utils/entities.js' import Article from './components/Article.vue' import Preview from './components/Preview.vue' import SearchResults from './components/SearchResults.vue' import Autocomplete from './components/Autocomplete.vue' var api_endpoint = 'https://beta.ordbok.uib.no/api/dict' axios.interceptors.request.use(function (config) { config.headers["x-api-key"] = "ZkYiyRVXxH86ijsvhx3cH4SY5Iik2ijI3BKVJGMm" return config; }, function (error) { return Promise.reject(error); }); window.onpopstate = function (event) { if (event.state) { // eslint-disable-next-line app.__vue__._data.article = event.state.article // eslint-disable-next-line app.__vue__._data.search_results = event.state.search_results // eslint-disable-next-line app.__vue__._data.lang = event.state.lang } } function navigate_to_article(self, source) { axios.get(api_endpoint + '/' + self.$route.params.lang + '/article/' + self.$route.params.id) .then(function(response){ self.article = response.data self.search_results = [] }) .catch(function(error){ if (error.response && error.response.status == 404) { self.article = { lemmas: [], error: "Vi har ingen artikkel med id " + article_id } } else { self.article = { lemmas: [], error: "Noe gikk galt..." } console.log(error) } }) .then(function(response){ self.waiting_for_articles = false history.replaceState({article: self.article, search_results: [], lang: self.lang}, '') if (source) { self.$plausible.trackEvent('internal link incoming', {props: {origin: source}}) } }) } function navigate_to_search(self, query) { axios.get(self.api_pref + 'search?q=' + query) .then(function(response){ self.search_results = response.data if (! self.search_results.length) { self.article = { lemmas: [], error: "Vi fant ingen resultater for ''" + query + "'. (Søkeforlag kommer i en senere oppatering av Ordbøkene)" } } self.waiting_for_articles = false history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang}, '') }) } function navigate_to_word(self, word) { axios.get(self.api_pref + 'suggest?q=' + word) .then(function(response){ self.search_results = response.data.filter(result => result.match.length == word.length) self.waiting_for_articles = false history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang}, '') }) } export default { name: 'app', data: function() { return { article_key: 0, search_results: [], lang: 'bob,nob', waiting_for_articles: true, waiting_for_metadata: true, article: {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}}, monthly_bm: {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}}, monthly_nn: {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}} } }, computed: { waiting: function() { return (this.waiting_for_articles || this.waiting_for_metadata) && this.$route.name != 'root' }, api_pref: function() { return api_endpoint + '/' + this.lang + '/article/' } }, components: { Article, Autocomplete, SearchResults, Preview }, methods: { select_result: function(event) { if(event.articles){ this.$router.push('/' + this.lang + '/w/' + event.word) this.search_results = event.articles this.article = {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}} history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang}, '') this.$plausible.trackEvent('dropdown selection', {props: {query: event.label, match: event.match}}) }else{ this.waiting_for_articles = true this.article = {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}} this.$router.push(`/${this.lang}/search/${event.q}`) navigate_to_search(this, event.q) this.$plausible.trackEvent('dropdown selection', {props: {query: event.label, match: '<fritekstsøk>'}}) } }, article_link_click: function(item) { if (this.article.article_id == item.article_id){ this.article_key++ history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang}, '') }else{ this.article = {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}} this.waiting_for_articles = true navigate_to_article(this, item.source) } } }, mounted: function(){ let self = this this.lang = 'bob,nob' Promise.all([ axios.get(api_endpoint + '/bob').then(function(response){ let concepts = response.data.concepts entities.bob = concepts }), axios.get(api_endpoint + '/nob').then(function(response){ let concepts = response.data.concepts entities.nob = concepts }) ]).then(function(_) { self.waiting_for_metadata = false if(self.$route.name == 'word') { self.lang = self.$route.params.lang navigate_to_word(self, self.$route.params.word) } else if(self.$route.name == 'lookup'){ navigate_to_article(self, self.$route.params.id) } else if (self.$route.name == 'search') { self.lang = self.$route.params.lang navigate_to_search(self, self.$route.params.query) } else { self.lang = self.$route.params.lang || 'bob,nob' self.waiting_for_articles = false history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang}, '') } // words of the month axios.get(api_endpoint + '/bob/article/5607').then(function(response){ self.monthly_bm = Object.assign(response.data, {dictionary: 'bob'}) }) axios.get(api_endpoint + '/nob/article/78569').then(function(response){ self.monthly_nn = Object.assign(response.data, {dictionary: 'nob'}) }) }) }, watch: { $route() { this.$plausible.trackEvent('language', {props: {code: this.$route.params.lang}}) } } } </script> <style> @import url('https://fonts.googleapis.com/css2?family=Inria+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Inria+Serif:ital,wght@0,400;0,700;1,400;1,700&family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap'); #app { font-family: 'Noto Sans', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; display: flex; flex-direction: column; height: 100%; } html, body { height: 100% } body { margin: 0px; } h1 { font-family: Inria Serif; font-size: 36px; color: #560027; } header > h1 { color: #BC477B; font-size: 40px; margin: 0px; } p.about-link { text-align: right; margin: 0px; float: right; } header a { color: inherit; text-decoration: none; } span.beta { color: #BBBBBB; } p.about-link > a{ text-decoration: none; border-bottom: solid #BC477B 4px; font-size: 12px; color: #FDF4F5; } main { padding-bottom: 20px; flex: 1 0 auto; background-color: #FDF4F5; } main.welcome { background-image: url('./assets/books.jpg'); background-repeat: no-repeat; background-attachment: fixed; } header, #search_results, #spinner, #single_article_container, footer, div.welcome, div.search_container { padding-left: calc((100vw - 1000px) / 2); padding-right: calc((100vw - 1000px) / 2); } #spinner { padding-top: 40px; } header { padding-top: 20px; padding-bottom: 20px; background-color: #560027; } div.monthly { padding: 20px; border-radius: 10px; display: grid; grid-template-columns: 50% 50%; grid-template-rows: auto auto auto; gap: 10px; width: 100%; background-color: rgba(256,256,256,0.5) } div.monthly > h2{ padding: 0px; grid-row: 1; grid-column: 1 / 3; } div.monthly > p { grid-row: 2; grid-column: 1 / 3; } div#monthly_bm { grid-row: 3; grid-column: 1; } div#monthly_nn { grid-row: 3; grid-column: 2; } .top { color: #560027; font-weight: bold; font-size: smaller; border-bottom: solid; border-color: #BC477B; } .mission-statement { font-size: 16px; margin: 0px; color: #FDF4F5; } .sub-title { font-size: 20px; margin: 0px; color: #FDF4F5; } .show { display: block; } .hide { display: none; } .autocomplete { width: 25em; border-bottom: solid #BC477B; border-radius: 0px; } .autocomplete-input { border-radius: 0px; background-color: #FFFFFF; } .autocomplete-result-list { max-height: 500px; } .lang-select-intro { font-size: smaller; font-weight: bold; } footer { font-size: smaller; display: table; flex-direction: row; background-color: #570B27; color: #ffffff; } .search_container { display: flex; flex-direction: row; padding-top: 50px; } li.suggestion { font-weight: bold; padding-left: 20px; padding-top: 5px; padding-bottom: 5px; border: 0px; background-image: none; } footer > div { display: table-cell; vertical-align: middle; padding: 10px; } #srlogo { height: 20px; } #uiblogo { height: 60px; } select.lang_select { appearance: none; background-color: #EEEEEE; border: none; padding: 0 1em 0 0; width: 100%; height: 100%; font-family: inherit; font-size: inherit; font-weight: bold; cursor: inherit; line-height: inherit; background-image: url('./assets/down_arrow.png'); background-position: 98% center; background-repeat: no-repeat; background-size: 10%; border-bottom: solid #BC477B; } ::selection { background: rgb(188, 71, 123); color: white; } </style>