├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc.json ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Canvas.ts ├── Directory.ts ├── IConfigJSON.ts ├── LICENSE.txt ├── README.md ├── TypeFormat.ts ├── Utils.ts ├── boilerplate ├── annotation.json ├── canvas.json ├── collection.json ├── collectionitem.json ├── imageservice.json ├── label.json ├── manifest.json ├── manifestitem.json └── thumbnail.json ├── config.json ├── index.ts ├── package-lock.json ├── package.json ├── test ├── common.js ├── fixtures │ ├── behavior-paged.js │ ├── canvas-label-annotation.js │ ├── cat-jpg.js │ ├── commenting-text-with-format.js │ ├── commenting-text-with-type.js │ ├── commenting-text-without-type-format.js │ ├── dimensions-info.js │ ├── epub-external-resource-annotation.js │ ├── external-resource-annotation.js │ ├── json-value-with-format.js │ ├── json-value-without-format.js │ ├── json-value-without-motivation-type-format.js │ ├── manifests.js │ ├── multiple-behavior.js │ ├── painting-gltf.js │ ├── painting-jpg-with-xywh.js │ ├── painting-jpg.js │ ├── painting-threejs-json-with-type.js │ └── presentation-3-image-service.js ├── index.html ├── index.js ├── readme.md └── tests │ ├── behavior-paged-manifest.js │ ├── canvas-label-annotation-manifest.js │ ├── canvas-with-dimensions-manifest.js │ ├── canvas-with-presentation-3-image-service-manifest.js │ ├── collection-no-manifests.js │ ├── collection.js │ ├── custom-annotations-manifest.js │ ├── dat-gateway.js │ ├── do-promises-work.js │ ├── epub-collection.js │ ├── external-resource-annotation-manifest.js │ ├── file-annotation-collection.js │ ├── files-only-collection.js │ ├── files-only-manifest-dat.js │ ├── files-only-manifest.js │ ├── generate-thumbs-dat-manifest.js │ ├── generate-thumbs-http-gateway-dat-manifest.js │ ├── generate-thumbs-manifest.js │ ├── gh-pages.js │ ├── image-dimensions-manifest.js │ ├── multiple-behavior-manifest.js │ ├── readme-manifest.js │ ├── sort-canvases-manifest.js │ ├── sort-canvases-numeric-manifest.js │ ├── sort-files-numeric-manifest.js │ ├── thumbs-single-manifest-dat.js │ ├── thumbs-single-manifest.js │ ├── url.js │ ├── utils.js │ └── vercel-manifest.js ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.js 3 | !test/**/*.js 4 | testroot/**/index.json 5 | collection* 6 | npm-debug.log.* 7 | *.map -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | tsconfig.json 3 | test 4 | *.ts 5 | collection -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test 3 | collection 4 | .gitignore 5 | .npmignore 6 | .prettierignore 7 | .vscode 8 | .travis.yml 9 | *.txt 10 | *.js 11 | *.js.map 12 | *.json -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IIIF-Commons/biiif/1677fc56af3bc8acf526fbf4243c6245d4c4da09/.prettierrc.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Test Build", 11 | "runtimeExecutable": "npm", 12 | "windows": { 13 | "runtimeExecutable": "npm.cmd" 14 | }, 15 | "runtimeArgs": ["run-script", "testbuild"], 16 | "port": 5858 17 | }, 18 | { 19 | "type": "node", 20 | "request": "launch", 21 | "name": "Mocha Tests", 22 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 23 | "args": [ 24 | "-u", 25 | "bdd", 26 | "--timeout", 27 | "999999", 28 | "--colors", 29 | "${workspaceFolder}/test" 30 | ], 31 | "internalConsoleOptions": "openOnSessionStart" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": true 4 | }, 5 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "npm:build", 6 | "type": "shell", 7 | "group": { "kind": "build", "isDefault": true }, 8 | "command": "npm run build" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Canvas.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AnnotationMotivation, 3 | ExternalResourceType, 4 | } from "@iiif/vocabulary/dist-commonjs/"; 5 | import { basename, dirname, extname, join } from "path"; 6 | import { Directory } from "./Directory"; 7 | import { IConfigJSON } from "./IConfigJSON"; 8 | import { promise as glob } from "glob-promise"; 9 | import { 10 | cloneJson, 11 | compare, 12 | fileExists, 13 | formatMetadata, 14 | generateImageTiles, 15 | getFileDimensions, 16 | getFormatByExtension, 17 | getFormatByExtensionAndType, 18 | getFormatByType, 19 | getLabel, 20 | getThumbnail, 21 | getTypeByExtension, 22 | getTypeByFormat, 23 | isDirectory, 24 | isURL, 25 | log, 26 | normaliseFilePath, 27 | normaliseType, 28 | readYml, 29 | warn, 30 | } from "./Utils"; 31 | import annotationBoilerplate from "./boilerplate/annotation.json"; 32 | import config from "./config.json"; 33 | import imageServiceBoilerplate from "./boilerplate/imageservice.json"; 34 | import urljoin from "url-join"; 35 | 36 | export class Canvas { 37 | public canvasJson: any; 38 | public directory: Directory; 39 | public parentDirectory: Directory; 40 | public filePath: string; 41 | public directoryFilePath: string; 42 | public infoYml: any = {}; 43 | public url: URL; 44 | 45 | private _config: IConfigJSON = config; 46 | 47 | constructor(filePath: string, parentDirectory: Directory) { 48 | this.filePath = filePath; 49 | 50 | if (!isDirectory(this.filePath)) { 51 | this.directoryFilePath = dirname(this.filePath); 52 | } else { 53 | this.directoryFilePath = this.filePath; 54 | } 55 | 56 | this.parentDirectory = parentDirectory; 57 | // we only need a directory object to reference the parent directory when determining the virtual path of this canvas 58 | // this.directory.read() is never called. 59 | this.directory = new Directory( 60 | this.directoryFilePath, 61 | this.parentDirectory.url.href, 62 | undefined, 63 | this.parentDirectory 64 | ); 65 | 66 | this.url = parentDirectory.url; 67 | } 68 | 69 | private _isCanvasDirectory(): boolean { 70 | return basename(this.directoryFilePath).startsWith("_"); 71 | } 72 | 73 | public async read(canvasJson: any): Promise { 74 | if (this.directory.parentDirectory.isManifest) { 75 | this.directory.isCanvas = true; 76 | } else { 77 | // there's no parent manifest directory, so this must be a manifest directory 78 | this.directory.isManifest = true; 79 | } 80 | 81 | this.canvasJson = canvasJson; 82 | await this._getInfo(); 83 | this._applyInfo(); 84 | 85 | // if the directoryPath starts with an underscore 86 | if (this._isCanvasDirectory()) { 87 | // first, determine if there are any custom annotations (files ending in .yml that aren't info.yml) 88 | // if there are, loop through them creating the custom annotations. 89 | // if none of them has a motivation of 'painting', loop through all paintable file types adding them to the canvas. 90 | 91 | const customAnnotationFiles: string[] = await glob( 92 | this.directoryFilePath + "/*.yml", 93 | { 94 | ignore: ["**/info.yml"], 95 | } 96 | ); 97 | 98 | // sort files 99 | customAnnotationFiles.sort((a, b) => { 100 | return compare(a, b); 101 | }); 102 | 103 | await Promise.all( 104 | customAnnotationFiles.map(async (file: string) => { 105 | let directoryName: string = dirname(file); 106 | directoryName = directoryName.substr(directoryName.lastIndexOf("/")); 107 | const name: string = basename(file, extname(file)); 108 | const annotationJson: any = cloneJson(annotationBoilerplate); 109 | const yml: any = await readYml(file); 110 | 111 | annotationJson.id = urljoin( 112 | canvasJson.id, 113 | "annotation", 114 | canvasJson.items[0].items.length 115 | ); 116 | 117 | let motivation: string | undefined = yml.motivation; 118 | 119 | if (!motivation) { 120 | // assume painting 121 | motivation = normaliseType(AnnotationMotivation.PAINTING); 122 | warn( 123 | `motivation property missing in ${file} guessed ${motivation}` 124 | ); 125 | } 126 | 127 | motivation = normaliseType(motivation); 128 | 129 | annotationJson.motivation = motivation; 130 | annotationJson.target = canvasJson.id; 131 | 132 | let id: string; 133 | 134 | // if the motivation is painting, or isn't recognised, set the id to the path of the yml value 135 | if ( 136 | (motivation.toLowerCase() === 137 | normaliseType(AnnotationMotivation.PAINTING) || 138 | !this._config.annotation.motivations[motivation]) && 139 | yml.value && 140 | extname(yml.value) 141 | ) { 142 | if (isURL(yml.value)) { 143 | id = yml.value; 144 | } else { 145 | id = urljoin(this.url.href, directoryName, yml.value); 146 | } 147 | 148 | // if the painting annotation has a target. 149 | if (yml.xywh) { 150 | id += "#xywh=" + yml.xywh; 151 | } 152 | } else { 153 | id = urljoin(this.url.href, "index.json", "annotations", name); 154 | } 155 | 156 | annotationJson.body.id = id; 157 | 158 | if (yml.type) { 159 | annotationJson.body.type = yml.type; 160 | } else if (yml.value && extname(yml.value)) { 161 | // guess the type from the extension 162 | const type: string | null = getTypeByExtension( 163 | motivation, 164 | extname(yml.value) 165 | ); 166 | 167 | if (type) { 168 | annotationJson.body.type = type; 169 | warn(`type property missing in ${file}, guessed ${type}`); 170 | } 171 | } else if (yml.format) { 172 | // guess the type from the format 173 | const type: string | null = getTypeByFormat(motivation, yml.format); 174 | 175 | if (type) { 176 | annotationJson.body.type = type; 177 | warn(`type property missing in ${file}, guessed ${type}`); 178 | } 179 | } 180 | 181 | if (!annotationJson.body.type) { 182 | delete annotationJson.body.type; 183 | warn(`unable to determine type of ${file}`); 184 | } 185 | 186 | if (yml.format) { 187 | annotationJson.body.format = yml.format; 188 | } else if (yml.value && extname(yml.value) && yml.type) { 189 | // guess the format from the extension and type 190 | const format: string | null = getFormatByExtensionAndType( 191 | motivation, 192 | extname(yml.value), 193 | yml.type 194 | ); 195 | 196 | if (format) { 197 | annotationJson.body.format = format; 198 | warn(`format property missing in ${file}, guessed ${format}`); 199 | } 200 | } else if (yml.value && extname(yml.value)) { 201 | // guess the format from the extension 202 | const format: string | null = getFormatByExtension( 203 | motivation, 204 | extname(yml.value) 205 | ); 206 | 207 | if (format) { 208 | annotationJson.body.format = format; 209 | warn(`format property missing in ${file}, guessed ${format}`); 210 | } 211 | } else if (yml.type) { 212 | // can only guess the format from the type if there is one typeformat for this motivation. 213 | const format: string | null = getFormatByType(motivation, yml.type); 214 | 215 | if (format) { 216 | annotationJson.body.format = format; 217 | warn(`format property missing in ${file}, guessed ${format}`); 218 | } 219 | } 220 | 221 | if (!annotationJson.body.format) { 222 | delete annotationJson.body.format; 223 | warn(`unable to determine format of ${file}`); 224 | } 225 | 226 | if (yml.label) { 227 | annotationJson.body.label = getLabel(yml.label); 228 | canvasJson.label = getLabel(yml.label); 229 | } else { 230 | annotationJson.body.label = getLabel(this.infoYml.label); 231 | } 232 | 233 | // if the annotation is an image and the id points to an info.json 234 | // add an image service pointing to the info.json 235 | if ( 236 | annotationJson.body.type && 237 | annotationJson.body.type.toLowerCase() === 238 | ExternalResourceType.IMAGE && 239 | extname(annotationJson.body.id) === ".json" 240 | ) { 241 | const service: any = cloneJson(imageServiceBoilerplate); 242 | service[0].id = annotationJson.body.id.substr( 243 | 0, 244 | annotationJson.body.id.lastIndexOf("/") 245 | ); 246 | annotationJson.body.service = service; 247 | } 248 | 249 | // if there's a value, and we're using a recognised motivation (except painting) 250 | if ( 251 | yml.value && 252 | this._config.annotation.motivations[motivation] && 253 | motivation !== normaliseType(AnnotationMotivation.PAINTING) 254 | ) { 255 | annotationJson.body.value = yml.value; 256 | } 257 | 258 | if (yml.value && !isURL(yml.value) && annotationJson.body.type) { 259 | // get the path to the annotated file 260 | const dirName: string = dirname(file); 261 | let path: string = join(dirName, yml.value); 262 | path = normaliseFilePath(path); 263 | await getFileDimensions( 264 | annotationJson.body.type, 265 | path, 266 | canvasJson, 267 | annotationJson 268 | ); 269 | } 270 | 271 | canvasJson.items[0].items.push(annotationJson); 272 | }) 273 | ); 274 | 275 | // for each jpg/pdf/mp4/obj in the canvas directory 276 | // add a painting annotation 277 | const paintableFiles: string[] = await glob( 278 | this.directoryFilePath + "/*.*", 279 | { 280 | ignore: [ 281 | "**/thumb.*", // ignore thumbs 282 | "**/info.yml*", // ignore info.yml 283 | ], 284 | } 285 | ); 286 | 287 | // sort files 288 | paintableFiles.sort((a, b) => { 289 | return compare(a, b); 290 | }); 291 | 292 | await this._annotateFiles(canvasJson, paintableFiles); 293 | } else { 294 | // a file was passed (not a directory starting with an underscore) 295 | // therefore, just annotate that file onto the canvas. 296 | await this._annotateFiles(canvasJson, [this.filePath]); 297 | } 298 | 299 | if (!canvasJson.items[0].items.length) { 300 | warn( 301 | `Could not find any files to annotate onto ${this.directoryFilePath}` 302 | ); 303 | } 304 | 305 | // if there's no thumb.[jpg, gif, png] 306 | // generate one from the first painted image 307 | await getThumbnail(this.canvasJson, this.directory, this.directoryFilePath); 308 | } 309 | 310 | private async _annotateFiles( 311 | canvasJson: any, 312 | files: string[] 313 | ): Promise { 314 | await Promise.all( 315 | files.map(async (file: string) => { 316 | file = normaliseFilePath(file); 317 | const extName: string = extname(file); 318 | 319 | // if this._config.annotation has a matching extension 320 | let defaultPaintingExtension: any = this._config.annotation.motivations 321 | .painting[extName]; 322 | 323 | let directoryName: string = ""; 324 | 325 | // if the canvas is being generated from a canvas directory (starts with an _) 326 | if (this._isCanvasDirectory()) { 327 | directoryName = dirname(file); 328 | directoryName = directoryName.substr(directoryName.lastIndexOf("/")); 329 | } 330 | 331 | const fileName: string = basename(file); 332 | const id: string = urljoin(this.url.href, directoryName, fileName); 333 | 334 | if (defaultPaintingExtension) { 335 | defaultPaintingExtension = defaultPaintingExtension[0]; 336 | const annotationJson: any = cloneJson(annotationBoilerplate); 337 | annotationJson.id = urljoin( 338 | canvasJson.id, 339 | "annotation", 340 | canvasJson.items[0].items.length 341 | ); 342 | annotationJson.motivation = normaliseType( 343 | AnnotationMotivation.PAINTING 344 | ); 345 | annotationJson.target = canvasJson.id; 346 | annotationJson.body.id = id; 347 | annotationJson.body.type = defaultPaintingExtension.type; 348 | annotationJson.body.format = defaultPaintingExtension.format; 349 | annotationJson.body.label = getLabel(this.infoYml.label); 350 | canvasJson.items[0].items.push(annotationJson); 351 | await getFileDimensions( 352 | defaultPaintingExtension.type, 353 | file, 354 | canvasJson, 355 | annotationJson 356 | ); 357 | 358 | if ( 359 | defaultPaintingExtension.type.toLowerCase() === 360 | ExternalResourceType.IMAGE 361 | ) { 362 | await generateImageTiles( 363 | file, 364 | this.url.href, 365 | directoryName, 366 | this.directoryFilePath, 367 | annotationJson 368 | ); 369 | } 370 | } 371 | }) 372 | ); 373 | } 374 | 375 | private async _getInfo(): Promise { 376 | this.infoYml = {}; 377 | 378 | // if there's an info.yml 379 | const ymlPath: string = join(this.directoryFilePath, "info.yml"); 380 | 381 | const exists: boolean = await fileExists(ymlPath); 382 | 383 | if (exists) { 384 | this.infoYml = await readYml(ymlPath); 385 | log(`got metadata for: ${this.directoryFilePath}`); 386 | } else { 387 | log(`no metadata found for: ${this.directoryFilePath}`); 388 | } 389 | 390 | if (!this.infoYml.label) { 391 | // default to the directory name 392 | this.infoYml.label = basename(this.directoryFilePath); 393 | } 394 | } 395 | 396 | private _applyInfo(): void { 397 | this.canvasJson.label = getLabel(this.infoYml.label); // defaults to directory name 398 | 399 | if (this.infoYml.width) { 400 | this.canvasJson.width = this.infoYml.width; 401 | } 402 | 403 | if (this.infoYml.height) { 404 | this.canvasJson.height = this.infoYml.height; 405 | } 406 | 407 | if (this.infoYml.duration) { 408 | this.canvasJson.duration = this.infoYml.duration; 409 | } 410 | 411 | if (this.infoYml.metadata) { 412 | this.canvasJson.metadata = formatMetadata(this.infoYml.metadata); 413 | } 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /Directory.ts: -------------------------------------------------------------------------------- 1 | import { Canvas } from "./Canvas"; 2 | import { join, basename } from "path"; 3 | import { promise as glob } from "glob-promise"; 4 | import { URL } from "url"; 5 | import { 6 | cloneJson, 7 | compare, 8 | fileExists, 9 | formatMetadata, 10 | getLabel, 11 | getThumbnail, 12 | hasManifestsYml, 13 | log, 14 | readYml, 15 | warn, 16 | writeJson, 17 | } from "./Utils"; 18 | // import urljoin from "url-join"; 19 | const urljoin = require("url-join"); 20 | // boilerplate json 21 | import canvasBoilerplate from "./boilerplate/canvas.json"; 22 | import collectionBoilerplate from "./boilerplate/collection.json"; 23 | import collectionItemBoilerplate from "./boilerplate/collectionitem.json"; 24 | import manifestBoilerplate from "./boilerplate/manifest.json"; 25 | import manifestItemBoilerplate from "./boilerplate/manifestitem.json"; 26 | import thumbnailBoilerplate from "./boilerplate/thumbnail.json"; 27 | 28 | export class Directory { 29 | public directories: Directory[] = []; 30 | public directoryFilePath: string; 31 | public generateThumbs: boolean; 32 | public indexJson: any; 33 | public infoYml: any; 34 | public isCanvas: boolean = false; 35 | public isCollection: boolean = false; 36 | public isManifest: boolean = false; 37 | public items: Canvas[] = []; 38 | public name: string; 39 | public parentDirectory: Directory | undefined; 40 | public url: URL; 41 | public virtualName: string | undefined; // used when root directories are dat/ipfs keys 42 | 43 | constructor( 44 | directoryFilePath: string, 45 | url: string, 46 | virtualName?: string, 47 | parentDirectory?: Directory 48 | ) { 49 | this.directoryFilePath = directoryFilePath; 50 | this.url = new URL(url); 51 | this.parentDirectory = parentDirectory; 52 | this.virtualName = virtualName; 53 | } 54 | 55 | public async read(): Promise { 56 | // canvases are directories starting with an underscore 57 | const canvasesPattern: string = this.directoryFilePath + "/_*"; 58 | 59 | const canvases: string[] = await glob(canvasesPattern, { 60 | ignore: ["**/*.yml", "**/thumb.*", "**/!*"], 61 | }); 62 | 63 | // sort canvases 64 | canvases.sort((a, b) => { 65 | return compare(a, b); 66 | }); 67 | 68 | await Promise.all( 69 | canvases.map(async (canvas: string) => { 70 | log(`creating canvas for: ${canvas}`); 71 | this.items.push(new Canvas(canvas, this)); 72 | }) 73 | ); 74 | 75 | // directories not starting with an underscore 76 | // these can be child manifests or child collections 77 | const directoriesPattern: string = this.directoryFilePath + "/*"; 78 | 79 | const directories: string[] = await glob(directoriesPattern, { 80 | ignore: [ 81 | "**/*.{crt,drc,epub,glb,gltf,gz,stl,jpg,jpeg,json,md,mp3,mp4,nii,obj,opf,pdf,ply,png,tif,tiff,toml,usdz,vtt,yml}", // ignore files (must include file extensions explicitly, otherwise directories with a . are matched) 82 | "**/_*", // ignore canvas folders 83 | "**/+*", // ignore generated folders 84 | "**/!*", // ignore folders starting with a ! 85 | ], 86 | }); 87 | 88 | // sort 89 | directories.sort((a, b) => { 90 | return compare(a, b); 91 | }); 92 | 93 | if (canvases.length) { 94 | this.isManifest = true; 95 | } else if ( 96 | directories.length > 0 || 97 | (await hasManifestsYml(this.directoryFilePath)) 98 | ) { 99 | this.isCollection = true; 100 | } 101 | 102 | await Promise.all( 103 | directories.map(async (directory: string) => { 104 | log(`creating directory for: ${directory}`); 105 | const name: string = basename(directory); 106 | const url: string = urljoin(this.url.href, name); 107 | const newDirectory: Directory = new Directory( 108 | directory, 109 | url, 110 | undefined, 111 | this 112 | ); 113 | await newDirectory.read(); 114 | this.directories.push(newDirectory); 115 | }) 116 | ); 117 | 118 | // if there are no canvas, manifest, or collection directories to read, 119 | // but there are paintable files in the current directory, 120 | // create a canvas for each. 121 | if (!this.directories.length && !canvases.length) { 122 | const paintableFiles: string[] = await glob( 123 | this.directoryFilePath + "/*.*", 124 | { 125 | ignore: ["**/*.yml", "**/thumb.*", "**/index.json"], 126 | } 127 | ); 128 | 129 | // sort files 130 | paintableFiles.sort((a, b) => { 131 | return compare(a, b); 132 | }); 133 | 134 | paintableFiles.forEach((file: string) => { 135 | log(`creating canvas for: ${file}`); 136 | this.items.push(new Canvas(file, this)); 137 | }); 138 | } 139 | 140 | await this._getInfo(); 141 | await this._createIndexJson(); 142 | 143 | if (this.isCollection) { 144 | log(`created collection: ${this.directoryFilePath}`); 145 | // if there are canvases, warn that they are being ignored 146 | if (this.items.length) { 147 | warn( 148 | `${this.items.length} unused canvas directories (starting with an underscore) found in the ${this.directoryFilePath} collection. Remove directories not starting with an underscore to convert into a manifest.` 149 | ); 150 | } 151 | } else { 152 | log(`created manifest: ${this.directoryFilePath}`); 153 | // if there aren't any canvases, warn that there should be 154 | if (!this.items.length) { 155 | warn( 156 | `${this.directoryFilePath} is a manifest, but no canvases (directories starting with an underscore) were found. Therefore it will not have any content.` 157 | ); 158 | } 159 | } 160 | } 161 | 162 | private async _getInfo(): Promise { 163 | this.infoYml = {}; 164 | 165 | // if there's an info.yml 166 | const ymlPath: string = join(this.directoryFilePath, "info.yml"); 167 | 168 | const exists: boolean = await fileExists(ymlPath); 169 | 170 | if (exists) { 171 | this.infoYml = await readYml(ymlPath); 172 | log(`got metadata for: ${this.directoryFilePath}`); 173 | } else { 174 | log(`no metadata found for: ${this.directoryFilePath}`); 175 | } 176 | 177 | if (!this.infoYml.label) { 178 | // default to the directory name 179 | this.infoYml.label = basename(this.directoryFilePath); 180 | } 181 | } 182 | 183 | private async _createIndexJson(): Promise { 184 | if (this.isCollection) { 185 | this.indexJson = cloneJson(collectionBoilerplate); 186 | 187 | // for each child directory, add a collectionitem or manifestitem json boilerplate to items. 188 | 189 | await Promise.all( 190 | this.directories.map(async (directory: Directory) => { 191 | let itemJson: any; 192 | 193 | if (directory.isCollection) { 194 | itemJson = cloneJson(collectionItemBoilerplate); 195 | } else { 196 | itemJson = cloneJson(manifestItemBoilerplate); 197 | } 198 | 199 | itemJson.id = urljoin(directory.url.href, "index.json"); 200 | itemJson.label = getLabel(directory.infoYml.label); 201 | 202 | await getThumbnail(itemJson, directory); 203 | 204 | this.indexJson.items.push(itemJson); 205 | }) 206 | ); 207 | 208 | // check for manifests.yml. if it exists, parse and add to items 209 | const hasYml: boolean = await hasManifestsYml(this.directoryFilePath); 210 | 211 | if (hasYml) { 212 | const manifestsPath: string = join( 213 | this.directoryFilePath, 214 | "manifests.yml" 215 | ); 216 | const manifestsYml: any = await readYml(manifestsPath); 217 | 218 | manifestsYml.manifests.forEach((manifest: any) => { 219 | const itemJson: any = cloneJson(manifestItemBoilerplate); 220 | itemJson.id = manifest.id; 221 | 222 | if (manifest.label) { 223 | itemJson.label = getLabel(manifest.label); 224 | } else { 225 | // no label supplied, use the last fragment of the url 226 | const url: URL = new URL(itemJson.id); 227 | const pathname: string[] = url.pathname.split("/"); 228 | 229 | if (pathname.length > 1) { 230 | itemJson.label = getLabel(pathname[pathname.length - 2]); 231 | } 232 | } 233 | 234 | if (manifest.thumbnail) { 235 | if (typeof manifest.thumbnail === "string") { 236 | const thumbnail: any[] = cloneJson(thumbnailBoilerplate); 237 | thumbnail[0].id = manifest.thumbnail; 238 | itemJson.thumbnail = thumbnail; 239 | } else { 240 | itemJson.thumbnail = manifest.thumbnail; 241 | } 242 | } 243 | 244 | this.indexJson.items.push(itemJson); 245 | }); 246 | 247 | log(`parsed manifests.yml for ${this.directoryFilePath}`); 248 | } else { 249 | log(`no manifests.yml found for: ${this.directoryFilePath}`); 250 | } 251 | 252 | // sort items 253 | this.indexJson.items.sort((a, b) => { 254 | return compare( 255 | a.label["@none"][0].toLowerCase(), 256 | b.label["@none"][0].toLowerCase() 257 | ); 258 | }); 259 | } else { 260 | this.indexJson = cloneJson(manifestBoilerplate); 261 | 262 | // for each canvas, add canvas json 263 | 264 | let index: number = 0; 265 | 266 | for (const canvas of this.items) { 267 | const canvasJson: any = cloneJson(canvasBoilerplate); 268 | canvasJson.id = urljoin(this.url.href, "index.json/canvas", index); 269 | canvasJson.items[0].id = urljoin( 270 | this.url.href, 271 | "index.json/canvas", 272 | index, 273 | "annotationpage/0" 274 | ); 275 | 276 | await canvas.read(canvasJson); 277 | 278 | // add canvas to items 279 | this.indexJson.items.push(canvasJson); 280 | 281 | index++; 282 | } 283 | 284 | this.indexJson.items.sort((a, b) => { 285 | return compare(a.id, b.id); 286 | }); 287 | } 288 | 289 | this.indexJson.id = urljoin(this.url.href, "index.json"); 290 | 291 | this._applyInfo(); 292 | 293 | await getThumbnail(this.indexJson, this); 294 | 295 | // write index.json 296 | const path: string = join(this.directoryFilePath, "index.json"); 297 | const json: string = JSON.stringify(this.indexJson, null, " "); 298 | 299 | log(`creating index.json for: ${this.directoryFilePath}`); 300 | 301 | await writeJson(path, json); 302 | } 303 | 304 | private _applyInfo(): void { 305 | this.indexJson.label = getLabel(this.infoYml.label); // defaults to directory name 306 | 307 | if (this.infoYml.metadata) { 308 | this.indexJson.metadata = formatMetadata(this.infoYml.metadata); 309 | } 310 | 311 | // add manifest-specific properties 312 | if (!this.isCollection) { 313 | if (this.infoYml.attribution) { 314 | this.indexJson.attribution = this.infoYml.attribution; 315 | } 316 | 317 | if (this.infoYml.description) { 318 | this.indexJson.description = this.infoYml.description; 319 | } 320 | 321 | if (this.infoYml.behavior) { 322 | this.indexJson.behavior = []; 323 | 324 | if (Array.isArray(this.infoYml.behavior)) { 325 | this.infoYml.behavior.forEach((behavior) => { 326 | this.indexJson.behavior.push(behavior); 327 | }); 328 | } else { 329 | this.indexJson.behavior.push(this.infoYml.behavior); 330 | } 331 | } 332 | } 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /IConfigJSON.ts: -------------------------------------------------------------------------------- 1 | import { TypeFormat } from "./TypeFormat"; 2 | 3 | export interface IConfigJSON { 4 | thumbnails: thumbnails; 5 | annotation: annotation; 6 | } 7 | 8 | export interface settings {} 9 | 10 | export interface thumbnails { 11 | width: number; 12 | height: number; 13 | } 14 | 15 | export interface painting { 16 | [key: string]: TypeFormat[]; 17 | } 18 | 19 | export interface commenting { 20 | [key: string]: TypeFormat[]; 21 | } 22 | 23 | export interface motivations { 24 | painting: painting; 25 | commenting: commenting; 26 | } 27 | 28 | export interface annotation { 29 | motivations: motivations; 30 | } 31 | 32 | export interface RootObject { 33 | settings: settings; 34 | thumbnails: thumbnails; 35 | annotation: annotation; 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # biiif (build iiif) 👷✨📃 2 | 3 | [![Node version](https://img.shields.io/node/v/biiif.svg?style=flat)](http://nodejs.org/download/) 4 | 5 | 6 | 7 | ```bash 8 | npm i biiif --save 9 | ``` 10 | 11 | ```bash 12 | const { build } = require('biiif'); 13 | build('myfolder', 'http://example.com/myfolder'); 14 | ``` 15 | 16 | Organise your files according to a simple [naming convention](https://github.com/edsilv/biiif#examples) to generate [IIIF](http://iiif.io) content/data using 100% node.js! [IPFS](https://github.com/ipfs) compatible. 17 | 18 | Use [biiif-cli](https://github.com/edsilv/biiif-cli) to run from a terminal. 19 | 20 | Note: This uses the [IIIF Presentation API v3](http://prezi3.iiif.io/api/presentation/3.0/), and is compatible with the [Universal Viewer](http://universalviewer.io) v3. 21 | 22 | Github template repo for hosting IIIF on Netlify and Vercel: https://github.com/iiif-commons/biiif-template 23 | 24 | Building static sites with biiif workshop: https://github.com/edsilv/biiif-workshop 25 | 26 | ## Parameters 27 | 28 | | Parameter | Type | Description | 29 | | :------------ | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------- | 30 | | `folder` | string | The source folder of your IIIF collection/manifest on disk | 31 | | `url` | string | The Url to use as the root for all generated manifest, asset identifiers | 32 | | `virtualName` | string | Overrides the source folder name when generating identifiers e.g. a dat archive id you need to appear in Urls instead of the source folder name | 33 | 34 | ## Conventions 35 | 36 | A collection is a folder with sub-folders whose names _do not_ start with an underscore. 37 | 38 | A manifest is a folder with sub-folders whose names _do_ start with an underscore. 39 | 40 | A collection's sub-folders (no underscore) are treated as further nested collections. 41 | 42 | A manifest's sub-folders (with underscore) are treated as canvases to add to the manifest. 43 | 44 | Files within 'canvas folders' (.jpg, .pdf, .mp4, .obj) are annotated onto the canvas with a `painting` motivation. 45 | 46 | ## Annotations 47 | 48 | IIIF Presentation 3.0 uses the [Web Annotation Data Model](https://www.w3.org/TR/annotation-model/) to annotate canvases. 49 | 50 | By default, biiif will annotate any files it finds in a canvas directory (except `info.yml` and `thumb.jpg`) onto the canvas with a `painting` motivation. 51 | 52 | This is handy as a quick way to generate simple manifests. However, what if you want to annotate some text onto a canvas with a `commenting` motivation? 53 | 54 | Or what happens when you have obj or gltf files that require image textures to be located in the same directory? You don't want these files to be annotated onto the canvas too! 55 | 56 | This is where custom annotations come in. Just create a file `my-annotation.yml` in the canvas directory and set the desired properties in that. 57 | 58 | For example, here is `my-comment.yml`: 59 | 60 | ```yml 61 | motivation: commenting 62 | value: This is my comment on the image 63 | ``` 64 | 65 | Here we've excluded the `type` (`TextualBody` is assumed), and `format` (`text/plain` is assumed). 66 | 67 | What about the gltf example? Here's how `my-3d-object.yml` could look: 68 | 69 | ```yml 70 | value: assets/myobject.gltf 71 | ``` 72 | 73 | Here we've excluded the `motivation` (`painting` is assumed), `type` (`Model` is assumed), and `format` (`model/gltf+json` is assumed). 74 | 75 | biiif knows that because it's a gltf file, it's likely to have all of the above values. You just need to include a `value` property pointing to where you've put the gltf file itself. In this case, an `assets` folder within the canvas directory. The associated image textures can live in the `assets` folder too, they won't get annotated unless you specifically ask for them to be. 76 | 77 | ## Image Tile Services 78 | 79 | biiif will automatically generate IIIF image tiles for any image it finds and put them in a `+tiles` directory, along with an associated `info.json`. The `+` is prepended to any directories generated by biiif and means it ignores them when generating manifests. 80 | The image service is added to the generated annotation for each image in your IIIF manifest. 81 | 82 | ## Metadata 83 | 84 | Metadata is not mandatory, but can be included as an `info.yml` file within a collection, manifest, or canvas folder. e.g. 85 | 86 | ```yml 87 | label: The Lord of the Rings 88 | description: The Lord of the Rings Trilogy 89 | attribution: J. R. R. Tolkien 90 | metadata: 91 | License: Copyright Tolkien Estate 92 | Author: J. R. R. Tolkien 93 | Published Date: 29 July 1954 94 | ``` 95 | 96 | Here's an example of an `info.yml` supplying descriptive + rights properties and metadata for a gold-broach image manifest: 97 | 98 | https://github.com/nomadproject/objects/blob/gh-pages/collection/gold-broach/info.yml 99 | 100 | This manifest contains a single canvas folder `_gold-broach` with an image to be painted onto the canvas. If there were many canvases in this manifest it might make sense to add an `info.yml` to each subfolder with extra image-specific metadata. 101 | 102 | Within the `info.yml` you can set the `label`, `description`, and `attribution` [descriptive and rights properties](https://iiif.io/api/presentation/3.0/#appendices) at the top-level. IIIF Presentation 3 (in beta) has renamed `description` to `summary`, and `attribution` to `requiredStatement` but these will still work in IIIF viewers. 103 | 104 | Under these you can add a `metadata` section that is essentially a list of key value pairs containing any info you like (there is deliberately no specification for this as the IIIF spec writers feel it falls outside of their remit). 105 | 106 | ## Thumbnails 107 | 108 | To add a thumbnail to your collection, manifest, or canvas simply include a file named `thumb.jpg` (any image file extension will work) in the directory. 109 | 110 | If no thumb image is found in a canvas directory, biiif checks to see if an image is being annotated onto the canvas with a painting motivation. If so, a thumb is generated (100 x 100px) from that. 111 | 112 | ## Linked Manifests 113 | 114 | Often it's necessary to include IIIF manifests in your collection from elsewhere. To do this, include a `manifests.yml` file in your collection folder e.g. 115 | 116 | ```yml 117 | manifests: 118 | - id: http://test.com/collection/linkedmanifest1/index.json 119 | label: Linked Manifest 1 120 | thumbnail: http://test.com/collection/linkedmanifest1/thumb.jpg 121 | - id: http://test.com/collection/linkedmanifest2/index.json 122 | label: Linked Manifest 2 123 | - id: http://test.com/collection/linkedmanifest3/index.json 124 | ``` 125 | 126 | If you leave the `label` property blank, it will default to the name of the last folder in the `id` URL. 127 | 128 | Including a `manifests.yml` file in a folder without any sub-folders forces it to behave like a collection. 129 | 130 | ## Examples 131 | 132 | 133 | 134 | A repo of test manifests: https://github.com/edsilv/biiif-test-manifests 135 | 136 | Collection for the [Nomad Project](https://nomad-project.co.uk): https://github.com/nomadproject/objects 137 | 138 | IIIF 3D manifests: https://github.com/edsilv/iiif-3d-manifests 139 | 140 | ... 141 | 142 | Here is an example of how to organise your files/folders for biiif. 143 | 144 | This example only has a single root collection, but biiif will happily build collections to any nested depth. 145 | 146 | biiif will accept a manifest folder too, generating a single manifest `index.json`. 147 | 148 | ```yml 149 | lord-of-the-rings // collection 150 | ├── info.yml // collection metadata 151 | ├── thumb.jpg // collection thumbnail 152 | ├── 0-the-fellowship-of-the-ring // manifest 153 | | ├── _page-1 // canvas 154 | | | ├── page-1.jpg // content annotation 155 | | | └── info.yml // canvas metadata 156 | | ├── _page-2 // canvas 157 | | | ├── page-2.jpg // content annotation 158 | | | └── info.yml // canvas metadata 159 | | ├── _page-n // canvas 160 | | | ├── page-n.jpg // content annotation 161 | | | └── info.yml // canvas metadata 162 | | ├── info.yml // manifest metadata 163 | | └── thumb.jpg // manifest thumbnail 164 | ├── 1-the-two-towers // manifest 165 | | ├── _page-1 // canvas 166 | | ├── _page-2 // canvas 167 | | ├── _page-n // canvas 168 | | ├── info.yml // manifest metadata 169 | | └── thumb.jpg // manifest thumbnail 170 | └── 2-the-return-of-the-king // manifest 171 | ├── _page-1 // canvas 172 | ├── _page-2 // canvas 173 | ├── _page-n // canvas 174 | ├── info.yml // manifest metadata 175 | └── thumb.jpg // manifest thumbnail 176 | ``` 177 | 178 | ## Tips 179 | 180 | If you need to include a folder in your project but don't want biiif to treat it as a manifest, add a `!` to the start of its name, e.g. `!ignorethisfolder`. 181 | 182 | Watch out for ":" in metadata descriptions, these will throw an error when parsing the YML. 183 | -------------------------------------------------------------------------------- /TypeFormat.ts: -------------------------------------------------------------------------------- 1 | export interface TypeFormat { 2 | type: string; 3 | format: string; 4 | } 5 | -------------------------------------------------------------------------------- /Utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AnnotationMotivation, 3 | ExternalResourceType, 4 | } from "@iiif/vocabulary/dist-commonjs/"; 5 | import { Directory } from "./Directory"; 6 | import { dirname, extname } from "path"; 7 | import { IConfigJSON } from "./IConfigJSON"; 8 | import { join, basename } from "path"; 9 | import { promise as glob } from "glob-promise"; 10 | import { TypeFormat } from "./TypeFormat"; 11 | import chalk from "chalk"; 12 | import config from "./config.json"; 13 | import ffprobe from "ffprobe"; 14 | import ffprobeStatic from "ffprobe-static"; 15 | import fs from "fs"; 16 | import isurl from "is-url"; 17 | import jsonfile from "jsonfile"; 18 | import labelBoilerplate from "./boilerplate/label.json"; 19 | import thumbnailBoilerplate from "./boilerplate/thumbnail.json"; 20 | import urljoin from "url-join"; 21 | import yaml from "js-yaml"; 22 | const sharp = require("sharp"); 23 | 24 | const _config: IConfigJSON = config; 25 | 26 | export const compare = (a: string, b: string): number => { 27 | const collator: Intl.Collator = new Intl.Collator(undefined, { 28 | numeric: true, 29 | sensitivity: "base", 30 | }); 31 | return collator.compare(a, b); 32 | }; 33 | 34 | export const normaliseType = (type: string): string => { 35 | type = type.toLowerCase(); 36 | 37 | if (type.indexOf(":") !== -1) { 38 | const split: string[] = type.split(":"); 39 | return split[1]; 40 | } 41 | 42 | return type; 43 | }; 44 | 45 | export const getTypeByExtension = ( 46 | motivation: string, 47 | extension: string 48 | ): string | null => { 49 | motivation = normaliseType(motivation); 50 | 51 | const m: any = _config.annotation.motivations[motivation]; 52 | 53 | if (m) { 54 | if (m[extension] && m[extension].length) { 55 | return m[extension][0].type; 56 | } 57 | } 58 | 59 | return null; 60 | }; 61 | 62 | export const getFormatByExtension = ( 63 | motivation: string, 64 | extension: string 65 | ): string | null => { 66 | motivation = normaliseType(motivation); 67 | 68 | const m: any = _config.annotation.motivations[motivation]; 69 | 70 | if (m) { 71 | if (m[extension] && m[extension].length) { 72 | return m[extension][0].format; 73 | } 74 | } 75 | 76 | return null; 77 | }; 78 | 79 | export const getFormatByExtensionAndType = ( 80 | motivation: string, 81 | extension: string, 82 | type: string 83 | ): string | null => { 84 | motivation = normaliseType(motivation); 85 | 86 | const m: any = _config.annotation.motivations[motivation]; 87 | 88 | if (m) { 89 | if (m[extension] && m[extension].length) { 90 | const typeformats: TypeFormat[] = m[extension]; 91 | for (let i = 0; i < typeformats.length; i++) { 92 | const typeformat: TypeFormat = typeformats[i]; 93 | if (typeformat.type === type) { 94 | return typeformat.format; 95 | } 96 | } 97 | } 98 | } 99 | 100 | return null; 101 | }; 102 | 103 | export const getTypeByFormat = ( 104 | motivation: string, 105 | format: string 106 | ): string | null => { 107 | motivation = normaliseType(motivation); 108 | 109 | const m: any = _config.annotation.motivations[motivation]; 110 | 111 | if (m) { 112 | for (const extension in m) { 113 | const typeformats: TypeFormat[] = m[extension]; 114 | for (let i = 0; i < typeformats.length; i++) { 115 | const typeformat: TypeFormat = typeformats[i]; 116 | if (typeformat.format === format) { 117 | return typeformat.type; 118 | } 119 | } 120 | } 121 | } 122 | 123 | return null; 124 | }; 125 | 126 | export const getFormatByType = ( 127 | motivation: string, 128 | type: string 129 | ): string | null => { 130 | motivation = normaliseType(motivation); 131 | 132 | const m: any = _config.annotation.motivations[motivation]; 133 | 134 | // only able to categorically say there's a matching format 135 | // if there's a single extension with a single type 136 | 137 | if (m) { 138 | if (Object.keys(m).length === 1) { 139 | const typeformats: TypeFormat[] = m[Object.keys(m)[0]]; 140 | 141 | if (typeformats.length === 1) { 142 | return typeformats[0].format; 143 | } 144 | } 145 | } 146 | 147 | return null; 148 | }; 149 | 150 | export const timeout = (ms: number): Promise => { 151 | return new Promise((resolve) => setTimeout(resolve, ms)); 152 | }; 153 | 154 | export const cloneJson = (json: any): any => { 155 | return JSON.parse(JSON.stringify(json)); 156 | }; 157 | 158 | export const formatMetadata = (metadata: any): any => { 159 | const formattedMetadata: any[] = []; 160 | 161 | for (let key in metadata) { 162 | if (metadata.hasOwnProperty(key)) { 163 | const value: string = metadata[key]; 164 | 165 | const item: any = {}; 166 | 167 | item.label = getLabel(key); 168 | item.value = getLabel(value); 169 | 170 | formattedMetadata.push(item); 171 | } 172 | } 173 | 174 | return formattedMetadata; 175 | }; 176 | 177 | // If filePath is: 178 | // C://Users/edsilv/github/edsilv/biiif-workshop/collection/_abyssinian/thumb.jpeg 179 | // and 'collection' has been replaced by the top-level virtual name 'virtualname' 180 | // it should return: 181 | // C://Users/edsilv/github/edsilv/biiif-workshop/virtualname/_abyssinian/thumb.jpeg 182 | // virtual names are needed when using dat or ipfs ids as the root directory. 183 | export const getVirtualFilePath = ( 184 | filePath: string, 185 | directory: Directory 186 | ): string => { 187 | // walk up directory parents building the realPath and virtualPath array as we go. 188 | // at the top level directory, use the real name for realPath and the virtual name for virtualPath. 189 | // reverse the arrays and join with a '/'. 190 | // replace the realPath section of filePath with virtualPath. 191 | 192 | let realPath: string[] = [basename(filePath)]; 193 | let virtualPath: string[] = [basename(filePath)]; 194 | 195 | while (directory) { 196 | const realName: string = basename(directory.directoryFilePath); 197 | const virtualName: string = directory.virtualName || realName; 198 | realPath.push(realName); 199 | virtualPath.push(virtualName); 200 | directory = directory.parentDirectory; 201 | } 202 | 203 | realPath = realPath.reverse(); 204 | virtualPath = virtualPath.reverse(); 205 | 206 | const realPathString: string = realPath.join("/"); 207 | const virtualPathString: string = virtualPath.join("/"); 208 | 209 | filePath = normaliseFilePath(filePath); 210 | filePath = filePath.replace(realPathString, virtualPathString); 211 | 212 | return filePath; 213 | }; 214 | 215 | export const isJsonFile = (path: string): boolean => { 216 | return extname(path) === ".json"; 217 | }; 218 | 219 | export const isDirectory = (path: string): boolean => { 220 | return fs.lstatSync(path).isDirectory(); 221 | }; 222 | 223 | export const getThumbnail = async ( 224 | json: any, 225 | directory: Directory, 226 | filePath?: string 227 | ): Promise => { 228 | let fp: string = filePath || directory.directoryFilePath; 229 | fp = normaliseFilePath(fp); 230 | 231 | const thumbnailPattern: string = fp + "/thumb.*"; 232 | const thumbnails: string[] = await glob(thumbnailPattern); 233 | 234 | if (thumbnails.length) { 235 | // there's alrady a thumbnail in the directory, add it to the canvas 236 | log(`found thumbnail for: ${fp}`); 237 | let thumbnail: string = thumbnails[0]; 238 | const thumbnailJson: any = cloneJson(thumbnailBoilerplate); 239 | const virtualFilePath = getVirtualFilePath(thumbnail, directory); 240 | thumbnailJson[0].id = mergePaths(directory.url, virtualFilePath); 241 | json.thumbnail = thumbnailJson; 242 | } else { 243 | // there isn't a thumbnail in the directory, so we'll need to generate it. 244 | // generate thumbnail 245 | if (json.items && json.items.length && json.items[0].items) { 246 | // find an annotation with a painting motivation of type image. 247 | const items: any[] = json.items[0].items; 248 | 249 | for (let i = 0; i < items.length; i++) { 250 | const item: any = items[i]; 251 | const body: any = item.body; 252 | if ( 253 | body && 254 | item.motivation === normaliseType(AnnotationMotivation.PAINTING) 255 | ) { 256 | // is it an image? (without an info.json) 257 | if ( 258 | body.type.toLowerCase() === ExternalResourceType.IMAGE && 259 | !isJsonFile(body.id) 260 | ) { 261 | let imageName: string = body.id.substr(body.id.lastIndexOf("/")); 262 | if (imageName.includes("#")) { 263 | imageName = imageName.substr(0, imageName.lastIndexOf("#")); 264 | } 265 | 266 | const imagePath: string = normaliseFilePath(join(fp, imageName)); 267 | let pathToThumb: string = normaliseFilePath( 268 | join(dirname(imagePath), "thumb.jpg") 269 | ); 270 | 271 | // todo: this currently assumes that the image to generate a thumb from is within the directory, 272 | // but it may be in an assets folder and painted by a custom annotation. 273 | // see canvas-with-dimensions-manifest.js 274 | if (await fileExists(imagePath)) { 275 | //const image: any = await Jimp.read(imagePath); 276 | //const thumb: any = image.clone(); 277 | // write image buffer to disk for testing 278 | // image.getBuffer(Jimp.AUTO, (err, buffer) => { 279 | // const arrBuffer = [...buffer]; 280 | // const pathToBuffer: string = imagePath.substr(0, imagePath.lastIndexOf('/')) + '/buffer.txt'; 281 | // fs.writeFile(pathToBuffer, arrBuffer); 282 | // }); 283 | //thumb.cover(_config.thumbnails.width, _config.thumbnails.height); 284 | //thumb.resize(_config.thumbnails.width, Jimp.AUTO); 285 | //pathToThumb += image.getExtension(); 286 | 287 | // a thumbnail may already exist at this path (when generating from a flat collection of images) 288 | const thumbExists: boolean = await fileExists(pathToThumb); 289 | 290 | if (!thumbExists) { 291 | try { 292 | await sharp(imagePath, { 293 | limitInputPixels: true, 294 | }) 295 | .resize({ 296 | width: _config.thumbnails.width, 297 | height: _config.thumbnails.height, 298 | fit: sharp.fit.cover, 299 | }) 300 | .toFormat("jpeg") 301 | .toFile(pathToThumb); 302 | 303 | // thumb.write(pathToThumb, () => { 304 | log(`generated thumbnail for: ${fp}`); 305 | } catch { 306 | warn(`unable to generate thumbnail for: ${fp}`); 307 | } 308 | } else { 309 | log(`found thumbnail for: ${fp}`); 310 | } 311 | } else { 312 | // placeholder img path 313 | pathToThumb += "jpg"; 314 | } 315 | 316 | const thumbnailJson: any = cloneJson(thumbnailBoilerplate); 317 | 318 | // const virtualPath: string = getVirtualFilePath( 319 | // pathToThumb, 320 | // directory 321 | // ); 322 | // const mergedPath: string = mergePaths(directory.url, virtualPath); 323 | // thumbnailJson[0].id = mergedPath; 324 | 325 | let path = getThumbnailUrl(directory); 326 | 327 | thumbnailJson[0].id = path; 328 | 329 | json.thumbnail = thumbnailJson; 330 | } 331 | } 332 | } 333 | } 334 | } 335 | }; 336 | 337 | const getThumbnailUrl = (directory: Directory) => { 338 | let path: string = ""; 339 | 340 | while (directory) { 341 | // if the directory is a manifest and doesn't have a parent collection 342 | if ( 343 | directory.isManifest && 344 | (!directory.parentDirectory || !directory.parentDirectory.isCollection) 345 | ) { 346 | break; 347 | } 348 | 349 | if (directory.isCollection && !directory.parentDirectory) { 350 | break; 351 | } 352 | 353 | const name = basename(directory.directoryFilePath); 354 | path = urljoin(path, name); 355 | directory = directory.parentDirectory; 356 | // todo: keep going unless you reach a manifest directory with no collection directory parent 357 | // if (directory.parentDirectory && directory.parentDirectory.isManifest) { 358 | // break; 359 | // } else { 360 | 361 | // } 362 | } 363 | 364 | return urljoin(directory.url.href, path, "thumb.jpg"); 365 | }; 366 | 367 | export const getLabel = (value: string): any => { 368 | const labelJson: any = cloneJson(labelBoilerplate); 369 | labelJson["@none"].push(value); 370 | return labelJson; 371 | }; 372 | 373 | export const getFileDimensions = async ( 374 | type: string, 375 | file: string, 376 | canvasJson: any, 377 | annotationJson: any 378 | ): Promise => { 379 | log(`getting file dimensions for: ${file}`); 380 | 381 | if (!isJsonFile(file)) { 382 | switch (type.toLowerCase()) { 383 | // if it's an image, get the width and height and add to the annotation body and canvas 384 | case ExternalResourceType.IMAGE: 385 | try { 386 | const image: any = await sharp(file, { 387 | limitInputPixels: true, 388 | }).metadata(); 389 | const width: number = image.width; 390 | const height: number = image.height; 391 | canvasJson.width = Math.max(canvasJson.width || 0, width); 392 | canvasJson.height = Math.max(canvasJson.height || 0, height); 393 | annotationJson.body.width = width; 394 | annotationJson.body.height = height; 395 | } catch (e) { 396 | warn(`getting file dimensions failed for: ${file}`); 397 | } 398 | break; 399 | // if it's a sound, get the duration and add to the canvas 400 | case ExternalResourceType.SOUND: 401 | case ExternalResourceType.VIDEO: 402 | try { 403 | const info: any = await ffprobe(file, { path: ffprobeStatic.path }); 404 | if (info && info.streams && info.streams.length) { 405 | const duration: number = Number(info.streams[0].duration); 406 | canvasJson.duration = duration; 407 | } 408 | } catch (error) { 409 | warn(`ffprobe couldn't load ${file}`); 410 | } 411 | 412 | break; 413 | } 414 | } 415 | }; 416 | 417 | export const generateImageTiles = async ( 418 | image: string, 419 | url: string, 420 | directoryName: string, 421 | directory: string, 422 | annotationJson: any 423 | ): Promise => { 424 | try { 425 | log(`generating image tiles for: ${image}`); 426 | 427 | const id: string = urljoin(url, directoryName, "+tiles"); 428 | 429 | annotationJson.body.service = [ 430 | { 431 | "@id": id, 432 | "@type": "ImageService2", 433 | profile: "http://iiif.io/api/image/2/level2.json", 434 | }, 435 | ]; 436 | 437 | await sharp(image, { 438 | limitInputPixels: true, 439 | }) 440 | .tile({ 441 | layout: "iiif", 442 | id: urljoin(url, directoryName), 443 | }) 444 | .toFile(join(directory, "+tiles")); 445 | } catch { 446 | warn(`generating image tiles failed for: ${image}`); 447 | } 448 | }; 449 | 450 | /* 451 | merge these two example paths: 452 | url: http://test.com/collection/manifest 453 | filePath: c:/user/documents/collection/manifest/_canvas/thumb.png 454 | 455 | into: http://test.com/collection/manifest/_canvas/thumb.png 456 | */ 457 | export const mergePaths = (url: URL, filePath: string): string => { 458 | // split the url (minus origin) and filePath into arrays 459 | // ['collection', 'manifest'] 460 | // ['c:', 'user', 'documents', 'collection', 'manifest', '_canvas', 'thumb.jpg'] 461 | // walk backwards through the filePath array adding to the newPath array until the last item of the url array is found. 462 | // then while the next url item matches the next filePath item, add it to newPath. 463 | // the final path is the url origin plus a reversed newPath joined with a '/' 464 | 465 | let origin = url.origin; 466 | 467 | if (url.protocol === "dat:") { 468 | origin = "dat://"; 469 | } 470 | 471 | const urlParts = getUrlParts(url); 472 | filePath = normaliseFilePath(filePath); 473 | const fileParts: string[] = filePath.split("/"); 474 | 475 | let newPath: string[] = []; 476 | 477 | // if there's a single root folder and none of the file path matches 478 | if (urlParts.length === 1 && !fileParts.includes(urlParts[0])) { 479 | newPath.push(fileParts[fileParts.length - 1]); 480 | newPath.push(urlParts[0]); 481 | } else { 482 | for (let f = fileParts.length - 1; f >= 0; f--) { 483 | const filePart: string = fileParts[f]; 484 | newPath.push(filePart); 485 | 486 | if (filePart === urlParts[urlParts.length - 1]) { 487 | if (urlParts.length > 1) { 488 | for (let u = urlParts.length - 2; u >= 0; u--) { 489 | f--; 490 | if (fileParts[f] === urlParts[u]) { 491 | newPath.push(fileParts[f]); 492 | } else { 493 | newPath.push(urlParts[u]); 494 | } 495 | } 496 | } 497 | break; 498 | } 499 | } 500 | } 501 | 502 | let id: string = urljoin(origin, ...newPath.reverse()); 503 | 504 | return id; 505 | }; 506 | 507 | export const normaliseFilePath = (filePath: string): string => { 508 | return filePath.replace(/\\/g, "/").replace(/\/\//g, "/"); 509 | }; 510 | 511 | export const getUrlParts = (url: URL): string[] => { 512 | let origin: string = url.origin; 513 | let urlParts: string[]; 514 | 515 | let href: string = url.href; 516 | 517 | if (href.endsWith("/")) { 518 | href = href.slice(0, -1); 519 | } 520 | 521 | if (url.protocol === "dat:") { 522 | origin = "dat://"; 523 | urlParts = href.replace(origin, "").split("/"); 524 | } else { 525 | urlParts = href.replace(origin + "/", "").split("/"); 526 | } 527 | 528 | return urlParts; 529 | }; 530 | 531 | export const readJson = (path: string): Promise => { 532 | return new Promise((resolve, reject) => { 533 | jsonfile.readFile(path, (err, json) => { 534 | if (err) reject(err); 535 | else resolve(json); 536 | }); 537 | }); 538 | }; 539 | 540 | export const writeJson = (path: string, json: string): Promise => { 541 | return new Promise((resolve, reject) => { 542 | fs.writeFile(path, json, (err) => { 543 | if (err) reject(err); 544 | else resolve(); 545 | }); 546 | }); 547 | }; 548 | 549 | export const readYml = (path: string): Promise => { 550 | return new Promise((resolve, reject) => { 551 | try { 552 | const doc = yaml.load(fs.readFileSync(path, "utf8")); 553 | resolve(doc); 554 | } catch (e) { 555 | reject(e); 556 | } 557 | }); 558 | }; 559 | 560 | export const fileExists = (path: string): Promise => { 561 | return new Promise((resolve, reject) => { 562 | const exists: boolean = fs.existsSync(path); 563 | resolve(exists); 564 | }); 565 | }; 566 | 567 | export const hasManifestsYml = (path: string): Promise => { 568 | return new Promise((resolve, reject) => { 569 | const manifestsPath: string = join(path, "manifests.yml"); 570 | 571 | fileExists(manifestsPath).then((exists) => { 572 | resolve(exists); 573 | }); 574 | }); 575 | }; 576 | 577 | export const isURL = (path: string): boolean => { 578 | return isurl(path); 579 | }; 580 | 581 | export const log = (message: string): void => { 582 | console.log(chalk.green(message)); 583 | }; 584 | 585 | export const warn = (message: string): void => { 586 | console.warn(chalk.yellow(message)); 587 | }; 588 | 589 | export const error = (message: string): void => { 590 | console.warn(chalk.red(message)); 591 | }; 592 | -------------------------------------------------------------------------------- /boilerplate/annotation.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "type": "Annotation", 4 | "motivation": "", 5 | "body": { 6 | "id": "", 7 | "type": "", 8 | "format": "", 9 | "label": "" 10 | }, 11 | "target": "" 12 | } 13 | -------------------------------------------------------------------------------- /boilerplate/canvas.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "type": "Canvas", 4 | "items": [ 5 | { 6 | "id": "", 7 | "type": "AnnotationPage", 8 | "items": [] 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /boilerplate/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "http://www.w3.org/ns/anno.jsonld", 4 | "http://iiif.io/api/presentation/3/context.json" 5 | ], 6 | "id": "", 7 | "type": "Collection", 8 | "items": [] 9 | } 10 | -------------------------------------------------------------------------------- /boilerplate/collectionitem.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "type": "Collection" 4 | } 5 | -------------------------------------------------------------------------------- /boilerplate/imageservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "", 4 | "type": "ImageService3", 5 | "profile": "level0" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /boilerplate/label.json: -------------------------------------------------------------------------------- 1 | { 2 | "@none": [] 3 | } 4 | -------------------------------------------------------------------------------- /boilerplate/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "http://www.w3.org/ns/anno.jsonld", 4 | "http://iiif.io/api/presentation/3/context.json" 5 | ], 6 | "id": "", 7 | "type": "Manifest", 8 | "items": [] 9 | } 10 | -------------------------------------------------------------------------------- /boilerplate/manifestitem.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "type": "Manifest" 4 | } 5 | -------------------------------------------------------------------------------- /boilerplate/thumbnail.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "", 4 | "type": "Image" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "thumbnails": { 3 | "width": 100, 4 | "height": 100 5 | }, 6 | "annotation": { 7 | "motivations": { 8 | "painting": { 9 | ".crt": [ 10 | { 11 | "type": "Model", 12 | "format": "application/corto" 13 | } 14 | ], 15 | ".drc": [ 16 | { 17 | "type": "Model", 18 | "format": "application/draco" 19 | } 20 | ], 21 | ".epub": [ 22 | { 23 | "type": "Text", 24 | "format": "application/epub+zip" 25 | } 26 | ], 27 | ".glb": [ 28 | { 29 | "type": "Model", 30 | "format": "model/gltf-binary" 31 | } 32 | ], 33 | ".gltf": [ 34 | { 35 | "type": "Model", 36 | "format": "model/gltf+json" 37 | } 38 | ], 39 | ".gz": [ 40 | { 41 | "type": "Model", 42 | "format": "application/gzip" 43 | } 44 | ], 45 | ".stl": [ 46 | { 47 | "type": "Model", 48 | "format": "model/stl" 49 | } 50 | ], 51 | ".jpg": [ 52 | { 53 | "type": "Image", 54 | "format": "image/jpeg" 55 | } 56 | ], 57 | ".jpeg": [ 58 | { 59 | "type": "Image", 60 | "format": "image/jpeg" 61 | } 62 | ], 63 | ".json": [ 64 | { 65 | "type": "Text", 66 | "format": "application/json" 67 | }, 68 | { 69 | "type": "Model", 70 | "format": "application/vnd.threejs+json" 71 | }, 72 | { 73 | "type": "Image", 74 | "format": "image/jpeg" 75 | } 76 | ], 77 | ".mp3": [ 78 | { 79 | "type": "Sound", 80 | "format": "audio/mp3" 81 | } 82 | ], 83 | ".mp4": [ 84 | { 85 | "type": "Video", 86 | "format": "video/mp4" 87 | } 88 | ], 89 | ".nii": [ 90 | { 91 | "type": "Model", 92 | "format": "application/octet-stream" 93 | } 94 | ], 95 | ".nrrd": [ 96 | { 97 | "type": "Model", 98 | "format": "application/octet-stream" 99 | } 100 | ], 101 | ".obj": [ 102 | { 103 | "type": "Model", 104 | "format": "text/plain" 105 | } 106 | ], 107 | ".opf": [ 108 | { 109 | "type": "Text", 110 | "format": "application/oebps-package+xml" 111 | } 112 | ], 113 | ".pdf": [ 114 | { 115 | "type": "PDF", 116 | "format": "application/pdf" 117 | } 118 | ], 119 | ".ply": [ 120 | { 121 | "type": "Model", 122 | "format": "application/ply" 123 | } 124 | ], 125 | ".png": [ 126 | { 127 | "type": "Image", 128 | "format": "image/png" 129 | } 130 | ], 131 | ".tif": [ 132 | { 133 | "type": "Image", 134 | "format": "image/jpeg" 135 | } 136 | ], 137 | ".tiff": [ 138 | { 139 | "type": "Image", 140 | "format": "image/jpeg" 141 | } 142 | ], 143 | ".usdz": [ 144 | { 145 | "type": "Model", 146 | "format": "model/vnd.usd+zip" 147 | } 148 | ], 149 | ".vtt": [ 150 | { 151 | "type": "Text", 152 | "format": "text/vtt" 153 | } 154 | ] 155 | }, 156 | "commenting": { 157 | ".txt": [ 158 | { 159 | "type": "TextualBody", 160 | "format": "text/plain" 161 | } 162 | ] 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { Directory } from "./Directory"; 2 | import { fileExists, log } from "./Utils"; 3 | 4 | export const build = async ( 5 | dir: string, 6 | url: string, 7 | virtualName?: string 8 | ): Promise => { 9 | log(`started biiifing ${dir}`); 10 | 11 | // validate inputs 12 | 13 | const exists: boolean = await fileExists(dir); 14 | 15 | if (!exists) { 16 | throw new Error("Directory does not exist"); 17 | } 18 | 19 | if (!url) { 20 | // if a url hasn't been passed, check if running on Netlify or Vercel and use the appropriate url 21 | if (process.env.NETLIFY) { 22 | url = 23 | process.env.PULL_REQUEST === "true" 24 | ? process.env.DEPLOY_PRIME_URL 25 | : process.env.URL; 26 | } else if (process.env.VERCEL) { 27 | url = `https://${process.env.VERCEL_URL}`; 28 | } else { 29 | throw new Error("You must pass a url parameter"); 30 | } 31 | } 32 | 33 | const directory: Directory = new Directory(dir, url, virtualName); 34 | 35 | await directory.read(); 36 | 37 | log(`finished biiifing ${dir}`); 38 | }; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "biiif", 3 | "version": "1.0.7", 4 | "description": "A CLI to build IIIF collections", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/edsilv/biiif.git" 9 | }, 10 | "scripts": { 11 | "build": "npm run format && tsc", 12 | "format": "prettier --write \"**/*.*\"", 13 | "test": "mocha", 14 | "testbuild": "node --nolazy --inspect-brk=5858 -e \"require('./index').build('test/collection', 'https://sitename.netlify.app')\"", 15 | "serve": "serve test" 16 | }, 17 | "engines": { 18 | "node": ">=8.9.1", 19 | "npm": ">=3.10.8" 20 | }, 21 | "keywords": [ 22 | "IIIF", 23 | "nodejs", 24 | "CLI" 25 | ], 26 | "author": "@edsilv", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/edsilv/biiif/issues" 30 | }, 31 | "homepage": "https://github.com/edsilv/biiif#readme", 32 | "dependencies": { 33 | "@iiif/vocabulary": "^1.0.20", 34 | "chalk": "^4.1.0", 35 | "ffprobe": "^1.1.2", 36 | "ffprobe-static": "^3.0.0", 37 | "glob": "^7.1.6", 38 | "glob-promise": "^4.0.1", 39 | "is-url": "^1.2.4", 40 | "js-yaml": "^4.0.0", 41 | "jsonfile": "^6.1.0", 42 | "sharp": "^0.28.0", 43 | "url-join": "^2.0.5" 44 | }, 45 | "devDependencies": { 46 | "@types/node": "^14.14.27", 47 | "mocha": "8.3.0", 48 | "mock-fs": "4.13.0", 49 | "prettier": "^2.2.1", 50 | "prettier-check": "^2.0.0", 51 | "serve": "^11.3.2", 52 | "tslint-config-prettier": "^1.18.0", 53 | "typescript": "4.1.5", 54 | "typescript-tslint-plugin": "^1.0.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | const { basename } = require("path"); 2 | const { build } = require("../index"); 3 | const { URL } = require("url"); 4 | const assert = require("assert"); 5 | const config = require("../IConfigJSON"); 6 | const fs = require("fs"); 7 | const jsonfile = require("jsonfile"); 8 | const mock = require("mock-fs"); 9 | const urljoin = require("url-join"); 10 | 11 | exports.assert = assert; 12 | exports.basename = basename; 13 | exports.build = build; 14 | exports.config = config; 15 | exports.fs = fs; 16 | exports.jsonfile = jsonfile; 17 | exports.mock = mock; 18 | exports.URL = URL; 19 | exports.urljoin = urljoin; 20 | 21 | exports.canvasHasContentAnnotations = (canvasJson, files) => { 22 | assert(canvasJson); 23 | 24 | const annotationPage = canvasJson.items[0]; 25 | assert(annotationPage); 26 | 27 | files.forEach((file, index) => { 28 | const annotation = annotationPage.items[index]; 29 | assert(annotation); 30 | 31 | const contentAnnotation = annotation.body; 32 | assert(contentAnnotation); 33 | 34 | assert(basename(contentAnnotation.id) === file); 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /test/fixtures/behavior-paged.js: -------------------------------------------------------------------------------- 1 | module.exports = "behavior: paged"; 2 | -------------------------------------------------------------------------------- /test/fixtures/canvas-label-annotation.js: -------------------------------------------------------------------------------- 1 | module.exports = "value: assets/file.jpg \n\ 2 | label: Custom Label"; 3 | -------------------------------------------------------------------------------- /test/fixtures/commenting-text-with-format.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | "motivation: commenting \n\ 3 | format: text/plain \n\ 4 | value: This is a comment on the image"; 5 | -------------------------------------------------------------------------------- /test/fixtures/commenting-text-with-type.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | "motivation: commenting \n\ 3 | type: TextualBody \n\ 4 | value: This is a comment on the image"; 5 | -------------------------------------------------------------------------------- /test/fixtures/commenting-text-without-type-format.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | "motivation: commenting \n\ 3 | value: This is a comment on the image"; 4 | -------------------------------------------------------------------------------- /test/fixtures/dimensions-info.js: -------------------------------------------------------------------------------- 1 | module.exports = "width: 600 \n\ 2 | height: 400"; 3 | -------------------------------------------------------------------------------- /test/fixtures/epub-external-resource-annotation.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | "value: https://s3.amazonaws.com/epubjs/books/alice/OPS/package.opf"; 3 | -------------------------------------------------------------------------------- /test/fixtures/external-resource-annotation.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | "value: https://www.morphosource.org/media/morphosource/dcm_sample/platypus/platypus_manifest_20_slices.json"; 3 | -------------------------------------------------------------------------------- /test/fixtures/json-value-with-format.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | "motivation: data \n\ 3 | format: application/json \n\ 4 | value: assets/data.json"; 5 | -------------------------------------------------------------------------------- /test/fixtures/json-value-without-format.js: -------------------------------------------------------------------------------- 1 | module.exports = "motivation: data \n\ 2 | value: assets/data.json"; 3 | -------------------------------------------------------------------------------- /test/fixtures/json-value-without-motivation-type-format.js: -------------------------------------------------------------------------------- 1 | module.exports = "value: assets/file.json"; 2 | -------------------------------------------------------------------------------- /test/fixtures/manifests.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | "manifests: \n\ 3 | - id: http://test.com/collection/linkedmanifest2/index.json \n\ 4 | label: Linked Manifest 2 \n\ 5 | - id: http://test.com/collection/linkedmanifest3/index.json \n\ 6 | - id: http://test.com/collection/linkedmanifest1/index.json \n\ 7 | label: Linked Manifest 1 \n\ 8 | thumbnail: http://test.com/collection/linkedmanifest1/thumb.jpg"; 9 | -------------------------------------------------------------------------------- /test/fixtures/multiple-behavior.js: -------------------------------------------------------------------------------- 1 | module.exports = "behavior: \n\ 2 | - paged \n\ 3 | - unordered"; 4 | -------------------------------------------------------------------------------- /test/fixtures/painting-gltf.js: -------------------------------------------------------------------------------- 1 | module.exports = "value: assets/file.gltf"; 2 | -------------------------------------------------------------------------------- /test/fixtures/painting-jpg-with-xywh.js: -------------------------------------------------------------------------------- 1 | module.exports = "value: assets/file.jpg \n\ 2 | xywh: 0,0,600,400"; 3 | -------------------------------------------------------------------------------- /test/fixtures/painting-jpg.js: -------------------------------------------------------------------------------- 1 | module.exports = "value: assets/file.jpg"; 2 | -------------------------------------------------------------------------------- /test/fixtures/painting-threejs-json-with-type.js: -------------------------------------------------------------------------------- 1 | module.exports = "type: PhysicalObject \n\ 2 | value: assets/file.json"; 3 | -------------------------------------------------------------------------------- /test/fixtures/presentation-3-image-service.js: -------------------------------------------------------------------------------- 1 | module.exports = "value: assets/tiles/info.json \n\ 2 | type: Image"; 3 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Static Template 8 | 9 | 10 |
11 | 12 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const common = require("./common"); 2 | const mock = common.mock; 3 | 4 | function importTest(name, path) { 5 | describe(name, function () { 6 | require(path); 7 | }); 8 | } 9 | 10 | before(async () => { 11 | const blob = new Buffer([8, 6, 7, 5, 3, 0, 9]); 12 | const jpg = new Buffer(require("./fixtures/cat-jpg")); 13 | 14 | mock({ 15 | "/thumbs-single-manifest": { 16 | "file.jpg": jpg, 17 | }, 18 | "/files-only-manifest": { 19 | "file.glb": blob, 20 | "file.gltf": blob, 21 | "file.jpeg": jpg, 22 | "file.jpg": jpg, 23 | "file.png": jpg, 24 | "file.usdz": blob, 25 | }, 26 | "/files-only-manifest-dat": { 27 | "file.gltf": blob, 28 | "file.jpg": jpg, 29 | "file.png": jpg, 30 | }, 31 | "/files-only-collection": { 32 | "files-only-manifest": { 33 | "file.gltf": blob, 34 | "file.jpg": jpg, 35 | "file.png": jpg, 36 | }, 37 | }, 38 | "/vercel-manifest": { 39 | "_page-1": { 40 | "info.yml": "label: Page 1", 41 | "1.jpg": jpg, 42 | }, 43 | }, 44 | "/gh-collection": { 45 | "info.yml": "label: My Test Collection", 46 | "thumb.png": jpg, 47 | vertebra: { 48 | "thumb.jpg": jpg, 49 | "info.yml": "label: Vertebra", 50 | _vertebra: { 51 | "diffuse.png": jpg, 52 | "normal.png": jpg, 53 | "vertebra.mtl": "...", 54 | "vertebra.obj": "...", 55 | }, 56 | }, 57 | }, 58 | "/manifests-collection": { 59 | "manifests.yml": require("./fixtures/manifests"), 60 | }, 61 | "/collection": { 62 | "info.yml": "label: My Test Collection", 63 | "thumb.png": jpg, 64 | a_manifest: { 65 | "info.yml": "label: A Manifest", 66 | "thumb.png": jpg, 67 | _canvas: { 68 | "info.yml": "label: A Canvas", 69 | "page_1.jpg": jpg, 70 | "thumb.png": jpg, 71 | }, 72 | }, 73 | "manifests.yml": require("./fixtures/manifests"), 74 | "sub-collection": { 75 | "info.yml": "label: My Test Sub-collection", 76 | "thumb.png": jpg, 77 | b_manifest: { 78 | "thumb.png": jpg, 79 | "info.yml": "label: My Test Submanifest", 80 | _canvas: { 81 | "info.yml": "label: My Test Subcanvas", 82 | "page_1.jpg": jpg, 83 | "thumb.png": jpg, 84 | }, 85 | }, 86 | }, 87 | }, 88 | "/file-annotation-collection": { 89 | "canvas-per-file": { 90 | _crt: { 91 | "file.crt": blob, 92 | }, 93 | _drc: { 94 | "file.drc": blob, 95 | }, 96 | _gltf: { 97 | "file.gltf": "gltf", 98 | }, 99 | _jpg: { 100 | "file.jpg": jpg, 101 | }, 102 | _json: { 103 | "file.json": "json", 104 | }, 105 | // _mp3: { 106 | // "file.mp3": blob, 107 | // }, 108 | // _mp4: { 109 | // "file.mp4": blob, 110 | // }, 111 | _obj: { 112 | "file.obj": "obj", 113 | }, 114 | _pdf: { 115 | "file.pdf": blob, 116 | }, 117 | _ply: { 118 | "file.ply": "ply", 119 | }, 120 | _png: { 121 | "file.png": jpg, 122 | }, 123 | _usdz: { 124 | "file.usdz": blob, 125 | }, 126 | }, 127 | "erroneous-file": { 128 | _files: { 129 | "file.abc": "abc", 130 | }, 131 | }, 132 | "files-per-canvas": { 133 | _files: { 134 | "file.crt": blob, 135 | "file.drc": blob, 136 | "file.gltf": "gltf", 137 | "file.jpg": jpg, 138 | "file.json": "json", 139 | // "file.mp3": blob, 140 | // "file.mp4": blob, 141 | "file.obj": "obj", 142 | "file.pdf": blob, 143 | "file.ply": "ply", 144 | "file.png": jpg, 145 | "file.usdz": blob, 146 | }, 147 | }, 148 | }, 149 | "/sort-canvases-manifest": { 150 | "_a-canvas": { 151 | "file.jpg": jpg, 152 | }, 153 | "_b-canvas": { 154 | "file.jpg": jpg, 155 | }, 156 | "_c-canvas": { 157 | "file.jpg": jpg, 158 | }, 159 | "_d-canvas": { 160 | "file.jpg": jpg, 161 | }, 162 | "_e-canvas": { 163 | "file.jpg": jpg, 164 | }, 165 | "_f-canvas": { 166 | "file.jpg": jpg, 167 | }, 168 | "_g-canvas": { 169 | "file.jpg": jpg, 170 | }, 171 | "_h-canvas": { 172 | "file.jpg": jpg, 173 | }, 174 | "_i-canvas": { 175 | "file.jpg": jpg, 176 | }, 177 | "_j-canvas": { 178 | "file.jpg": jpg, 179 | }, 180 | "_k-canvas": { 181 | "file.jpg": jpg, 182 | }, 183 | }, 184 | "/sort-canvases-numeric-manifest": { 185 | "_page-1": { 186 | "file.jpg": jpg, 187 | }, 188 | "_page-2": { 189 | "file.jpg": jpg, 190 | }, 191 | "_page-3": { 192 | "file.jpg": jpg, 193 | }, 194 | "_page-4": { 195 | "file.jpg": jpg, 196 | }, 197 | "_page-5": { 198 | "file.jpg": jpg, 199 | }, 200 | "_page-6": { 201 | "file.jpg": jpg, 202 | }, 203 | "_page-7": { 204 | "file.jpg": jpg, 205 | }, 206 | "_page-8": { 207 | "file.jpg": jpg, 208 | }, 209 | "_page-9": { 210 | "file.jpg": jpg, 211 | }, 212 | "_page-10": { 213 | "file.jpg": jpg, 214 | }, 215 | "_page-11": { 216 | "file.jpg": jpg, 217 | }, 218 | "_page-12": { 219 | "file.jpg": jpg, 220 | }, 221 | "_page-13": { 222 | "file.jpg": jpg, 223 | }, 224 | "_page-14": { 225 | "file.jpg": jpg, 226 | }, 227 | "_page-15": { 228 | "file.jpg": jpg, 229 | }, 230 | "_page-16": { 231 | "file.jpg": jpg, 232 | }, 233 | "_page-17": { 234 | "file.jpg": jpg, 235 | }, 236 | "_page-18": { 237 | "file.jpg": jpg, 238 | }, 239 | "_page-19": { 240 | "file.jpg": jpg, 241 | }, 242 | "_page-20": { 243 | "file.jpg": jpg, 244 | }, 245 | "_page-21": { 246 | "file.jpg": jpg, 247 | }, 248 | }, 249 | "/sort-files-numeric-manifest": { 250 | "page1.jpg": jpg, 251 | "page2.jpg": jpg, 252 | "page3.jpg": jpg, 253 | "page4.jpg": jpg, 254 | "page5.jpg": jpg, 255 | "page6.jpg": jpg, 256 | "page7.jpg": jpg, 257 | "page8.jpg": jpg, 258 | "page9.jpg": jpg, 259 | "page10.jpg": jpg, 260 | "page11.jpg": jpg, 261 | "page12.jpg": jpg, 262 | "page13.jpg": jpg, 263 | "page14.jpg": jpg, 264 | "page15.jpg": jpg, 265 | "page16.jpg": jpg, 266 | "page17.jpg": jpg, 267 | "page18.jpg": jpg, 268 | "page19.jpg": jpg, 269 | "page20.jpg": jpg, 270 | "page21.jpg": jpg, 271 | }, 272 | "/custom-annotations-manifest": { 273 | "_commenting-text-with-format": { 274 | "commenting-text-with-format.yml": require("./fixtures/commenting-text-with-format"), 275 | }, 276 | "_commenting-text-with-type": { 277 | "commenting-text-with-type.yml": require("./fixtures/commenting-text-with-type"), 278 | }, 279 | "_commenting-text-without-type-format": { 280 | "commenting-text-without-type-format.yml": require("./fixtures/commenting-text-without-type-format"), 281 | }, 282 | "_json-value-with-format": { 283 | "json-value-with-format.yml": require("./fixtures/json-value-with-format"), 284 | }, 285 | "_json-value-without-format": { 286 | "json-value-without-format.yml": require("./fixtures/json-value-without-format"), 287 | }, 288 | "_json-value-without-motivation-type-format": { 289 | assets: { 290 | "file.json": "json", 291 | }, 292 | "json-value-without-motivation-type-format.yml": require("./fixtures/json-value-without-motivation-type-format"), 293 | }, 294 | "_painting-gltf": { 295 | assets: { 296 | "file.gltf": "gltf", 297 | "texture.png": jpg, 298 | }, 299 | "painting-gltf.yml": require("./fixtures/painting-gltf"), 300 | }, 301 | "_painting-jpg": { 302 | assets: { 303 | "file.jpg": jpg, 304 | }, 305 | "painting-jpg.yml": require("./fixtures/painting-jpg"), 306 | "file.jpg": jpg, 307 | }, 308 | "_painting-threejs-json-with-type": { 309 | assets: { 310 | "file.json": "json", 311 | "texture.png": jpg, 312 | }, 313 | "painting-threejs-json-with-type.yml": require("./fixtures/painting-threejs-json-with-type"), 314 | }, 315 | }, 316 | "/generate-thumbs-manifest": { 317 | "_page-1": { 318 | "file.jpg": jpg, 319 | }, 320 | "_page-2": { 321 | "file.jpg": jpg, 322 | }, 323 | }, 324 | "/generate-thumbs-dat-manifest": { 325 | "_page-1": { 326 | "file.jpg": jpg, 327 | }, 328 | "_page-2": { 329 | "file.jpg": jpg, 330 | }, 331 | }, 332 | "/canvas-with-dimensions-manifest": { 333 | "_canvas-with-dimensions": { 334 | assets: { 335 | "file.jpg": jpg, 336 | }, 337 | "painting-jpg-with-xywh.yml": require("./fixtures/painting-jpg-with-xywh"), 338 | "info.yml": require("./fixtures/dimensions-info"), 339 | }, 340 | }, 341 | "/canvas-with-presentation-3-image-service-manifest": { 342 | "_canvas-with-presentation-3-image-service": { 343 | "presentation-3-image-service.yml": require("./fixtures/presentation-3-image-service"), 344 | }, 345 | }, 346 | "/behavior-paged-manifest": { 347 | "info.yml": require("./fixtures/behavior-paged"), 348 | "_page-1": { 349 | "file.jpg": jpg, 350 | }, 351 | "_page-2": { 352 | "file.jpg": jpg, 353 | }, 354 | }, 355 | "/multiple-behavior-manifest": { 356 | "info.yml": require("./fixtures/multiple-behavior"), 357 | "_page-1": { 358 | "file.jpg": jpg, 359 | }, 360 | "_page-2": { 361 | "file.jpg": jpg, 362 | }, 363 | }, 364 | "/image-dimensions-manifest": { 365 | "_page-1": { 366 | "file.jpg": jpg, 367 | }, 368 | }, 369 | "/external-resource-annotation-manifest": { 370 | _platypus: { 371 | "platypus.yml": require("./fixtures/external-resource-annotation"), 372 | }, 373 | }, 374 | "/canvas-label-annotation-manifest": { 375 | "_canvas-label-annotation": { 376 | assets: { 377 | "file.jpg": jpg, 378 | }, 379 | "label.yml": require("./fixtures/canvas-label-annotation"), 380 | }, 381 | }, 382 | "/readme-manifest": { 383 | "README.md": "readme contents", 384 | }, 385 | "/epub-collection": { 386 | "alice-in-wonderland": { 387 | "_alice-in-wonderland": { 388 | "alice-in-wonderland.yml": require("./fixtures/epub-external-resource-annotation"), 389 | }, 390 | }, 391 | "cc-shared-culture": { 392 | "_cc-shared-culture": { 393 | "cc-shared-culture.epub": blob, 394 | }, 395 | }, 396 | }, 397 | }); 398 | }); 399 | 400 | after(async () => { 401 | mock.restore(); 402 | }); 403 | 404 | importTest("utils", "./tests/utils"); 405 | importTest("url", "./tests/url"); 406 | importTest("do-promises-work", "./tests/do-promises-work"); 407 | importTest("thumbs-single-manifest", "./tests/thumbs-single-manifest"); 408 | importTest("thumbs-single-manifest-dat", "./tests/thumbs-single-manifest-dat"); 409 | importTest("files-only-manifest", "./tests/files-only-manifest"); 410 | importTest("files-only-manifest-dat", "./tests/files-only-manifest-dat"); 411 | importTest("files-only-collection", "./tests/files-only-collection"); 412 | importTest("vercel-manifest", "./tests/vercel-manifest"); 413 | importTest("gh-pages", "./tests/gh-pages"); 414 | importTest("collection-no-manifests", "./tests/collection-no-manifests"); 415 | importTest("collection", "./tests/collection"); 416 | importTest("file-annotation-collection", "./tests/file-annotation-collection"); 417 | importTest("sort-canvases-manifest", "./tests/sort-canvases-manifest"); 418 | importTest( 419 | "sort-canvases-numeric-manifest", 420 | "./tests/sort-canvases-numeric-manifest" 421 | ); 422 | importTest( 423 | "sort-files-numeric-manifest", 424 | "./tests/sort-files-numeric-manifest" 425 | ); 426 | importTest( 427 | "custom-annotations-manifest", 428 | "./tests/custom-annotations-manifest" 429 | ); 430 | importTest("generate-thumbs-manifest", "./tests/generate-thumbs-manifest"); 431 | importTest( 432 | "generate-thumbs-dat-manifest", 433 | "./tests/generate-thumbs-dat-manifest" 434 | ); 435 | importTest( 436 | "generate-thumbs-http-gateway-dat-manifest", 437 | "./tests/generate-thumbs-http-gateway-dat-manifest" 438 | ); 439 | importTest("dat-gateway", "./tests/dat-gateway"); 440 | importTest( 441 | "canvas-with-dimensions-manifest", 442 | "./tests/canvas-with-dimensions-manifest" 443 | ); 444 | importTest( 445 | "canvas-with-presentation-3-image-service-manifest", 446 | "./tests/canvas-with-presentation-3-image-service-manifest" 447 | ); 448 | importTest("behavior-paged-manifest", "./tests/behavior-paged-manifest"); 449 | importTest("multiple-behavior-manifest", "./tests/multiple-behavior-manifest"); 450 | importTest("image-dimensions-manifest", "./tests/image-dimensions-manifest"); 451 | importTest( 452 | "external-resource-annotation-manifest", 453 | "./tests/external-resource-annotation-manifest" 454 | ); 455 | importTest( 456 | "canvas-label-annotation-manifest", 457 | "./tests/canvas-label-annotation-manifest" 458 | ); 459 | importTest("readme-manifest", "./tests/readme-manifest"); 460 | importTest("epub-collection", "./tests/epub-collection"); 461 | -------------------------------------------------------------------------------- /test/readme.md: -------------------------------------------------------------------------------- 1 | Run the `Test Build` task to build test/collection (F5). 2 | 3 | Drag and drop the collection folder to https://app.netlify.com/drop to deploy. 4 | 5 | `npm run serve` to test using test/index.html 6 | -------------------------------------------------------------------------------- /test/tests/behavior-paged-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson; 5 | const manifest = "/behavior-paged-manifest"; 6 | const manifestUrl = "http://test.com/behavior-paged-manifest"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/behavior-paged-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("has paged behavior", async () => { 20 | assert(manifestJson.behavior[0] === "paged"); 21 | }); 22 | -------------------------------------------------------------------------------- /test/tests/canvas-label-annotation-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, annotationPage, annotation, annotationBody; 5 | const manifest = "/canvas-label-annotation-manifest"; 6 | const manifestUrl = "http://test.com/canvas-label-annotation-manifest"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/canvas-label-annotation-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("can find canvas", async () => { 20 | canvasJson = manifestJson.items[0]; 21 | assert(canvasJson); 22 | }); 23 | 24 | it("has correct canvas id", async () => { 25 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 26 | }); 27 | 28 | it("has correct canvas label", async () => { 29 | assert(canvasJson.label["@none"][0] === "Custom Label"); 30 | }); 31 | 32 | it("has an annotation page", async () => { 33 | annotationPage = canvasJson.items[0]; 34 | assert(annotationPage); 35 | }); 36 | 37 | it("has annotation", async () => { 38 | annotation = annotationPage.items[0]; 39 | assert(annotation); 40 | }); 41 | 42 | it("has correct annotation motivation", async () => { 43 | assert(annotation.motivation === "painting"); 44 | }); 45 | 46 | it("has correct annotation target", async () => { 47 | assert(annotation.target === manifestUrl + "/index.json/canvas/0"); 48 | }); 49 | 50 | it("has an annotation body", async () => { 51 | annotationBody = annotation.body; 52 | assert(annotationBody); 53 | }); 54 | 55 | it("has correct annotation label", async () => { 56 | assert(annotationBody.label["@none"][0] === "Custom Label"); 57 | }); 58 | -------------------------------------------------------------------------------- /test/tests/canvas-with-dimensions-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, annotationPage, annotation, annotationBody; 5 | const manifest = "/canvas-with-dimensions-manifest"; 6 | const manifestUrl = "http://test.com/canvas-with-dimensions-manifest"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/canvas-with-dimensions-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("can find canvas", async () => { 20 | canvasJson = manifestJson.items[0]; 21 | assert(canvasJson); 22 | }); 23 | 24 | it("has correct canvas id", async () => { 25 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 26 | }); 27 | 28 | it("has correct dimensions", async () => { 29 | assert(canvasJson.width === 600); 30 | assert(canvasJson.height === 400); 31 | }); 32 | 33 | it("has an annotation page", async () => { 34 | annotationPage = canvasJson.items[0]; 35 | assert(annotationPage); 36 | }); 37 | 38 | it("has annotation", async () => { 39 | annotation = annotationPage.items[0]; 40 | assert(annotation); 41 | }); 42 | 43 | it("has correct annotation motivation", async () => { 44 | assert(annotation.motivation === "painting"); 45 | }); 46 | 47 | it("has correct annotation target", async () => { 48 | assert(annotation.target === manifestUrl + "/index.json/canvas/0"); 49 | }); 50 | 51 | it("has an annotation body", async () => { 52 | annotationBody = annotation.body; 53 | assert(annotationBody); 54 | }); 55 | 56 | it("has correct annotation body id", async () => { 57 | assert( 58 | annotationBody.id === 59 | manifestUrl + "/_canvas-with-dimensions/assets/file.jpg#xywh=0,0,600,400" 60 | ); 61 | }); 62 | -------------------------------------------------------------------------------- /test/tests/canvas-with-presentation-3-image-service-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, 5 | canvasJson, 6 | annotationPage, 7 | annotation, 8 | annotationBody, 9 | service; 10 | const manifest = "/canvas-with-presentation-3-image-service-manifest"; 11 | const manifestUrl = 12 | "http://test.com/canvas-with-presentation-3-image-service-manifest"; 13 | 14 | it("can build manifest", async () => { 15 | assert(await fileExists(manifest)); 16 | return build(manifest, manifestUrl); 17 | }).timeout(1000); // should take less than a second 18 | 19 | it("can find manifest index.json", async () => { 20 | const file = "/canvas-with-presentation-3-image-service-manifest/index.json"; 21 | assert(await fileExists(file)); 22 | manifestJson = await readJson(file); 23 | }); 24 | 25 | it("can find canvas", async () => { 26 | canvasJson = manifestJson.items[0]; 27 | assert(canvasJson); 28 | }); 29 | 30 | it("has correct canvas id", async () => { 31 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 32 | }); 33 | 34 | it("has an annotation page", async () => { 35 | annotationPage = canvasJson.items[0]; 36 | assert(annotationPage); 37 | }); 38 | 39 | it("has annotation", async () => { 40 | annotation = annotationPage.items[0]; 41 | assert(annotation); 42 | }); 43 | 44 | it("has correct annotation motivation", async () => { 45 | assert(annotation.motivation === "painting"); 46 | }); 47 | 48 | it("has correct annotation target", async () => { 49 | assert(annotation.target === manifestUrl + "/index.json/canvas/0"); 50 | }); 51 | 52 | it("has an annotation body", async () => { 53 | annotationBody = annotation.body; 54 | assert(annotationBody); 55 | }); 56 | 57 | it("has correct annotation body id", async () => { 58 | assert( 59 | annotationBody.id === 60 | manifestUrl + 61 | "/_canvas-with-presentation-3-image-service/assets/tiles/info.json" 62 | ); 63 | }); 64 | 65 | it("has correct annotation body type", async () => { 66 | assert(annotationBody.type === "Image"); 67 | }); 68 | 69 | it("has correct annotation body format", async () => { 70 | assert(annotationBody.format === "image/jpeg"); 71 | }); 72 | 73 | it("has an image service", async () => { 74 | service = annotationBody.service; 75 | assert(service && service.length); 76 | }); 77 | 78 | it("has correct image service id", async () => { 79 | assert( 80 | service[0].id === 81 | manifestUrl + "/_canvas-with-presentation-3-image-service/assets/tiles" 82 | ); 83 | }); 84 | -------------------------------------------------------------------------------- /test/tests/collection-no-manifests.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let collectionJson; 5 | const collection = "/manifests-collection"; 6 | const collectionUrl = "http://test.com/collection"; 7 | 8 | it("can build collection", async () => { 9 | assert(await fileExists(collection)); 10 | return build(collection, collectionUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find collection index.json", async () => { 14 | const file = "/manifests-collection/index.json"; 15 | assert(await fileExists(file)); 16 | collectionJson = await readJson(file); 17 | }); 18 | 19 | it("has correct number of items", async () => { 20 | assert(collectionJson.items.length === 3); 21 | }); 22 | -------------------------------------------------------------------------------- /test/tests/collection.js: -------------------------------------------------------------------------------- 1 | const { assert, build, urljoin } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let collectionJson, 5 | manifestJson, 6 | canvasJson, 7 | thumbnailJson, 8 | item, 9 | annotationPage, 10 | imageAnnotation; 11 | const collection = "/collection"; 12 | const collectionUrl = "http://test.com/collection"; 13 | 14 | it("can build collection", async () => { 15 | assert(await fileExists(collection)); 16 | return build(collection, collectionUrl); 17 | }).timeout(1000); // should take less than a second 18 | 19 | it("can find collection index.json", async () => { 20 | const file = "/collection/index.json"; 21 | assert(await fileExists(file)); 22 | collectionJson = await readJson(file); 23 | }); 24 | 25 | it("has correct collection id", async () => { 26 | assert(collectionJson.id === collectionUrl + "/index.json"); 27 | }); 28 | 29 | it("has correct collection label", async () => { 30 | assert(collectionJson.label["@none"][0] === "My Test Collection"); 31 | }); 32 | 33 | it("has a collection thumbnail", async () => { 34 | thumbnailJson = collectionJson.thumbnail[0]; 35 | assert(thumbnailJson); 36 | }); 37 | 38 | it("has the correct collection thumbnail id", async () => { 39 | const id = urljoin(collectionUrl, "thumb.png"); 40 | assert(thumbnailJson.id === id); 41 | }); 42 | 43 | it("has correct number of items", async () => { 44 | assert(collectionJson.items.length === 5); 45 | }); 46 | 47 | it("has an item manifest", async () => { 48 | item = collectionJson.items[0]; 49 | assert(item); 50 | }); 51 | 52 | it("has correct item id", async () => { 53 | assert(item.id === collectionUrl + "/a_manifest/index.json"); 54 | }); 55 | 56 | it("has correct item label", async () => { 57 | assert(item.label["@none"][0] === "A Manifest"); 58 | }); 59 | 60 | it("has item thumbnail", async () => { 61 | thumbnailJson = item.thumbnail; 62 | assert(thumbnailJson); 63 | }); 64 | 65 | it("has a linked item manifest", async () => { 66 | item = collectionJson.items[1]; 67 | assert(item); 68 | }); 69 | 70 | it("has correct linked item id", async () => { 71 | assert(item.id === "http://test.com/collection/linkedmanifest1/index.json"); 72 | }); 73 | 74 | it("has correct linked item label", async () => { 75 | assert(item.label["@none"][0] === "Linked Manifest 1"); 76 | }); 77 | 78 | it("has linked item thumbnail", async () => { 79 | thumbnailJson = item.thumbnail; 80 | assert(thumbnailJson); 81 | }); 82 | 83 | it("has correct linked item thumbnail id", async () => { 84 | assert( 85 | thumbnailJson[0].id === 86 | "http://test.com/collection/linkedmanifest1/thumb.jpg" 87 | ); 88 | }); 89 | 90 | it("has a linked item manifest", async () => { 91 | item = collectionJson.items[3]; 92 | assert(item); 93 | }); 94 | 95 | it("has correct linked item id", async () => { 96 | assert(item.id === "http://test.com/collection/linkedmanifest3/index.json"); 97 | }); 98 | 99 | it("has correct linked item label", async () => { 100 | assert(item.label["@none"][0] === "linkedmanifest3"); 101 | }); 102 | 103 | it("can find manifest index.json", async () => { 104 | const file = "/collection/a_manifest/index.json"; 105 | assert(await fileExists(file)); 106 | manifestJson = await readJson(file); 107 | }); 108 | 109 | it("has correct manifest id", async () => { 110 | assert(manifestJson.id === collectionUrl + "/a_manifest/index.json"); 111 | }); 112 | 113 | it("has correct manifest label", async () => { 114 | assert(manifestJson.label["@none"][0] === "A Manifest"); 115 | }); 116 | 117 | it("can find canvas", async () => { 118 | canvasJson = manifestJson.items[0]; 119 | assert(canvasJson); 120 | }); 121 | 122 | it("has correct canvas id", async () => { 123 | assert(canvasJson.id === collectionUrl + "/a_manifest/index.json/canvas/0"); 124 | }); 125 | 126 | it("has correct canvas label", async () => { 127 | assert(canvasJson.label["@none"][0] === "A Canvas"); 128 | }); 129 | 130 | it("has a canvas thumbnail", async () => { 131 | thumbnailJson = canvasJson.thumbnail[0]; 132 | assert(thumbnailJson); 133 | }); 134 | 135 | it("has the correct canvas thumbnail id", async () => { 136 | const id = urljoin(collectionUrl, "/a_manifest/_canvas/thumb.png"); 137 | assert(thumbnailJson.id === id); 138 | }); 139 | 140 | it("has an annotation page", async () => { 141 | annotationPage = canvasJson.items[0]; 142 | assert(annotationPage); 143 | }); 144 | 145 | it("has the correct annotation page id", async () => { 146 | annotationPage = canvasJson.items[0]; 147 | assert( 148 | annotationPage.id === 149 | collectionUrl + "/a_manifest/index.json/canvas/0/annotationpage/0" 150 | ); 151 | }); 152 | 153 | it("has an annotation", async () => { 154 | annotation = annotationPage.items[0]; 155 | assert(annotation); 156 | }); 157 | 158 | it("has an image annotation body", async () => { 159 | imageAnnotation = annotation.body; 160 | assert(imageAnnotation); 161 | }); 162 | 163 | it("has an annotation body", async () => { 164 | imageAnnotation = annotation.body; 165 | assert(imageAnnotation); 166 | }); 167 | 168 | it("has correct annotation id", async () => { 169 | assert( 170 | imageAnnotation.id === collectionUrl + "/a_manifest/_canvas/page_1.jpg" 171 | ); 172 | }); 173 | 174 | describe("sub-collection", async () => { 175 | it("can find collection index.json", async () => { 176 | const file = "/collection/sub-collection/index.json"; 177 | assert(await fileExists(file)); 178 | collectionJson = await readJson(file); 179 | }); 180 | 181 | it("has correct collection id", async () => { 182 | assert(collectionJson.id === collectionUrl + "/sub-collection/index.json"); 183 | }); 184 | 185 | it("has correct collection label", async () => { 186 | assert(collectionJson.label["@none"][0] === "My Test Sub-collection"); 187 | }); 188 | 189 | it("has a collection thumbnail", async () => { 190 | thumbnailJson = collectionJson.thumbnail[0]; 191 | assert(thumbnailJson); 192 | }); 193 | 194 | it("has the correct collection thumbnail id", async () => { 195 | const id = urljoin(collectionUrl, "/sub-collection/thumb.png"); 196 | assert(thumbnailJson.id === id); 197 | }); 198 | 199 | it("has a item manifest", async () => { 200 | item = collectionJson.items[0]; 201 | assert(item); 202 | }); 203 | 204 | it("has correct item id", async () => { 205 | assert(item.id === collectionUrl + "/sub-collection/b_manifest/index.json"); 206 | }); 207 | 208 | it("has correct item label", async () => { 209 | assert(item.label["@none"][0] === "My Test Submanifest"); 210 | }); 211 | 212 | it("has item thumbnail", async () => { 213 | thumbnailJson = item.thumbnail; 214 | assert(thumbnailJson); 215 | }); 216 | 217 | it("has correct item thumbnail id", async () => { 218 | assert( 219 | thumbnailJson[0].id === 220 | collectionUrl + "/sub-collection/b_manifest/thumb.png" 221 | ); 222 | }); 223 | 224 | it("can find manifest index.json", async () => { 225 | const file = "/collection/sub-collection/b_manifest/index.json"; 226 | assert(await fileExists(file)); 227 | manifestJson = await readJson(file); 228 | }); 229 | 230 | it("has correct manifest id", async () => { 231 | assert( 232 | manifestJson.id === collectionUrl + "/sub-collection/b_manifest/index.json" 233 | ); 234 | }); 235 | 236 | it("has correct manifest label", async () => { 237 | assert(manifestJson.label["@none"][0] === "My Test Submanifest"); 238 | }); 239 | 240 | it("can find canvas", async () => { 241 | canvasJson = manifestJson.items[0]; 242 | assert(canvasJson); 243 | }); 244 | 245 | it("has correct canvas id", async () => { 246 | assert( 247 | canvasJson.id === 248 | collectionUrl + "/sub-collection/b_manifest/index.json/canvas/0" 249 | ); 250 | }); 251 | 252 | it("has correct canvas label", async () => { 253 | assert(canvasJson.label["@none"][0] === "My Test Subcanvas"); 254 | }); 255 | 256 | it("has a canvas thumbnail", async () => { 257 | thumbnailJson = canvasJson.thumbnail[0]; 258 | assert(thumbnailJson); 259 | }); 260 | 261 | it("has the correct canvas thumbnail id", async () => { 262 | const id = urljoin( 263 | collectionUrl, 264 | "/sub-collection/b_manifest/_canvas/thumb.png" 265 | ); 266 | assert(thumbnailJson.id === id); 267 | }); 268 | 269 | it("has an annotation page", async () => { 270 | annotationPage = canvasJson.items[0]; 271 | assert(annotationPage); 272 | }); 273 | 274 | it("has the correct annotation page id", async () => { 275 | annotationPage = canvasJson.items[0]; 276 | assert( 277 | annotationPage.id === 278 | collectionUrl + 279 | "/sub-collection/b_manifest/index.json/canvas/0/annotationpage/0" 280 | ); 281 | }); 282 | 283 | it("has an annotation", async () => { 284 | annotation = annotationPage.items[0]; 285 | assert(annotation); 286 | }); 287 | 288 | it("has an image annotation body", async () => { 289 | imageAnnotation = annotation.body; 290 | assert(imageAnnotation); 291 | }); 292 | 293 | it("has an annotation body", async () => { 294 | imageAnnotation = annotation.body; 295 | assert(imageAnnotation); 296 | }); 297 | 298 | it("image annotation has correct id", async () => { 299 | assert( 300 | imageAnnotation.id === 301 | collectionUrl + "/sub-collection/b_manifest/_canvas/page_1.jpg" 302 | ); 303 | }); 304 | }); 305 | -------------------------------------------------------------------------------- /test/tests/custom-annotations-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, annotation, annotationPage, annotationBody; 5 | const manifest = "/custom-annotations-manifest"; 6 | const customAnnotationsManifestUrl = 7 | "http://test.com/custom-annotations-manifest"; 8 | 9 | it("can build custom annotations collection", async () => { 10 | assert(await fileExists(manifest)); 11 | return build(manifest, customAnnotationsManifestUrl); 12 | }).timeout(1000); // should take less than a second 13 | 14 | it("can find manifest index.json", async () => { 15 | const file = "/custom-annotations-manifest/index.json"; 16 | assert(await fileExists(file)); 17 | manifestJson = await readJson(file); 18 | }); 19 | 20 | describe("commenting text with format", async () => { 21 | it("can find canvas", async () => { 22 | canvasJson = manifestJson.items[0]; 23 | assert(canvasJson); 24 | }); 25 | 26 | it("has correct canvas id", async () => { 27 | assert( 28 | canvasJson.id === customAnnotationsManifestUrl + "/index.json/canvas/0" 29 | ); 30 | }); 31 | 32 | it("has correct canvas label", async () => { 33 | assert(canvasJson.label["@none"][0] === "_commenting-text-with-format"); 34 | }); 35 | 36 | it("has an annotation page", async () => { 37 | annotationPage = canvasJson.items[0]; 38 | assert(annotationPage); 39 | }); 40 | 41 | it("has the correct annotation page id", async () => { 42 | annotationPage = canvasJson.items[0]; 43 | assert( 44 | annotationPage.id === 45 | customAnnotationsManifestUrl + "/index.json/canvas/0/annotationpage/0" 46 | ); 47 | }); 48 | 49 | it("has annotation", async () => { 50 | annotation = annotationPage.items[0]; 51 | assert(annotation); 52 | }); 53 | 54 | it("has correct annotation id", async () => { 55 | assert( 56 | annotation.id === 57 | customAnnotationsManifestUrl + "/index.json/canvas/0/annotation/0" 58 | ); 59 | }); 60 | 61 | it("has correct annotation motivation", async () => { 62 | assert(annotation.motivation === "commenting"); 63 | }); 64 | 65 | it("has correct annotation target", async () => { 66 | assert( 67 | annotation.target === 68 | customAnnotationsManifestUrl + "/index.json/canvas/0" 69 | ); 70 | }); 71 | 72 | it("has an annotation body", async () => { 73 | annotationBody = annotation.body; 74 | assert(annotationBody); 75 | }); 76 | 77 | it("has correct annotation body id", async () => { 78 | assert( 79 | annotationBody.id === 80 | customAnnotationsManifestUrl + 81 | "/index.json/annotations/commenting-text-with-format" 82 | ); 83 | }); 84 | 85 | it("has correct annotation body type", async () => { 86 | assert(annotationBody.type === "TextualBody"); 87 | }); 88 | 89 | it("has correct annotation body format", async () => { 90 | assert(annotationBody.format === "text/plain"); 91 | }); 92 | 93 | it("has correct annotation body value", async () => { 94 | assert(annotationBody.value === "This is a comment on the image"); 95 | }); 96 | }); 97 | 98 | describe("commenting text with type", async () => { 99 | it("can find canvas", async () => { 100 | canvasJson = manifestJson.items[1]; 101 | assert(canvasJson); 102 | }); 103 | 104 | it("has correct canvas id", async () => { 105 | assert( 106 | canvasJson.id === customAnnotationsManifestUrl + "/index.json/canvas/1" 107 | ); 108 | }); 109 | 110 | it("has correct canvas label", async () => { 111 | assert(canvasJson.label["@none"][0] === "_commenting-text-with-type"); 112 | }); 113 | 114 | it("has an annotation page", async () => { 115 | annotationPage = canvasJson.items[0]; 116 | assert(annotationPage); 117 | }); 118 | 119 | it("has the correct annotation page id", async () => { 120 | annotationPage = canvasJson.items[0]; 121 | assert( 122 | annotationPage.id === 123 | customAnnotationsManifestUrl + "/index.json/canvas/1/annotationpage/0" 124 | ); 125 | }); 126 | 127 | it("has annotation", async () => { 128 | annotation = annotationPage.items[0]; 129 | assert(annotation); 130 | }); 131 | 132 | it("has correct annotation id", async () => { 133 | assert( 134 | annotation.id === 135 | customAnnotationsManifestUrl + "/index.json/canvas/1/annotation/0" 136 | ); 137 | }); 138 | 139 | it("has correct annotation motivation", async () => { 140 | assert(annotation.motivation === "commenting"); 141 | }); 142 | 143 | it("has correct annotation target", async () => { 144 | assert( 145 | annotation.target === 146 | customAnnotationsManifestUrl + "/index.json/canvas/1" 147 | ); 148 | }); 149 | 150 | it("has an annotation body", async () => { 151 | annotationBody = annotation.body; 152 | assert(annotationBody); 153 | }); 154 | 155 | it("has correct annotation body id", async () => { 156 | assert( 157 | annotationBody.id === 158 | customAnnotationsManifestUrl + 159 | "/index.json/annotations/commenting-text-with-type" 160 | ); 161 | }); 162 | 163 | it("has correct annotation body type", async () => { 164 | assert(annotationBody.type === "TextualBody"); 165 | }); 166 | 167 | it("has correct annotation body format", async () => { 168 | assert(annotationBody.format === "text/plain"); 169 | }); 170 | 171 | it("has correct annotation body value", async () => { 172 | assert(annotationBody.value === "This is a comment on the image"); 173 | }); 174 | }); 175 | 176 | describe("commenting text without type and format", async () => { 177 | it("can find canvas", async () => { 178 | canvasJson = manifestJson.items[2]; 179 | assert(canvasJson); 180 | }); 181 | 182 | it("has correct canvas id", async () => { 183 | assert( 184 | canvasJson.id === customAnnotationsManifestUrl + "/index.json/canvas/2" 185 | ); 186 | }); 187 | 188 | it("has correct canvas label", async () => { 189 | assert( 190 | canvasJson.label["@none"][0] === "_commenting-text-without-type-format" 191 | ); 192 | }); 193 | 194 | it("has an annotation page", async () => { 195 | annotationPage = canvasJson.items[0]; 196 | assert(annotationPage); 197 | }); 198 | 199 | it("has the correct annotation page id", async () => { 200 | annotationPage = canvasJson.items[0]; 201 | assert( 202 | annotationPage.id === 203 | customAnnotationsManifestUrl + "/index.json/canvas/2/annotationpage/0" 204 | ); 205 | }); 206 | 207 | it("has annotation", async () => { 208 | annotation = annotationPage.items[0]; 209 | assert(annotation); 210 | }); 211 | 212 | it("has correct annotation id", async () => { 213 | assert( 214 | annotation.id === 215 | customAnnotationsManifestUrl + "/index.json/canvas/2/annotation/0" 216 | ); 217 | }); 218 | 219 | it("has correct annotation motivation", async () => { 220 | assert(annotation.motivation === "commenting"); 221 | }); 222 | 223 | it("has correct annotation target", async () => { 224 | assert( 225 | annotation.target === 226 | customAnnotationsManifestUrl + "/index.json/canvas/2" 227 | ); 228 | }); 229 | 230 | it("has an annotation body", async () => { 231 | annotationBody = annotation.body; 232 | assert(annotationBody); 233 | }); 234 | 235 | it("has correct annotation body id", async () => { 236 | assert( 237 | annotationBody.id === 238 | customAnnotationsManifestUrl + 239 | "/index.json/annotations/commenting-text-without-type-format" 240 | ); 241 | }); 242 | 243 | it("has no annotation body type", async () => { 244 | assert(annotationBody.type === undefined); 245 | }); 246 | 247 | it("has no annotation body format", async () => { 248 | assert(annotationBody.format === undefined); 249 | }); 250 | 251 | it("has correct annotation body value", async () => { 252 | assert(annotationBody.value === "This is a comment on the image"); 253 | }); 254 | }); 255 | /* 256 | describe('json value with format', async () => { 257 | 258 | it('can find canvas', async () => { 259 | canvasJson = manifestJson.items[3]; 260 | assert(canvasJson); 261 | }); 262 | 263 | it('has correct canvas id', async () => { 264 | assert(canvasJson.id === customAnnotationsManifestUrl + '/index.json/canvas/3'); 265 | }); 266 | 267 | it('has correct canvas label', async () => { 268 | assert(canvasJson.label['@none'][0] === '_json-value-with-format'); 269 | }); 270 | 271 | it('has an annotation page', async () => { 272 | annotationPage = canvasJson.items[0]; 273 | assert(annotationPage); 274 | }); 275 | 276 | it('has the correct annotation page id', async () => { 277 | annotationPage = canvasJson.items[0]; 278 | assert(annotationPage.id === customAnnotationsManifestUrl + '/index.json/canvas/3/annotationpage/0'); 279 | }); 280 | 281 | it('has annotation', async () => { 282 | annotation = annotationPage.items[0]; 283 | assert(annotation); 284 | }); 285 | 286 | it('has only one annotation', async () => { 287 | assert(annotationPage.items.length === 1); 288 | }); 289 | 290 | it('has correct annotation id', async () => { 291 | assert(annotation.id === customAnnotationsManifestUrl + '/index.json/canvas/3/annotation/0'); 292 | }); 293 | 294 | it('has correct annotation motivation', async () => { 295 | assert(annotation.motivation === 'data'); 296 | }); 297 | 298 | it('has correct annotation target', async () => { 299 | assert(annotation.target === customAnnotationsManifestUrl + '/index.json/canvas/3'); 300 | }); 301 | 302 | it('has an annotation body', async () => { 303 | annotationBody = annotation.body; 304 | assert(annotationBody); 305 | }); 306 | 307 | it('has correct annotation body id', async () => { 308 | assert(annotationBody.id === customAnnotationsManifestUrl + '/_json-value-with-format/assets/data.json'); 309 | }); 310 | 311 | it('has correct annotation body type', async () => { 312 | assert(annotationBody.type === undefined); 313 | }); 314 | 315 | it('has correct annotation body format', async () => { 316 | assert(annotationBody.format === 'application/json'); 317 | }); 318 | 319 | it('has no annotation body value', async () => { 320 | assert(annotationBody.value === undefined); 321 | }); 322 | 323 | }); 324 | 325 | describe('json value without format', async () => { 326 | 327 | it('can find canvas', async () => { 328 | canvasJson = manifestJson.items[4]; 329 | assert(canvasJson); 330 | }); 331 | 332 | it('has correct canvas id', async () => { 333 | assert(canvasJson.id === customAnnotationsManifestUrl + '/index.json/canvas/4'); 334 | }); 335 | 336 | it('has correct canvas label', async () => { 337 | assert(canvasJson.label['@none'][0] === '_json-value-without-format'); 338 | }); 339 | 340 | it('has an annotation page', async () => { 341 | annotationPage = canvasJson.items[0]; 342 | assert(annotationPage); 343 | }); 344 | 345 | it('has the correct annotation page id', async () => { 346 | annotationPage = canvasJson.items[0]; 347 | assert(annotationPage.id === customAnnotationsManifestUrl + '/index.json/canvas/4/annotationpage/0'); 348 | }); 349 | 350 | it('has annotation', async () => { 351 | annotation = annotationPage.items[0]; 352 | assert(annotation); 353 | }); 354 | 355 | it('has only one annotation', async () => { 356 | assert(annotationPage.items.length === 1); 357 | }); 358 | 359 | it('has correct annotation id', async () => { 360 | assert(annotation.id === customAnnotationsManifestUrl + '/index.json/canvas/4/annotation/0'); 361 | }); 362 | 363 | it('has correct annotation motivation', async () => { 364 | assert(annotation.motivation === 'data'); 365 | }); 366 | 367 | it('has correct annotation target', async () => { 368 | assert(annotation.target === customAnnotationsManifestUrl + '/index.json/canvas/4'); 369 | }); 370 | 371 | it('has an annotation body', async () => { 372 | annotationBody = annotation.body; 373 | assert(annotationBody); 374 | }); 375 | 376 | it('has correct annotation body id', async () => { 377 | assert(annotationBody.id === customAnnotationsManifestUrl + '/_json-value-without-format/assets/data.json'); 378 | }); 379 | 380 | it('has correct annotation body type', async () => { 381 | assert(annotationBody.type === undefined); 382 | }); 383 | 384 | it('has no annotation body format', async () => { 385 | assert(annotationBody.format === undefined); 386 | }); 387 | 388 | it('has no annotation body value', async () => { 389 | assert(annotationBody.value === undefined); 390 | }); 391 | 392 | }); 393 | 394 | describe('json value without motivation, type, or format', async () => { 395 | 396 | it('can find canvas', async () => { 397 | canvasJson = manifestJson.items[5]; 398 | assert(canvasJson); 399 | }); 400 | 401 | it('has correct canvas id', async () => { 402 | assert(canvasJson.id === customAnnotationsManifestUrl + '/index.json/canvas/5'); 403 | }); 404 | 405 | it('has correct canvas label', async () => { 406 | assert(canvasJson.label['@none'][0] === '_json-value-without-motivation-type-format'); 407 | }); 408 | 409 | it('has an annotation page', async () => { 410 | annotationPage = canvasJson.items[0]; 411 | assert(annotationPage); 412 | }); 413 | 414 | it('has the correct annotation page id', async () => { 415 | annotationPage = canvasJson.items[0]; 416 | assert(annotationPage.id === customAnnotationsManifestUrl + '/index.json/canvas/5/annotationpage/0'); 417 | }); 418 | 419 | it('has annotation', async () => { 420 | annotation = annotationPage.items[0]; 421 | assert(annotation); 422 | }); 423 | 424 | it('has only one annotation', async () => { 425 | assert(annotationPage.items.length === 1); 426 | }); 427 | 428 | it('has correct annotation id', async () => { 429 | assert(annotation.id === customAnnotationsManifestUrl + '/index.json/canvas/5/annotation/0'); 430 | }); 431 | 432 | it('has correct annotation motivation', async () => { 433 | assert(annotation.motivation === 'painting'); 434 | }); 435 | 436 | it('has correct annotation target', async () => { 437 | assert(annotation.target === customAnnotationsManifestUrl + '/index.json/canvas/5'); 438 | }); 439 | 440 | it('has an annotation body', async () => { 441 | annotationBody = annotation.body; 442 | assert(annotationBody); 443 | }); 444 | 445 | it('has correct annotation body id', async () => { 446 | assert(annotationBody.id === customAnnotationsManifestUrl + '/_json-value-without-motivation-type-format/assets/file.json'); 447 | }); 448 | 449 | it('has correct annotation body type', async () => { 450 | assert(annotationBody.type === 'Text'); 451 | }); 452 | 453 | it('has correct annotation body format', async () => { 454 | assert(annotationBody.format === 'application/json'); 455 | }); 456 | 457 | it('has no annotation body value', async () => { 458 | assert(annotationBody.value === undefined); 459 | }); 460 | 461 | }); 462 | 463 | describe('painting gltf', async () => { 464 | 465 | it('can find canvas', async () => { 466 | canvasJson = manifestJson.items[6]; 467 | assert(canvasJson); 468 | }); 469 | 470 | it('has correct canvas id', async () => { 471 | assert(canvasJson.id === customAnnotationsManifestUrl + '/index.json/canvas/6'); 472 | }); 473 | 474 | it('has correct canvas label', async () => { 475 | assert(canvasJson.label['@none'][0] === '_painting-gltf'); 476 | }); 477 | 478 | it('has an annotation page', async () => { 479 | annotationPage = canvasJson.items[0]; 480 | assert(annotationPage); 481 | }); 482 | 483 | it('has the correct annotation page id', async () => { 484 | annotationPage = canvasJson.items[0]; 485 | assert(annotationPage.id === customAnnotationsManifestUrl + '/index.json/canvas/6/annotationpage/0'); 486 | }); 487 | 488 | it('has annotation', async () => { 489 | annotation = annotationPage.items[0]; 490 | assert(annotation); 491 | }); 492 | 493 | it('has only one annotation', async () => { 494 | assert(annotationPage.items.length === 1); 495 | }); 496 | 497 | it('has correct annotation id', async () => { 498 | assert(annotation.id === customAnnotationsManifestUrl + '/index.json/canvas/6/annotation/0'); 499 | }); 500 | 501 | it('has correct annotation motivation', async () => { 502 | assert(annotation.motivation === 'painting'); 503 | }); 504 | 505 | it('has correct annotation target', async () => { 506 | assert(annotation.target === customAnnotationsManifestUrl + '/index.json/canvas/6'); 507 | }); 508 | 509 | it('has an annotation body', async () => { 510 | annotationBody = annotation.body; 511 | assert(annotationBody); 512 | }); 513 | 514 | it('has correct annotation body id', async () => { 515 | assert(annotationBody.id === customAnnotationsManifestUrl + '/_painting-gltf/assets/file.gltf'); 516 | }); 517 | 518 | it('has correct annotation body type', async () => { 519 | assert(annotationBody.type === 'PhysicalObject'); 520 | }); 521 | 522 | it('has correct annotation body format', async () => { 523 | assert(annotationBody.format === 'model/gltf+json'); 524 | }); 525 | 526 | it('has no annotation body value', async () => { 527 | assert(annotationBody.value === undefined); 528 | }); 529 | 530 | }); 531 | 532 | describe('painting jpg', async () => { 533 | 534 | it('can find canvas', async () => { 535 | canvasJson = manifestJson.items[7]; 536 | assert(canvasJson); 537 | }); 538 | 539 | it('has correct canvas id', async () => { 540 | assert(canvasJson.id === customAnnotationsManifestUrl + '/index.json/canvas/7'); 541 | }); 542 | 543 | it('has correct canvas label', async () => { 544 | assert(canvasJson.label['@none'][0] === '_painting-jpg'); 545 | }); 546 | 547 | it('has an annotation page', async () => { 548 | annotationPage = canvasJson.items[0]; 549 | assert(annotationPage); 550 | }); 551 | 552 | it('has the correct annotation page id', async () => { 553 | annotationPage = canvasJson.items[0]; 554 | assert(annotationPage.id === customAnnotationsManifestUrl + '/index.json/canvas/7/annotationpage/0'); 555 | }); 556 | 557 | it('has annotation', async () => { 558 | annotation = annotationPage.items[0]; 559 | assert(annotation); 560 | }); 561 | 562 | it('has only one annotation', async () => { 563 | assert(annotationPage.items.length === 1); 564 | }); 565 | 566 | it('has correct annotation id', async () => { 567 | assert(annotation.id === customAnnotationsManifestUrl + '/index.json/canvas/7/annotation/0'); 568 | }); 569 | 570 | it('has correct annotation motivation', async () => { 571 | assert(annotation.motivation === 'painting'); 572 | }); 573 | 574 | it('has correct annotation target', async () => { 575 | assert(annotation.target === customAnnotationsManifestUrl + '/index.json/canvas/7'); 576 | }); 577 | 578 | it('has an annotation body', async () => { 579 | annotationBody = annotation.body; 580 | assert(annotationBody); 581 | }); 582 | 583 | it('has correct annotation body id', async () => { 584 | assert(annotationBody.id === customAnnotationsManifestUrl + '/_painting-jpg/assets/file.jpg'); 585 | }); 586 | 587 | it('has correct annotation body type', async () => { 588 | assert(annotationBody.type === 'Image'); 589 | }); 590 | 591 | it('has no annotation body value', async () => { 592 | assert(annotationBody.value === undefined); 593 | }); 594 | 595 | }); 596 | 597 | describe('painting three.js json with type', async () => { 598 | 599 | it('can find canvas', async () => { 600 | canvasJson = manifestJson.items[8]; 601 | assert(canvasJson); 602 | }); 603 | 604 | it('has correct canvas id', async () => { 605 | assert(canvasJson.id === customAnnotationsManifestUrl + '/index.json/canvas/8'); 606 | }); 607 | 608 | it('has correct canvas label', async () => { 609 | assert(canvasJson.label['@none'][0] === '_painting-threejs-json-with-type'); 610 | }); 611 | 612 | it('has an annotation page', async () => { 613 | annotationPage = canvasJson.items[0]; 614 | assert(annotationPage); 615 | }); 616 | 617 | it('has the correct annotation page id', async () => { 618 | annotationPage = canvasJson.items[0]; 619 | assert(annotationPage.id === customAnnotationsManifestUrl + '/index.json/canvas/8/annotationpage/0'); 620 | }); 621 | 622 | it('has annotation', async () => { 623 | annotation = annotationPage.items[0]; 624 | assert(annotation); 625 | }); 626 | 627 | it('has only one annotation', async () => { 628 | assert(annotationPage.items.length === 1); 629 | }); 630 | 631 | it('has correct annotation id', async () => { 632 | assert(annotation.id === customAnnotationsManifestUrl + '/index.json/canvas/8/annotation/0'); 633 | }); 634 | 635 | it('has correct annotation motivation', async () => { 636 | assert(annotation.motivation === 'painting'); 637 | }); 638 | 639 | it('has correct annotation target', async () => { 640 | assert(annotation.target === customAnnotationsManifestUrl + '/index.json/canvas/8'); 641 | }); 642 | 643 | it('has an annotation body', async () => { 644 | annotationBody = annotation.body; 645 | assert(annotationBody); 646 | }); 647 | 648 | it('has correct annotation body id', async () => { 649 | assert(annotationBody.id === customAnnotationsManifestUrl + '/_painting-threejs-json-with-type/assets/file.json'); 650 | }); 651 | 652 | it('has correct annotation body type', async () => { 653 | assert(annotationBody.type === 'PhysicalObject'); 654 | }); 655 | 656 | it('has correct annotation body format', async () => { 657 | assert(annotationBody.format === 'application/vnd.threejs+json'); 658 | }); 659 | 660 | it('has no annotation body value', async () => { 661 | assert(annotationBody.value === undefined); 662 | }); 663 | 664 | }); 665 | */ 666 | -------------------------------------------------------------------------------- /test/tests/dat-gateway.js: -------------------------------------------------------------------------------- 1 | const { assert, build, urljoin } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let collectionJson, thumbnailJson, item, manifestJson; 5 | const collection = "/collection"; 6 | const collectionUrl = 7 | "http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772"; 8 | 9 | it("can build collection", async () => { 10 | assert(await fileExists(collection)); 11 | return build( 12 | collection, 13 | collectionUrl, 14 | false, 15 | "0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772" 16 | ); 17 | }).timeout(1000); // should take less than a second 18 | 19 | it("can find collection index.json", async () => { 20 | const file = "/collection/index.json"; 21 | assert(await fileExists(file)); 22 | collectionJson = await readJson(file); 23 | }); 24 | 25 | it("has correct collection id", async () => { 26 | assert(collectionJson.id === collectionUrl + "/index.json"); 27 | }); 28 | 29 | it("has correct collection label", async () => { 30 | assert(collectionJson.label["@none"][0] === "My Test Collection"); 31 | }); 32 | 33 | it("has a collection thumbnail", async () => { 34 | thumbnailJson = collectionJson.thumbnail[0]; 35 | assert(thumbnailJson); 36 | }); 37 | 38 | it("has the correct collection thumbnail id", async () => { 39 | const id = urljoin(collectionUrl, "thumb.png"); 40 | assert(thumbnailJson.id === id); 41 | }); 42 | 43 | it("has correct number of items", async () => { 44 | assert(collectionJson.items.length === 5); 45 | }); 46 | 47 | it("has an item manifest", async () => { 48 | item = collectionJson.items[0]; 49 | assert(item); 50 | }); 51 | 52 | it("has correct item id", async () => { 53 | assert(item.id === collectionUrl + "/a_manifest/index.json"); 54 | }); 55 | 56 | it("has correct item label", async () => { 57 | assert(item.label["@none"][0] === "A Manifest"); 58 | }); 59 | 60 | it("has item thumbnail", async () => { 61 | thumbnailJson = item.thumbnail; 62 | assert(thumbnailJson); 63 | }); 64 | 65 | it("has a linked item manifest", async () => { 66 | item = collectionJson.items[1]; 67 | assert(item); 68 | }); 69 | 70 | it("has correct linked item id", async () => { 71 | assert(item.id === "http://test.com/collection/linkedmanifest1/index.json"); 72 | }); 73 | 74 | it("has correct linked item label", async () => { 75 | assert(item.label["@none"][0] === "Linked Manifest 1"); 76 | }); 77 | 78 | it("has linked item thumbnail", async () => { 79 | thumbnailJson = item.thumbnail; 80 | assert(thumbnailJson); 81 | }); 82 | 83 | it("has correct linked item thumbnail id", async () => { 84 | assert( 85 | thumbnailJson[0].id === 86 | "http://test.com/collection/linkedmanifest1/thumb.jpg" 87 | ); 88 | }); 89 | 90 | it("has a linked item manifest", async () => { 91 | item = collectionJson.items[3]; 92 | assert(item); 93 | }); 94 | 95 | it("has correct linked item id", async () => { 96 | assert(item.id === "http://test.com/collection/linkedmanifest3/index.json"); 97 | }); 98 | 99 | it("has correct linked item label", async () => { 100 | assert(item.label["@none"][0] === "linkedmanifest3"); 101 | }); 102 | 103 | it("can find manifest index.json", async () => { 104 | const file = "/collection/a_manifest/index.json"; 105 | assert(await fileExists(file)); 106 | manifestJson = await readJson(file); 107 | }); 108 | 109 | it("has correct manifest id", async () => { 110 | assert(manifestJson.id === collectionUrl + "/a_manifest/index.json"); 111 | }); 112 | 113 | it("has correct manifest label", async () => { 114 | assert(manifestJson.label["@none"][0] === "A Manifest"); 115 | }); 116 | 117 | it("can find canvas", async () => { 118 | canvasJson = manifestJson.items[0]; 119 | assert(canvasJson); 120 | }); 121 | 122 | it("has correct canvas id", async () => { 123 | assert(canvasJson.id === collectionUrl + "/a_manifest/index.json/canvas/0"); 124 | }); 125 | 126 | it("has correct canvas label", async () => { 127 | assert(canvasJson.label["@none"][0] === "A Canvas"); 128 | }); 129 | 130 | it("has a canvas thumbnail", async () => { 131 | thumbnailJson = canvasJson.thumbnail[0]; 132 | assert(thumbnailJson); 133 | }); 134 | 135 | it("has the correct canvas thumbnail id", async () => { 136 | const id = urljoin(collectionUrl, "/a_manifest/_canvas/thumb.png"); 137 | assert(thumbnailJson.id === id); 138 | }); 139 | 140 | it("has an annotation page", async () => { 141 | annotationPage = canvasJson.items[0]; 142 | assert(annotationPage); 143 | }); 144 | 145 | it("has the correct annotation page id", async () => { 146 | annotationPage = canvasJson.items[0]; 147 | assert( 148 | annotationPage.id === 149 | collectionUrl + "/a_manifest/index.json/canvas/0/annotationpage/0" 150 | ); 151 | }); 152 | 153 | it("has an annotation", async () => { 154 | annotation = annotationPage.items[0]; 155 | assert(annotation); 156 | }); 157 | 158 | it("has an image annotation body", async () => { 159 | imageAnnotation = annotation.body; 160 | assert(imageAnnotation); 161 | }); 162 | 163 | it("has an annotation body", async () => { 164 | imageAnnotation = annotation.body; 165 | assert(imageAnnotation); 166 | }); 167 | 168 | it("has correct annotation id", async () => { 169 | assert( 170 | imageAnnotation.id === collectionUrl + "/a_manifest/_canvas/page_1.jpg" 171 | ); 172 | }); 173 | 174 | describe("sub-collection", async () => { 175 | it("can find collection index.json", async () => { 176 | const file = "/collection/sub-collection/index.json"; 177 | assert(await fileExists(file)); 178 | collectionJson = await readJson(file); 179 | }); 180 | 181 | it("has correct collection id", async () => { 182 | assert(collectionJson.id === collectionUrl + "/sub-collection/index.json"); 183 | }); 184 | 185 | it("has correct collection label", async () => { 186 | assert(collectionJson.label["@none"][0] === "My Test Sub-collection"); 187 | }); 188 | 189 | it("has a collection thumbnail", async () => { 190 | thumbnailJson = collectionJson.thumbnail[0]; 191 | assert(thumbnailJson); 192 | }); 193 | 194 | it("has the correct collection thumbnail id", async () => { 195 | const id = urljoin(collectionUrl, "/sub-collection/thumb.png"); 196 | assert(thumbnailJson.id === id); 197 | }); 198 | 199 | it("has a item manifest", async () => { 200 | item = collectionJson.items[0]; 201 | assert(item); 202 | }); 203 | 204 | it("has correct item id", async () => { 205 | assert(item.id === collectionUrl + "/sub-collection/b_manifest/index.json"); 206 | }); 207 | 208 | it("has correct item label", async () => { 209 | assert(item.label["@none"][0] === "My Test Submanifest"); 210 | }); 211 | 212 | it("has item thumbnail", async () => { 213 | thumbnailJson = item.thumbnail; 214 | assert(thumbnailJson); 215 | }); 216 | 217 | it("has correct item thumbnail id", async () => { 218 | assert( 219 | thumbnailJson[0].id === 220 | collectionUrl + "/sub-collection/b_manifest/thumb.png" 221 | ); 222 | }); 223 | 224 | it("can find manifest index.json", async () => { 225 | const file = "/collection/sub-collection/b_manifest/index.json"; 226 | assert(await fileExists(file)); 227 | manifestJson = await readJson(file); 228 | }); 229 | 230 | it("has correct manifest id", async () => { 231 | assert( 232 | manifestJson.id === collectionUrl + "/sub-collection/b_manifest/index.json" 233 | ); 234 | }); 235 | 236 | it("has correct manifest label", async () => { 237 | assert(manifestJson.label["@none"][0] === "My Test Submanifest"); 238 | }); 239 | 240 | it("can find canvas", async () => { 241 | canvasJson = manifestJson.items[0]; 242 | assert(canvasJson); 243 | }); 244 | 245 | it("has correct canvas id", async () => { 246 | assert( 247 | canvasJson.id === 248 | collectionUrl + "/sub-collection/b_manifest/index.json/canvas/0" 249 | ); 250 | }); 251 | 252 | it("has correct canvas label", async () => { 253 | assert(canvasJson.label["@none"][0] === "My Test Subcanvas"); 254 | }); 255 | 256 | it("has a canvas thumbnail", async () => { 257 | thumbnailJson = canvasJson.thumbnail[0]; 258 | assert(thumbnailJson); 259 | }); 260 | 261 | it("has the correct canvas thumbnail id", async () => { 262 | const id = urljoin( 263 | collectionUrl, 264 | "/sub-collection/b_manifest/_canvas/thumb.png" 265 | ); 266 | assert(thumbnailJson.id === id); 267 | }); 268 | 269 | it("has an annotation page", async () => { 270 | annotationPage = canvasJson.items[0]; 271 | assert(annotationPage); 272 | }); 273 | 274 | it("has the correct annotation page id", async () => { 275 | annotationPage = canvasJson.items[0]; 276 | assert( 277 | annotationPage.id === 278 | collectionUrl + 279 | "/sub-collection/b_manifest/index.json/canvas/0/annotationpage/0" 280 | ); 281 | }); 282 | 283 | it("has an annotation", async () => { 284 | annotation = annotationPage.items[0]; 285 | assert(annotation); 286 | }); 287 | 288 | it("has an image annotation body", async () => { 289 | imageAnnotation = annotation.body; 290 | assert(imageAnnotation); 291 | }); 292 | 293 | it("has an annotation body", async () => { 294 | imageAnnotation = annotation.body; 295 | assert(imageAnnotation); 296 | }); 297 | 298 | it("image annotation has correct id", async () => { 299 | assert( 300 | imageAnnotation.id === 301 | collectionUrl + "/sub-collection/b_manifest/_canvas/page_1.jpg" 302 | ); 303 | }); 304 | }); 305 | -------------------------------------------------------------------------------- /test/tests/do-promises-work.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists } = require("../../Utils"); 3 | 4 | const manifest = "/files-only-manifest"; 5 | const manifestUrl = "http://test.com/files-only-manifest"; 6 | 7 | it("can build manifest", async () => { 8 | assert(await fileExists(manifest)); 9 | return build(manifest, manifestUrl); 10 | }).timeout(1000); // should take less than a second 11 | 12 | it("happens after build", async () => { 13 | console.log("I should happen after build"); 14 | }); 15 | -------------------------------------------------------------------------------- /test/tests/epub-collection.js: -------------------------------------------------------------------------------- 1 | const { assert, build, urljoin } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | const collection = "/epub-collection"; 5 | const collectionUrl = "http://test.com/epub-collection"; 6 | 7 | it("can build epub collection", async () => { 8 | assert(await fileExists(collection)); 9 | return build(collection, collectionUrl); 10 | }).timeout(1000); // should take less than a second 11 | 12 | it("can find collection index.json", async () => { 13 | const file = "/epub-collection/index.json"; 14 | assert(await fileExists(file)); 15 | collectionJson = await readJson(file); 16 | }); 17 | 18 | describe("painting opf", async () => { 19 | let manifest = "/alice-in-wonderland"; 20 | let manifestJson, canvases; 21 | 22 | it("can find " + manifest + " index.json", async () => { 23 | const file = urljoin(collection, manifest, "index.json"); 24 | assert(await fileExists(file)); 25 | manifestJson = await readJson(file); 26 | canvases = manifestJson.items; 27 | assert(canvases.length === 1); 28 | }); 29 | 30 | it("can find canvas", async () => { 31 | canvasJson = manifestJson.items[0]; 32 | assert(canvasJson); 33 | }); 34 | 35 | it("has correct canvas id", async () => { 36 | assert(canvasJson.id === collectionUrl + manifest + "/index.json/canvas/0"); 37 | }); 38 | 39 | it("has correct canvas label", async () => { 40 | assert(canvasJson.label["@none"][0] === "_alice-in-wonderland"); 41 | }); 42 | 43 | it("has an annotation page", async () => { 44 | annotationPage = canvasJson.items[0]; 45 | assert(annotationPage); 46 | }); 47 | 48 | it("has the correct annotation page id", async () => { 49 | annotationPage = canvasJson.items[0]; 50 | assert( 51 | annotationPage.id === 52 | collectionUrl + manifest + "/index.json/canvas/0/annotationpage/0" 53 | ); 54 | }); 55 | 56 | it("has annotation", async () => { 57 | annotation = annotationPage.items[0]; 58 | assert(annotation); 59 | }); 60 | 61 | it("has correct annotation id", async () => { 62 | assert( 63 | annotation.id === 64 | collectionUrl + manifest + "/index.json/canvas/0/annotation/0" 65 | ); 66 | }); 67 | 68 | it("has correct annotation motivation", async () => { 69 | assert(annotation.motivation === "painting"); 70 | }); 71 | 72 | it("has correct annotation target", async () => { 73 | assert( 74 | annotation.target === collectionUrl + manifest + "/index.json/canvas/0" 75 | ); 76 | }); 77 | 78 | it("has an annotation body", async () => { 79 | annotationBody = annotation.body; 80 | assert(annotationBody); 81 | }); 82 | 83 | it("has correct annotation body id", async () => { 84 | assert( 85 | annotationBody.id === 86 | "https://s3.amazonaws.com/epubjs/books/alice/OPS/package.opf" 87 | ); 88 | }); 89 | 90 | it("has correct annotation body type", async () => { 91 | assert(annotationBody.type === "Text"); 92 | }); 93 | 94 | it("has correct annotation body format", async () => { 95 | assert(annotationBody.format === "application/oebps-package+xml"); 96 | }); 97 | }); 98 | 99 | describe("painting epub", async () => { 100 | let manifest = "/cc-shared-culture"; 101 | let manifestJson, canvases; 102 | 103 | it("can find " + manifest + " index.json", async () => { 104 | const file = urljoin(collection, manifest, "index.json"); 105 | assert(await fileExists(file)); 106 | manifestJson = await readJson(file); 107 | canvases = manifestJson.items; 108 | assert(canvases.length === 1); 109 | }); 110 | 111 | it("can find canvas", async () => { 112 | canvasJson = manifestJson.items[0]; 113 | assert(canvasJson); 114 | }); 115 | 116 | it("has correct canvas id", async () => { 117 | assert(canvasJson.id === collectionUrl + manifest + "/index.json/canvas/0"); 118 | }); 119 | 120 | it("has correct canvas label", async () => { 121 | assert(canvasJson.label["@none"][0] === "_cc-shared-culture"); 122 | }); 123 | 124 | it("has an annotation page", async () => { 125 | annotationPage = canvasJson.items[0]; 126 | assert(annotationPage); 127 | }); 128 | 129 | it("has the correct annotation page id", async () => { 130 | annotationPage = canvasJson.items[0]; 131 | assert( 132 | annotationPage.id === 133 | collectionUrl + manifest + "/index.json/canvas/0/annotationpage/0" 134 | ); 135 | }); 136 | 137 | it("has annotation", async () => { 138 | annotation = annotationPage.items[0]; 139 | assert(annotation); 140 | }); 141 | 142 | it("has correct annotation id", async () => { 143 | assert( 144 | annotation.id === 145 | collectionUrl + manifest + "/index.json/canvas/0/annotation/0" 146 | ); 147 | }); 148 | 149 | it("has correct annotation motivation", async () => { 150 | assert(annotation.motivation === "painting"); 151 | }); 152 | 153 | it("has correct annotation target", async () => { 154 | assert( 155 | annotation.target === collectionUrl + manifest + "/index.json/canvas/0" 156 | ); 157 | }); 158 | 159 | it("has an annotation body", async () => { 160 | annotationBody = annotation.body; 161 | assert(annotationBody); 162 | }); 163 | 164 | it("has correct annotation body id", async () => { 165 | assert( 166 | annotationBody.id === 167 | "http://test.com/epub-collection/cc-shared-culture/_cc-shared-culture/cc-shared-culture.epub" 168 | ); 169 | }); 170 | 171 | it("has correct annotation body type", async () => { 172 | assert(annotationBody.type === "Text"); 173 | }); 174 | 175 | it("has correct annotation body format", async () => { 176 | assert(annotationBody.format === "application/epub+zip"); 177 | }); 178 | }); 179 | -------------------------------------------------------------------------------- /test/tests/external-resource-annotation-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, annotation, annotationPage, annotationBody; 5 | const manifest = "/external-resource-annotation-manifest"; 6 | const customAnnotationsManifestUrl = 7 | "http://test.com/external-resource-annotation-manifest"; 8 | 9 | it("can build custom annotations collection", async () => { 10 | assert(await fileExists(manifest)); 11 | return build(manifest, customAnnotationsManifestUrl); 12 | }).timeout(1000); // should take less than a second 13 | 14 | it("can find manifest index.json", async () => { 15 | const file = "/external-resource-annotation-manifest/index.json"; 16 | assert(await fileExists(file)); 17 | manifestJson = await readJson(file); 18 | }); 19 | 20 | describe("commenting text with format", async () => { 21 | it("can find canvas", async () => { 22 | canvasJson = manifestJson.items[0]; 23 | assert(canvasJson); 24 | }); 25 | 26 | it("has correct canvas id", async () => { 27 | assert( 28 | canvasJson.id === customAnnotationsManifestUrl + "/index.json/canvas/0" 29 | ); 30 | }); 31 | 32 | it("has correct canvas label", async () => { 33 | assert(canvasJson.label["@none"][0] === "_platypus"); 34 | }); 35 | 36 | it("has an annotation page", async () => { 37 | annotationPage = canvasJson.items[0]; 38 | assert(annotationPage); 39 | }); 40 | 41 | it("has the correct annotation page id", async () => { 42 | annotationPage = canvasJson.items[0]; 43 | assert( 44 | annotationPage.id === 45 | customAnnotationsManifestUrl + "/index.json/canvas/0/annotationpage/0" 46 | ); 47 | }); 48 | 49 | it("has annotation", async () => { 50 | annotation = annotationPage.items[0]; 51 | assert(annotation); 52 | }); 53 | 54 | it("has correct annotation id", async () => { 55 | assert( 56 | annotation.id === 57 | customAnnotationsManifestUrl + "/index.json/canvas/0/annotation/0" 58 | ); 59 | }); 60 | 61 | it("has correct annotation motivation", async () => { 62 | assert(annotation.motivation === "painting"); 63 | }); 64 | 65 | it("has correct annotation target", async () => { 66 | assert( 67 | annotation.target === 68 | customAnnotationsManifestUrl + "/index.json/canvas/0" 69 | ); 70 | }); 71 | 72 | it("has an annotation body", async () => { 73 | annotationBody = annotation.body; 74 | assert(annotationBody); 75 | }); 76 | 77 | it("has correct annotation body id", async () => { 78 | assert( 79 | annotationBody.id === 80 | "https://www.morphosource.org/media/morphosource/dcm_sample/platypus/platypus_manifest_20_slices.json" 81 | ); 82 | }); 83 | 84 | it("has correct annotation body type", async () => { 85 | assert(annotationBody.type === "Text"); 86 | }); 87 | 88 | it("has correct annotation body format", async () => { 89 | assert(annotationBody.format === "application/json"); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /test/tests/file-annotation-collection.js: -------------------------------------------------------------------------------- 1 | const { assert, build, urljoin, canvasHasContentAnnotations } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | const collection = "/file-annotation-collection"; 5 | const collectionUrl = "http://test.com/file-annotation-collection"; 6 | 7 | it("can build collection", async () => { 8 | assert(await fileExists(collection)); 9 | return build(collection, collectionUrl); 10 | }).timeout(2000); 11 | 12 | it("can find collection index.json", async () => { 13 | const file = urljoin(collection, "/index.json"); 14 | assert(await fileExists(file)); 15 | collectionJson = await readJson(file); 16 | }); 17 | 18 | describe("canvas per file", async () => { 19 | let manifest = "canvas-per-file"; 20 | let manifestJson, canvases; 21 | 22 | it("can find " + manifest + " index.json", async () => { 23 | const file = urljoin(collection, manifest, "index.json"); 24 | assert(await fileExists(file)); 25 | manifestJson = await readJson(file); 26 | canvases = manifestJson.items; 27 | assert(canvases.length === 10); 28 | }); 29 | 30 | it("has all content annotations", async () => { 31 | // todo: fix ffprobe - unable to load mp3, mp4 32 | // remember to change [index] for these if making alterations 33 | canvasHasContentAnnotations(canvases[0], ["file.crt"]); 34 | canvasHasContentAnnotations(canvases[1], ["file.drc"]); 35 | canvasHasContentAnnotations(canvases[2], ["file.gltf"]); 36 | canvasHasContentAnnotations(canvases[3], ["file.jpg"]); 37 | canvasHasContentAnnotations(canvases[4], ["file.json"]); 38 | // canvasHasContentAnnotations(canvases[5], ["file.mp3"]); 39 | // canvasHasContentAnnotations(canvases[6], ["file.mp4"]); 40 | canvasHasContentAnnotations(canvases[5], ["file.obj"]); 41 | canvasHasContentAnnotations(canvases[6], ["file.pdf"]); 42 | canvasHasContentAnnotations(canvases[7], ["file.ply"]); 43 | canvasHasContentAnnotations(canvases[8], ["file.png"]); 44 | canvasHasContentAnnotations(canvases[9], ["file.usdz"]); 45 | }); 46 | }); 47 | 48 | describe("files per canvas", async () => { 49 | let manifest = "files-per-canvas"; 50 | let manifestJson, canvasJson; 51 | 52 | it("can find " + manifest + " index.json", async () => { 53 | const file = urljoin(collection, manifest, "index.json"); 54 | assert(await fileExists(file)); 55 | manifestJson = await readJson(file); 56 | }); 57 | 58 | it("can find " + manifest + " index.json", async () => { 59 | const file = urljoin(collection, manifest, "index.json"); 60 | assert(await fileExists(file)); 61 | manifestJson = await readJson(file); 62 | canvasJson = manifestJson.items[0]; 63 | }); 64 | 65 | it("has all content annotations", async () => { 66 | canvasHasContentAnnotations(canvasJson, [ 67 | "file.crt", 68 | "file.drc", 69 | "file.gltf", 70 | "file.jpg", 71 | "file.json", 72 | // "file.mp3", 73 | // "file.mp4", 74 | "file.obj", 75 | "file.pdf", 76 | "file.ply", 77 | "file.png", 78 | "file.usdz", 79 | ]); 80 | }); 81 | }); 82 | 83 | describe("erroneous file", async () => { 84 | let manifest = "erroneous-file"; 85 | let manifestJson, canvasJson, annotationPage; 86 | 87 | it("can find " + manifest + " index.json", async () => { 88 | const file = urljoin(collection, manifest, "index.json"); 89 | assert(await fileExists(file)); 90 | manifestJson = await readJson(file); 91 | canvasJson = manifestJson.items[0]; 92 | }); 93 | 94 | it("has no content annotations", async () => { 95 | assert(canvasJson); 96 | annotationPage = canvasJson.items[0]; 97 | assert(annotationPage); 98 | assert(annotationPage.items.length === 0); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /test/tests/files-only-collection.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let collectionJson, manifestJson, canvasJson, annotationPage; 5 | const collection = "/files-only-collection"; 6 | const collectionUrl = "http://test.com/files-only-collection"; 7 | 8 | it("can build collection", async () => { 9 | assert(await fileExists(collection)); 10 | return build(collection, collectionUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find collection index.json", async () => { 14 | const file = "/files-only-collection/index.json"; 15 | assert(await fileExists(file)); 16 | collectionJson = await readJson(file); 17 | }); 18 | 19 | it("has correct collection id", async () => { 20 | assert( 21 | collectionJson.id === "http://test.com/files-only-collection/index.json" 22 | ); 23 | }); 24 | 25 | it("can find manifest index.json", async () => { 26 | const file = "/files-only-collection/files-only-manifest/index.json"; 27 | assert(await fileExists(file)); 28 | manifestJson = await readJson(file); 29 | }); 30 | 31 | it("has correct manifest id", async () => { 32 | assert( 33 | manifestJson.id === 34 | "http://test.com/files-only-collection/files-only-manifest/index.json" 35 | ); 36 | }); 37 | 38 | it("has correct number of canvases", async () => { 39 | assert(manifestJson.items.length === 3); 40 | }); 41 | 42 | it("can find canvas", async () => { 43 | canvasJson = manifestJson.items[0]; 44 | assert(canvasJson); 45 | }); 46 | 47 | it("has correct canvas id", async () => { 48 | assert( 49 | canvasJson.id === 50 | "http://test.com/files-only-collection/files-only-manifest/index.json/canvas/0" 51 | ); 52 | }); 53 | 54 | it("has an annotation page", async () => { 55 | annotationPage = canvasJson.items[0]; 56 | assert(annotationPage); 57 | }); 58 | 59 | it("has the correct annotation page id", async () => { 60 | annotationPage = canvasJson.items[0]; 61 | assert( 62 | annotationPage.id === 63 | "http://test.com/files-only-collection/files-only-manifest/index.json/canvas/0/annotationpage/0" 64 | ); 65 | }); 66 | 67 | it("has an annotation", async () => { 68 | annotation = annotationPage.items[0]; 69 | assert(annotation); 70 | }); 71 | 72 | it("has an annotation body", async () => { 73 | annotationBody = annotation.body; 74 | assert(annotationBody); 75 | }); 76 | 77 | it("has correct annotation id", async () => { 78 | assert( 79 | annotationBody.id === 80 | "http://test.com/files-only-collection/files-only-manifest/file.gltf" 81 | ); 82 | }); 83 | -------------------------------------------------------------------------------- /test/tests/files-only-manifest-dat.js: -------------------------------------------------------------------------------- 1 | const { assert, build, urljoin } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, annotationPage; 5 | const manifest = "/files-only-manifest-dat"; 6 | const manifestUrl = 7 | "http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772"; 8 | 9 | it("can build manifest", async () => { 10 | assert(await fileExists(manifest)); 11 | return build( 12 | manifest, 13 | manifestUrl, 14 | false, 15 | "0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772" 16 | ); 17 | }).timeout(1000); // should take less than a second 18 | 19 | it("can find " + manifest + " index.json", async () => { 20 | const file = urljoin(manifest, "index.json"); 21 | assert(await fileExists(file)); 22 | manifestJson = await readJson(file); 23 | }); 24 | 25 | it("has correct manifest id", async () => { 26 | assert( 27 | manifestJson.id === 28 | "http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772/index.json" 29 | ); 30 | }); 31 | 32 | it("has correct number of canvases", async () => { 33 | assert(manifestJson.items.length === 3); 34 | }); 35 | 36 | it("can find canvas", async () => { 37 | canvasJson = manifestJson.items[0]; 38 | assert(canvasJson); 39 | }); 40 | 41 | it("has correct canvas id", async () => { 42 | assert( 43 | canvasJson.id === 44 | "http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772/index.json/canvas/0" 45 | ); 46 | }); 47 | 48 | it("has an annotation page", async () => { 49 | annotationPage = canvasJson.items[0]; 50 | assert(annotationPage); 51 | }); 52 | 53 | it("has the correct annotation page id", async () => { 54 | annotationPage = canvasJson.items[0]; 55 | assert( 56 | annotationPage.id === 57 | "http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772/index.json/canvas/0/annotationpage/0" 58 | ); 59 | }); 60 | 61 | it("has an annotation", async () => { 62 | annotation = annotationPage.items[0]; 63 | assert(annotation); 64 | }); 65 | 66 | it("has an annotation body", async () => { 67 | annotationBody = annotation.body; 68 | assert(annotationBody); 69 | }); 70 | 71 | it("has correct annotation id", async () => { 72 | assert( 73 | annotationBody.id === 74 | "http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772/file.gltf" 75 | ); 76 | }); 77 | -------------------------------------------------------------------------------- /test/tests/files-only-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build, urljoin } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, annotationPage, annotation, annotationBody; 5 | const manifest = "/files-only-manifest"; 6 | const manifestUrl = "http://test.com/files-only-manifest"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(100000000); // should take less than a second 12 | 13 | it("can find " + manifest + " index.json", async () => { 14 | const file = urljoin(manifest, "index.json"); 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("has correct manifest id", async () => { 20 | assert(manifestJson.id === "http://test.com/files-only-manifest/index.json"); 21 | }); 22 | 23 | it("has correct number of canvases", async () => { 24 | assert(manifestJson.items.length === 6); 25 | }); 26 | 27 | // first canvas 28 | 29 | it("can find first canvas", async () => { 30 | canvasJson = manifestJson.items[0]; 31 | assert(canvasJson); 32 | }); 33 | 34 | it("first canvas has correct id", async () => { 35 | assert( 36 | canvasJson.id === "http://test.com/files-only-manifest/index.json/canvas/0" 37 | ); 38 | }); 39 | 40 | it("first canvas has an annotation page", async () => { 41 | annotationPage = canvasJson.items[0]; 42 | assert(annotationPage); 43 | }); 44 | 45 | it("first canvas has the correct annotation page id", async () => { 46 | annotationPage = canvasJson.items[0]; 47 | assert( 48 | annotationPage.id === 49 | "http://test.com/files-only-manifest/index.json/canvas/0/annotationpage/0" 50 | ); 51 | }); 52 | 53 | it("first canvas has an annotation", async () => { 54 | annotation = annotationPage.items[0]; 55 | assert(annotation); 56 | }); 57 | 58 | it("first canvas has an annotation body", async () => { 59 | annotationBody = annotation.body; 60 | assert(annotationBody); 61 | }); 62 | 63 | it("first canvas has correct annotation id", async () => { 64 | assert(annotationBody.id === "http://test.com/files-only-manifest/file.glb"); 65 | }); 66 | 67 | // second canvas 68 | 69 | it("can find second canvas", async () => { 70 | canvasJson = manifestJson.items[1]; 71 | assert(canvasJson); 72 | }); 73 | 74 | it("second canvas has correct id", async () => { 75 | assert( 76 | canvasJson.id === "http://test.com/files-only-manifest/index.json/canvas/1" 77 | ); 78 | }); 79 | 80 | it("second canvas has an annotation page", async () => { 81 | annotationPage = canvasJson.items[0]; 82 | assert(annotationPage); 83 | }); 84 | 85 | it("second canvas has the correct annotation page id", async () => { 86 | annotationPage = canvasJson.items[0]; 87 | assert( 88 | annotationPage.id === 89 | "http://test.com/files-only-manifest/index.json/canvas/1/annotationpage/0" 90 | ); 91 | }); 92 | 93 | it("second canvas has an annotation", async () => { 94 | annotation = annotationPage.items[0]; 95 | assert(annotation); 96 | }); 97 | 98 | it("second canvas has an annotation body", async () => { 99 | annotationBody = annotation.body; 100 | assert(annotationBody); 101 | }); 102 | 103 | it("second canvas has correct annotation id", async () => { 104 | assert(annotationBody.id === "http://test.com/files-only-manifest/file.gltf"); 105 | }); 106 | 107 | // third canvas 108 | 109 | it("can find third canvas", async () => { 110 | canvasJson = manifestJson.items[2]; 111 | assert(canvasJson); 112 | }); 113 | 114 | it("third canvas has correct id", async () => { 115 | assert( 116 | canvasJson.id === "http://test.com/files-only-manifest/index.json/canvas/2" 117 | ); 118 | }); 119 | 120 | it("third canvas has an annotation page", async () => { 121 | annotationPage = canvasJson.items[0]; 122 | assert(annotationPage); 123 | }); 124 | 125 | it("third canvas has the correct annotation page id", async () => { 126 | annotationPage = canvasJson.items[0]; 127 | assert( 128 | annotationPage.id === 129 | "http://test.com/files-only-manifest/index.json/canvas/2/annotationpage/0" 130 | ); 131 | }); 132 | 133 | it("third canvas has an annotation", async () => { 134 | annotation = annotationPage.items[0]; 135 | assert(annotation); 136 | }); 137 | 138 | it("third canvas has an annotation body", async () => { 139 | annotationBody = annotation.body; 140 | assert(annotationBody); 141 | }); 142 | 143 | it("third canvas has correct annotation id", async () => { 144 | assert(annotationBody.id === "http://test.com/files-only-manifest/file.jpeg"); 145 | }); 146 | 147 | // fourth canvas 148 | 149 | it("can find fourth canvas", async () => { 150 | canvasJson = manifestJson.items[3]; 151 | assert(canvasJson); 152 | }); 153 | 154 | it("fourth canvas has correct id", async () => { 155 | assert( 156 | canvasJson.id === "http://test.com/files-only-manifest/index.json/canvas/3" 157 | ); 158 | }); 159 | 160 | it("fourth canvas has an annotation page", async () => { 161 | annotationPage = canvasJson.items[0]; 162 | assert(annotationPage); 163 | }); 164 | 165 | it("fourth canvas has the correct annotation page id", async () => { 166 | annotationPage = canvasJson.items[0]; 167 | assert( 168 | annotationPage.id === 169 | "http://test.com/files-only-manifest/index.json/canvas/3/annotationpage/0" 170 | ); 171 | }); 172 | 173 | it("fourth canvas has an annotation", async () => { 174 | annotation = annotationPage.items[0]; 175 | assert(annotation); 176 | }); 177 | 178 | it("fourth canvas has an annotation body", async () => { 179 | annotationBody = annotation.body; 180 | assert(annotationBody); 181 | }); 182 | 183 | it("fourth canvas has correct annotation id", async () => { 184 | assert(annotationBody.id === "http://test.com/files-only-manifest/file.jpg"); 185 | }); 186 | 187 | // fifth canvas 188 | 189 | it("can find fifth canvas", async () => { 190 | canvasJson = manifestJson.items[4]; 191 | assert(canvasJson); 192 | }); 193 | 194 | it("fifth canvas has correct id", async () => { 195 | assert( 196 | canvasJson.id === "http://test.com/files-only-manifest/index.json/canvas/4" 197 | ); 198 | }); 199 | 200 | it("fifth canvas has an annotation page", async () => { 201 | annotationPage = canvasJson.items[0]; 202 | assert(annotationPage); 203 | }); 204 | 205 | it("fifth canvas has the correct annotation page id", async () => { 206 | annotationPage = canvasJson.items[0]; 207 | assert( 208 | annotationPage.id === 209 | "http://test.com/files-only-manifest/index.json/canvas/4/annotationpage/0" 210 | ); 211 | }); 212 | 213 | it("fifth canvas has an annotation", async () => { 214 | annotation = annotationPage.items[0]; 215 | assert(annotation); 216 | }); 217 | 218 | it("fifth canvas has an annotation body", async () => { 219 | annotationBody = annotation.body; 220 | assert(annotationBody); 221 | }); 222 | 223 | it("fifth canvas has correct annotation id", async () => { 224 | assert(annotationBody.id === "http://test.com/files-only-manifest/file.png"); 225 | }); 226 | 227 | // sixth canvas 228 | 229 | it("can find sixth canvas", async () => { 230 | canvasJson = manifestJson.items[5]; 231 | assert(canvasJson); 232 | }); 233 | 234 | it("sixth canvas has correct id", async () => { 235 | assert( 236 | canvasJson.id === "http://test.com/files-only-manifest/index.json/canvas/5" 237 | ); 238 | }); 239 | 240 | it("sixth canvas has an annotation page", async () => { 241 | annotationPage = canvasJson.items[0]; 242 | assert(annotationPage); 243 | }); 244 | 245 | it("sixth canvas has the correct annotation page id", async () => { 246 | annotationPage = canvasJson.items[0]; 247 | assert( 248 | annotationPage.id === 249 | "http://test.com/files-only-manifest/index.json/canvas/5/annotationpage/0" 250 | ); 251 | }); 252 | 253 | it("sixth canvas has an annotation", async () => { 254 | annotation = annotationPage.items[0]; 255 | assert(annotation); 256 | }); 257 | 258 | it("sixth canvas has an annotation body", async () => { 259 | annotationBody = annotation.body; 260 | assert(annotationBody); 261 | }); 262 | 263 | it("sixth canvas has correct annotation id", async () => { 264 | assert(annotationBody.id === "http://test.com/files-only-manifest/file.usdz"); 265 | }); 266 | -------------------------------------------------------------------------------- /test/tests/generate-thumbs-dat-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, thumbnailJson; 5 | const manifest = "/generate-thumbs-dat-manifest"; 6 | const datId = 7 | "5fe9b8d2ce257bccf05211597350d2459d7cf76701264cca70f3ffbec7bf605f"; 8 | const generateThumbsManifestUrl = "dat://" + datId; 9 | 10 | it("can build generate-thumbs-dat-manifest", async () => { 11 | assert(await fileExists(manifest)); 12 | return build(manifest, generateThumbsManifestUrl, datId); 13 | }).timeout(1000); // should take less than a second 14 | 15 | it("can find manifest index.json", async () => { 16 | const file = "/generate-thumbs-dat-manifest/index.json"; 17 | assert(await fileExists(file)); 18 | manifestJson = await readJson(file); 19 | }); 20 | 21 | it("can find canvas", async () => { 22 | canvasJson = manifestJson.items[0]; 23 | assert(canvasJson); 24 | }); 25 | 26 | it("has correct canvas id", async () => { 27 | assert(canvasJson.id === generateThumbsManifestUrl + "/index.json/canvas/0"); 28 | }); 29 | 30 | it("has correct canvas id", async () => { 31 | assert(canvasJson.id === generateThumbsManifestUrl + "/index.json/canvas/0"); 32 | }); 33 | 34 | it("has a canvas thumbnail", async () => { 35 | thumbnailJson = canvasJson.thumbnail[0]; 36 | assert(thumbnailJson); 37 | }); 38 | 39 | it("has correct canvas thumbnail id", async () => { 40 | assert( 41 | thumbnailJson.id === generateThumbsManifestUrl + "/_page-1/thumb.jpg" 42 | ); 43 | }); 44 | -------------------------------------------------------------------------------- /test/tests/generate-thumbs-http-gateway-dat-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, thumbnailJson; 5 | const manifest = "/generate-thumbs-dat-manifest"; 6 | const datId = 7 | "5fe9b8d2ce257bccf05211597350d2459d7cf76701264cca70f3ffbec7bf605f"; 8 | const generateThumbsManifestUrl = "http://174.138.105.19:3000/" + datId; 9 | 10 | it("can build generate-thumbs-dat-manifest", async () => { 11 | assert(await fileExists(manifest)); 12 | return build(manifest, generateThumbsManifestUrl, datId); 13 | }).timeout(1000); // should take less than a second 14 | 15 | it("can find manifest index.json", async () => { 16 | const file = "/generate-thumbs-dat-manifest/index.json"; 17 | assert(await fileExists(file)); 18 | manifestJson = await readJson(file); 19 | }); 20 | 21 | it("can find canvas", async () => { 22 | canvasJson = manifestJson.items[0]; 23 | assert(canvasJson); 24 | }); 25 | 26 | it("has correct canvas id", async () => { 27 | assert(canvasJson.id === generateThumbsManifestUrl + "/index.json/canvas/0"); 28 | }); 29 | 30 | it("has correct canvas id", async () => { 31 | assert(canvasJson.id === generateThumbsManifestUrl + "/index.json/canvas/0"); 32 | }); 33 | 34 | it("has a canvas thumbnail", async () => { 35 | thumbnailJson = canvasJson.thumbnail[0]; 36 | assert(thumbnailJson); 37 | }); 38 | 39 | it("has correct canvas thumbnail id", async () => { 40 | assert( 41 | thumbnailJson.id === generateThumbsManifestUrl + "/_page-1/thumb.jpg" 42 | ); 43 | }); 44 | -------------------------------------------------------------------------------- /test/tests/generate-thumbs-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, thumbnailJson; 5 | const manifest = "/generate-thumbs-manifest"; 6 | const generateThumbsManifestUrl = "http://test.com/generate-thumbs-manifest"; 7 | 8 | it("can build generate-thumbs-manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, generateThumbsManifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/generate-thumbs-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("can find canvas", async () => { 20 | canvasJson = manifestJson.items[0]; 21 | assert(canvasJson); 22 | }); 23 | 24 | it("has correct canvas id", async () => { 25 | assert(canvasJson.id === generateThumbsManifestUrl + "/index.json/canvas/0"); 26 | }); 27 | 28 | it("has correct canvas id", async () => { 29 | assert(canvasJson.id === generateThumbsManifestUrl + "/index.json/canvas/0"); 30 | }); 31 | 32 | it("has a canvas thumbnail", async () => { 33 | thumbnailJson = canvasJson.thumbnail[0]; 34 | assert(thumbnailJson); 35 | }); 36 | 37 | it("has correct canvas thumbnail id", async () => { 38 | assert( 39 | thumbnailJson.id === generateThumbsManifestUrl + "/_page-1/thumb.jpg" 40 | ); 41 | }); 42 | -------------------------------------------------------------------------------- /test/tests/gh-pages.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let collectionJson, thumbnailJson, manifestJson, canvasJson; 5 | const collection = "/gh-collection"; 6 | const githubpagesUrl = 7 | "https://username.github.io/uv-app-starter-fork/gh-collection"; 8 | 9 | it("can build collection", async () => { 10 | assert(await fileExists(collection)); 11 | return build(collection, githubpagesUrl); 12 | }).timeout(1000); // should take less than a second 13 | 14 | it("can find collection index.json", async () => { 15 | const file = "/gh-collection/index.json"; 16 | assert(await fileExists(file)); 17 | collectionJson = await readJson(file); 18 | }); 19 | 20 | it("has correct collection id", async () => { 21 | assert(collectionJson.id === githubpagesUrl + "/index.json"); 22 | }); 23 | 24 | it("has a manifest", async () => { 25 | item = collectionJson.items[0]; 26 | assert(item); 27 | }); 28 | 29 | it("has correct manifest id", async () => { 30 | assert(item.id === githubpagesUrl + "/vertebra/index.json"); 31 | }); 32 | 33 | it("has correct manifest label", async () => { 34 | assert(item.label["@none"][0] === "Vertebra"); 35 | }); 36 | 37 | it("has manifest thumbnail", async () => { 38 | thumbnailJson = item.thumbnail; 39 | assert(thumbnailJson); 40 | }); 41 | 42 | it("has correct manifest thumbnail id", async () => { 43 | const id = thumbnailJson[0].id; 44 | assert(id === githubpagesUrl + "/vertebra/thumb.jpg"); 45 | }); 46 | 47 | it("can find manifest index.json", async () => { 48 | const file = "/gh-collection/vertebra/index.json"; 49 | assert(await fileExists(file)); 50 | manifestJson = await readJson(file); 51 | }); 52 | 53 | it("can find canvas", async () => { 54 | canvasJson = manifestJson.items[0]; 55 | assert(canvasJson); 56 | }); 57 | 58 | it("has correct canvas id", async () => { 59 | assert(canvasJson.id === githubpagesUrl + "/vertebra/index.json/canvas/0"); 60 | }); 61 | -------------------------------------------------------------------------------- /test/tests/image-dimensions-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, annotationPage, annotation, annotationBody; 5 | const manifest = "/image-dimensions-manifest"; 6 | const manifestUrl = "http://test.com/image-dimensions-manifest"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/image-dimensions-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("can find canvas", async () => { 20 | canvasJson = manifestJson.items[0]; 21 | assert(canvasJson); 22 | }); 23 | 24 | it("has correct canvas id", async () => { 25 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 26 | }); 27 | 28 | // todo: can't do this any more as sharp fails with mock-fs 29 | // need to use the actual filesystem 30 | // it("has correct canvas width and height", async () => { 31 | // assert(canvasJson.width === 582); 32 | // assert(canvasJson.height === 328); 33 | // }); 34 | 35 | it("has an annotation page", async () => { 36 | annotationPage = canvasJson.items[0]; 37 | assert(annotationPage); 38 | }); 39 | 40 | it("has annotation", async () => { 41 | annotation = annotationPage.items[0]; 42 | assert(annotation); 43 | }); 44 | 45 | it("has an annotation body", async () => { 46 | annotationBody = annotation.body; 47 | assert(annotationBody); 48 | }); 49 | 50 | // todo: can't do this any more as sharp fails with mock-fs 51 | // need to use the actual filesystem 52 | // it("has correct annotation width and height", async () => { 53 | // assert(annotationBody.width === 582); 54 | // assert(annotationBody.height === 328); 55 | // }); 56 | -------------------------------------------------------------------------------- /test/tests/multiple-behavior-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson; 5 | const manifest = "/multiple-behavior-manifest"; 6 | const manifestUrl = "http://test.com/multiple-behavior-manifest"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/multiple-behavior-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("has paged and auto behavior", async () => { 20 | assert(manifestJson.behavior[0] === "paged"); 21 | assert(manifestJson.behavior[1] === "unordered"); 22 | }); 23 | -------------------------------------------------------------------------------- /test/tests/readme-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists } = require("../../Utils"); 3 | 4 | const manifest = "/readme-manifest"; 5 | const manifestUrl = "http://test.com/readme-manifest"; 6 | 7 | it("can build manifest", async () => { 8 | assert(await fileExists(manifest)); 9 | return build(manifest, manifestUrl); 10 | }).timeout(1000); // should take less than a second 11 | -------------------------------------------------------------------------------- /test/tests/sort-canvases-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvases; 5 | const manifest = "/sort-canvases-manifest"; 6 | const manifestUrl = "http://test.com/sort-canvases-manifest"; 7 | 8 | it("can build sort canvases manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(2000); 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/sort-canvases-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("sorts canvases correctly", async () => { 20 | canvases = manifestJson.items; 21 | assert(canvases.length === 11); 22 | assert( 23 | canvases[0].id === 24 | "http://test.com/sort-canvases-manifest/index.json/canvas/0" 25 | ); 26 | assert( 27 | canvases[1].id === 28 | "http://test.com/sort-canvases-manifest/index.json/canvas/1" 29 | ); 30 | assert( 31 | canvases[2].id === 32 | "http://test.com/sort-canvases-manifest/index.json/canvas/2" 33 | ); 34 | assert( 35 | canvases[10].id === 36 | "http://test.com/sort-canvases-manifest/index.json/canvas/10" 37 | ); 38 | }); 39 | -------------------------------------------------------------------------------- /test/tests/sort-canvases-numeric-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvases; 5 | const manifest = "/sort-canvases-numeric-manifest"; 6 | const manifestUrl = "http://test.com/sort-canvases-numeric-manifest"; 7 | 8 | it("can build sort canvases numeric manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(3000); 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/sort-canvases-numeric-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("sorts canvases correctly", async () => { 20 | canvases = manifestJson.items; 21 | assert(canvases.length === 21); 22 | assert( 23 | canvases[0].id === 24 | "http://test.com/sort-canvases-numeric-manifest/index.json/canvas/0" 25 | ); 26 | assert( 27 | canvases[1].id === 28 | "http://test.com/sort-canvases-numeric-manifest/index.json/canvas/1" 29 | ); 30 | assert( 31 | canvases[2].id === 32 | "http://test.com/sort-canvases-numeric-manifest/index.json/canvas/2" 33 | ); 34 | assert( 35 | canvases[10].id === 36 | "http://test.com/sort-canvases-numeric-manifest/index.json/canvas/10" 37 | ); 38 | assert( 39 | canvases[11].id === 40 | "http://test.com/sort-canvases-numeric-manifest/index.json/canvas/11" 41 | ); 42 | assert( 43 | canvases[20].id === 44 | "http://test.com/sort-canvases-numeric-manifest/index.json/canvas/20" 45 | ); 46 | }); 47 | -------------------------------------------------------------------------------- /test/tests/sort-files-numeric-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvases; 5 | const manifest = "/sort-files-numeric-manifest"; 6 | const manifestUrl = "http://test.com/sort-files-numeric-manifest"; 7 | 8 | it("can build sort files numeric manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(3000); 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/sort-files-numeric-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("sorts canvases correctly", async () => { 20 | canvases = manifestJson.items; 21 | assert(canvases.length === 21); 22 | assert( 23 | canvases[0].id === 24 | "http://test.com/sort-files-numeric-manifest/index.json/canvas/0" 25 | ); 26 | assert( 27 | canvases[1].id === 28 | "http://test.com/sort-files-numeric-manifest/index.json/canvas/1" 29 | ); 30 | assert( 31 | canvases[2].id === 32 | "http://test.com/sort-files-numeric-manifest/index.json/canvas/2" 33 | ); 34 | assert( 35 | canvases[10].id === 36 | "http://test.com/sort-files-numeric-manifest/index.json/canvas/10" 37 | ); 38 | assert( 39 | canvases[11].id === 40 | "http://test.com/sort-files-numeric-manifest/index.json/canvas/11" 41 | ); 42 | assert( 43 | canvases[20].id === 44 | "http://test.com/sort-files-numeric-manifest/index.json/canvas/20" 45 | ); 46 | }); 47 | -------------------------------------------------------------------------------- /test/tests/thumbs-single-manifest-dat.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, thumbnailJson; 5 | const manifest = "/thumbs-single-manifest"; 6 | const manifestUrl = 7 | "http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772"; 8 | 9 | it("can build manifest", async () => { 10 | assert(await fileExists(manifest)); 11 | return build( 12 | manifest, 13 | manifestUrl, 14 | true, 15 | "0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772" 16 | ); 17 | }).timeout(1000); // should take less than a second 18 | 19 | it("can find manifest index.json", async () => { 20 | const file = "/thumbs-single-manifest/index.json"; 21 | assert(await fileExists(file)); 22 | manifestJson = await readJson(file); 23 | }); 24 | 25 | it("can find canvas", async () => { 26 | canvasJson = manifestJson.items[0]; 27 | assert(canvasJson); 28 | }); 29 | 30 | it("has correct canvas id", async () => { 31 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 32 | }); 33 | 34 | it("has correct canvas id", async () => { 35 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 36 | }); 37 | 38 | it("has a canvas thumbnail", async () => { 39 | thumbnailJson = canvasJson.thumbnail[0]; 40 | assert(thumbnailJson); 41 | }); 42 | 43 | it("has correct canvas thumbnail url", async () => { 44 | assert(thumbnailJson.id === manifestUrl + "/thumb.jpg"); 45 | }); 46 | -------------------------------------------------------------------------------- /test/tests/thumbs-single-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, thumbnailJson; 5 | const manifest = "/thumbs-single-manifest"; 6 | const manifestUrl = "http://test.com/thumbs-single-manifest"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/thumbs-single-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("can find canvas", async () => { 20 | canvasJson = manifestJson.items[0]; 21 | assert(canvasJson); 22 | }); 23 | 24 | it("has correct canvas id", async () => { 25 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 26 | }); 27 | 28 | it("has a canvas thumbnail", async () => { 29 | thumbnailJson = canvasJson.thumbnail[0]; 30 | assert(thumbnailJson); 31 | }); 32 | 33 | it("has correct canvas thumbnail url", async () => { 34 | assert(thumbnailJson.id === manifestUrl + "/thumb.jpg"); 35 | }); 36 | -------------------------------------------------------------------------------- /test/tests/url.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists } = require("../../Utils"); 3 | 4 | const manifest = "/files-only-manifest"; 5 | const manifestUrl = "https://biiif-template-test-vercel-9abrhgri5-mnemoscene.vercel.app"; 6 | 7 | it("can build manifest", async () => { 8 | assert(await fileExists(manifest)); 9 | return build(manifest, manifestUrl); 10 | }).timeout(1000); // should take less than a second 11 | 12 | -------------------------------------------------------------------------------- /test/tests/utils.js: -------------------------------------------------------------------------------- 1 | const { assert, URL } = require("../common"); 2 | const { mergePaths } = require("../../Utils"); 3 | 4 | let url, filePath, id; 5 | 6 | it("correctly creates thumbnail ids", async () => { 7 | url = new URL("http://test.com/manifest"); 8 | filePath = "c:/user/documents/manifest/_canvas/thumb.png"; 9 | id = mergePaths(url, filePath); 10 | assert(id === "http://test.com/manifest/_canvas/thumb.png"); 11 | 12 | url = new URL("http://test.com/manifest"); 13 | filePath = "c:/manifest/_canvas/thumb.png"; 14 | id = mergePaths(url, filePath); 15 | assert(id === "http://test.com/manifest/_canvas/thumb.png"); 16 | 17 | url = new URL("http://test.com/manifest"); 18 | filePath = "c:\\manifest\\_canvas\\thumb.png"; 19 | id = mergePaths(url, filePath); 20 | assert(id === "http://test.com/manifest/_canvas/thumb.png"); 21 | 22 | url = new URL("http://test.com/collection/manifest"); 23 | filePath = "c:/user/documents/collection/manifest/_canvas/thumb.png"; 24 | id = mergePaths(url, filePath); 25 | assert(id === "http://test.com/collection/manifest/_canvas/thumb.png"); 26 | 27 | url = new URL( 28 | "http://test.com/collection/sub-collection/sub_collection/sub-collection/manifest" 29 | ); 30 | filePath = 31 | "c:/user/documents/collection/sub-collection/sub_collection/sub-collection/manifest/_canvas/thumb.png"; 32 | id = mergePaths(url, filePath); 33 | assert( 34 | id === 35 | "http://test.com/collection/sub-collection/sub_collection/sub-collection/manifest/_canvas/thumb.png" 36 | ); 37 | 38 | url = new URL( 39 | "http://localhost:8888/collection/sub-collection/sub_collection/sub-collection/manifest" 40 | ); 41 | filePath = 42 | "c:/user/documents/github/collection/sub-collection/sub_collection/sub-collection/manifest/_canvas/thumb.png"; 43 | id = mergePaths(url, filePath); 44 | assert( 45 | id === 46 | "http://localhost:8888/collection/sub-collection/sub_collection/sub-collection/manifest/_canvas/thumb.png" 47 | ); 48 | 49 | url = new URL( 50 | "https://edsilv.github.io/uv-app-starter/gh-collection/human_skull" 51 | ); 52 | filePath = 53 | "c:/Users/edsilv/github/uv-app-starter/gh-collection/human_skull/thumb.png"; 54 | id = mergePaths(url, filePath); 55 | assert( 56 | id === 57 | "https://edsilv.github.io/uv-app-starter/gh-collection/human_skull/thumb.png" 58 | ); 59 | 60 | url = new URL( 61 | "dat://5d317729a67e4a1e5c28be9cf08493ec025a749a00ba4d9d4bf7ea6c439027ba/collection" 62 | ); 63 | filePath = 64 | "c:/Users/edsilv/github/uv-app-starter/collection/human_skull/thumb.png"; 65 | id = mergePaths(url, filePath); 66 | assert( 67 | id === 68 | "dat://5d317729a67e4a1e5c28be9cf08493ec025a749a00ba4d9d4bf7ea6c439027ba/collection/human_skull/thumb.png" 69 | ); 70 | 71 | url = new URL( 72 | "dat://5d317729a67e4a1e5c28be9cf08493ec025a749a00ba4d9d4bf7ea6c439027ba/collection" 73 | ); 74 | filePath = 75 | "c:/Users/edsilv/github/uv-app-starter/collection/human_skull/thumb.png"; 76 | id = mergePaths(url, filePath); 77 | assert( 78 | id === 79 | "dat://5d317729a67e4a1e5c28be9cf08493ec025a749a00ba4d9d4bf7ea6c439027ba/collection/human_skull/thumb.png" 80 | ); 81 | 82 | // url = new URL('http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772'); 83 | // filePath = 'C://Users/edsilv/github/edsilv/biiif-workshop/collection/_abyssinian/thumb.jpeg'; 84 | // id = mergePaths(url, filePath, 'collection'); 85 | // assert(id === 'http://174.138.105.19:3000/0cd3f6a6b3b11700b299f70fe4dbc054d83590676ec18d7d623ccd31791fc772/_abyssinian/thumb.jpeg'); 86 | }); 87 | -------------------------------------------------------------------------------- /test/tests/vercel-manifest.js: -------------------------------------------------------------------------------- 1 | const { assert, build } = require("../common"); 2 | const { fileExists, readJson } = require("../../Utils"); 3 | 4 | let manifestJson, canvasJson, thumbnailJson; 5 | const manifest = "/vercel-manifest"; 6 | const manifestUrl = "https://biiif-template-code-of-ethics-zine.vercel.app"; 7 | 8 | it("can build manifest", async () => { 9 | assert(await fileExists(manifest)); 10 | return build(manifest, manifestUrl); 11 | }).timeout(1000); // should take less than a second 12 | 13 | it("can find manifest index.json", async () => { 14 | const file = "/vercel-manifest/index.json"; 15 | assert(await fileExists(file)); 16 | manifestJson = await readJson(file); 17 | }); 18 | 19 | it("can find canvas", async () => { 20 | canvasJson = manifestJson.items[0]; 21 | assert(canvasJson); 22 | }); 23 | 24 | it("has correct canvas id", async () => { 25 | assert(canvasJson.id === manifestUrl + "/index.json/canvas/0"); 26 | }); 27 | 28 | it("has a canvas thumbnail", async () => { 29 | thumbnailJson = canvasJson.thumbnail[0]; 30 | assert(thumbnailJson); 31 | }); 32 | 33 | it("has correct canvas thumbnail url", async () => { 34 | assert(thumbnailJson.id === manifestUrl + "/_page-1/thumb.jpg"); 35 | }); 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "es2017"], 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "target": "es2017", 7 | "noUnusedLocals": true, 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "types": ["@iiif/vocabulary", "@types/node"], 11 | "sourceMap": true, 12 | "watch": true 13 | }, 14 | "include": ["./"], 15 | "exclude": ["node_modules", "test"] 16 | } 17 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-config-prettier"], 3 | "rules": { 4 | "class-name": true, 5 | "comment-format": [true, "check-space"], 6 | "curly": true, 7 | "forin": true, 8 | "interface-name": [true, "never-prefix"], 9 | "jsdoc-format": true, 10 | "label-position": true, 11 | "member-ordering": [true], 12 | "no-any": true, 13 | "no-arg": true, 14 | "no-bitwise": true, 15 | "no-console": [ 16 | true, 17 | "log", 18 | "error", 19 | "debug", 20 | "info", 21 | "time", 22 | "timeEnd", 23 | "trace" 24 | ], 25 | "no-construct": true, 26 | "no-debugger": true, 27 | "no-duplicate-variable": true, 28 | "no-empty": true, 29 | "no-eval": true, 30 | "no-shadowed-variable": true, 31 | "no-string-literal": true, 32 | "no-switch-case-fall-through": true, 33 | "no-trailing-whitespace": false, 34 | "no-unused-expression": true, 35 | "no-use-before-declare": true, 36 | "object-literal-sort-keys": false, 37 | "radix": true, 38 | "switch-default": true, 39 | "triple-equals": [true, "allow-null-check"], 40 | "typedef": [true, "parameter", "property-declaration"], 41 | "variable-name": [ 42 | true, 43 | "ban-keywords", 44 | "check-format", 45 | "allow-leading-underscore", 46 | "allow-pascal-case" 47 | ] 48 | } 49 | } 50 | --------------------------------------------------------------------------------