App.vue 9.36 KB
Newer Older
Mathias Goebel's avatar
Mathias Goebel committed
1
2
<template>
  <div id="q-app">
3
    <q-layout view="hHh lpr fFf">
nwindis's avatar
nwindis committed
4
5
      <Header
        v-if="config.headers.all"
6
        :collectiontitle="collectiontitle"
7
        :config="config"
8
        :imageurl="imageurl"
9
        :item="item"
10
11
        :itemurls="itemurls"
        :manifests="manifests"
12
        :panels="panels"
13
14
15
16
17
      />

      <q-page-container>
        <router-view
          :collection="collection"
18
          :config="config"
schneider210's avatar
schneider210 committed
19
          :fontsize="fontsize"
20
          :imageurl="imageurl"
21
          :item="item"
22
          :labels="config.labels"
23
          :language="language"
24
          :manifests="manifests"
25
          :panels="panels"
26
          :request="request"
27
28
          :transcription="transcription"
          :transliteration="transliteration"
29
30
31
          :tree="tree"
        />
      </q-page-container>
32

nwindis's avatar
DUMP    
nwindis committed
33
34
      <Footer
        :projectcolors="config.colors"
schneider210's avatar
schneider210 committed
35
        :standalone="config.standalone"
nwindis's avatar
nwindis committed
36
      />
37
    </q-layout>
Mathias Goebel's avatar
Mathias Goebel committed
38
39
40
41
  </div>
</template>

<script>
schneider210's avatar
schneider210 committed
42
import { colors } from 'quasar';
schneider210's avatar
schneider210 committed
43
import Footer from '@/components/footer.vue';
44
import Header from '@/components/header.vue';
45
import Panels from '@/mixins/panels';
46

Mathias Goebel's avatar
Mathias Goebel committed
47
export default {
48
  name: 'TIDO',
49
50
  components: {
    Header,
51
    Footer,
52
  },
53
  mixins: [Panels],
54
55
56
  data() {
    return {
      collection: {},
57
      collectiontitle: '',
58
      config: {},
schneider210's avatar
schneider210 committed
59
      fontsize: 14,
60
      imageurl: '',
61
      isCollection: false,
62
      item: {},
63
64
      itemurl: '',
      itemurls: [],
65
      language: '',
66
      manifests: [],
67
68
      transcription: '',
      transliteration: '',
69
70
71
      tree: [],
    };
  },
nwindis's avatar
nwindis committed
72
73
74
75
76
  created() {
    this.getConfig();
    this.init();
    this.itemurls.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));

schneider210's avatar
schneider210 committed
77
78
    this.$q.dark.set('auto');

nwindis's avatar
nwindis committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    if (this.config.colors.primary && this.config.colors.secondary && this.config.colors.accent) {
      colors.setBrand('primary', this.config.colors.primary);
      colors.setBrand('secondary', this.config.colors.secondary);
      colors.setBrand('accent', this.config.colors.accent);
    }
  },
  mounted() {
    /**
      * listen to fontsize change (user interaction). emitted in @/components/content.vue
      * in- or rather decrease fontsize of the text by 1px
      * default fontsize: 14px
      *
      * @param number fontsize
      */
    this.$root.$on('update-fontsize', (fontsize) => {
      this.fontsize = fontsize;
    });
    this.$root.$on('panels-position', (newPanels) => {
      this.panels = newPanels;
    });
    /**
      * listen to item change (user interaction).
      * emitted in: *getItemurls*; handler for tree nodes. fired on user interaction
      *
      * @param string url
      */
    this.$root.$on('update-item', (url) => {
      this.itemurl = url;
      this.$router.push({ query: { itemurl: url } });
      // NOTE: Set imageurl to an empty string. Otherwise, if there is no corresponding image,
      // the "preceding" image according to the "preceding" item will be shown.
      this.imageurl = '';
      this.getItemData(url);
    });
  },
