diff --git a/package.json b/package.json index 81957731c481ca99050a2345b4220e2a06cf6b75..d1dfb12883f16bca44176ea75c7e11f2c0a740a1 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "axios": "^0.21.1", "core-js": "~3.6.5", "debounce": "^1.2.1", - "inflection-table": "https://git.app.uib.no/api/v4/projects/16442/jobs/artifacts/0.2.47/raw/module.tar.gz?job=publish", + "inflection-table": "https://git.app.uib.no/api/v4/projects/16442/jobs/artifacts/1.0.0/raw/module.tar.gz?job=publish", "vue": "^2.6.12", "vue-i18n": "^8.26.7", "vue-i18n-bridge": "^9.2.0-beta.10", diff --git a/src/App.vue b/src/App.vue index 74c3b6ef3ac4e05b7adb5b5f80a0b6e8836ee4cf..1434225b21d8faa2392d4cec0b8755aa98b88cdf 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,7 +1,7 @@ <template> <v-app id="app"> <div v-if="no_locale"> - <v-dialog overlay-opacity="1" overlay-color="tertiary" persistent max-width="600px" v-if="!this.$store.state.currentLocale" v-model="no_locale"> + <v-dialog overlay-opacity="1" overlay-color="tertiary" persistent max-width="600px" v-model="no_locale"> <v-card> <v-toolbar elevation="0" color="primary" text> <v-toolbar-title class="language-dialog-title">Ordbøkene</v-toolbar-title></v-toolbar> @@ -45,6 +45,7 @@ <footer> + <div id="photo-attribution" v-if="!$route.name">{{$t('photo')}}</div> <div :class="$vuetify.breakpoint.xs?'sm':'lg'"> <div> <img id="srlogo" src="./assets/Sprakradet_logo_neg.png" alt=""> @@ -63,20 +64,19 @@ import TopBar from './components/TopBar.vue' export default { computed: { no_locale: function() { - return !this.$store.state.locale + return !this.$store.state.currentLocale }, show_banner_text: function() { return !this.$route.name || (window.innerHeight > 800 && this.$route.name != 'about' ) } }, mounted: function(){ - if (!this.no_locale) { + if (this.no_locale) { this._i18n.locale = this.$store.state.locale } }, methods: { choose_locale: function(locale) { - this.locale_chosen = true this.$store.commit("setLocale", {value: locale, i18n: this._i18n}) } }, @@ -273,4 +273,21 @@ th[class="infl-group"] { color: var(--v-text-base) } + +#photo-attribution { + position: absolute; + top: -32px; + right: 0px; + width: 100%; + text-align: right; + padding-right: 6px; + color: white; + font-size: 12; + text-shadow: -1px 1px 0 black, + 1px 1px 0 black, + 1px -1px 0 black, + -1px -1px 0 black; +} + + </style> diff --git a/src/components/Article.vue b/src/components/Article.vue index 35a090a3970b940fd9a6ad6fe2e4a34d08026742..837e432cfd26e1460adcce4ee5b7194d0b20d865 100644 --- a/src/components/Article.vue +++ b/src/components/Article.vue @@ -1,32 +1,31 @@ <template> - <article v-bind:class="{'collapsable': collapsable, 'collapsed': collapsed, 'v-sheet v-card rounded-xl': !$parent.article}" v-if="article"> + <article v-bind:class="{'collapsable': collapsable, 'hide-label': hide_label, 'v-sheet v-card rounded-xl': !$parent.article}" v-if="article"> <div :class="$vuetify.breakpoint.name" v-if="!invalid"> <Header :title_id="title_id" :lemmas="article.lemmas" :dictionary="dictionary" :article_id="article.article_id" @toggle-collapse = "toggle_collapse"/> - <InflectionButton :lemmas="article.lemmas" :dictionary="dictionary" :article_id="article.article_id"/> <div class="article_content" :class="$vuetify.breakpoint.name" v-if="!collapsed"> <section v-if="article.body.pronunciation && article.body.pronunciation.length" class="pronunciation"> <h3>{{$t('article.headings.pronunciation', content_locale)}}</h3> <ul> - <DefElement v-for="(element, index) in article.body.pronunciation" :dictionary="dictionary" :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" @error="article_error"/> </ul> </section> <section v-if="article.body.etymology && article.body.etymology.length" class="etymology"> <h3>{{$t('article.headings.etymology', content_locale)}}</h3> <ul> - <DefElement v-for="(element, index) in article.body.etymology" :dictionary="dictionary" :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" @error="article_error"/> </ul> </section> <section class="definitions" v-if="has_content"> <h3>{{$t('article.headings.definitions', content_locale)}}</h3> <ol> - <Definition v-for="definition in article.body.definitions" :dictionary="dictionary" :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" @error="article_error"/> </ol> </section> <section v-if="sub_articles.length" class="expressions"> <h3>{{$t('article.headings.expressions', content_locale)}}</h3> <ul> - <SubArticle :body="subart" v-for="(subart, index) in sub_articles" :dictionary="dictionary" :key="index" @article-click="article_link_click" /> + <SubArticle :body="subart" v-for="(subart, index) in sub_articles" :dictionary="dictionary" :key="index" @article-click="article_link_click" @error="article_error"/> </ul> </section> </div> @@ -42,21 +41,30 @@ import Definition from './Definition.vue' import SubArticle from './SubArticle.vue' import Header from './Header.vue' import ArticleFooter from './ArticleFooter.vue' -import InflectionButton from './InflectionButton.vue' import entities from '../utils/entities.js' function find_sub_articles(definition) { let sub_art_list = [] - let sub_definitions = definition.elements.filter(el => el.type_ == 'definition') + try { + let sub_definitions = definition.elements.filter(el => el.type_ == 'definition') + let sub_articles = definition.elements.filter(el => el.type_ == 'sub_article' && el.lemmas) + sub_art_list = sub_art_list.concat(sub_articles) + return sub_art_list + + } + catch(error) { + console.log("find_sub_articles", this.article.article_id, this.dictionary, '"'+error.message+'"') + + return [] + } + sub_definitions.forEach((subdef, i) => { sub_art_list = sub_art_list.concat(find_sub_articles(subdef)) }) - let sub_articles = definition.elements.filter(el => el.type_ == 'sub_article' && el.lemmas) - sub_art_list = sub_art_list.concat(sub_articles) - return sub_art_list + } function find_content(definition) { @@ -95,6 +103,21 @@ export default { } }, computed: { + hide_label: function() { + //collapsable || (collapsed && $store.state.collapseArticles != 'never') + if (this.$parent.count_bm || this.$parent.count_nn) { + let two_results = (this.$parent.count_bm + this.$parent.count_nn) < 3 + if (two_results) { + return false + } + else if (this.$vuetify.breakpoint.mdAndUp && (this.collapsed || this.$store.state.collapseArticles == 'never')) { + return true + } + else { + return false + } + } + }, content_locale: function() { if (this.$i18n.locale == 'eng') { return 'eng' @@ -154,7 +177,7 @@ export default { article: this.article } } catch(error) { - console.error("link_to_self",this.article.article_id, this.dictionary, error.message) + console.log("link_to_self",this.article.article_id, this.dictionary, '"'+error.message+'"') this.invalid = true //console.error(error) return {ref: "", article: this.article} @@ -175,8 +198,7 @@ export default { Definition, SubArticle, Header, - ArticleFooter, - InflectionButton + ArticleFooter }, mounted: function() { if (this.$route.hash == "#"+ this.title_id) { @@ -189,6 +211,9 @@ export default { } }, methods: { + article_error: function(payload) { + console.log("DefElement",payload.location, this.article.article_id, this.dictionary, '"'+payload.message+'"') + }, parse_definitions: function(node) { let definitionTexts = [] try { @@ -213,8 +238,17 @@ export default { } + /* if (!definition.elements[0].items[linkIndex].id) { + console.log("NO LINKINDEX ID", this.article.article_id, this.dictionary) + console.log(definition.elements[0].items[linkIndex]) } + else { + console.log("HAS LINKINDEX ID", this.article.article_id, this.dictionary) + console.log(definition.elements[0].items[linkIndex]) + + } + */ linkIndex += 1 } else { @@ -235,7 +269,7 @@ export default { } }) } catch(error) { - console.error("parse_definitions",this.article.article_id, this.dictionary, error.message) + console.log("parse_definitions",this.article.article_id, this.dictionary, '"'+error.message+'"') this.invalid = true //console.error(error) definitionTexts = [] @@ -275,6 +309,10 @@ section.xs article, section.sm article { margin-bottom: 10px !important; } +section.md article.hide-label, section.lg article.hide-label, section.xl article.hide-label { + padding-top: 10px; +} + .welcome .article_footer { display: block; diff --git a/src/components/DefElement.vue b/src/components/DefElement.vue index ed3cbd71189b89be6378fb63bb22d0cfb26336e3..e824cf94f996d2bdcc05bee94f3f245a010db079 100644 --- a/src/components/DefElement.vue +++ b/src/components/DefElement.vue @@ -2,6 +2,7 @@ <li :is="tag" :class="body.type_"><!-- --><span :is="item.tag || 'span'" v-for="(item, index) in assemble_text" :class="item.type" + @error="article_error" :key="index" v-bind="item.props"><!-- --><span v-for="(subitem, index) in highlight_hits(item.html, {fulltext_highlight})" :key="'a'+index" :class="subitem.type">{{subitem.text}}</span><!-- @@ -54,35 +55,44 @@ export default { return this.$store.state.fulltextHighlight }, unparsed: function(){ - let lang = this.dictionary - let path = this.path - return this.body.items.map( - function(item){ - if (item.type_ == 'usage') return {type: item.type_, html: item.text, tag: 'mark'} - else if (item.type_ == 'article_ref') return { - type: item.type_, - html: '', - lemmas: item.lemmas, - link_text: item.word_form || item.lemmas[0].lemma, - ref: '/' + lang + '/' + item.article_id + '/' + encodeURIComponent(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, - source: path - } - else if (item.type_ == 'pronunciation') return {type: item.type_, html: item.string} - else if (item.type_ == 'pronunciation_guide') return {type: item.type_, body: item, html: '', tag: 'DefElement', props: {body: item, tag: 'i', dictionary: lang}} - else if (item.type_ == 'superscript') return {type: item.type_, html: item.text, tag: 'sup'} - else if (item.type_ == 'subscript') return {type: item.type_, html: item.text, 'tag': 'sub'} - else if (item.type_ == 'quote_inset') return {type: item.type_, body: item, html: '', tag: 'DefElement', props: {body: item, tag: 'i', dictionary: lang}} - else if (item.type_ == 'fraction') return helpers.fraction(item.numerator, item.denominator) - else if (item.id) return {type: item.type_, html: (entities[lang][item.id] || {})['expansion'] || item.id} - else return {type: item.type_ || 'plain', html: item} + try { + let lang = this.dictionary + let path = this.path + return this.body.items.map( + function(item){ + if (item.type_ == 'usage') return {type: item.type_, html: item.text, tag: 'mark'} + else if (item.type_ == 'article_ref') return { + type: item.type_, + html: '', + lemmas: item.lemmas, + link_text: item.word_form || item.lemmas[0].lemma, + ref: '/' + lang + '/' + item.article_id + '/' + encodeURIComponent(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, + source: path + } + else if (item.type_ == 'pronunciation') return {type: item.type_, html: item.string} + else if (item.type_ == 'pronunciation_guide') return {type: item.type_, body: item, html: '', tag: 'DefElement', props: {body: item, tag: 'i', dictionary: lang}} + else if (item.type_ == 'superscript') return {type: item.type_, html: item.text, tag: 'sup'} + else if (item.type_ == 'subscript') return {type: item.type_, html: item.text, 'tag': 'sub'} + else if (item.type_ == 'quote_inset') return {type: item.type_, body: item, html: '', tag: 'DefElement', props: {body: item, tag: 'i', dictionary: lang}} + else if (item.type_ == 'fraction') return helpers.fraction(item.numerator, item.denominator) + else if (item.id) return {type: item.type_, html: (entities[lang][item.id] || {})['expansion'] || item.id} + else return {type: item.type_ || 'plain', html: item} } ) + + } + catch(error) { + this.$emit('error', {location: "unparsed", message: error.message} ) + return {type: 'plain', html: item} + } + }, assemble_text: function(){ - var old_parts = this.body.content.split(/(\$)/) + try { + var old_parts = this.body.content.split(/(\$)/) var text_items = this.unparsed.slice(0).reverse() var new_parts = [] old_parts.forEach(function(item){ @@ -93,6 +103,13 @@ export default { } }) return new_parts + + } + catch(error) { + this.$emit('error', {location: "assemble_text", message: error.message} ) + return [] + } + } }, methods: { @@ -100,6 +117,9 @@ export default { article_link_click: function(item) { this.$emit('article-click', item) }, + article_error: function(payload) { + this.$emit('error', payload) + }, roman_hgno: helpers.roman_hgno } } diff --git a/src/components/Definition.vue b/src/components/Definition.vue index 35914cebd04deb79e354d4f3ecc081bf9c2b86c2..83346ca39334840a0b6c7f59c19630484bbf0279 100644 --- a/src/components/Definition.vue +++ b/src/components/Definition.vue @@ -41,17 +41,41 @@ var Definition = { }, computed: { explanations: function() { + try { return this.body.elements.filter(el => el.type_ == 'explanation') + } catch (error) { + this.$emit('error', {location: "explanations", message: error.message}) + return [] + } }, examples: function() { + try { return this.body.elements.filter(el => el.type_ == 'example') + } catch (error) { + this.$emit('error', {location: "examples", message: error.message}) + return [] + } + }, compund_lists: function() { + try { return this.body.elements.filter(el => el.type_ == 'compound_list') + } catch (error) { + this.$emit('error', {location: "compound_lists", message: error.message}) + return [] + } + }, subdefs: function() { + try { + return this.body.elements.filter(el => el.type_ == 'definition').filter(def => def.elements.filter(el => el.type_ != 'sub_article').length > 0) + + } catch (error) { + this.$emit('error', {location: "subdefs", message: error.message}) + return [] + } // filtrerer bort definisjoner som bare inneholder underartikler - return this.body.elements.filter(el => el.type_ == 'definition').filter(def => def.elements.filter(el => el.type_ != 'sub_article').length > 0) + }, content_locale: function() { return this.$parent.content_locale @@ -103,11 +127,6 @@ q { background-color: var(--v-tertiary-darken1); border-radius: 5px; } -.search_symbol { - background-color: var(--v-tertiary-darken1); - padding: 2px; - border-radius: 4px; -} h4 { color: var(--v-primary-base); diff --git a/src/components/DictionaryView.vue b/src/components/DictionaryView.vue index 0f8854af467ac77aa081b644bd64d5fe719766f7..53d0f53f2269cf9cc6d28e3fe6bdb8020fdb63e4 100644 --- a/src/components/DictionaryView.vue +++ b/src/components/DictionaryView.vue @@ -20,7 +20,7 @@ color="primary">info</v-icon><em>{{$route.query.q}}</em> {{$t('notifications.inflected')}}<!-- --><span v-for="(item,index) in inflection_suggestions" :key="index"><!-- - --><router-link :to="generate_path({query: {q: item[0]}})" + --><router-link :to="generate_path({q: item[0]})" @click.native="inflection_link(item[0])">{{item[0]}}</router-link><!-- -->{{index == inflection_suggestions.length-1? '.' : ', '}}</span> </div> @@ -30,7 +30,7 @@ color="primary">info</v-icon>{{$t('notifications.similar_nn')}}<!-- --><span v-for="(item,index) in similar" :key="index"><!-- - --><router-link :to="generate_path({query: {q: $route.query.q+'|'+item[0]}})" + --><router-link :to="generate_path({q: $route.query.q+'|'+item[0]})" @click.native="similar_link($route.query.q+'|'+item[0])">{{item[0]}}</router-link><!-- -->{{index == similar.length-1? '.' : ', '}} </span> @@ -41,7 +41,7 @@ color="primary">info</v-icon>{{$t('notifications.similar_bm')}}<!-- --><span v-for="(item,index) in similar" :key="index"><!-- - --><router-link :to="generate_path({query: {q: $route.query.q+'|'+item[0]}})" + --><router-link :to="generate_path({q: $route.query.q+'|'+item[0]})" @click.native="similar_link($route.query.q+'|'+item[0])">{{item[0]}}</router-link><!-- -->{{index == similar.length-1? '.' : ', '}} </span> @@ -49,7 +49,7 @@ </div> <div id="return_to_results" - v-if="article && $store.state.searchRoute"> + v-if="$vuetify.breakpoint.mdAndUp && article && $store.state.searchRoute"> <router-link id="return_link" :to="$store.state.searchRoute" @click.native="return_to_results()"> @@ -71,23 +71,23 @@ <em>{{$route.query.q}}</em>{{$t('notifications.inflected')}} <span v-for="(item,index) in inflection_suggestions" :key="index"><!-- - --><router-link :to="generate_path({query: {q: item[0]}})" + --><router-link :to="generate_path({q: item[0]})" @click.native="inflection_link(item[0])">{{item[0]}}</router-link><!-- -->{{index == inflection_suggestions.length-1? '.' : ', '}}</span> <p class="below-notification" v-if="lang=='bm' && suggest_other_dict">{{$t('notifications.suggest_dict[1]')}} - <router-link :to="generate_path({params: {lang: 'nn'}})" + <router-link :to="generate_lang_path('nn')" @click.native="language_link('nn')">{{$t('dicts.nn')}}</router-link><!-- --></p> <p class="below-notification" v-if="lang=='nn' && suggest_other_dict">{{$t('notifications.suggest_dict[0]')}}<br>{{$t('notifications.suggest_dict[1]')}} - <router-link :to="generate_path({params: {lang: 'bm'}})" + <router-link :to="generate_lang_path('bm')" @click.native="language_link('bm')">{{$t('dicts_inline.bm')}}</router-link> </p> <p class="below-notification" v-if="suggest_fulltext"> {{$t('notifications.fulltext[0]')}} - <router-link :to="generate_path({query: {scope: scope+'f'}})" + <router-link :to="generate_path({scope: scope+'f'})" @click.native="fulltext_link()"> {{$t('notifications.fulltext[1]')}} </router-link> @@ -98,7 +98,7 @@ <v-list> <template v-for="(item, index) in similar"> <v-list-item :key="index"> - <router-link :to="generate_path({query: {q: item[0]}})" + <router-link :to="generate_path({q: item[0]})" @click.native="similar_link(item[0])">{{item[0]}}</router-link> <span class="dict-parentheses" v-if="lang=='bm,nn'"> ({{["bokmÃ¥l","nynorsk","bokmÃ¥l, nynorsk"][item[1]-1]}})</span> @@ -135,7 +135,6 @@ articleLookup/> </div> <div class="welcome" - :title="$t('photo')" v-show="$route.name=='/' || !$route.name"> <div class="monthly" :class="$vuetify.breakpoint.name"> @@ -206,12 +205,12 @@ async function load_articles(self, query, offset, n, dict) { if (article_IDs) { if (offset > article_IDs.length) { - + n = 0 } else if (offset + n > article_IDs.length) { n = article_IDs.length % n - + } if (n > 0 && (self.lang == dict || self.lang == "bm,nn")) { @@ -262,7 +261,7 @@ function navigate_to_query(self, word, keep_page) { if (!keep_page) { self.page = 1 } - let advanced_search = /[_*%|]/.test(q) + let advanced_search = /[?_*%|]/.test(q) // Get inflections if (!advanced_search) { @@ -270,10 +269,10 @@ function navigate_to_query(self, word, keep_page) { api.get('suggest?', {params}) //axios.get('https://httpstat.us/502') .then((response) => { - self.inflection_suggestions = response.data.a.inflect + self.inflection_suggestions = response.data.a.inflect }).catch(error =>{ self.handle_error(error) - self.replace_history() + self.replace_history() }) } @@ -297,19 +296,19 @@ function navigate_to_query(self, word, keep_page) { // Similar if (bm_length == 0 || nn_length == 0) { if (!advanced_search) { - + if (dict == 'bm,nn' && total_length > 0) { dict = bm_length == 0? 'bm' : 'nn' } let params = {q, dict, dform: 'int', include: "s", wc: self.pos_selected} api.get('suggest?', {params}) //axios.get('https://httpstat.us/502') - .then((response) => { + .then((response) => { self.similar = response.data.a.similar - self.replace_history() + self.replace_history() }).catch(error => { self.handle_error(error) - self.replace_history() + self.replace_history() }) } else { self.similar = [] @@ -321,20 +320,20 @@ function navigate_to_query(self, word, keep_page) { self.no_results = true if (!self.scope.includes('f')) { let params = {q, dict, n: 1, dform: 'int', include: 'f', wc: self.pos_selected} - api.get('suggest?', {params}).then((response) => { + api.get('suggest?', {params}).then((response) => { self.suggest_fulltext = response.data.cnt > 0 }).catch(error => { self.handle_error(error) - self.replace_history() + self.replace_history() }) } if (dict != 'bm,nn') { let params = {q, n: 1, dict: dict=='bm'?'nn':'bm', dform: 'int', include: 'e', wc: self.pos_selected} - api.get('suggest?', {params}).then((response) => { + api.get('suggest?', {params}).then((response) => { self.suggest_other_dict = response.data.cnt > 0 && response.data.a.exact[0][0] == q }).catch(error => { self.handle_error(error) - self.replace_history() + self.replace_history() }) } self.replace_history() // fixes routing bug when going back from suggested search @@ -353,7 +352,7 @@ function navigate_to_query(self, word, keep_page) { } }).catch(error =>{ self.handle_error(error) - self.replace_history() + self.replace_history() }) } @@ -400,7 +399,7 @@ export default { else { let desc = " viser skrivemÃ¥te og bøying i trÃ¥d med norsk rettskriving. SprÃ¥krÃ¥det og Universitetet i Bergen stÃ¥r bak ordbøkene." switch (this.lang) { - case 'bm,nn': return {title: 'ordbøkene.no', + case 'bm,nn': return {title: 'ordbøkene.no', meta: [{name: "description", vmid: 'description', content: "BokmÃ¥lsordboka og Nynorskordboka"+desc}]} case 'bm': return {title: "BokmÃ¥lsordboka", meta: [{name: "description", vmid: 'description', content: "BokmÃ¥lsordboka"+desc}]} @@ -417,10 +416,10 @@ export default { }, methods: { replace_history: function() { - history.replaceState({article: this.article, - search_results: this.search_results, + history.replaceState({article: this.article, + search_results: this.search_results, article_info: this.article_info, - lang: this.lang, + lang: this.lang, error: this.error, no_results: this.no_results, pos_selected: this.pos_selected, @@ -452,14 +451,14 @@ export default { axios.get(ARTICLE_ENDPOINT + 'bm/article/120608.json').then(function(response){ self.monthly_bm = Object.assign(response.data, {dictionary: 'bm'}) }) - }, + }, load_monthly_nn: function() { let self = this axios.get(ARTICLE_ENDPOINT + 'nn/article/44621.json').then(function(response){ self.monthly_nn = Object.assign(response.data, {dictionary: 'nn'}) }) }, - handle_error: function(error, article) { + handle_error: function(error, article) { this.waiting_for_articles = false this.no_results = false this.search_results = {} @@ -524,7 +523,7 @@ export default { if (this.scope) query["scope"] = this.scope this.$router.push({path, query}) navigate_to_query(this) - + // Tracking let track_props = {query: event.q} if (event.match) track_props.match = event.match @@ -558,6 +557,14 @@ export default { load_articles(this, query, offset, this.perPage, "nn")]).then(() => { self.replace_history() self.$forceUpdate() + /* + // Debugging + if (self.page < Math.ceil(Math.max(self.article_info.articles.bm.length, self.article_info.articles.nn.length)/self.perPage)) { + self.page+=1 + self.update_page() + } + */ + } ).then(() => { this.$store.commit('setSearchRoute', this.$route.fullPath) @@ -566,14 +573,11 @@ export default { }, generate_path: function(params) { - let new_params = {} - if (params.query) { - new_params['query']= {...this.$route.query, ...params.query} - } - if (params.params) { - new_params['params']= {...this.$route.query, ...params.params} - } - return this.$router.resolve(new_params).href + return this.$router.resolve({query: {...this.$route.query, ...params}}).href + }, + + generate_lang_path: function(dict) { + return this.$route.fullPath.replace(/\/(bm|nn|bm,nn)\//, "/"+dict+"/") }, @@ -582,12 +586,14 @@ export default { if (q) { let path = `/${this.lang}/search` let pos = this.pos_param() - let query = {q: q} + let query = {q} if (pos) query.pos = pos if (this.scope) query.scope = this.scope + if (this.scope) query.scope = this.scope + if (this.perPage) query.perPage = this.perPage this.$router.push({path, query}) navigate_to_query(this, q) - } + } }, update_lang_form: function (lang) { @@ -605,6 +611,12 @@ export default { this.page = 1 this.reload_params() }, + update_per_page: function(perPage) { + this.perPage = perPage + this.$store.commit('setPerPage', this.perPage) + this.page = 1 + this.reload_params() + }, article_link_click: function(item) { let event = window.event @@ -612,15 +624,9 @@ export default { if (this.article && this.article.article_id == item.article_id){ this.article_key++ this.replace_history() - if (!this.$route.hash) { - window.scrollTo(0,0) - } }else{ navigate_to_article(this, item.source) - if (!this.$route.hash) { - window.scrollTo(0,0) - } } } }, @@ -633,13 +639,13 @@ export default { }, return_to_results: function() { this.article = null + this.replace_history() }, set_fulltext_highlight: function() { if (this.$route.query.q && this.scope.includes("f")) { let q = this.$route.query.q - q = q.replace("*", "[^\\s]*") - q = q.replace("%", "[^\\s]*") - q = q.replace("_", "[^\\s]") + q = q.replace(/\*|%/, "[^\\s]*") + q = q.replace(/_|\?/, "[^\\s]") this.$store.commit('setFulltextHighlight', q) } else { @@ -657,9 +663,14 @@ export default { if (self.$route.query.scope) { self.scope = self.$route.query.scope self.set_fulltext_highlight() - } + } if (self.$route.query.page) self.page = parseInt(self.$route.query.page) - if (self.$route.query.perPage) self.perPage = parseInt(self.$route.query.perPage) + if (self.$route.query.perPage) { + self.perPage = parseInt(self.$route.query.perPage) + } + else { + self.perPage = parseInt(self.$store.state.perPage) + } Promise.all([ axios.get(ARTICLE_ENDPOINT + 'bm/concepts.json').then(function(response){ @@ -672,15 +683,9 @@ export default { }) ]).then(function(_) { self.waiting_for_metadata = false - if (self.$route.name == 'query') { + if (self.$route.name == 'search') { navigate_to_query(self, self.$route.query.q, true) } - else if(self.$route.name == 'search') { - navigate_to_query(self, self.$route.params.query, true) - } - else if(self.$route.name == 'word') { - navigate_to_query(self, self.$route.params.query, true) - } else if(self.$route.name == 'lookup'){ navigate_to_article(self, self.$route.path) } @@ -697,7 +702,7 @@ export default { } else if (error.response) { self.error = self.$t('error.server', {code: error.response.status}) - } + } else { self.error = self.$t('error.generic') } @@ -708,7 +713,7 @@ export default { watch: { $route(to, from) { this.previous = from - if (to.fullPath == "/") { + if (to.fullPath == "/") { this.load_monthly_bm() this.load_monthly_nn() } @@ -716,7 +721,7 @@ export default { this.$store.commit('setSearchRoute', null) } - if (to.name == 'query') { + if (to.name == 'search') { this.set_fulltext_highlight() } } @@ -725,20 +730,33 @@ export default { let self = this window.onpopstate = function (event) { if (event.state) { - self.article = event.state.article - self.search_results = event.state.search_results - self.article_info = event.state.article_info - self.lang = event.state.lang - self.pos_selected = event.state.pos_selected - self.scope = event.state.scope - self.error = event.state.error - self.no_results = event.state.no_results - self.page = event.state.page, - self.perPage = event.state.perPage - self.inflection_suggestions = event.state.inflection_suggestions, - self.similar = event.state.similar, - self.suggest_fulltext = event.state.suggest_fulltext, - self.suggest_other_dict = event.state.suggest_other_dict + if (event.state.lang) { + self.article = event.state.article + self.search_results = event.state.search_results + self.article_info = event.state.article_info + self.lang = event.state.lang + self.pos_selected = event.state.pos_selected + self.scope = event.state.scope + self.error = event.state.error + self.no_results = event.state.no_results + self.page = event.state.page, + self.perPage = event.state.perPage + self.inflection_suggestions = event.state.inflection_suggestions, + self.similar = event.state.similar, + self.suggest_fulltext = event.state.suggest_fulltext, + self.suggest_other_dict = event.state.suggest_other_dict + + if (!self.$route.hash && self.$route.name != 'search') { + history.scrollRestoration = 'manual' + window.scrollTo(0,0) + } + else { + history.scrollRestoration = 'auto' + } + } + else { + console.log("Navigation errror") + } } } } @@ -760,6 +778,7 @@ div.welcome { background-image: url('../assets/designecologist-5mj5jLhYWpY-unsplash.webp'); background-repeat: no-repeat; background-position-x: center; + padding-bottom: 10px; } div.welcome article { @@ -775,12 +794,15 @@ div.welcome article { margin: auto; } -#search_results, #spinner, #notifications, #single_article_container, div.welcome, div.search_container, .error { +#search_results, #spinner, #notifications, #single_article_container, div.search_container, .error { padding-left: calc((100vw - 1200px) / 2); padding-right: calc((100vw - 1200px) / 2); } - +div.welcome { + padding-left: calc((100vw - 917px) / 2); + padding-right: calc((100vw - 917px) / 2); +} #single_article_container { box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%); @@ -788,6 +810,16 @@ div.welcome article { height: 100%; } +#single_article_container.nn { + justify-content: flex-end !important; + display: flex; +} + +#single_article_container.nn article, #single_article_container.bm article{ + width: 50%; + +} + #notifications .search_notification { padding-top: 10px; padding-bottom: 0px; @@ -831,14 +863,6 @@ div.monthly.sm, div.monthly.xs { flex-direction: column; } -div.monthly article.bm .dict-label::before { - content: "fra "; -} - -div.monthly article.nn .dict-label::before { - content: "frÃ¥ "; -} - div.monthly details, div.monthly h3 { display: none; } @@ -905,5 +929,4 @@ li.suggestion { font-size: 85%; } - </style> diff --git a/src/components/Header.vue b/src/components/Header.vue index 95d8f6ac6e4affee485e68ff29774b102383a2d0..8bcebef9aef50434b650b78f2ace2bc38b80b04a 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -1,7 +1,7 @@ <!-- eslint-disable --> <template> <div class="header"> - <span class="dict-label">{{dict_label}}</span> + <span v-if="!$parent.hide_label" class="dict-label">{{dict_label}}</span> <div v-bind:class="{ 'lookup': $route.name=='lookup'}" v-for="(lemma_group, i) in lemma_groups" :key="i"> <div v-if="$route.name!='lookup'"> <router-link @@ -25,7 +25,7 @@ </span> </span> </h2> -<h2 v-if="secondary_header_text">{{secondary_header_text}}</h2> +<h2 v-if="secondary_header_text">{{secondary_header_text}}</h2> <span class="subheader"> <span class="header_group_list" v-if="lemma_group.description">{{lemma_group.description}}</span> @@ -34,7 +34,10 @@ </span> - </router-link></div><div v-else><h2> + </router-link> + <InflectionButton v-if="i == lemma_groups.length-1" class="inflection-button" :lemmas="$parent.article.lemmas" :dictionary="dictionary" :article_id="$parent.article.article_id"/> + + </div><div v-else><h2> <span :id="title_id" v-for="(lemma, index) in lemma_group.lemmas" @@ -57,6 +60,8 @@ <span v-if="$store.state.showInflectionNo" class="inflection_classes">{{lemma_group.inflection_classes}}</span> </span> + <InflectionButton class="inflection-button" :lemmas="$parent.article.lemmas" :dictionary="dictionary" :article_id="$parent.article.article_id"/> + </div> @@ -72,9 +77,10 @@ <v-btn class="expand_icon" v-if="$parent.collapsed" text small + right @click="$emit('toggle-collapse')"> {{$t('article.show')}} - <v-icon small right>expand_more</v-icon></v-btn> + <v-icon right>expand_more</v-icon></v-btn> <v-btn class="expand_icon" v-else text small @@ -89,6 +95,7 @@ /* eslint-disable */ import helpers from '../utils/helpers.js' import SplitInf from './SplitInf.vue' +import InflectionButton from './InflectionButton.vue' export default { name: 'Header', @@ -103,8 +110,9 @@ export default { long_lemma: false } }, - components: { - SplitInf + components: { + SplitInf, + InflectionButton }, mounted: function() { if (this.$el.scrollWidth > this.$el.offsetWidth) { @@ -154,10 +162,16 @@ export default { return a_forms.join(', ') }, dict_label: function() { - return { + let label = { 'bm': 'bokmÃ¥lsordboka', 'nn': 'nynorskordboka' }[this.dictionary] || '' + if (this.$route.name) { + return label + } else { + return this.$t('from', this.content_locale) + " " + label + } + }, hgno_arabic: function() { let hgnos = [] @@ -207,7 +221,7 @@ export default { groups[index]['inflection_classes'] = this.inflection_classes(lemma_group.lemmas) }) } catch(error) { - console.error("lemma_groups",this.article_id, this.dictionary, error.message) + console.log("lemma_groups",this.article_id, this.dictionary, '"'+error.message+'"') this.$parent.invalid = true //console.error(error) } @@ -256,23 +270,6 @@ article h2.secondary_header { font-variant-caps: all-small-caps; } -#single_article_container .dict-label { - font-size: 140%; - right: 24px; - left: unset; -} - -#single_article_container .sm .dict-label { - font-size: 120%; - right: 24px; - left: unset; -} - -#single_article_container .xs .dict-label { - font-size: 100%; - right: 24px; - left: unset; -} @@ -338,5 +335,4 @@ h2 a { - </style> diff --git a/src/components/Help.vue b/src/components/Help.vue index 383dede5cebd9b1c67578c98dd891d5ab86b8a25..ad8ac8c1887e63ba3e7d9c0365760bd6a9908e8f 100644 --- a/src/components/Help.vue +++ b/src/components/Help.vue @@ -8,8 +8,8 @@ <p>Søkjer du pÃ¥ eit ord eller ein ordkombinasjon som gjev treff i meir enn to artiklar, ser du ei komprimert utgÃ¥ve av artiklane. Det er to mÃ¥tar Ã¥ sjÃ¥ heile innhaldet pÃ¥: Enten klikkar du pÃ¥ sjølve oppslagsordet i burgunder øvst i artikkelen, eller sÃ¥ kan du klikke der det stÃ¥r «vis artikkel».</p> <h3>Søk med jokerteikn</h3> <p>Dersom du er usikker pÃ¥ skrivemÃ¥ten av eit ord eller ønskjer treff i meir - enn éin artikkel, kan du bruke <span class="search_symbol">%</span> og <span class="search_symbol">*</span> for Ã¥ fÃ¥ treff pÃ¥ null, eitt eller fleire teikn. -Symbolet <span class="search_symbol">_</span> erstattar berre eitt teikn. Du kan plassere jokerteikna fleire stader i søkjeordet.</p> + enn éin artikkel, kan du bruke <code>%</code> og <code>*</code> for Ã¥ fÃ¥ treff pÃ¥ null, eitt eller fleire teikn. +Symbolet <code>_</code> erstattar berre eitt teikn. Du kan plassere jokerteikna fleire stader i søkjeordet.</p> Døme: <p>Søkjer du pÃ¥ «<a href="https://ordbokene.no/bm,nn/search?q=arbeid%2ar&scope=ei">arbeid*r</a>», er dei første treffa i nedtrekksmenyen «arbeider» i <em>BokmÃ¥lsordboka</em> og «arbeidar» i <em>Nynorskordboka</em>.</p> @@ -17,7 +17,7 @@ Døme: <p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=f%C3%B8rsk%25l%C3%A6r_r&scope=ei">førsk%lær_r</a>» gjev treff pÃ¥ «førskolelærer» i <em>BokmÃ¥lsordboka</em> og «førskolelærar/førskulelærar» i <em>Nynorskordboka</em>.</p> <h3>Kombiner søkjeord</h3> -<p>Symbolet <span class="search_symbol">|</span> gjer det mogleg Ã¥ søkje med fleire søkjeord samtidig. Det er òg mogleg Ã¥ kombinere søkjeord som inneheld jokerteikn.</p> +<p>Symbolet <code>|</code> gjer det mogleg Ã¥ søkje med fleire søkjeord samtidig. Det er òg mogleg Ã¥ kombinere søkjeord som inneheld jokerteikn.</p> Døme: <p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=kj%C3%A6rlighet%7Ckj%C3%A6rleik&scope=ei">kjærlighet|kjærleik</a>» gjev treff pÃ¥ «kjærlighet» i <em>BokmÃ¥lsordboka</em> og «kjærleik» i <em>Nynorskordboka</em>.</p> @@ -33,8 +33,8 @@ Døme: <p>Søker du pÃ¥ et ord eller en ordkombinasjon som gir treff i mer enn to artikler, ser du en komprimert utgave av artiklene. Det er to mÃ¥ter Ã¥ se hele innholdet pÃ¥: Enten klikker du pÃ¥ selve oppslagsordet i burgunder øverst i artikkelen, eller sÃ¥ kan du klikke der det stÃ¥r «vis artikkel». </p> <h3>Søk med jokertegn</h3> -<p>Dersom du er usikker pÃ¥ skrivemÃ¥ten av et ord eller ønsker treff i mer enn én artikkel, kan du bruke <span class="search_symbol">%</span> og <span class="search_symbol">*</span> for Ã¥ fÃ¥ treff pÃ¥ null, ett eller flere tegn. -Symbolet <span class="search_symbol">_</span> erstatter kun ett tegn. Du kan plassere jokertegnene flere steder i søkeordet.</p> +<p>Dersom du er usikker pÃ¥ skrivemÃ¥ten av et ord eller ønsker treff i mer enn én artikkel, kan du bruke <code>%</code> og <code>*</code> for Ã¥ fÃ¥ treff pÃ¥ null, ett eller flere tegn. +Symbolet <code>_</code> erstatter kun ett tegn. Du kan plassere jokertegnene flere steder i søkeordet.</p> Eksempler: <p>Søker du pÃ¥ «<a href="https://ordbokene.no/bm,nn/search?q=arbeid%2ar&scope=ei">arbeid*r</a>», er de første treffene i nedtrekksmenyen «arbeider» i <em>BokmÃ¥lsordboka</em> og «arbeidar» i <em>Nynorskordboka</em>.</p> @@ -42,7 +42,7 @@ Eksempler: <p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=f%C3%B8rsk%25l%C3%A6r_r&scope=ei">førsk%lær_r</a>» gir treff pÃ¥ «førskolelærer» i <em>BokmÃ¥lsordboka</em> og «førskolelærar/førskulelærar» i <em>Nynorskordboka</em>.</p> <h3>Kombiner søkeord</h3> -<p>Symbolet <span class="search_symbol">|</span> gjør det mulig Ã¥ søke med flere søkeord samtidig. Det er ogsÃ¥ mulig Ã¥ kombinere søkeord som inneholder jokertegn.</p> +<p>Symbolet <code>|</code> gjør det mulig Ã¥ søke med flere søkeord samtidig. Det er ogsÃ¥ mulig Ã¥ kombinere søkeord som inneholder jokertegn.</p> Eksempler: <p>Søket «<a href="https://ordbokene.no/bm,nn/search?q=kj%C3%A6rlighet%7Ckj%C3%A6rleik&scope=ei">kjærlighet|kjærleik</a>» gir treff pÃ¥ «kjærlighet» i <em>BokmÃ¥lsordboka</em> og «kjærleik» i <em>Nynorskordboka</em>.</p> @@ -55,8 +55,8 @@ Eksempler: <em>gi katten i</em> (not give a damn). It is also possible to find such expressions at the end of each article or the articles they are linked to. When typing a single word in the search field, the drop-down menu also shows the common expressions the word is part of. </p> <h3>Wildcard search</h3> -<p>If unsure of the spelling of a word or want results from more than one article, it is possible to use <span class="search_symbol">%</span> and <span class="search_symbol">*</span> to replace zero, one or more characters. -Underscore (<span class="search_symbol">_</span>) replaces only one character. You may use the wilcard symbols multiple times in you search expression.</p> +<p>If unsure of the spelling of a word or want results from more than one article, it is possible to use <code>%</code> and <code>*</code> to replace zero, one or more characters. +Underscore (<code>_</code>) replaces only one character. You may use the wilcard symbols multiple times in your search expression.</p> Examples: <p>The search “<a href="https://ordbokene.no/bm,nn/search?q=arbeid%2ar&scope=ei">arbeid*r</a>â€, will result in results in the drop-down menu with “arbeider†(worker) in the BokmÃ¥l dictionary and “arbeidar†(worker) in the Nynorsk Dictionary.</p> @@ -64,7 +64,7 @@ Examples: <p>Søket “<a href="https://ordbokene.no/bm,nn/search?q=f%C3%B8rsk%25l%C3%A6r_r&scope=ei">førsk%lær_r</a>†gives resutls for “førskolelærer†(pre-school teacher) in the BokmÃ¥l dictionary and “førskolelærar/førskulelærar†(pre-school teacher) in the Nynorsk dictionary.</p> <h3>Combine search terms</h3> -<p>The symbol <span class="search_symbol">|</span> allows you to use in several search words at the same time. You can also combine search words that contain wildcard symbols.</p> +<p>The symbol <code>|</code> allows you to use in several search words at the same time. You can also combine search words that contain wildcard symbols.</p> Examples: <p>The search “<a href="https://ordbokene.no/bm,nn/search?q=kj%C3%A6rlighet%7Ckj%C3%A6rleik&scope=ei">kjærlighet|kjærleik</a>†yields the result “kjærlighet†in the BokmÃ¥l dictionary and “kjærleik†(love) in the Nynorsk dictionary.</p> @@ -90,9 +90,12 @@ Examples: text-decoration: none; } - - blockquote { margin-left:50px; } + +code { + background-color: var(--v-tertiary-darken1) !important; +} + </style> diff --git a/src/components/InflectionButton.vue b/src/components/InflectionButton.vue index 65af2104c6773eabaeda4c057c444dfd429fab6e..2047a6cfcedb7638f0169718c15841d885371467 100644 --- a/src/components/InflectionButton.vue +++ b/src/components/InflectionButton.vue @@ -1,16 +1,16 @@ <!-- eslint-disable --> <template> - <div class="inflection-wrapper" v-if="inflected && !$parent.collapsed"> + <span class="inflection-wrapper" v-if="inflected"> - <v-btn class="show-inflection" width="160px" rounded depressed small @click.native="toggle" :class="$vuetify.breakpoint.name"> - <span>{{inflection_expanded? $t('article.hide_inflection', $parent.content_locale):$t('article.show_inflection', $parent.content_locale)}}</span><span class = "inflection-arrow"><v-icon right>{{this.inflection_expanded? "expand_less" : "expand_more"}}</v-icon></span> + <v-btn class="show-inflection" x-small text rounded @click.native="toggle" :class="$vuetify.breakpoint.name"> + <span>{{inflection_expanded? $t('article.hide_inflection', $parent.content_locale):$t('article.show_inflection', $parent.content_locale)}}</span><span class = "inflection-arrow"><v-icon x-small right>{{this.inflection_expanded? "remove_circle" : "add_circle"}}</v-icon></span> </v-btn> <div class="inflection-canvas" v-if="inflection_expanded"> <inflectionTable :lemmaList="lemmas_with_word_class_and_lang" :mq="$vuetify.breakpoint.name" /> </div> -</div> +</span> </template> <script> @@ -59,14 +59,9 @@ export default { <style> .show-inflection .v-icon { color: var(--v-primary-base) !important; + margin-left: 4px !important; } -.inflection-arrow { - position: absolute; - right: -4px; - margin-left: 3px; - font-size: 10px; -} @keyframes open { 0% { @@ -84,6 +79,7 @@ export default { background-color: rgba(255, 255, 255, 0); /*max-width: 100vw;*/ padding-top: 10px; + padding-bottom: 10px; } diff --git a/src/components/Menu.vue b/src/components/Menu.vue index a6d89166364650a9a41ac49376ac909dca08dcbc..9c19bf8d1c5bdc30fbd10c183a58670900db0bd5 100644 --- a/src/components/Menu.vue +++ b/src/components/Menu.vue @@ -55,7 +55,7 @@ </v-list-group> - <v-list-item v-on:click='$store.commit("resetStore")'> + <v-list-item v-if="!$store.state.unavailable" v-on:click='$store.commit("resetStore")'> <v-list-item-icon><v-icon>delete</v-icon></v-list-item-icon> <v-list-item-title>{{$t("settings.reset")}}</v-list-item-title> </v-list-item> diff --git a/src/components/Pagination.vue b/src/components/Pagination.vue index bb80ab0507e6425f99eb0e82eeeef2517a5b699b..dfd6f6c061487ef59f328a1b2ccef15afd57e67c 100644 --- a/src/components/Pagination.vue +++ b/src/components/Pagination.vue @@ -1,19 +1,18 @@ <template> <div v-bind:class="{'centered': bottom}"> <span color="tertiary" class = "pagination"> - <v-pagination @input="update" v-model="$parent.$parent.page" :class="$vuetify.breakpoint.name" :total-visible="$vuetify.breakpoint.smAndDown ? 5 : 8" circle :length="Math.ceil(Math.max($parent.count_bm, $parent.count_nn)/$parent.$parent.perPage)"></v-pagination> - <span class="result-counts" v-if="!bottom"> + <v-pagination @input="update" v-model="$parent.$parent.page" :class="$vuetify.breakpoint.name" :total-visible="$vuetify.breakpoint.smAndDown && !bottom ? 5 : 8" circle :length="Math.ceil(Math.max($parent.count_bm, $parent.count_nn)/$parent.$parent.perPage)"></v-pagination> + <span class="result-counts" v-if="!bottom && $vuetify.breakpoint.smAndDown"> <span class="total-results">{{$parent.$parent.total_results()}} treff </span> <span v-if="$parent.$parent.lang=='bm,nn'" class="dict-counts"> | {{$vuetify.breakpoint.mdAndDown? 'bokmÃ¥l': 'BokmÃ¥lsordboka'}}: {{$parent.count_bm}} | {{$vuetify.breakpoint.mdAndDown? 'nynorsk': 'Nynorskordboka'}}: {{$parent.count_nn}}</span> <span v-if="$parent.$parent.lang!='bm,nn'" class="dict-counts"> | {{$t('dicts_inline.'+$parent.lang)}}</span> </span> - </span> - <v-btn v-if="bottom && $parent.$parent.total_results() > 6" text @click.native="to_top"><v-icon left>expand_less</v-icon>{{$t('to_top')}}</v-btn> </div> </template> + <script> export default ({ @@ -42,6 +41,7 @@ export default ({ </script> <style scoped> + .centered { text-align: center; padding-bottom: 10px; @@ -52,13 +52,5 @@ export default ({ padding-bottom: 10px } -div { - padding-bottom: 10px; - padding-top: 10px; -} - -.lg div, .xl div { - padding-top: 24px; -} </style> \ No newline at end of file diff --git a/src/components/SearchResults.vue b/src/components/SearchResults.vue index 865e457b33c6665decc346baf3e6f05c38229a6e..2a0e289023241fc7c6838cdd3570dce501afa7e6 100644 --- a/src/components/SearchResults.vue +++ b/src/components/SearchResults.vue @@ -1,6 +1,22 @@ <template> <section id="search_results" :class="$vuetify.breakpoint.name"> + + <div v-if="show_per_page" class="pagination-container"> <Pagination v-if="show_pagination" @update-page="$emit('update-page')"/> + <span id="per-page"> + <span id="per-page-label">{{$t('per_page')}}</span> + <v-select class="per-page-select" + aria-labelledby="per-page-label" + v-model="$parent.perPage" + :items="[10, 20, 50, 100]" + @input="$parent.update_per_page" + hide-details + dense + :menu-props="{ offsetY: true }" + append-icon="expand_more"/> + </span> + </div> + <div class="flex-container" v-if="!$parent.waiting" :class="$vuetify.breakpoint.name"> <div class="hits" v-if="$vuetify.breakpoint.smAndDown"> @@ -16,6 +32,7 @@ </div> <div class="hits" v-if="$vuetify.breakpoint.mdAndUp && lang!= 'nn'" v-bind:class="{'wide': lang=='bm'}"> + <div v-if="(count_bm + count_nn) > 2" class="dict-label-top"> <span class="dict-label-title"> bokmÃ¥lsordboka </span> | {{count_bm}} treff</div> <div class="article_container" v-for="(result, index) in results_bm" :key="index + bm_hash"> <Article :article="result" @@ -26,6 +43,7 @@ </div> </div> <div class="hits" v-if="$vuetify.breakpoint.mdAndUp && lang!='bm'" v-bind:class="{'wide': lang=='nn'}"> + <div v-if="(count_bm + count_nn) > 2" class="dict-label-top"> <span class="dict-label-title"> nynorskordboka </span> | {{count_nn}} treff</div> <div class="article_container" v-for="(result, index) in results_nn" :key="index + nn_hash"> <Article :article="result" @@ -82,7 +100,11 @@ export default { }, show_pagination: function() { return !this.$parent.article && this.$parent.article_info && (this.count_bm > this.$parent.perPage || this.count_nn > this.$parent.perPage) - } + }, + + show_per_page: function() { + return this.$parent.article_info && (this.count_bm > 10 || this.count_nn > 10) && !this.$parent.article + }, }, methods: { article_link_click: function(item) { @@ -137,7 +159,6 @@ export default { } .pagination { - margin-left: 10px; display: flex; flex-wrap: wrap; @@ -161,5 +182,49 @@ export default { justify-content: right; } + #per-page { + padding-bottom: 10px; + padding-right: 10px; + margin-left: 10px; + display: flex; + flex-direction: row; + gap: 10px; + align-items: baseline; + +} + + + +.per-page-select { + max-width: 64px;; +} + + +.pagination-container { + margin-left: 10px; + padding-bottom: 10px; + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.lg .pagination-container, .xl .pagination-container { + padding-top: 10px; + +} + +.dict-label-title { + color: var(--v-primary-base) ; + font-weight: bold; + font-size: 24px; + font-variant-caps: all-small-caps; +} + +.dict-label-top { + padding: 10px; + padding-left: 24px; +} + </style> diff --git a/src/components/SearchToolbar.vue b/src/components/SearchToolbar.vue index 1be19b5ebb509314c3e70299e99f9abc7973d983..cc931d62538243304309f90b36560996df402e02 100644 --- a/src/components/SearchToolbar.vue +++ b/src/components/SearchToolbar.vue @@ -52,7 +52,7 @@ hide-details :label="$t('options.fulltext')"/> </span> - <span> + <span id="toolbar-pos-select"> <v-select v-model="$parent.pos_selected" @@ -179,5 +179,9 @@ export default { width: 300px; } +#toolbar-pos-select { + max-width: 200px; +} + </style> diff --git a/src/components/SplitInf.vue b/src/components/SplitInf.vue index 74e35fa790af63bba7916d549d51ea23d372ba68..65946c042952aa94e332d1d5be90b690601ccdf6 100644 --- a/src/components/SplitInf.vue +++ b/src/components/SplitInf.vue @@ -4,21 +4,21 @@ v-model="menu" offset-x max-width="200px"> <template v-slot:activator="{ on, attrs }"> - <v-btn aria-label="Mer informasjon om kløyvd infinitiv" + <v-btn :aria-label="$t('split_inf.label', $parent.content_locale)" x-small icon v-on="on" v-bind="attrs" class="info-button"> <v-icon color="primary" - size="14px">$vuetify.icons.value.info</v-icon> + x-small>info</v-icon> </v-btn> </template> <v-card tile class="info-card"> - Dersom du bruker kløyvd infinitiv, skal dette verbet ha <em>-a</em> i infinitiv. Les meir + {{$t('split_inf.content[0]', $parent.content_locale)}} <em>-a</em> {{$t('split_inf.content[1]', $parent.content_locale)}} <a target="_blank" - href="https://www.sprakradet.no/svardatabase/sporsmal-og-svar/kloyvd-infinitiv-/">her</a>. + href="https://www.sprakradet.no/svardatabase/sporsmal-og-svar/kloyvd-infinitiv-/">{{$t('split_inf.content[2]', $parent.content_locale)}}</a> </v-card> </v-menu> </span> diff --git a/src/components/SubArticle.vue b/src/components/SubArticle.vue index 5d35065dc52c0f69e32fb9c86cc54ff1cc4c0425..8fa2ad0f3b3723388d03ed0947fb4feb7dcd0172 100644 --- a/src/components/SubArticle.vue +++ b/src/components/SubArticle.vue @@ -4,8 +4,8 @@ {{body.lemmas[0]}} </span> <ul> - <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" /> + <DefElement :body="body.intro" v-if="body.intro" :dictionary="dictionary" @article-click="article_link_click" @error="article_error"/> + <Definition :level="9" :body="body.article.body.definitions[0]" :dictionary="dictionary" @article-click="article_link_click" @error="article_error"/> </ul> </li> </template> @@ -33,6 +33,9 @@ export default { methods: { article_link_click: function(item) { this.$emit('article-click', item) + }, + article_error: function(payload) { + this.$emit('error', payload) } } } diff --git a/src/locales/eng.json b/src/locales/eng.json index 37a71662cdb9930087cf4565b40f2de339ea0e6d..14ff27315d81281f192bde52116677dfea32bc02 100644 --- a/src/locales/eng.json +++ b/src/locales/eng.json @@ -1,11 +1,13 @@ { "sub_title": "BokmÃ¥lsordboka and Nynorskordboka", - "photo": "Photo by Jeremy Bishop on Unsplash", + "photo": "Photo by Jeremy Bishop on unsplash.com", "close": "Close", "cancel": "Cancel", "or": " or ", "and": " and ", + "from": "from", "to_top": "Back to top", + "per_page": "Results per page: ", "search_placeholder": "Search here", "footer_description": " show spelling and inflection in line with official Norwegian spelling. The Language Council and the University of Bergen are behind the dictionaries. Feel free to give us feedback on", "accessibility": { @@ -74,6 +76,11 @@ "ADP": "prepositions", "VERB": "verbs", "INTJ": "interjections" +}, +"split_inf": { + "title": "split infinitive", + "label": "More information about split infinitive", + "content": ["If you use split infinitive, the infinitive form of this verb will have the suffix", "", "Read more here (Norwegian)"] }, "settings": { "title": "Settings", diff --git a/src/locales/nno.json b/src/locales/nno.json index 9a6b714faa8442f4edf341dc43c091a822ecc51f..5a7df1d42a550ce4fbd9451175a41402c40caa70 100644 --- a/src/locales/nno.json +++ b/src/locales/nno.json @@ -1,11 +1,13 @@ { "sub_title": "BokmÃ¥lsordboka og Nynorskordboka", - "photo": "Foto: Jeremy Bishop (Unsplash)", + "photo": "Foto: Jeremy Bishop (unsplash.com)", "close": "Lukk", "cancel": "Avbryt", "to_top": "Til toppen", + "per_page": "Resultat per side: ", "or": " eller ", "and": " og ", + "from": "frÃ¥", "search_placeholder": "Søk her", "footer_description": " viser skrivemÃ¥te og bøying i trÃ¥d med norsk rettskriving. SprÃ¥krÃ¥det og Universitetet i Bergen stÃ¥r bak ordbøkene. Gje oss gjerne ei tilbakemelding pÃ¥", "accessibility": { @@ -75,6 +77,11 @@ "VERB": "verb", "INTJ": "interjeksjonar" }, + "split_inf": { + "title": "kløyvd infinitiv", + "label": "Meir informasjon om kløyvd infinitiv", + "content": ["Dersom du bruker kløyvd infinitiv, skal dette verbet ha", "i infinitiv.", "Les meir her"] +}, "settings": { "title": "Innstillingar", "inflection_no": "Vis bøyingskodane frÃ¥ dei trykte ordbøkene", diff --git a/src/locales/nob.json b/src/locales/nob.json index 0e6581057edd50432e6780f9e3c632e0b68c155d..45e2a831162750cbe8c0ee557f90b0d33e3a6bbb 100644 --- a/src/locales/nob.json +++ b/src/locales/nob.json @@ -1,11 +1,13 @@ { "sub_title": "BokmÃ¥lsordboka og Nynorskordboka", - "photo": "Foto: Jeremy Bishop (Unsplash)", + "photo": "Foto: Jeremy Bishop (unsplash.com)", "close": "Lukk", "cancel": "Avbryt", "to_top": "Til toppen", + "per_page": "Resultater per side: ", "or": " eller ", "and": " og ", + "from": "fra", "search_placeholder": "Søk her", "footer_description": " viser skrivemÃ¥te og bøyning i trÃ¥d med norsk rettskrivning. SprÃ¥krÃ¥det og Universitetet i Bergen stÃ¥r bak ordbøkene. Gi oss gjerne tilbakemelding pÃ¥", "accessibility": { @@ -86,6 +88,11 @@ "ADP": "preposisjoner", "VERB": "verb", "INTJ": "interjeksjoner" +}, +"split_inf": { + "title": "kløyvd infinitiv", + "label": "Mer informasjon om kløyvd infinitiv", + "content": ["Dersom du bruker kløyvd infinitiv, skal dette verbet ha", "i infinitiv.", "Les meir her"] }, "settings": { "title": "Innstillinger", diff --git a/src/main.js b/src/main.js index bcfb2f217ec04ce54abf8dc914f7c549adc7f390..dfdc4b5a9282a5b6a0bd497de5a0d834a3b968ab 100644 --- a/src/main.js +++ b/src/main.js @@ -50,20 +50,18 @@ const router = new VueRouter({ children: [ { path: 'search', - name: 'query', - params: {q: '', scope: 'w'} - }, - { - name: 'word', - path: 'w/:query' + name: 'search' }, { name: 'lookup', path: ':id(\\d+)/:lemma?' }, { - name: 'search', - path: 'search/:query' + name: "short", + path: ':query', + redirect: to => { + return {path: 'search', query: {q: to.params.query, scope: 'e'}} + } } ] }, @@ -98,22 +96,30 @@ const store = new Vuex.Store({ state: { showSearchToolbar: null, showInflectionNo: null, - currentLocale: null, - collapseArticles: null, + currentLocale: 'nno', + collapseArticles: 'auto', defaultDict: null, + perPage: 20, menuOpen: false, noMouse: null, searchRoute: null, - fulltextHighlight: false + fulltextHighlight: false, + unavailable: null }, mutations: { initStore(state) { + try { state.showSearchToolbar = localStorage.getItem('showSearchToolbar') || false state.showInflectionNo = localStorage.getItem('showInflectionNo') || false - state.currentLocale = localStorage.getItem('currentLocale') + state.currentLocale = localStorage.getItem('currentLocale') || null state.defaultDict = localStorage.getItem('defaultDict') || 'bm,nn' + state.perPage = localStorage.getItem('perPage') || 20 state.collapseArticles = localStorage.getItem('collapseArticles') || 'auto' - state.noMouse = !window.matchMedia('(pointer: fine)').matches + } catch(e) { + console.log("localStorage unavailable") + state.unavailable = true + } + state.noMouse = navigator.userAgentData? navigator.userAgentData.mobile : !window.matchMedia('(pointer: fine)').matches if (state.currentLocale) { i18n.locale = state.currentLocale @@ -123,27 +129,31 @@ const store = new Vuex.Store({ Vue.$plausible.trackEvent("set locale", {props: {from: state.currentLocale, to: payload.locale}}) state.currentLocale = payload.value i18n.locale = payload.value - localStorage.setItem("currentLocale", payload.value); + if (!state.unavailable) localStorage.setItem("currentLocale", payload.value); }, setCollapse(state, value) { Vue.$plausible.trackEvent("set collapse", {props: {from: state.collapseArticles, to: value}}) - localStorage.setItem("collapseArticles", value) + if (!state.unavailable) localStorage.setItem("collapseArticles", value) state.collapseArticles = value }, setFulltextHighlight(state, value) { state.fulltextHighlight = value }, + setPerPage(state, value) { + if (!state.unavailable) localStorage.setItem("perPage", value) + state.perPage = value + }, toggleInflectionNo(state) { Vue.$plausible.trackEvent("toggle inflection number", {props: {from: state.showInflectionNo, to: !state.showInflectionNo}}) state.showInflectionNo = !state.showInflectionNo - localStorage.setItem('showInflectionNo', state.showInflectionNo); + if (!state.unavailable) localStorage.setItem('showInflectionNo', state.showInflectionNo); }, toggleMenu(state) { state.menuOpen = !state.menuOpen }, setDefaultDict(state, value) { Vue.$plausible.trackEvent("set default dict", {props: {from: state.defaultDict, to: value}}) - localStorage.setItem("defaultDict", value) + if (!state.unavailable) localStorage.setItem("defaultDict", value) state.defaultDict = value }, resetStore() {