Skip to content
Snippets Groups Projects
Commit 2531550f authored by Ole Voldsæter's avatar Ole Voldsæter
Browse files

Merge branch 'multi_article' into 'master'

visning av flere artikler samtidig

See merge request spraksamlingane/beta.ordbok.uib.no!1
parents 7a59dfe1 073c962a
No related branches found
No related tags found
No related merge requests found
Showing with 159 additions and 90 deletions
......@@ -5,6 +5,8 @@ stages:
build:
image: node:latest
stage: build
only:
- master
script:
- npm install --progress=false
- npm run build
......@@ -16,6 +18,8 @@ build:
deploy:
image: uibit/awscli:latest
stage: deploy
only:
- master
script:
- rm -rf ~/.aws
- mv .aws ~/
......
......@@ -5,23 +5,26 @@
<span class="top">EID AV UNIVERSITETET I BERGEN OG SPRÅKRÅDET</span>
<p class="mission-statement">Bokmålsordboka og Nynorskordboka er dei einaste fritt tilgjengelege ordbøkene som gjer svar på korleis ord skal skrivast og bøyast
i norsk i tråd med gjeldande rettskriving.</p>
<autocomplete :debounceTime="100" :auto-select="true" :search="search" @submit="select_result" placeholder="søk..." ref="search">
<template #result="{result, props}">
<Preview v-bind="props" :searchHit="result">
</Preview>
</template>
</autocomplete>
<span class="lang-select-intro">SØK PÅ</span>
<input type="radio" id="radio_both" value="bob,nob" v-model="lang">
<label for="radio_both">Begge</label>
<input type="radio" id="radio_bob" value="bob" v-model="lang">
<label for="radio_bob">Bokmål</label>
<input type="radio" id="radio_nob" value="nob" v-model="lang">
<label for="radio_nob">Nynorsk</label>
<div class="search_container">
<autocomplete :debounceTime="100" :auto-select="true" :search="search" @submit="select_result" placeholder="søk..." ref="search">
<template #result="{result, props}">
<li class="suggestion" v-bind="props">{{result.label}}</li>
</template>
</autocomplete>
<div class="lang_select_container">
<select class="lang_select" name="lang" v-model="lang">
<option value="bob,nob">Begge målformer </option>
<option value="bob">Bokmål</option>
<option value="nob">Nynorsk</option>
</select>
</div>
</div>
</header>
<img id="spinner" :class="waiting ? 'show' : 'hide'" src="./assets/spinner.gif" alt="Venter på innhold" />
<SearchResults :hits="search_results" :lang="lang" @search-hit-click="search_hit_click" v-show="! waiting" />
<Article :key="article_key" :article="article" @article-click="article_link_click" />
<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)">
<p>Bør ordet <em>crew</em> inn i dei norske standardordbøkene? Korleis definerar vi <em>c-kjendis</em>, og korleis
bøyar vi eigentleg <em>abaya</em> på nynorsk - er det fleire <em>abayaer</em> eller <em>abayaar</em>? Blir <em>en vegetar</em>
......@@ -75,14 +78,11 @@ window.onpopstate = function (event) {
}
}
function navigate_to_article(self, article_id, origin_article, origin_lemma) {
if (origin_article) {
self.$plausible.trackEvent('internal link incoming', {props: {origin: `/${self.$route.params.lang}/${origin_article}/${origin_lemma || '_'}`}})
}
axios.get(self.api_pref + '' + article_id)
function navigate_to_article(self) {
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) {
......@@ -100,7 +100,7 @@ function navigate_to_article(self, article_id, origin_article, origin_lemma) {
})
.then(function(response){
self.waiting_for_articles = false
history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang}, '')
history.replaceState({article: self.article, search_results: [], lang: self.lang}, '')
})
}
......@@ -113,6 +113,15 @@ function navigate_to_search(self, query) {
})
}
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() {
......@@ -139,9 +148,15 @@ export default {
return new Promise(resolve => {
return axios.get(self.api_pref + 'suggest?q=' + q).then(
function(response) {
let hits = q.length ? [{}] : []
hits = hits.concat(response.data)
resolve(hits.map(h => Object.assign(h, {q: q})))
let hits = q.length ? [{q: q, label: q + ' (alt)'}] : []
response.data.forEach((item, i) => {
if (hits[0].label != item.match) {
hits.splice(0, 0, {q: q, label: item.match, articles: []})
}
hits[0].articles.push(item)
});
hits.reverse()
resolve(hits)
})
})
}
......@@ -157,10 +172,10 @@ export default {
select_result: function(event) {
this.$refs.search.value = ''
document.activeElement.blur()
if(event.body){
this.$router.push('/' + event.dictionary + '/' + event.article_id + '/' + event.match)
this.search_results = []
this.article = event
if(event.articles){
this.$router.push('/' + this.lang + '/w/' + event.label)
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.q, dictionary: event.dictionary, match: event.match}})
}else{
......@@ -175,11 +190,9 @@ export default {
this.article_key++
history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang}, '')
}else{
let current_art_id = this.article.article_id
let current_lemma = this.article.lemmas[0].lemma
this.article = {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}}
this.waiting_for_articles = true
navigate_to_article(this, item.article_id, current_art_id, current_lemma)
navigate_to_article(this)
}
},
search_hit_click: function(article){
......@@ -202,22 +215,18 @@ export default {
self.waiting_for_nob_metadata = false
})
this.lang = this.$route.params.lang || this.$route.query.lang || 'bob,nob'
if(this.$route.query.q) {
navigate_to_search(this, this.$route.query.q)
this.lang = 'bob,nob'
if(this.$route.name == 'word') {
navigate_to_word(this, this.$route.params.word)
}
else if(this.$route.params.id){
else if(this.$route.name == 'lookup'){
navigate_to_article(this, this.$route.params.id)
}
else {
this.waiting_for_articles = false
history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang}, '')
}
},
watch: {
$route() {
this.lang = this.$route.params.lang || this.$route.query.lang || 'bob,nob'
}
}
}
</script>
......@@ -249,7 +258,7 @@ main {
background-attachment: fixed;
}
header, #search_results, #spinner, article, footer, div.welcome {
header, #search_results, #spinner, #single_article_container, footer, div.welcome {
padding-left: calc((100vw - 1000px) / 2);
padding-right: calc((100vw - 1000px) / 2);
background-image: url('./assets/beta.png');
......@@ -333,6 +342,15 @@ footer {
padding-bottom: 20px;
}
.search_container {
display: flex;
flex-direction: row;
}
li.suggestion {
font-weight: bold;
}
footer p {
padding-left: 20px;
padding-right: 20px;
......@@ -350,4 +368,24 @@ footer p {
padding: 10px;
padding-left: 20px;
}
select.lang_select {
appearance: none;
background-color: transparent;
border: none;
padding: 0 1em 0 0;
margin: 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%;
}
</style>
src/assets/down_arrow.png

319 B

<template>
<article v-show="article.lemmas.length || article.error">
<Header :lemmas="article.lemmas" />
<Header :lemmas="article.lemmas" :dictionary="dictionary" />
<section v-if="! article.error && article.body.pronunciation && article.body.pronunciation.length">
<h3>Uttale</h3>
<ul>
<DefElement v-for="(element, index) in article.body.pronunciation" :key="index" :body='element' @article-click="article_link_click" />
<DefElement v-for="(element, index) in article.body.pronunciation" :dictionary="dictionary" :key="index" :body='element' @article-click="article_link_click" />
</ul>
</section>
<section v-if="! article.error && article.body.etymology && article.body.etymology.length">
<h3>Etymologi</h3>
<ul>
<DefElement v-for="(element, index) in article.body.etymology" :key="index" :body='element' @article-click="article_link_click" />
<DefElement v-for="(element, index) in article.body.etymology" :dictionary="dictionary" :key="index" :body='element' @article-click="article_link_click" />
</ul>
</section>
<section v-if="! article.error">
<h3>Definisjoner</h3>
<ol>
<Definition v-for="definition in article.body.definitions" :level="1" :key="definition.id" :body='definition' @article-click="article_link_click" />
<Definition v-for="definition in article.body.definitions" :dictionary="dictionary" :level="1" :key="definition.id" :body='definition' @article-click="article_link_click" />
</ol>
</section>
<section v-if="article.error">
......@@ -36,6 +36,11 @@ export default {
props: {
article: Object
},
computed: {
dictionary: function() {
return this.article.dictionary || this.$route.params.lang
}
},
components: {
DefElement,
Definition,
......@@ -72,6 +77,10 @@ li.level1.definition {
list-style: upper-alpha;
}
li.level2.definition {
list-style: decimal;
}
li.level3.definition {
list-style: lower-alpha;
}
......
<template>
<li class="compound_list">
<ul>
<DefElement :body="body.intro" v-if="body.intro" />
<DefElement :body="body.intro" v-if="body.intro" :dictionary="dictionary" />
<li :key="index" v-for="(item, index) in body.elements">
<router-link :to="item.article_id + (item.definition_id ? '#def'+item.definition_id : '')" @click.native="article_link_click(item)">
<router-link :to="'/' + dictionary + '/' + item.article_id + (item.definition_id ? '#def'+item.definition_id : '')" @click.native="article_link_click(item)">
<span class="homograph" :key="index">{{item.lemmas[0].hgno ? String.fromCharCode(0x215f + item.lemmas[0].hgno) : ''}}</span>
{{item.lemmas[0].lemma}};
</router-link>
......@@ -18,7 +18,8 @@ import DefElement from './DefElement.vue'
export default {
name: 'CompoundList',
props: {
body: Object
body: Object,
dictionary: String
},
components: {
DefElement
......
......@@ -21,11 +21,12 @@ export default {
tag: {
type: String,
default: 'li'
}
},
dictionary: String
},
computed: {
unparsed: function(){
let lang = this.$route.params.lang
let lang = this.dictionary
let path_lemma = this.$route.params.lemma
return this.body.items.map(
function(item){
......@@ -34,7 +35,7 @@ export default {
type: item.type_,
html: '',
link_text: item.word_form || item.lemmas[0].lemma,
ref: (path_lemma ? '../' : '') + item.article_id + '/' + (item.word_form || item.lemmas[0].lemma) + (item.definition_id ? '#def' + item.definition_id : ''),
ref: '/' + lang + '/' + item.article_id + '/' + (item.word_form || item.lemmas[0].lemma) + (item.definition_id ? '#def' + item.definition_id : ''),
article_id: item.article_id,
definition_id: item.definition_id,
definition_order: item.definition_order
......
<template>
<li :class="['definition', 'level'+level]" :ref="'def' + body.id" :id="'def' + body.id">
<ul class="explanation">
<li :is="element_wrapper.template" :body="element_wrapper.element" v-for="(element_wrapper, index) in template_name_added" :key="index" @article-click="article_link_click">{{element_wrapper.element}}</li>
<li :is="element_wrapper.template" :body="element_wrapper.element" :dictionary="element_wrapper.dictionary" v-for="(element_wrapper, index) in template_name_added" :key="index" @article-click="article_link_click">{{element_wrapper.element}}</li>
</ul>
<div :is="level < 3 ? 'ol' : 'ul'" class="sub_definitions" v-if="subdefs.length">
<Definition :level="level+1" :body="subdef" v-for="(subdef, index) in subdefs" :key="index" @article-click="article_link_click" />
<Definition :level="level+1" :body="subdef" v-for="(subdef, index) in subdefs" :dictionary="dictionary" :key="index" @article-click="article_link_click" />
</div>
<ul class="sub_articles" v-if="subArticles.length">
<SubArticle :body="subart" v-for="(subart, index) in subArticles" :key="index" @article-click="article_link_click" />
<SubArticle :body="subart" v-for="(subart, index) in subArticles" :dictionary="dictionary" :key="index" @article-click="article_link_click" />
</ul>
</li>
</template>
......@@ -22,7 +22,8 @@ var Definition = {
name: 'Definition',
props: {
body: Object,
level: Number
level: Number,
dictionary: String
},
components: {
DefElement,
......@@ -33,6 +34,7 @@ var Definition = {
},
computed: {
template_name_added: function(){
let dictionary = this.dictionary
return this.body.elements.filter(el => ! ['definition', 'sub_article'].includes(el.type_)).map(
function(element){
return {
......@@ -41,7 +43,8 @@ var Definition = {
'example': 'Example',
'compound_list': 'CompoundList'
}[element.type_] || 'li',
'element': element
'element': element,
'dictionary': dictionary
}
})
},
......
<template>
<li class="example">
<DefElement tag="q" :body="body.quote" @article-click="article_link_click" /><span v-if="body.explanation && body.explanation.content.length"> - </span>
<DefElement tag="span" :body="body.explanation" v-if="body.explanation && body.explanation.content.length" @article-click="article_link_click" />
<DefElement tag="q" :body="body.quote" @article-click="article_link_click" :dictionary="dictionary" /><span v-if="body.explanation && body.explanation.content.length"> - </span>
<DefElement tag="span" :body="body.explanation" v-if="body.explanation && body.explanation.content.length" @article-click="article_link_click" :dictionary="dictionary" />
</li>
</template>
......@@ -11,7 +11,8 @@ import DefElement from './DefElement.vue'
export default {
name: 'Example',
props: {
body: Object
body: Object,
dictionary: String
},
components: {
DefElement
......
......@@ -11,6 +11,7 @@
<component v-for="grp in Object.keys(lemma.inflection_groups)"
:key="grp.replace('/', '_')"
:is="grp.replace('/', '_')"
:dictionary="dictionary"
:standardisations="lemma.inflection_groups[grp]"></component>
</div>
</div>
......@@ -36,7 +37,8 @@ import VERB_sPass from './inflection/VerbSPass.vue'
export default {
name: 'Header',
props: {
lemmas: Array
lemmas: Array,
dictionary: String
},
computed: {
group_list: function() {
......
......@@ -4,25 +4,24 @@
<div class="flex-container">
<ul class="hits" v-if="results_bob.length">
<li><h4>Bokmål</h4></li>
<Preview v-for="(result, index) in results_bob" :key="index" :searchHit="result" @click.native="article_link_click(result)">
</Preview>
<li class="article_container" v-for="(result, index) in results_bob" :key="index">
<Article :article="result" @article-click="article_link_click">
</Article>
</li>
</ul>
<ul class="hits" v-if="results_nob.length">
<li><h4>Nynorsk</h4></li>
<Preview v-for="(result, index) in results_nob" :key="index" :searchHit="result" @click.native="article_link_click(result)">
</Preview>
</ul>
<ul class="hits" v-if="results_norsk.length">
<li><h4>Norsk Ordbok</h4></li>
<Preview v-for="(result, index) in results_norsk" :key="index" :searchHit="result" @click.native="article_link_click(result)">
</Preview>
<li class="article_container" v-for="(result, index) in results_nob" :key="index">
<Article :article="result" @article-click="article_link_click">
</Article>
</li>
</ul>
</div>
</section>
</template>
<script>
import Preview from './Preview.vue'
import Article from './Article.vue'
export default {
name: 'SearchResults',
......@@ -36,19 +35,15 @@ export default {
},
results_nob: function(){
return this.hits.filter(hit => hit.dictionary == 'nob')
},
results_norsk: function(){
return this.hits.filter(hit => hit.dictionary == 'norsk_ordbok')
}
},
methods: {
article_link_click: function(result) {
this.$router.push('/' + result.dictionary + '/' + result.article_id)
this.$emit('search-hit-click', result)
article_link_click: function(item) {
this.$emit('article-click', item)
}
},
components: {
Preview
Article
}
}
......@@ -71,6 +66,8 @@ export default {
.hits {
margin-top:0px;
margin-left: 20px;
margin-right: 20px;
}
.flex-container {
......@@ -84,4 +81,8 @@ export default {
.flex-container h4 {
margin: 0px;
}
li.article_container {
border-bottom: solid 2px #560027;
}
</style>
<template>
<li class="sub_article">
<span class="sub_article_header">
<router-link :to="link_prefix + body.article_id" @click.native="article_link_click(body)">
<router-link :to="'/' + dictionary + '/' + body.article_id" @click.native="article_link_click(body)">
{{body.lemmas[0]}}
</router-link>
</span>
<ul>
<DefElement :body="body.intro" v-if="body.intro" @article-click="article_link_click" />
<Definition :level="9" :body="body.article.body.definitions[0]" @article-click="article_link_click" />
<DefElement :body="body.intro" v-if="body.intro" :dictionary="dictionary" @article-click="article_link_click" />
<Definition :level="9" :body="body.article.body.definitions[0]" :dictionary="dictionary" @article-click="article_link_click" />
</ul>
</li>
</template>
......@@ -18,7 +18,8 @@ import DefElement from './DefElement.vue'
export default {
name: 'SubArticle',
props: {
body: Object
body: Object,
dictionary: String
},
components: {
DefElement
......
......@@ -32,14 +32,15 @@
export default {
name: "ADJ",
props: {
standardisations: Array
standardisations: Array,
dictionary: String
},
computed: {
i18n: function() {
return {
bob: ['Entall', 'Flertall', 'Hun', 'Intet', 'estemt'],
nob: ['Eintal', 'Fleirtal', 'Ho', 'Inkje', 'unden']
}[this.$route.params.lang]
}[this.dictionary]
}
}
}
......
......@@ -34,14 +34,15 @@
export default {
name: "ADJ_masc_fem",
props: {
standardisations: Array
standardisations: Array,
dictionary: String
},
computed: {
i18n: function() {
return {
bob: ['Entall', 'Flertall', 'Hun', 'Intet', 'estemt'],
nob: ['Eintal', 'Fleirtal', 'Ho', 'Inkje', 'unden']
}[this.$route.params.lang]
}[this.dictionary]
}
}
}
......
......@@ -20,14 +20,15 @@
export default {
name: "NOUN",
props: {
standardisations: Array
standardisations: Array,
dictionary: String
},
computed: {
i18n: function() {
return {
bob: ['Entall', 'Flertall', 'Ubestemt', 'Bestemt', {Masc: ['mask.', 'en'], Fem: ['fem.', 'ei'], Neuter: ['nøytr.', 'et']}],
nob: ['Eintal', 'Fleirtal', 'Ubunden', 'Bunden', {Masc: ['mask.', 'ein'], Fem: ['fem.', 'ei'], Neuter: ['nøytr.', 'eit']}]
}[this.$route.params.lang]
}[this.dictionary]
}
}
}
......
......@@ -36,14 +36,15 @@
export default {
name: "VERB",
props: {
standardisations: Array
standardisations: Array,
dictionary: String
},
computed: {
i18n: function() {
return {
bob: ['hun', 'Intet', 'Bestemt', 'Flertall'],
nob: ['ho', 'Inkje', 'Bunden', 'Fleirtal']
}[this.$route.params.lang]
}[this.dictionary]
}
}
}
......
......@@ -19,6 +19,10 @@ const router = new VueRouter({
name: 'root',
path: '/',
component: App }, // No props, no nothing
{
name: 'word',
path: '/:lang/w/:word'
},
{
name: 'lookup',
path: '/:lang/:id(\\d+)/:lemma?',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment