From 2f7cbabf5601a22a4178a5eb767783fcc44eb4a8 Mon Sep 17 00:00:00 2001 From: Roberto Polli Date: Thu, 6 Oct 2022 00:30:22 +0200 Subject: [PATCH 01/13] Publish docker image --- .github/workflows/docker-image.yml | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..cf107fa --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,48 @@ +name: Docker Image CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} +jobs: + publish: + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Log in to the Github Container registry + uses: docker/login-action@v1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]${GITHUB_REF##*/}" + id: extract_branch + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + maintainer=Team Digitale + org.opencontainers.image.source.branch=${{ steps.extract_branch.outputs.branch }} + tags: | + type=raw,value={{date 'YYYYMMDD'}}-${{github.run_number}}-{{sha}} + type=raw, value=latest + - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} From a19997845bea8201bbcd009e993eafa6cb10eb06 Mon Sep 17 00:00:00 2001 From: Roberto Polli Date: Thu, 6 Oct 2022 12:31:09 +0200 Subject: [PATCH 02/13] Add deployment doc. [skip ci] --- DEPLOY.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 DEPLOY.md diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..fff891e --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,22 @@ +# Deployment process + +The deployent process described by this diagram. +The gh-action deploys the image on ghcr.io. + +```mermaid +flowchart LR + classDef default stroke:white,color:#fff,clusterBkg:none,fill:#3344d0 + classDef cluster font-weight: bold,fill:none,color:darkgray,stroke:#3344d0,stroke-width:2px + classDef subgraph_padding fill:none, stroke:none, opacity:0 + classDef bounded_context stroke-dasharray:5 5 + +subgraph Viewer Repository +direction LR +merge1[merge] --> |1. gh-action| build -->|2. upload| ghcr.io +end + +subgraph Kube Repository +merge2 -.-o|3. references| ghcr.io +merge2[merge] --> |4. gh-action| deploy +end +``` From d2e46b2cffd368914742eb9c2fddefc068447f0b Mon Sep 17 00:00:00 2001 From: Giuseppe Nespolino Date: Tue, 25 Oct 2022 16:41:13 +0200 Subject: [PATCH 03/13] Rebased on Spring boot 2 (#5) * switch to spring boot 2 / gradlew minor code refactoring * DockerFile to build image --- .gitignore | 136 +- Dockerfile | 13 +- WebContent/META-INF/MANIFEST.MF | 3 - build.gradle | 84 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 240 ++ gradlew.bat | 91 + pom.xml | 161 -- settings.gradle | 1 + .../lodviewng/LodviewNgApplication.java | 20 + .../lodviewng/lodview/bean/OntologyBean.java | 82 + .../lodviewng/lodview/bean/PropertyBean.java | 12 + .../lodviewng/lodview/bean/ResultBean.java | 113 + .../lodviewng/lodview/bean/TripleBean.java | 18 + .../lodview/builder/ResourceBuilder.java | 328 +++ .../lodview/conf/ConfigurationBean.java | 228 ++ .../lodview/conf/ConfigurationBeanConfig.java | 18 + .../lodview/controllers/ErrorController.java | 91 + .../LinkedResourcesController.java | 134 ++ .../lodview/controllers/LodController.java | 91 + .../controllers/ResourceController.java | 388 ++++ .../lodview/controllers/SPARQLController.java | 17 + .../lodview/controllers/StaticController.java | 73 + .../lodview/endpoint/SPARQLEndPoint.java | 341 +++ .../lodviewng/lodview/utils/Misc.java | 139 ++ .../utils/ResourceClassPathLoader.java | 47 + .../org/dvcama/lodview/bean/OntologyBean.java | 125 - .../org/dvcama/lodview/bean/PropertyBean.java | 86 - .../org/dvcama/lodview/bean/ResultBean.java | 198 -- .../org/dvcama/lodview/bean/TripleBean.java | 101 - .../lodview/builder/ResourceBuilder.java | 330 --- .../lodview/conf/ConfigurationBean.java | 372 --- .../lodview/controllers/ErrorController.java | 100 - .../LinkedResourcesController.java | 154 -- .../lodview/controllers/LodController.java | 116 - .../controllers/ResourceController.java | 408 ---- .../lodview/controllers/SPARQLController.java | 20 - .../lodview/controllers/StaticController.java | 85 - .../lodview/endpoint/SPARQLEndPoint.java | 334 --- .../java/org/dvcama/lodview/utils/Misc.java | 146 -- src/main/resources/application.properties | 4 + src/main/resources/conf/conf-linked.ttl | 124 + src/main/resources/conf/conf.ttl | 225 ++ src/main/resources/logback.xml | 26 - src/main/resources/messages_en.properties | 37 - src/main/resources/messages_fr.properties | 50 - src/main/resources/messages_gl.properties | 49 - src/main/resources/messages_it.properties | 49 - src/main/resources/messages_nl.properties | 36 - src/main/resources/messages_sk.properties | 49 - .../ontologies/dbpedia_2014.owl | 0 .../ontologies/dcelements.rdf | 0 .../ontologies/dcterms.rdf | 0 .../ontologies/dctype.rdf | 0 .../WEB-INF => resources}/ontologies/foaf.rdf | 0 .../WEB-INF => resources}/ontologies/owl.ttl | 0 .../WEB-INF => resources}/ontologies/rdf.ttl | 0 .../WEB-INF => resources}/ontologies/rdfs.ttl | 0 .../WEB-INF => resources}/ontologies/rel.rdf | 0 .../resources/spring/application-config.xml | 12 - .../static/staticResources/css/commons.css | 1031 ++++++++ .../static/staticResources/css/commons_2x.css | 36 + .../static/staticResources/css/custom.css | 34 + .../static/staticResources/css/reset.css | 78 + .../static/staticResources}/img/favicon.png | Bin .../img/loading-new-nero@2x.gif | Bin .../staticResources}/img/loading-new@2x.gif | Bin .../img/loading-newB-nero@2x.gif | Bin .../staticResources}/img/lodview-sprite.png | Bin .../img/lodview-sprite@2x.png | Bin .../staticResources}/img/lodview_sharer.png | Bin .../img/logo-footer-lodview.png | Bin .../img/logo-footer-lodview@2x.png | Bin .../img/logo-header-lodview.png | Bin .../img/logo-header-lodview@2x.png | Bin .../static/staticResources}/img/no_image.png | Bin .../staticResources}/img/no_image@2x.png | Bin .../static/staticResources}/img/owl404.png | Bin .../static/staticResources}/img/owl404@2x.png | Bin .../static/staticResources}/img/owl500.png | Bin .../static/staticResources}/img/owl500@2x.png | Bin .../img/segnaposto-immagine.png | Bin .../img/segnaposto-immagine@2x.png | Bin .../staticResources}/img/separatoreCorto.png | Bin .../img/separatoreCorto@2x.png | Bin .../static/staticResources/style.css | 7 + .../jplayercircle/circle.skin/buffer.png | Bin .../circle.skin/circle.player.css | 144 ++ .../jplayercircle/circle.skin/controls.png | Bin .../jplayercircle/circle.skin/progress.png | Bin .../circle.skin/progress_sprite.png | Bin .../vendor/jplayercircle/js/Jplayer.swf | Bin .../vendor/jplayercircle/js/circle.player.js | 241 ++ .../vendor/jplayercircle/js/jquery.grab.js | 209 ++ .../vendor/jplayercircle/js/jquery.jplayer.js | 2041 ++++++++++++++++ .../jplayercircle/js/jquery.transform.js | 532 +++++ .../jplayercircle/js/mod.csstransforms.min.js | 0 .../staticResources}/vendor/jquery.min.js | 0 .../vendor/leaflet/images/layers-2x.png | Bin .../vendor/leaflet/images/layers.png | Bin .../vendor/leaflet/images/marker-icon-2x.png | Bin .../vendor/leaflet/images/marker-icon.png | Bin .../vendor/leaflet/images/marker-shadow.png | Bin .../vendor/leaflet/leaflet.css | 562 +++++ .../vendor/leaflet/leaflet.js | 0 .../vendor/masonry.pkgd.min.js | 0 .../vendor/modernizr-custom.min.js | 0 .../WEB-INF/spring/appServlet/controllers.xml | 15 - .../spring/appServlet/servlet-context.xml | 42 - .../webapp/WEB-INF/spring/root-context.xml | 18 - src/main/webapp/WEB-INF/views/error.jsp | 93 +- src/main/webapp/WEB-INF/views/func/bnodes.jsp | 30 +- .../webapp/WEB-INF/views/func/contents.jsp | 28 +- .../webapp/WEB-INF/views/func/literals.jsp | 90 +- .../webapp/WEB-INF/views/func/resources.jsp | 41 +- src/main/webapp/WEB-INF/views/home.jsp | 70 +- .../WEB-INF/views/inc/custom_footer.jsp | 12 +- src/main/webapp/WEB-INF/views/inc/footer.jsp | 65 +- src/main/webapp/WEB-INF/views/inc/header.jsp | 83 +- src/main/webapp/WEB-INF/views/inc/scripts.jsp | 2064 +++++++++-------- src/main/webapp/WEB-INF/views/inc/widgets.jsp | 119 +- src/main/webapp/WEB-INF/views/resource.jsp | 392 ++-- src/main/webapp/WEB-INF/web.xml | 55 - src/main/webapp/resources/css/commons.css | 1009 -------- src/main/webapp/resources/css/commons_2x.css | 29 - src/main/webapp/resources/css/custom.css | 25 - src/main/webapp/resources/css/reset.css | 15 - src/main/webapp/resources/style.css | 4 - .../circle.skin/circle.player.css | 141 -- .../vendor/jplayercircle/js/circle.player.js | 243 -- .../vendor/jplayercircle/js/jquery.grab.js | 201 -- .../vendor/jplayercircle/js/jquery.jplayer.js | 2028 ---------------- .../jplayercircle/js/jquery.transform.js | 532 ----- .../resources/vendor/leaflet/leaflet.css | 478 ---- .../lodviewng/LodviewNgApplicationTests.java | 13 + 136 files changed, 9581 insertions(+), 9379 deletions(-) delete mode 100644 WebContent/META-INF/MANIFEST.MF create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat delete mode 100644 pom.xml create mode 100644 settings.gradle create mode 100644 src/main/java/it/innovaway/lodviewng/LodviewNgApplication.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/bean/OntologyBean.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/bean/PropertyBean.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/bean/ResultBean.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/bean/TripleBean.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/builder/ResourceBuilder.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBean.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBeanConfig.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/controllers/ErrorController.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/controllers/LinkedResourcesController.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/controllers/LodController.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/controllers/ResourceController.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/controllers/SPARQLController.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/controllers/StaticController.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/endpoint/SPARQLEndPoint.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/utils/Misc.java create mode 100644 src/main/java/it/innovaway/lodviewng/lodview/utils/ResourceClassPathLoader.java delete mode 100644 src/main/java/org/dvcama/lodview/bean/OntologyBean.java delete mode 100644 src/main/java/org/dvcama/lodview/bean/PropertyBean.java delete mode 100644 src/main/java/org/dvcama/lodview/bean/ResultBean.java delete mode 100644 src/main/java/org/dvcama/lodview/bean/TripleBean.java delete mode 100644 src/main/java/org/dvcama/lodview/builder/ResourceBuilder.java delete mode 100644 src/main/java/org/dvcama/lodview/conf/ConfigurationBean.java delete mode 100644 src/main/java/org/dvcama/lodview/controllers/ErrorController.java delete mode 100644 src/main/java/org/dvcama/lodview/controllers/LinkedResourcesController.java delete mode 100644 src/main/java/org/dvcama/lodview/controllers/LodController.java delete mode 100644 src/main/java/org/dvcama/lodview/controllers/ResourceController.java delete mode 100644 src/main/java/org/dvcama/lodview/controllers/SPARQLController.java delete mode 100644 src/main/java/org/dvcama/lodview/controllers/StaticController.java delete mode 100644 src/main/java/org/dvcama/lodview/endpoint/SPARQLEndPoint.java delete mode 100644 src/main/java/org/dvcama/lodview/utils/Misc.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/conf/conf-linked.ttl create mode 100644 src/main/resources/conf/conf.ttl delete mode 100644 src/main/resources/logback.xml delete mode 100644 src/main/resources/messages_en.properties delete mode 100644 src/main/resources/messages_fr.properties delete mode 100644 src/main/resources/messages_gl.properties delete mode 100644 src/main/resources/messages_it.properties delete mode 100644 src/main/resources/messages_nl.properties delete mode 100644 src/main/resources/messages_sk.properties rename src/main/{webapp/WEB-INF => resources}/ontologies/dbpedia_2014.owl (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/dcelements.rdf (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/dcterms.rdf (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/dctype.rdf (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/foaf.rdf (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/owl.ttl (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/rdf.ttl (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/rdfs.ttl (100%) rename src/main/{webapp/WEB-INF => resources}/ontologies/rel.rdf (100%) delete mode 100644 src/main/resources/spring/application-config.xml create mode 100644 src/main/resources/static/staticResources/css/commons.css create mode 100644 src/main/resources/static/staticResources/css/commons_2x.css create mode 100644 src/main/resources/static/staticResources/css/custom.css create mode 100644 src/main/resources/static/staticResources/css/reset.css rename src/main/{webapp/resources => resources/static/staticResources}/img/favicon.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/loading-new-nero@2x.gif (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/loading-new@2x.gif (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/loading-newB-nero@2x.gif (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/lodview-sprite.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/lodview-sprite@2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/lodview_sharer.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/logo-footer-lodview.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/logo-footer-lodview@2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/logo-header-lodview.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/logo-header-lodview@2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/no_image.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/no_image@2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/owl404.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/owl404@2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/owl500.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/owl500@2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/segnaposto-immagine.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/segnaposto-immagine@2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/separatoreCorto.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/img/separatoreCorto@2x.png (100%) create mode 100644 src/main/resources/static/staticResources/style.css rename src/main/{webapp/resources => resources/static/staticResources}/vendor/jplayercircle/circle.skin/buffer.png (100%) create mode 100644 src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/circle.player.css rename src/main/{webapp/resources => resources/static/staticResources}/vendor/jplayercircle/circle.skin/controls.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/jplayercircle/circle.skin/progress.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/jplayercircle/circle.skin/progress_sprite.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/jplayercircle/js/Jplayer.swf (100%) create mode 100644 src/main/resources/static/staticResources/vendor/jplayercircle/js/circle.player.js create mode 100644 src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.grab.js create mode 100644 src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.jplayer.js create mode 100644 src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.transform.js rename src/main/{webapp/resources => resources/static/staticResources}/vendor/jplayercircle/js/mod.csstransforms.min.js (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/jquery.min.js (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/leaflet/images/layers-2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/leaflet/images/layers.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/leaflet/images/marker-icon-2x.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/leaflet/images/marker-icon.png (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/leaflet/images/marker-shadow.png (100%) create mode 100644 src/main/resources/static/staticResources/vendor/leaflet/leaflet.css rename src/main/{webapp/resources => resources/static/staticResources}/vendor/leaflet/leaflet.js (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/masonry.pkgd.min.js (100%) rename src/main/{webapp/resources => resources/static/staticResources}/vendor/modernizr-custom.min.js (100%) delete mode 100644 src/main/webapp/WEB-INF/spring/appServlet/controllers.xml delete mode 100644 src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml delete mode 100644 src/main/webapp/WEB-INF/spring/root-context.xml delete mode 100644 src/main/webapp/WEB-INF/web.xml delete mode 100644 src/main/webapp/resources/css/commons.css delete mode 100644 src/main/webapp/resources/css/commons_2x.css delete mode 100644 src/main/webapp/resources/css/custom.css delete mode 100644 src/main/webapp/resources/css/reset.css delete mode 100644 src/main/webapp/resources/style.css delete mode 100644 src/main/webapp/resources/vendor/jplayercircle/circle.skin/circle.player.css delete mode 100644 src/main/webapp/resources/vendor/jplayercircle/js/circle.player.js delete mode 100644 src/main/webapp/resources/vendor/jplayercircle/js/jquery.grab.js delete mode 100644 src/main/webapp/resources/vendor/jplayercircle/js/jquery.jplayer.js delete mode 100644 src/main/webapp/resources/vendor/jplayercircle/js/jquery.transform.js delete mode 100644 src/main/webapp/resources/vendor/leaflet/leaflet.css create mode 100644 src/test/java/it/innovaway/lodviewng/LodviewNgApplicationTests.java diff --git a/.gitignore b/.gitignore index 3e8ce0d..6405921 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,86 @@ +################# +# Jetbrains IDEs +################# + + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + ################# ## Eclipse ################# @@ -70,14 +153,12 @@ build/ *.tlb *.tli *.tlh -*.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb -*.log *.scc # Visual C++ cache files @@ -196,7 +277,6 @@ $RECYCLE.BIN/ *.egg *.egg-info dist/ -build/ eggs/ parts/ var/ @@ -216,3 +296,53 @@ pip-log.txt #Mr Developer .mr.developer.cfg + +############# +## Other exclusions from gitignore.io +############# + +# Created by http://gitignore.io + +### Linux ### +.* +!.gitignore +!.git* + +### OSX ### +.AppleDouble +.LSOverride +Icon + + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +### Java ### +*.class + +# Package Files # +*.jar +*.war +*.ear + +### Gradle ### +# Exclude Folder List # +.gradle/ + +### Eclipse ### +bin/** +tmp/** +tmp/**/* + +### IntelliJ ### +*.iml +*.ipr + +### SVN ### +.svn/ + +/bin diff --git a/Dockerfile b/Dockerfile index c420f20..c3c5b4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,10 @@ -FROM maven:3-jdk-8 AS builder +FROM openjdk:11-jdk-slim as builder WORKDIR /app COPY . /app -RUN mvn compile war:war +RUN ./gradlew clean build -FROM tomcat:7 -LABEL maintainer=adrian.gschwend@zazuko.com -ENV CATALINA_OPTS="-XX:+UseSerialGC" -COPY --from=builder /app/target/lodview.war /usr/local/tomcat/webapps/lodview.war -CMD ["catalina.sh", "run"] +FROM openjdk:11-jdk-slim +LABEL maintainer=g.nespolino@gmail.com +COPY --from=builder /app/build/libs/lodview.jar /lodview.jar +CMD ["java", "-jar", "/lodview.jar"] EXPOSE 8080 8009 diff --git a/WebContent/META-INF/MANIFEST.MF b/WebContent/META-INF/MANIFEST.MF deleted file mode 100644 index 254272e..0000000 --- a/WebContent/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..e167181 --- /dev/null +++ b/build.gradle @@ -0,0 +1,84 @@ +plugins { + id 'org.springframework.boot' version '2.7.4' + id 'io.spring.dependency-management' version '1.0.14.RELEASE' + id 'java' + id "org.owasp.dependencycheck" version "7.3.0" + id 'com.github.spotbugs' version '4.5.1' +} + +group = 'it.innovaway' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.apache.jena:apache-jena-libs:4.6.1' + implementation 'org.apache.tomcat.embed:tomcat-embed-jasper' + implementation 'org.apache.commons:commons-text:1.10.0' + implementation 'jstl:jstl:1.2' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +spotbugsMain { + excludeFilter = file("${rootProject.projectDir}/config/spotbugs/exclude-filter.xml") + reports { + html { + enabled = true + destination = file("$buildDir/reports/spotbugs/main/spotbugs.html") + stylesheet = 'fancy-hist.xsl' + } + } +} + +spotbugsTest { + excludeFilter = file("${rootProject.projectDir}/config/spotbugs/exclude-filter.xml") + reports { + html { + enabled = true + destination = file("$buildDir/reports/spotbugs/test/spotbugs.html") + stylesheet = 'fancy-hist.xsl' + } + } +} + +dependencyCheck { +//set up a quality gate for vulnerabilities with high severity level: +//let's consider that a vulnerability has a high severity level if its CVSS score is higher than 7 +//the build is going to fail if vulnerabilities with high severity level found + failBuildOnCVSS = 7 + +//specify a list of known issues which contain: +//false-positives +//confirmed vulnerabilities which are not fixed yet, but we have a ticket for that + suppressionFile = 'config/dependency-check/dependency-check-known-issues.xml' +} + +gradle.taskGraph.whenReady { graph -> + if (graph.hasTask(build)) { + spotbugsMain.enabled = false + dependencyCheckAnalyze.enabled = false + spotbugsTest.enabled = false + } +} + +bootJar { + enabled = true + archiveName("lodview.jar") +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f127cfd --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 443a022..0000000 --- a/pom.xml +++ /dev/null @@ -1,161 +0,0 @@ - - 4.0.0 - lodview - lodview - 1.2.1-SNAPSHOT - war - lodview - lodview - - UTF-8 - 1.7 - 4.2.4.RELEASE - 1.8.1 - 1.6.1 - - - - org.apache.jena - apache-jena-libs - pom - 2.13.0 - - - - - org.springframework - spring-context - ${org.springframework-version} - - - - commons-logging - commons-logging - - - - - org.springframework - spring-webmvc - ${org.springframework-version} - - - - - org.slf4j - slf4j-api - 1.7.1 - - - org.slf4j - jcl-over-slf4j - 1.7.1 - - - ch.qos.logback - logback-classic - 1.0.7 - - - - - javax.servlet - javax.servlet-api - 3.0.1 - provided - - - javax.servlet.jsp - jsp-api - 2.1 - provided - - - javax.servlet.jsp.jstl - jstl-api - 1.2 - - - javax.servlet - servlet-api - - - - - org.glassfish.web - jstl-impl - 1.2 - - - javax.servlet - servlet-api - - - - - - - - org.apache.commons - commons-lang3 - 3.3.1 - - - org.springframework.boot - spring-boot-starter-integration - 1.1.4.RELEASE - - - - - ${project.artifactId} - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - ${java-version} - ${java-version} - - - - org.apache.maven.plugins - maven-dependency-plugin - - - install - install - - sources - - - - - - org.apache.tomcat.maven - tomcat7-maven-plugin - 2.2 - - - org.eclipse.jetty - jetty-maven-plugin - 9.2.7.v20150116 - - - /${project.artifactId} - - - - - - - - io.spring.repo.maven.milestone - http://repo.spring.io/milestone/ - - false - - - - diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..63a16d7 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'lodview-ng' diff --git a/src/main/java/it/innovaway/lodviewng/LodviewNgApplication.java b/src/main/java/it/innovaway/lodviewng/LodviewNgApplication.java new file mode 100644 index 0000000..56b0ecd --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/LodviewNgApplication.java @@ -0,0 +1,20 @@ +package it.innovaway.lodviewng; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +@SpringBootApplication +public class LodviewNgApplication extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(LodviewNgApplication.class, args); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { + return builder.sources(LodviewNgApplication.class); + } + +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/bean/OntologyBean.java b/src/main/java/it/innovaway/lodviewng/lodview/bean/OntologyBean.java new file mode 100644 index 0000000..4c4fc33 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/bean/OntologyBean.java @@ -0,0 +1,82 @@ +package it.innovaway.lodviewng.lodview.bean; + + +import it.innovaway.lodviewng.lodview.utils.ResourceClassPathLoader; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.text.StringEscapeUtils; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.NodeIterator; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.util.FileManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.util.List; + +@Component +@Slf4j +public class OntologyBean { + + private static final String DEFAULT_VALUE = ""; + @Getter + private final Model model = ModelFactory.createDefaultModel(); + @Value("${lode.ontoDir}") + private String ontoDir; + + @PostConstruct + void init() { + List ontologies = ResourceClassPathLoader.toFiles(ontoDir); + + if (ontologies.isEmpty()) { + log.debug("no ontologies found in directory " + ontoDir); + return; + } + + ontologies.forEach(file -> { + if (!file.isDirectory()) { + try { + log.info("Parsing ontology " + file.getName()); + log.debug("loading " + file.getCanonicalPath()); + FileManager.get().readModel(model, file.getAbsolutePath()); + log.debug("read successfully!"); + } catch (Exception e) { + log.error("error loading " + e.getMessage()); + } + } + }); + } + + public String getValue(String what, String preferredLanguage, String iri) { + Resource resource = model.createResource(iri); + return getSingleValue(preferredLanguage, resource, "http://www.w3.org/2000/01/rdf-schema#" + what); + } + + public String getEscapedValue(String what, String preferredLanguage, String iri) { + return StringEscapeUtils.escapeHtml4(getValue(what, preferredLanguage, iri)); + } + + private String getSingleValue(String preferredLanguage, Resource iri, String prop) { + NodeIterator iter = model.listObjectsOfProperty(iri, model.createProperty(prop)); + String result = DEFAULT_VALUE; + boolean betterTitleMatch = false; + while (iter.hasNext()) { + RDFNode node = iter.nextNode(); + Literal l = node.asLiteral(); + if (!betterTitleMatch && (result.equals(DEFAULT_VALUE) || l.getLanguage().equals("en") || l.getLanguage().equals(preferredLanguage))) { + if (preferredLanguage.equals(l.getLanguage())) { + betterTitleMatch = true; + } + result = l.getLexicalForm(); + } + + } + return result; + } + +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/bean/PropertyBean.java b/src/main/java/it/innovaway/lodviewng/lodview/bean/PropertyBean.java new file mode 100644 index 0000000..449c6d0 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/bean/PropertyBean.java @@ -0,0 +1,12 @@ +package it.innovaway.lodviewng.lodview.bean; + +import lombok.Data; + +@Data +public class PropertyBean { + private String nsProperty; + private String property; + private String propertyUrl; + private String label = ""; + private String comment = ""; +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/bean/ResultBean.java b/src/main/java/it/innovaway/lodviewng/lodview/bean/ResultBean.java new file mode 100644 index 0000000..5eace7e --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/bean/ResultBean.java @@ -0,0 +1,113 @@ +package it.innovaway.lodviewng.lodview.bean; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Data +public class ResultBean { + + private String title; + private String latitude; + private String longitude; + private String mainIRI; + private PropertyBean descriptionProperty; + private PropertyBean typeProperty; + private List images; + private List linking; + private List videos; + private List audios; + private Map>> literals = new HashMap<>(); + private Map>> resources = new HashMap<>(); + private Map>> bnodes = new HashMap<>(); + + private void addEle(String iri, TripleBean tripleBean, Map>> ele) { + if (ele.get(iri) == null || ele.get(iri).get(tripleBean.getProperty()) == null) { + Map> a = ele.get(iri); + if (a == null) { + a = new LinkedHashMap<>(); + } + List b = new ArrayList<>(); + b.add(tripleBean); + a.put(tripleBean.getProperty(), b); + ele.put(iri, a); + } else { + Map> a = ele.get(iri); + List b = a.get(tripleBean.getProperty()); + b.add(tripleBean); + a.put(tripleBean.getProperty(), b); + ele.put(iri, a); + } + } + + private void removeEle(String iri, TripleBean tripleBean, Map>> ele) { + if (!(ele.get(iri) == null || ele.get(iri).get(tripleBean.getProperty()) == null)) { + Map> a = ele.get(iri); + List b = a.get(tripleBean.getProperty()); + b.remove(tripleBean); + a.put(tripleBean.getProperty(), b); + ele.put(iri, a); + } + } + + public void setLiterals(String iri, List localLiterals) { + for (TripleBean tripleBean : localLiterals) { + addEle(iri, tripleBean, literals); + } + } + + public void setBnodes(String iri, List localBnodes) { + for (TripleBean tripleBean : localBnodes) { + addEle(iri, tripleBean, bnodes); + } + } + + public void setResources(String iri, List localResources) { + for (TripleBean tripleBean : localResources) { + addEle(iri, tripleBean, resources); + } + } + + public Map> getResources(String iri) { + return resources.get(iri); + } + + public Map> getLiterals(String iri) { + return literals.get(iri); + } + + public Map> getBnodes(String IRI) { + return bnodes.get(IRI); + } + + public void addBnode(TripleBean tripleBean, String iri) { + addEle(iri, tripleBean, bnodes); + } + + public void addLiteral(TripleBean tripleBean, String iri) { + addEle(iri, tripleBean, literals); + } + + public void addResource(TripleBean tripleBean, String iri) { + addEle(iri, tripleBean, resources); + } + + public void removeBnode(TripleBean tripleBean, String iri) { + + removeEle(iri, tripleBean, bnodes); + } + + public void removeLiteral(TripleBean tripleBean, String iri) { + removeEle(iri, tripleBean, literals); + + } + + public void removeResource(TripleBean tripleBean, String iri) { + removeEle(iri, tripleBean, resources); + } + +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/bean/TripleBean.java b/src/main/java/it/innovaway/lodviewng/lodview/bean/TripleBean.java new file mode 100644 index 0000000..b1fb634 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/bean/TripleBean.java @@ -0,0 +1,18 @@ +package it.innovaway.lodviewng.lodview.bean; + +import lombok.Data; + +@Data +public class TripleBean { + private PropertyBean property; + private String nsValue; + private String type; + private String iri; + private String nsIri; + private String value; + private String dataType; + private String nsDataType; + private String lang; + private String url; + private boolean isLocal = false; +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/builder/ResourceBuilder.java b/src/main/java/it/innovaway/lodviewng/lodview/builder/ResourceBuilder.java new file mode 100644 index 0000000..7fecf00 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/builder/ResourceBuilder.java @@ -0,0 +1,328 @@ +package it.innovaway.lodviewng.lodview.builder; + + +import it.innovaway.lodviewng.lodview.bean.OntologyBean; +import it.innovaway.lodviewng.lodview.bean.ResultBean; +import it.innovaway.lodviewng.lodview.bean.TripleBean; +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import it.innovaway.lodviewng.lodview.endpoint.SPARQLEndPoint; +import it.innovaway.lodviewng.lodview.utils.Misc; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.RDFWriterI; +import org.apache.jena.riot.Lang; +import org.springframework.context.MessageSource; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +@NoArgsConstructor +@AllArgsConstructor +public class ResourceBuilder { + + public static final String ERROR_NO_CONTENT_NEGOTIATION = "error.noContentNegotiation"; + public static final String SORRY_BUT_CONTENT_NEGOTIATION_IS_NOT_SUPPORTED_BY_THE_IRI = "sorry but content negotiation is not supported by the IRI"; + public static final String LITERAL = "literal"; + public static final String IRI = "iri"; + public static final String BNODE = "bnode"; + public static final String SHOW_XML_DECLARATION = "showXMLDeclaration"; + public static final String RELATIVE_URIS = "relativeURIs"; + private MessageSource messageSource; + + public ResultBean buildHtmlResource(String iri, Locale locale, ConfigurationBean conf, OntologyBean ontoBean) throws Exception { + return buildHtmlResource(iri, locale, conf, ontoBean, false); + } + + public ResultBean buildHtmlResource(String iri, Locale locale, ConfigurationBean conf, OntologyBean ontoBean, boolean localMode) throws Exception { + ResultBean result = new ResultBean(); + List videos = new ArrayList<>(); + List audios = new ArrayList<>(); + List images = new ArrayList<>(); + List linking = new ArrayList<>(); + SPARQLEndPoint se = new SPARQLEndPoint(conf, ontoBean, locale.getLanguage()); + result.setMainIRI(iri); + + String preferredLanguage = conf.getPreferredLanguage(); + if (preferredLanguage.equals("auto")) { + preferredLanguage = locale.getLanguage(); + } + List triples; + if (conf.getEndPointUrl() != null && conf.getEndPointUrl().equals("<>")) { + localMode = true; + } + if (localMode) { + /* looking for data via content negotiation */ + Model m = ModelFactory.createDefaultModel(); + try { + m.read(iri); + } catch (Exception e) { + throw new Exception(messageSource.getMessage(ERROR_NO_CONTENT_NEGOTIATION, null, SORRY_BUT_CONTENT_NEGOTIATION_IS_NOT_SUPPORTED_BY_THE_IRI, locale)); + } + triples = se.doLocalQuery(m, iri, conf.getDefaultQueries()); + } else { + triples = se.doQuery(iri, conf.getDefaultQueries(), null); + } + boolean betterTitleMatch = false; + boolean betterDescrMatch = false; + for (TripleBean tripleBean : triples) { + + if (tripleBean.getIri() == null) { + tripleBean.setIri(iri); + tripleBean.setNsIri(Misc.toNsResource(tripleBean.getIri(), conf)); + } + + if (conf.getTitleProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getTitleProperties().contains(tripleBean.getProperty().getProperty())) { + if (tripleBean.getIri().equals(iri) && !betterTitleMatch && (result.getTitle() == null || result.getTitle().trim().equals("") || (tripleBean.getLang() != null && (preferredLanguage.equals(tripleBean.getLang()) || tripleBean.getLang().equals("en"))))) { + result.setTitle(Misc.stripHTML(tripleBean.getValue())); + if (preferredLanguage.equals(tripleBean.getLang())) { + betterTitleMatch = true; + } + } + } else if (conf.getDescriptionProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getDescriptionProperties().contains(tripleBean.getProperty().getProperty())) { + if (tripleBean.getIri().equals(iri) && !betterDescrMatch && (result.getDescriptionProperty() == null || (tripleBean.getLang() != null && (preferredLanguage.equals(tripleBean.getLang()) || tripleBean.getLang().equals("en"))))) { + result.setDescriptionProperty(tripleBean.getProperty()); + if (preferredLanguage.equals(tripleBean.getLang())) { + betterDescrMatch = true; + } + } + } else if (conf.getLatitudeProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getLatitudeProperties().contains(tripleBean.getProperty().getProperty())) { + result.setLatitude(tripleBean.getValue()); + } else if (conf.getLongitudeProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getLongitudeProperties().contains(tripleBean.getProperty().getProperty())) { + result.setLongitude(tripleBean.getValue()); + } else if (conf.getImageProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getImageProperties().contains(tripleBean.getProperty().getProperty())) { + images.add(tripleBean.getValue()); + } else if (conf.getAudioProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getAudioProperties().contains(tripleBean.getProperty().getProperty())) { + audios.add(tripleBean.getValue()); + } else if (conf.getVideoProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getVideoProperties().contains(tripleBean.getProperty().getProperty())) { + videos.add(tripleBean.getValue()); + } else if (conf.getLinkingProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getLinkingProperties().contains(tripleBean.getProperty().getProperty())) { + linking.add(tripleBean.getValue()); + } else if (conf.getTypeProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getTypeProperties().contains(tripleBean.getProperty().getProperty())) { + result.setTypeProperty(tripleBean.getProperty()); + } + + if (tripleBean.getType().equals(IRI)) { + tripleBean.setUrl(Misc.toBrowsableUrl(tripleBean.getValue(), conf)); + tripleBean.setNsValue(Misc.toNsResource(tripleBean.getValue(), conf)); + if (!tripleBean.getUrl().equals(tripleBean.getValue()) || tripleBean.getValue().startsWith(conf.getPublicUrlPrefix())) { + tripleBean.setLocal(true); + } + result.addResource(tripleBean, tripleBean.getIri()); + } else if (tripleBean.getType().equals(LITERAL)) { + result.addLiteral(tripleBean, tripleBean.getIri()); + } else if (tripleBean.getType().equals(BNODE)) { + result.addBnode(tripleBean, tripleBean.getIri()); + } + } + + result.setImages(images); + result.setLinking(linking); + result.setVideos(videos); + result.setAudios(audios); + + return result; + } + + public String buildRDFResource(String iri, String sparql, Lang lang, ConfigurationBean conf) throws Exception { + String result; + Model model = ModelFactory.createDefaultModel(); + model.setNsPrefixes(conf.getPrefixes()); + + SPARQLEndPoint se = new SPARQLEndPoint(conf, null, null); + model = se.extractData(model, iri, sparql, conf.getDefaultRawDataQueries()); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + RDFWriterI rdfWriter = model.getWriter(lang.getName()); + rdfWriter.setProperty(SHOW_XML_DECLARATION, "true"); + rdfWriter.setProperty(RELATIVE_URIS, ""); + + rdfWriter.write(model, baos, conf.getIRInamespace()); + + result = baos.toString(); + + return result; + } + + public String buildRDFResource(String iri, Model m, Lang lang, ConfigurationBean conf) throws Exception { + String result; + Model model = ModelFactory.createDefaultModel(); + model.setNsPrefixes(conf.getPrefixes()); + + SPARQLEndPoint se = new SPARQLEndPoint(conf, null, null); + model = se.extractLocalData(model, iri, m, conf.getDefaultRawDataQueries()); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + RDFWriterI rdfWriter = model.getWriter(lang.getName()); + rdfWriter.setProperty(SHOW_XML_DECLARATION, "true"); + rdfWriter.setProperty(RELATIVE_URIS, ""); + + rdfWriter.write(model, baos, conf.getIRInamespace()); + rdfWriter.setProperty(SHOW_XML_DECLARATION, "true"); + rdfWriter.setProperty(RELATIVE_URIS, ""); + + result = baos.toString(); + + return result; + } + + public ResultBean buildPartialHtmlResource(String IRI, String[] abouts, Locale locale, ConfigurationBean conf, OntologyBean ontoBean, List filterProperties) { + + SPARQLEndPoint se = new SPARQLEndPoint(conf, ontoBean, locale.getLanguage()); + ResultBean result = new ResultBean(); + List literals = new ArrayList<>(); + + String preferredLanguage = conf.getPreferredLanguage(); + if (preferredLanguage.equals("auto")) { + preferredLanguage = locale.getLanguage(); + } + + List triples = new ArrayList<>(); + + /* + * FIXME: make more distinct queries to avoid length limits, eg + * http://dati.camera.it/ocd/assemblea.rdf/a16 + */ + + StringBuilder filter = new StringBuilder(); + for (String titleProperty : filterProperties) { + if (titleProperty.toLowerCase().startsWith("http:")) { + filter.append("(?filterProperty = <").append(titleProperty).append(">)"); + } else { + filter.append("(?filterProperty = ").append(titleProperty).append(")"); + } + filter.append(" || "); + } + + for (String about : abouts) { + List sparqlQueries = new ArrayList<>(); + sparqlQueries.add(("select distinct ?o " + "{ <" + about + "> ?filterProperty ?o. FILTER (" + filter + "))} ").replaceAll("\\|\\| \\)", "")); + try { + + if (conf.getEndPointUrl().equals("<>")) { + /* looking for data via content negotiation */ + Model m = ModelFactory.createDefaultModel(); + try { + m.read(about); + } catch (Exception e) { + throw new Exception(messageSource.getMessage(ERROR_NO_CONTENT_NEGOTIATION, null, SORRY_BUT_CONTENT_NEGOTIATION_IS_NOT_SUPPORTED_BY_THE_IRI, locale)); + } + triples.addAll(se.doLocalQuery(m, about, sparqlQueries, about)); + } else { + triples.addAll(se.doQuery(null, sparqlQueries, about)); + } + + } catch (Exception e) { + } + } + + Map> l = new HashMap<>(); + + for (TripleBean tripleBean : triples) { + if (tripleBean.getType().equals(LITERAL)) { + List al = l.get(tripleBean.getProperty().getProperty()); + if (al == null) { + al = new ArrayList<>(); + } + al.add(tripleBean); + l.put(tripleBean.getProperty().getProperty(), al); + } + } + for (String about : l.keySet()) { + List al = l.get(about); + boolean betterTitleMatch = false; + TripleBean title = null; + for (TripleBean tripleBean : al) { + if (!betterTitleMatch && (title == null || title.getValue() == null || title.getValue().trim().equals("") || preferredLanguage.equals(tripleBean.getLang()) || tripleBean.getLang().equals("en"))) { + title = tripleBean; + if (preferredLanguage.equals(tripleBean.getLang())) { + betterTitleMatch = true; + } + } + } + if (title != null) { + literals.add(title); + } + } + result.setLiterals(IRI, literals); + return result; + } + + public ResultBean buildHtmlInverseResource(String iri, String property, int start, Locale locale, ConfigurationBean conf, OntologyBean ontoBean) throws Exception { + ResultBean result = new ResultBean(); + + SPARQLEndPoint se = new SPARQLEndPoint(conf, ontoBean, locale.getLanguage()); + String preferredLanguage = conf.getPreferredLanguage(); + if (preferredLanguage.equals("auto")) { + preferredLanguage = locale.getLanguage(); + } + if (property == null) { + /* counting */ + List resources = new ArrayList<>(); + List triples; + + if (conf.getEndPointUrl().equals("<>")) { + /* looking for data via content negotiation */ + Model m = ModelFactory.createDefaultModel(); + try { + m.read(iri); + } catch (Exception e) { + throw new Exception(messageSource.getMessage(ERROR_NO_CONTENT_NEGOTIATION, null, SORRY_BUT_CONTENT_NEGOTIATION_IS_NOT_SUPPORTED_BY_THE_IRI, locale)); + } + triples = se.doLocalQuery(m, iri, conf.getDefaultInversesCountQueries()); + } else { + triples = se.doQuery(iri, conf.getDefaultInversesCountQueries(), null); + } + + for (TripleBean tripleBean : triples) { + if (tripleBean.getType().equals(LITERAL)) { + resources.add(tripleBean); + } + } + + result.setResources(iri, resources); + + } else { + /* listing */ + List resources = new ArrayList<>(); + List triples; + + if (conf.getEndPointUrl().equals("<>")) { + /* looking for data via content negotiation */ + Model m = ModelFactory.createDefaultModel(); + try { + m.read(iri); + } catch (Exception e) { + throw new Exception(messageSource.getMessage(ERROR_NO_CONTENT_NEGOTIATION, null, SORRY_BUT_CONTENT_NEGOTIATION_IS_NOT_SUPPORTED_BY_THE_IRI, locale)); + } + triples = se.doLocalQuery(m, iri, property, start, conf.getDefaultInversesQueries(), null); + } else { + triples = se.doQuery(iri, property, start, conf.getDefaultInversesQueries(), null, null); + } + + Map controlList = new HashMap<>(); + for (TripleBean tripleBean : triples) { + if (tripleBean.getType().equals(LITERAL)) { + if (controlList.get(tripleBean.getProperty().getProperty()) == null || preferredLanguage.equals(tripleBean.getLang())) { + controlList.put(tripleBean.getProperty().getProperty(), tripleBean); + } + } + } + + controlList.forEach((ignored, tripleBean) -> resources.add(tripleBean)); + result.setResources(iri, resources); + + } + + return result; + } + + public ResultBean buildHtmlInverseResource(String iri, Locale locale, ConfigurationBean conf, OntologyBean ontoBean) throws Exception { + return buildHtmlInverseResource(iri, null, -1, locale, conf, ontoBean); + } +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBean.java b/src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBean.java new file mode 100644 index 0000000..baa2e50 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBean.java @@ -0,0 +1,228 @@ +package it.innovaway.lodviewng.lodview.conf; + + +import it.innovaway.lodviewng.lodview.utils.ResourceClassPathLoader; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.NodeIterator; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.ResIterator; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.riot.RDFDataMgr; +import org.springframework.web.context.ServletContextAware; + +import javax.servlet.ServletContext; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +@Slf4j +@Data +@RequiredArgsConstructor +public class ConfigurationBean implements ServletContextAware, Cloneable { + + private static final Random RAND = new Random(); + protected final String confFile; + protected Model confModel; + protected ServletContext context; + private String endPointType; + private String redirectionStrategy; + private String forceIriEncoding; + private String httpRedirectExcludeList; + private String homeUrl; + private String license; + private String httpRedirectSuffix; + private String httpRedirectPrefix; + private String endPointUrl; + private String IRInamespace; + private String contentEncoding; + private String staticResourceURL; + private String preferredLanguage; + private String publicUrlPrefix; + private String publicUrlSuffix = ""; + private String authUsername; + private String authPassword; + private String defaultInverseBehaviour = "collapse"; + private ColorStrategy colorStrategy = ColorStrategy.RANDOM; + private List defaultQueries; + private List defaultRawDataQueries; + private List defaultInversesQueries; + private List defaultInversesTest; + private List defaultInversesCountQueries; + private List typeProperties; + private List audioProperties; + private List imageProperties; + private List videoProperties; + private List linkingProperties; + private List titleProperties; + private List descriptionProperties; + private List longitudeProperties; + private List latitudeProperties; + private List colorPair; + private List skipDomains; + private List mainOntologiesPrefixes; + private Map colorPairMatcher; + + public ColorStrategy getColorStrategy() { + return colorStrategy; + } + + public void populateBean() throws Exception { + log.debug("Initializing configuration " + confFile); + + confModel = RDFDataMgr.loadModel(ResourceClassPathLoader.toFile("conf/" + confFile).getAbsolutePath()); + + endPointUrl = getSingleConfValue("endpoint"); + endPointType = getSingleConfValue("endpointType", ""); + authPassword = getSingleConfValue("authPassword"); + authUsername = getSingleConfValue("authUsername"); + forceIriEncoding = getSingleConfValue("forceIriEncoding", "auto"); + redirectionStrategy = getSingleConfValue("redirectionStrategy", ""); + + IRInamespace = getSingleConfValue("IRInamespace", ""); + + httpRedirectSuffix = getSingleConfValue("httpRedirectSuffix", ""); + httpRedirectPrefix = getSingleConfValue("httpRedirectPrefix", ""); + httpRedirectExcludeList = getSingleConfValue("httpRedirectExcludeList", ""); + + publicUrlPrefix = getSingleConfValue("publicUrlPrefix", ""); + publicUrlPrefix = publicUrlPrefix.replaceAll("^(.+/)?auto$", context.getContextPath() + "/"); + + contentEncoding = getSingleConfValue("contentEncoding"); + staticResourceURL = getSingleConfValue("staticResourceURL", ""); + homeUrl = getSingleConfValue("homeUrl", "/"); + staticResourceURL = staticResourceURL.replaceAll("^(.+/)?auto$", context.getContextPath() + "/staticResources/"); + + preferredLanguage = getSingleConfValue("preferredLanguage"); + + typeProperties = getMultiConfValue("typeProperties"); + titleProperties = getMultiConfValue("titleProperties"); + descriptionProperties = getMultiConfValue("descriptionProperties"); + imageProperties = getMultiConfValue("imageProperties"); + audioProperties = getMultiConfValue("audioProperties"); + videoProperties = getMultiConfValue("videoProperties"); + linkingProperties = getMultiConfValue("linkingProperties"); + longitudeProperties = getMultiConfValue("longitudeProperties"); + latitudeProperties = getMultiConfValue("latitudeProperties"); + + defaultQueries = getMultiConfValue("defaultQueries"); + defaultRawDataQueries = getMultiConfValue("defaultRawDataQueries"); + + defaultInversesQueries = getMultiConfValue("defaultInversesQueries"); + defaultInversesTest = getMultiConfValue("defaultInversesTest"); + defaultInversesCountQueries = getMultiConfValue("defaultInversesCountQueries"); + + defaultInverseBehaviour = getSingleConfValue("defaultInverseBehaviour", defaultInverseBehaviour); + mainOntologiesPrefixes = getMultiConfValue("mainOntologiesPrefixes"); + + license = getSingleConfValue("license", ""); + + colorPair = getMultiConfValue("colorPair"); + + if (colorPair.size() == 1 && colorPair.get(0).startsWith("http://")) { + String colorPairSuffix = colorPair.get(0).replace("http://lodview.it/conf#", ""); + if ("byClass".equals(colorPairSuffix)) { + colorStrategy = ColorStrategy.CLASS; + } else if ("byPrefix".equals(colorPairSuffix)) { + colorStrategy = ColorStrategy.PREFIX; + } + colorPairMatcher = populateColorPairMatcher(); + } + + skipDomains = getMultiConfValue("skipDomains"); + } + + private Map populateColorPairMatcher() { + Map result = new HashMap<>(); + ResIterator iter = confModel.listSubjectsWithProperty(confModel.createProperty(confModel.getNsPrefixURI("conf"), "hasColorPair")); + while (iter.hasNext()) { + Resource res = iter.next(); + NodeIterator values = confModel.listObjectsOfProperty(res, confModel.createProperty(confModel.getNsPrefixURI("conf"), "hasColorPair")); + if (values.hasNext()) { + result.put(res.toString(), values.next().toString()); + } + } + return result; + } + + private String getSingleConfValue(String prop) { + return getSingleConfValue(prop, null); + } + + private String getSingleConfValue(String prop, String defaultValue) { + String value = System.getenv("LodView" + prop); + if (value != null) { + return value; + } + NodeIterator iter = confModel.listObjectsOfProperty(confModel.createProperty(confModel.getNsPrefixURI("conf"), prop)); + if (iter.hasNext()) { + return iter.next().toString(); + } + return defaultValue; + } + + private List getMultiConfValue(String prop) { + List result = new ArrayList<>(); + NodeIterator iter = confModel.listObjectsOfProperty(confModel.createProperty(confModel.getNsPrefixURI("conf"), prop)); + while (iter.hasNext()) { + RDFNode node = iter.next(); + result.add(node.toString()); + } + return result; + } + + @Override + public void setServletContext(ServletContext arg0) { + this.context = arg0; + try { + populateBean(); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public Map getPrefixes() { + return confModel.getNsPrefixMap(); + } + + public String getNsPrefixURI(String prefix) { + return confModel.getNsPrefixURI(prefix); + } + + public String getNsURIPrefix(String iri) { + return confModel.getNsURIPrefix(iri); + } + + public String getRandomColorPair() { + if (colorStrategy != ColorStrategy.RANDOM) { + return "#914848-#7d3e3e"; + } + int randomNum = RAND.nextInt(colorPair.size()); + return colorPair.get(randomNum); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new Error("Something impossible just happened"); + } + } + + public String getForceIriEncoding() { + return forceIriEncoding; + } + + public String getEndPointType() { + return endPointType; + } + + public enum ColorStrategy {RANDOM, CLASS, PREFIX} + +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBeanConfig.java b/src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBeanConfig.java new file mode 100644 index 0000000..629467c --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/conf/ConfigurationBeanConfig.java @@ -0,0 +1,18 @@ +package it.innovaway.lodviewng.lodview.conf; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ConfigurationBeanConfig { + + @Bean + public ConfigurationBean conf() { + return new ConfigurationBean("conf.ttl"); + } + + @Bean + public ConfigurationBean confLinked() { + return new ConfigurationBean("conf-linked.ttl"); + } +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/controllers/ErrorController.java b/src/main/java/it/innovaway/lodviewng/lodview/controllers/ErrorController.java new file mode 100644 index 0000000..deaae88 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/controllers/ErrorController.java @@ -0,0 +1,91 @@ +package it.innovaway.lodviewng.lodview.controllers; + +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +@Controller +@Slf4j +@RequiredArgsConstructor +public class ErrorController { + + public static final String COLOR_PAIR = "colorPair"; + private final ConfigurationBean conf; + + /* TODO: change the handler to send "error" param to the client */ + @ResponseStatus(value = HttpStatus.NOT_ACCEPTABLE, reason = "unhandled encoding") + @RequestMapping(value = "/406") + public String error406(HttpServletResponse res, ModelMap model, @CookieValue(value = COLOR_PAIR) String colorPair) { + log.error("not acceptable"); + model.addAttribute("statusCode", "406"); + model.addAttribute("conf", conf); + colors(colorPair, res, model); + res.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); + return "error"; + } + + @RequestMapping(value = "/404") + public String error404(HttpServletResponse res, ModelMap model, @RequestParam(value = "error", defaultValue = "") String error, @CookieValue(value = COLOR_PAIR, defaultValue = "") String colorPair, @RequestParam(value = "IRI", defaultValue = "") String IRI, @RequestParam(value = "endpoint", defaultValue = "") String endpoint) { + log.error("not found " + error + " -- " + IRI + " -- " + endpoint); + /* spring bug? */ + model.addAttribute("IRI", IRI); + model.addAttribute("endpoint", endpoint); + model.addAttribute("error", error); + model.addAttribute("conf", conf); + colors(colorPair, res, model); + model.addAttribute("statusCode", "404"); + res.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error"; + } + + @RequestMapping(value = "/400") + public String error400(HttpServletResponse res, ModelMap model, @RequestParam(value = "IRI", defaultValue = "") String IRI, @CookieValue(value = COLOR_PAIR, defaultValue = "") String colorPair) { + log.error("error on " + IRI); + /* spring bug? */ + model.addAttribute("IRI", IRI.replaceAll("(http://.+),http://.+", "$1")); + model.addAttribute("conf", conf); + colors(colorPair, res, model); + model.addAttribute("statusCode", "400"); + res.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); + return "error"; + } + + @RequestMapping(value = {"/500", "/error"}) + public String error500(HttpServletResponse res, ModelMap model, @RequestParam(value = "error", defaultValue = "") String error, @CookieValue(value = COLOR_PAIR, defaultValue = "") String colorPair, @RequestParam(value = "IRI", defaultValue = "") String IRI, @RequestParam(value = "endpoint", defaultValue = "") String endpoint) { + log.error("error on " + error + " -- " + IRI + " -- " + endpoint); + /* spring bug? */ + model.addAttribute("IRI", IRI); + model.addAttribute("endpoint", endpoint); + model.addAttribute("error", error); + model.addAttribute("conf", conf); + colors(colorPair, res, model); + model.addAttribute("statusCode", "500"); + res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return "error"; + } + + private void colors(String colorPair, HttpServletResponse res, ModelMap model) { + if (colorPair.equals("")) { + colorPair = conf.getRandomColorPair(); + Cookie c = new Cookie(COLOR_PAIR, colorPair); + c.setPath("/"); + res.addCookie(c); + } + if (conf != null && conf.getColorPairMatcher() != null && conf.getColorPairMatcher().size() > 0) { + model.addAttribute(COLOR_PAIR, conf.getColorPairMatcher().get("http://lodview.it/conf#otherClasses")); + } else { + model.addAttribute(COLOR_PAIR, colorPair); + } + + } +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/controllers/LinkedResourcesController.java b/src/main/java/it/innovaway/lodviewng/lodview/controllers/LinkedResourcesController.java new file mode 100644 index 0000000..fc08403 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/controllers/LinkedResourcesController.java @@ -0,0 +1,134 @@ +package it.innovaway.lodviewng.lodview.controllers; + +import it.innovaway.lodviewng.lodview.bean.OntologyBean; +import it.innovaway.lodviewng.lodview.bean.PropertyBean; +import it.innovaway.lodviewng.lodview.bean.ResultBean; +import it.innovaway.lodviewng.lodview.bean.TripleBean; +import it.innovaway.lodviewng.lodview.builder.ResourceBuilder; +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import it.innovaway.lodviewng.lodview.utils.Misc; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.MessageSource; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import static org.apache.commons.text.StringEscapeUtils.escapeHtml4; +import static org.apache.commons.text.StringEscapeUtils.escapeXml11; + +@Controller +@RequiredArgsConstructor +@Slf4j +public class LinkedResourcesController { + + public static final String ERROR_LINKED_RESOURCE_UNAVAILABLE = "error.linkedResourceUnavailable"; + public static final String UNABLE_TO_RETRIEVE_DATA = "unable to retrieve data"; + public static final String ROOT = ""; + public static final String RESOURCE_ABOUT = "\n"); + try { + ResultBean results = new ResourceBuilder(messageSource).buildPartialHtmlResource(IRI, abouts, locale, conf, null, conf.getTitleProperties()); + Map> literals = results.getLiterals(IRI); + if (literals == null || literals.size() == 0) { + return ("" + messageSource.getMessage("error.noLiteral", null, "no literal values where found", locale) + ""); + } + for (PropertyBean key : literals.keySet()) { + for (TripleBean tripleBean : literals.get(key)) { + result.append(RESOURCE_ABOUT) + .append(escapeXml11(tripleBean.getProperty().getProperty())) + .append(NSABOUT) + .append(escapeXml11(tripleBean.getProperty().getNsProperty())) + .append("\"><![CDATA[") + .append(escapeHtml4(Misc.stripHTML(tripleBean.getValue()))) + .append("]]>\n"); + } + } + result.append(ROOT); + return result.toString(); + } catch (Exception e) { + // 404? + return ("" + messageSource.getMessage(ERROR_LINKED_RESOURCE_UNAVAILABLE, null, UNABLE_TO_RETRIEVE_DATA, locale) + "" + e.getMessage() + ""); + } + } + + @ResponseBody + @RequestMapping(value = "/linkedResourceInverses", produces = "application/xml;charset=UTF-8") + public String resourceInversesController(ModelMap model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "IRI") String IRI, @RequestParam(value = "property", defaultValue = "") String property, @RequestParam(value = "start", defaultValue = "-1") int start) throws Exception { + return resourceInverses(model, conf, ontoBean, req, res, locale, IRI, property, start); + } + + public String resourceInverses(ModelMap model, ConfigurationBean conf, OntologyBean ontoBean, HttpServletRequest req, HttpServletResponse res, Locale locale, String IRI, String property, int start) throws Exception { + StringBuilder result = new StringBuilder("\n"); + if (property.equals("")) { + try { + ResultBean results = new ResourceBuilder(messageSource).buildHtmlInverseResource(IRI, locale, conf, ontoBean); + Map> resources = results.getResources(IRI); + if (resources != null) { + for (PropertyBean key : resources.keySet()) { + for (TripleBean tripleBean : resources.get(key)) { + if (tripleBean.getProperty().getProperty() == null || tripleBean.getProperty().getProperty().equals("")) { + throw new Exception("no content"); + } + result.append(RESOURCE_ABOUT).append(escapeXml11(tripleBean.getProperty().getProperty())) + .append(NSABOUT).append(escapeXml11(tripleBean.getProperty().getNsProperty())) + .append("\" propertyurl=\"").append(escapeXml11(tripleBean.getProperty().getPropertyUrl())) + .append("\" propertylabel=\"").append(escapeXml11(Misc.stripHTML(tripleBean.getProperty().getLabel()))) + .append("\" propertycomment=\"").append(escapeXml11(tripleBean.getProperty().getComment())) + .append("\" >\n"); + } + } + } + + result.append(ROOT); + return result.toString(); + + } catch (Exception e) { + return ("" + messageSource.getMessage(ERROR_LINKED_RESOURCE_UNAVAILABLE, null, UNABLE_TO_RETRIEVE_DATA, locale) + "" + e.getMessage() + ""); + } + } else { + + /* retrieving inverse relations */ + try { + ResultBean results = new ResourceBuilder(messageSource).buildHtmlInverseResource(IRI, property, start, locale, conf, null); + Map> resources = results.getResources(IRI); + for (PropertyBean key : resources.keySet()) { + for (TripleBean tripleBean : resources.get(key)) { + result.append(RESOURCE_ABOUT).append(escapeXml11(tripleBean.getProperty().getProperty())) + .append(NSABOUT).append(escapeXml11(tripleBean.getProperty().getNsProperty())) + .append("\" propertyurl=\"").append(escapeXml11(tripleBean.getProperty().getPropertyUrl())) + .append("\" >\n"); + } + } + + result.append(ROOT); + return result.toString(); + + } catch (Exception e) { + e.printStackTrace(); + return ("" + messageSource.getMessage(ERROR_LINKED_RESOURCE_UNAVAILABLE, null, UNABLE_TO_RETRIEVE_DATA, locale) + ""); + } + } + } +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/controllers/LodController.java b/src/main/java/it/innovaway/lodviewng/lodview/controllers/LodController.java new file mode 100644 index 0000000..57a60cb --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/controllers/LodController.java @@ -0,0 +1,91 @@ +package it.innovaway.lodviewng.lodview.controllers; + +import it.innovaway.lodviewng.lodview.bean.ResultBean; +import it.innovaway.lodviewng.lodview.bean.TripleBean; +import it.innovaway.lodviewng.lodview.builder.ResourceBuilder; +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.text.StringEscapeUtils; +import org.springframework.context.MessageSource; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Locale; + +@Controller +@RequiredArgsConstructor +@Slf4j +public class LodController { + + public static final String LOD_CONTROLLER_RESOURCE_LOAD = " LodController.resource() - load - "; + private final ConfigurationBean confLinked; + private final MessageSource messageSource; + + + @ResponseBody + @RequestMapping(value = {"/linkedResource", "/lodview/linkedResource"}, produces = "application/xml;charset=UTF-8") + public String resource(HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "IRI") String IRI) throws Exception { + + if (confLinked.getSkipDomains().contains(IRI.replaceAll("http[s]*://([^/]+)/.*", "$1"))) { + return "" + // + StringEscapeUtils.escapeXml11(messageSource.getMessage("error.skipedDomain", null, "skiping this URI", locale)) + // + ""; + } + try { + log.info(LOD_CONTROLLER_RESOURCE_LOAD + IRI); + + ResultBean results = new ResourceBuilder(messageSource).buildHtmlResource(IRI, locale, confLinked, null, true); + + StringBuilder result = new StringBuilder("\n"); + + result.append("<![CDATA[" + StringEscapeUtils.escapeHtml4(results.getTitle()) + "]]>"); + + String lang = locale.getLanguage().toLowerCase(); + String descr = ""; + List descrProperties = results.getLiterals(IRI).get(results.getDescriptionProperty()); + if (descrProperties != null) { + for (TripleBean tripleBean : descrProperties) { + if (lang.equals(tripleBean.getLang())) { + descr = tripleBean.getValue(); + lang = tripleBean.getLang(); + break; + } else if (tripleBean.getLang().equals("en")) { + lang = tripleBean.getLang(); + descr = tripleBean.getValue(); + } else if (descr.equals("")) { + descr = tripleBean.getValue(); + lang = tripleBean.getLang(); + } + } + } + + result.append(""); + + for (String img : results.getImages()) { + result.append(""); + } + for (String link : results.getLinking()) { + result.append(""); + } + result.append(""); + result.append(""); + result.append(""); + + result.append(""); + return result.toString(); + + } catch (Exception e) { + // e.printStackTrace(); + log.error(IRI + " unable to retrieve data " + e.getMessage()); + return "" + // + messageSource.getMessage("error.linkedResourceUnavailable", null, "unable to retrieve data", locale) + // + ""; + } + } +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/controllers/ResourceController.java b/src/main/java/it/innovaway/lodviewng/lodview/controllers/ResourceController.java new file mode 100644 index 0000000..ca2b19f --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/controllers/ResourceController.java @@ -0,0 +1,388 @@ +package it.innovaway.lodviewng.lodview.controllers; + +import it.innovaway.lodviewng.lodview.bean.OntologyBean; +import it.innovaway.lodviewng.lodview.bean.ResultBean; +import it.innovaway.lodviewng.lodview.bean.TripleBean; +import it.innovaway.lodviewng.lodview.builder.ResourceBuilder; +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import it.innovaway.lodviewng.lodview.utils.Misc; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.jena.atlas.web.AcceptList; +import org.apache.jena.atlas.web.MediaType; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFLanguages; +import org.springframework.context.MessageSource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.view.RedirectView; +import org.springframework.web.util.UrlPathHelper; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; + +@Controller +@RequestMapping(value = "/") +@RequiredArgsConstructor +@Slf4j +public class ResourceController { + + final AcceptList offeringRDF = new AcceptList("text/turtle, application/turtle, " // + + "application/x-turtle, application/rdf+xml, " // + + "application/rdf+json, application/ld+json, " // + + "text/plain, application/n-triples, text/trig, " // + + "application/n-quads, application/x-trig, application/trig, " // + + "text/n-quads, text/nquads, application/trix+xml, " // + + "text/rdf+n3, application/n3, " // + + "text/n3"); + final AcceptList offeringResources = new AcceptList("text/html, application/xhtml+xml"); + private final ConfigurationBean conf; + private final OntologyBean ontoBean; + private final MessageSource messageSource; + + @RequestMapping(value = {"{path:(?!staticResources).*$}", "{path:(?!staticResources).*$}/**"}) + public Object resourceController(ModelMap model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "output", defaultValue = "") String output, @CookieValue(value = "colorPair", defaultValue = "") String colorPair) throws UnsupportedEncodingException { + if (colorPair.equals("")) { + colorPair = conf.getRandomColorPair(); + Cookie c = new Cookie("colorPair", colorPair); + c.setPath("/"); + res.addCookie(c); + } + return resource(conf, model, req, res, locale, output, "", colorPair); + } + + public Object resource(ConfigurationBean conf, ModelMap model, HttpServletRequest req, HttpServletResponse res, Locale locale, String output, String forceIRI, String colorPair) throws UnsupportedEncodingException { + + model.addAttribute("conf", conf); + + String IRIsuffix = new UrlPathHelper().getLookupPathForRequest(req).replaceAll("/lodview/", "/"); + String requestUrl = req.getRequestURI(); + + log.info("IRIsuffix " + IRIsuffix); + log.info("requestUrl " + requestUrl); + + model.addAttribute("path", new UrlPathHelper().getContextPath(req).replaceAll("/lodview/", "/")); + model.addAttribute("locale", locale.getLanguage()); + + boolean redirect = false; + boolean redirected = false; + boolean avoidRedirection = false; + + // managing redirections + if (!conf.getHttpRedirectExcludeList().equals("")) { + String[] excList = conf.getHttpRedirectExcludeList().split(","); + for (String exclude : excList) { + if (requestUrl.matches(exclude)) { + avoidRedirection = true; + break; + } + } + } + if (!avoidRedirection && !conf.getHttpRedirectSuffix().equals("")) { + // 303 dereferencing mode + if (IRIsuffix.matches(".+" + conf.getHttpRedirectSuffix() + "$")) { + // after redirect + IRIsuffix = IRIsuffix.replaceAll(conf.getHttpRedirectSuffix() + "$", ""); + + redirected = true; + } else { + // before redirect + redirect = true; + } + } else if (!avoidRedirection && !conf.getHttpRedirectPrefix().equals("")) { + // 303 dereferencing mode + if (IRIsuffix.matches("^" + conf.getHttpRedirectPrefix() + ".+")) { + // after redirect + IRIsuffix = IRIsuffix.replaceAll("^" + conf.getHttpRedirectPrefix(), ""); + if (conf.getRedirectionStrategy().equals("pubby")) { + IRIsuffix = "/resource/" + IRIsuffix; + IRIsuffix = IRIsuffix.replaceAll("//", "/"); + } + redirected = true; + } else { + // before redirect + redirect = true; + } + } + + IRIsuffix = IRIsuffix.replaceAll("^/", ""); + + String IRIprefix = conf.getIRInamespace().replaceAll("/$", ""); + String IRI = IRIprefix + "/" + IRIsuffix.replaceAll(" ", "%20"); + + if (forceIRI != null && !forceIRI.equals("")) { + IRI = forceIRI; + } + + if (conf.getForceIriEncoding().equals("encode")) { + String[] IRItokens = IRI.split("/"); + for (int i = 0; i < IRItokens.length; i++) { + IRItokens[i] = URLEncoder.encode(IRItokens[i], StandardCharsets.UTF_8); + } + IRI = StringUtils.join("/"); + } else if (conf.getForceIriEncoding().equals("decode")) { + String[] IRItokens = IRI.split("/"); + for (int i = 0; i < IRItokens.length; i++) { + IRItokens[i] = java.net.URLDecoder.decode(IRItokens[i], StandardCharsets.UTF_8); + } + IRI = StringUtils.join("/"); + } + + if (conf.getRedirectionStrategy().equals("pubby")) { + /* + * http://dbpedia.org/data/Barack_Obama.ntriples + * http://dbpedia.org/data/Barack_Obama.n3 + * http://dbpedia.org/data/Barack_Obama.json + * http://dbpedia.org/data/Barack_Obama.rdf + */ + if (requestUrl.matches(".+\\.(ntriples|n3|json|rdf)")) { + String outputType = ""; + String newUrl = requestUrl.replaceFirst("/data/", "/resource/").replaceAll("\\.(ntriples|n3|json|rdf)$", ""); + RedirectView r = new RedirectView(); + r.setExposeModelAttributes(false); + if (requestUrl.endsWith(".ntriples")) { + outputType = "text/plain"; + } else if (requestUrl.endsWith(".n3")) { + outputType = "text/turtle"; + } else if (requestUrl.endsWith(".json")) { + outputType = "application/rdf+json"; + } else if (requestUrl.endsWith(".rdf")) { + outputType = "application/rdf+xml"; + } + r.setUrl(newUrl + "?" + (req.getQueryString() != null ? req.getQueryString() + "&" : "") + "output=" + outputType); + return r; + } + } + + log.info("####################################################################"); + log.info("################# looking for " + IRI + " ################# "); + + String[] acceptedContent = req.getHeader("Accept").split(","); + if (redirected) { + acceptedContent = "text/html".split(","); + } + // log.trace("Accept " + req.getHeader("Accept")); + + AcceptList a = AcceptList.create(acceptedContent); + // log.trace("-- AcceptList: " + a); + // log.trace("-- OffertList: " + offeringRDF); + + MediaType matchItem = AcceptList.match(offeringRDF, a); + Lang lang = matchItem != null ? RDFLanguages.contentTypeToLang(matchItem.getContentTypeStr()) : null; + + // override content negotiation + if (!output.equals("")) { + try { + output = output.replaceAll("([a-zA-Z]) ([a-zA-Z])", "$1+$2"); + a = AcceptList.create(output.split(",")); + matchItem = AcceptList.match(offeringRDF, a); + lang = RDFLanguages.contentTypeToLang(matchItem.getContentTypeStr()); + } catch (Exception e) { + return new ErrorController(conf).error406(res, model, colorPair); + } + log.debug("override content type " + matchItem.getContentTypeStr()); + } + + try { + if (lang == null) { + matchItem = AcceptList.match(offeringResources, a); + // probably you are asking for an HTML page + if (matchItem != null) { + if (redirect && !redirected) { + return redirect(req, IRIsuffix); + } else { + return htmlResource(model, IRI, colorPair, locale, req, res); + } + } else { + return new ErrorController(conf).error406(res, model, colorPair); + } + } else { + return resourceRaw(conf, model, IRI, conf.getEndPointUrl(), matchItem.getContentTypeStr()); + } + } catch (Exception e) { + e.printStackTrace(); + if (e.getMessage() != null && e.getMessage().startsWith("404")) { + return new ErrorController(conf).error404(res, model, e.getMessage(), colorPair, IRI, conf.getEndPointUrl()); + } else { + return new ErrorController(conf).error500(res, model, e.getMessage(), colorPair, IRI, conf.getEndPointUrl()); + } + } + + } + + private String htmlResource(ModelMap model, String IRI, String colorPair, Locale locale, HttpServletRequest req, HttpServletResponse res) throws Exception { + model.addAttribute("contextPath", new UrlPathHelper().getContextPath(req)); + ResultBean r = new ResourceBuilder(messageSource).buildHtmlResource(IRI, locale, conf, ontoBean); + model.addAttribute("colorPair", Misc.guessColor(colorPair, r, conf)); + model.addAttribute("results", Misc.guessClass(r, conf, ontoBean)); + model.addAttribute("ontoBean", ontoBean); + + addDataLinks(IRI, model, req, locale); + addLodliveLink(locale, model, IRI); + enrichResponse(model, r, req, res); + return "resource"; + } + + private void addDataLinks(String IRI, ModelMap model, HttpServletRequest req, Locale locale) throws UnsupportedEncodingException { + + Map> rawdatalinks = new LinkedHashMap>(); + String queryString = (req.getQueryString() != null ? "&" + req.getQueryString().replaceAll("&", "&") : ""); + + if (conf.getRedirectionStrategy().equals("pubby")) { + + Map list = new LinkedHashMap(); + list.put("xml", "?output=" + URLEncoder.encode("application/rdf+xml", StandardCharsets.UTF_8) + queryString); + list.put("ntriples", "?output=" + URLEncoder.encode("text/plain", StandardCharsets.UTF_8) + queryString); + list.put("turtle", "?output=" + URLEncoder.encode("text/turtle", StandardCharsets.UTF_8) + queryString); + list.put("json", "?output=" + URLEncoder.encode("application/rdf+json", StandardCharsets.UTF_8) + queryString); + list.put("ld+json", "?output=" + URLEncoder.encode("application/ld+json", StandardCharsets.UTF_8) + queryString); + rawdatalinks.put("rdf:", list); + + } else { + Map list = new LinkedHashMap(); + list.put("xml", "?output=" + URLEncoder.encode("application/rdf+xml", StandardCharsets.UTF_8) + queryString); + list.put("ntriples", "?output=" + URLEncoder.encode("text/plain", StandardCharsets.UTF_8) + queryString); + list.put("turtle", "?output=" + URLEncoder.encode("text/turtle", StandardCharsets.UTF_8) + queryString); + list.put("ld+json", "?output=" + URLEncoder.encode("application/ld+json", StandardCharsets.UTF_8) + queryString); + rawdatalinks.put(messageSource.getMessage("footer.viewAs", null, "view as", locale), list); + } + + if (conf.getEndPointType().equals("virtuoso")) { + { + Map list = new LinkedHashMap(); + list.put("atom", conf.getEndPointUrl() + "?output=application%2Fatom%2Bxml&query=DESCRIBE+%3C" + IRI + "%3E"); + list.put("json", conf.getEndPointUrl() + "?output=application%2Fodata%2Bjson&query=DESCRIBE+%3C" + IRI + "%3E"); + rawdatalinks.put("odata:", list); + } + { + Map list = new LinkedHashMap(); + list.put("html", conf.getEndPointUrl() + "?output=text%2Fhtml&query=DESCRIBE+%3C" + IRI + "%3E"); + list.put("json", conf.getEndPointUrl() + "?output=application%2Fmicrodata%2Bjson&query=DESCRIBE+%3C" + IRI + "%3E"); + rawdatalinks.put("microdata:", list); + } + { + Map list = new LinkedHashMap(); + list.put("csv", conf.getEndPointUrl() + "?output=text%2Fcsv&query=DESCRIBE+%3C" + IRI + "%3E"); + list.put("cxml", conf.getEndPointUrl() + "?output=format=text%2Fcxml&query=DESCRIBE+%3C" + IRI + "%3E"); + rawdatalinks.put("rawdata:", list); + } + } + model.addAttribute("rawdatalinks", rawdatalinks); + + } + + private RedirectView redirect(HttpServletRequest req, String IRIsuffix) throws UnsupportedEncodingException { + + RedirectView r = new RedirectView(); + // preventing redirect of model attributes + r.setExposeModelAttributes(false); + r.setContentType("text/html"); + r.setHttp10Compatible(false); + if (!conf.getHttpRedirectPrefix().equals("")) { + // prefix mode + String redirectUrl = conf.getHttpRedirectPrefix().replaceAll("^/", ""); + if (conf.getRedirectionStrategy().equals("pubby")) { + r.setUrl(conf.getPublicUrlPrefix() + redirectUrl + IRIsuffix.replaceAll("^resource/", "") + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); + } else { + r.setUrl(conf.getPublicUrlPrefix().replaceAll(IRIsuffix + "$", "") + redirectUrl + IRIsuffix + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); + } + } else { + // suffix mode + String redirectUrl = conf.getHttpRedirectSuffix(); + // String[] redirectUrlArray = redirectUrl.split("/"); + // redirectUrl = ""; + // for (String string : redirectUrlArray) { + // redirectUrl += URLEncoder.encode(string, "UTF-8") + "/"; + // } + // redirectUrl = redirectUrl.replaceAll("/$", ""); + r.setUrl(req.getRequestURL() + redirectUrl + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); + } + + return r; + + } + + private void addLodliveLink(Locale locale, ModelMap model, String IRI) { + if (locale.getLanguage().equals("it")) { + model.addAttribute("lodliveUrl", "http://lodlive.it?" + IRI.replaceAll("#", "%23")); + } else if (locale.getLanguage().equals("fr")) { + model.addAttribute("lodliveUrl", "http://fr.lodlive.it?" + IRI.replaceAll("#", "%23")); + } else if (locale.getLanguage().equals("gl")) { + model.addAttribute("lodliveUrl", "http://gl.lodlive.it?" + IRI.replaceAll("#", "%23")); + } else { + model.addAttribute("lodliveUrl", "http://en.lodlive.it?" + IRI.replaceAll("#", "%23")); + } + } + + private void enrichResponse(ModelMap model, ResultBean r, HttpServletRequest req, HttpServletResponse res) { + + String publicUrl = r.getMainIRI(); + res.addHeader("Link", "<" + publicUrl + ">; rel=\"about\""); + + @SuppressWarnings("unchecked") + Map> rawdatalinks = (LinkedHashMap>) model.get("rawdatalinks"); + for (String k : rawdatalinks.keySet()) { + for (String k1 : rawdatalinks.get(k).keySet()) { + res.addHeader("Link", "<" + rawdatalinks.get(k).get(k1) + ">; rel=\"alternate\"; type=\"application/rdf+xml\"; title=\"Structured Descriptor Document (" + k1 + ")\""); + } + } + try { + for (TripleBean t : r.getResources(r.getMainIRI()).get(r.getTypeProperty())) { + res.addHeader("Link", "<" + t.getProperty().getProperty() + ">; rel=\"type\""); + } + } catch (Exception e) { + // TODO: handle exception + } + } + + @RequestMapping(value = "/rawdata") + public ResponseEntity resourceRawController(ModelMap model, @RequestParam(value = "IRI") String IRI, @RequestParam(value = "sparql") String sparql, @RequestParam(value = "contentType", defaultValue = "application/rdf+xml") String contentType) { + return resourceRaw(conf, model, IRI, sparql, contentType); + } + + public ResponseEntity resourceRaw(ConfigurationBean conf, ModelMap model, @RequestParam(value = "IRI") String IRI, @RequestParam(value = "sparql") String sparql, @RequestParam(value = "contentType", defaultValue = "application/rdf+xml") String contentType) { + // logger.trace("ResourceController.resourceRaw()"); + contentType = contentType.replaceAll("([a-zA-Z]) ([a-zA-Z])", "$1+$2"); + Lang lang = RDFLanguages.contentTypeToLang(contentType); + try { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", contentType + "; charset=" + conf.getContentEncoding()); + + if (sparql != null && sparql.equals("<>")) { + Model m = ModelFactory.createDefaultModel(); + try { + m.read(IRI); + } catch (Exception e) { + throw new Exception(messageSource.getMessage("error.noContentNegotiation", null, "sorry but content negotiation is not supported by the IRI", Locale.ENGLISH)); + } + return new ResponseEntity(new ResourceBuilder(messageSource).buildRDFResource(IRI, m, lang, conf), headers, HttpStatus.OK); + } else { + return new ResponseEntity(new ResourceBuilder(messageSource).buildRDFResource(IRI, sparql, lang, conf), headers, HttpStatus.OK); + } + + } catch (Exception e) { + if (e.getMessage() != null && e.getMessage().startsWith("404")) { + return new ResponseEntity(e.getMessage(), HttpStatus.NOT_FOUND); + } else { + return new ResponseEntity(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + } +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/controllers/SPARQLController.java b/src/main/java/it/innovaway/lodviewng/lodview/controllers/SPARQLController.java new file mode 100644 index 0000000..67b791c --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/controllers/SPARQLController.java @@ -0,0 +1,17 @@ +package it.innovaway.lodviewng.lodview.controllers; + +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequiredArgsConstructor +public class SPARQLController { + private final ConfigurationBean conf; + + @RequestMapping(value = {"/sparql", "/SPARQL"}) + public String sparql() { + return "redirect:" + conf.getEndPointUrl(); + } +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/controllers/StaticController.java b/src/main/java/it/innovaway/lodviewng/lodview/controllers/StaticController.java new file mode 100644 index 0000000..8c17d65 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/controllers/StaticController.java @@ -0,0 +1,73 @@ +package it.innovaway.lodviewng.lodview.controllers; + +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.MessageSource; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.util.UrlPathHelper; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Locale; + +@Controller +@RequiredArgsConstructor +@Slf4j +public class StaticController { + + + private final ConfigurationBean conf; + + private final MessageSource messageSource; + + @RequestMapping(value = "/") + public String home(HttpServletRequest req, HttpServletResponse res, Model model, Locale locale, @CookieValue(value = "colorPair", defaultValue = "") String colorPair) { + colorPair = conf.getRandomColorPair(); + Cookie c = new Cookie("colorPair", colorPair); + c.setPath("/"); + res.addCookie(c); + model.addAttribute("colorPair", colorPair); + model.addAttribute("conf", conf); + model.addAttribute("locale", locale.getLanguage()); + model.addAttribute("path", new UrlPathHelper().getContextPath(req).replaceAll("/lodview/", "/")); + log.debug("home controller"); + return "home"; + } + + @RequestMapping(value = "/lodviewmenu") + public String lodviewmenu(Model model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "IRI") String IRI, @CookieValue(value = "colorPair", defaultValue = "") String colorPair) { + if (colorPair.equals("")) { + colorPair = conf.getRandomColorPair(); + Cookie c = new Cookie("colorPair", colorPair); + c.setPath("/"); + res.addCookie(c); + } + return lodviewmenu(req, res, model, locale, IRI, conf, colorPair); + } + + @RequestMapping(value = {"/lodviewcolor", "/**/lodviewcolor"}) + public ResponseEntity lodviewcolor(Model model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "colorPair") String colorPair) { + Cookie c = new Cookie("colorPair", colorPair); + c.setPath("/"); + res.addCookie(c); + return new ResponseEntity<>(HttpStatus.OK); + } + + public String lodviewmenu(HttpServletRequest req, HttpServletResponse res, Model model, Locale locale, @RequestParam(value = "IRI", defaultValue = "") String IRI, ConfigurationBean conf, String colorPair) { + model.addAttribute("conf", conf); + model.addAttribute("locale", locale.getLanguage()); + model.addAttribute("IRI", IRI); + model.addAttribute("colorPair", colorPair); + model.addAttribute("path", new UrlPathHelper().getContextPath(req).replaceAll("/lodview/", "/")); + return "menu"; + } + +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/endpoint/SPARQLEndPoint.java b/src/main/java/it/innovaway/lodviewng/lodview/endpoint/SPARQLEndPoint.java new file mode 100644 index 0000000..8c6802f --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/endpoint/SPARQLEndPoint.java @@ -0,0 +1,341 @@ +package it.innovaway.lodviewng.lodview.endpoint; + + +import it.innovaway.lodviewng.lodview.bean.OntologyBean; +import it.innovaway.lodviewng.lodview.bean.PropertyBean; +import it.innovaway.lodviewng.lodview.bean.TripleBean; +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import it.innovaway.lodviewng.lodview.utils.Misc; +import org.apache.jena.graph.Node; +import org.apache.jena.http.auth.AuthEnv; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.sparql.exec.http.QueryExecutionHTTP; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public class SPARQLEndPoint { + + private static final Logger logger = LoggerFactory.getLogger(SPARQLEndPoint.class); + protected ConfigurationBean conf; + OntologyBean ontoBean; + String locale = "en"; + + public SPARQLEndPoint(ConfigurationBean conf, OntologyBean ontoBean, String locale) { + this.locale = locale; + this.ontoBean = ontoBean; + this.conf = conf; + // TODO Auto-generated constructor stub + } + + public List doQuery(String IRI, String aProperty, int start, List queries, String filter, String overrideProperty) throws Exception { + // logger.trace("executing query on " + conf.getEndPointUrl()); + List results = new ArrayList(); + if (conf.getAuthPassword() != null && !conf.getAuthPassword().equals("")) { + AuthEnv.get().registerUsernamePassword(URI.create(conf.getEndPointUrl()), conf.getAuthUsername(), conf.getAuthPassword()); + } + for (String query : queries) { + // logger.trace("-- " + parseQuery(query, IRI, aProperty, + // start, filter)); + QueryExecution qe = QueryExecutionHTTP.create() + .endpoint(conf.getEndPointUrl()) + .query(parseQuery(query, IRI, aProperty, start, filter)) + .build(); + //QueryExecutionFactory.sparqlService(conf.getEndPointUrl(), parseQuery(query, IRI, aProperty, start, filter), auth); + results = moreThenOneQuery(qe, results, 0, overrideProperty); + qe.close(); + } + + if (results.size() == 0) { + if (IRI != null) { + boolean hasInverses = false; + for (String query : conf.getDefaultInversesTest()) { + // logger.trace("query!!! " + parseQuery(query, IRI, + // aProperty, start, filter)); + QueryExecution qe = QueryExecutionHTTP.create() + .endpoint(conf.getEndPointUrl()) + .query(parseQuery(query, IRI, aProperty, start, filter)) + .build(); + if (!hasInverses) { + hasInverses = qe.execAsk(); + } + qe.close(); + } + if (!hasInverses) { + throw new Exception("404 - not found"); + } + } + } + return results; + } + + private List moreThenOneQuery(QueryExecution qe, List results, int retry, String overrideProperty) throws Exception { + + try { + ResultSet rs = qe.execSelect(); + while (rs.hasNext()) { + TripleBean rb = new TripleBean(); + QuerySolution qs = rs.next(); + String property = ""; + if (overrideProperty != null) { + property = overrideProperty; + } else if (qs.get("p") != null) { + property = qs.get("p").asNode().toString(); + } + + try { + if (qs.get("s") != null && !qs.get("s").asNode().toString().startsWith("http://")) { // probably + // a + // bn + rb.setIri(qs.get("s").asNode().toString()); + rb.setNsIri("_:" + rb.getIri()); + } else if (qs.get("s") != null && qs.get("s").asNode().toString().startsWith("http://")) { + rb.setIri(qs.get("s").asNode().toString()); + rb.setNsIri(Misc.toNsResource(rb.getIri(), conf)); + rb.setUrl(Misc.toBrowsableUrl(rb.getIri(), conf)); + } + + PropertyBean p = new PropertyBean(); + p.setNsProperty(Misc.toNsResource(property, conf)); + p.setProperty(property); + if (ontoBean != null) { + p.setLabel(ontoBean.getEscapedValue("label", locale, property)); + p.setComment(ontoBean.getEscapedValue("comment", locale, property)); + } + p.setPropertyUrl(Misc.toBrowsableUrl(property, conf)); + rb.setProperty(p); + if (qs.get("o") != null) { + Node object = qs.get("o").asNode(); + if (object.isURI()) { + rb.setType("iri"); + rb.setValue(object.toString(false)); + } else if (object.isLiteral()) { + rb.setType("literal"); + rb.setDataType(object.getLiteralDatatypeURI()); + rb.setNsDataType(Misc.toNsResource(object.getLiteralDatatypeURI(), conf)); + rb.setLang(object.getLiteralLanguage()); + rb.setValue(object.getLiteralLexicalForm()); + } else if (object.isBlank()) { + rb.setType("bnode"); + rb.setValue(object.toString(false)); + } + } else { + rb.setType("literal"); + rb.setValue(""); + } + results.add(rb); + } catch (Exception e) { + logger.error("error? " + e.getMessage()); + // e.printStackTrace(); + } + } + } catch (Exception ez) { + if (retry < 3) { + retry++; + // logger.trace("query failed (" + ez.getMessage() + + // "), I'm giving another chance (" + retry + "/3)"); + return moreThenOneQuery(qe, results, retry, overrideProperty); + } + ez.printStackTrace(); + throw new Exception("connection refused"); + } + + return results; + } + + public List doQuery(String IRI, List queries, String overrideProperty) throws Exception { + return doQuery(IRI, null, -1, queries, null, overrideProperty); + } + + public List doLocalQuery(Model m, String IRI, List queries, String about) throws Exception { + return doLocalQuery(m, IRI, null, -1, queries, about); + } + + public List doLocalQuery(Model model, String IRI, List queries) throws Exception { + return doLocalQuery(model, IRI, null, -1, queries, null); + } + + public List doLocalQuery(Model model, String IRI, String localProperty, int start, List queries, String overrideProperty) throws Exception { + // logger.trace("executing query on model based on " + IRI); + List results = new ArrayList(); + + for (String query : queries) { + QueryExecution qe = QueryExecutionFactory.create(parseQuery(query, IRI, localProperty, start, null), model); + try { + ResultSet rs = qe.execSelect(); + while (rs.hasNext()) { + TripleBean rb = new TripleBean(); + QuerySolution qs = rs.next(); + String property = ""; + if (overrideProperty != null) { + property = overrideProperty; + } else if (qs.get("p") != null) { + property = qs.get("p").asNode().toString(); + } + try { + if (qs.get("s") != null && !qs.get("s").asNode().toString().startsWith("http://")) { // probably + // blanknode + rb.setIri(qs.get("s").asNode().toString()); + rb.setNsIri("_:" + rb.getIri()); + } else if (qs.get("s") != null && qs.get("s").asNode().toString().startsWith("http://")) { + rb.setIri(qs.get("s").asNode().toString()); + rb.setNsIri(Misc.toNsResource(rb.getIri(), conf)); + rb.setUrl(Misc.toBrowsableUrl(rb.getIri(), conf)); + } + PropertyBean p = new PropertyBean(); + p.setNsProperty(Misc.toNsResource(property, conf)); + p.setProperty(property); + p.setPropertyUrl(Misc.toBrowsableUrl(property, conf)); + if (ontoBean != null) { + p.setLabel(ontoBean.getEscapedValue("label", locale, property)); + p.setComment(ontoBean.getEscapedValue("comment", locale, property)); + } + rb.setProperty(p); + if (qs.get("o") != null) { + Node object = qs.get("o").asNode(); + if (object.isURI()) { + rb.setType("iri"); + rb.setValue(object.toString(false)); + } else if (object.isLiteral()) { + rb.setType("literal"); + rb.setDataType(object.getLiteralDatatypeURI()); + rb.setNsDataType(Misc.toNsResource(object.getLiteralDatatypeURI(), conf)); + rb.setLang(object.getLiteralLanguage()); + rb.setValue(object.getLiteralLexicalForm()); + } else if (object.isBlank()) { + rb.setType("bnode"); + rb.setValue(object.toString(false)); + } + } else { + rb.setType("literal"); + rb.setValue(""); + } + results.add(rb); + } catch (Exception e) { + logger.error("error? " + e.getMessage()); + e.printStackTrace(); + } + } + } catch (Exception e) { + throw new Exception("500 - " + e.getMessage()); + } finally { + qe.close(); + } + } + + if (results.size() == 0) { + throw new Exception("404 - not found"); + } + return results; + } + + public Model extractData(Model result, String IRI, String sparql, List queries) throws Exception { + + // logger.trace("executing query on " + sparql); + Resource subject = result.createResource(IRI); + for (String query : queries) { + QueryExecution qe = QueryExecutionFactory.sparqlService(sparql, parseQuery(query, IRI, null, -1, null)); + try { + ResultSet rs = qe.execSelect(); + + List sl = new ArrayList(); + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + RDFNode subject2 = qs.get("s"); + RDFNode property = qs.get("p"); + RDFNode object = qs.get("o"); + Property property1 = result.createProperty(property.asNode().toString()); + result.add(result.createStatement(subject2 != null ? subject2.asResource() : subject, property1, object)); + } + result.add(sl); + } catch (Exception e) { + e.printStackTrace(); + throw new Exception("error in query execution: " + e.getMessage()); + } finally { + qe.close(); + } + } + return result; + } + + public Model extractLocalData(Model result, String IRI, Model m, List queries) throws Exception { + + // logger.trace("executing query on IRI"); + Resource subject = result.createResource(IRI); + for (String query : queries) { + QueryExecution qe = QueryExecutionFactory.create(parseQuery(query, IRI, null, -1, null), m); + try { + ResultSet rs = qe.execSelect(); + List sl = new ArrayList(); + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + RDFNode subject2 = qs.get("s"); + RDFNode property = qs.get("p"); + RDFNode object = qs.get("o"); + Property property1 = result.createProperty(property.asNode().toString()); + result.add(result.createStatement(subject2 != null ? subject2.asResource() : subject, property1, object)); + } + result.add(sl); + } catch (Exception e) { + e.printStackTrace(); + throw new Exception("error in query execution: " + e.getMessage()); + } finally { + qe.close(); + } + } + return result; + } + + public String parseQuery(String query, String IRI, String property, int start, String filter) { + if (IRI != null) { + /* managing issues depending on "$" in some IRIs */ + query = query.replaceAll("\\$\\{IRI\\}", IRI.replaceAll("\\$", "%24")).replaceAll("%24", "\\$"); + } + if (property != null) { + query = query.replaceAll("\\$\\{PROPERTY\\}", property); + } + if (filter != null) { + query = query.replaceAll("\\$\\{FILTERPROPERTY\\}", filter); + } + if (query.indexOf("STARTFROM") > 0) { + query = query.replaceAll("\\$\\{STARTFROM\\}", "" + start); + } else if (start > 0) { + query = query.replaceAll("LIMIT (.+)$", "OFFSET " + start + " LIMIT $1"); + } + return query; + } + + public String testEndpoint(ConfigurationBean conf) { + logger.info("testing connection on " + conf.getEndPointUrl()); + QueryExecution qe = QueryExecutionFactory.sparqlService(conf.getEndPointUrl(), "select ?s {?s ?p ?o} LIMIT 1"); + String msg = ""; + try { + ResultSet rs = qe.execSelect(); + if (rs.hasNext()) { + logger.info("is online"); + msg = "online"; + } else { + logger.info("is offline"); + msg = "offline"; + } + } catch (Exception e) { + logger.info("is offline " + e.getMessage()); + msg = "offline " + e.getMessage(); + } finally { + qe.close(); + } + return msg; + } + +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/utils/Misc.java b/src/main/java/it/innovaway/lodviewng/lodview/utils/Misc.java new file mode 100644 index 0000000..7f6d273 --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/utils/Misc.java @@ -0,0 +1,139 @@ +package it.innovaway.lodviewng.lodview.utils; + + +import it.innovaway.lodviewng.lodview.bean.OntologyBean; +import it.innovaway.lodviewng.lodview.bean.PropertyBean; +import it.innovaway.lodviewng.lodview.bean.ResultBean; +import it.innovaway.lodviewng.lodview.bean.TripleBean; +import it.innovaway.lodviewng.lodview.conf.ConfigurationBean; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.NodeIterator; +import org.apache.jena.rdf.model.RDFNode; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Misc { + + public static String toNsResource(String iri, ConfigurationBean conf) { + if (iri != null && !iri.equals("")) { + return conf.getNsURIPrefix(iri.replaceAll("[^/#]+$", "")) + ":" + iri.replaceAll(".+[/|#]([^/#]+)$", "$1"); + + } else { + return null; + } + } + + public static String toBrowsableUrl(String value, ConfigurationBean conf) { + + if (!conf.getPublicUrlSuffix().equals("") && value.startsWith(conf.getIRInamespace())) { + return conf.getPublicUrlPrefix() + "?" + conf.getPublicUrlSuffix() + "IRI=" + java.net.URLEncoder.encode(value, StandardCharsets.UTF_8); + } else { + return value + .replaceAll("^" + conf.getIRInamespace() + "(.+)$", conf.getPublicUrlPrefix() + "$1") + .replaceAll("([^:])//", "$1/") + .replaceAll("^//", "/"); + } + } + + public static String guessColor(String colorPair, ResultBean r, ConfigurationBean conf) { + switch (conf.getColorStrategy()) { + case CLASS: { + try { + List m = r.getResources(r.getMainIRI()).get(r.getTypeProperty()); + for (TripleBean tripleBean : m) { + colorPair = conf.getColorPairMatcher().get(tripleBean.getValue()); + if (colorPair != null) return colorPair; + } + } catch (Exception e) { + } + return conf.getColorPairMatcher().get("http://lodview.it/conf#otherClasses"); + } + case PREFIX: { + for (String prefix : conf.getColorPairMatcher().keySet()) { + if (r.getMainIRI().startsWith(prefix)) { + return conf.getColorPairMatcher().get(prefix); + } + } + break; + } + default: + break; + } + return colorPair; + } + + public static ResultBean guessClass(ResultBean r, ConfigurationBean conf, OntologyBean ontoBean) { + try { + if (!conf.getMainOntologiesPrefixes().isEmpty()) { + String mainIri = r.getMainIRI(); + Map> mainResource = r.getResources(mainIri); + TreeMap> resultOrderedMap = new TreeMap<>(); + + List m = new ArrayList<>(mainResource.get(r.getTypeProperty())); + Model model = ontoBean.getModel(); + for (TripleBean tripleBean : m) { + int dept = 0; + if (startsWithAtLeastOne(tripleBean.getValue(), conf.getMainOntologiesPrefixes())) { + dept = countFathers(tripleBean.getValue(), 0, model); + } + List l = null; + if (resultOrderedMap.get(dept) != null) { + l = resultOrderedMap.get(dept); + } else { + l = new ArrayList<>(); + } + l.add(tripleBean); + resultOrderedMap.put(dept, l); + // removing types + r.removeResource(tripleBean, mainIri); + } + if (resultOrderedMap.size() > 0) { + for (Integer dept : resultOrderedMap.descendingKeySet()) { + List l = resultOrderedMap.get(dept); + for (TripleBean tripleBean : l) { + // adding ordered types + r.addResource(tripleBean, mainIri); + } + } + } + } + return r; + + } catch (Exception e) { + } + return r; + } + + private static Integer countFathers(String value, int i, Model model) { + NodeIterator iter = model.listObjectsOfProperty(model.createResource(value), model.createProperty("http://www.w3.org/2000/01/rdf-schema#subClassOf")); + while (iter.hasNext()) { + RDFNode node = iter.next(); + return countFathers(node.toString(), ++i, model); + } + return i; + } + + private static boolean startsWithAtLeastOne(String value, List startsWithList) { + for (String string : startsWithList) { + if (value.startsWith(string)) { + return true; + } + } + return false; + } + + public static String stripHTML(String value) { + + value = value.replaceAll("]*>", ""); + + return value; + } + +} diff --git a/src/main/java/it/innovaway/lodviewng/lodview/utils/ResourceClassPathLoader.java b/src/main/java/it/innovaway/lodviewng/lodview/utils/ResourceClassPathLoader.java new file mode 100644 index 0000000..33b7c7e --- /dev/null +++ b/src/main/java/it/innovaway/lodviewng/lodview/utils/ResourceClassPathLoader.java @@ -0,0 +1,47 @@ +package it.innovaway.lodviewng.lodview.utils; + + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.apache.commons.io.FileUtils; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import java.io.File; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ResourceClassPathLoader { + + @SneakyThrows + public static File toFile(@NonNull Resource resource) { + String fileExtension = Optional.of(resource) + .map(Resource::getFilename) + .map(s -> s.substring(s.lastIndexOf("."))) + .orElse(""); + InputStream initialStream = resource.getInputStream(); + File tempFileName = File.createTempFile("lodview", fileExtension); + FileUtils.copyInputStreamToFile(initialStream, tempFileName); + return tempFileName; + } + + public static File toFile(String resource) { + return toFile(new ClassPathResource(resource)); + } + + @SneakyThrows + public static List toFiles(String directory) { + PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = pathMatchingResourcePatternResolver.getResources("classpath*:" + directory + "/*"); + return Arrays.stream(resources) + .map(ResourceClassPathLoader::toFile) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/dvcama/lodview/bean/OntologyBean.java b/src/main/java/org/dvcama/lodview/bean/OntologyBean.java deleted file mode 100644 index f0eff64..0000000 --- a/src/main/java/org/dvcama/lodview/bean/OntologyBean.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.dvcama.lodview.bean; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.ServletContext; - -import org.apache.commons.lang3.StringEscapeUtils; -import org.springframework.web.context.ServletContextAware; - -import com.hp.hpl.jena.rdf.model.Literal; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.NodeIterator; -import com.hp.hpl.jena.rdf.model.RDFNode; -import com.hp.hpl.jena.rdf.model.Resource; -import com.hp.hpl.jena.util.FileManager; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class OntologyBean implements ServletContextAware { - - private String ontoDir; - private ServletContext context; - private Model model; - final Logger logger = LoggerFactory.getLogger(OntologyBean.class); - - public void init() { - File ontoDirFile = new File(ontoDir); - if (!ontoDirFile.isAbsolute()) { - ontoDirFile = new File(context.getRealPath("/") + "/WEB-INF/" + ontoDir); - } - model = ModelFactory.createDefaultModel(); - if (ontoDirFile.exists()) { - logger.debug("ontologies dir founded!"); - File[] list = ontoDirFile.listFiles(); - for (File file : list) { - if (!file.isDirectory()) { - try { - logger.debug("loading " + file.getCanonicalPath()); - FileManager.get().readModel(model, file.getAbsolutePath()); - logger.debug("read successfully!"); - } catch (Exception e) { - logger.error("error loading " + e.getMessage()); - // e.printStackTrace(); - } - } - } - } else { - logger.debug("no ontologies founded " + ontoDirFile.getAbsolutePath()); - } - - // logger.debug("------------------- " + getHashResult("en", - // "http://dati.camera.it/ocd/parentCountry")); - // logger.debug("------------------- " + getHashResult("it", - // "http://dati.camera.it/ocd/parentCountry")); - - } - - @Override - public void setServletContext(ServletContext arg0) { - this.context = arg0; - try { - init(); - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public String getOntoDir() { - return ontoDir; - } - - public void setOntoDir(String ontoDir) { - this.ontoDir = ontoDir; - } - - public String getValue(String what, String preferredLanguage, String IRI) { - Resource IRIresource = model.createResource(IRI); - return getSingleValue(preferredLanguage, IRIresource, "http://www.w3.org/2000/01/rdf-schema#" + what, ""); - } - - public String getEscapedValue(String what, String preferredLanguage, String IRI) { - return StringEscapeUtils.escapeHtml4(getValue(what, preferredLanguage, IRI)); - } - - public Map getHashResult(String preferredLanguage, String IRI) { - Map result = new HashMap(); - Resource IRIresource = model.createResource(IRI); - result.put("label", getSingleValue(preferredLanguage, IRIresource, "http://www.w3.org/2000/01/rdf-schema#label", "")); - result.put("comment", getSingleValue(preferredLanguage, IRIresource, "http://www.w3.org/2000/01/rdf-schema#comment", "")); - return result; - } - - private String getSingleValue(String preferredLanguage, Resource IRI, String prop, String defaultValue) { - NodeIterator iter = model.listObjectsOfProperty(IRI, model.createProperty(prop)); - String result = defaultValue; - boolean betterTitleMatch = false; - while (iter.hasNext()) { - RDFNode node = iter.nextNode(); - Literal l = node.asLiteral(); - //logger.debug(IRI + " " + preferredLanguage + " --> " + l.getLanguage() + " --> " + l.getLexicalForm()); - if (!betterTitleMatch && (result.equals(defaultValue) || l.getLanguage().equals("en") || l.getLanguage().equals(preferredLanguage))) { - if (preferredLanguage.equals(l.getLanguage())) { - betterTitleMatch = true; - } - result = l.getLexicalForm(); - } - - } - return result; - } - - public Model getModel() { - return model; - } - - public void setModel(Model model) { - this.model = model; - } - -} diff --git a/src/main/java/org/dvcama/lodview/bean/PropertyBean.java b/src/main/java/org/dvcama/lodview/bean/PropertyBean.java deleted file mode 100644 index 7de314a..0000000 --- a/src/main/java/org/dvcama/lodview/bean/PropertyBean.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.dvcama.lodview.bean; - -public class PropertyBean { - - private String nsProperty = null, property = null, propertyUrl = null; - private String label = ""; - private String comment = ""; - - public String getPropertyUrl() { - return propertyUrl; - } - - public void setPropertyUrl(String propertyUrl) { - this.propertyUrl = propertyUrl; - } - - public String getNsProperty() { - return nsProperty; - } - - public void setNsProperty(String nsProperty) { - this.nsProperty = nsProperty; - } - - public String getProperty() { - return property; - } - - public void setProperty(String property) { - this.property = property; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((nsProperty == null) ? 0 : nsProperty.hashCode()); - result = prime * result + ((property == null) ? 0 : property.hashCode()); - result = prime * result + ((propertyUrl == null) ? 0 : propertyUrl.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PropertyBean other = (PropertyBean) obj; - if (nsProperty == null) { - if (other.nsProperty != null) - return false; - } else if (!nsProperty.equals(other.nsProperty)) - return false; - if (property == null) { - if (other.property != null) - return false; - } else if (!property.equals(other.property)) - return false; - if (propertyUrl == null) { - if (other.propertyUrl != null) - return false; - } else if (!propertyUrl.equals(other.propertyUrl)) - return false; - return true; - } - - public String getComment() { - return comment; - } - - public void setComment(String comment) { - this.comment = comment; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - -} diff --git a/src/main/java/org/dvcama/lodview/bean/ResultBean.java b/src/main/java/org/dvcama/lodview/bean/ResultBean.java deleted file mode 100644 index 20b36dc..0000000 --- a/src/main/java/org/dvcama/lodview/bean/ResultBean.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.dvcama.lodview.bean; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public class ResultBean { - - private String title, latitude = null, longitude = null, mainIRI = null; - private PropertyBean descriptionProperty = null, typeProperty = null; - private List images = null, linking = null, videos = null, audios = null; - private Map>> literals = new HashMap>>(), resources = new HashMap>>(), bnodes = new HashMap>>(); - - private Map>> addEle(String IRI, TripleBean tripleBean, Map>> ele) { - if (ele.get(IRI) == null || ele.get(IRI).get(tripleBean.getProperty()) == null) { - LinkedHashMap> a = ele.get(IRI); - if (a == null) { - a = new LinkedHashMap>(); - } - List b = new ArrayList(); - b.add(tripleBean); - a.put(tripleBean.getProperty(), b); - ele.put(IRI, a); - } else { - LinkedHashMap> a = ele.get(IRI); - List b = a.get(tripleBean.getProperty()); - b.add(tripleBean); - a.put(tripleBean.getProperty(), b); - ele.put(IRI, a); - } - return ele; - } - - private Map>> removeEle(String IRI, TripleBean tripleBean, Map>> ele) { - if (ele.get(IRI) == null || ele.get(IRI).get(tripleBean.getProperty()) == null) { - - } else { - LinkedHashMap> a = ele.get(IRI); - List b = a.get(tripleBean.getProperty()); - b.remove(tripleBean); - a.put(tripleBean.getProperty(), b); - ele.put(IRI, a); - } - return ele; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public List getImages() { - return images; - } - - public void setImages(List images) { - this.images = images; - } - - public void setLiterals(String IRI, List localLiterals) { - for (TripleBean tripleBean : localLiterals) { - literals = addEle(IRI, tripleBean, literals); - } - - } - - public void setBnodes(String IRI, List localBnodes) { - for (TripleBean tripleBean : localBnodes) { - bnodes = addEle(IRI, tripleBean, bnodes); - } - - } - - public void setResources(String IRI, List Localresources) { - for (TripleBean tripleBean : Localresources) { - resources = addEle(IRI, tripleBean, resources); - } - - } - - public LinkedHashMap> getResources(String IRI) { - return resources.get(IRI); - } - - public LinkedHashMap> getLiterals(String IRI) { - return literals.get(IRI); - } - - public Map>> getLiterals() { - return literals; - } - public LinkedHashMap> getBnodes(String IRI) { - return bnodes.get(IRI); - } - - public String getLongitude() { - return longitude; - } - - public void setLongitude(String longitude) { - this.longitude = longitude; - } - - public String getLatitude() { - return latitude; - } - - public List getAudios() { - return audios; - } - - public List getVideos() { - return videos; - } - - public void setLatitude(String latitude) { - this.latitude = latitude; - } - - public List getLinking() { - return linking; - } - - public void setLinking(List linking) { - this.linking = linking; - } - - @Override - public String toString() { - return "ResultBean [title=" + title + ", \ndescriptionProperty=" + descriptionProperty + ", \nlatitude=" + latitude + ", \nlongitude=" + longitude + ", \nimages=" + images + ", \nlinking=" + linking + ", \nliterals=" + literals + ", \nresources=" + resources + ", \nbnodes=" + bnodes + "]"; - } - - public String getMainIRI() { - return mainIRI; - } - - public void setMainIRI(String mainIRI) { - this.mainIRI = mainIRI; - } - - public void addBnode(TripleBean tripleBean, String IRI) { - - bnodes = addEle(IRI, tripleBean, bnodes); - } - - public void addLiteral(TripleBean tripleBean, String IRI) { - literals = addEle(IRI, tripleBean, literals); - - } - - public void addResource(TripleBean tripleBean, String IRI) { - resources = addEle(IRI, tripleBean, resources); - } - - public void removeBnode(TripleBean tripleBean, String IRI) { - - bnodes = removeEle(IRI, tripleBean, bnodes); - } - - public void removeLiteral(TripleBean tripleBean, String IRI) { - literals = removeEle(IRI, tripleBean, literals); - - } - - public void removeResource(TripleBean tripleBean, String IRI) { - resources = removeEle(IRI, tripleBean, resources); - } - - public PropertyBean getDescriptionProperty() { - return descriptionProperty; - } - - public void setDescriptionProperty(PropertyBean descriptionProperty) { - this.descriptionProperty = descriptionProperty; - } - - public PropertyBean getTypeProperty() { - return typeProperty; - } - - public void setTypeProperty(PropertyBean typeProperty) { - this.typeProperty = typeProperty; - } - - public void setVideos(List videos) { - this.videos = videos; - } - - public void setAudios(List audios) { - this.audios = audios; - } - -} diff --git a/src/main/java/org/dvcama/lodview/bean/TripleBean.java b/src/main/java/org/dvcama/lodview/bean/TripleBean.java deleted file mode 100644 index 53bae1a..0000000 --- a/src/main/java/org/dvcama/lodview/bean/TripleBean.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.dvcama.lodview.bean; - - -public class TripleBean { - private String nsValue = null, type = null, IRI = null, nsIRI = null, value = null, dataType = null, nsDataType = null, lang = null, url = null; - private boolean isLocal = false; - PropertyBean property = null; - - public PropertyBean getProperty() { - return property; - } - - public void setProperty(PropertyBean property) { - this.property = property; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getValue() { - return value; - } - - public String getNsValue() { - return nsValue; - } - - public void setValue(String value) { - this.value = value; - } - - public void setNsValue(String nsValue) { - this.nsValue = nsValue; - } - - public String getLang() { - return lang; - } - - public void setLang(String lang) { - this.lang = lang; - } - - public String getDataType() { - return dataType; - } - - public void setDataType(String dataType) { - this.dataType = dataType; - } - - @Override - public String toString() { - return "TripleBean [property=" + property + ", nsValue=" + nsValue + ", type=" + type + ", IRI=" + IRI + ", nsIRI=" + nsIRI + ", value=" + value + ", dataType=" + dataType + ", nsDataType=" + nsDataType + ", lang=" + lang + ", url=" + url + ", isLocal=" + isLocal + "]"; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public boolean isLocal() { - return isLocal; - } - - public void setLocal(boolean isLocal) { - this.isLocal = isLocal; - } - - public String getIRI() { - return IRI; - } - - public void setIRI(String iRI) { - IRI = iRI; - } - - public String getNsDataType() { - return nsDataType; - } - - public void setNsDataType(String nsDataType) { - this.nsDataType = nsDataType; - } - - public String getNsIRI() { - return nsIRI; - } - - public void setNsIRI(String nsIRI) { - this.nsIRI = nsIRI; - } -} diff --git a/src/main/java/org/dvcama/lodview/builder/ResourceBuilder.java b/src/main/java/org/dvcama/lodview/builder/ResourceBuilder.java deleted file mode 100644 index e5de9d3..0000000 --- a/src/main/java/org/dvcama/lodview/builder/ResourceBuilder.java +++ /dev/null @@ -1,330 +0,0 @@ -package org.dvcama.lodview.builder; - -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import org.apache.jena.riot.Lang; -import org.dvcama.lodview.bean.OntologyBean; -import org.dvcama.lodview.bean.ResultBean; -import org.dvcama.lodview.bean.TripleBean; -import org.dvcama.lodview.conf.ConfigurationBean; -import org.dvcama.lodview.endpoint.SPARQLEndPoint; -import org.dvcama.lodview.utils.Misc; -import org.springframework.context.MessageSource; - -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.RDFWriter; - -public class ResourceBuilder { - - private MessageSource messageSource; - - public ResourceBuilder() { - } - - public ResourceBuilder(MessageSource messageSource) { - this.messageSource = messageSource; - } - - public ResultBean buildHtmlResource(String IRI, Locale locale, ConfigurationBean conf, OntologyBean ontoBean) throws Exception { - return buildHtmlResource(IRI, locale, conf, ontoBean, false); - } - - public ResultBean buildHtmlResource(String IRI, Locale locale, ConfigurationBean conf, OntologyBean ontoBean, boolean localMode) throws Exception { - ResultBean result = new ResultBean(); - List videos = new ArrayList(); - List audios = new ArrayList(); - List images = new ArrayList(); - List linking = new ArrayList(); - SPARQLEndPoint se = new SPARQLEndPoint(conf, ontoBean, locale.getLanguage()); - result.setMainIRI(IRI); - - String preferredLanguage = conf.getPreferredLanguage(); - if (preferredLanguage.equals("auto")) { - preferredLanguage = locale.getLanguage(); - } - List triples = new ArrayList(); - if (conf.getEndPointUrl() != null && conf.getEndPointUrl().equals("<>")) { - localMode = true; - } - if (localMode) { - /* looking for data via content negotiation */ - Model m = ModelFactory.createDefaultModel(); - try { - m.read(IRI); - } catch (Exception e) { - throw new Exception(messageSource.getMessage("error.noContentNegotiation", null, "sorry but content negotiation is not supported by the IRI", locale)); - } - triples = se.doLocalQuery(m, IRI, conf.getDefaultQueries()); - } else { - triples = se.doQuery(IRI, conf.getDefaultQueries(), null); - } - boolean betterTitleMatch = false, betterDescrMatch = false; - for (TripleBean tripleBean : triples) { - - if (tripleBean.getIRI() == null) { - tripleBean.setIRI(IRI); - tripleBean.setNsIRI(Misc.toNsResource(tripleBean.getIRI(), conf)); - } - - if (conf.getTitleProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getTitleProperties().contains(tripleBean.getProperty().getProperty())) { - if (tripleBean.getIRI().equals(IRI) && !betterTitleMatch && (result.getTitle() == null || result.getTitle().trim().equals("") || (tripleBean.getLang() != null && (preferredLanguage.equals(tripleBean.getLang()) || tripleBean.getLang().equals("en"))))) { - result.setTitle(Misc.stripHTML(tripleBean.getValue())); - if (preferredLanguage.equals(tripleBean.getLang())) { - betterTitleMatch = true; - } - } - } else if (conf.getDescriptionProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getDescriptionProperties().contains(tripleBean.getProperty().getProperty())) { - if (tripleBean.getIRI().equals(IRI) && !betterDescrMatch && (result.getDescriptionProperty() == null || (tripleBean.getLang() != null && (preferredLanguage.equals(tripleBean.getLang()) || tripleBean.getLang().equals("en"))))) { - result.setDescriptionProperty(tripleBean.getProperty()); - if (preferredLanguage.equals(tripleBean.getLang())) { - betterDescrMatch = true; - } - } - } else if (conf.getLatitudeProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getLatitudeProperties().contains(tripleBean.getProperty().getProperty())) { - result.setLatitude(tripleBean.getValue()); - } else if (conf.getLongitudeProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getLongitudeProperties().contains(tripleBean.getProperty().getProperty())) { - result.setLongitude(tripleBean.getValue()); - } else if (conf.getImageProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getImageProperties().contains(tripleBean.getProperty().getProperty())) { - images.add(tripleBean.getValue()); - } else if (conf.getAudioProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getAudioProperties().contains(tripleBean.getProperty().getProperty())) { - audios.add(tripleBean.getValue()); - } else if (conf.getVideoProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getVideoProperties().contains(tripleBean.getProperty().getProperty())) { - videos.add(tripleBean.getValue()); - } else if (conf.getLinkingProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getLinkingProperties().contains(tripleBean.getProperty().getProperty())) { - linking.add(tripleBean.getValue()); - } else if (conf.getTypeProperties().contains(tripleBean.getProperty().getNsProperty()) || conf.getTypeProperties().contains(tripleBean.getProperty().getProperty())) { - result.setTypeProperty(tripleBean.getProperty()); - } - - if (tripleBean.getType().equals("iri")) { - tripleBean.setUrl(Misc.toBrowsableUrl(tripleBean.getValue(), conf)); - tripleBean.setNsValue(Misc.toNsResource(tripleBean.getValue(), conf)); - if (!tripleBean.getUrl().equals(tripleBean.getValue()) || tripleBean.getValue().startsWith(conf.getPublicUrlPrefix())) { - tripleBean.setLocal(true); - } - result.addResource(tripleBean, tripleBean.getIRI()); - } else if (tripleBean.getType().equals("literal")) { - result.addLiteral(tripleBean, tripleBean.getIRI()); - } else if (tripleBean.getType().equals("bnode")) { - result.addBnode(tripleBean, tripleBean.getIRI()); - } - } - - result.setImages(images); - result.setLinking(linking); - result.setVideos(videos); - result.setAudios(audios); - - return result; - } - - public String buildRDFResource(String IRI, String sparql, Lang lang, ConfigurationBean conf) throws Exception { - String result = "empty content"; - Model model = ModelFactory.createDefaultModel(); - model.setNsPrefixes(conf.getPrefixes()); - - SPARQLEndPoint se = new SPARQLEndPoint(conf, null, null); - model = se.extractData(model, IRI, sparql, conf.getDefaultRawDataQueries()); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - RDFWriter rdfWriter = model.getWriter(lang.getName()); - rdfWriter.setProperty("showXMLDeclaration","true"); - rdfWriter.setProperty("relativeURIs",""); - - rdfWriter.write(model, baos, conf.getIRInamespace()); - - byte[] resultByteArray = baos.toByteArray(); - result = new String(resultByteArray); - - return result; - } - - public String buildRDFResource(String IRI, Model m, Lang lang, ConfigurationBean conf) throws Exception { - String result = "empty content"; - Model model = ModelFactory.createDefaultModel(); - model.setNsPrefixes(conf.getPrefixes()); - - SPARQLEndPoint se = new SPARQLEndPoint(conf, null, null); - model = se.extractLocalData(model, IRI, m, conf.getDefaultRawDataQueries()); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - RDFWriter rdfWriter = model.getWriter(lang.getName()); - rdfWriter.setProperty("showXMLDeclaration","true"); - rdfWriter.setProperty("relativeURIs",""); - - rdfWriter.write(model, baos, conf.getIRInamespace()); - rdfWriter.setProperty("showXMLDeclaration","true"); - rdfWriter.setProperty("relativeURIs",""); - - byte[] resultByteArray = baos.toByteArray(); - result = new String(resultByteArray); - - return result; - } - - public ResultBean buildPartialHtmlResource(String IRI, String[] abouts, Locale locale, ConfigurationBean conf, OntologyBean ontoBean, List filterProperties) throws Exception { - - SPARQLEndPoint se = new SPARQLEndPoint(conf, ontoBean, locale.getLanguage()); - ResultBean result = new ResultBean(); - List literals = new ArrayList(); - - String preferredLanguage = conf.getPreferredLanguage(); - if (preferredLanguage.equals("auto")) { - preferredLanguage = locale.getLanguage(); - } - - List triples = new ArrayList(); - - /* - * FIXME: make more distinct queries to avoid length limits, eg - * http://dati.camera.it/ocd/assemblea.rdf/a16 - */ - - StringBuilder filter = new StringBuilder(); - for (String titleProperty : filterProperties) { - if (titleProperty.toLowerCase().startsWith("http:")) { - filter.append("(?filterProperty = <" + titleProperty + ">)"); - } else { - filter.append("(?filterProperty = " + titleProperty + ")"); - } - filter.append(" || "); - } - - for (String about : abouts) { - StringBuilder sparqlQuery = new StringBuilder("select distinct ?o "); - sparqlQuery.append("{ <" + about + "> ?filterProperty ?o. FILTER (" + filter + "))} "); - List sparqlQueries = new ArrayList(); - sparqlQueries.add(sparqlQuery.toString().replaceAll("\\|\\| \\)", "")); - try { - - if (conf.getEndPointUrl().equals("<>")) { - /* looking for data via content negotiation */ - Model m = ModelFactory.createDefaultModel(); - try { - m.read(about); - } catch (Exception e) { - throw new Exception(messageSource.getMessage("error.noContentNegotiation", null, "sorry but content negotiation is not supported by the IRI", locale)); - } - triples.addAll(se.doLocalQuery(m, about, sparqlQueries, about)); - } else { - triples.addAll(se.doQuery(null, sparqlQueries, about)); - } - - } catch (Exception e) { - } - } - - Map> l = new HashMap>(); - - for (TripleBean tripleBean : triples) { - if (tripleBean.getType().equals("literal")) { - List al = l.get(tripleBean.getProperty().getProperty()); - if (al == null) { - al = new ArrayList(); - } - al.add(tripleBean); - l.put(tripleBean.getProperty().getProperty(), al); - } - } - for (String about : l.keySet()) { - List al = l.get(about); - boolean betterTitleMatch = false; - TripleBean title = null; - for (TripleBean tripleBean : al) { - if (!betterTitleMatch && (title == null || title.getValue() == null || title.getValue().trim().equals("") || preferredLanguage.equals(tripleBean.getLang()) || tripleBean.getLang().equals("en"))) { - title = tripleBean; - if (preferredLanguage.equals(tripleBean.getLang())) { - betterTitleMatch = true; - } - } - } - if (title != null) { - literals.add(title); - } - } - result.setLiterals(IRI, literals); - return result; - } - - public ResultBean buildHtmlInverseResource(String IRI, String property, int start, Locale locale, ConfigurationBean conf, OntologyBean ontoBean) throws Exception { - ResultBean result = new ResultBean(); - - SPARQLEndPoint se = new SPARQLEndPoint(conf, ontoBean, locale.getLanguage()); - String preferredLanguage = conf.getPreferredLanguage(); - if (preferredLanguage.equals("auto")) { - preferredLanguage = locale.getLanguage(); - } - if (property == null) { - /* counting */ - List resources = new ArrayList(); - List triples = new ArrayList(); - - if (conf.getEndPointUrl().equals("<>")) { - /* looking for data via content negotiation */ - Model m = ModelFactory.createDefaultModel(); - try { - m.read(IRI); - } catch (Exception e) { - throw new Exception(messageSource.getMessage("error.noContentNegotiation", null, "sorry but content negotiation is not supported by the IRI", locale)); - } - triples = se.doLocalQuery(m, IRI, conf.getDefaultInversesCountQueries()); - } else { - triples = se.doQuery(IRI, conf.getDefaultInversesCountQueries(), null); - } - - for (TripleBean tripleBean : triples) { - if (tripleBean.getType().equals("literal")) { - resources.add(tripleBean); - } - } - - result.setResources(IRI, resources); - - } else { - /* listing */ - List resources = new ArrayList(); - List triples = new ArrayList(); - - if (conf.getEndPointUrl().equals("<>")) { - /* looking for data via content negotiation */ - Model m = ModelFactory.createDefaultModel(); - try { - m.read(IRI); - } catch (Exception e) { - throw new Exception(messageSource.getMessage("error.noContentNegotiation", null, "sorry but content negotiation is not supported by the IRI", locale)); - } - triples = se.doLocalQuery(m, IRI, property, start, conf.getDefaultInversesQueries(), null); - } else { - triples = se.doQuery(IRI, property, start, conf.getDefaultInversesQueries(), null, null); - } - - Map controlList = new HashMap(); - for (TripleBean tripleBean : triples) { - if (tripleBean.getType().equals("literal")) { - if (controlList.get(tripleBean.getProperty().getProperty()) == null || preferredLanguage.equals(tripleBean.getLang())) { - controlList.put(tripleBean.getProperty().getProperty(), tripleBean); - } - } - } - - for (String at : controlList.keySet()) { - resources.add(controlList.get(at)); - } - - result.setResources(IRI, resources); - - } - - return result; - } - - public ResultBean buildHtmlInverseResource(String IRI, Locale locale, ConfigurationBean conf, OntologyBean ontoBean) throws Exception { - return buildHtmlInverseResource(IRI, null, -1, locale, conf, ontoBean); - } -} diff --git a/src/main/java/org/dvcama/lodview/conf/ConfigurationBean.java b/src/main/java/org/dvcama/lodview/conf/ConfigurationBean.java deleted file mode 100644 index 87632c0..0000000 --- a/src/main/java/org/dvcama/lodview/conf/ConfigurationBean.java +++ /dev/null @@ -1,372 +0,0 @@ -package org.dvcama.lodview.conf; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import javax.servlet.ServletContext; - -import org.apache.jena.riot.RDFDataMgr; -import org.springframework.web.context.ServletContextAware; - -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.NodeIterator; -import com.hp.hpl.jena.rdf.model.RDFNode; -import com.hp.hpl.jena.rdf.model.ResIterator; -import com.hp.hpl.jena.rdf.model.Resource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ConfigurationBean implements ServletContextAware, Cloneable { - - final Logger logger = LoggerFactory.getLogger(ConfigurationBean.class); - - protected Model confModel = null; - protected ServletContext context; - protected String confFile; - private String endPointType; - private String redirectionStrategy; - private String forceIriEncoding; - private String httpRedirectExcludeList; - private String homeUrl; - private String license; - private String httpRedirectSuffix; - private String httpRedirectPrefix; - private String endPointUrl; - private String IRInamespace; - private String contentEncoding; - private String staticResourceURL; - private String preferredLanguage; - private String publicUrlPrefix = null; - private String publicUrlSuffix = ""; - private String authUsername = null; - private String authPassword = null; - private String defaultInverseBehaviour = "collapse"; - - public enum ColorStrategy {RANDOM, CLASS, PREFIX} - private ColorStrategy colorStrategy = ColorStrategy.RANDOM; - public ColorStrategy getColorStrategy() {return colorStrategy;} - - private List defaultQueries = null, defaultRawDataQueries = null, defaultInversesQueries = null, defaultInversesTest = null, defaultInversesCountQueries = null, typeProperties = null, audioProperties = null, imageProperties = null, videoProperties = null, linkingProperties = null, titleProperties = null, descriptionProperties = null, longitudeProperties = null, latitudeProperties = null; - private List colorPair = null, skipDomains = null, mainOntologiesPrefixes = null; - private Map colorPairMatcher = null; - - Random rand = new Random(); - - public ConfigurationBean() throws IOException, Exception { - - } - - public void populateBean() throws IOException, Exception { - logger.debug("Initializing configuration " + confFile); - File configFile = new File(confFile); - if (!configFile.isAbsolute()) { - configFile = new File(context.getRealPath("/") + "/WEB-INF/" + confFile); - } - if (!configFile.exists()) { - throw new Exception("Configuration file not found (" + configFile.getAbsolutePath() + ")"); - } - confModel = RDFDataMgr.loadModel(configFile.getAbsolutePath()); - - endPointUrl = getSingleConfValue("endpoint"); - endPointType = getSingleConfValue("endpointType", ""); - authPassword = getSingleConfValue("authPassword"); - authUsername = getSingleConfValue("authUsername"); - forceIriEncoding = getSingleConfValue("forceIriEncoding", "auto"); - redirectionStrategy = getSingleConfValue("redirectionStrategy", ""); - - IRInamespace = getSingleConfValue("IRInamespace", ""); - - httpRedirectSuffix = getSingleConfValue("httpRedirectSuffix", ""); - httpRedirectPrefix = getSingleConfValue("httpRedirectPrefix", ""); - httpRedirectExcludeList = getSingleConfValue("httpRedirectExcludeList", ""); - - publicUrlPrefix = getSingleConfValue("publicUrlPrefix", ""); - publicUrlPrefix = publicUrlPrefix.replaceAll("^(.+/)?auto$", context.getContextPath() + "/"); - - contentEncoding = getSingleConfValue("contentEncoding"); - staticResourceURL = getSingleConfValue("staticResourceURL", ""); - homeUrl = getSingleConfValue("homeUrl", "/"); - staticResourceURL = staticResourceURL.replaceAll("^(.+/)?auto$", context.getContextPath() + "/staticResources/"); - - preferredLanguage = getSingleConfValue("preferredLanguage"); - - typeProperties = getMultiConfValue("typeProperties"); - titleProperties = getMultiConfValue("titleProperties"); - descriptionProperties = getMultiConfValue("descriptionProperties"); - imageProperties = getMultiConfValue("imageProperties"); - audioProperties = getMultiConfValue("audioProperties"); - videoProperties = getMultiConfValue("videoProperties"); - linkingProperties = getMultiConfValue("linkingProperties"); - longitudeProperties = getMultiConfValue("longitudeProperties"); - latitudeProperties = getMultiConfValue("latitudeProperties"); - - defaultQueries = getMultiConfValue("defaultQueries"); - defaultRawDataQueries = getMultiConfValue("defaultRawDataQueries"); - - defaultInversesQueries = getMultiConfValue("defaultInversesQueries"); - defaultInversesTest = getMultiConfValue("defaultInversesTest"); - defaultInversesCountQueries = getMultiConfValue("defaultInversesCountQueries"); - - defaultInverseBehaviour = getSingleConfValue("defaultInverseBehaviour", defaultInverseBehaviour); - mainOntologiesPrefixes = getMultiConfValue("mainOntologiesPrefixes"); - - license = getSingleConfValue("license", ""); - - colorPair = getMultiConfValue("colorPair"); - - if (colorPair != null && colorPair.size() == 1 && colorPair.get(0).startsWith("http://")) - { - switch(colorPair.get(0).replace("http://lodview.it/conf#", "")) - { - case "byClass": colorStrategy= ColorStrategy.CLASS; break; - case "byPrefix": colorStrategy= ColorStrategy.PREFIX; break; - } - colorPairMatcher = populateColorPairMatcher(); - } - - skipDomains = getMultiConfValue("skipDomains"); - } - - private Map populateColorPairMatcher() { - Map result = new HashMap(); - ResIterator iter = confModel.listSubjectsWithProperty(confModel.createProperty(confModel.getNsPrefixURI("conf"), "hasColorPair")); - while (iter.hasNext()) { - Resource res = iter.next(); - NodeIterator values = confModel.listObjectsOfProperty(res, confModel.createProperty(confModel.getNsPrefixURI("conf"), "hasColorPair")); - while (values.hasNext()) { - RDFNode node = values.next(); - result.put(res.toString(), node.toString()); - break; - } - } - return result; - } - - private String getSingleConfValue(String prop) { - return getSingleConfValue(prop, null); - } - - private String getSingleConfValue(String prop, String defaultValue) { - String value = System.getenv("LodView"+prop); - if(value!=null) {return value;} - NodeIterator iter = confModel.listObjectsOfProperty(confModel.createProperty(confModel.getNsPrefixURI("conf"), prop)); - while (iter.hasNext()) { - RDFNode node = iter.next(); - return node.toString(); - } - return defaultValue; - } - - private List getMultiConfValue(String prop) { - List result = new ArrayList(); - NodeIterator iter = confModel.listObjectsOfProperty(confModel.createProperty(confModel.getNsPrefixURI("conf"), prop)); - while (iter.hasNext()) { - RDFNode node = iter.next(); - result.add(node.toString()); - } - return result; - } - - @Override - public void setServletContext(ServletContext arg0) { - this.context = arg0; - try { - populateBean(); - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public Model getConfModel() { - return confModel; - } - - public Map getPrefixes() { - return confModel.getNsPrefixMap(); - } - - public String getRedirectionStrategy() { - return redirectionStrategy; - } - - public String getPreferredLanguage() { - return preferredLanguage; - } - - public String getNsPrefixURI(String prefix) { - return confModel.getNsPrefixURI(prefix); - } - - public String getNsURIPrefix(String IRI) { - return confModel.getNsURIPrefix(IRI); - } - - public String getEndPointUrl() { - return endPointUrl; - } - - public List getDefaultQueries() { - return defaultQueries; - } - - public List getTypeProperties() { - return typeProperties; - } - - public String getIRInamespace() { - return IRInamespace; - } - - public String getPublicUrlPrefix() { - return publicUrlPrefix; - } - - public String getPublicUrlSuffix() { - return publicUrlSuffix; - } - - public List getDefaultRawDataQueries() { - return defaultRawDataQueries; - } - - public List getDefaultInversesQueries() { - return defaultInversesQueries; - } - - public List getDefaultInversesTest() { - return defaultInversesTest; - } - - public List getDefaultInversesCountQueries() { - return defaultInversesCountQueries; - } - - public String getStaticResourceURL() { - return staticResourceURL; - } - - public String getContentEncoding() { - return contentEncoding; - } - - public List getTitleProperties() { - return titleProperties; - } - - public List getLongitudeProperties() { - return longitudeProperties; - } - - public List getLatitudeProperties() { - return latitudeProperties; - } - - public List getDescriptionProperties() { - return descriptionProperties; - } - - public List getImageProperties() { - return imageProperties; - } - - public List getAudioProperties() { - return audioProperties; - } - - public List getVideoProperties() { - return videoProperties; - } - - public List getLinkingProperties() { - return linkingProperties; - } - - public String getLicense() { - return license; - } - - public List getColorPair() { - return colorPair; - } - - public String getRandomColorPair() { - if(colorStrategy!=ColorStrategy.RANDOM) {return "#914848-#7d3e3e";} - int randomNum = rand.nextInt(colorPair.size()); - return colorPair.get(randomNum); - } - - public List getSkipDomains() { - return skipDomains; - } - - public String getAuthPassword() { - return authPassword; - } - - public String getAuthUsername() { - return authUsername; - } - - public String getDefaultInverseBehaviour() { - return defaultInverseBehaviour; - } - - public Map getColorPairMatcher() { - return colorPairMatcher; - } - - @Override - public Object clone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new Error("Something impossible just happened"); - } - } - - @Override - public String toString() { - return "ConfigurationBean [confModel=" + confModel + ", context=" + context + ", confFile=" + confFile + ", endPointUrl=" + endPointUrl + ", IRInamespace=" + IRInamespace + ", contentEncoding=" + contentEncoding + ", staticResourceURL=" + staticResourceURL + ", preferredLanguage=" + preferredLanguage + ", publicUrlPrefix=" + publicUrlPrefix + ", authUsername=" + authUsername + ", authPassword=" + authPassword + ", defaultInverseBehaviour=" + defaultInverseBehaviour + ", defaultQueries=" + defaultQueries + ", defaultRawDataQueries=" + defaultRawDataQueries + ", defaultInversesQueries=" + defaultInversesQueries + ", defaultInversesTest=" + defaultInversesTest + ", defaultInversesCountQueries=" + defaultInversesCountQueries + ", typeProperties=" + typeProperties - + ", imageProperties=" + imageProperties + ", audioProperties=" + audioProperties + ", videoProperties=" + videoProperties + ", linkingProperties=" + linkingProperties + ", titleProperties=" + titleProperties + ", descriptionProperties=" + descriptionProperties + ", longitudeProperties=" + longitudeProperties + ", latitudeProperties=" + latitudeProperties + ", colorPair=" + colorPair + ", skipDomains=" + skipDomains + ", rand=" + rand + "]"; - } - - public String getHomeUrl() { - return homeUrl; - } - - public String getHttpRedirectSuffix() { - return httpRedirectSuffix; - } - - public String getHttpRedirectPrefix() { - return httpRedirectPrefix; - } - - public String getHttpRedirectExcludeList() { - return httpRedirectExcludeList; - } - - public List getMainOntologiesPrefixes() { - return mainOntologiesPrefixes; - } - - public String getForceIriEncoding() { - return forceIriEncoding; - } - - public String getEndPointType() { - return endPointType; - } - - public void setConfFile(String confFile) { - this.confFile = confFile; - } - -} diff --git a/src/main/java/org/dvcama/lodview/controllers/ErrorController.java b/src/main/java/org/dvcama/lodview/controllers/ErrorController.java deleted file mode 100644 index 69d944f..0000000 --- a/src/main/java/org/dvcama/lodview/controllers/ErrorController.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.dvcama.lodview.controllers; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; - -import org.dvcama.lodview.conf.ConfigurationBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseStatus; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Controller -public class ErrorController { - - final Logger logger = LoggerFactory.getLogger(ErrorController.class); - - @Autowired - ConfigurationBean conf; - - public ErrorController(ConfigurationBean conf) { - this.conf = conf; - } - - public ErrorController() { - } - - /* TODO: change the handler to send "error" param to the client */ - @ResponseStatus(value = HttpStatus.NOT_ACCEPTABLE, reason = "unhandled encoding") - @RequestMapping(value = "/406") - public String error406(HttpServletResponse res, ModelMap model, @CookieValue(value = "colorPair") String colorPair) { - logger.error("not acceptable"); - model.addAttribute("statusCode", "406"); - model.addAttribute("conf", conf); - colors(colorPair, res, model); - res.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); - return "error"; - } - - @RequestMapping(value = "/404") - public String error404(HttpServletResponse res, ModelMap model, @RequestParam(value = "error", defaultValue = "") String error, @CookieValue(value = "colorPair", defaultValue = "") String colorPair, @RequestParam(value = "IRI", defaultValue = "") String IRI, @RequestParam(value = "endpoint", defaultValue = "") String endpoint) { - logger.error("not found " + error + " -- " + IRI + " -- " + endpoint); - /* spring bug? */ - model.addAttribute("IRI", IRI); - model.addAttribute("endpoint", endpoint); - model.addAttribute("error", error); - model.addAttribute("conf", conf); - colors(colorPair, res, model); - model.addAttribute("statusCode", "404"); - res.setStatus(HttpServletResponse.SC_NOT_FOUND); - return "error"; - } - - @RequestMapping(value = "/400") - public String error400(HttpServletResponse res, ModelMap model, @RequestParam(value = "IRI", defaultValue = "") String IRI, @CookieValue(value = "colorPair", defaultValue = "") String colorPair) { - logger.error("error on " + IRI); - /* spring bug? */ - model.addAttribute("IRI", IRI.replaceAll("(http://.+),http://.+", "$1")); - model.addAttribute("conf", conf); - colors(colorPair, res, model); - model.addAttribute("statusCode", "400"); - res.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); - return "error"; - } - - @RequestMapping(value = { "/500", "/error" }) - public String error500(HttpServletResponse res, ModelMap model, @RequestParam(value = "error", defaultValue = "") String error, @CookieValue(value = "colorPair", defaultValue = "") String colorPair, @RequestParam(value = "IRI", defaultValue = "") String IRI, @RequestParam(value = "endpoint", defaultValue = "") String endpoint) { - logger.error("error on " + error + " -- " + IRI + " -- " + endpoint); - /* spring bug? */ - model.addAttribute("IRI", IRI); - model.addAttribute("endpoint", endpoint); - model.addAttribute("error", error); - model.addAttribute("conf", conf); - colors(colorPair, res, model); - model.addAttribute("statusCode", "500"); - res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return "error"; - } - - private void colors(String colorPair, HttpServletResponse res, ModelMap model) { - if (colorPair.equals("")) { - colorPair = conf.getRandomColorPair(); - Cookie c = new Cookie("colorPair", colorPair); - c.setPath("/"); - res.addCookie(c); - } - if (conf != null && conf.getColorPairMatcher() != null && conf.getColorPairMatcher().size() > 0) { - model.addAttribute("colorPair", conf.getColorPairMatcher().get("http://lodview.it/conf#otherClasses")); - } else { - model.addAttribute("colorPair", colorPair); - } - - } -} diff --git a/src/main/java/org/dvcama/lodview/controllers/LinkedResourcesController.java b/src/main/java/org/dvcama/lodview/controllers/LinkedResourcesController.java deleted file mode 100644 index de3c819..0000000 --- a/src/main/java/org/dvcama/lodview/controllers/LinkedResourcesController.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.dvcama.lodview.controllers; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringEscapeUtils; -import org.dvcama.lodview.bean.OntologyBean; -import org.dvcama.lodview.bean.PropertyBean; -import org.dvcama.lodview.bean.ResultBean; -import org.dvcama.lodview.bean.TripleBean; -import org.dvcama.lodview.builder.ResourceBuilder; -import org.dvcama.lodview.conf.ConfigurationBean; -import org.dvcama.lodview.utils.Misc; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Controller -public class LinkedResourcesController implements MessageSourceAware { - - final Logger logger = LoggerFactory.getLogger(LinkedResourcesController.class); - - @Autowired - private MessageSource messageSource; - - @Autowired - ConfigurationBean conf; - - @Autowired - OntologyBean ontoBean; - - public LinkedResourcesController() { - } - - public LinkedResourcesController(MessageSource messageSource) { - this.messageSource = messageSource; - } - - @ResponseBody - @RequestMapping(value = "/linkedResourceTitles", produces = "application/xml;charset=UTF-8") - public String resourceTitles(ModelMap model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "IRI") String IRI, @RequestParam(value = "abouts[]") String[] abouts) throws IOException, Exception { - return resourceTitles(model, conf, req, res, locale, IRI, abouts); - } - - public String resourceTitles(ModelMap model, ConfigurationBean conf, HttpServletRequest req, HttpServletResponse res, Locale locale, String IRI, String[] abouts) throws IOException, Exception { - // logger.trace("LinkedResourcesController.resourceTitles() locale: " - // + locale.getLanguage()); - StringBuilder result = new StringBuilder("\n"); - try { - ResultBean results = new ResourceBuilder(messageSource).buildPartialHtmlResource(IRI, abouts, locale, conf, null, conf.getTitleProperties()); - Map> literals = results.getLiterals(IRI); - if (literals == null || literals.size() == 0) { - return ("" + messageSource.getMessage("error.noLiteral", null, "no literal values where found", locale) + ""); - } - for (PropertyBean key : literals.keySet()) { - for (TripleBean tripleBean : literals.get(key)) { - result.append("<![CDATA[" + StringEscapeUtils.escapeHtml4(Misc.stripHTML(tripleBean.getValue())) + // - "]]>\n"); - } - } - result.append(""); - return result.toString(); - } catch (Exception e) { - // 404? - return ("" + messageSource.getMessage("error.linkedResourceUnavailable", null, "unable to retrieve data", locale) + "" + e.getMessage() + ""); - } - } - - @ResponseBody - @RequestMapping(value = "/linkedResourceInverses", produces = "application/xml;charset=UTF-8") - public String resourceInversesController(ModelMap model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "IRI") String IRI, @RequestParam(value = "property", defaultValue = "") String property, @RequestParam(value = "start", defaultValue = "-1") int start) throws IOException, Exception { - return resourceInverses(model, conf, ontoBean, req, res, locale, IRI, property, start); - } - - public String resourceInverses(ModelMap model, ConfigurationBean conf, OntologyBean ontoBean, HttpServletRequest req, HttpServletResponse res, Locale locale, String IRI, String property, int start) throws IOException, Exception { - StringBuilder result = new StringBuilder("\n"); - // logger.trace("LinkedResourcesController.resourceInverses()"); - if (property.equals("")) { - /* counting inverse relations */ - try { - - ResultBean results = new ResourceBuilder(messageSource).buildHtmlInverseResource(IRI, locale, conf, ontoBean); - Map> resources = results.getResources(IRI); - if (resources != null) { - for (PropertyBean key : resources.keySet()) { - for (TripleBean tripleBean : resources.get(key)) { - if (tripleBean.getProperty().getProperty() == null || tripleBean.getProperty().getProperty().equals("")) { - throw new Exception("no content"); - } - result.append("\n"); - } - } - } - - result.append(""); - return result.toString(); - - } catch (Exception e) { - // e.printStackTrace(); - return ("" + messageSource.getMessage("error.linkedResourceUnavailable", null, "unable to retrieve data", locale) + "" + e.getMessage() + ""); - } - } else { - - /* retrieving inverse relations */ - try { - ResultBean results = new ResourceBuilder(messageSource).buildHtmlInverseResource(IRI, property, start, locale, conf, null); - Map> resources = results.getResources(IRI); - for (PropertyBean key : resources.keySet()) { - for (TripleBean tripleBean : resources.get(key)) { - result.append("\n"); - } - } - - result.append(""); - return result.toString(); - - } catch (Exception e) { - e.printStackTrace(); - return ("" + messageSource.getMessage("error.linkedResourceUnavailable", null, "unable to retrieve data", locale) + ""); - } - } - } - - @Override - public void setMessageSource(MessageSource arg0) { - // TODO Auto-generated method stub - - } -} diff --git a/src/main/java/org/dvcama/lodview/controllers/LodController.java b/src/main/java/org/dvcama/lodview/controllers/LodController.java deleted file mode 100644 index 7e3cc13..0000000 --- a/src/main/java/org/dvcama/lodview/controllers/LodController.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.dvcama.lodview.controllers; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringEscapeUtils; -import org.dvcama.lodview.bean.ResultBean; -import org.dvcama.lodview.bean.TripleBean; -import org.dvcama.lodview.builder.ResourceBuilder; -import org.dvcama.lodview.conf.ConfigurationBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Controller -public class LodController { - - final Logger logger = LoggerFactory.getLogger(LodController.class); - - @Autowired - ConfigurationBean confLinked; - - @Autowired - private MessageSource messageSource; - - - @ResponseBody - @RequestMapping(value = { "/linkedResource", "/lodview/linkedResource" }, produces = "application/xml;charset=UTF-8") - public String resource(HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "IRI") String IRI) throws IOException, Exception { - - if (confLinked.getSkipDomains().contains(IRI.replaceAll("http[s]*://([^/]+)/.*", "$1"))) { - // logger.info("LodController.resource() - skip - " + IRI); - return "" + // - StringEscapeUtils.escapeXml11(messageSource.getMessage("error.skipedDomain", null, "skiping this URI", locale)) + // - ""; - } - try { - logger.info(" LodController.resource() - load - " + IRI); - /* TODO: change this in UNION queries for better performance */ - ResultBean results = new ResourceBuilder(messageSource).buildHtmlResource(IRI, locale, confLinked, null, true); - - StringBuilder result = new StringBuilder("\n"); - - result.append("<![CDATA[" + StringEscapeUtils.escapeHtml4(results.getTitle()) + "]]>"); - - String lang = locale.getLanguage().toLowerCase(); - String descr = ""; - List descrProperties = results.getLiterals(IRI).get(results.getDescriptionProperty()); - if (descrProperties != null) { - for (TripleBean tripleBean : descrProperties) { - if (lang.equals(tripleBean.getLang())) { - descr = tripleBean.getValue(); - lang = tripleBean.getLang(); - break; - } else if (tripleBean.getLang().equals("en")) { - lang = tripleBean.getLang(); - descr = tripleBean.getValue(); - } else if (descr.equals("")) { - descr = tripleBean.getValue(); - lang = tripleBean.getLang(); - } - } - } - /* - * List descrProperties = - * results.getLiterals(IRI).get(results.getDescriptionProperty()); - * if (descrProperties != null) { boolean betterDescrMatch = false; - * for (TripleBean tripleBean : descrProperties) { if - * (confLinked.getDescriptionProperties - * ().contains(tripleBean.getProperty().getNsProperty()) || - * confLinked - * .getDescriptionProperties().contains(tripleBean.getProperty - * ().getProperty())) { if (!betterDescrMatch && (descr.equals("") - * || preferredLanguage.equals(tripleBean.getLang()) || - * tripleBean.getLang().equals("en"))) { descr = - * tripleBean.getValue(); lang = tripleBean.getLang(); if - * (preferredLanguage.equals(tripleBean.getLang())) { - * betterDescrMatch = true; } } } } - * - * } - */ - - result.append(""); - - for (String img : results.getImages()) { - result.append(""); - } - for (String link : results.getLinking()) { - result.append(""); - } - result.append(""); - result.append(""); - result.append(""); - - result.append(""); - return result.toString(); - - } catch (Exception e) { - // e.printStackTrace(); - logger.error(IRI + " unable to retrieve data " + e.getMessage()); - return "" + // - messageSource.getMessage("error.linkedResourceUnavailable", null, "unable to retrieve data", locale) + // - ""; - } - } -} diff --git a/src/main/java/org/dvcama/lodview/controllers/ResourceController.java b/src/main/java/org/dvcama/lodview/controllers/ResourceController.java deleted file mode 100644 index 4bcca76..0000000 --- a/src/main/java/org/dvcama/lodview/controllers/ResourceController.java +++ /dev/null @@ -1,408 +0,0 @@ -package org.dvcama.lodview.controllers; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; -import org.apache.jena.atlas.web.AcceptList; -import org.apache.jena.atlas.web.MediaType; -import org.apache.jena.riot.Lang; -import org.apache.jena.riot.RDFLanguages; -import org.dvcama.lodview.bean.OntologyBean; -import org.dvcama.lodview.bean.ResultBean; -import org.dvcama.lodview.bean.TripleBean; -import org.dvcama.lodview.builder.ResourceBuilder; -import org.dvcama.lodview.conf.ConfigurationBean; -import org.dvcama.lodview.utils.Misc; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.CookieValue; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.view.RedirectView; -import org.springframework.web.util.UrlPathHelper; - -import com.hp.hpl.jena.rdf.model.ModelFactory; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Controller -@RequestMapping(value = "/") -public class ResourceController { - - final Logger logger = LoggerFactory.getLogger(ResourceController.class); - - @Autowired - private MessageSource messageSource; - - @Autowired - ConfigurationBean conf; - - @Autowired - OntologyBean ontoBean; - - final AcceptList offeringRDF = new AcceptList("text/turtle, application/turtle, " // - + "application/x-turtle, application/rdf+xml, " // - + "application/rdf+json, application/ld+json, " // - + "text/plain, application/n-triples, text/trig, " // - + "application/n-quads, application/x-trig, application/trig, " // - + "text/n-quads, text/nquads, application/trix+xml, " // - + "text/rdf+n3, application/n3, " // - + "text/n3"); - - final AcceptList offeringResources = new AcceptList("text/html, application/xhtml+xml"); - - public ResourceController() { - - } - - public ResourceController(MessageSource messageSource, OntologyBean ontoBean) { - this.messageSource = messageSource; - this.ontoBean = ontoBean; - } - - @RequestMapping(value = { "{path:(?!staticResources).*$}", "{path:(?!staticResources).*$}/**" }) - public Object resourceController(ModelMap model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "output", defaultValue = "") String output, @CookieValue(value = "colorPair", defaultValue = "") String colorPair) throws UnsupportedEncodingException { - if (colorPair.equals("")) { - colorPair = conf.getRandomColorPair(); - Cookie c = new Cookie("colorPair", colorPair); - c.setPath("/"); - res.addCookie(c); - } - return resource(conf, model, req, res, locale, output, "", colorPair); - } - - public Object resource(ConfigurationBean conf, ModelMap model, HttpServletRequest req, HttpServletResponse res, Locale locale, String output, String forceIRI, String colorPair) throws UnsupportedEncodingException { - - model.addAttribute("conf", conf); - - String IRIsuffix = new UrlPathHelper().getLookupPathForRequest(req).replaceAll("/lodview/", "/"); - String requestUrl = req.getRequestURI(); - - logger.info("IRIsuffix " + IRIsuffix); - logger.info("requestUrl " + requestUrl); - - model.addAttribute("path", new UrlPathHelper().getContextPath(req).replaceAll("/lodview/", "/")); - model.addAttribute("locale", locale.getLanguage()); - - boolean redirect = false; - boolean redirected = false; - boolean avoidRedirection = false; - - // managing redirections - if (!conf.getHttpRedirectExcludeList().equals("")) { - String[] excList = conf.getHttpRedirectExcludeList().split(","); - for (String exclude : excList) { - if (requestUrl.matches(exclude)) { - avoidRedirection = true; - break; - } - } - } - if (!avoidRedirection && !conf.getHttpRedirectSuffix().equals("")) { - // 303 dereferencing mode - if (IRIsuffix.matches(".+" + conf.getHttpRedirectSuffix() + "$")) { - // after redirect - IRIsuffix = IRIsuffix.replaceAll(conf.getHttpRedirectSuffix() + "$", ""); - - redirected = true; - } else { - // before redirect - redirect = true; - } - } else if (!avoidRedirection && !conf.getHttpRedirectPrefix().equals("")) { - // 303 dereferencing mode - if (IRIsuffix.matches("^" + conf.getHttpRedirectPrefix() + ".+")) { - // after redirect - IRIsuffix = IRIsuffix.replaceAll("^" + conf.getHttpRedirectPrefix(), ""); - if (conf.getRedirectionStrategy().equals("pubby")) { - IRIsuffix = "/resource/" + IRIsuffix; - IRIsuffix = IRIsuffix.replaceAll("//", "/"); - } - redirected = true; - } else { - // before redirect - redirect = true; - } - } - - IRIsuffix = IRIsuffix.replaceAll("^/", ""); - - String IRIprefix = conf.getIRInamespace().replaceAll("/$", ""); - String IRI = IRIprefix + "/" + IRIsuffix.replaceAll(" ", "%20"); - - if (forceIRI != null && !forceIRI.equals("")) { - IRI = forceIRI; - } - - if (conf.getForceIriEncoding().equals("encode")) { - String[] IRItokens = IRI.split("/"); - for (int i = 0; i < IRItokens.length; i++) { - IRItokens[i] = java.net.URLEncoder.encode(IRItokens[i], "UTF-8"); - } - IRI = StringUtils.join("/"); - } else if (conf.getForceIriEncoding().equals("decode")) { - String[] IRItokens = IRI.split("/"); - for (int i = 0; i < IRItokens.length; i++) { - IRItokens[i] = java.net.URLDecoder.decode(IRItokens[i], "UTF-8"); - } - IRI = StringUtils.join("/"); - } - - if (conf.getRedirectionStrategy().equals("pubby")) { - /* - * http://dbpedia.org/data/Barack_Obama.ntriples - * http://dbpedia.org/data/Barack_Obama.n3 - * http://dbpedia.org/data/Barack_Obama.json - * http://dbpedia.org/data/Barack_Obama.rdf - */ - if (requestUrl.matches(".+\\.(ntriples|n3|json|rdf)")) { - String outputType = ""; - String newUrl = requestUrl.replaceFirst("/data/", "/resource/").replaceAll("\\.(ntriples|n3|json|rdf)$", ""); - RedirectView r = new RedirectView(); - r.setExposeModelAttributes(false); - if (requestUrl.endsWith(".ntriples")) { - outputType = "text/plain"; - } else if (requestUrl.endsWith(".n3")) { - outputType = "text/turtle"; - } else if (requestUrl.endsWith(".json")) { - outputType = "application/rdf+json"; - } else if (requestUrl.endsWith(".rdf")) { - outputType = "application/rdf+xml"; - } - r.setUrl(newUrl + "?" + (req.getQueryString() != null ? req.getQueryString() + "&" : "") + "output=" + outputType); - return r; - } - } - - logger.info("####################################################################"); - logger.info("################# looking for " + IRI + " ################# "); - - String[] acceptedContent = req.getHeader("Accept").split(","); - if (redirected) { - acceptedContent = "text/html".split(","); - } - // log.trace("Accept " + req.getHeader("Accept")); - - AcceptList a = AcceptList.create(acceptedContent); - // log.trace("-- AcceptList: " + a); - // log.trace("-- OffertList: " + offeringRDF); - - MediaType matchItem = AcceptList.match(offeringRDF, a); - Lang lang = matchItem != null ? RDFLanguages.contentTypeToLang(matchItem.getContentType()) : null; - - // override content negotiation - if (!output.equals("")) { - try { - output = output.replaceAll("([a-zA-Z]) ([a-zA-Z])", "$1+$2"); - a = AcceptList.create(output.split(",")); - matchItem = AcceptList.match(offeringRDF, a); - lang = RDFLanguages.contentTypeToLang(matchItem.getContentType()); - } catch (Exception e) { - return new ErrorController(conf).error406(res, model, colorPair); - } - logger.debug("override content type " + matchItem.getContentType()); - } - - try { - if (lang == null) { - matchItem = AcceptList.match(offeringResources, a); - // probably you are asking for an HTML page - if (matchItem != null) { - if (redirect && !redirected) { - return redirect(req, IRIsuffix); - } else { - return htmlResource(model, IRI, colorPair, locale, req, res); - } - } else { - return new ErrorController(conf).error406(res, model, colorPair); - } - } else { - return resourceRaw(conf, model, IRI, conf.getEndPointUrl(), matchItem.getContentType()); - } - } catch (Exception e) { - e.printStackTrace(); - if (e.getMessage() != null && e.getMessage().startsWith("404")) { - return new ErrorController(conf).error404(res, model, e.getMessage(), colorPair, IRI, conf.getEndPointUrl()); - } else { - return new ErrorController(conf).error500(res, model, e.getMessage(), colorPair, IRI, conf.getEndPointUrl()); - } - } - - } - - private String htmlResource(ModelMap model, String IRI, String colorPair, Locale locale, HttpServletRequest req, HttpServletResponse res) throws Exception { - model.addAttribute("contextPath", new UrlPathHelper().getContextPath(req)); - ResultBean r = new ResourceBuilder(messageSource).buildHtmlResource(IRI, locale, conf, ontoBean); - model.addAttribute("colorPair", Misc.guessColor(colorPair, r, conf)); - model.addAttribute("results", Misc.guessClass(r, conf, ontoBean)); - model.addAttribute("ontoBean", ontoBean); - - addDataLinks(IRI, model, req, locale); - addLodliveLink(locale, model, IRI); - enrichResponse(model, r, req, res); - return "resource"; - } - - private void addDataLinks(String IRI, ModelMap model, HttpServletRequest req, Locale locale) throws UnsupportedEncodingException { - - Map> rawdatalinks = new LinkedHashMap>(); - String queryString = (req.getQueryString() != null ? "&" + req.getQueryString().replaceAll("&", "&") : ""); - - if (conf.getRedirectionStrategy().equals("pubby")) { - - Map list = new LinkedHashMap(); - list.put("xml", "?output=" + URLEncoder.encode("application/rdf+xml", "UTF-8") + queryString); - list.put("ntriples", "?output=" + URLEncoder.encode("text/plain", "UTF-8") + queryString); - list.put("turtle", "?output=" + URLEncoder.encode("text/turtle", "UTF-8") + queryString); - list.put("json", "?output=" + URLEncoder.encode("application/rdf+json", "UTF-8") + queryString); - list.put("ld+json", "?output=" + URLEncoder.encode("application/ld+json", "UTF-8") + queryString); - rawdatalinks.put("rdf:", list); - - } else { - Map list = new LinkedHashMap(); - list.put("xml", "?output=" + URLEncoder.encode("application/rdf+xml", "UTF-8") + queryString); - list.put("ntriples", "?output=" + URLEncoder.encode("text/plain", "UTF-8") + queryString); - list.put("turtle", "?output=" + URLEncoder.encode("text/turtle", "UTF-8") + queryString); - list.put("ld+json", "?output=" + URLEncoder.encode("application/ld+json", "UTF-8") + queryString); - rawdatalinks.put(messageSource.getMessage("footer.viewAs", null, "view as", locale), list); - } - - if (conf.getEndPointType().equals("virtuoso")) { - { - Map list = new LinkedHashMap(); - list.put("atom", conf.getEndPointUrl() + "?output=application%2Fatom%2Bxml&query=DESCRIBE+%3C" + IRI + "%3E"); - list.put("json", conf.getEndPointUrl() + "?output=application%2Fodata%2Bjson&query=DESCRIBE+%3C" + IRI + "%3E"); - rawdatalinks.put("odata:", list); - } - { - Map list = new LinkedHashMap(); - list.put("html", conf.getEndPointUrl() + "?output=text%2Fhtml&query=DESCRIBE+%3C" + IRI + "%3E"); - list.put("json", conf.getEndPointUrl() + "?output=application%2Fmicrodata%2Bjson&query=DESCRIBE+%3C" + IRI + "%3E"); - rawdatalinks.put("microdata:", list); - } - { - Map list = new LinkedHashMap(); - list.put("csv", conf.getEndPointUrl() + "?output=text%2Fcsv&query=DESCRIBE+%3C" + IRI + "%3E"); - list.put("cxml", conf.getEndPointUrl() + "?output=format=text%2Fcxml&query=DESCRIBE+%3C" + IRI + "%3E"); - rawdatalinks.put("rawdata:", list); - } - } - model.addAttribute("rawdatalinks", rawdatalinks); - - } - - private RedirectView redirect(HttpServletRequest req, String IRIsuffix) throws UnsupportedEncodingException { - - RedirectView r = new RedirectView(); - // preventing redirect of model attributes - r.setExposeModelAttributes(false); - r.setContentType("text/html"); - r.setHttp10Compatible(false); - if (!conf.getHttpRedirectPrefix().equals("")) { - // prefix mode - String redirectUrl = conf.getHttpRedirectPrefix().replaceAll("^/", ""); - if (conf.getRedirectionStrategy().equals("pubby")) { - r.setUrl(conf.getPublicUrlPrefix() + redirectUrl + IRIsuffix.replaceAll("^resource/", "") + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); - } else { - r.setUrl(conf.getPublicUrlPrefix().replaceAll(IRIsuffix + "$", "") + redirectUrl + IRIsuffix + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); - } - } else { - // suffix mode - String redirectUrl = conf.getHttpRedirectSuffix(); - // String[] redirectUrlArray = redirectUrl.split("/"); - // redirectUrl = ""; - // for (String string : redirectUrlArray) { - // redirectUrl += URLEncoder.encode(string, "UTF-8") + "/"; - // } - // redirectUrl = redirectUrl.replaceAll("/$", ""); - r.setUrl(req.getRequestURL() + redirectUrl + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); - } - - return r; - - } - - private void addLodliveLink(Locale locale, ModelMap model, String IRI) { - if (locale.getLanguage().equals("it")) { - model.addAttribute("lodliveUrl", "http://lodlive.it?" + IRI.replaceAll("#", "%23")); - } else if (locale.getLanguage().equals("fr")) { - model.addAttribute("lodliveUrl", "http://fr.lodlive.it?" + IRI.replaceAll("#", "%23")); - } else if (locale.getLanguage().equals("gl")) { - model.addAttribute("lodliveUrl", "http://gl.lodlive.it?" + IRI.replaceAll("#", "%23")); - } else { - model.addAttribute("lodliveUrl", "http://en.lodlive.it?" + IRI.replaceAll("#", "%23")); - } - } - - private void enrichResponse(ModelMap model, ResultBean r, HttpServletRequest req, HttpServletResponse res) { - - String publicUrl = r.getMainIRI(); - res.addHeader("Link", "<" + publicUrl + ">; rel=\"about\""); - - @SuppressWarnings("unchecked") - Map> rawdatalinks = (LinkedHashMap>) model.get("rawdatalinks"); - for (String k : rawdatalinks.keySet()) { - for (String k1 : rawdatalinks.get(k).keySet()) { - res.addHeader("Link", "<" + rawdatalinks.get(k).get(k1) + ">; rel=\"alternate\"; type=\"application/rdf+xml\"; title=\"Structured Descriptor Document (" + k1 + ")\""); - } - } - try { - for (TripleBean t : r.getResources(r.getMainIRI()).get(r.getTypeProperty())) { - res.addHeader("Link", "<" + t.getProperty().getProperty() + ">; rel=\"type\""); - } - } catch (Exception e) { - // TODO: handle exception - } - } - - @RequestMapping(value = "/rawdata") - public ResponseEntity resourceRawController(ModelMap model, @RequestParam(value = "IRI") String IRI, @RequestParam(value = "sparql") String sparql, @RequestParam(value = "contentType", defaultValue = "application/rdf+xml") String contentType) { - return resourceRaw(conf, model, IRI, sparql, contentType); - } - - public ResponseEntity resourceRaw(ConfigurationBean conf, ModelMap model, @RequestParam(value = "IRI") String IRI, @RequestParam(value = "sparql") String sparql, @RequestParam(value = "contentType", defaultValue = "application/rdf+xml") String contentType) { - // logger.trace("ResourceController.resourceRaw()"); - contentType = contentType.replaceAll("([a-zA-Z]) ([a-zA-Z])", "$1+$2"); - Lang lang = RDFLanguages.contentTypeToLang(contentType); - try { - HttpHeaders headers = new HttpHeaders(); - headers.add("Content-Type", contentType + "; charset=" + conf.getContentEncoding()); - - if (sparql != null && sparql.equals("<>")) { - com.hp.hpl.jena.rdf.model.Model m = ModelFactory.createDefaultModel(); - try { - m.read(IRI); - } catch (Exception e) { - throw new Exception(messageSource.getMessage("error.noContentNegotiation", null, "sorry but content negotiation is not supported by the IRI", Locale.ENGLISH)); - } - return new ResponseEntity(new ResourceBuilder(messageSource).buildRDFResource(IRI, m, lang, conf), headers, HttpStatus.OK); - } else { - return new ResponseEntity(new ResourceBuilder(messageSource).buildRDFResource(IRI, sparql, lang, conf), headers, HttpStatus.OK); - } - - } catch (Exception e) { - if (e.getMessage() != null && e.getMessage().startsWith("404")) { - return new ResponseEntity(e.getMessage(), HttpStatus.NOT_FOUND); - } else { - return new ResponseEntity(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - } -} diff --git a/src/main/java/org/dvcama/lodview/controllers/SPARQLController.java b/src/main/java/org/dvcama/lodview/controllers/SPARQLController.java deleted file mode 100644 index 0a04531..0000000 --- a/src/main/java/org/dvcama/lodview/controllers/SPARQLController.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.dvcama.lodview.controllers; - -import org.dvcama.lodview.conf.ConfigurationBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -@Controller -public class SPARQLController { - @Autowired - ConfigurationBean conf; - - @RequestMapping(value = { "/sparql", "/SPARQL" }) - public String sparql() { - - /* TODO: make this configurable allowing "proxy" features */ - - return "redirect:" + conf.getEndPointUrl(); - } -} diff --git a/src/main/java/org/dvcama/lodview/controllers/StaticController.java b/src/main/java/org/dvcama/lodview/controllers/StaticController.java deleted file mode 100644 index 1ba0149..0000000 --- a/src/main/java/org/dvcama/lodview/controllers/StaticController.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.dvcama.lodview.controllers; - -import java.util.Locale; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.dvcama.lodview.conf.ConfigurationBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.util.UrlPathHelper; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Controller -public class StaticController { - - final Logger logger = LoggerFactory.getLogger(StaticController.class); - - @Autowired - ConfigurationBean conf; - - @Autowired - private MessageSource messageSource; - - public StaticController() { - // TODO Auto-generated constructor stub - } - - public StaticController(MessageSource messageSource) { - this.messageSource = messageSource; - } - - @RequestMapping(value = "/") - public String home(HttpServletRequest req, HttpServletResponse res, Model model, Locale locale, @CookieValue(value = "colorPair", defaultValue = "") String colorPair) { - colorPair = conf.getRandomColorPair(); - Cookie c = new Cookie("colorPair", colorPair); - c.setPath("/"); - res.addCookie(c); - model.addAttribute("colorPair", colorPair); - model.addAttribute("conf", conf); - model.addAttribute("locale", locale.getLanguage()); - model.addAttribute("path", new UrlPathHelper().getContextPath(req).replaceAll("/lodview/", "/")); - logger.debug("home controller"); - return "home"; - } - - @RequestMapping(value = "/lodviewmenu") - public String lodviewmenu(Model model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "IRI") String IRI, @CookieValue(value = "colorPair", defaultValue = "") String colorPair) { - if (colorPair.equals("")) { - colorPair = conf.getRandomColorPair(); - Cookie c = new Cookie("colorPair", colorPair); - c.setPath("/"); - res.addCookie(c); - } - return lodviewmenu(req, res, model, locale, IRI, conf, colorPair); - } - - @RequestMapping(value = { "/lodviewcolor", "/**/lodviewcolor" }) - public ResponseEntity lodviewcolor(Model model, HttpServletRequest req, HttpServletResponse res, Locale locale, @RequestParam(value = "colorPair") String colorPair) { - Cookie c = new Cookie("colorPair", colorPair); - c.setPath("/"); - res.addCookie(c); - return new ResponseEntity(HttpStatus.OK); - } - - public String lodviewmenu(HttpServletRequest req, HttpServletResponse res, Model model, Locale locale, @RequestParam(value = "IRI", defaultValue = "") String IRI, ConfigurationBean conf, String colorPair) { - model.addAttribute("conf", conf); - model.addAttribute("locale", locale.getLanguage()); - model.addAttribute("IRI", IRI); - model.addAttribute("colorPair", colorPair); - model.addAttribute("path", new UrlPathHelper().getContextPath(req).replaceAll("/lodview/", "/")); - return "menu"; - } - -} diff --git a/src/main/java/org/dvcama/lodview/endpoint/SPARQLEndPoint.java b/src/main/java/org/dvcama/lodview/endpoint/SPARQLEndPoint.java deleted file mode 100644 index f64166f..0000000 --- a/src/main/java/org/dvcama/lodview/endpoint/SPARQLEndPoint.java +++ /dev/null @@ -1,334 +0,0 @@ -package org.dvcama.lodview.endpoint; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.jena.atlas.web.auth.HttpAuthenticator; -import org.apache.jena.atlas.web.auth.SimpleAuthenticator; -import org.dvcama.lodview.bean.OntologyBean; -import org.dvcama.lodview.bean.PropertyBean; -import org.dvcama.lodview.bean.TripleBean; -import org.dvcama.lodview.conf.ConfigurationBean; -import org.dvcama.lodview.utils.Misc; - -import com.hp.hpl.jena.graph.Node; -import com.hp.hpl.jena.query.QueryExecution; -import com.hp.hpl.jena.query.QueryExecutionFactory; -import com.hp.hpl.jena.query.QuerySolution; -import com.hp.hpl.jena.query.ResultSet; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.Property; -import com.hp.hpl.jena.rdf.model.RDFNode; -import com.hp.hpl.jena.rdf.model.Resource; -import com.hp.hpl.jena.rdf.model.Statement; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SPARQLEndPoint { - - private static Logger logger = LoggerFactory.getLogger(SPARQLEndPoint.class); - - OntologyBean ontoBean; - String locale = "en"; - protected ConfigurationBean conf; - - public SPARQLEndPoint(ConfigurationBean conf, OntologyBean ontoBean, String locale) { - this.locale = locale; - this.ontoBean = ontoBean; - this.conf = conf; - // TODO Auto-generated constructor stub - } - - public List doQuery(String IRI, String aProperty, int start, List queries, String filter, String overrideProperty) throws Exception { - // logger.trace("executing query on " + conf.getEndPointUrl()); - List results = new ArrayList(); - HttpAuthenticator auth = null; - if (conf.getAuthPassword() != null && !conf.getAuthPassword().equals("")) { - auth = new SimpleAuthenticator(conf.getAuthUsername(), conf.getAuthPassword().toCharArray()); - } - for (String query : queries) { - // logger.trace("-- " + parseQuery(query, IRI, aProperty, - // start, filter)); - QueryExecution qe = QueryExecutionFactory.sparqlService(conf.getEndPointUrl(), parseQuery(query, IRI, aProperty, start, filter), auth); - results = moreThenOneQuery(qe, results, 0, overrideProperty); - qe.close(); - } - - if (results.size() == 0) { - if (IRI != null) { - boolean hasInverses = false; - for (String query : conf.getDefaultInversesTest()) { - // logger.trace("query!!! " + parseQuery(query, IRI, - // aProperty, start, filter)); - QueryExecution qe = QueryExecutionFactory.sparqlService(conf.getEndPointUrl(), parseQuery(query, IRI, aProperty, start, filter), auth); - if (!hasInverses) { - hasInverses = qe.execAsk(); - } - qe.close(); - } - if (!hasInverses) { - throw new Exception("404 - not found"); - } - } - } - return results; - } - - private List moreThenOneQuery(QueryExecution qe, List results, int retry, String overrideProperty) throws Exception { - - try { - ResultSet rs = qe.execSelect(); - while (rs.hasNext()) { - TripleBean rb = new TripleBean(); - QuerySolution qs = rs.next(); - String property = ""; - if (overrideProperty != null) { - property = overrideProperty; - } else if (qs.get("p") != null) { - property = qs.get("p").asNode().toString(); - } - - try { - if (qs.get("s") != null && !qs.get("s").asNode().toString().startsWith("http://")) { // probably - // a - // bn - rb.setIRI(qs.get("s").asNode().toString()); - rb.setNsIRI("_:" + rb.getIRI()); - } else if (qs.get("s") != null && qs.get("s").asNode().toString().startsWith("http://")) { - rb.setIRI(qs.get("s").asNode().toString()); - rb.setNsIRI(Misc.toNsResource(rb.getIRI(), conf)); - rb.setUrl(Misc.toBrowsableUrl(rb.getIRI(), conf)); - } - - PropertyBean p = new PropertyBean(); - p.setNsProperty(Misc.toNsResource(property, conf)); - p.setProperty(property); - if (ontoBean != null) { - p.setLabel(ontoBean.getEscapedValue("label", locale, property)); - p.setComment(ontoBean.getEscapedValue("comment", locale, property)); - } - p.setPropertyUrl(Misc.toBrowsableUrl(property, conf)); - rb.setProperty(p); - if (qs.get("o") != null) { - Node object = qs.get("o").asNode(); - if (object.isURI()) { - rb.setType("iri"); - rb.setValue(object.toString(false)); - } else if (object.isLiteral()) { - rb.setType("literal"); - rb.setDataType(object.getLiteralDatatypeURI()); - rb.setNsDataType(Misc.toNsResource(object.getLiteralDatatypeURI(), conf)); - rb.setLang(object.getLiteralLanguage()); - rb.setValue(object.getLiteralLexicalForm()); - } else if (object.isBlank()) { - rb.setType("bnode"); - rb.setValue(object.toString(false)); - } - } else { - rb.setType("literal"); - rb.setValue(""); - } - results.add(rb); - } catch (Exception e) { - logger.error("error? " + e.getMessage()); - // e.printStackTrace(); - } - } - } catch (Exception ez) { - if (retry < 3) { - retry++; - // logger.trace("query failed (" + ez.getMessage() + - // "), I'm giving another chance (" + retry + "/3)"); - return moreThenOneQuery(qe, results, retry, overrideProperty); - } - ez.printStackTrace(); - throw new Exception("connection refused"); - } - - return results; - } - - public List doQuery(String IRI, List queries, String overrideProperty) throws Exception { - return doQuery(IRI, null, -1, queries, null, overrideProperty); - } - - public List doLocalQuery(Model m, String IRI, List queries, String about) throws Exception { - return doLocalQuery(m, IRI, null, -1, queries, about); - } - - public List doLocalQuery(Model model, String IRI, List queries) throws Exception { - return doLocalQuery(model, IRI, null, -1, queries, null); - } - - public List doLocalQuery(Model model, String IRI, String localProperty, int start, List queries, String overrideProperty) throws Exception { - // logger.trace("executing query on model based on " + IRI); - List results = new ArrayList(); - - for (String query : queries) { - QueryExecution qe = QueryExecutionFactory.create(parseQuery(query, IRI, localProperty, start, null), model); - try { - ResultSet rs = qe.execSelect(); - while (rs.hasNext()) { - TripleBean rb = new TripleBean(); - QuerySolution qs = rs.next(); - String property = ""; - if (overrideProperty != null) { - property = overrideProperty; - } else if (qs.get("p") != null) { - property = qs.get("p").asNode().toString(); - } - try { - if (qs.get("s") != null && !qs.get("s").asNode().toString().startsWith("http://")) { // probably - // blanknode - rb.setIRI(qs.get("s").asNode().toString()); - rb.setNsIRI("_:" + rb.getIRI()); - } else if (qs.get("s") != null && qs.get("s").asNode().toString().startsWith("http://")) { - rb.setIRI(qs.get("s").asNode().toString()); - rb.setNsIRI(Misc.toNsResource(rb.getIRI(), conf)); - rb.setUrl(Misc.toBrowsableUrl(rb.getIRI(), conf)); - } - PropertyBean p = new PropertyBean(); - p.setNsProperty(Misc.toNsResource(property, conf)); - p.setProperty(property); - p.setPropertyUrl(Misc.toBrowsableUrl(property, conf)); - if (ontoBean != null) { - p.setLabel(ontoBean.getEscapedValue("label", locale, property)); - p.setComment(ontoBean.getEscapedValue("comment", locale, property)); - } - rb.setProperty(p); - if (qs.get("o") != null) { - Node object = qs.get("o").asNode(); - if (object.isURI()) { - rb.setType("iri"); - rb.setValue(object.toString(false)); - } else if (object.isLiteral()) { - rb.setType("literal"); - rb.setDataType(object.getLiteralDatatypeURI()); - rb.setNsDataType(Misc.toNsResource(object.getLiteralDatatypeURI(), conf)); - rb.setLang(object.getLiteralLanguage()); - rb.setValue(object.getLiteralLexicalForm()); - } else if (object.isBlank()) { - rb.setType("bnode"); - rb.setValue(object.toString(false)); - } - } else { - rb.setType("literal"); - rb.setValue(""); - } - results.add(rb); - } catch (Exception e) { - logger.error("error? " + e.getMessage()); - e.printStackTrace(); - } - } - } catch (Exception e) { - throw new Exception("500 - " + e.getMessage()); - } finally { - qe.close(); - } - } - - if (results.size() == 0) { - throw new Exception("404 - not found"); - } - return results; - } - - public Model extractData(Model result, String IRI, String sparql, List queries) throws Exception { - - // logger.trace("executing query on " + sparql); - Resource subject = result.createResource(IRI); - for (String query : queries) { - QueryExecution qe = QueryExecutionFactory.sparqlService(sparql, parseQuery(query, IRI, null, -1, null)); - try { - ResultSet rs = qe.execSelect(); - - List sl = new ArrayList(); - while (rs.hasNext()) { - QuerySolution qs = rs.next(); - RDFNode subject2 = qs.get("s"); - RDFNode property = qs.get("p"); - RDFNode object = qs.get("o"); - result.add(result.createStatement(subject2 != null ? subject2.asResource() : subject, property.as(Property.class), object)); - } - result.add(sl); - } catch (Exception e) { - e.printStackTrace(); - throw new Exception("error in query execution: " + e.getMessage()); - } finally { - qe.close(); - } - } - return result; - } - - public Model extractLocalData(Model result, String IRI, Model m, List queries) throws Exception { - - // logger.trace("executing query on IRI"); - Resource subject = result.createResource(IRI); - for (String query : queries) { - QueryExecution qe = QueryExecutionFactory.create(parseQuery(query, IRI, null, -1, null), m); - try { - ResultSet rs = qe.execSelect(); - List sl = new ArrayList(); - while (rs.hasNext()) { - QuerySolution qs = rs.next(); - RDFNode subject2 = qs.get("s"); - RDFNode property = qs.get("p"); - RDFNode object = qs.get("o"); - result.add(result.createStatement(subject2 != null ? subject2.asResource() : subject, property.as(Property.class), object)); - } - result.add(sl); - } catch (Exception e) { - e.printStackTrace(); - throw new Exception("error in query execution: " + e.getMessage()); - } finally { - qe.close(); - } - } - return result; - } - - public String parseQuery(String query, String IRI, String property, int start, String filter) { - if (IRI != null) { - /* managing issues depending on "$" in some IRIs */ - query = query.replaceAll("\\$\\{IRI\\}", IRI.replaceAll("\\$", "%24")).replaceAll("%24", "\\$"); - } - if (property != null) { - query = query.replaceAll("\\$\\{PROPERTY\\}", property); - } - if (filter != null) { - query = query.replaceAll("\\$\\{FILTERPROPERTY\\}", filter); - } - if (query.indexOf("STARTFROM") > 0) { - query = query.replaceAll("\\$\\{STARTFROM\\}", "" + start); - } else if (start > 0) { - query = query.replaceAll("LIMIT (.+)$", "OFFSET " + start + " LIMIT $1"); - } - return query; - } - - public String testEndpoint(ConfigurationBean conf) { - logger.info("testing connection on " + conf.getEndPointUrl()); - QueryExecution qe = QueryExecutionFactory.sparqlService(conf.getEndPointUrl(), "select ?s {?s ?p ?o} LIMIT 1"); - String msg = ""; - try { - ResultSet rs = qe.execSelect(); - if (rs.hasNext()) { - logger.info("is online"); - msg = "online"; - } else { - logger.info("is offline"); - msg = "offline"; - } - } catch (Exception e) { - logger.info("is offline " + e.getMessage()); - msg = "offline " + e.getMessage(); - } finally { - qe.close(); - } - return msg; - } - -} diff --git a/src/main/java/org/dvcama/lodview/utils/Misc.java b/src/main/java/org/dvcama/lodview/utils/Misc.java deleted file mode 100644 index 54f47e8..0000000 --- a/src/main/java/org/dvcama/lodview/utils/Misc.java +++ /dev/null @@ -1,146 +0,0 @@ -package org.dvcama.lodview.utils; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.dvcama.lodview.bean.OntologyBean; -import org.dvcama.lodview.bean.PropertyBean; -import org.dvcama.lodview.bean.ResultBean; -import org.dvcama.lodview.bean.TripleBean; -import org.dvcama.lodview.conf.ConfigurationBean; -import org.dvcama.lodview.conf.ConfigurationBean.ColorStrategy; - -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.NodeIterator; -import com.hp.hpl.jena.rdf.model.RDFNode; - -public class Misc { - - public static String toNsResource(String iri, ConfigurationBean conf) { - if (iri != null && !iri.equals("")) { - // if (iri.startsWith(conf.getIRInamespace())) { - // return conf.getNsURIPrefix(conf.getIRInamespace()) + ":" + - // iri.replaceAll(conf.getIRInamespace() + "(.+)$", "$1"); - // } else { - // } - return conf.getNsURIPrefix(iri.replaceAll("[^/#]+$", "")) + ":" + iri.replaceAll(".+[/|#]([^/#]+)$", "$1"); - - } else { - return null; - } - } - - public static String toBrowsableUrl(String value, ConfigurationBean conf) { - - if (!conf.getPublicUrlSuffix().equals("") && value.startsWith(conf.getIRInamespace())) { - try { - return conf.getPublicUrlPrefix() + "?" + conf.getPublicUrlSuffix() + "IRI=" + java.net.URLEncoder.encode(value, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return value; - } - } else { - return value.replaceAll("^" + conf.getIRInamespace() + "(.+)$", conf.getPublicUrlPrefix() + "$1").replaceAll("([^:])//", "$1/"); - } - - } - - public static String guessColor(String colorPair, ResultBean r, ConfigurationBean conf) { - switch(conf.getColorStrategy()) - { - case CLASS: { - try { - List m = r.getResources(r.getMainIRI()).get(r.getTypeProperty()); - for (TripleBean tripleBean : m) - { - colorPair = conf.getColorPairMatcher().get(tripleBean.getValue()); - if(colorPair!=null) return colorPair; - } - } catch (Exception e) { - } - return conf.getColorPairMatcher().get("http://lodview.it/conf#otherClasses"); - } - case PREFIX: { - for (String prefix : conf.getColorPairMatcher().keySet()) { - if(r.getMainIRI().startsWith(prefix)) - { - return conf.getColorPairMatcher().get(prefix); - } - } - } - default: break; - } - return colorPair; - } - - public static ResultBean guessClass(ResultBean r, ConfigurationBean conf, OntologyBean ontoBean) { - try { - if (conf.getMainOntologiesPrefixes().size() > 0) { - String mainIri = r.getMainIRI(); - Map> mainResource = r.getResources(mainIri); - TreeMap> resultOrderedMap = new TreeMap>(); - - List m = new ArrayList(mainResource.get(r.getTypeProperty())); - Model model = ontoBean.getModel(); - for (TripleBean tripleBean : m) { - int dept = 0; - if (startsWithAtLeastOne(tripleBean.getValue(), conf.getMainOntologiesPrefixes())) { - dept = countFathers(tripleBean.getValue(), 0, model); - } - List l = null; - if (resultOrderedMap.get(dept) != null) { - l = resultOrderedMap.get(dept); - } else { - l = new ArrayList(); - } - l.add(tripleBean); - resultOrderedMap.put(dept, l); - // removing types - r.removeResource(tripleBean, mainIri); - } - if (resultOrderedMap.size() > 0) { - for (Integer dept : resultOrderedMap.descendingKeySet()) { - List l = resultOrderedMap.get(dept); - for (TripleBean tripleBean : l) { - // adding ordered types - r.addResource(tripleBean, mainIri); - } - } - } - } - return r; - - } catch (Exception e) { - // e.printStackTrace(); - } - return r; - } - - private static Integer countFathers(String value, int i, Model model) { - NodeIterator iter = model.listObjectsOfProperty(model.createResource(value), model.createProperty("http://www.w3.org/2000/01/rdf-schema#subClassOf")); - while (iter.hasNext()) { - RDFNode node = iter.next(); - return countFathers(node.toString(), ++i, model); - } - return i; - } - - private static boolean startsWithAtLeastOne(String value, List startsWithList) { - for (String string : startsWithList) { - if (value.startsWith(string)) { - return true; - } - } - return false; - } - - public static String stripHTML(String value) { - - value = value.replaceAll("]*>", ""); - - return value; - } - -} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..9dbe827 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,4 @@ +spring.mvc.pathmatch.matching-strategy=ant_path_matcher +spring.mvc.view.prefix=/WEB-INF/views/ +spring.mvc.view.suffix=.jsp +lode.ontoDir=ontologies diff --git a/src/main/resources/conf/conf-linked.ttl b/src/main/resources/conf/conf-linked.ttl new file mode 100644 index 0000000..f67dfd0 --- /dev/null +++ b/src/main/resources/conf/conf-linked.ttl @@ -0,0 +1,124 @@ +@prefix conf: . +@prefix meta: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix owl: . +@prefix dc: . +@prefix dcterms: . +@prefix foaf: . +@prefix skos: . +@prefix geo: . +@prefix ocd: . +@prefix units: . +@prefix geonames: . +@prefix void: . +@prefix dbpedia-owl: . +@prefix yago: . +@prefix gml: . +@prefix dbpedia: . +@prefix dbpprop: . +@prefix metalex: . +@prefix frbr: . +@prefix gn: . +@prefix schema-org: . +@prefix dwc: . +@prefix ibc: . +@prefix bio: . +@prefix ods: . +@prefix shoah: . +@prefix bibo: . +@prefix org: . +@prefix bbc: . + +<> a conf:Configuration; + +## EndPoint + + conf:contentEncoding "UTF-8"; + + +## default query used to get direct properties, you can add a "FROM" clause here, +## ${IRI} will be replaced, it's always recommended to specify a limit for the query + + conf:defaultQueries "select distinct * {<${IRI}> ?p ?o} LIMIT 10000"; + + +## default query used to get resource to be included in serializations different +## from HTML, it's always recommended to specify a limit for the query +## ${IRI} will be replaced + + conf:defaultRawDataQueries """select distinct * { {<${IRI}> ?p ?o} + UNION + {<${IRI}> ?p1 ?s . ?s ?p ?o FILTER(isBlank(?s))} + } LIMIT 10000""" ; + +## skip domains that are probably offline + + conf:skipDomains "ace.dbpedia.org" , "af.dbpedia.org" , "als.dbpedia.org" , "am.dbpedia.org" , "an.dbpedia.org" , + "ang.dbpedia.org" , "ar.dbpedia.org" , "arc.dbpedia.org" , "arz.dbpedia.org" , "ast.dbpedia.org" , + "ay.dbpedia.org" , "az.dbpedia.org" , "ba.dbpedia.org" , "bar.dbpedia.org" , "bat_smg.dbpedia.org" , + "bcl.dbpedia.org" , "be.dbpedia.org" , "be_x_old.dbpedia.org" , "bg.dbpedia.org" , "bi.dbpedia.org" , + "bn.dbpedia.org" , "bo.dbpedia.org" , "br.dbpedia.org" , "bs.dbpedia.org" , "bxr.dbpedia.org" , + "ca.dbpedia.org" , "cbk_zam.dbpedia.org" , "ce.dbpedia.org" , "ceb.dbpedia.org" , "chy.dbpedia.org" , + "ckb.dbpedia.org" , "co.dbpedia.org" , "crh.dbpedia.org" , "csb.dbpedia.org" , "cu.dbpedia.org" , + "cv.dbpedia.org" , "cy.dbpedia.org" , "da.dbpedia.org" , "diq.dbpedia.org" , "dsb.dbpedia.org" , + "ee.dbpedia.org" , "eo.dbpedia.org" , "et.dbpedia.org" , "ext.dbpedia.org" , "fa.dbpedia.org" , + "fi.dbpedia.org" , "fiu_vro.dbpedia.org" , "fo.dbpedia.org" , "frp.dbpedia.org" , "frr.dbpedia.org" , + "fur.dbpedia.org" , "fy.dbpedia.org" , "ga.dbpedia.org" , "gag.dbpedia.org" , "gan.dbpedia.org" , + "gd.dbpedia.org" , "gl.dbpedia.org" , "gn.dbpedia.org" , "got.dbpedia.org" , "gv.dbpedia.org" , + "he.dbpedia.org" , "hi.dbpedia.org" , "hif.dbpedia.org" , "hr.dbpedia.org" , "hsb.dbpedia.org" , + "ht.dbpedia.org" , "hu.dbpedia.org" , "hy.dbpedia.org" , "ia.dbpedia.org" , "ie.dbpedia.org" , + "ilo.dbpedia.org" , "io.dbpedia.org" , "is.dbpedia.org" , "jbo.dbpedia.org" , "jv.dbpedia.org" , + "ka.dbpedia.org" , "kaa.dbpedia.org" , "kg.dbpedia.org" , "kk.dbpedia.org" , "kl.dbpedia.org" , + "km.dbpedia.org" , "kn.dbpedia.org" , "krc.dbpedia.org" , "ku.dbpedia.org" , "kv.dbpedia.org" , + "kw.dbpedia.org" , "la.dbpedia.org" , "lad.dbpedia.org" , "lb.dbpedia.org" , "lez.dbpedia.org" , + "li.dbpedia.org" , "lij.dbpedia.org" , "lmo.dbpedia.org" , "ln.dbpedia.org" , "lt.dbpedia.org" , + "lv.dbpedia.org" , "mdf.dbpedia.org" , "mg.dbpedia.org" , "mhr.dbpedia.org" , "mi.dbpedia.org" , + "mk.dbpedia.org" , "ml.dbpedia.org" , "mn.dbpedia.org" , "mr.dbpedia.org" , "ms.dbpedia.org" , + "mt.dbpedia.org" , "mwl.dbpedia.org" , "my.dbpedia.org" , "na.dbpedia.org" , "nah.dbpedia.org" , + "nap.dbpedia.org" , "nds.dbpedia.org" , "nds_nl.dbpedia.org" , "ne.dbpedia.org" , "new.dbpedia.org" , + "nn.dbpedia.org" , "no.dbpedia.org" , "nov.dbpedia.org" , "nrm.dbpedia.org" , "oc.dbpedia.org" , + "or.dbpedia.org" , "os.dbpedia.org" , "pa.dbpedia.org" , "pap.dbpedia.org" , "pcd.dbpedia.org" , + "pdc.dbpedia.org" , "pms.dbpedia.org" , "pnb.dbpedia.org" , "pnt.dbpedia.org" , "qu.dbpedia.org" , + "rm.dbpedia.org" , "rn.dbpedia.org" , "ro.dbpedia.org" , "roa_rup.dbpedia.org" , "roa_tara.dbpedia.org" , + "ru.dbpedia.org" , "rue.dbpedia.org" , "rw.dbpedia.org" , "sa.dbpedia.org" , "sah.dbpedia.org" , + "sc.dbpedia.org" , "scn.dbpedia.org" , "sco.dbpedia.org" , "se.dbpedia.org" , "sg.dbpedia.org" , + "sh.dbpedia.org" , "si.dbpedia.org" , "simple.dbpedia.org" , "sk.dbpedia.org" , "sl.dbpedia.org" , + "so.dbpedia.org" , "sq.dbpedia.org" , "sr.dbpedia.org" , "srn.dbpedia.org" , "stq.dbpedia.org" , + "su.dbpedia.org" , "sv.dbpedia.org" , "sw.dbpedia.org" , "szl.dbpedia.org" , "ta.dbpedia.org" , + "te.dbpedia.org" , "tg.dbpedia.org" , "th.dbpedia.org" , "tk.dbpedia.org" , "tl.dbpedia.org" , + "tpi.dbpedia.org" , "tr.dbpedia.org" , "tt.dbpedia.org" , "ty.dbpedia.org" , "udm.dbpedia.org" , + "ug.dbpedia.org" , "uk.dbpedia.org" , "ur.dbpedia.org" , "uz.dbpedia.org" , "ve.dbpedia.org" , + "vec.dbpedia.org" , "vep.dbpedia.org" , "vi.dbpedia.org" , "vls.dbpedia.org" , "vo.dbpedia.org" , + "war.dbpedia.org" , "wo.dbpedia.org" , "wuu.dbpedia.org" , "xmf.dbpedia.org" , "yi.dbpedia.org" , + "yo.dbpedia.org" , "zea.dbpedia.org" , "zh.dbpedia.org" , "zh_classical.dbpedia.org" , "zh_min_nan.dbpedia.org" , + "zh_yue.dbpedia.org" , "eml.dbpedia.org" , "ksh.dbpedia.org" , "pfl.dbpedia.org" , "zh-yue.dbpedia.org" , + "zh-min-nan.dbpedia.org" , "be-x-old.dbpedia.org" , "bat-smg.dbpedia.org" ; + + +######################################################################################## +######################## configuring the webapp ######################## +######################################################################################## + + +## document specific property used in the HTML resource page, it's possible to use +## prefixes or not + + conf:typeProperties rdf:type; + conf:titleProperties dc:title , dcterms:title, rdfs:label , , , , gn:officialName , gn:name , rdf:value , foaf:surname , , , schema-org:name, ; + conf:descriptionProperties rdfs:comment , skos:definition , dc:description , dcterms:description , dbpedia-owl:abstract ; + conf:imageProperties foaf:depiction; + conf:longitudeProperties geo:long; + conf:latitudeProperties geo:lat; + conf:linkingProperties owl:sameAs , skos:exactMatch , dbpedia-owl:wikiPageRedirects; + + +## preferred language for title and description ("auto" for delegate to the client) + + conf:preferredLanguage "auto"; ## eg. "en" or "it" + + +## just a marker to insert a point on the last row + + conf:last "." . \ No newline at end of file diff --git a/src/main/resources/conf/conf.ttl b/src/main/resources/conf/conf.ttl new file mode 100644 index 0000000..1b01b53 --- /dev/null +++ b/src/main/resources/conf/conf.ttl @@ -0,0 +1,225 @@ +@prefix conf: . +@prefix meta: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix owl: . +@prefix dc: . +@prefix dcterms: . +@prefix foaf: . +@prefix skos: . +@prefix geo: . +@prefix ocd: . +@prefix units: . +@prefix geonames: . +@prefix void: . +@prefix dbpedia-owl: . +@prefix yago: . +@prefix gml: . +@prefix dbpedia: . +@prefix dbpprop: . +@prefix metalex: . +@prefix frbr: . +@prefix gn: . +@prefix schema-org: . +@prefix dwc: . +@prefix ibc: . +@prefix bio: . +@prefix ods: . +@prefix shoah: . +@prefix bibo: . +@prefix org: . +@prefix bbc: . +@prefix npg: . +@prefix prism21: . +@prefix rso: . +@prefix crm: . +@prefix bmuseum: . +@prefix bbc: . +@prefix po: . +@prefix lgdo: . +@prefix oad: . +@prefix crm-owl: . +@prefix aemetonto: . +@prefix bibleontology: . +@prefix cdoc: . +@prefix cc: . +@prefix prov: . +@prefix skos-xl: . +@prefix muninn: . +@prefix eac-cpf: . +@prefix time: . +@prefix claros: . +@prefix crm120111: . +@prefix rel: . + + +<> a conf:Configuration; + +## first of all +## IRIs namespace we use to replace the webapp base (eg. http://localhost:8080/lodview/), an +## installation on dbpedia domain should use + + conf:IRInamespace ; + +## EndPoint + + conf:endpoint ; + conf:endpointType "virtuoso"; ## just "virtuoso" is handled right now, this will add some "rawdata" + ## options in the footer, leave this blank if you don't want these links + +########## give it a try opening http://localhost:8080/lodview/Rome + + +## we don't like it so much, but you can set a prefix to activate 303 redirects +## to the HTML representation of the resources, live it blank if you want to +## use the "pure content negotiation mode", WARNING use suffix OR prefix, not both + + conf:httpRedirectSuffix ".html"; ## or eg. ".html"; + ##conf:httpRedirectPrefix "/page/"; ## or eg. ""; + + ##conf:httpRedirectExcludeList ""; ## a comma separeted list of regex to match which resources will use "pure content negotiation mode" + ## usually you have to leave this blank but if conf:redirectionStrategy is setted to "pubby" this could be + ## useful (eg. dbpedia uses /resource --> /page but leaves /ontology --> /ontology) + + conf:redirectionStrategy ""; ## set it to "pubby" to use exactly the dbpedia redirection behaviour + + + +## mode configurations + + conf:contentEncoding "UTF-8"; + + ## base64 HTTP authentication to the endpoint + conf:authUsername ""; + conf:authPassword ""; + + + +## default query used to get direct properties, you can add a "FROM" clause here, +## ${IRI} will be replaced, it's always recommended to specify a limit for the query +## the query can be seplified removing the last 2 unions to improve performance (usually not needed) + + conf:defaultQueries """select distinct ?s ?p ?o { + {<${IRI}> ?p ?o} + UNION + {<${IRI}> ?p1 ?s . FILTER(isBlank(?s)) . ?s ?p ?o} + UNION + {<${IRI}> ?p1 ?s1 . FILTER(isBlank(?s1)) . ?s1 ?p2 ?s . FILTER(isBlank(?s)) . ?s ?p ?o} + UNION + {<${IRI}> ?p1 ?s1 . FILTER(isBlank(?s1)) . ?s1 ?p2 ?s2 . FILTER(isBlank(?s2)) . ?s2 ?p3 ?s . FILTER(isBlank(?s)) . ?s ?p ?o} + } LIMIT 10000""" ; + + +## default query used to get resource to be included in serializations different +## from HTML, it's always recommended to specify a limit for the query +## ${IRI} will be replaced +## the query can be seplified removing the last 2 unions to improve performance (usually not needed) + + conf:defaultRawDataQueries """select distinct ?s ?p ?o { + {<${IRI}> ?p ?o} + UNION + {<${IRI}> ?p1 ?s . FILTER(isBlank(?s)) . ?s ?p ?o} + UNION + {<${IRI}> ?p1 ?s1 . FILTER(isBlank(?s1)) . ?s1 ?p2 ?s . FILTER(isBlank(?s)) . ?s ?p ?o} + UNION + {<${IRI}> ?p1 ?s1 . FILTER(isBlank(?s1)) . ?s1 ?p2 ?s2 . FILTER(isBlank(?s2)) . ?s2 ?p3 ?s . FILTER(isBlank(?s)) . ?s ?p ?o} + } LIMIT 10000""" ; + + +## default query used to extract inverse relations + + conf:defaultInversesQueries "select distinct ?p {?p <${PROPERTY}> <${IRI}>.} OFFSET ${STARTFROM} LIMIT 10"; + conf:defaultInversesCountQueries "select (count(distinct ?s) AS ?o) ?p {?s ?p <${IRI}>. FILTER(!isBlank(?s))} GROUP BY ?p LIMIT 100"; + conf:defaultInversesTest "ASK {?s ?p <${IRI}>}"; + +## define if inverse relations have to be automatically opened (open|close) + conf:defaultInverseBehaviour "close"; + +######################################################################################## +######################## configuring the webapp ######################## +######################################################################################## + +## define how clients access your app, typically you have to specify your public domain +## if you are using a proxy, set it at for testing, an installation on dbpedia +## domain should use + + conf:publicUrlPrefix ; ## or + + +## solve IRI issues in particular environments (proxypass, etc.) + + conf:forceIriEncoding "auto"; ## or "decode" or "encode" + +## home link (accessible from the banner) + + conf:homeUrl ; + + +## static resources publishing point, "staticResources/" is needed if you are going +## to serve static content within the web application, probably on a production +## environment you have to change "http://localhost:8080/lodview/" to something like +## "http://data.yourdomain.com/staticResources/", on a test environment you can set it +## at + + conf:staticResourceURL ; + +## choosing the lower class in hierarchy to put it on the top right of the page +## put in mainOntologies the prefixes you need to filter the resource's classes +## you need also to add the ontology file in WEB-INF/ontologies +## leave conf:mainOntologies blank if you don't want lodlive do this job + +## conf:mainOntologiesPrefixes YOUR-PREFIX:; + +## document specific property used in the HTML resource page, it's possible to use +## prefixes or not + + conf:typeProperties rdf:type; + + conf:titleProperties dc:title , dcterms:title, rdfs:label , , + , , + gn:officialName , gn:name , rdf:value , foaf:surname , , ; + + conf:descriptionProperties rdfs:comment , dc:description , dcterms:description , + dc:description , skos:definition , ; + + conf:imageProperties foaf:depiction , , crm-owl:P138i_has_representation , + ; + + conf:longitudeProperties geo:long; + conf:latitudeProperties geo:lat; + conf:linkingProperties owl:sameAs , skos:exactMatch , gn:locatedIn , ; + + conf:videoProperties ; + conf:audioProperties ; + +## preferred language for title and description ("auto" for delegate to the client) + + conf:preferredLanguage "auto"; ## eg. "en" or "it" + + +## interface colors, choose a pair or a list for random colors + + conf:colorPair "#06c-#0059b3" ; + +#### or bind classes and colors +## +## conf:colorPair conf:byClass; + + +## license information (will be added at the very end of the page) + + conf:license "" ; ## eg. "Licensed under Creative Commons Attribution 4.0 International (CC BY 4.0)" ; + +## just a marker to insert a point on the last row + + conf:last ".". + +#### uncomment only if you have configured "colors by class" +## +## conf:hasColorPair "#c3a116-#ac8c13". +## ontology:Place conf:hasColorPair "#715287-#624775". +## conf:hasColorPair "#914848-#7d3e3e". +## conf:hasColorPair "#6d8058-#5e6f4c". +## conf:hasColorPair "#5b8a97-#4f7783". +## conf:hasColorPair "#528775-#477565". diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index 80e6c5c..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - true - - - - - - - - %-5level %logger{0} - %msg%n - - - - - - - - - - diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties deleted file mode 100644 index de515ef..0000000 --- a/src/main/resources/messages_en.properties +++ /dev/null @@ -1,37 +0,0 @@ -label.resource = resource -label.resources = resources -label.connectedResource = connected resource -label.connectedResources = connected resources -label.toTop = to top -label.go = go -label.inverseProperty = is {0} of -label.moreExample = see more examples -label.anEntityOfType = an entity of type - -title.colorPicker = pick a color -title.blankNodes = blank nodes -title.lodCloud = data from the linked data cloud -title.inverse = inverse relations -title.lookingAt = now looking at -title.directRelations = direct relations -title.pickExample = pick an example - -error.linkedResourceUnavailable = unable to retrieve data -error.noContentNegotiation = sorry but content negotiation is not supported by the IRI -error.unacceptable = you requested an unacceptable content -error.somethingWrong = Oops! something went wrong - -message.loadingConnected = loading connected resource titles -message.loadingInverses = loading inverse relations -message.grabData = retrieving data from the LOD cloud ({0} of {1}) -message.grabDataTotal = Resource connected {0} -message.grabDataTotalLoaded = Resource loaded {0} -message.grabDataTotalErrors = Resource not online {0} -message.noImage = image not available, broken URL? - -footer.noSparql = data from: dereferenceable IRI -footer.yesSparql = data from: -footer.viewLodlive = view on LodLive -footer.viewAs = view as: -footer.download = download lodview to publish your data - diff --git a/src/main/resources/messages_fr.properties b/src/main/resources/messages_fr.properties deleted file mode 100644 index 6b65057..0000000 --- a/src/main/resources/messages_fr.properties +++ /dev/null @@ -1,50 +0,0 @@ -label.resource = ressource -label.resources = ressources -label.connectedResource = ressource connectée -label.connectedResources = ressources connectées - -label.toTop = en haut -label.go = go - -label.inverseProperty = is {0} of -label.moreExample = voir plus d'exemples - -title.colorPicker = choisir une couleur -title.blankNodes = nœuds anonymes -title.lodCloud = donnée du "linked Data Cloud" -title.inverse = relations inverses - -title.lookingAt = maintenant on observe -title.directRelations = relations directes -title.pickExample = choisir un exemple - -error.linkedResourceUnavailable = impossible de retrouver les données -error.noContentNegotiation = désolé, la négociation de contenu n'est pas supportée par cette IRI -error.unacceptable = vous avez demandé un contenu inacceptable -error.somethingWrong = Oops! y'a un truc qui va pas. - -message.loadingConnected = chargement des titres de la ressource connectée -message.loadingInverses = chargemement des relations inverses -message.grabData = chargement des données depuis le cloud LOD({0} sur {1}) -message.grabDataTotal = Ressources connectées {0} -message.grabDataTotalLoaded = Ressources chargées {0} -message.grabDataTotalErrors = Ressources hors ligne {0} -message.noImage = image non disponible. (lien cassé ?) - -footer.noSparql = donnée venant de : IRI déréférençable -footer.yesSparql = donnée venant de: -footer.viewLodlive = visualiser sur LodLive -footer.viewAs = visualiser comme: -footer.download = télécharger Lodview pour publier vos données - -home.iriInput = IRI (OBLIGATOIRE) -home.sparqlInput = SPARQL ENDPOINT (OPTIONNEL) -home.prefixInput = PREFIX (OPTIONNEL) - -home.iriInfo = mettre une IRI déréférençable ou une ressource publiée sur un point d'accès SPARQL -home.sparqlInfo = URL du point d'accès SPARQL - ex http://dbpedia.org/sparql -home.prefixInfo = Préfixe de la ressource, utile pour améliorer le rendu dans le navigateur - ex http://dbpedia.org/resource - -home.share = PARTAGER -home.download = TELECHARGER -home.examples = EXEMPLES \ No newline at end of file diff --git a/src/main/resources/messages_gl.properties b/src/main/resources/messages_gl.properties deleted file mode 100644 index 8277d71..0000000 --- a/src/main/resources/messages_gl.properties +++ /dev/null @@ -1,49 +0,0 @@ -label.resource = recurso -label.resources = recursos -label.connectedResource = recurso conectado -label.connectedResources = recursos conectados -label.toTop = inicio da páxina -label.inverseProperty = é {0} de -label.go = vai -label.moreExample = ver máis exemplos -label.anEntityOfType = unha entidade de tipo - -title.colorPicker = seleccione unha cor -title.blankNodes = nodos anónimos -title.lodCloud = datos da Linked Data Cloud -title.inverse = relacións inversas -title.lookingAt = recurso actual -title.directRelations = relacións directas -title.pickExample = seleccione un exemplo - -error.linkedResourceUnavailable = non foi posíbel obter os datos -error.noContentNegotiation = sentímolo, a IRI non permite a negociación de contidos -error.unacceptable = solicitouse un contido inaceptábel -error.somethingWrong = Oh! algo non funcionou - -message.loadingConnected = cargando os títulos dos recursos conectados -message.loadingInverses = cargando as relacións inversas -message.grabData = recuperando datos da LOD cloud ({0} de {1}) -message.grabDataTotal = recursos conectados {0} -message.grabDataTotalLoaded = recursos cargados {0} -message.grabDataTotalErrors = recursos que non están en liña {0} -message.noImage = imaxe non dispoñíbel, erro na URL? - -footer.noSparql = datos de: IRI diferenciada -footer.yesSparql = datos de: -footer.viewLodlive = ver en LodLive -footer.viewAs = ver como: -footer.download = descargue lodview para publicar os seus datos - - -home.iriInput = IRI (OBRIGATORIO) -home.sparqlInput = PUNTO DE ACCESO SPARQL (OPCIONAL) -home.prefixInput = PREFIXO (OPCIONAL) - -home.iriInfo = indique unha IRI diferenciábel ou un recurso publicado nun punto de acceso SPARQL -home.sparqlInfo = enderezo URL do punto de acceso SPARQL; ex. http://dbpedia.org/sparql -home.prefixInfo = prefixo do recurso, útil para mellorar a navegación; ex. http://dbpedia.org/resource - -home.share = COMPARTIR -home.download = DESCARGAR -home.examples = EXEMPLOS diff --git a/src/main/resources/messages_it.properties b/src/main/resources/messages_it.properties deleted file mode 100644 index f525a93..0000000 --- a/src/main/resources/messages_it.properties +++ /dev/null @@ -1,49 +0,0 @@ -label.resource = risorsa -label.resources = risorse -label.connectedResource = risorsa connessa -label.connectedResources = risorse connesse -label.toTop = inizio pagina -label.inverseProperty = è {0} di -label.go = vai -label.moreExample = altri esempi di risorse -label.anEntityOfType = entità di tipo - -title.colorPicker = scegli un colore -title.blankNodes = blank nodes -title.lodCloud = risorse dalla linked data cloud -title.inverse = relazioni inverse -title.lookingAt = risorsa corrente -title.directRelations = relazioni dirette -title.pickExample = scegli un esempio - -error.linkedResourceUnavailable = risorsa non disponibile online -error.noContentNegotiation = spiacenti ma la IRI non supporta il content negotiation -error.unacceptable = il content richiesto non è accetabile -error.somethingWrong = Oops! qualcosa non ha funzionato - -message.loadingConnected = carico i titoli delle risorse collegate -message.loadingInverses = seguo le relazioni inverse -message.grabData = recupero dati dalla LOD cloud ({0} of {1}) -message.grabDataTotal = risorse connesse {0} -message.grabDataTotalLoaded = risorse caricate {0} -message.grabDataTotalErrors = risorse non online {0} -message.noImage = immagine non disponibile, URL errata? - -footer.noSparql = dati da: IRI dereferenziata -footer.yesSparql = dati da: -footer.viewLodlive = visualizza su LodLive -footer.viewAs = visualizza come: -footer.download = scarica lodview per pubblicare i tuoi dati - - -home.iriInput = IRI (OBBLIGATORIO) -home.sparqlInput = SPARQL ENDPOINT (OPZIONALE) -home.prefixInput = PREFIX (OPZIONALE) - -home.iriInfo = IRI deferenziabile o una risorsa pubblicata in uno SPARQL endpoint -home.sparqlInfo = indirizzo dello SPARQL endpoint - es. http://dbpedia.org/sparql -home.prefixInfo = prefisso delle risorse, utile per migliorare la navigazione - es. http://dbpedia.org/resource - -home.share = CONDIVIDI -home.download = DOWNLOAD -home.examples = ESEMPI diff --git a/src/main/resources/messages_nl.properties b/src/main/resources/messages_nl.properties deleted file mode 100644 index 2491370..0000000 --- a/src/main/resources/messages_nl.properties +++ /dev/null @@ -1,36 +0,0 @@ -label.resource = resource -label.resources = resources -label.connectedResource = connected resource -label.connectedResources = connected resources -label.toTop = naar boven -label.go = start -label.inverseProperty = is {0} van -label.moreExample = zie meer voorbeelden -label.anEntityOfType = type entiteit - -title.colorPicker = kies een kleur -title.blankNodes = blank nodes -title.lodCloud = data uit de linked data cloud -title.inverse = inverse relaties -title.lookingAt = nu in beeld -title.directRelations = directe relaties -title.pickExample = kies een voorbeeld - -error.linkedResourceUnavailable = data kan niet opgehaald worden -error.noContentNegotiation = sorry, content negotiation wordt niet ondersteund door deze IRI -error.unacceptable = content is niet geaccepteerd -error.somethingWrong = Oeps, iets ging er mis! - -message.loadingConnected = verbonden resources worden opgehaald -message.loadingInverses = inverse relaties ophalen -message.grabData = ontvangen van data uit de LOD cloud ({0} van {1}) -message.grabDataTotal = Resource verbonden` {0} -message.grabDataTotalLoaded = Resource opgehaald {0} -message.grabDataTotalErrors = Resource niet online {0} -message.noImage = afbeelding niet beschikbaar, foute URL? - -footer.noSparql = data van: herleidbare IRI -footer.yesSparql = data from: -footer.viewLodlive = bekijk in LodLive -footer.viewAs = bekijk als: -footer.download = download lodview om je data te publiceren \ No newline at end of file diff --git a/src/main/resources/messages_sk.properties b/src/main/resources/messages_sk.properties deleted file mode 100644 index ceca3f1..0000000 --- a/src/main/resources/messages_sk.properties +++ /dev/null @@ -1,49 +0,0 @@ -label.resource = zdroj -label.resources = zdroje -label.connectedResource = prepojený zdroj -label.connectedResources = prepojené zdroje -label.toTop = nahor -label.inverseProperty = je {0} z -label.go = potvrdiť -label.moreExample = viď iné príklady -label.anEntityOfType = typ entity - -title.colorPicker = vyber farbu -title.blankNodes = prázdne uzly -title.lodCloud = zdroje linked dáta cloud -title.inverse = inverzná relácia -title.lookingAt = aktuálny zdroj -title.directRelations = priama relácia -title.pickExample = vyber príklad - -error.linkedResourceUnavailable = nedostupný zdroj -error.noContentNegotiation = ľutujeme, iri nepodporuje content negotiation -error.unacceptable = neprijateľný content -error.somethingWrong = Ups, niečo sa pokazilo! - -message.loadingConnected = sťahujem názvy prepojených zdrojov -message.loadingInverses = sťahujem inverzné relácie -message.grabData = vyberám dáta z LOD cloud ({0} z {1}) -message.grabDataTotal = pripojené zdroje {0} -message.grabDataTotalLoaded = stiahnuté zdroje {0} -message.grabDataTotalErrors = nedostupné zdroje {0} -message.noImage = obrázok nedostupný, chyba v URL? - -footer.noSparql = dáta od: IRI s nepriamym odkazom -footer.yesSparql = dáta od: -footer.viewLodlive = zobraz na LodLive -footer.viewAs = zobraz ako: -footer.download = stiahni lodview pre publikáciu tvojich dát - - -home.iriInput = IRI (POVINNÝ) -home.sparqlInput = SPARQL ENDPOINT (VOLITEĽNÝ) -home.prefixInput = PREFIX (VOLITEĽNÝ) - -home.iriInfo = s nepriamym odkazom alebo zdroj publikovaný v SPARQL endpoint -home.sparqlInfo = adresa SPARQL endpoint – napr. http://dbpedia.org/sparql -home.prefixInfo = predvoľba zdrojov, užitočné pre zlepšenie navigovania – napr. http://dbpedia.org/resource - -home.share = ZDIEĽAJ -home.download = DOWNLOAD -home.examples = PRÍKLADY \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/ontologies/dbpedia_2014.owl b/src/main/resources/ontologies/dbpedia_2014.owl similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/dbpedia_2014.owl rename to src/main/resources/ontologies/dbpedia_2014.owl diff --git a/src/main/webapp/WEB-INF/ontologies/dcelements.rdf b/src/main/resources/ontologies/dcelements.rdf similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/dcelements.rdf rename to src/main/resources/ontologies/dcelements.rdf diff --git a/src/main/webapp/WEB-INF/ontologies/dcterms.rdf b/src/main/resources/ontologies/dcterms.rdf similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/dcterms.rdf rename to src/main/resources/ontologies/dcterms.rdf diff --git a/src/main/webapp/WEB-INF/ontologies/dctype.rdf b/src/main/resources/ontologies/dctype.rdf similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/dctype.rdf rename to src/main/resources/ontologies/dctype.rdf diff --git a/src/main/webapp/WEB-INF/ontologies/foaf.rdf b/src/main/resources/ontologies/foaf.rdf similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/foaf.rdf rename to src/main/resources/ontologies/foaf.rdf diff --git a/src/main/webapp/WEB-INF/ontologies/owl.ttl b/src/main/resources/ontologies/owl.ttl similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/owl.ttl rename to src/main/resources/ontologies/owl.ttl diff --git a/src/main/webapp/WEB-INF/ontologies/rdf.ttl b/src/main/resources/ontologies/rdf.ttl similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/rdf.ttl rename to src/main/resources/ontologies/rdf.ttl diff --git a/src/main/webapp/WEB-INF/ontologies/rdfs.ttl b/src/main/resources/ontologies/rdfs.ttl similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/rdfs.ttl rename to src/main/resources/ontologies/rdfs.ttl diff --git a/src/main/webapp/WEB-INF/ontologies/rel.rdf b/src/main/resources/ontologies/rel.rdf similarity index 100% rename from src/main/webapp/WEB-INF/ontologies/rel.rdf rename to src/main/resources/ontologies/rel.rdf diff --git a/src/main/resources/spring/application-config.xml b/src/main/resources/spring/application-config.xml deleted file mode 100644 index 94f5c72..0000000 --- a/src/main/resources/spring/application-config.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/src/main/resources/static/staticResources/css/commons.css b/src/main/resources/static/staticResources/css/commons.css new file mode 100644 index 0000000..657c65e --- /dev/null +++ b/src/main/resources/static/staticResources/css/commons.css @@ -0,0 +1,1031 @@ +/* ********************************************** */ +/* DO NOT CHANGE THIS FILE USE custom.css INSTEAD */ +/* ********************************************** */ +.sp { + background-image: url(../img/lodview-sprite.png); + background-size: 1000px 600px; +} + +div#logoBanner div#logo { + background-size: 134px 40px; + background-image: url(../img/logo-header-lodview.png); + height: 80px; + background-position: left center; + background-repeat: no-repeat; + cursor: pointer; + width: 150px; +} + +a { + text-decoration: none; +} + +body { + font-family: 'Roboto', sans-serif; + font-weight: 300; + line-height: 20px; + font-size: 14px; + background-color: #212121; +} + +strong { + font-weight: 500; +} + +.value div.fixed, .valuecnt div.fixed, #abstract .c2 { + max-width: 760px; + overflow-x: hidden; +} + +label { + display: inline-block; +} + +label a { + font-weight: 300; + font-size: 14px; +} + +label a span { + font-weight: 500; +} + +div#logoBanner { + height: 80px; + background-color: #fff; + padding: 0 24px; +} + +hgroup { + display: block; + min-height: 210px; + color: #fff; + padding: 0 24px; +} + +hgroup h1 { + font-size: 32px; + padding-top: 120px; + line-height: 32px; + font-weight: 300; + margin-right: 100px; +} + +hgroup h1 span { + display: inline-block; + padding-right: 250px; +} + +#seeOnLodlive a { + display: inline-block; + width: 19px; + height: 27px; +} + +#seeOnLodlive { + background-position: -130px 0; + width: 19px; + height: 27px; + position: absolute; + top: 116px; + right: 24px; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; + filter: alpha(opacity=30); + opacity: 0.3; +} + +#seeOnLodlive:hover { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + opacity: 1; +} + +hgroup h2 > a.iri { + display: inline-block; + margin-bottom: 8px; + color: #fff; + margin-right: 8px; +} + +hgroup h2 > a.iri:hover { + text-decoration: underline; +} + +hgroup h2 > span.istance { + float: none; + display: inline-block; + margin-left: -8px; +} + + +hgroup h2 { + font-size: 16px; + padding: 8px 0 24px 0; + font-weight: 300; +} + +hgroup h2 > span a { + color: #fff; + font-weight: 300; + display: inline-block; + margin-left: 8px; + margin-top: 2px; + /*text-transform: uppercase;*/ + letter-spacing: .7; + font-size: 13px; +} + +hgroup h2 > span a span { + color: #fff; + font-weight: 500; +} + +hgroup h2 > span.istance a span.istanceOf { + font-size: 11px; + text-transform: uppercase; + font-weight: 300; +} + +header div#abstract { + padding: 24px; + color: #fff; +} + +header div#abstract .value a { + text-decoration: underline; + color: #fff; +} + +header div#abstract .value a:hover { + text-decoration: none +} + +header div#abstract label { + padding: 0 24px 0 0; +} + +header div#abstract label a { + color: #fff; +} + +header div#abstract label a span { + color: #fff; +} + +aside { + display: block; + background-color: #eee; + color: #fff; + padding: 24px 24px 16px 24px; +} + +#widgets > div#images > a:hover, #widgets > div#linking > a:hover { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; + filter: alpha(opacity=90); + opacity: 0.9; +} + +#widgets > div#images > a { + min-width: 136px; + height: 136px; + text-align: left; + display: inline-block; + background-image: url(../img/segnaposto-immagine.png); + background-size: 48px 48px; + background-position: center center; + background-repeat: no-repeat; + margin-right: 8px; + margin-bottom: 8px; +} + +aside { + font-size: 0px +} + +aside div.noImg { + width: 125px; + height: 125px; + display: inline-block; + background-position: 0 -105px; + background-size: 300px 250px; +} + +aside div#linking, aside div#images, aside div#audio, aside div#video { + display: inline-block; +} + +aside div#resourceMapCnt { + display: inline-block; + margin-right: 8px; +} + +aside div#images a img, aside div#linking a span, aside div#resourceMapCnt map, aside div#audio .audio { + height: 136px; + display: inline-block; + width: auto; +} + +aside div#video { + height: 144px; +} + +aside div#video * { + height: 136px; + display: inline-block; + margin-right: 8px; +} + +aside div#audio .audio { + height: 136px; + display: inline-block; + width: 136px; + margin-right: 8px; + margin-bottom: 8px; + background-position: -520px -65px; +} + +aside div#linking { + margin-bottom: 8px; +} + +aside div#linking a span { + width: 136px; + background-position: -280px -65px; +} + +aside div#resourceMapCnt map { + width: 272px; + background-position: 0 -65px; +} + +.leaflet-popup-content { + color: #000 +} + +.c1, .c3 { + position: absolute +} + +h3 { + text-transform: uppercase; + letter-spacing: .7; + font-weight: 300; + color: #212121; + font-size: 11px; +} + +div#directs { + padding: 24px; + background: #fff; + color: #212121; + line-height: 20px; + font-size: 14px; + overflow-x: hidden; +} + +div#bnodes { + padding: 24px; + background: #eee; + color: #212121; + line-height: 20px; + font-size: 14px; +} + +div#inverses { + padding: 24px; + background: #d4d4d4; + color: #212121; + line-height: 20px; + font-size: 14px; +} + +.lloading { + background-image: url(../img/loading-new@2x.gif); + background-size: 22px 7px; + width: 22px; + height: 7px; + display: inline-block; + margin-right: 8px; +} + +.lloadingb { + background-image: url(../img/loading-new-nero@2x.gif); + background-size: 22px 7px; + width: 22px; + height: 7px; + display: inline-block; + margin-left: 16px; +} + +div#inverses .nextArrow { + display: inline-block; + width: 12px; + height: 14px; + margin-left: 8px; + background-position: -60px 3px; +} + +div#inverses .prevArrow { + display: inline-block; + width: 12px; + height: 14px; + margin-left: 16px; + background-position: -45px 3px; +} + +div#lodCloud { + padding: 24px 24px 16px 24px; + background: #fafafa; + color: #212121; + line-height: 16px; + font-size: 12px; + min-height: 64px; +} + +div#lodCloud map { + background-position: -260px -210px; + display: inline-block; + width: 250px; + height: 250px; +} + +div#lodCloud .connected div#counterBlock.content { + color: #fff; +} + +div.toOneLine { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.value div, .valuecnt div { + min-height: 20px +} + +.value div span.derivedTitle, .valuecnt div span.derivedTitle { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + background-repeat: no-repeat; +} + +.value div span.derivedTitle tt, .valuecnt div span.derivedTitle tt { + margin: 5px 0 0 2px; + background-position: -45px -20px; + width: 11px; + display: inline-block; + height: 10px; +} + +div#directs .value a span.derivedTitle, div#bnodes .value a span.derivedTitle, div#inverses .value a span.derivedTitle { + font-weight: 300; +} + +.value div a:hover span.derivedTitle { + text-decoration: none; +} + +div#directs .value > div:not(.value), div#bnodes .valuecnt > div:not(.value), div#inverses .value > div:not(.value) { + padding-bottom: 12px; + margin-top: 12px; + background-image: url(../img/separatoreCorto.png); + background-repeat: no-repeat; + background-size: 16px 1px; + background-position: bottom left; +} + +div#directs .c2.multiInLineBlock, div#directs .c4.multiInLineBlock { + padding-bottom: 8.5px; + padding-top: 8.5px; +} + +.multiInLine { + display: inline-block; + margin-right: 20px; + line-height: 27px; +} + +div#directs .value > div:not(.value):first-child, div#bnodes .valuecnt > div:not(.value):first-child, div#inverses .value > div:not(.value):first-child, div#inverses .value > div:not(.value):nth-of-type(n+2):not(.toOneLine):not(.toMultiLine), div#bnodes .valuecnt > div:not(.value):nth-of-type(n+2):not(.toOneLine):not(.toMultiLine) { + margin-top: 0; +} + +div#directs .value > div:not(.value):last-child, div#bnodes .valuecnt > div:not(.value):last-child, div#inverses .value > div:not(.value):last-child { + padding: 0; + background: none; +} + +div#directs label, div#bnodes label, div#inverses label { + padding: 0 24px 0 0; +} + +div#directs label a, div#bnodes label a, div#inverses label a { + color: #212121; +} + +div#directs label a span, div#bnodes label a span, div#inverses label a span { + color: #212121; +} + +div#directs .dType, div#bnodes .dType { + font-size: 14px; + font-weight: 300; + color: #9e9e9e; + position: absolute; + right: 24px; + text-align: right; + padding-left: 16px; + background-color: #fff; +} + +div#bnodes .dType { + background-color: #eee; +} + +div#directs span.clang, div#abstract span.clang, div#bnodes span.clang, div#bnodes span.clang { + display: inline-block; + text-transform: uppercase; + letter-spacing: .7; + margin-right: 5px; + cursor: pointer; + margin-bottom: 12px; +} + +div#directs span.clang.sel, div#abstract span.clang.sel, div#bnodes span.clang.sel { + text-decoration: underline; +} + +div#directs .value a, div#directs .valuecnt a, div#bnodes .value a, div#bnodes .valuecnt a, div#inverses .value a, div#inverses .valuecnt a { + font-weight: 300; + font-size: 14px; + color: #212121; +} + +div#directs .value a span, div#bnodes .value a span, div#inverses .value a span { + font-weight: 500; + color: #212121; +} + +div#directs .value a:hover, div#bnodes .value a:hover, div#inverses .value a:hover, div#directs .valuecnt a:hover, div#bnodes .valuecnt a:hover, div#inverses .valuecnt a:hover { + text-decoration: underline; +} + +div#directs .c2, div#directs .c4 { + border-bottom: 1px solid #dbdbdb; + padding-bottom: 12px; + padding-top: 12px; +} + +div#bnodes .c2, div#bnodes .c4 { + border-bottom: 1px solid #d2d2d2; + padding-bottom: 12px; + padding-top: 12px; +} + +div#inverses .c2, div#inverses .c4 { + padding-bottom: 12px; + padding-top: 12px; + border-bottom: 1px solid #b6b6b6; +} + +div#inverses .c2 .toOneLine { + min-height: 40px; +} + +div#inverses .c2.opened { + min-height: 648px; +} + +div#bnodes .c2:last-child, div#directs .c2:last-child, div#inverses .c2:last-child, div#bnodes .c4:last-child, div#directs .c4:last-child, div#inverses .c4:last-child { + border-bottom: 0; + padding-bottom: 0; + padding-top: 12px; +} + +div#directs label:nth-child(1), div#bnodes label:nth-child(1), div#inverses label:nth-child(1) { + padding-top: 0; +} + +div#directs label, div#bnodes label, div#inverses label { + padding-top: 12px; +} + +div#directs .c2:nth-child(2), div#bnodes .c2:nth-child(2), div#inverses .c2:nth-child(2), div#directs .c4:nth-child(2), div#bnodes .c4:nth-child(2), div#inverses .c4:nth-child(2) { + padding-top: 0; +} + +div#lodCloud h3 { + margin-bottom: 24px; +} + +div#lodCloud .lloading { + position: absolute; + right: 22px; + top: 28px; +} + +div#lodCloud .connected { + width: 250px; + display: inline-block; + margin-right: 16px; + vertical-align: top; + margin-bottom: 16px; +} + +div#lodCloud .connected .content { + padding: 24px; + background: #e9e9e9; +} + +div#lodCloud .connected .content :last-child { + margin-bottom: 0; +} + +div#lodCloud .connected h5 { + font-weight: 500; +} + +div#lodCloud .connected .more { + margin-top: 1px; + font-weight: 300; + letter-spacing: .7; + font-size: 11px; + text-transform: uppercase; + padding: 24px; + background: #e9e9e9; +} + +div#lodCloud .connected a.link { + color: #212121; + display: block; + margin-top: 8px; + margin-bottom: 24px; +} + +div#lodCloud .connected a:hover { + text-decoration: underline; +} + +div#lodCloud .connected span.imgCnt { + background-position: 0 -210px; + width: 250px; + display: block; +} + +div#lodCloud .connected img.main { + width: 250px; + height: auto; + background: #ffffff; + +} + +.px1 { + width: 1px; + font-size: 1px; + line-height: 1px; +} + +aside.empty, #bnodes.empty, #directs.empty, #inverses.empty, #abstract.empty { + padding: 0; + min-height: 10px; +} + +#lodCloud.empty { + padding: 0; + min-height: 5px; +} + +#inverses.empty { + padding: 0; + min-height: 20px; +} + +footer { + min-height: 176px; + color: #fff; + padding: 24px; +} + +#download { + float: left; + margin-bottom: 30px; +} + +#download a#linkGit { + margin-top: 160px; + display: block; + font-size: 11px; + text-transform: uppercase; + letter-spacing: .7; + color: #b4b4b4; + text-decoration: none; +} + +#download a:hover { + text-decoration: underline +} + +#endpoint { + position: relative; + top: -7px; + font-size: 12px; + line-height: 24px; +} + +#endpoint { + float: right; + color: #fff; +} + +#endpoint ul li { + min-height: 24px; +} + +#endpoint ul { + list-style: none; +} + +#endpoint a { + color: #fff; + cursor: pointer; +} + +#endpoint a:hover { + text-decoration: underline +} + +div#directs .value > div.lang, div#bnodes .value > div.lang, div#inverses .value > div.lang { + background: none; + padding-bottom: 0; + margin-top: 0 +} + +div#loadPanel { + position: fixed; + width: 100%; + bottom: 0; + left: 0; + line-height: 30px; + height: 30px; + padding-left: 24px; + overflow: hidden; + z-index: 1004; +} + +div#loadPanel.cfix { + padding-left: 100%; + margin-left: -300px +} + +div#loadPanel span.ok { + width: 17px; + height: 14px; + display: inline-block; + margin-left: 6px; +} + +div#loadPanel span.ok img { + display: none +} + +div#loadPanel > p { + font-family: 'Roboto', sans-serif; + font-weight: 300; + line-height: 30px; + font-size: 12px; + color: #fff; +} + +#images .errorImg { + width: 136px; + height: 136px; + margin-right: 8px; +} + +.connected .errorImg { + width: 250px; + height: 250px; +} + +#errorPage hgroup h1 { + font-size: 150px; + font-weight: 100; + padding-left: 66px; + padding-top: 80px; +} + +#errorPage.error404 div#owl { + background-image: url(../img/owl404.png); + background-size: 224px 283px; + width: 224px; + height: 283px; + position: absolute; + left: 660px; + top: 105px +} + +#errorPage.error500 div#owl { + background-image: url(../img/owl500.png); + background-size: 224px 283px; + position: absolute; + width: 224px; + height: 283px; + left: 660px; + top: 105px +} + +#errorPage div#abstract { + height: 65px; + padding: 0; +} + +#errorPage div#bnodes { + font-size: 20px; + min-height: 186px; + padding: 0 24px 24px 90px; +} + +#errorPage div#bnodes p { + font-size: 30px; + line-height: 40px; + padding-top: 60px; +} + +footer { + +} + +footer #linkBack { + display: block; + background-size: 134px 25px; + background-image: url(../img/logo-footer-lodview.png); + background-position: 0 0; + height: 25px; + width: 134px; + background-repeat: no-repeat; +} + +footer #linkBack:hover { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=6); + opacity: 0.6; +} + +#navigator { + position: fixed; + right: 0; + top: 50%; + margin-top: -32px; + z-index: 5; + width: 40px; + height: 78px; +} + +#navigator div { + width: 40px; + height: 20px; + cursor: pointer; + margin-bottom: 1px; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=10); + opacity: 0.1; +} + +#navigator div span { + display: none; +} + +#navigator div.hover:hover { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=10); + opacity: 1; + position: static; +} + +#navigator div.hover:hover span { + display: inline-block; + position: fixed; + right: 41px; + background-color: #222; + padding: 0 8px; + height: 20px; + color: #fff; + line-height: 20px; + font-size: 12px; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=10); + opacity: 1; +} + +#navigator div a { + display: block; + width: 40px; + height: 20px; +} + +#navigator div.down { + background-position: 0 -40px; +} + +#navigator div.up { + background-position: 0 0; +} + +#navigator div.top { + background-position: 0 -20px; +} + +@media screen and (max-width: 768px) { + hgroup h1 span { + padding-right: 150px; + } +} + +.iph { + width: 22px; + height: 20px; + position: absolute; + left: -20px; +} + +.i { + background-position: -105px -35px; + width: 22px; + height: 20px; + position: absolute; + left: -20px; + z-index: 9; + display: none; + cursor: help; +} + +.i span { + display: inline-block; + background-position: -105px -35px; + width: 11px; + height: 11px; + position: relative; + left: 4px; + top: 5px; + z-index: 9; +} + +.tooltip { + display: none; + background-color: #212121; + font-family: 'Roboto', sans-serif; + font-weight: 300; + line-height: 15px; + font-size: 12px; + color: #fff; + padding: 10px; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + position: absolute; + left: 8px; + margin-top: 22px; + width: 250px; + z-index: 9; +} + +#license { + padding: 12px 0; + line-height: 20px; + font-family: 'Roboto', sans-serif; + font-weight: 300; + font-size: 12px; + float: left; + width: 100%; + background: #fff; +} + +#license div { + padding: 0 24px; +} + +#license div a { + font-weight: 500; + color: #222222 +} + +#license div a:hover { + text-decoration: underline; +} + +/* ********************************** */ +/* CUSTOM FOOTER, HIDDEN BY DEFAULT */ +/* ********************************** */ +#customFooter { + display: none; +} + +/* useful to add information about the project partners (logo) */ +#customFooter #credits { + height: 86px; + background: #e9e9e9; +} + +#customFooter #credits div { + padding: 24px 0 0 24px; +} + +/* useful to add a custom navigation menu */ +#customFooter menu { + height: 43px; + background: #d4d4d4; +} + +#customFooter menu li { + margin: 0 -8px 0 0; + padding: 0 0 0 24px; + font-family: 'Roboto', sans-serif; + font-weight: 300; + font-size: 12px; + display: inline-block; + line-height: 43px; +} + +#customFooter menu li a { + color: #000; +} + +#customFooter menu li a:hover { + text-decoration: underline; +} + +/** image zoomer **/ +#hover { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + background: #fff; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; + filter: alpha(opacity=90); + display: none; + z-index: 1006; +} + +div.hover { + position: fixed; + z-index: 1007; +} + +div.closemapzoom { + width: 14px; + height: 14px; + display: inline-block; + background-position: -120px -35px; + position: fixed; + right: 19px; + top: 19px; + cursor: pointer; + +} + +img.hover { + position: fixed; + z-index: 1007; + top: 100%; + left: 100%; + display: none; +} + +.imgTools { + position: absolute; + overflow: hidden; + z-index: 999; + display: none; +} + +.imgTools .zoom { + width: 29px; + height: 29px; + background-position: -430px -65px; + display: block; + cursor: pointer; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); + opacity: 0.6; +} + +.imgTools .open:hover, .imgTools .zoom:hover { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + opacity: 1; +} + +.imgTools .open { + margin-bottom: 5px; + cursor: pointer; + width: 14px; + height: 14px; + background-position: -430px -94px; + display: block; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); + opacity: 0.6; +} diff --git a/src/main/resources/static/staticResources/css/commons_2x.css b/src/main/resources/static/staticResources/css/commons_2x.css new file mode 100644 index 0000000..7b640f5 --- /dev/null +++ b/src/main/resources/static/staticResources/css/commons_2x.css @@ -0,0 +1,36 @@ +/* **************************************** */ +/* SPECIAL DIRECTIVES FOR PIXEL RATIO > 1 */ +/* **************************************** */ +@media ( -webkit-min-device-pixel-ratio: 2) , ( min-resolution: 192dpi) { + .sp { + background-image: url(../img/lodview-sprite@2x.png); + } + + div#logoBanner div#logo { + background-image: url(../img/logo-header-lodview@2x.png); + } + + footer #linkBack { + background-image: url(../img/logo-footer-lodview@2x.png); + } + + div#directs .value > div:not (.value ), div#bnodes .valuecnt > div:not (.value ), div#inverses .value > div:not (.value ) { + background-image: url("img/separatoreCorto@2x.png"); + } + + div#directs .value > div.lang, div#bnodes .value > div.lang, div#inverses .value > div.lang { + background: none + } + + #errorPage.error404 div#owl { + background-image: url(../img/owl404@2x.png); + } + + #errorPage.error500 div#owl { + background-image: url(../img/owl500@2x.png); + } + + #widgets > div#images > a { + background-image: url(../img/segnaposto-immagine@2x.png); + } +} diff --git a/src/main/resources/static/staticResources/css/custom.css b/src/main/resources/static/staticResources/css/custom.css new file mode 100644 index 0000000..1b7bede --- /dev/null +++ b/src/main/resources/static/staticResources/css/custom.css @@ -0,0 +1,34 @@ +/* ***************** */ +/* CUSTOM CSS HERE */ +/* ***************** */ +div#logoBanner div#logo { + background-size: 134px 40px; + background-image: url(../img/logo-header-lodview.png) !important; + height: 80px; + background-position: left center; + background-repeat: no-repeat; + cursor: pointer; + width: 150px; +} + +@media ( -webkit-min-device-pixel-ratio: 2) , ( min-resolution: 192dpi) { + div#logoBanner div#logo { + background-image: url(../img/logo-header-lodview@2x.png) !important; + } +} + +body { + background-color: #00264d !important; +} + +div#inverses { + background: #cce6ff !important; +} + + +/* ********************************** */ +/* CUSTOM FOOTER, HIDDEN BY DEFAULT */ +/* ********************************** */ +#customFooter { + display: none; +} diff --git a/src/main/resources/static/staticResources/css/reset.css b/src/main/resources/static/staticResources/css/reset.css new file mode 100644 index 0000000..21813d0 --- /dev/null +++ b/src/main/resources/static/staticResources/css/reset.css @@ -0,0 +1,78 @@ +/* html5doctor.com Reset v1.6.1 (http://html5doctor.com/html-5-reset-stylesheet/) - http://cssreset.com */ +html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent +} + +body { + line-height: 1 +} + +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { + display: block +} + +nav ul { + list-style: none +} + +blockquote, q { + quotes: none +} + +blockquote:before, blockquote:after, q:before, q:after { + content: none +} + +a { + margin: 0; + padding: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent +} + +ins { + background-color: #ff9; + color: #000; + text-decoration: none +} + +mark { + background-color: #ff9; + color: #000; + font-style: italic; + font-weight: bold +} + +del { + text-decoration: line-through +} + +abbr[title], dfn[title] { + border-bottom: 1px dotted; + cursor: help +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0 +} + +input, select { + vertical-align: middle +} diff --git a/src/main/webapp/resources/img/favicon.png b/src/main/resources/static/staticResources/img/favicon.png similarity index 100% rename from src/main/webapp/resources/img/favicon.png rename to src/main/resources/static/staticResources/img/favicon.png diff --git a/src/main/webapp/resources/img/loading-new-nero@2x.gif b/src/main/resources/static/staticResources/img/loading-new-nero@2x.gif similarity index 100% rename from src/main/webapp/resources/img/loading-new-nero@2x.gif rename to src/main/resources/static/staticResources/img/loading-new-nero@2x.gif diff --git a/src/main/webapp/resources/img/loading-new@2x.gif b/src/main/resources/static/staticResources/img/loading-new@2x.gif similarity index 100% rename from src/main/webapp/resources/img/loading-new@2x.gif rename to src/main/resources/static/staticResources/img/loading-new@2x.gif diff --git a/src/main/webapp/resources/img/loading-newB-nero@2x.gif b/src/main/resources/static/staticResources/img/loading-newB-nero@2x.gif similarity index 100% rename from src/main/webapp/resources/img/loading-newB-nero@2x.gif rename to src/main/resources/static/staticResources/img/loading-newB-nero@2x.gif diff --git a/src/main/webapp/resources/img/lodview-sprite.png b/src/main/resources/static/staticResources/img/lodview-sprite.png similarity index 100% rename from src/main/webapp/resources/img/lodview-sprite.png rename to src/main/resources/static/staticResources/img/lodview-sprite.png diff --git a/src/main/webapp/resources/img/lodview-sprite@2x.png b/src/main/resources/static/staticResources/img/lodview-sprite@2x.png similarity index 100% rename from src/main/webapp/resources/img/lodview-sprite@2x.png rename to src/main/resources/static/staticResources/img/lodview-sprite@2x.png diff --git a/src/main/webapp/resources/img/lodview_sharer.png b/src/main/resources/static/staticResources/img/lodview_sharer.png similarity index 100% rename from src/main/webapp/resources/img/lodview_sharer.png rename to src/main/resources/static/staticResources/img/lodview_sharer.png diff --git a/src/main/webapp/resources/img/logo-footer-lodview.png b/src/main/resources/static/staticResources/img/logo-footer-lodview.png similarity index 100% rename from src/main/webapp/resources/img/logo-footer-lodview.png rename to src/main/resources/static/staticResources/img/logo-footer-lodview.png diff --git a/src/main/webapp/resources/img/logo-footer-lodview@2x.png b/src/main/resources/static/staticResources/img/logo-footer-lodview@2x.png similarity index 100% rename from src/main/webapp/resources/img/logo-footer-lodview@2x.png rename to src/main/resources/static/staticResources/img/logo-footer-lodview@2x.png diff --git a/src/main/webapp/resources/img/logo-header-lodview.png b/src/main/resources/static/staticResources/img/logo-header-lodview.png similarity index 100% rename from src/main/webapp/resources/img/logo-header-lodview.png rename to src/main/resources/static/staticResources/img/logo-header-lodview.png diff --git a/src/main/webapp/resources/img/logo-header-lodview@2x.png b/src/main/resources/static/staticResources/img/logo-header-lodview@2x.png similarity index 100% rename from src/main/webapp/resources/img/logo-header-lodview@2x.png rename to src/main/resources/static/staticResources/img/logo-header-lodview@2x.png diff --git a/src/main/webapp/resources/img/no_image.png b/src/main/resources/static/staticResources/img/no_image.png similarity index 100% rename from src/main/webapp/resources/img/no_image.png rename to src/main/resources/static/staticResources/img/no_image.png diff --git a/src/main/webapp/resources/img/no_image@2x.png b/src/main/resources/static/staticResources/img/no_image@2x.png similarity index 100% rename from src/main/webapp/resources/img/no_image@2x.png rename to src/main/resources/static/staticResources/img/no_image@2x.png diff --git a/src/main/webapp/resources/img/owl404.png b/src/main/resources/static/staticResources/img/owl404.png similarity index 100% rename from src/main/webapp/resources/img/owl404.png rename to src/main/resources/static/staticResources/img/owl404.png diff --git a/src/main/webapp/resources/img/owl404@2x.png b/src/main/resources/static/staticResources/img/owl404@2x.png similarity index 100% rename from src/main/webapp/resources/img/owl404@2x.png rename to src/main/resources/static/staticResources/img/owl404@2x.png diff --git a/src/main/webapp/resources/img/owl500.png b/src/main/resources/static/staticResources/img/owl500.png similarity index 100% rename from src/main/webapp/resources/img/owl500.png rename to src/main/resources/static/staticResources/img/owl500.png diff --git a/src/main/webapp/resources/img/owl500@2x.png b/src/main/resources/static/staticResources/img/owl500@2x.png similarity index 100% rename from src/main/webapp/resources/img/owl500@2x.png rename to src/main/resources/static/staticResources/img/owl500@2x.png diff --git a/src/main/webapp/resources/img/segnaposto-immagine.png b/src/main/resources/static/staticResources/img/segnaposto-immagine.png similarity index 100% rename from src/main/webapp/resources/img/segnaposto-immagine.png rename to src/main/resources/static/staticResources/img/segnaposto-immagine.png diff --git a/src/main/webapp/resources/img/segnaposto-immagine@2x.png b/src/main/resources/static/staticResources/img/segnaposto-immagine@2x.png similarity index 100% rename from src/main/webapp/resources/img/segnaposto-immagine@2x.png rename to src/main/resources/static/staticResources/img/segnaposto-immagine@2x.png diff --git a/src/main/webapp/resources/img/separatoreCorto.png b/src/main/resources/static/staticResources/img/separatoreCorto.png similarity index 100% rename from src/main/webapp/resources/img/separatoreCorto.png rename to src/main/resources/static/staticResources/img/separatoreCorto.png diff --git a/src/main/webapp/resources/img/separatoreCorto@2x.png b/src/main/resources/static/staticResources/img/separatoreCorto@2x.png similarity index 100% rename from src/main/webapp/resources/img/separatoreCorto@2x.png rename to src/main/resources/static/staticResources/img/separatoreCorto@2x.png diff --git a/src/main/resources/static/staticResources/style.css b/src/main/resources/static/staticResources/style.css new file mode 100644 index 0000000..6044476 --- /dev/null +++ b/src/main/resources/static/staticResources/style.css @@ -0,0 +1,7 @@ +@import url('css/reset.css'); +/* first of all, reset! */ +@import url('css/commons.css'); +/* do not touch this */ +@import url('css/commons_2x.css'); +/* pixel ratio > 1 */ +@import url('css/custom.css'); /* your style */ diff --git a/src/main/webapp/resources/vendor/jplayercircle/circle.skin/buffer.png b/src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/buffer.png similarity index 100% rename from src/main/webapp/resources/vendor/jplayercircle/circle.skin/buffer.png rename to src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/buffer.png diff --git a/src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/circle.player.css b/src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/circle.player.css new file mode 100644 index 0000000..4437fb2 --- /dev/null +++ b/src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/circle.player.css @@ -0,0 +1,144 @@ +/* + * Project: CirclePlayer + * http://www.jplayer.org + * + * Copyright (c) 2011 Happyworm Ltd + * + * Author: Silvia Benvenuti + * Edited by: Mark J Panaghiston + * Date: 6th May 2011 + * Artwork inspired by: http://forrst.com/posts/Untitled-CJz + */ +.cp-jplayer { + display: inline-block +} + +.cp-container { + position: relative; + display: inline-block; + width: 70px; /* 200 - (2 * 48) */ + height: 70px; + background-size: 70px 70px; + padding: 33px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + margin-right: 8px; +} + +.cp-container :focus { + border: none; + outline: 0; +} + +.cp-buffer-1, +.cp-buffer-2, +.cp-progress-1, +.cp-progress-2 { + position: absolute; + top: 0; + left: 0; + width: 70px; + height: 70px; + clip: rect(0px, 35px, 70px, 0px); + + -moz-border-radius: 35px; + -webkit-border-radius: 35px; + border-radius: 35px; +} + +.cp-buffer-1, +.cp-buffer-2 { + /*background: url("buffer.png") 0 0 no-repeat; + background-size:70px 70px;*/ +} + + +/* FALLBACK for .progress + * (24 steps starting from 1hr filled progress, Decrease second value by 104px for next step) + * (It needs the container selector to work. Or use div) + */ + +.cp-container .cp-fallback { + background: url("progress_sprite.png") no-repeat; + background-position: 0 70px; + background-size: 70px 1680px; +} + +.cp-progress-1, +.cp-progress-2 { + background: url("progress.png") 0 0 no-repeat; + background-size: 70px 70px; +} + +.cp-buffer-holder, +.cp-progress-holder, +.cp-circle-control { + position: absolute; + width: 70px; + height: 70px; +} + +.cp-circle-control { + cursor: pointer; +} + +.cp-buffer-holder, +.cp-progress-holder { + clip: rect(0px, 70px, 70px, 35px); + display: none; +} + + +/* This is needed when progress is greater than 50% or for fallback */ + +.cp-buffer-holder.cp-gt50, +.cp-progress-holder.cp-gt50, +.cp-progress.cp-fallback { + clip: rect(auto, auto, auto, auto); +} + +.cp-controls { + margin: 0; + padding: 15px; +} + +.cp-controls li { + list-style-type: none; + display: block; + + /*IE Fix*/ + position: absolute; +} + +.cp-controls li a { + position: relative; + display: block; + width: 40px; + height: 40px; + text-indent: -9999px; + z-index: 1; +} + +.cp-controls .cp-play { + background: url("controls.png") 0 0 no-repeat; + background-size: 80px 80px; +} + +.cp-controls .cp-play:hover { + background: url("controls.png") -40px 0 no-repeat; + background-size: 80px 80px; +} + +.cp-controls .cp-pause { + background: url("controls.png") 0 -40px no-repeat; + background-size: 80px 80px; +} + +.cp-controls .cp-pause:hover { + background: url("controls.png") -40px -40px no-repeat; + background-size: 80px 80px; +} + +.cp-jplayer { + width: 0; + height: 0; +} diff --git a/src/main/webapp/resources/vendor/jplayercircle/circle.skin/controls.png b/src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/controls.png similarity index 100% rename from src/main/webapp/resources/vendor/jplayercircle/circle.skin/controls.png rename to src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/controls.png diff --git a/src/main/webapp/resources/vendor/jplayercircle/circle.skin/progress.png b/src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/progress.png similarity index 100% rename from src/main/webapp/resources/vendor/jplayercircle/circle.skin/progress.png rename to src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/progress.png diff --git a/src/main/webapp/resources/vendor/jplayercircle/circle.skin/progress_sprite.png b/src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/progress_sprite.png similarity index 100% rename from src/main/webapp/resources/vendor/jplayercircle/circle.skin/progress_sprite.png rename to src/main/resources/static/staticResources/vendor/jplayercircle/circle.skin/progress_sprite.png diff --git a/src/main/webapp/resources/vendor/jplayercircle/js/Jplayer.swf b/src/main/resources/static/staticResources/vendor/jplayercircle/js/Jplayer.swf similarity index 100% rename from src/main/webapp/resources/vendor/jplayercircle/js/Jplayer.swf rename to src/main/resources/static/staticResources/vendor/jplayercircle/js/Jplayer.swf diff --git a/src/main/resources/static/staticResources/vendor/jplayercircle/js/circle.player.js b/src/main/resources/static/staticResources/vendor/jplayercircle/js/circle.player.js new file mode 100644 index 0000000..acd90b0 --- /dev/null +++ b/src/main/resources/static/staticResources/vendor/jplayercircle/js/circle.player.js @@ -0,0 +1,241 @@ +/* + * CirclePlayer for the jPlayer Plugin (jQuery) + * http://www.jplayer.org + * + * Copyright (c) 2009 - 2011 Happyworm Ltd + * Dual licensed under the MIT and GPL licenses. + * - http://www.opensource.org/licenses/mit-license.php + * - http://www.gnu.org/copyleft/gpl.html + * + * Version: 1.0.1 (jPlayer 2.0.9) + * Date: 30th May 2011 + * + * Author: Mark J Panaghiston @thepag + * + * CirclePlayer prototype developed by: + * Mark Boas @maboa + * Silvia Benvenuti @aulentina + * Jussi Kalliokoski @quinirill + * + * Inspired by : + * Neway @imneway http://imneway.net/ http://forrst.com/posts/Untitled-CPt + * and + * Liam McKay @liammckay http://dribbble.com/shots/50882-Purple-Play-Pause + * + * Standing on the shoulders of : + * John Resig @jresig + * Mark Panaghiston @thepag + * Louis-Rémi Babé @Louis_Remi + */ + + +var CirclePlayer = function (jPlayerSelector, media, options) { + var self = this, + + defaults = { + // solution: "flash, html", // For testing Flash with CSS3 + supplied: "m4a, oga", + // Android 2.3 corrupts media element if preload:"none" is used. + // preload: "none", // No point preloading metadata since no times are displayed. It helps keep the buffer state correct too. + cssSelectorAncestor: "#cp_container_1", + cssSelector: { + play: ".cp-play", + pause: ".cp-pause" + } + }, + + cssSelector = { + bufferHolder: ".cp-buffer-holder", + buffer1: ".cp-buffer-1", + buffer2: ".cp-buffer-2", + progressHolder: ".cp-progress-holder", + progress1: ".cp-progress-1", + progress2: ".cp-progress-2", + circleControl: ".cp-circle-control" + }; + + this.cssClass = { + gt50: "cp-gt50", + fallback: "cp-fallback" + }; + + this.spritePitch = 104; + this.spriteRatio = 0.24; // Number of steps / 100 + + this.player = $(jPlayerSelector); + this.media = $.extend({}, media); + this.options = $.extend(true, {}, defaults, options); // Deep copy + + this.cssTransforms = Modernizr.csstransforms; + this.audio = {}; + this.dragging = false; // Indicates if the progressbar is being 'dragged'. + + this.eventNamespace = ".CirclePlayer"; // So the events can easily be removed in destroy. + + this.jq = {}; + $.each(cssSelector, function (entity, cssSel) { + self.jq[entity] = $(self.options.cssSelectorAncestor + " " + cssSel); + }); + + this._initSolution(); + this._initPlayer(); +}; + +CirclePlayer.prototype = { + _createHtml: function () { + }, + _initPlayer: function () { + var self = this; + this.player.jPlayer(this.options); + + this.player.bind($.jPlayer.event.ready + this.eventNamespace, function (event) { + if (event.jPlayer.html.used && event.jPlayer.html.audio.available) { + self.audio = $(this).data("jPlayer").htmlElement.audio; + } + $(this).jPlayer("setMedia", self.media); + self._initCircleControl(); + }); + + this.player.bind($.jPlayer.event.play + this.eventNamespace, function (event) { + $(this).jPlayer("pauseOthers"); + }); + + // This event fired as play time increments + this.player.bind($.jPlayer.event.timeupdate + this.eventNamespace, function (event) { + if (!self.dragging) { + self._timeupdate(event.jPlayer.status.currentPercentAbsolute); + } + }); + + // This event fired as buffered time increments + this.player.bind($.jPlayer.event.progress + this.eventNamespace, function (event) { + var percent = 0; + if ((typeof self.audio.buffered === "object") && (self.audio.buffered.length > 0)) { + if (self.audio.duration > 0) { + var bufferTime = 0; + for (var i = 0; i < self.audio.buffered.length; i++) { + bufferTime += self.audio.buffered.end(i) - self.audio.buffered.start(i); + // console.log(i + " | start = " + self.audio.buffered.start(i) + " | end = " + self.audio.buffered.end(i) + " | bufferTime = " + bufferTime + " | duration = " + self.audio.duration); + } + percent = 100 * bufferTime / self.audio.duration; + } // else the Metadata has not been read yet. + // console.log("percent = " + percent); + } else { // Fallback if buffered not supported + // percent = event.jPlayer.status.seekPercent; + percent = 0; // Cleans up the inital conditions on all browsers, since seekPercent defaults to 100 when object is undefined. + } + self._progress(percent); // Problem here at initial condition. Due to the Opera clause above of buffered.length > 0 above... Removing it means Opera's white buffer ring never shows like with polyfill. + // Firefox 4 does not always give the final progress event when buffered = 100% + }); + + this.player.bind($.jPlayer.event.ended + this.eventNamespace, function (event) { + self._resetSolution(); + }); + }, + _initSolution: function () { + if (this.cssTransforms) { + this.jq.progressHolder.show(); + this.jq.bufferHolder.show(); + } else { + this.jq.progressHolder.addClass(this.cssClass.gt50).show(); + this.jq.progress1.addClass(this.cssClass.fallback); + this.jq.progress2.hide(); + this.jq.bufferHolder.hide(); + } + this._resetSolution(); + }, + _resetSolution: function () { + if (this.cssTransforms) { + this.jq.progressHolder.removeClass(this.cssClass.gt50); + this.jq.progress1.css({'transform': 'rotate(0deg)'}); + this.jq.progress2.css({'transform': 'rotate(0deg)'}).hide(); + } else { + this.jq.progress1.css('background-position', '0 ' + this.spritePitch + 'px'); + } + }, + _initCircleControl: function () { + var self = this; + this.jq.circleControl.grab({ + onstart: function () { + self.dragging = true; + }, onmove: function (event) { + var pc = self._getArcPercent(event.position.x, event.position.y); + self.player.jPlayer("playHead", pc).jPlayer("play"); + self._timeupdate(pc); + }, onfinish: function (event) { + self.dragging = false; + var pc = self._getArcPercent(event.position.x, event.position.y); + self.player.jPlayer("playHead", pc).jPlayer("play"); + } + }); + }, + _timeupdate: function (percent) { + var degs = percent * 3.6 + "deg"; + + var spriteOffset = (Math.floor((Math.round(percent)) * this.spriteRatio) - 1) * -this.spritePitch; + + if (percent <= 50) { + if (this.cssTransforms) { + this.jq.progressHolder.removeClass(this.cssClass.gt50); + this.jq.progress1.css({'transform': 'rotate(' + degs + ')'}); + this.jq.progress2.hide(); + } else { // fall back + this.jq.progress1.css('background-position', '0 ' + spriteOffset + 'px'); + } + } else if (percent <= 100) { + if (this.cssTransforms) { + this.jq.progressHolder.addClass(this.cssClass.gt50); + this.jq.progress1.css({'transform': 'rotate(180deg)'}); + this.jq.progress2.css({'transform': 'rotate(' + degs + ')'}); + this.jq.progress2.show(); + } else { // fall back + this.jq.progress1.css('background-position', '0 ' + spriteOffset + 'px'); + } + } + }, + _progress: function (percent) { + var degs = percent * 3.6 + "deg"; + + if (this.cssTransforms) { + if (percent <= 50) { + this.jq.bufferHolder.removeClass(this.cssClass.gt50); + this.jq.buffer1.css({'transform': 'rotate(' + degs + ')'}); + this.jq.buffer2.hide(); + } else if (percent <= 100) { + this.jq.bufferHolder.addClass(this.cssClass.gt50); + this.jq.buffer1.css({'transform': 'rotate(180deg)'}); + this.jq.buffer2.show(); + this.jq.buffer2.css({'transform': 'rotate(' + degs + ')'}); + } + } + }, + _getArcPercent: function (pageX, pageY) { + var offset = this.jq.circleControl.offset(), + x = pageX - offset.left - this.jq.circleControl.width() / 2, + y = pageY - offset.top - this.jq.circleControl.height() / 2, + theta = Math.atan2(y, x); + + if (theta > -1 * Math.PI && theta < -0.5 * Math.PI) { + theta = 2 * Math.PI + theta; + } + + // theta is now value between -0.5PI and 1.5PI + // ready to be normalized and applied + + return (theta + Math.PI / 2) / 2 * Math.PI * 10; + }, + setMedia: function (media) { + this.media = $.extend({}, media); + this.player.jPlayer("setMedia", this.media); + }, + play: function (time) { + this.player.jPlayer("play", time); + }, + pause: function (time) { + this.player.jPlayer("pause", time); + }, + destroy: function () { + this.player.unbind(this.eventNamespace); + this.player.jPlayer("destroy"); + } +}; diff --git a/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.grab.js b/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.grab.js new file mode 100644 index 0000000..2d0e053 --- /dev/null +++ b/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.grab.js @@ -0,0 +1,209 @@ +/* +jQuery grab +https://github.com/jussi-kalliokoski/jQuery.grab +Ported from Jin.js::gestures +https://github.com/jussi-kalliokoski/jin.js/ +Created by Jussi Kalliokoski +Licensed under MIT License. + +Includes fix for IE +*/ + + +(function ($) { + var extend = $.extend, + mousedown = 'mousedown', + mousemove = 'mousemove', + mouseup = 'mouseup', + touchstart = 'touchstart', + touchmove = 'touchmove', + touchend = 'touchend', + touchcancel = 'touchcancel'; + + function unbind(elem, type, func) { + if (type.substr(0, 5) !== 'touch') { // A temporary fix for IE8 data passing problem in Jin. + return $(elem).unbind(type, func); + } + var fnc, i; + for (i = 0; i < bind._binds.length; i++) { + if (bind._binds[i].elem === elem && bind._binds[i].type === type && bind._binds[i].func === func) { + if (document.addEventListener) { + elem.removeEventListener(type, bind._binds[i].fnc, false); + } else { + elem.detachEvent('on' + type, bind._binds[i].fnc); + } + bind._binds.splice(i--, 1); + } + } + } + + function bind(elem, type, func, pass) { + if (type.substr(0, 5) !== 'touch') { // A temporary fix for IE8 data passing problem in Jin. + return $(elem).bind(type, pass, func); + } + var fnc, i; + if (bind[type]) { + return bind[type].bind(elem, type, func, pass); + } + fnc = function (e) { + if (!e) { // Fix some ie bugs... + e = window.event; + } + if (!e.stopPropagation) { + e.stopPropagation = function () { + this.cancelBubble = true; + }; + } + e.data = pass; + func.call(elem, e); + }; + if (document.addEventListener) { + elem.addEventListener(type, fnc, false); + } else { + elem.attachEvent('on' + type, fnc); + } + bind._binds.push({elem: elem, type: type, func: func, fnc: fnc}); + } + + function grab(elem, options) { + var data = { + move: {x: 0, y: 0}, + offset: {x: 0, y: 0}, + position: {x: 0, y: 0}, + start: {x: 0, y: 0}, + affects: document.documentElement, + stopPropagation: false, + preventDefault: true, + touch: true // Implementation unfinished, and doesn't support multitouch + }; + extend(data, options); + data.element = elem; + bind(elem, mousedown, mouseDown, data); + if (data.touch) { + bind(elem, touchstart, touchStart, data); + } + } + + function ungrab(elem) { + unbind(elem, mousedown, mousedown); + } + + function mouseDown(e) { + e.data.position.x = e.pageX; + e.data.position.y = e.pageY; + e.data.start.x = e.pageX; + e.data.start.y = e.pageY; + e.data.event = e; + if (e.data.onstart && e.data.onstart.call(e.data.element, e.data)) { + return; + } + if (e.preventDefault && e.data.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation && e.data.stopPropagation) { + e.stopPropagation(); + } + bind(e.data.affects, mousemove, mouseMove, e.data); + bind(e.data.affects, mouseup, mouseUp, e.data); + } + + function mouseMove(e) { + if (e.preventDefault && e.data.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation && e.data.preventDefault) { + e.stopPropagation(); + } + e.data.move.x = e.pageX - e.data.position.x; + e.data.move.y = e.pageY - e.data.position.y; + e.data.position.x = e.pageX; + e.data.position.y = e.pageY; + e.data.offset.x = e.pageX - e.data.start.x; + e.data.offset.y = e.pageY - e.data.start.y; + e.data.event = e; + if (e.data.onmove) { + e.data.onmove.call(e.data.element, e.data); + } + } + + function mouseUp(e) { + if (e.preventDefault && e.data.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation && e.data.stopPropagation) { + e.stopPropagation(); + } + unbind(e.data.affects, mousemove, mouseMove); + unbind(e.data.affects, mouseup, mouseUp); + e.data.event = e; + if (e.data.onfinish) { + e.data.onfinish.call(e.data.element, e.data); + } + } + + function touchStart(e) { + e.data.position.x = e.touches[0].pageX; + e.data.position.y = e.touches[0].pageY; + e.data.start.x = e.touches[0].pageX; + e.data.start.y = e.touches[0].pageY; + e.data.event = e; + if (e.data.onstart && e.data.onstart.call(e.data.element, e.data)) { + return; + } + if (e.preventDefault && e.data.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation && e.data.stopPropagation) { + e.stopPropagation(); + } + bind(e.data.affects, touchmove, touchMove, e.data); + bind(e.data.affects, touchend, touchEnd, e.data); + } + + function touchMove(e) { + if (e.preventDefault && e.data.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation && e.data.stopPropagation) { + e.stopPropagation(); + } + e.data.move.x = e.touches[0].pageX - e.data.position.x; + e.data.move.y = e.touches[0].pageY - e.data.position.y; + e.data.position.x = e.touches[0].pageX; + e.data.position.y = e.touches[0].pageY; + e.data.offset.x = e.touches[0].pageX - e.data.start.x; + e.data.offset.y = e.touches[0].pageY - e.data.start.y; + e.data.event = e; + if (e.data.onmove) { + e.data.onmove.call(e.data.elem, e.data); + } + } + + function touchEnd(e) { + if (e.preventDefault && e.data.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation && e.data.stopPropagation) { + e.stopPropagation(); + } + unbind(e.data.affects, touchmove, touchMove); + unbind(e.data.affects, touchend, touchEnd); + e.data.event = e; + if (e.data.onfinish) { + e.data.onfinish.call(e.data.element, e.data); + } + } + + bind._binds = []; + + $.fn.grab = function (a, b) { + return this.each(function () { + return grab(this, a, b); + }); + }; + $.fn.ungrab = function (a) { + return this.each(function () { + return ungrab(this, a); + }); + }; +})(jQuery); diff --git a/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.jplayer.js b/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.jplayer.js new file mode 100644 index 0000000..acc29c9 --- /dev/null +++ b/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.jplayer.js @@ -0,0 +1,2041 @@ +/* + * jPlayer Plugin for jQuery JavaScript Library + * http://www.jplayer.org + * + * Copyright (c) 2009 - 2011 Happyworm Ltd + * Dual licensed under the MIT and GPL licenses. + * - http://www.opensource.org/licenses/mit-license.php + * - http://www.gnu.org/copyleft/gpl.html + * + * Author: Mark J Panaghiston + * Version: 2.0.15 + * Date: 21th June 2011 + */ + +/* Code verified using http://www.jshint.com/ */ +/*jshint asi:false, bitwise:false, boss:false, browser:true, curly:true, debug:false, eqeqeq:true, eqnull:false, evil:false, forin:false, immed:false, jquery:true, laxbreak:false, newcap:true, noarg:true, noempty:true, nonew:true, nomem:false, onevar:false, passfail:false, plusplus:false, regexp:false, undef:true, sub:false, strict:false, white:false */ +/*global jQuery:false, ActiveXObject:false, alert:false */ + +(function ($, undefined) { + + // Adapted from jquery.ui.widget.js (1.8.7): $.widget.bridge + $.fn.jPlayer = function (options) { + var name = "jPlayer"; + var isMethodCall = typeof options === "string", + args = Array.prototype.slice.call(arguments, 1), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.extend.apply(null, [true, options].concat(args)) : + options; + + // prevent calls to internal methods + if (isMethodCall && options.charAt(0) === "_") { + return returnValue; + } + + if (isMethodCall) { + this.each(function () { + var instance = $.data(this, name), + methodValue = instance && $.isFunction(instance[options]) ? + instance[options].apply(instance, args) : + instance; + if (methodValue !== instance && methodValue !== undefined) { + returnValue = methodValue; + return false; + } + }); + } else { + this.each(function () { + var instance = $.data(this, name); + if (instance) { + // instance.option( options || {} )._init(); // Orig jquery.ui.widget.js code: Not recommend for jPlayer. ie., Applying new options to an existing instance (via the jPlayer constructor) and performing the _init(). The _init() is what concerns me. It would leave a lot of event handlers acting on jPlayer instance and the interface. + instance.option(options || {}); // The new constructor only changes the options. Changing options only has basic support atm. + } else { + $.data(this, name, new $.jPlayer(options, this)); + } + }); + } + + return returnValue; + }; + + $.jPlayer = function (options, element) { + // allow instantiation without initializing for simple inheritance + if (arguments.length) { + this.element = $(element); + this.options = $.extend(true, {}, + this.options, + options + ); + var self = this; + this.element.bind("remove.jPlayer", function () { + self.destroy(); + }); + this._init(); + } + }; + // End of: (Adapted from jquery.ui.widget.js (1.8.7)) + + // Emulated HTML5 methods and properties + $.jPlayer.emulateMethods = "load play pause"; + $.jPlayer.emulateStatus = "src readyState networkState currentTime duration paused ended playbackRate"; + $.jPlayer.emulateOptions = "muted volume"; + + // Reserved event names generated by jPlayer that are not part of the HTML5 Media element spec + $.jPlayer.reservedEvent = "ready resize error warning"; + + // Events generated by jPlayer + $.jPlayer.event = { + ready: "jPlayer_ready", + resize: "jPlayer_resize", // Not implemented. + error: "jPlayer_error", // Event error code in event.jPlayer.error.type. See $.jPlayer.error + warning: "jPlayer_warning", // Event warning code in event.jPlayer.warning.type. See $.jPlayer.warning + + // Other events match HTML5 spec. + loadstart: "jPlayer_loadstart", + progress: "jPlayer_progress", + suspend: "jPlayer_suspend", + abort: "jPlayer_abort", + emptied: "jPlayer_emptied", + stalled: "jPlayer_stalled", + play: "jPlayer_play", + pause: "jPlayer_pause", + loadedmetadata: "jPlayer_loadedmetadata", + loadeddata: "jPlayer_loadeddata", + waiting: "jPlayer_waiting", + playing: "jPlayer_playing", + canplay: "jPlayer_canplay", + canplaythrough: "jPlayer_canplaythrough", + seeking: "jPlayer_seeking", + seeked: "jPlayer_seeked", + timeupdate: "jPlayer_timeupdate", + ended: "jPlayer_ended", + ratechange: "jPlayer_ratechange", + durationchange: "jPlayer_durationchange", + volumechange: "jPlayer_volumechange" + }; + + $.jPlayer.htmlEvent = [ // These HTML events are bubbled through to the jPlayer event, without any internal action. + "loadstart", + // "progress", // jPlayer uses internally before bubbling. + // "suspend", // jPlayer uses internally before bubbling. + "abort", + // "error", // jPlayer uses internally before bubbling. + "emptied", + "stalled", + // "play", // jPlayer uses internally before bubbling. + // "pause", // jPlayer uses internally before bubbling. + "loadedmetadata", + "loadeddata", + // "waiting", // jPlayer uses internally before bubbling. + // "playing", // jPlayer uses internally before bubbling. + // "canplay", // jPlayer fixes the volume (for Chrome) before bubbling. + "canplaythrough", + // "seeking", // jPlayer uses internally before bubbling. + // "seeked", // jPlayer uses internally before bubbling. + // "timeupdate", // jPlayer uses internally before bubbling. + // "ended", // jPlayer uses internally before bubbling. + "ratechange" + // "durationchange" // jPlayer uses internally before bubbling. + // "volumechange" // Handled by jPlayer in volume() method, primarily due to the volume fix (for Chrome) in the canplay event. [*] Need to review whether the latest Chrome still needs the fix sometime. + ]; + + $.jPlayer.pause = function () { + // $.each($.jPlayer.instances, function(i, element) { + $.each($.jPlayer.prototype.instances, function (i, element) { + if (element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event. + element.jPlayer("pause"); + } + }); + }; + + $.jPlayer.timeFormat = { + showHour: false, + showMin: true, + showSec: true, + padHour: false, + padMin: true, + padSec: true, + sepHour: ":", + sepMin: ":", + sepSec: "" + }; + + $.jPlayer.convertTime = function (s) { + var myTime = new Date(s * 1000); + var hour = myTime.getUTCHours(); + var min = myTime.getUTCMinutes(); + var sec = myTime.getUTCSeconds(); + var strHour = ($.jPlayer.timeFormat.padHour && hour < 10) ? "0" + hour : hour; + var strMin = ($.jPlayer.timeFormat.padMin && min < 10) ? "0" + min : min; + var strSec = ($.jPlayer.timeFormat.padSec && sec < 10) ? "0" + sec : sec; + return (($.jPlayer.timeFormat.showHour) ? strHour + $.jPlayer.timeFormat.sepHour : "") + (($.jPlayer.timeFormat.showMin) ? strMin + $.jPlayer.timeFormat.sepMin : "") + (($.jPlayer.timeFormat.showSec) ? strSec + $.jPlayer.timeFormat.sepSec : ""); + }; + + // Adapting jQuery 1.4.4 code for jQuery.browser. Required since jQuery 1.3.2 does not detect Chrome as webkit. + $.jPlayer.uaBrowser = function (userAgent) { + var ua = userAgent.toLowerCase(); + + // Useragent RegExp + var rwebkit = /(webkit)[ \/]([\w.]+)/; + var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/; + var rmsie = /(msie) ([\w.]+)/; + var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/; + + var match = rwebkit.exec(ua) || + ropera.exec(ua) || + rmsie.exec(ua) || + ua.indexOf("compatible") < 0 && rmozilla.exec(ua) || + []; + + return {browser: match[1] || "", version: match[2] || "0"}; + }; + + // Platform sniffer for detecting mobile devices + $.jPlayer.uaPlatform = function (userAgent) { + var ua = userAgent.toLowerCase(); + + // Useragent RegExp + var rplatform = /(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/; + var rtablet = /(ipad|playbook)/; + var randroid = /(android)/; + var rmobile = /(mobile)/; + + var platform = rplatform.exec(ua) || []; + var tablet = rtablet.exec(ua) || + !rmobile.exec(ua) && randroid.exec(ua) || + []; + + return {platform: platform[1] || "", tablet: tablet[1] || ""}; + }; + + $.jPlayer.browser = {}; + $.jPlayer.platform = {}; + + var browserMatch = $.jPlayer.uaBrowser(navigator.userAgent); + if (browserMatch.browser) { + $.jPlayer.browser[browserMatch.browser] = true; + $.jPlayer.browser.version = browserMatch.version; + } + var platformMatch = $.jPlayer.uaPlatform(navigator.userAgent); + if (platformMatch.platform) { + $.jPlayer.platform[platformMatch.platform] = true; + $.jPlayer.platform.mobile = !platformMatch.tablet; + $.jPlayer.platform.tablet = !!platformMatch.tablet; + } + + $.jPlayer.prototype = { + count: 0, // Static Variable: Change it via prototype. + version: { // Static Object + script: "2.0.15", + needFlash: "2.0.9", + flash: "unknown" + }, + options: { // Instanced in $.jPlayer() constructor + swfPath: "js", // Path to Jplayer.swf. Can be relative, absolute or server root relative. + solution: "html, flash", // Valid solutions: html, flash. Order defines priority. 1st is highest, + supplied: "mp3", // Defines which formats jPlayer will try and support and the priority by the order. 1st is highest, + preload: 'metadata', // HTML5 Spec values: none, metadata, auto. + volume: 0.8, // The volume. Number 0 to 1. + muted: false, + wmode: "window", // Default Flash wmode is: window. Valid wmode: transparent, opaque, direct, gpu + backgroundColor: "#000000", // To define the jPlayer div and Flash background color. + cssSelectorAncestor: "#jp_container_1", + cssSelector: { // * denotes properties that should only be required when video media type required. _cssSelector() would require changes to enable splitting these into Audio and Video defaults. + videoPlay: ".jp-video-play", // * + play: ".jp-play", + pause: ".jp-pause", + stop: ".jp-stop", + seekBar: ".jp-seek-bar", + playBar: ".jp-play-bar", + mute: ".jp-mute", + unmute: ".jp-unmute", + volumeBar: ".jp-volume-bar", + volumeBarValue: ".jp-volume-bar-value", + currentTime: ".jp-current-time", + duration: ".jp-duration", + fullScreen: ".jp-full-screen", // * + restoreScreen: ".jp-restore-screen" // * + }, + fullScreen: false, + // globalVolume: false, // Not implemented: Set to make volume changes affect all jPlayer instances + // globalMute: false, // Not implemented: Set to make mute changes affect all jPlayer instances + idPrefix: "jp", // Prefix for the ids of html elements created by jPlayer. For flash, this must not include characters: . - + * / \ + noConflict: "jQuery", + emulateHtml: false, // Emulates the HTML5 Media element on the jPlayer element. + errorAlerts: false, + warningAlerts: false + }, + optionsAudio: { + size: { + width: "0px", + height: "0px", + cssClass: "" + }, + sizeFull: { + width: "0px", + height: "0px", + cssClass: "" + } + }, + optionsVideo: { + size: { + width: "480px", + height: "270px", + cssClass: "jp-video-270p" + }, + sizeFull: { + width: "100%", + height: "90%", + cssClass: "jp-video-full" + } + }, + instances: {}, // Static Object + status: { // Instanced in _init() + src: "", + media: {}, + paused: true, + format: {}, + formatType: "", + waitForPlay: true, // Same as waitForLoad except in case where preloading. + waitForLoad: true, + srcSet: false, + video: false, // True if playing a video + seekPercent: 0, + currentPercentRelative: 0, + currentPercentAbsolute: 0, + currentTime: 0, + duration: 0, + readyState: 0, + networkState: 0, + playbackRate: 1, + ended: 0 + + /* Persistant status properties created dynamically at _init(): + width + height + cssClass + */ + }, + + internal: { // Instanced in _init() + ready: false + // instance: undefined, + // domNode: undefined, + // htmlDlyCmdId: undefined + }, + solution: { // Static Object: Defines the solutions built in jPlayer. + html: true, + flash: true + }, + // 'MPEG-4 support' : canPlayType('video/mp4; codecs="mp4v.20.8"') + format: { // Static Object + mp3: { + codec: 'audio/mpeg; codecs="mp3"', + flashCanPlay: true, + media: 'audio' + }, + m4a: { // AAC / MP4 + codec: 'audio/mp4; codecs="mp4a.40.2"', + flashCanPlay: true, + media: 'audio' + }, + oga: { // OGG + codec: 'audio/ogg; codecs="vorbis"', + flashCanPlay: false, + media: 'audio' + }, + wav: { // PCM + codec: 'audio/wav; codecs="1"', + flashCanPlay: false, + media: 'audio' + }, + webma: { // WEBM + codec: 'audio/webm; codecs="vorbis"', + flashCanPlay: false, + media: 'audio' + }, + fla: { // FLV / F4A + codec: 'audio/x-flv', + flashCanPlay: true, + media: 'audio' + }, + m4v: { // H.264 / MP4 + codec: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', + flashCanPlay: true, + media: 'video' + }, + ogv: { // OGG + codec: 'video/ogg; codecs="theora, vorbis"', + flashCanPlay: false, + media: 'video' + }, + webmv: { // WEBM + codec: 'video/webm; codecs="vorbis, vp8"', + flashCanPlay: false, + media: 'video' + }, + flv: { // FLV / F4V + codec: 'video/x-flv', + flashCanPlay: true, + media: 'video' + } + }, + _init: function () { + var self = this; + + this.element.empty(); + + this.status = $.extend({}, this.status); // Copy static to unique instance. + this.internal = $.extend({}, this.internal); // Copy static to unique instance. + + this.internal.domNode = this.element.get(0); + + this.formats = []; // Array based on supplied string option. Order defines priority. + this.solutions = []; // Array based on solution string option. Order defines priority. + this.require = {}; // Which media types are required: video, audio. + + this.htmlElement = {}; // DOM elements created by jPlayer + this.html = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. + this.html.audio = {}; + this.html.video = {}; + this.flash = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. + + this.css = {}; + this.css.cs = {}; // Holds the css selector strings + this.css.jq = {}; // Holds jQuery selectors. ie., $(css.cs.method) + + this.ancestorJq = []; // Holds jQuery selector of cssSelectorAncestor. Init would use $() instead of [], but it is only 1.4+ + + this.options.volume = this._limitValue(this.options.volume, 0, 1); // Limit volume value's bounds. + + // Create the formats array, with prority based on the order of the supplied formats string + $.each(this.options.supplied.toLowerCase().split(","), function (index1, value1) { + var format = value1.replace(/^\s+|\s+$/g, ""); //trim + if (self.format[format]) { // Check format is valid. + var dupFound = false; + $.each(self.formats, function (index2, value2) { // Check for duplicates + if (format === value2) { + dupFound = true; + return false; + } + }); + if (!dupFound) { + self.formats.push(format); + } + } + }); + + // Create the solutions array, with prority based on the order of the solution string + $.each(this.options.solution.toLowerCase().split(","), function (index1, value1) { + var solution = value1.replace(/^\s+|\s+$/g, ""); //trim + if (self.solution[solution]) { // Check solution is valid. + var dupFound = false; + $.each(self.solutions, function (index2, value2) { // Check for duplicates + if (solution === value2) { + dupFound = true; + return false; + } + }); + if (!dupFound) { + self.solutions.push(solution); + } + } + }); + + this.internal.instance = "jp_" + this.count; + this.instances[this.internal.instance] = this.element; + + // Check the jPlayer div has an id and create one if required. Important for Flash to know the unique id for comms. + if (this.element.attr("id") === "") { + this.element.attr("id", this.options.idPrefix + "_jplayer_" + this.count); + } + + this.internal.self = $.extend({}, { + id: this.element.attr("id"), + jq: this.element + }); + this.internal.audio = $.extend({}, { + id: this.options.idPrefix + "_audio_" + this.count, + jq: undefined + }); + this.internal.video = $.extend({}, { + id: this.options.idPrefix + "_video_" + this.count, + jq: undefined + }); + this.internal.flash = $.extend({}, { + id: this.options.idPrefix + "_flash_" + this.count, + jq: undefined, + swf: this.options.swfPath + ((this.options.swfPath !== "" && this.options.swfPath.slice(-1) !== "/") ? "/" : "") + "Jplayer.swf" + }); + this.internal.poster = $.extend({}, { + id: this.options.idPrefix + "_poster_" + this.count, + jq: undefined + }); + + // Register listeners defined in the constructor + $.each($.jPlayer.event, function (eventName, eventType) { + if (self.options[eventName] !== undefined) { + self.element.bind(eventType + ".jPlayer", self.options[eventName]); // With .jPlayer namespace. + self.options[eventName] = undefined; // Destroy the handler pointer copy on the options. Reason, events can be added/removed in other ways so this could be obsolete and misleading. + } + }); + + // Determine if we require solutions for audio, video or both media types. + this.require.audio = false; + this.require.video = false; + $.each(this.formats, function (priority, format) { + self.require[self.format[format].media] = true; + }); + + // Now required types are known, finish the options default settings. + if (this.require.video) { + this.options = $.extend(true, {}, + this.optionsVideo, + this.options + ); + } else { + this.options = $.extend(true, {}, + this.optionsAudio, + this.options + ); + } + this._setSize(); // update status and jPlayer element size + + // Create the poster image. + this.htmlElement.poster = document.createElement('img'); + this.htmlElement.poster.id = this.internal.poster.id; + this.htmlElement.poster.onload = function () { // Note that this did not work on Firefox 3.6: poster.addEventListener("onload", function() {}, false); Did not investigate x-browser. + if (!self.status.video || self.status.waitForPlay) { + self.internal.poster.jq.show(); + } + }; + this.element.append(this.htmlElement.poster); + this.internal.poster.jq = $("#" + this.internal.poster.id); + this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height}); + this.internal.poster.jq.hide(); + + // Generate the required media elements + this.html.audio.available = false; + if (this.require.audio) { // If a supplied format is audio + this.htmlElement.audio = document.createElement('audio'); + this.htmlElement.audio.id = this.internal.audio.id; + this.html.audio.available = !!this.htmlElement.audio.canPlayType; + } + this.html.video.available = false; + if (this.require.video) { // If a supplied format is video + this.htmlElement.video = document.createElement('video'); + this.htmlElement.video.id = this.internal.video.id; + this.html.video.available = !!this.htmlElement.video.canPlayType; + } + + this.flash.available = this._checkForFlash(10); + + this.html.canPlay = {}; + this.flash.canPlay = {}; + $.each(this.formats, function (priority, format) { + self.html.canPlay[format] = self.html[self.format[format].media].available && "" !== self.htmlElement[self.format[format].media].canPlayType(self.format[format].codec); + self.flash.canPlay[format] = self.format[format].flashCanPlay && self.flash.available; + }); + this.html.desired = false; + this.flash.desired = false; + $.each(this.solutions, function (solutionPriority, solution) { + if (solutionPriority === 0) { + self[solution].desired = true; + } else { + var audioCanPlay = false; + var videoCanPlay = false; + $.each(self.formats, function (formatPriority, format) { + if (self[self.solutions[0]].canPlay[format]) { // The other solution can play + if (self.format[format].media === 'video') { + videoCanPlay = true; + } else { + audioCanPlay = true; + } + } + }); + self[solution].desired = (self.require.audio && !audioCanPlay) || (self.require.video && !videoCanPlay); + } + }); + // This is what jPlayer will support, based on solution and supplied. + this.html.support = {}; + this.flash.support = {}; + $.each(this.formats, function (priority, format) { + self.html.support[format] = self.html.canPlay[format] && self.html.desired; + self.flash.support[format] = self.flash.canPlay[format] && self.flash.desired; + }); + // If jPlayer is supporting any format in a solution, then the solution is used. + this.html.used = false; + this.flash.used = false; + $.each(this.solutions, function (solutionPriority, solution) { + $.each(self.formats, function (formatPriority, format) { + if (self[solution].support[format]) { + self[solution].used = true; + return false; + } + }); + }); + + // Init solution active state and the event gates to false. + this.html.active = false; + this.html.audio.gate = false; + this.html.video.gate = false; + this.flash.active = false; + this.flash.gate = false; + + // Set up the css selectors for the control and feedback entities. + this._cssSelectorAncestor(this.options.cssSelectorAncestor); + + // If neither html nor flash are being used by this browser, then media playback is not possible. Trigger an error event. + if (!(this.html.used || this.flash.used)) { + this._error({ + type: $.jPlayer.error.NO_SOLUTION, + context: "{solution:'" + this.options.solution + "', supplied:'" + this.options.supplied + "'}", + message: $.jPlayer.errorMsg.NO_SOLUTION, + hint: $.jPlayer.errorHint.NO_SOLUTION + }); + } + + // Add the flash solution if it is being used. + if (this.flash.used) { + var htmlObj, + flashVars = 'jQuery=' + encodeURI(this.options.noConflict) + '&id=' + encodeURI(this.internal.self.id) + '&vol=' + this.options.volume + '&muted=' + this.options.muted; + + // Code influenced by SWFObject 2.2: http://code.google.com/p/swfobject/ + // Non IE browsers have an initial Flash size of 1 by 1 otherwise the wmode affected the Flash ready event. + + if ($.browser.msie && Number($.browser.version) <= 8) { + var objStr = ''; + + var paramStr = [ + '', + '', + '', + '', + '' + ]; + + htmlObj = document.createElement(objStr); + for (var i = 0; i < paramStr.length; i++) { + htmlObj.appendChild(document.createElement(paramStr[i])); + } + } else { + var createParam = function (el, n, v) { + var p = document.createElement("param"); + p.setAttribute("name", n); + p.setAttribute("value", v); + el.appendChild(p); + }; + + htmlObj = document.createElement("object"); + htmlObj.setAttribute("id", this.internal.flash.id); + htmlObj.setAttribute("data", this.internal.flash.swf); + htmlObj.setAttribute("type", "application/x-shockwave-flash"); + htmlObj.setAttribute("width", "1"); // Non-zero + htmlObj.setAttribute("height", "1"); // Non-zero + createParam(htmlObj, "flashvars", flashVars); + createParam(htmlObj, "allowscriptaccess", "always"); + createParam(htmlObj, "bgcolor", this.options.backgroundColor); + createParam(htmlObj, "wmode", this.options.wmode); + } + + this.element.append(htmlObj); + this.internal.flash.jq = $(htmlObj); + } + + // Add the HTML solution if being used. + if (this.html.used) { + + // The HTML Audio handlers + if (this.html.audio.available) { + this._addHtmlEventListeners(this.htmlElement.audio, this.html.audio); + this.element.append(this.htmlElement.audio); + this.internal.audio.jq = $("#" + this.internal.audio.id); + } + + // The HTML Video handlers + if (this.html.video.available) { + this._addHtmlEventListeners(this.htmlElement.video, this.html.video); + this.element.append(this.htmlElement.video); + this.internal.video.jq = $("#" + this.internal.video.id); + this.internal.video.jq.css({'width': '0px', 'height': '0px'}); // Using size 0x0 since a .hide() causes issues in iOS + } + } + + // Create the bridge that emulates the HTML Media element on the jPlayer DIV + if (this.options.emulateHtml) { + this._emulateHtmlBridge(); + } + + if (this.html.used && !this.flash.used) { // If only HTML, then emulate flash ready() call after 100ms. + window.setTimeout(function () { + self.internal.ready = true; + self.version.flash = "n/a"; + self._trigger($.jPlayer.event.ready); + }, 100); + } + + this._updateInterface(); + this._updateButtons(false); + this._updateVolume(this.options.volume); + this._updateMute(this.options.muted); + if (this.css.jq.videoPlay.length) { + this.css.jq.videoPlay.hide(); + } + $.jPlayer.prototype.count++; // Change static variable via prototype. + }, + destroy: function () { + // MJP: The background change remains. Would need to store the original to restore it correctly. + + // Reset the interface, remove seeking effect and times. + this._resetStatus(); + this._updateInterface(); + this._seeked(); + if (this.css.jq.currentTime.length) { + this.css.jq.currentTime.text(""); + } + if (this.css.jq.duration.length) { + this.css.jq.duration.text(""); + } + + if (this.status.srcSet) { // Or you get a bogus error event + this.pause(); // Pauses the media and clears any delayed commands used in the HTML solution. + } + $.each(this.css.jq, function (fn, jq) { // Remove any bindings from the interface controls. + // Check selector is valid before trying to execute method. + if (jq.length) { + jq.unbind(".jPlayer"); + } + }); + if (this.options.emulateHtml) { + this._destroyHtmlBridge(); + } + this.element.removeData("jPlayer"); // Remove jPlayer data + this.element.unbind(".jPlayer"); // Remove all event handlers created by the jPlayer constructor + this.element.empty(); // Remove the inserted child elements + + this.instances[this.internal.instance] = undefined; // Clear the instance on the static instance object + }, + enable: function () { // Plan to implement + // options.disabled = false + }, + disable: function () { // Plan to implement + // options.disabled = true + }, + _addHtmlEventListeners: function (mediaElement, entity) { + var self = this; + mediaElement.preload = this.options.preload; + mediaElement.muted = this.options.muted; + mediaElement.volume = this.options.volume; + + // Create the event listeners + // Only want the active entity to affect jPlayer and bubble events. + // Using entity.gate so that object is referenced and gate property always current + + mediaElement.addEventListener("progress", function () { + if (entity.gate && !self.status.waitForLoad) { + self._getHtmlStatus(mediaElement); + self._updateInterface(); + self._trigger($.jPlayer.event.progress); + } + }, false); + mediaElement.addEventListener("timeupdate", function () { + if (entity.gate && !self.status.waitForLoad) { + self._getHtmlStatus(mediaElement); + self._updateInterface(); + self._trigger($.jPlayer.event.timeupdate); + } + }, false); + mediaElement.addEventListener("durationchange", function () { + if (entity.gate && !self.status.waitForLoad) { + self.status.duration = this.duration; + self._getHtmlStatus(mediaElement); + self._updateInterface(); + self._trigger($.jPlayer.event.durationchange); + } + }, false); + mediaElement.addEventListener("play", function () { + if (entity.gate && !self.status.waitForLoad) { + self._updateButtons(true); + self._trigger($.jPlayer.event.play); + } + }, false); + mediaElement.addEventListener("playing", function () { + if (entity.gate && !self.status.waitForLoad) { + self._updateButtons(true); + self._seeked(); + self._trigger($.jPlayer.event.playing); + } + }, false); + mediaElement.addEventListener("pause", function () { + if (entity.gate && !self.status.waitForLoad) { + self._updateButtons(false); + self._trigger($.jPlayer.event.pause); + } + }, false); + mediaElement.addEventListener("waiting", function () { + if (entity.gate && !self.status.waitForLoad) { + self._seeking(); + self._trigger($.jPlayer.event.waiting); + } + }, false); + mediaElement.addEventListener("canplay", function () { + if (entity.gate && !self.status.waitForLoad) { + mediaElement.volume = self._volumeFix(self.options.volume); + self._trigger($.jPlayer.event.canplay); + } + }, false); + mediaElement.addEventListener("seeking", function () { + if (entity.gate && !self.status.waitForLoad) { + self._seeking(); + self._trigger($.jPlayer.event.seeking); + } + }, false); + mediaElement.addEventListener("seeked", function () { + if (entity.gate && !self.status.waitForLoad) { + self._seeked(); + self._trigger($.jPlayer.event.seeked); + } + }, false); + mediaElement.addEventListener("suspend", function () { // Seems to be the only way of capturing that the iOS4 browser did not actually play the media from the page code. ie., It needs a user gesture. + if (entity.gate && !self.status.waitForLoad) { + self._seeked(); + self._trigger($.jPlayer.event.suspend); + } + }, false); + mediaElement.addEventListener("ended", function () { + if (entity.gate && !self.status.waitForLoad) { + // Order of the next few commands are important. Change the time and then pause. + // Solves a bug in Firefox, where issuing pause 1st causes the media to play from the start. ie., The pause is ignored. + if (!$.jPlayer.browser.webkit) { // Chrome crashes if you do this in conjunction with a setMedia command in an ended event handler. ie., The playlist demo. + self.htmlElement.media.currentTime = 0; // Safari does not care about this command. ie., It works with or without this line. (Both Safari and Chrome are Webkit.) + } + self.htmlElement.media.pause(); // Pause otherwise a click on the progress bar will play from that point, when it shouldn't, since it stopped playback. + self._updateButtons(false); + self._getHtmlStatus(mediaElement, true); // With override true. Otherwise Chrome leaves progress at full. + self._updateInterface(); + self._trigger($.jPlayer.event.ended); + } + }, false); + mediaElement.addEventListener("error", function () { + if (entity.gate && !self.status.waitForLoad) { + self._updateButtons(false); + self._seeked(); + if (self.status.srcSet) { // Deals with case of clearMedia() causing an error event. + clearTimeout(self.internal.htmlDlyCmdId); // Clears any delayed commands used in the HTML solution. + self.status.waitForLoad = true; // Allows the load operation to try again. + self.status.waitForPlay = true; // Reset since a play was captured. + if (self.status.video) { + self.internal.video.jq.css({'width': '0px', 'height': '0px'}); + } + if (self._validString(self.status.media.poster)) { + self.internal.poster.jq.show(); + } + if (self.css.jq.videoPlay.length) { + self.css.jq.videoPlay.show(); + } + self._error({ + type: $.jPlayer.error.URL, + context: self.status.src, // this.src shows absolute urls. Want context to show the url given. + message: $.jPlayer.errorMsg.URL, + hint: $.jPlayer.errorHint.URL + }); + } + } + }, false); + // Create all the other event listeners that bubble up to a jPlayer event from html, without being used by jPlayer. + $.each($.jPlayer.htmlEvent, function (i, eventType) { + mediaElement.addEventListener(this, function () { + if (entity.gate && !self.status.waitForLoad) { + self._trigger($.jPlayer.event[eventType]); + } + }, false); + }); + }, + _getHtmlStatus: function (media, override) { + var ct = 0, d = 0, cpa = 0, sp = 0, cpr = 0; + + if (media.duration) { // Fixes the duration bug in iOS, where the durationchange event occurs when media.duration is not always correct. + this.status.duration = media.duration; + } + ct = media.currentTime; + cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0; + if ((typeof media.seekable === "object") && (media.seekable.length > 0)) { + sp = (this.status.duration > 0) ? 100 * media.seekable.end(media.seekable.length - 1) / this.status.duration : 100; + cpr = 100 * media.currentTime / media.seekable.end(media.seekable.length - 1); + } else { + sp = 100; + cpr = cpa; + } + + if (override) { + ct = 0; + cpr = 0; + cpa = 0; + } + + this.status.seekPercent = sp; + this.status.currentPercentRelative = cpr; + this.status.currentPercentAbsolute = cpa; + this.status.currentTime = ct; + + this.status.readyState = media.readyState; + this.status.networkState = media.networkState; + this.status.playbackRate = media.playbackRate; + this.status.ended = media.ended; + }, + _resetStatus: function () { + this.status = $.extend({}, this.status, $.jPlayer.prototype.status); // Maintains the status properties that persist through a reset. + }, + _trigger: function (eventType, error, warning) { // eventType always valid as called using $.jPlayer.event.eventType + var event = $.Event(eventType); + event.jPlayer = {}; + event.jPlayer.version = $.extend({}, this.version); + event.jPlayer.options = $.extend(true, {}, this.options); // Deep copy + event.jPlayer.status = $.extend(true, {}, this.status); // Deep copy + event.jPlayer.html = $.extend(true, {}, this.html); // Deep copy + event.jPlayer.flash = $.extend(true, {}, this.flash); // Deep copy + if (error) { + event.jPlayer.error = $.extend({}, error); + } + if (warning) { + event.jPlayer.warning = $.extend({}, warning); + } + this.element.trigger(event); + }, + jPlayerFlashEvent: function (eventType, status) { // Called from Flash + if (eventType === $.jPlayer.event.ready && !this.internal.ready) { + this.internal.ready = true; + this.internal.flash.jq.css({'width': '0px', 'height': '0px'}); // Once Flash generates the ready event, minimise to zero as it is not affected by wmode anymore. + + this.version.flash = status.version; + if (this.version.needFlash !== this.version.flash) { + this._error({ + type: $.jPlayer.error.VERSION, + context: this.version.flash, + message: $.jPlayer.errorMsg.VERSION + this.version.flash, + hint: $.jPlayer.errorHint.VERSION + }); + } + this._trigger(eventType); + } + if (this.flash.gate) { + switch (eventType) { + case $.jPlayer.event.progress: + this._getFlashStatus(status); + this._updateInterface(); + this._trigger(eventType); + break; + case $.jPlayer.event.timeupdate: + this._getFlashStatus(status); + this._updateInterface(); + this._trigger(eventType); + break; + case $.jPlayer.event.play: + this._seeked(); + this._updateButtons(true); + this._trigger(eventType); + break; + case $.jPlayer.event.pause: + this._updateButtons(false); + this._trigger(eventType); + break; + case $.jPlayer.event.ended: + this._updateButtons(false); + this._trigger(eventType); + break; + case $.jPlayer.event.error: + this.status.waitForLoad = true; // Allows the load operation to try again. + this.status.waitForPlay = true; // Reset since a play was captured. + if (this.status.video) { + this.internal.flash.jq.css({'width': '0px', 'height': '0px'}); + } + if (this._validString(this.status.media.poster)) { + this.internal.poster.jq.show(); + } + if (this.css.jq.videoPlay.length) { + this.css.jq.videoPlay.show(); + } + if (this.status.video) { // Set up for another try. Execute before error event. + this._flash_setVideo(this.status.media); + } else { + this._flash_setAudio(this.status.media); + } + this._error({ + type: $.jPlayer.error.URL, + context: status.src, + message: $.jPlayer.errorMsg.URL, + hint: $.jPlayer.errorHint.URL + }); + break; + case $.jPlayer.event.seeking: + this._seeking(); + this._trigger(eventType); + break; + case $.jPlayer.event.seeked: + this._seeked(); + this._trigger(eventType); + break; + case $.jPlayer.event.ready: + // The ready event is handled outside the switch statement. + // Captured here otherwise 2 ready events would be generated if the ready event handler used setMedia. + break; + default: + this._trigger(eventType); + } + } + return false; + }, + _getFlashStatus: function (status) { + this.status.seekPercent = status.seekPercent; + this.status.currentPercentRelative = status.currentPercentRelative; + this.status.currentPercentAbsolute = status.currentPercentAbsolute; + this.status.currentTime = status.currentTime; + this.status.duration = status.duration; + + // The Flash does not generate this information in this release + this.status.readyState = 4; // status.readyState; + this.status.networkState = 0; // status.networkState; + this.status.playbackRate = 1; // status.playbackRate; + this.status.ended = false; // status.ended; + }, + _updateButtons: function (playing) { + this.status.paused = !playing; + if (this.css.jq.play.length && this.css.jq.pause.length) { + if (playing) { + this.css.jq.play.hide(); + this.css.jq.pause.show(); + } else { + this.css.jq.play.show(); + this.css.jq.pause.hide(); + } + } + }, + _updateInterface: function () { + if (this.css.jq.seekBar.length) { + this.css.jq.seekBar.width(this.status.seekPercent + "%"); + } + if (this.css.jq.playBar.length) { + this.css.jq.playBar.width(this.status.currentPercentRelative + "%"); + } + if (this.css.jq.currentTime.length) { + this.css.jq.currentTime.text($.jPlayer.convertTime(this.status.currentTime)); + } + if (this.css.jq.duration.length) { + this.css.jq.duration.text($.jPlayer.convertTime(this.status.duration)); + } + }, + _seeking: function () { + if (this.css.jq.seekBar.length) { + this.css.jq.seekBar.addClass("jp-seeking-bg"); + } + }, + _seeked: function () { + if (this.css.jq.seekBar.length) { + this.css.jq.seekBar.removeClass("jp-seeking-bg"); + } + }, + setMedia: function (media) { + + /* media[format] = String: URL of format. Must contain all of the supplied option's video or audio formats. + * media.poster = String: Video poster URL. + * media.subtitles = String: * NOT IMPLEMENTED * URL of subtitles SRT file + * media.chapters = String: * NOT IMPLEMENTED * URL of chapters SRT file + * media.stream = Boolean: * NOT IMPLEMENTED * Designating actual media streams. ie., "false/undefined" for files. Plan to refresh the flash every so often. + */ + + var self = this; + + this._seeked(); + clearTimeout(this.internal.htmlDlyCmdId); // Clears any delayed commands used in the HTML solution. + + // Store the current html gates, since we need for clearMedia() conditions. + var audioGate = this.html.audio.gate; + var videoGate = this.html.video.gate; + + var supported = false; + $.each(this.formats, function (formatPriority, format) { + var isVideo = self.format[format].media === 'video'; + $.each(self.solutions, function (solutionPriority, solution) { + if (self[solution].support[format] && self._validString(media[format])) { // Format supported in solution and url given for format. + var isHtml = solution === 'html'; + + if (isVideo) { + if (isHtml) { + self.html.audio.gate = false; + self.html.video.gate = true; + self.flash.gate = false; + } else { + self.html.audio.gate = false; + self.html.video.gate = false; + self.flash.gate = true; + } + } else { + if (isHtml) { + self.html.audio.gate = true; + self.html.video.gate = false; + self.flash.gate = false; + } else { + self.html.audio.gate = false; + self.html.video.gate = false; + self.flash.gate = true; + } + } + + // Clear media of the previous solution if: + // - it was Flash + // - changing from HTML to Flash + // - the HTML solution media type (audio or video) remained the same. + // Note that, we must be careful with clearMedia() on iPhone, otherwise clearing the video when changing to audio corrupts the built in video player. + if (self.flash.active || (self.html.active && self.flash.gate) || (audioGate === self.html.audio.gate && videoGate === self.html.video.gate)) { + self.clearMedia(); + } else if (audioGate !== self.html.audio.gate && videoGate !== self.html.video.gate) { // If switching between html elements + self._html_pause(); + // Hide the video if it was being used. + if (self.status.video) { + self.internal.video.jq.css({'width': '0px', 'height': '0px'}); + } + self._resetStatus(); // Since clearMedia usually does this. Execute after status.video useage. + } + + if (isVideo) { + if (isHtml) { + self._html_setVideo(media); + self.html.active = true; + self.flash.active = false; + } else { + self._flash_setVideo(media); + self.html.active = false; + self.flash.active = true; + } + if (self.css.jq.videoPlay.length) { + self.css.jq.videoPlay.show(); + } + self.status.video = true; + } else { + if (isHtml) { + self._html_setAudio(media); + self.html.active = true; + self.flash.active = false; + } else { + self._flash_setAudio(media); + self.html.active = false; + self.flash.active = true; + } + if (self.css.jq.videoPlay.length) { + self.css.jq.videoPlay.hide(); + } + self.status.video = false; + } + + supported = true; + return false; // Exit $.each + } + }); + if (supported) { + return false; // Exit $.each + } + }); + + if (supported) { + // Set poster after the possible clearMedia() command above. IE had issues since the IMG onload event occurred immediately when cached. ie., The clearMedia() hide the poster. + if (this._validString(media.poster)) { + if (this.htmlElement.poster.src !== media.poster) { // Since some browsers do not generate img onload event. + this.htmlElement.poster.src = media.poster; + } else { + this.internal.poster.jq.show(); + } + } else { + this.internal.poster.jq.hide(); // Hide if not used, since clearMedia() does not always occur above. ie., HTML audio <-> video switching. + } + this.status.srcSet = true; + this.status.media = $.extend({}, media); + this._updateButtons(false); + this._updateInterface(); + } else { // jPlayer cannot support any formats provided in this browser + // Pause here if old media could be playing. Otherwise, playing media being changed to bad media would leave the old media playing. + if (this.status.srcSet && !this.status.waitForPlay) { + this.pause(); + } + // Reset all the control flags + this.html.audio.gate = false; + this.html.video.gate = false; + this.flash.gate = false; + this.html.active = false; + this.flash.active = false; + // Reset status and interface. + this._resetStatus(); + this._updateInterface(); + this._updateButtons(false); + // Hide the any old media + this.internal.poster.jq.hide(); + if (this.html.used && this.require.video) { + this.internal.video.jq.css({'width': '0px', 'height': '0px'}); + } + if (this.flash.used) { + this.internal.flash.jq.css({'width': '0px', 'height': '0px'}); + } + // Send an error event + this._error({ + type: $.jPlayer.error.NO_SUPPORT, + context: "{supplied:'" + this.options.supplied + "'}", + message: $.jPlayer.errorMsg.NO_SUPPORT, + hint: $.jPlayer.errorHint.NO_SUPPORT + }); + } + }, + clearMedia: function () { + this._resetStatus(); + this._updateButtons(false); + + this.internal.poster.jq.hide(); + + clearTimeout(this.internal.htmlDlyCmdId); + + if (this.html.active) { + this._html_clearMedia(); + } else if (this.flash.active) { + this._flash_clearMedia(); + } + }, + load: function () { + if (this.status.srcSet) { + if (this.html.active) { + this._html_load(); + } else if (this.flash.active) { + this._flash_load(); + } + } else { + this._urlNotSetError("load"); + } + }, + play: function (time) { + time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler + if (this.status.srcSet) { + if (this.html.active) { + this._html_play(time); + } else if (this.flash.active) { + this._flash_play(time); + } + } else { + this._urlNotSetError("play"); + } + }, + videoPlay: function (e) { // Handles clicks on the play button over the video poster + this.play(); + }, + pause: function (time) { + time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler + if (this.status.srcSet) { + if (this.html.active) { + this._html_pause(time); + } else if (this.flash.active) { + this._flash_pause(time); + } + } else { + this._urlNotSetError("pause"); + } + }, + pauseOthers: function () { + var self = this; + $.each(this.instances, function (i, element) { + if (self.element !== element) { // Do not this instance. + if (element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event. + element.jPlayer("pause"); + } + } + }); + }, + stop: function () { + if (this.status.srcSet) { + if (this.html.active) { + this._html_pause(0); + } else if (this.flash.active) { + this._flash_pause(0); + } + } else { + this._urlNotSetError("stop"); + } + }, + playHead: function (p) { + p = this._limitValue(p, 0, 100); + if (this.status.srcSet) { + if (this.html.active) { + this._html_playHead(p); + } else if (this.flash.active) { + this._flash_playHead(p); + } + } else { + this._urlNotSetError("playHead"); + } + }, + _muted: function (muted) { + this.options.muted = muted; + if (this.html.used) { + this._html_mute(muted); + } + if (this.flash.used) { + this._flash_mute(muted); + } + this._updateMute(muted); + this._updateVolume(this.options.volume); + this._trigger($.jPlayer.event.volumechange); + }, + mute: function (mute) { // mute is either: undefined (true), an event object (true) or a boolean (muted). + mute = mute === undefined ? true : !!mute; + this._muted(mute); + }, + unmute: function (unmute) { // unmute is either: undefined (true), an event object (true) or a boolean (!muted). + unmute = unmute === undefined ? true : !!unmute; + this._muted(!unmute); + }, + _updateMute: function (mute) { + if (this.css.jq.mute.length && this.css.jq.unmute.length) { + if (mute) { + this.css.jq.mute.hide(); + this.css.jq.unmute.show(); + } else { + this.css.jq.mute.show(); + this.css.jq.unmute.hide(); + } + } + }, + volume: function (v) { + v = this._limitValue(v, 0, 1); + this.options.volume = v; + + if (this.html.used) { + this._html_volume(v); + } + if (this.flash.used) { + this._flash_volume(v); + } + this._updateVolume(v); + this._trigger($.jPlayer.event.volumechange); + }, + volumeBar: function (e) { // Handles clicks on the volumeBar + if (!this.options.muted && this.css.jq.volumeBar.length) { // Ignore clicks when muted + var offset = this.css.jq.volumeBar.offset(); + var x = e.pageX - offset.left; + var w = this.css.jq.volumeBar.width(); + var v = x / w; + this.volume(v); + } + }, + volumeBarValue: function (e) { // Handles clicks on the volumeBarValue + this.volumeBar(e); + }, + _updateVolume: function (v) { + v = this.options.muted ? 0 : v; + if (this.css.jq.volumeBarValue.length) { + this.css.jq.volumeBarValue.width((v * 100) + "%"); + } + }, + _volumeFix: function (v) { // Need to review if this is still necessary on latest Chrome + var rnd = 0.001 * Math.random(); // Fix for Chrome 4: Fix volume being set multiple times before playing bug. + var fix = (v < 0.5) ? rnd : -rnd; // Fix for Chrome 4: Solves volume change before play bug. (When new vol == old vol Chrome 4 does nothing!) + return (v + fix); // Fix for Chrome 4: Event solves initial volume not being set correctly. + }, + _cssSelectorAncestor: function (ancestor) { + var self = this; + this.options.cssSelectorAncestor = ancestor; + this._removeUiClass(); + this.ancestorJq = ancestor ? $(ancestor) : []; // Would use $() instead of [], but it is only 1.4+ + if (ancestor && this.ancestorJq.length !== 1) { // So empty strings do not generate the warning. + this._warning({ + type: $.jPlayer.warning.CSS_SELECTOR_COUNT, + context: ancestor, + message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.ancestorJq.length + " found for cssSelectorAncestor.", + hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT + }); + } + this._addUiClass(); + $.each(this.options.cssSelector, function (fn, cssSel) { + self._cssSelector(fn, cssSel); + }); + }, + _cssSelector: function (fn, cssSel) { + var self = this; + if (typeof cssSel === 'string') { + if ($.jPlayer.prototype.options.cssSelector[fn]) { + if (this.css.jq[fn] && this.css.jq[fn].length) { + this.css.jq[fn].unbind(".jPlayer"); + } + this.options.cssSelector[fn] = cssSel; + this.css.cs[fn] = this.options.cssSelectorAncestor + " " + cssSel; + + if (cssSel) { // Checks for empty string + this.css.jq[fn] = $(this.css.cs[fn]); + } else { + this.css.jq[fn] = []; // To comply with the css.jq[fn].length check before its use. As of jQuery 1.4 could have used $() for an empty set. + } + + if (this.css.jq[fn].length) { + var handler = function (e) { + self[fn](e); + $(this).blur(); + return false; + }; + this.css.jq[fn].bind("click.jPlayer", handler); // Using jPlayer namespace + } + + if (cssSel && this.css.jq[fn].length !== 1) { // So empty strings do not generate the warning. ie., they just remove the old one. + this._warning({ + type: $.jPlayer.warning.CSS_SELECTOR_COUNT, + context: this.css.cs[fn], + message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.css.jq[fn].length + " found for " + fn + " method.", + hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT + }); + } + } else { + this._warning({ + type: $.jPlayer.warning.CSS_SELECTOR_METHOD, + context: fn, + message: $.jPlayer.warningMsg.CSS_SELECTOR_METHOD, + hint: $.jPlayer.warningHint.CSS_SELECTOR_METHOD + }); + } + } else { + this._warning({ + type: $.jPlayer.warning.CSS_SELECTOR_STRING, + context: cssSel, + message: $.jPlayer.warningMsg.CSS_SELECTOR_STRING, + hint: $.jPlayer.warningHint.CSS_SELECTOR_STRING + }); + } + }, + seekBar: function (e) { // Handles clicks on the seekBar + if (this.css.jq.seekBar) { + var offset = this.css.jq.seekBar.offset(); + var x = e.pageX - offset.left; + var w = this.css.jq.seekBar.width(); + var p = 100 * x / w; + this.playHead(p); + } + }, + playBar: function (e) { // Handles clicks on the playBar + this.seekBar(e); + }, + currentTime: function (e) { // Handles clicks on the text + // Added to avoid errors using cssSelector system for the text + }, + duration: function (e) { // Handles clicks on the text + // Added to avoid errors using cssSelector system for the text + }, + // Options code adapted from ui.widget.js (1.8.7). Made changes so the key can use dot notation. To match previous getData solution in jPlayer 1. + option: function (key, value) { + var options = key; + + // Enables use: options(). Returns a copy of options object + if (arguments.length === 0) { + return $.extend(true, {}, this.options); + } + + if (typeof key === "string") { + var keys = key.split("."); + + // Enables use: options("someOption") Returns a copy of the option. Supports dot notation. + if (value === undefined) { + + var opt = $.extend(true, {}, this.options); + for (var i = 0; i < keys.length; i++) { + if (opt[keys[i]] !== undefined) { + opt = opt[keys[i]]; + } else { + this._warning({ + type: $.jPlayer.warning.OPTION_KEY, + context: key, + message: $.jPlayer.warningMsg.OPTION_KEY, + hint: $.jPlayer.warningHint.OPTION_KEY + }); + return undefined; + } + } + return opt; + } + + // Enables use: options("someOptionObject", someObject}). Creates: {someOptionObject:someObject} + // Enables use: options("someOption", someValue). Creates: {someOption:someValue} + // Enables use: options("someOptionObject.someOption", someValue). Creates: {someOptionObject:{someOption:someValue}} + + options = {}; + var opts = options; + + for (var j = 0; j < keys.length; j++) { + if (j < keys.length - 1) { + opts[keys[j]] = {}; + opts = opts[keys[j]]; + } else { + opts[keys[j]] = value; + } + } + } + + // Otherwise enables use: options(optionObject). Uses original object (the key) + + this._setOptions(options); + + return this; + }, + _setOptions: function (options) { + var self = this; + $.each(options, function (key, value) { // This supports the 2 level depth that the options of jPlayer has. Would review if we ever need more depth. + self._setOption(key, value); + }); + + return this; + }, + _setOption: function (key, value) { + var self = this; + + // The ability to set options is limited at this time. + + switch (key) { + case "volume" : + this.volume(value); + break; + case "muted" : + this._muted(value); + break; + case "cssSelectorAncestor" : + this._cssSelectorAncestor(value); // Set and refresh all associations for the new ancestor. + break; + case "cssSelector" : + $.each(value, function (fn, cssSel) { + self._cssSelector(fn, cssSel); // NB: The option is set inside this function, after further validity checks. + }); + break; + case "fullScreen" : + if (this.options[key] !== value) { // if changed + this._removeUiClass(); + this.options[key] = value; + this._refreshSize(); + } + break; + case "size" : + if (!this.options.fullScreen && this.options[key].cssClass !== value.cssClass) { + this._removeUiClass(); + } + this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. + this._refreshSize(); + break; + case "sizeFull" : + if (this.options.fullScreen && this.options[key].cssClass !== value.cssClass) { + this._removeUiClass(); + } + this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. + this._refreshSize(); + break; + case "emulateHtml" : + if (this.options[key] !== value) { // To avoid multiple event handlers being created, if true already. + this.options[key] = value; + if (value) { + this._emulateHtmlBridge(); + } else { + this._destroyHtmlBridge(); + } + } + break; + } + + return this; + }, + // End of: (Options code adapted from ui.widget.js) + + _refreshSize: function () { + this._setSize(); // update status and jPlayer element size + this._addUiClass(); // update the ui class + this._updateSize(); // update internal sizes + }, + _setSize: function () { + // Determine the current size from the options + if (this.options.fullScreen) { + this.status.width = this.options.sizeFull.width; + this.status.height = this.options.sizeFull.height; + this.status.cssClass = this.options.sizeFull.cssClass; + } else { + this.status.width = this.options.size.width; + this.status.height = this.options.size.height; + this.status.cssClass = this.options.size.cssClass; + } + + // Set the size of the jPlayer area. + this.element.css({'width': this.status.width, 'height': this.status.height}); + }, + _addUiClass: function () { + if (this.ancestorJq.length) { + this.ancestorJq.addClass(this.status.cssClass); + } + }, + _removeUiClass: function () { + if (this.ancestorJq.length) { + this.ancestorJq.removeClass(this.status.cssClass); + } + }, + _updateSize: function () { + // The poster uses show/hide so can simply resize it. + this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height}); + + // Video html or flash resized if necessary at this time. + if (!this.status.waitForPlay) { + if (this.html.active && this.status.video) { // Only if video media + this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height}); + } else if (this.flash.active) { + this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height}); + } + } + }, + fullScreen: function () { + this._setOption("fullScreen", true); + }, + restoreScreen: function () { + this._setOption("fullScreen", false); + }, + _html_initMedia: function () { + if (this.status.srcSet && !this.status.waitForPlay) { + this.htmlElement.media.pause(); + } + if (this.options.preload !== 'none') { + this._html_load(); + } + this._trigger($.jPlayer.event.timeupdate); // The flash generates this event for its solution. + }, + _html_setAudio: function (media) { + var self = this; + // Always finds a format due to checks in setMedia() + $.each(this.formats, function (priority, format) { + if (self.html.support[format] && media[format]) { + self.status.src = media[format]; + self.status.format[format] = true; + self.status.formatType = format; + return false; + } + }); + this.htmlElement.media = this.htmlElement.audio; + this._html_initMedia(); + }, + _html_setVideo: function (media) { + var self = this; + // Always finds a format due to checks in setMedia() + $.each(this.formats, function (priority, format) { + if (self.html.support[format] && media[format]) { + self.status.src = media[format]; + self.status.format[format] = true; + self.status.formatType = format; + return false; + } + }); + this.htmlElement.media = this.htmlElement.video; + this._html_initMedia(); + }, + _html_clearMedia: function () { + if (this.htmlElement.media) { + if (this.htmlElement.media.id === this.internal.video.id) { + this.internal.video.jq.css({'width': '0px', 'height': '0px'}); + } + this.htmlElement.media.pause(); + this.htmlElement.media.src = ""; + this.htmlElement.media.load(); // Stops an old, "in progress" download from continuing the download. Triggers the loadstart, error and emptied events, due to the empty src. Also an abort event if a download was in progress. + } + }, + _html_load: function () { + if (this.status.waitForLoad) { + this.status.waitForLoad = false; + this.htmlElement.media.src = this.status.src; + this.htmlElement.media.load(); + } + clearTimeout(this.internal.htmlDlyCmdId); + }, + _html_play: function (time) { + var self = this; + this._html_load(); // Loads if required and clears any delayed commands. + + this.htmlElement.media.play(); // Before currentTime attempt otherwise Firefox 4 Beta never loads. + + if (!isNaN(time)) { + try { + this.htmlElement.media.currentTime = time; + } catch (err) { + this.internal.htmlDlyCmdId = setTimeout(function () { + self.play(time); + }, 100); + return; // Cancel execution and wait for the delayed command. + } + } + this._html_checkWaitForPlay(); + }, + _html_pause: function (time) { + var self = this; + + if (time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation. + this._html_load(); // Loads if required and clears any delayed commands. + } else { + clearTimeout(this.internal.htmlDlyCmdId); + } + + // Order of these commands is important for Safari (Win) and IE9. Pause then change currentTime. + this.htmlElement.media.pause(); + + if (!isNaN(time)) { + try { + this.htmlElement.media.currentTime = time; + } catch (err) { + this.internal.htmlDlyCmdId = setTimeout(function () { + self.pause(time); + }, 100); + return; // Cancel execution and wait for the delayed command. + } + } + if (time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button. + this._html_checkWaitForPlay(); + } + }, + _html_playHead: function (percent) { + var self = this; + this._html_load(); // Loads if required and clears any delayed commands. + try { + if ((typeof this.htmlElement.media.seekable === "object") && (this.htmlElement.media.seekable.length > 0)) { + this.htmlElement.media.currentTime = percent * this.htmlElement.media.seekable.end(this.htmlElement.media.seekable.length - 1) / 100; + } else if (this.htmlElement.media.duration > 0 && !isNaN(this.htmlElement.media.duration)) { + this.htmlElement.media.currentTime = percent * this.htmlElement.media.duration / 100; + } else { + throw "e"; + } + } catch (err) { + this.internal.htmlDlyCmdId = setTimeout(function () { + self.playHead(percent); + }, 100); + return; // Cancel execution and wait for the delayed command. + } + if (!this.status.waitForLoad) { + this._html_checkWaitForPlay(); + } + }, + _html_checkWaitForPlay: function () { + if (this.status.waitForPlay) { + this.status.waitForPlay = false; + if (this.css.jq.videoPlay.length) { + this.css.jq.videoPlay.hide(); + } + if (this.status.video) { + this.internal.poster.jq.hide(); + this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height}); + } + } + }, + _html_volume: function (v) { + if (this.html.audio.available) { + this.htmlElement.audio.volume = v; + } + if (this.html.video.available) { + this.htmlElement.video.volume = v; + } + }, + _html_mute: function (m) { + if (this.html.audio.available) { + this.htmlElement.audio.muted = m; + } + if (this.html.video.available) { + this.htmlElement.video.muted = m; + } + }, + _flash_setAudio: function (media) { + var self = this; + try { + // Always finds a format due to checks in setMedia() + $.each(this.formats, function (priority, format) { + if (self.flash.support[format] && media[format]) { + switch (format) { + case "m4a" : + case "fla" : + self._getMovie().fl_setAudio_m4a(media[format]); + break; + case "mp3" : + self._getMovie().fl_setAudio_mp3(media[format]); + break; + } + self.status.src = media[format]; + self.status.format[format] = true; + self.status.formatType = format; + return false; + } + }); + + if (this.options.preload === 'auto') { + this._flash_load(); + this.status.waitForLoad = false; + } + } catch (err) { + this._flashError(err); + } + }, + _flash_setVideo: function (media) { + var self = this; + try { + // Always finds a format due to checks in setMedia() + $.each(this.formats, function (priority, format) { + if (self.flash.support[format] && media[format]) { + switch (format) { + case "m4v" : + case "flv" : + self._getMovie().fl_setVideo_m4v(media[format]); + break; + } + self.status.src = media[format]; + self.status.format[format] = true; + self.status.formatType = format; + return false; + } + }); + + if (this.options.preload === 'auto') { + this._flash_load(); + this.status.waitForLoad = false; + } + } catch (err) { + this._flashError(err); + } + }, + _flash_clearMedia: function () { + this.internal.flash.jq.css({'width': '0px', 'height': '0px'}); // Must do via CSS as setting attr() to zero causes a jQuery error in IE. + try { + this._getMovie().fl_clearMedia(); + } catch (err) { + this._flashError(err); + } + }, + _flash_load: function () { + try { + this._getMovie().fl_load(); + } catch (err) { + this._flashError(err); + } + this.status.waitForLoad = false; + }, + _flash_play: function (time) { + try { + this._getMovie().fl_play(time); + } catch (err) { + this._flashError(err); + } + this.status.waitForLoad = false; + this._flash_checkWaitForPlay(); + }, + _flash_pause: function (time) { + try { + this._getMovie().fl_pause(time); + } catch (err) { + this._flashError(err); + } + if (time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button. + this.status.waitForLoad = false; + this._flash_checkWaitForPlay(); + } + }, + _flash_playHead: function (p) { + try { + this._getMovie().fl_play_head(p); + } catch (err) { + this._flashError(err); + } + if (!this.status.waitForLoad) { + this._flash_checkWaitForPlay(); + } + }, + _flash_checkWaitForPlay: function () { + if (this.status.waitForPlay) { + this.status.waitForPlay = false; + if (this.css.jq.videoPlay.length) { + this.css.jq.videoPlay.hide(); + } + if (this.status.video) { + this.internal.poster.jq.hide(); + this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height}); + } + } + }, + _flash_volume: function (v) { + try { + this._getMovie().fl_volume(v); + } catch (err) { + this._flashError(err); + } + }, + _flash_mute: function (m) { + try { + this._getMovie().fl_mute(m); + } catch (err) { + this._flashError(err); + } + }, + _getMovie: function () { + return document[this.internal.flash.id]; + }, + _checkForFlash: function (version) { + // Function checkForFlash adapted from FlashReplace by Robert Nyman + // http://code.google.com/p/flashreplace/ + var flashIsInstalled = false; + var flash; + if (window.ActiveXObject) { + try { + flash = new ActiveXObject(("ShockwaveFlash.ShockwaveFlash." + version)); + flashIsInstalled = true; + } catch (e) { + // Throws an error if the version isn't available + } + } else if (navigator.plugins && navigator.mimeTypes.length > 0) { + flash = navigator.plugins["Shockwave Flash"]; + if (flash) { + var flashVersion = navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/, "$1"); + if (flashVersion >= version) { + flashIsInstalled = true; + } + } + } + return flashIsInstalled; + }, + _validString: function (url) { + return (url && typeof url === "string"); // Empty strings return false + }, + _limitValue: function (value, min, max) { + return (value < min) ? min : ((value > max) ? max : value); + }, + _urlNotSetError: function (context) { + this._error({ + type: $.jPlayer.error.URL_NOT_SET, + context: context, + message: $.jPlayer.errorMsg.URL_NOT_SET, + hint: $.jPlayer.errorHint.URL_NOT_SET + }); + }, + _flashError: function (error) { + this._error({ + type: $.jPlayer.error.FLASH, + context: this.internal.flash.swf, + message: $.jPlayer.errorMsg.FLASH + error.message, + hint: $.jPlayer.errorHint.FLASH + }); + }, + _error: function (error) { + this._trigger($.jPlayer.event.error, error); + if (this.options.errorAlerts) { + this._alert("Error!" + (error.message ? "\n\n" + error.message : "") + (error.hint ? "\n\n" + error.hint : "") + "\n\nContext: " + error.context); + } + }, + _warning: function (warning) { + this._trigger($.jPlayer.event.warning, undefined, warning); + if (this.options.warningAlerts) { + this._alert("Warning!" + (warning.message ? "\n\n" + warning.message : "") + (warning.hint ? "\n\n" + warning.hint : "") + "\n\nContext: " + warning.context); + } + }, + _alert: function (message) { + alert("jPlayer " + this.version.script + " : id='" + this.internal.self.id + "' : " + message); + }, + _emulateHtmlBridge: function () { + var self = this, + methods = $.jPlayer.emulateMethods; + + // Emulate methods on jPlayer's DOM element. + $.each($.jPlayer.emulateMethods.split(/\s+/g), function (i, name) { + self.internal.domNode[name] = function (arg) { + self[name](arg); + }; + + }); + + // Bubble jPlayer events to its DOM element. + $.each($.jPlayer.event, function (eventName, eventType) { + var nativeEvent = true; + $.each($.jPlayer.reservedEvent.split(/\s+/g), function (i, name) { + if (name === eventName) { + nativeEvent = false; + return false; + } + }); + if (nativeEvent) { + self.element.bind(eventType + ".jPlayer.jPlayerHtml", function () { // With .jPlayer & .jPlayerHtml namespaces. + self._emulateHtmlUpdate(); + var domEvent = document.createEvent("Event"); + domEvent.initEvent(eventName, false, true); + self.internal.domNode.dispatchEvent(domEvent); + }); + } + // The error event would require a special case + }); + + // IE9 has a readyState property on all elements. The document should have it, but all (except media) elements inherit it in IE9. This conflicts with Popcorn, which polls the readyState. + }, + _emulateHtmlUpdate: function () { + var self = this; + + $.each($.jPlayer.emulateStatus.split(/\s+/g), function (i, name) { + self.internal.domNode[name] = self.status[name]; + }); + $.each($.jPlayer.emulateOptions.split(/\s+/g), function (i, name) { + self.internal.domNode[name] = self.options[name]; + }); + }, + _destroyHtmlBridge: function () { + var self = this; + + // Bridge event handlers are also removed by destroy() through .jPlayer namespace. + this.element.unbind(".jPlayerHtml"); // Remove all event handlers created by the jPlayer bridge. So you can change the emulateHtml option. + + // Remove the methods and properties + var emulated = $.jPlayer.emulateMethods + " " + $.jPlayer.emulateStatus + " " + $.jPlayer.emulateOptions; + $.each(emulated.split(/\s+/g), function (i, name) { + delete self.internal.domNode[name]; + }); + } + }; + + $.jPlayer.error = { + FLASH: "e_flash", + NO_SOLUTION: "e_no_solution", + NO_SUPPORT: "e_no_support", + URL: "e_url", + URL_NOT_SET: "e_url_not_set", + VERSION: "e_version" + }; + + $.jPlayer.errorMsg = { + FLASH: "jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ", // Used in: _flashError() + NO_SOLUTION: "No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.", // Used in: _init() + NO_SUPPORT: "It is not possible to play any media format provided in setMedia() on this browser using your current options.", // Used in: setMedia() + URL: "Media URL could not be loaded.", // Used in: jPlayerFlashEvent() and _addHtmlEventListeners() + URL_NOT_SET: "Attempt to issue media playback commands, while no media url is set.", // Used in: load(), play(), pause(), stop() and playHead() + VERSION: "jPlayer " + $.jPlayer.prototype.version.script + " needs Jplayer.swf version " + $.jPlayer.prototype.version.needFlash + " but found " // Used in: jPlayerReady() + }; + + $.jPlayer.errorHint = { + FLASH: "Check your swfPath option and that Jplayer.swf is there.", + NO_SOLUTION: "Review the jPlayer options: support and supplied.", + NO_SUPPORT: "Video or audio formats defined in the supplied option are missing.", + URL: "Check media URL is valid.", + URL_NOT_SET: "Use setMedia() to set the media URL.", + VERSION: "Update jPlayer files." + }; + + $.jPlayer.warning = { + CSS_SELECTOR_COUNT: "e_css_selector_count", + CSS_SELECTOR_METHOD: "e_css_selector_method", + CSS_SELECTOR_STRING: "e_css_selector_string", + OPTION_KEY: "e_option_key" + }; + + $.jPlayer.warningMsg = { + CSS_SELECTOR_COUNT: "The number of css selectors found did not equal one: ", + CSS_SELECTOR_METHOD: "The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.", + CSS_SELECTOR_STRING: "The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.", + OPTION_KEY: "The option requested in jPlayer('option') is undefined." + }; + + $.jPlayer.warningHint = { + CSS_SELECTOR_COUNT: "Check your css selector and the ancestor.", + CSS_SELECTOR_METHOD: "Check your method name.", + CSS_SELECTOR_STRING: "Check your css selector is a string.", + OPTION_KEY: "Check your option name." + }; +})(jQuery); diff --git a/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.transform.js b/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.transform.js new file mode 100644 index 0000000..4683de8 --- /dev/null +++ b/src/main/resources/static/staticResources/vendor/jplayercircle/js/jquery.transform.js @@ -0,0 +1,532 @@ +/* + * transform: A jQuery cssHooks adding cross-browser 2d transform capabilities to $.fn.css() and $.fn.animate() + * + * limitations: + * - requires jQuery 1.4.3+ + * - Should you use the *translate* property, then your elements need to be absolutely positionned in a relatively positionned wrapper **or it will fail in IE678**. + * - transformOrigin is not accessible + * + * latest version and complete README available on Github: + * https://github.com/louisremi/jquery.transform.js + * + * Copyright 2011 @louis_remi + * Licensed under the MIT license. + * + * This saved you an hour of work? + * Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON + * + */ +(function ($) { + + /* + * Feature tests and global variables + */ + var div = document.createElement('div'), + divStyle = div.style, + propertyName = 'transform', + suffix = 'Transform', + testProperties = [ + 'O' + suffix, + 'ms' + suffix, + 'Webkit' + suffix, + 'Moz' + suffix, + // prefix-less property + propertyName + ], + i = testProperties.length, + supportProperty, + supportMatrixFilter, + propertyHook, + propertyGet, + rMatrix = /Matrix([^)]*)/; + +// test different vendor prefixes of this property + while (i--) { + if (testProperties[i] in divStyle) { + $.support[propertyName] = supportProperty = testProperties[i]; + + } + } +// IE678 alternative + if (!supportProperty) { + $.support.matrixFilter = supportMatrixFilter = divStyle.filter === ''; + } +// prevent IE memory leak + div = divStyle = null; + +// px isn't the default unit of this property + $.cssNumber[propertyName] = true; + + /* + * fn.css() hooks + */ + if (supportProperty && supportProperty != propertyName) { + // Modern browsers can use jQuery.cssProps as a basic hook + $.cssProps[propertyName] = supportProperty; + + // Firefox needs a complete hook because it stuffs matrix with 'px' + if (supportProperty == 'Moz' + suffix) { + propertyHook = { + get: function (elem, computed) { + return (computed ? + // remove 'px' from the computed matrix + $.css(elem, supportProperty).split('px').join('') : + elem.style[supportProperty] + ) + }, + set: function (elem, value) { + // remove 'px' from matrices + elem.style[supportProperty] = /matrix[^)p]*\)/.test(value) ? + value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, 'matrix$1$2px,$3px') : + value; + } + } + /* Fix two jQuery bugs still present in 1.5.1 + * - rupper is incompatible with IE9, see http://jqbug.com/8346 + * - jQuery.css is not really jQuery.cssProps aware, see http://jqbug.com/8402 + */ + } else if (/^1\.[0-5](?:\.|$)/.test($.fn.jquery)) { + propertyHook = { + get: function (elem, computed) { + return (computed ? + $.css(elem, supportProperty.replace(/^ms/, 'Ms')) : + elem.style[supportProperty] + ) + } + } + } + /* TODO: leverage hardware acceleration of 3d transform in Webkit only + else if ( supportProperty == 'Webkit' + suffix && support3dTransform ) { + propertyHook = { + set: function( elem, value ) { + elem.style[supportProperty] = + value.replace(); + } + } + }*/ + + } else if (supportMatrixFilter) { + propertyHook = { + get: function (elem, computed) { + var elemStyle = (computed && elem.currentStyle ? elem.currentStyle : elem.style), + matrix; + + if (elemStyle && rMatrix.test(elemStyle.filter)) { + matrix = RegExp.$1.split(','); + matrix = [ + matrix[0].split('=')[1], + matrix[2].split('=')[1], + matrix[1].split('=')[1], + matrix[3].split('=')[1] + ]; + } else { + matrix = [1, 0, 0, 1]; + } + matrix[4] = elemStyle ? elemStyle.left : 0; + matrix[5] = elemStyle ? elemStyle.top : 0; + return "matrix(" + matrix + ")"; + }, + set: function (elem, value, animate) { + var elemStyle = elem.style, + currentStyle, + Matrix, + filter; + + if (!animate) { + elemStyle.zoom = 1; + } + + value = matrix(value); + + // rotate, scale and skew + if (!animate || animate.M) { + Matrix = [ + "Matrix(" + + "M11=" + value[0], + "M12=" + value[2], + "M21=" + value[1], + "M22=" + value[3], + "SizingMethod='auto expand'" + ].join(); + filter = (currentStyle = elem.currentStyle) && currentStyle.filter || elemStyle.filter || ""; + + elemStyle.filter = rMatrix.test(filter) ? + filter.replace(rMatrix, Matrix) : + filter + " progid:DXImageTransform.Microsoft." + Matrix + ")"; + + // center the transform origin, from pbakaus's Transformie http://github.com/pbakaus/transformie + if ((centerOrigin = $.transform.centerOrigin)) { + elemStyle[centerOrigin == 'margin' ? 'marginLeft' : 'left'] = -(elem.offsetWidth / 2) + (elem.clientWidth / 2) + 'px'; + elemStyle[centerOrigin == 'margin' ? 'marginTop' : 'top'] = -(elem.offsetHeight / 2) + (elem.clientHeight / 2) + 'px'; + } + } + + // translate + if (!animate || animate.T) { + // We assume that the elements are absolute positionned inside a relative positionned wrapper + elemStyle.left = value[4] + 'px'; + elemStyle.top = value[5] + 'px'; + } + } + } + } +// populate jQuery.cssHooks with the appropriate hook if necessary + if (propertyHook) { + $.cssHooks[propertyName] = propertyHook; + } +// we need a unique setter for the animation logic + propertyGet = propertyHook && propertyHook.get || $.css; + + /* + * fn.animate() hooks + */ + $.fx.step.transform = function (fx) { + var elem = fx.elem, + start = fx.start, + end = fx.end, + split, + pos = fx.pos, + transform, + translate, + rotate, + scale, + skew, + T = false, + M = false, + prop; + translate = rotate = scale = skew = ''; + + // fx.end and fx.start need to be converted to their translate/rotate/scale/skew components + // so that we can interpolate them + if (!start || typeof start === "string") { + // the following block can be commented out with jQuery 1.5.1+, see #7912 + if (!start) { + start = propertyGet(elem, supportProperty); + } + + // force layout only once per animation + if (supportMatrixFilter) { + elem.style.zoom = 1; + } + + // if the start computed matrix is in end, we are doing a relative animation + split = end.split(start); + if (split.length == 2) { + // remove the start computed matrix to make animations more accurate + end = split.join(''); + fx.origin = start; + start = 'none'; + } + + // start is either 'none' or a matrix(...) that has to be parsed + fx.start = start = start == 'none' ? + { + translate: [0, 0], + rotate: 0, + scale: [1, 1], + skew: [0, 0] + } : + unmatrix(toArray(start)); + + // fx.end has to be parsed and decomposed + fx.end = end = ~end.indexOf('matrix') ? + // bullet-proof parser + unmatrix(matrix(end)) : + // faster and more precise parser + components(end); + + // get rid of properties that do not change + for (prop in start) { + if (prop == 'rotate' ? + start[prop] == end[prop] : + start[prop][0] == end[prop][0] && start[prop][1] == end[prop][1] + ) { + delete start[prop]; + } + } + } + + /* + * We want a fast interpolation algorithm. + * This implies avoiding function calls and sacrifying DRY principle: + * - avoid $.each(function(){}) + * - round values using bitewise hacks, see http://jsperf.com/math-round-vs-hack/3 + */ + if (start.translate) { + // round translate to the closest pixel + translate = ' translate(' + + ((start.translate[0] + (end.translate[0] - start.translate[0]) * pos + .5) | 0) + 'px,' + + ((start.translate[1] + (end.translate[1] - start.translate[1]) * pos + .5) | 0) + 'px' + + ')'; + T = true; + } + if (start.rotate != undefined) { + rotate = ' rotate(' + (start.rotate + (end.rotate - start.rotate) * pos) + 'rad)'; + M = true; + } + if (start.scale) { + scale = ' scale(' + + (start.scale[0] + (end.scale[0] - start.scale[0]) * pos) + ',' + + (start.scale[1] + (end.scale[1] - start.scale[1]) * pos) + + ')'; + M = true; + } + if (start.skew) { + skew = ' skew(' + + (start.skew[0] + (end.skew[0] - start.skew[0]) * pos) + 'rad,' + + (start.skew[1] + (end.skew[1] - start.skew[1]) * pos) + 'rad' + + ')'; + M = true; + } + + // In case of relative animation, restore the origin computed matrix here. + transform = fx.origin ? + fx.origin + translate + skew + scale + rotate : + translate + rotate + scale + skew; + + propertyHook && propertyHook.set ? + propertyHook.set(elem, transform, {M: M, T: T}) : + elem.style[supportProperty] = transform; + }; + + /* + * Utility functions + */ + +// turns a transform string into its 'matrix(A,B,C,D,X,Y)' form (as an array, though) + function matrix(transform) { + transform = transform.split(')'); + var + trim = $.trim + // last element of the array is an empty string, get rid of it + , i = transform.length - 1 + , split, prop, val + , A = 1 + , B = 0 + , C = 0 + , D = 1 + , A_, B_, C_, D_ + , tmp1, tmp2 + , X = 0 + , Y = 0 + ; + // Loop through the transform properties, parse and multiply them + while (i--) { + split = transform[i].split('('); + prop = trim(split[0]); + val = split[1]; + A_ = B_ = C_ = D_ = 0; + + switch (prop) { + case 'translateX': + X += parseInt(val, 10); + continue; + + case 'translateY': + Y += parseInt(val, 10); + continue; + + case 'translate': + val = val.split(','); + X += parseInt(val[0], 10); + Y += parseInt(val[1] || 0, 10); + continue; + + case 'rotate': + val = toRadian(val); + A_ = Math.cos(val); + B_ = Math.sin(val); + C_ = -Math.sin(val); + D_ = Math.cos(val); + break; + + case 'scaleX': + A_ = val; + D_ = 1; + break; + + case 'scaleY': + A_ = 1; + D_ = val; + break; + + case 'scale': + val = val.split(','); + A_ = val[0]; + D_ = val.length > 1 ? val[1] : val[0]; + break; + + case 'skewX': + A_ = D_ = 1; + C_ = Math.tan(toRadian(val)); + break; + + case 'skewY': + A_ = D_ = 1; + B_ = Math.tan(toRadian(val)); + break; + + case 'skew': + A_ = D_ = 1; + val = val.split(','); + C_ = Math.tan(toRadian(val[0])); + B_ = Math.tan(toRadian(val[1] || 0)); + break; + + case 'matrix': + val = val.split(','); + A_ = +val[0]; + B_ = +val[1]; + C_ = +val[2]; + D_ = +val[3]; + X += parseInt(val[4], 10); + Y += parseInt(val[5], 10); + } + // Matrix product + tmp1 = A * A_ + B * C_; + B = A * B_ + B * D_; + tmp2 = C * A_ + D * C_; + D = C * B_ + D * D_; + A = tmp1; + C = tmp2; + } + return [A, B, C, D, X, Y]; + } + +// turns a matrix into its rotate, scale and skew components +// algorithm from http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp + function unmatrix(matrix) { + var + scaleX + , scaleY + , skew + , A = matrix[0] + , B = matrix[1] + , C = matrix[2] + , D = matrix[3] + ; + + // Make sure matrix is not singular + if (A * D - B * C) { + // step (3) + scaleX = Math.sqrt(A * A + B * B); + A /= scaleX; + B /= scaleX; + // step (4) + skew = A * C + B * D; + C -= A * skew; + D -= B * skew; + // step (5) + scaleY = Math.sqrt(C * C + D * D); + C /= scaleY; + D /= scaleY; + skew /= scaleY; + // step (6) + if (A * D < B * C) { + //scaleY = -scaleY; + //skew = -skew; + A = -A; + B = -B; + skew = -skew; + scaleX = -scaleX; + } + + // matrix is singular and cannot be interpolated + } else { + rotate = scaleX = scaleY = skew = 0; + } + + return { + translate: [+matrix[4], +matrix[5]], + rotate: Math.atan2(B, A), + scale: [scaleX, scaleY], + skew: [skew, 0] + } + } + +// parse tranform components of a transform string not containing 'matrix(...)' + function components(transform) { + // split the != transforms + transform = transform.split(')'); + + var translate = [0, 0], + rotate = 0, + scale = [1, 1], + skew = [0, 0], + i = transform.length - 1, + trim = $.trim, + split, name, value; + + // add components + while (i--) { + split = transform[i].split('('); + name = trim(split[0]); + value = split[1]; + + if (name == 'translateX') { + translate[0] += parseInt(value, 10); + + } else if (name == 'translateY') { + translate[1] += parseInt(value, 10); + + } else if (name == 'translate') { + value = value.split(','); + translate[0] += parseInt(value[0], 10); + translate[1] += parseInt(value[1] || 0, 10); + + } else if (name == 'rotate') { + rotate += toRadian(value); + + } else if (name == 'scaleX') { + scale[0] *= value; + + } else if (name == 'scaleY') { + scale[1] *= value; + + } else if (name == 'scale') { + value = value.split(','); + scale[0] *= value[0]; + scale[1] *= (value.length > 1 ? value[1] : value[0]); + + } else if (name == 'skewX') { + skew[0] += toRadian(value); + + } else if (name == 'skewY') { + skew[1] += toRadian(value); + + } else if (name == 'skew') { + value = value.split(','); + skew[0] += toRadian(value[0]); + skew[1] += toRadian(value[1] || '0'); + } + } + + return { + translate: translate, + rotate: rotate, + scale: scale, + skew: skew + }; + } + +// converts an angle string in any unit to a radian Float + function toRadian(value) { + return ~value.indexOf('deg') ? + parseInt(value, 10) * (Math.PI * 2 / 360) : + ~value.indexOf('grad') ? + parseInt(value, 10) * (Math.PI / 200) : + parseFloat(value); + } + +// Converts 'matrix(A,B,C,D,X,Y)' to [A,B,C,D,X,Y] + function toArray(matrix) { + // Fremove the unit of X and Y for Firefox + matrix = /\(([^,]*),([^,]*),([^,]*),([^,]*),([^,p]*)(?:px)?,([^)p]*)(?:px)?/.exec(matrix); + return [matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6]]; + } + + $.transform = { + centerOrigin: 'margin' + }; + +})(jQuery); diff --git a/src/main/webapp/resources/vendor/jplayercircle/js/mod.csstransforms.min.js b/src/main/resources/static/staticResources/vendor/jplayercircle/js/mod.csstransforms.min.js similarity index 100% rename from src/main/webapp/resources/vendor/jplayercircle/js/mod.csstransforms.min.js rename to src/main/resources/static/staticResources/vendor/jplayercircle/js/mod.csstransforms.min.js diff --git a/src/main/webapp/resources/vendor/jquery.min.js b/src/main/resources/static/staticResources/vendor/jquery.min.js similarity index 100% rename from src/main/webapp/resources/vendor/jquery.min.js rename to src/main/resources/static/staticResources/vendor/jquery.min.js diff --git a/src/main/webapp/resources/vendor/leaflet/images/layers-2x.png b/src/main/resources/static/staticResources/vendor/leaflet/images/layers-2x.png similarity index 100% rename from src/main/webapp/resources/vendor/leaflet/images/layers-2x.png rename to src/main/resources/static/staticResources/vendor/leaflet/images/layers-2x.png diff --git a/src/main/webapp/resources/vendor/leaflet/images/layers.png b/src/main/resources/static/staticResources/vendor/leaflet/images/layers.png similarity index 100% rename from src/main/webapp/resources/vendor/leaflet/images/layers.png rename to src/main/resources/static/staticResources/vendor/leaflet/images/layers.png diff --git a/src/main/webapp/resources/vendor/leaflet/images/marker-icon-2x.png b/src/main/resources/static/staticResources/vendor/leaflet/images/marker-icon-2x.png similarity index 100% rename from src/main/webapp/resources/vendor/leaflet/images/marker-icon-2x.png rename to src/main/resources/static/staticResources/vendor/leaflet/images/marker-icon-2x.png diff --git a/src/main/webapp/resources/vendor/leaflet/images/marker-icon.png b/src/main/resources/static/staticResources/vendor/leaflet/images/marker-icon.png similarity index 100% rename from src/main/webapp/resources/vendor/leaflet/images/marker-icon.png rename to src/main/resources/static/staticResources/vendor/leaflet/images/marker-icon.png diff --git a/src/main/webapp/resources/vendor/leaflet/images/marker-shadow.png b/src/main/resources/static/staticResources/vendor/leaflet/images/marker-shadow.png similarity index 100% rename from src/main/webapp/resources/vendor/leaflet/images/marker-shadow.png rename to src/main/resources/static/staticResources/vendor/leaflet/images/marker-shadow.png diff --git a/src/main/resources/static/staticResources/vendor/leaflet/leaflet.css b/src/main/resources/static/staticResources/vendor/leaflet/leaflet.css new file mode 100644 index 0000000..d1f4fee --- /dev/null +++ b/src/main/resources/static/staticResources/vendor/leaflet/leaflet.css @@ -0,0 +1,562 @@ +/* required styles */ + +.leaflet-map-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-pane, +.leaflet-tile-container, +.leaflet-overlay-pane, +.leaflet-shadow-pane, +.leaflet-marker-pane, +.leaflet-popup-pane, +.leaflet-overlay-pane svg, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { + position: absolute; + left: 0; + top: 0; +} + +.leaflet-container { + overflow: hidden; + -ms-touch-action: none; +} + +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; +} + +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; +} + +/* map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container img { + max-width: none !important; +} + +/* stupid Android 2 doesn't understand "max-width: none" properly */ +.leaflet-container img.leaflet-image-layer { + max-width: 15000px !important; +} + +.leaflet-tile { + filter: inherit; + visibility: hidden; +} + +.leaflet-tile-loaded { + visibility: inherit; +} + +.leaflet-zoom-box { + width: 0; + height: 0; +} + +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; +} + +.leaflet-tile-pane { + z-index: 2; +} + +.leaflet-objects-pane { + z-index: 3; +} + +.leaflet-overlay-pane { + z-index: 4; +} + +.leaflet-shadow-pane { + z-index: 5; +} + +.leaflet-marker-pane { + z-index: 6; +} + +.leaflet-popup-pane { + z-index: 7; +} + +.leaflet-vml-shape { + width: 1px; + height: 1px; +} + +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; +} + + +/* control positioning */ + +.leaflet-control { + position: relative; + z-index: 7; + pointer-events: auto; +} + +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; +} + +.leaflet-top { + top: 0; +} + +.leaflet-right { + right: 0; +} + +.leaflet-bottom { + bottom: 0; +} + +.leaflet-left { + left: 0; +} + +.leaflet-control { + float: left; + clear: both; +} + +.leaflet-right .leaflet-control { + float: right; +} + +.leaflet-top .leaflet-control { + margin-top: 10px; +} + +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; +} + +.leaflet-left .leaflet-control { + margin-left: 10px; +} + +.leaflet-right .leaflet-control { + margin-right: 10px; +} + + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-tile, +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; +} + +.leaflet-fade-anim .leaflet-tile-loaded, +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; +} + +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0, 0, 0.25, 1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0, 0, 0.25, 1); + -o-transition: -o-transform 0.25s cubic-bezier(0, 0, 0.25, 1); + transition: transform 0.25s cubic-bezier(0, 0, 0.25, 1); +} + +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile, +.leaflet-touching .leaflet-zoom-animated { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; +} + + +/* cursors */ + +.leaflet-clickable { + cursor: pointer; +} + +.leaflet-container { + cursor: -webkit-grab; + cursor: -moz-grab; +} + +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; +} + +.leaflet-dragging .leaflet-container, +.leaflet-dragging .leaflet-clickable { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; +} + + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline: 0; +} + +.leaflet-container a { + color: #0078A8; +} + +.leaflet-container a.leaflet-active { + outline: 2px solid orange; +} + +.leaflet-zoom-box { + border: 2px dotted #38f; + background: rgba(255, 255, 255, 0.5); +} + + +/* general typography */ +.leaflet-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; +} + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65); + border-radius: 4px; +} + +.leaflet-bar a, +.leaflet-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; +} + +.leaflet-bar a, +.leaflet-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; +} + +.leaflet-bar a:hover { + background-color: #f4f4f4; +} + +.leaflet-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +.leaflet-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; +} + +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; +} + +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + line-height: 30px; +} + + +/* zoom control */ + +.leaflet-control-zoom-in, +.leaflet-control-zoom-out { + font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; +} + +.leaflet-control-zoom-out { + font-size: 20px; +} + +.leaflet-touch .leaflet-control-zoom-in { + font-size: 22px; +} + +.leaflet-touch .leaflet-control-zoom-out { + font-size: 24px; +} + + +/* layers control */ + +.leaflet-control-layers { + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4); + background: #fff; + border-radius: 5px; +} + +.leaflet-control-layers-toggle { + background-image: url(images/layers.png); + width: 36px; + height: 36px; +} + +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; +} + +.leaflet-touch .leaflet-control-layers-toggle { + width: 44px; + height: 44px; +} + +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; +} + +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; +} + +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; +} + +.leaflet-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; +} + +.leaflet-control-layers label { + display: block; +} + +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; +} + + +/* attribution and scale controls */ + +.leaflet-container .leaflet-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.7); + margin: 0; +} + +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; +} + +.leaflet-control-attribution a { + text-decoration: none; +} + +.leaflet-control-attribution a:hover { + text-decoration: underline; +} + +.leaflet-container .leaflet-control-attribution, +.leaflet-container .leaflet-control-scale { + font-size: 11px; +} + +.leaflet-left .leaflet-control-scale { + margin-left: 5px; +} + +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; +} + +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: content-box; + box-sizing: content-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); +} + +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; +} + +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; +} + +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + box-shadow: none; +} + +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 2px solid rgba(0, 0, 0, 0.2); + background-clip: padding-box; +} + + +/* popup */ + +.leaflet-popup { + position: absolute; + text-align: center; +} + +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + border-radius: 12px; +} + +.leaflet-popup-content { + margin: 13px 19px; + line-height: 1.4; +} + +.leaflet-popup-content p { + margin: 18px 0; +} + +.leaflet-popup-tip-container { + margin: 0 auto; + width: 40px; + height: 20px; + position: relative; + overflow: hidden; +} + +.leaflet-popup-tip { + width: 17px; + height: 17px; + padding: 1px; + + margin: -10px auto 0; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} + +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: white; + + box-shadow: 0 3px 14px rgba(0, 0, 0, 0.4); +} + +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + padding: 4px 4px 0 0; + text-align: center; + width: 18px; + height: 14px; + font: 16px/14px Tahoma, Verdana, sans-serif; + color: #c3c3c3; + text-decoration: none; + font-weight: bold; + background: transparent; +} + +.leaflet-container a.leaflet-popup-close-button:hover { + color: #999; +} + +.leaflet-popup-scrolled { + overflow: auto; + border-bottom: 1px solid #ddd; + border-top: 1px solid #ddd; +} + +.leaflet-oldie .leaflet-popup-content-wrapper { + zoom: 1; +} + +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; + + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); +} + +.leaflet-oldie .leaflet-popup-tip-container { + margin-top: -1px; +} + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; +} + + +/* div icon */ + +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; +} diff --git a/src/main/webapp/resources/vendor/leaflet/leaflet.js b/src/main/resources/static/staticResources/vendor/leaflet/leaflet.js similarity index 100% rename from src/main/webapp/resources/vendor/leaflet/leaflet.js rename to src/main/resources/static/staticResources/vendor/leaflet/leaflet.js diff --git a/src/main/webapp/resources/vendor/masonry.pkgd.min.js b/src/main/resources/static/staticResources/vendor/masonry.pkgd.min.js similarity index 100% rename from src/main/webapp/resources/vendor/masonry.pkgd.min.js rename to src/main/resources/static/staticResources/vendor/masonry.pkgd.min.js diff --git a/src/main/webapp/resources/vendor/modernizr-custom.min.js b/src/main/resources/static/staticResources/vendor/modernizr-custom.min.js similarity index 100% rename from src/main/webapp/resources/vendor/modernizr-custom.min.js rename to src/main/resources/static/staticResources/vendor/modernizr-custom.min.js diff --git a/src/main/webapp/WEB-INF/spring/appServlet/controllers.xml b/src/main/webapp/WEB-INF/spring/appServlet/controllers.xml deleted file mode 100644 index 07ed76d..0000000 --- a/src/main/webapp/WEB-INF/spring/appServlet/controllers.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml b/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml deleted file mode 100644 index d9602ae..0000000 --- a/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/spring/root-context.xml b/src/main/webapp/WEB-INF/spring/root-context.xml deleted file mode 100644 index 635bf84..0000000 --- a/src/main/webapp/WEB-INF/spring/root-context.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/main/webapp/WEB-INF/views/error.jsp b/src/main/webapp/WEB-INF/views/error.jsp index b293f15..bedb8c5 100644 --- a/src/main/webapp/WEB-INF/views/error.jsp +++ b/src/main/webapp/WEB-INF/views/error.jsp @@ -1,44 +1,55 @@ -<%@page session="true"%><%@taglib uri="http://www.springframework.org/tags" prefix="sp"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@page session="true" %> +<%@taglib uri="http://www.springframework.org/tags" prefix="sp" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + -LodView — error ${statusCode} - + LodView — error ${statusCode} + -

-
-
-
-
-

${statusCode}

-

-
-
-
-
- - -

-
${endpoint.replaceAll("<>","")}
${error }
<${IRI.replaceAll("([^a-zA-Z0-9])","$1​")}> -

-
- -

-

-

-
- -

-
${endpoint.replaceAll("<>","")}
doesn't contain any information about
<${IRI.replaceAll("([^a-zA-Z0-9])","$1​")}> -

-
-
-
-
- - \ No newline at end of file +
+
+ +
+
+
+
+

${statusCode}

+

+
+
+
+
+ + +

+
+ ${endpoint.replaceAll("<>","")}
${error } +
<${IRI.replaceAll("([^a-zA-Z0-9])","$1​")}> +

+
+ +

+

+

+
+ +

+
+ ${endpoint.replaceAll("<>","")}
doesn't contain any information about
+ <${IRI.replaceAll("([^a-zA-Z0-9])","$1​")}> +

+
+
+
+
+ + diff --git a/src/main/webapp/WEB-INF/views/func/bnodes.jsp b/src/main/webapp/WEB-INF/views/func/bnodes.jsp index 0622535..8bdadd8 100644 --- a/src/main/webapp/WEB-INF/views/func/bnodes.jsp +++ b/src/main/webapp/WEB-INF/views/func/bnodes.jsp @@ -1,14 +1,16 @@ -<%@page session="true" %> - - - \ No newline at end of file +<%@page session="true" %> + + + + diff --git a/src/main/webapp/WEB-INF/views/func/contents.jsp b/src/main/webapp/WEB-INF/views/func/contents.jsp index 114207e..5f95f34 100644 --- a/src/main/webapp/WEB-INF/views/func/contents.jsp +++ b/src/main/webapp/WEB-INF/views/func/contents.jsp @@ -1,16 +1,16 @@ <%@page session="true" %> <% - /* literals */ - %> - - <%@include file="literals.jsp"%> - <% - /* resources */ - %> - - <%@include file="resources.jsp"%> - <% - /* bnodes */ - %> - - <%@include file="bnodes.jsp"%> \ No newline at end of file + /* literals */ +%> + +<%@include file="literals.jsp" %> +<% + /* resources */ +%> + +<%@include file="resources.jsp" %> +<% + /* bnodes */ +%> + +<%@include file="bnodes.jsp" %> diff --git a/src/main/webapp/WEB-INF/views/func/literals.jsp b/src/main/webapp/WEB-INF/views/func/literals.jsp index 4a823c6..0e61456 100644 --- a/src/main/webapp/WEB-INF/views/func/literals.jsp +++ b/src/main/webapp/WEB-INF/views/func/literals.jsp @@ -1,46 +1,50 @@ -<%@page session="true"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> - - - - - - - - - - - - -
multiInLineBlock"> - - - - +<%@page session="true" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + + + + + + + + + + +
multiInLineBlock"> + + + + - - ${ela.getNsDataType().replaceAll("null:(.*)","<$1>")} - - - ${ela.getValue()}  + + ${ela.getNsDataType().replaceAll("null:(.*)","<$1>")} + + + ${ela.getValue()}  - - -
lang ${ela.getLang()}" data-lang="${ela.getLang()}"> -
- - ${ela.getNsDataType().replaceAll("null:(.*)","<$1>")} - - ${ela.getValue()}  -
-
-
-
+ + +
lang ${ela.getLang()}" + data-lang="${ela.getLang()}"> +
+ + ${ela.getNsDataType().replaceAll("null:(.*)","<$1>")} + + ${ela.getValue()}  +
+
+
+ -
-
-
-
\ No newline at end of file +
+
+
+
diff --git a/src/main/webapp/WEB-INF/views/func/resources.jsp b/src/main/webapp/WEB-INF/views/func/resources.jsp index 532b0cb..25f04ff 100644 --- a/src/main/webapp/WEB-INF/views/func/resources.jsp +++ b/src/main/webapp/WEB-INF/views/func/resources.jsp @@ -1,18 +1,23 @@ -<%@page session="true" %> - - - \ No newline at end of file +<%@page session="true" %> + + + + diff --git a/src/main/webapp/WEB-INF/views/home.jsp b/src/main/webapp/WEB-INF/views/home.jsp index 7dae535..68105e9 100644 --- a/src/main/webapp/WEB-INF/views/home.jsp +++ b/src/main/webapp/WEB-INF/views/home.jsp @@ -1,40 +1,54 @@ -<%@page session="true"%><%@taglib uri="http://www.springframework.org/tags" prefix="sp"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@page session="true" %> +<%@taglib uri="http://www.springframework.org/tags" prefix="sp" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + -${results.getTitle()}—LodView - + ${results.getTitle()}—LodView + -
-
- -
-
-
-

- This is your own homepage -

-

-
-
-
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.
-
+
+
+ +
+
+
+

+ This is your own homepage +

+

+
+
+
It is a long established fact that a reader will be distracted by the readable content of + a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal + distribution of letters, as opposed to using 'Content here, content here', making it look like readable + English. +
+
-
+
- + -
+
-
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
+
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has + been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type + and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap + into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the + release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing + software like Aldus PageMaker including versions of Lorem Ipsum. +
-
+
-
- -
- +
+ + + diff --git a/src/main/webapp/WEB-INF/views/inc/custom_footer.jsp b/src/main/webapp/WEB-INF/views/inc/custom_footer.jsp index d87d922..26386ad 100644 --- a/src/main/webapp/WEB-INF/views/inc/custom_footer.jsp +++ b/src/main/webapp/WEB-INF/views/inc/custom_footer.jsp @@ -1,8 +1,8 @@
-
-
-
- - -
\ No newline at end of file +
+
+
+ + + diff --git a/src/main/webapp/WEB-INF/views/inc/footer.jsp b/src/main/webapp/WEB-INF/views/inc/footer.jsp index 4f89c60..13c1adb 100644 --- a/src/main/webapp/WEB-INF/views/inc/footer.jsp +++ b/src/main/webapp/WEB-INF/views/inc/footer.jsp @@ -1,32 +1,39 @@ -<%@page session="true"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><%@taglib uri="http://www.springframework.org/tags" prefix="sp"%>