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

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

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

<script>
schneider210's avatar
schneider210 committed
41
import Footer from '@/components/footer.vue';
42
import Header from '@/components/header.vue';
dindigala's avatar
dindigala committed
43
import PanelsMixin from '@/config/panels.js';
nwindis's avatar
DUMP    
nwindis committed
44
import { colors } from 'quasar';
45

Mathias Goebel's avatar
Mathias Goebel committed
46
export default {
47
48
49
  name: 'Viewer',
  components: {
    Header,
50
    Footer,
51
  },
dindigala's avatar
dindigala committed
52
  mixins: [PanelsMixin],
53
54
  data() {
    return {
55
      annotations: {},
56
      collection: {},
57
58
      collectiontitle: '',
      contenturl: '',
59
      config: {},
schneider210's avatar
schneider210 committed
60
      fontsize: 14,
61
      imageurl: '',
62
      isCollection: false,
63
      itemlabel: '',
64
      itemlanguage: '',
65
66
67
68
69
70
71
72
      itemurl: '',
      itemurls: [],
      label: '',
      manifests: [],
      tree: [],
    };
  },
  methods: {
schneider210's avatar
schneider210 committed
73
74
75
76
77
78
79
80
81
82
    /**
      * 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
      */
83
84
85
86
87
88
    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
89
90
91
92
93
94
95
96
    /**
      * 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
      */
97
    getCollection(url) {
98
99
      this.isCollection = true;

100
101
102
103
104
      this.request(url)
        .then((data) => {
          this.collection = data;
          this.label = this.getLabel(data);

105
106
107
108
109
          this.request(data.annotationCollection)
            .then((annotations) => {
              this.annotations = annotations.annotationCollection;
            });

110
111
112
113
114
115
116
117
118
119
120
          this.tree.push(
            {
              children: [],
              handler: (node) => {
                this.$root.$emit('update-tree-knots', node.label);
              },
              label: this.label,
              'label-key': this.label,
              selectable: false,
            },
          );
121
122
123
124
125
126

          if (Array.isArray(data.sequence)) {
            data.sequence.forEach((seq) => this.getManifest(seq.id));
          }
        });
    },
schneider210's avatar
schneider210 committed
127
128
129
130
    /**
      * get config object (JSON) from index.html
      * caller: *created-hook*
      */
131
132
133
    getConfig() {
      this.config = JSON.parse(document.getElementById('emo-config').text);
    },
schneider210's avatar
schneider210 committed
134
135
136
137
138
139
    /**
      * fetch all data provided on 'item level'
      * caller: *mounted-hook*, *getManifest()*
      *
      * @param string url
      */
140
    getItemData(url) {
141
142
      this.request(url)
        .then((data) => {
143
          this.collectiontitle = data.title;
144

145
          this.contenturl = data.content;
146
          this.imageurl = data.image && data.image.id ? data.image.id : '';
147
          this.itemlabel = data.n ? data.n : 'No itemlabel :(';
148
149
150
151
152
153

          // note: the scholars didn't mark the item language yet, so atm the API provides them all.
          // since we know, we are dealing with the arabic part of the collection, we define the language to be arabic.
          const [arabic] = data['x-langString'].split(',');

          this.itemlanguage = arabic;
154
155
        });
    },
schneider210's avatar
schneider210 committed
156
157
158
159
160
161
162
    /**
      * caller: *getItemUrls()*
      *
      * @param string nodelabel
      *
      * @return number idx
      */
163
164
165
166
167
168
169
170
171
    getItemIndex(nodelabel) {
      let idx = 0;
      this.itemurls.forEach((item, index) => {
        if (item === nodelabel) {
          idx = index;
        }
      });
      return idx;
    },
schneider210's avatar
schneider210 committed
172
173
174
175
176
177
178
179
180
    /**
      * 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
      */
181
182
    getItemUrls(sequence, label) {
      const urls = [];
183

184
      sequence.forEach((obj) => {
185
186
        const pagelabel = this.getPageLabel(obj.id);

187
188
189
        urls.push(
          {
            label: obj.id,
190
            'label-key': `${this.config.labels.item} ${pagelabel}`,
191
192
193
194
            handler: (node) => {
              if (this.itemurl === node.label) {
                return;
              }
schneider210's avatar
schneider210 committed
195
196
197
              // node.label === itemurl
              // @param label === manifest label; passed by getManifest()
              this.$root.$emit('update-item', node.label, this.getSequenceIndex(label));
198
              this.$root.$emit('update-item-index', this.getItemIndex(node.label));
199
              this.$root.$emit('update-sequence-index', this.getSequenceIndex(label));
200
201
202
203
204
205
            },
          },
        );
      });
      return urls;
    },
schneider210's avatar
schneider210 committed
206
207
208
209
210
211
212
213
    /**
      * get the collection label, if provided; otherwise get the manifest label
      * caller: *getCollection()*, *getManifest()*
      *
      * @param object data
      *
      * @return string 'label'
      */
214
215
216
217
218
219
    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
220
221
222
223
224
225
    /**
      * get all the data provided on 'manifest level'
      * caller: *init()*, *getCollection()*
      *
      * @param string url
      */
226
227
228
    getManifest(url) {
      this.request(url)
        .then((data) => {
229
230
231
232
233
          // 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: [] });
          }

234
235
236
237
238
239
240
241
242
243
          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(
244
245
            {
              children: this.getItemUrls(data.sequence, data.label),
246
247
248
249
250
251
              label: data.label,
              'label-key': data.label,
              handler: (node) => {
                this.$root.$emit('update-tree-knots', node.label);
              },
              selectable: false,
252
            },
253
254
255
256
257
258
259
260
          );

          if (!this.label) {
            this.label = this.getLabel(data);
          }
          // make sure that urls are set just once on init
          if (!this.itemurl && data.sequence[0]) {
            this.itemurl = data.sequence[0].id;
261
            this.getItemData(data.sequence[0].id);
262
263
264
          }
        });
    },
schneider210's avatar
schneider210 committed
265
266
267
268
269
270
271
272
    /**
      * extract the 'label part' of the itemurl
      * caller: *getItemUrls()*
      *
      * @param string itemurl
      *
      * @return string 'label part'
      */
273
274
275
    getPageLabel(itemurl) {
      return itemurl.replace(/.*-(.*)\/latest.*$/, '$1');
    },
schneider210's avatar
schneider210 committed
276
277
278
279
280
281
282
    /**
      * caller: *getItemUrls()*
      *
      * @param string label
      *
      * @return number index
      */
283
    getSequenceIndex(label) {
284
285
      let index = 0;
      this.manifests.forEach((manifest, idx) => {
286
        if (manifest.label === label) {
287
288
289
290
291
          index = idx;
        }
      });
      return index;
    },
schneider210's avatar
schneider210 committed
292
293
294
295
296
297
    /**
      * decide whether to start with a collection or a single manifest
      * caller: *created-hook*
      *
      * @return function getCollection() | getManifest()
      */
298
299
300
301
302
303
304
305
306
    init() {
      return this.config.entrypoint.match(/collection.json\s?$/)
        ? this.getCollection(this.config.entrypoint)
        : this.getManifest(this.config.entrypoint);
    },
  },
  created() {
    this.getConfig();
    this.init();
nwindis's avatar
DUMP    
nwindis committed
307
308
    this.$q.dark.set('auto');
    this.itemurls.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
309

nwindis's avatar
nwindis committed
310
    if (this.config.colors.primary && this.config.colors.secondary && this.config.colors.accent) {
nwindis's avatar
nwindis committed
311
312
313
314
      colors.setBrand('primary', this.config.colors.primary);
      colors.setBrand('secondary', this.config.colors.secondary);
      colors.setBrand('accent', this.config.colors.accent);
    }
315
316
  },
  mounted() {
schneider210's avatar
schneider210 committed
317
318
319
320
321
322
323
    /**
      * 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
      */
324
325
326
    this.$root.$on('update-fontsize', (fontsize) => {
      this.fontsize = fontsize;
    });
327
328
329
    this.$root.$on('panels-position', (newPanels) => {
      this.panels = newPanels;
    });
schneider210's avatar
schneider210 committed
330
331
332
333
334
335
    /**
      * listen to item change (user interaction).
      * emitted in: *getItemurls*; handler for tree nodes. fired on user interaction
      *
      * @param string url
      */
336
337
338
339
    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,
340
      // the "preceding" image according to the "preceding" item will be shown.
341
      this.imageurl = '';
342
      this.getItemData(url);
343
344
    });
  },
Mathias Goebel's avatar
Mathias Goebel committed
345
346
};
</script>