114
  methods: {
schneider210's avatar
schneider210 committed
115
116
117
118
119
120
121
122
123
124
    /**
      * get resources using JavaScript's native fetch api
      * caller: *getCollection()*, *getItemData()*, *getManifest()*
      *         *@/components/content.vue::getSupport()*, *@/components/content.vue::created-hook*
      *
      * @param string url
      * @param string responsetype
      *
      * @return promise data
      */
125
126
127
128
129
130
    async request(url, responsetype = 'json') {
      const response = await fetch(url);
      const data = await (responsetype === 'text' ? response.text() : response.json());

      return data;
    },
schneider210's avatar
schneider210 committed
131
132
133
134
135
136
137
138
    /**
      * get collection data according to 'entrypoint'
      * (number of requests equal the number of manifests contained within a collection)
      * initialize the tree's root node
      * caller: *init()*
      *
      * @param string url
      */
139
    getCollection(url) {
140
141
      this.isCollection = true;

142
143
144
      this.request(url)
        .then((data) => {
          this.collection = data;
145
          this.collectiontitle = this.getLabel(data);
146

147
148
149
150
151
152
          this.tree.push(
            {
              children: [],
              handler: (node) => {
                this.$root.$emit('update-tree-knots', node.label);
              },
153
154
              label: this.collectiontitle,
              'label-key': this.collectiontitle,
155
156
157
              selectable: false,
            },
          );
158
159
160
161
162
163

          if (Array.isArray(data.sequence)) {
            data.sequence.forEach((seq) => this.getManifest(seq.id));
          }
        });
    },
schneider210's avatar
schneider210 committed
164
165
166
167
    /**
      * get config object (JSON) from index.html
      * caller: *created-hook*
      */
168
    getConfig() {
169
      this.config = JSON.parse(document.getElementById('tido-config').text);
170
    },
171
172
173
174
175
176
177
178
179
180
181
182
    /**
      * caller: *getItemData()*
      *
      * @param string array
      *
      * @return array
      */
    getContent(content) {
      return [
        content[0].url, content[1].url,
      ];
    },
schneider210's avatar
schneider210 committed
183
184
185
186
187
188
    /**
      * fetch all data provided on 'item level'
      * caller: *mounted-hook*, *getManifest()*
      *
      * @param string url
      */
189
    getItemData(url) {
190
191
      this.request(url)
        .then((data) => {
192
          this.item = data;
193

194
195
          [this.transcription, this.transliteration] = this.getContent(data.content);

196
          this.imageurl = data.image.id || '';
197
198
        });
    },
schneider210's avatar
schneider210 committed
199
200
201
202
203
204
205
    /**
      * caller: *getItemUrls()*
      *
      * @param string nodelabel
      *
      * @return number idx
      */
206
207
208
209
210
211
212
213
214
    getItemIndex(nodelabel) {
      let idx = 0;
      this.itemurls.forEach((item, index) => {
        if (item === nodelabel) {
          idx = index;
        }
      });
      return idx;
    },
215
216
217
218
219
220
221
222
223
224
225
    /**
      * extract the 'label part' of the itemurl
      * caller: *getItemUrls()*
      *
      * @param string itemurl
      *
      * @return string 'label part'
      */
    getItemLabel(itemurl) {
      return itemurl.replace(/.*-(.*)\/latest.*$/, '$1');
    },
schneider210's avatar
schneider210 committed
226
227
228
229
230
231
232
233
234
    /**
      * get all itemurls hosted by each manifest's sequence to populate the aprropriate tree node
      * caller: *getManifest()*
      *
      * @param array sequence
      * @param string label
      *
      * @return array urls
      */
235
236
    getItemUrls(sequence, label) {
      const urls = [];
237

238
239
      sequence.forEach((item) => {
        const itemLabel = this.getItemLabel(item.id);
240

241
242
        urls.push(
          {
243
244
            label: item.id,
            'label-key': `${this.config.labels.item} ${itemLabel}`,
245
246
247
248
            handler: (node) => {
              if (this.itemurl === node.label) {
                return;
              }
schneider210's avatar
schneider210 committed
249
250
251
              // node.label === itemurl
              // @param label === manifest label; passed by getManifest()
              this.$root.$emit('update-item', node.label, this.getSequenceIndex(label));
252
              this.$root.$emit('update-item-index', this.getItemIndex(node.label));
253
              this.$root.$emit('update-sequence-index', this.getSequenceIndex(label));
254
255
256
257
258
259
            },
          },
        );
      });
      return urls;
    },
schneider210's avatar
schneider210 committed
260
261
262
263
264
265
266
267
    /**
      * get the collection label, if provided; otherwise get the manifest label
      * caller: *getCollection()*, *getManifest()*
      *
      * @param object data
      *
      * @return string 'label'
      */
268
269
270
271
272
273
    getLabel(data) {
      if (Object.keys(this.collection).length) {
        return data.title && data.title[0].title ? data.title[0].title : data.label;
      }
      return data.label ? data.label : 'Manifest <small>(No label available)</small>';
    },
schneider210's avatar
schneider210 committed
274
275
276
277
278
279
    /**
      * get all the data provided on 'manifest level'
      * caller: *init()*, *getCollection()*
      *
      * @param string url
      */
280
281
282
    getManifest(url) {
      this.request(url)
        .then((data) => {
283
284
285
286
287
          // if the entrypoint points to a single manifest, initialize the tree
          if (this.isCollection === false) {
            this.tree.push({ label: '', 'label-key': this.config.labels.manifest, children: [] });
          }

288
289
290
291
292
293
294
295
296
297
          if (!Array.isArray(data.sequence)) {
            data.sequence = [data.sequence];
          }

          if (data.sequence[0] !== 'undefined') {
            data.sequence.map((seq) => this.itemurls.push(seq.id));
          }
          this.manifests.push(data);

          this.tree[0].children.push(
298
299
            {
              children: this.getItemUrls(data.sequence, data.label),
300
301
302
303
304
305
              label: data.label,
              'label-key': data.label,
              handler: (node) => {
                this.$root.$emit('update-tree-knots', node.label);
              },
              selectable: false,
306
            },
307
308
309
310
          );
          // make sure that urls are set just once on init
          if (!this.itemurl && data.sequence[0]) {
            this.itemurl = data.sequence[0].id;
311
            this.getItemData(data.sequence[0].id);
312
313
314
          }
        });
    },
schneider210's avatar
schneider210 committed
315
316
317
318
319
320
321
    /**
      * caller: *getItemUrls()*
      *
      * @param string label
      *
      * @return number index
      */
322
    getSequenceIndex(label) {
323
324
      let index = 0;
      this.manifests.forEach((manifest, idx) => {
325
        if (manifest.label === label) {
326
327
328
329
330
          index = idx;
        }
      });
      return index;
    },
schneider210's avatar
schneider210 committed
331
332
333
334
335
336
    /**
      * decide whether to start with a collection or a single manifest
      * caller: *created-hook*
      *
      * @return function getCollection() | getManifest()
      */
337
338
339
340
341
342
343
    init() {
      return this.config.entrypoint.match(/collection.json\s?$/)
        ? this.getCollection(this.config.entrypoint)
        : this.getManifest(this.config.entrypoint);
    },
  },

Mathias Goebel's avatar
Mathias Goebel committed
344
345
};
</script>