implement first version of sparql client interface
authorymh <ymh.work@gmail.com>
Mon, 31 Oct 2016 14:24:23 +0100
changeset 386 c731ab9b934d
parent 385 f8200c5482ec
child 387 7fba86fa8604
implement first version of sparql client interface
.hgignore
build/build.sh
server/src/app/Facades/RdfHelperFacade.php
server/src/app/Helpers/RdfHelper.php
server/src/app/Http/Controllers/Sparql/SparqlClientController.php
server/src/app/Libraries/Sparql/SparqlQueryAnalyser.php
server/src/app/Libraries/Utils.php
server/src/app/Providers/RdfHelperServiceProvider.php
server/src/bower.json
server/src/config/app.php
server/src/gulpfile.js
server/src/public/css/app.css
server/src/public/img/favicon.ico
server/src/public/img/logoCNRS.jpg
server/src/public/img/logoIRI.jpg
server/src/public/img/logo_corpus.png
server/src/public/img/marianne.jpg
server/src/resources/assets/js/sparqlclient.js
server/src/resources/assets/sass/_app-core.scss
server/src/resources/assets/sass/_variables.scss
server/src/resources/assets/sass/app.scss
server/src/resources/assets/sass/img/banner_img_reloaded.jpg
server/src/resources/views/app.blade.php
server/src/resources/views/base.blade.php
server/src/resources/views/sparql/sparqlClientForm.blade.php
server/src/resources/views/sparql/sparqlClientResultBase.blade.php
server/src/resources/views/sparql/sparqlClientResultBoolean.blade.php
server/src/resources/views/sparql/sparqlClientResultList.blade.php
server/src/routes/web.php
server/src/tests/Libraries/Sparql/SparqlQueryAnalyserTest.php
server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/ask.sparql
server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/base.sparql
server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/graph.sparql
server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/limit_offset.sparql
server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/prefixes.sparql
server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/select.sparql
server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/unknown.sparql
--- a/.hgignore	Sun Oct 23 01:07:09 2016 +0200
+++ b/.hgignore	Mon Oct 31 14:24:23 2016 +0100
@@ -33,7 +33,7 @@
 ^server/src/storage/framework/views/
 ^server/node_modules
 ^server/src/public/css
-^server/src/public/js/vendor
+^server/src/public/js
 ^server/src/public/fonts
 ^server/src/public/corpus-app
 ^server/bo_client/.dir-locals.el$
--- a/build/build.sh	Sun Oct 23 01:07:09 2016 +0200
+++ b/build/build.sh	Mon Oct 31 14:24:23 2016 +0100
@@ -92,8 +92,14 @@
 [[ -z "$environment" ]] && environment='production'
 
 case $environment in
-    development) build_option='--dev' ;;
-    *) build_option='--prod' ;;
+    development)
+        build_option='--dev'
+        build_option_back='--development'
+        ;;
+    *)
+        build_option='--prod'
+        build_option_back='--production'
+        ;;
 esac
 
 echo "environment: $environment"
@@ -113,7 +119,7 @@
 pushd ../server/src
 version=$(sed -n "s/[[:space:]]*\'version\'[[:space:]]*=>[[:space:]]*\'\([\.0-9]*\)\'/\1/p" config/version.php)
 version=${version:-0.0.0}
-./node_modules/.bin/gulp copy-build ${build_option}
+./node_modules/.bin/gulp copy-build ${build_option_back}
 popd
 echoblue "---> buiding back done"
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/app/Facades/RdfHelperFacade.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,12 @@
+<?php
+namespace CorpusParole\Facades;
+
+use Illuminate\Support\Facades\Facade;
+
+class RdfHelperFacade extends Facade {
+
+    protected static function getFacadeAccessor() {
+        return 'RdfHelper';
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/app/Helpers/RdfHelper.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,130 @@
+<?php
+namespace CorpusParole\Helpers;
+
+use EasyRdf\Resource;
+use EasyRdf\Literal;
+use EasyRdf\RdfNamespace;
+
+use EasyRdf\Serialiser\Turtle;
+
+/**
+ * Globally Inspired by EasyRdf\Serialiser\Turtle::serialiseResource
+ */
+class RdfHelper {
+
+    private $prefixes = [];
+
+    public function getPrefixes() {
+        return $this->prefixes;
+    }
+
+    /**
+     * Keep track of the prefixes used while serialising
+     * @ignore
+     */
+    public function addPrefix($qname)
+    {
+        list ($prefix) = explode(':', $qname);
+        $this->prefixes[$prefix] = true;
+        return $prefix;
+    }
+
+
+    /**
+     * Given a IRI string, escape and enclose in angle brackets.
+     *
+     * @param  string $resourceIri
+     *
+     * @return string
+     */
+    public function escapeIri($resourceIri) {
+        $escapedIri = str_replace('>', '\\>', $resourceIri);
+        return "<span class=\"corpus-rdf-resource\"><span class=\"corpus-rdf-resource-delimiter\">&lt;</span><a class=\"corpus-rdf-resource-link\" href=\"$resourceIri\" target=\"_blank\">".htmlentities($escapedIri)."</a><span class=\"corpus-rdf-resource-delimiter\">&gt;</span></span>";
+    }
+
+    /**
+     * Insprired by EasyRdf\Serialiser\Turtle::serialiseLiteral
+     * Given a string, enclose in quotes and escape any quotes in the string.
+     * Strings containing tabs, linefeeds or carriage returns will be
+     * enclosed in three double quotes (""").
+     *
+     * @param  string $value
+     *
+     * @return string
+     */
+    public function serialiseLiteral($literal)
+    {
+        $value = strval($literal);
+        $quoted = Turtle::quotedString($value);
+
+        if ($datatype = $literal->getDatatypeUri()) {
+            if ($datatype == 'http://www.w3.org/2001/XMLSchema#integer') {
+                return sprintf('<span class="corpus-rdf-literal corpus-rdf-literal-value">%d</span>', $value);
+            } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#decimal') {
+                return sprintf('<span class="corpus-rdf-literal corpus-rdf-literal-value">%s</span>', $value);
+            } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#double') {
+                return sprintf('<span class="corpus-rdf-literal corpus-rdf-literal-value">%e</span>', $value);
+            } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#boolean') {
+                return sprintf('<span class="corpus-rdf-literal corpus-rdf-literal-value">%s</span>', $value);
+            } else {
+                $escaped = $this->serialiseResource($datatype, false);
+                return sprintf('<span class="corpus-rdf-literal"><span class="corpus-rdf-literal-value corpus-rdf-literal-value-str">%s</span><span class="corpus-rdf-literal-type-separator">^^</span><span class="corpus-rdf-literal-type">%s</span></span>', $quoted, $escaped);
+            }
+        } elseif ($lang = $literal->getLang()) {
+            return sprintf('<span class="corpus-rdf-literal"><span class="corpus-rdf-literal-value corpus-rdf-literal-value-str">%s</span><span class="corpus-rdf-literal-lang-separator">@</span><span class="corpus-rdf-literal-lang">%s</span></span>',$quoted, $lang);
+        } else {
+            return "<span class=\"corpus-rdf-literal corpus-rdf-literal-value\">$quoted</span>";
+        }
+    }
+
+    /**
+     * Given a an EasyRdf\Resource or URI, convert it into a string, suitable to
+     * be written to a Turtle document. URIs will be shortened into CURIES
+     * where possible.
+     *
+     * @param  Resource|string $resource The resource to convert to a Turtle string
+     * @param  boolean $createNamespace  If true, a new namespace may be created
+     *
+     * @return string
+     */
+    public function serialiseResource($resource, $createNamespace = false)
+    {
+        if (is_object($resource)) {
+            if ($resource->isBNode()) {
+                $uri = preg_replace("/^_\:/", "", $resource->getUri(), 1);
+                return "<span class=\"corpus-rdf-blank-node\"><span class=\"corpus-rdf-blank-node-prefix\">_:</span>".htmlentities($uri)."</span>";
+            }
+
+            $resource = $resource->getUri();
+        }
+
+        $short = RdfNamespace::shorten($resource, $createNamespace);
+
+        if ($short) {
+            $prefix = $this->addPrefix($short);
+            $shortVal = substr($short, strlen($prefix)+1);
+            $namespaceVal = RdfNamespace::namespaces()[$prefix];
+
+            return "<span class=\"corpus-rdf-resource\"><span class=\"corpus-rdf-resource-prefix\" title=\"$namespaceVal\">$prefix</span><span class=\"corpus-rdf-resource-short-separator\">:</span><a class=\"corpus-rdf-resource-link\" href=\"$resource\" target=\"_blank\" title=\"$resource\">".htmlentities($shortVal)."</a></span>";
+        }
+
+        return $this->escapeIri($resource);
+    }
+
+
+    public function serialiseValue($val) {
+
+        if($val instanceof Resource) {
+            return $this->serialiseResource($val, false);
+        } elseif($val instanceof Literal) {
+            return $this->serialiseLiteral($val);
+        } else {
+            throw new \InvalidArgumentException(
+                "serialiseObject() requires \$object to be ".
+                'of type EasyRdf\Resource or EasyRdf\Literal'
+            );
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/app/Http/Controllers/Sparql/SparqlClientController.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,215 @@
+<?php
+
+namespace CorpusParole\Http\Controllers\Sparql;
+
+use Log;
+
+use Illuminate\Http\Request;
+use GuzzleHttp\Client;
+use EasyRdf\Sparql\Result as SparqlResult;
+use EasyRdf\Graph;
+use EasyRdf\RdfNamespace;
+
+use CorpusParole\Http\Controllers\Controller;
+use CorpusParole\Libraries\Utils;
+
+use CorpusParole\Libraries\Sparql\SparqlClient;
+use CorpusParole\Libraries\Sparql\SparqlQueryAnalyser;
+use RdfHelper;
+
+class SparqlClientController extends Controller
+{
+
+    const HEADERS_FORWARDED = [ "host", "user-agent", "accept", "accept-language", "accept-encoding", "connection" ];
+
+    const SELECT_RESULT_FORMAT = [
+        "SPARQL/CSV" => "text/csv",
+        "SPARQL/JSON" => "application/sparql-results+json",
+        "SPARQL/XML" => "application/sparql-results+xml",
+        "SPARQL/TSV" => "text/tab-separated-values",
+        "BINARY" => "application/x-binary-rdf-results-table"
+    ];
+
+    const GRAPH_RESULT_FORMAT = [
+        "N-Triples" => "application/n-triples",
+        "RDF/XML" => "application/rdf+xml",
+        "Turtle" => "text/turtle",
+        "N3" => "text/n3",
+        "RDF/JSON" => "application/rdf+json",
+        "TriG" => "application/trig",
+        "N-Quads" => "application/n-quads",
+        "BinaryRDF" => "application/x-binary-rdf",
+        "TriX" => "application/trix",
+        "JSON-LD" => "application/ld+json"
+    ];
+
+
+    private $sparqlClient;
+    private $httpClient;
+
+    public function __construct(SparqlClient $sparqlClient, Client $httpClient) {
+        $this->sparqlClient = $sparqlClient;
+        $this->httpClient = $httpClient;
+    }
+
+    // display form
+    public function index() {
+        return view('sparql/sparqlClientForm');
+    }
+
+    private function querySelect(Request $request, $query, $analyser) {
+        $countResult = $this->sparqlClient->query($analyser->getCountQuery());
+        $countField = $countResult->getFields()[0];
+        $count = $countResult->current()->$countField;
+        $docs = $this->sparqlClient->query($query);
+        $fields = $docs->getFields();
+        $results = [];
+        foreach($docs as $row) {
+            $results[] = array_reduce($fields, function($res, $field) use ($row) {
+                if(isset($row->$field)) {
+                    $res[$field] = RdfHelper::serialiseValue($row->$field);
+                } else {
+                    $res[$field] = "<span class=\"corpus-rdf-blank-value\">&nbsp;</span>";
+                }
+                return $res;
+            }, []);
+        }
+        $namespaces = array_reduce(array_keys(RdfHelper::getPrefixes()), function($res, $p) {
+            $res[$p] = RdfNamespace::namespaces()[$p];
+            return $res;
+        }, []);
+        $data = [
+            'query' => $query,
+            'count' => $count,
+            'fields' => $fields,
+            'fieldPrefix' => "?",
+            'results' => $results,
+            'namespaces' => $namespaces,
+            'downloadFormats' => self::SELECT_RESULT_FORMAT
+        ];
+        $view = 'sparql/sparqlClientResultList';
+        return [$view, $data];
+    }
+
+    private function queryGraph(Request $request, $query, $analyser) {
+
+        $docs = $this->sparqlClient->query($query);
+        $fields = ["subject", "predicate", "object"];
+        $results = [];
+        foreach ($docs->resources() as $resource ) {
+            foreach ($resource->propertyUris() as $property) {
+                $propertyResource = $docs->resource($property);
+                foreach ($resource->all($propertyResource) as $value) {
+                    $results[] = [
+                        'subject' => RdfHelper::serialiseValue($resource),
+                        'predicate'=> RdfHelper::serialiseValue($propertyResource),
+                        'object'=> RdfHelper::serialiseValue($value)
+                    ];
+                }
+            }
+        }
+        $namespaces = array_reduce(array_keys(RdfHelper::getPrefixes()), function($res, $p) {
+            $res[$p] = RdfNamespace::namespaces()[$p];
+            return $res;
+        }, []);
+
+
+        $data = [
+            'query' => $query,
+            'count' => count($results),
+            'fields' => $fields,
+            'fieldPrefix' => "",
+            'results' => $results,
+            'namespaces' => $namespaces,
+            'downloadFormats' => self::GRAPH_RESULT_FORMAT
+        ];
+        $view = 'sparql/sparqlClientResultList';
+
+        return [$view, $data];
+    }
+
+    private function queryAsk(Request $request, $query, $analyser) {
+        $data = [
+            'results' => $this->sparqlClient->query($query),
+            'namespaces' => $analyser->getPrefixes()
+        ];
+
+        $view = 'sparql/sparqlClientResultBoolean';
+        return [$view, $data];
+    }
+
+    private function showHtml(Request $request) {
+
+        $query = $request->input('query');
+
+        $analyser = new SparqlQueryAnalyser($query);
+
+        $queryType = $analyser->getQueryType();
+
+        $namespaces = $analyser->getPrefixes();
+
+        foreach($namespaces as $prefix => $nUri) {
+            RdfNamespace::set($prefix,$nUri);
+        }
+
+        if($queryType === SparqlQueryAnalyser::SELECT_QUERY) {
+            list($view, $data) = $this->querySelect($request, $query, $analyser);
+        } elseif($queryType === SparqlQueryAnalyser::GRAPH_QUERY) {
+            list($view, $data) = $this->queryGraph($request, $query, $analyser);
+        } elseif($queryType === SparqlQueryAnalyser::ASK_QUERY) {
+            list($view, $data) = $this->queryAsk($request, $query, $analyser);
+        } else {
+            //return 500
+            abort(500, "Serialization format unknown");
+        }
+
+        return view($view, $data);
+
+    }
+
+
+    private function proxyQuery(Request $request, $format=null) {
+        $query = $request->input('query');
+        $headers = [];
+        foreach (self::HEADERS_FORWARDED as $h) {
+            $headerValue = $request->header($h);
+            if($headerValue) {
+                $headers[$h] = $headerValue;
+            }
+        }
+
+        if(!empty($format)){
+            $headers['Accept'] = $format;
+        }
+
+        $sesameResp = $this->httpClient->get(config('corpusparole.sesame_query_url'), ['query' => $request->all(), 'headers' => $headers]);
+
+        $resp = response((string)$sesameResp->getBody(), $sesameResp->getStatusCode());
+        foreach ($sesameResp->getHeaders() as $name => $values) {
+            if($name != 'Transfer-Encoding') {
+                $resp->header($name, $values);
+            }
+        }
+
+        return $resp;
+    }
+
+    // display result
+    public function show(Request $request) {
+
+        $format = $request->input('format');
+
+        if($format === 'text/html') {
+            return $this->showHtml($request);
+        } else {
+            return $this->proxyQuery($request, $format);
+        }
+
+    }
+
+    // do the query
+    public function query(Request $request) {
+        return $this->proxyQuery($request);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/app/Libraries/Sparql/SparqlQueryAnalyser.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,126 @@
+<?php
+namespace CorpusParole\Libraries\Sparql;
+
+use CorpusParole\Libraries\Utils;
+
+class SparqlQueryAnalyser {
+
+    const SPARQL_PREFIX_BASE_REGEXP = '(((?:prefix\s+([\p{L}-\d]+)\s*\:)|base)\s*\<((?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?)\>)\s*';
+
+    const SPARQL_SELECT_QUERY_REGEXP = '^(?:\s*(?:'.self::SPARQL_PREFIX_BASE_REGEXP.')*select)';
+    const SPARQL_ASK_QUERY_REGEXP = '^(?:\s*(?:'.self::SPARQL_PREFIX_BASE_REGEXP.')*ask)';
+    const SPARQL_GRAPH_QUERY_REGEXP = '^(?:\s*(?:'.self::SPARQL_PREFIX_BASE_REGEXP.')*(?:(?:construct)|(?:describe)))';
+
+    const SPARQL_LIMIT_OFFSET_QUERY_REGEXP = '(?:(?:(limit\s+(\d+))|(offset\s+(\d+)))\s*)+\s*$';
+
+    const UNKNOWN_QUERY = 0;
+    const SELECT_QUERY = 1;
+    const GRAPH_QUERY = 2;
+    const ASK_QUERY = 2;
+
+    private $query;
+    private $queryType = false;
+    private $rawPrefixes = false;
+    private $prefixes = false;
+    private $limit = false;
+    private $offset = false;
+    private $rawQuery = false;
+    private $countVar = false;
+
+    public function __construct($query) {
+        $this->query = $query;
+    }
+
+    public function getQueryType() {
+
+        if($this->queryType === false) {
+            if(preg_match("%".self::SPARQL_SELECT_QUERY_REGEXP."%iu", $this->query) === 1) {
+                $this->queryType = self::SELECT_QUERY;
+            } elseif(preg_match("%".self::SPARQL_GRAPH_QUERY_REGEXP."%iu", $this->query) === 1) {
+                $this->queryType = self::GRAPH_QUERY;
+            } elseif(preg_match("%".self::SPARQL_ASK_QUERY_REGEXP."%iu", $this->query) === 1) {
+                $this->queryType = self::ASK_QUERY;
+            } else {
+                $this->queryType = self::UNKNOWN_QUERY;
+            }
+        }
+        return $this->queryType;
+    }
+
+    private function extractPrefix() {
+        $prefixes = [];
+        $rawPrefixes = [];
+        $res = preg_replace_callback("%".self::SPARQL_PREFIX_BASE_REGEXP."%iu", function($m) use (&$prefixes, &$rawPrefixes) {
+            $rawPrefixes[] = trim($m[0]);
+            $prefixes[$m[3]?$m[3]:""] = $m[4];
+            return "";
+        }, $this->query);
+
+        return [$rawPrefixes, $prefixes, trim($res)];
+    }
+
+    public function getRawPrefixes() {
+        if($this->rawPrefixes === false) {
+            list($this->rawPrefixes, $this->prefixes, $this->rawQuery) = $this->extractPrefix();
+        }
+        return $this->rawPrefixes;
+    }
+
+    public function getPrefixes() {
+        if($this->prefixes === false) {
+            list($this->rawPrefixes, $this->prefixes, $this->rawQuery) = $this->extractPrefix();
+        }
+        return $this->prefixes;
+    }
+
+    public function getRawQuery() {
+        if($this->rawQuery === false) {
+            list($this->rawPrefixes, $this->prefixes, $this->rawQuery) = $this->extractPrefix();
+        }
+        return $this->rawQuery;
+    }
+
+    public function getCountVar() {
+        if($this->countVar === false) {
+            $this->countVar = "?count_cp_".hash('md5', $this->query);
+        }
+        return $this->countVar;
+    }
+
+    public function getCountQuery() {
+        return implode(" ", $this->getRawPrefixes())." select (count(*) as ".$this->getCountVar().") { ".$this->getRawQuery()." }";
+    }
+
+    private function setLimitOffset() {
+        if(preg_match("%".self::SPARQL_LIMIT_OFFSET_QUERY_REGEXP."%iu", $this->query, $m) === 1) {
+            for($i=0;$i<(count($m)-1)/2;$i++) {
+                if(Utils::startsWith(strtolower($m[2*$i+1]), "limit")) {
+                    $this->limit = intval($m[$i*2+2]);
+                } elseif (Utils::startsWith(strtolower($m[2*$i+1]), "offset")) {
+                    $this->offset = intval($m[$i*2+2]);
+                }
+            }
+        }
+        if($this->limit === false) {
+            $this->limit = null;
+        }
+        if($this->offset === false) {
+            $this->offset = null;
+        }
+    }
+
+    public function getLimit() {
+        if($this->limit === false) {
+            $this->setLimitOffset();
+        }
+        return $this->limit;
+    }
+
+    public function getOffset() {
+        if($this->offset === false) {
+            $this->setLimitOffset();
+        }
+        return $this->offset;
+    }
+
+}
--- a/server/src/app/Libraries/Utils.php	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/app/Libraries/Utils.php	Mon Oct 31 14:24:23 2016 +0100
@@ -11,6 +11,7 @@
  */
 class Utils {
 
+
     /**
      * convert DateIntervals to milliseconds
      * Months and year calculated by approximation based on average number
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/app/Providers/RdfHelperServiceProvider.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,24 @@
+<?php
+
+namespace CorpusParole\Providers;
+
+use Illuminate\Support\ServiceProvider;
+use CorpusParole\Helpers\RdfHelper;
+
+/**
+ * guzzle Service Provider, inspired by https://github.com/urakozz/laravel-guzzle
+ */
+class RdfHelperServiceProvider extends ServiceProvider
+{
+    /**
+     * Register the RdfHelper provider
+     *
+     * @return CorpusParole\Helpers\RdfHelper
+     */
+    public function register()
+    {
+        $this->app->singleton('RdfHelper', function() {
+            return new RdfHelper();
+        });
+    }
+}
--- a/server/src/bower.json	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/bower.json	Mon Oct 31 14:24:23 2016 +0100
@@ -20,6 +20,8 @@
   "dependencies": {
     "bootstrap-sass": "bootstrap-sass-official#~3.3.5",
     "bourbon": "~4.2.4",
-    "font-awesome": "~4.4.0"
+    "font-awesome": "~4.4.0",
+    "yasqe": "yasgui-yasqe#^2.11.4",
+    "yasr": "yasgui-yasr#^2.9.0"
   }
 }
--- a/server/src/config/app.php	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/config/app.php	Mon Oct 31 14:24:23 2016 +0100
@@ -160,7 +160,7 @@
         'CorpusParole\Providers\GeonamesServiceProvider',
         'CorpusParole\Providers\VersionServiceProvider',
         'CorpusParole\Providers\PaginationServiceProvider',
-
+        'CorpusParole\Providers\RdfHelperServiceProvider',
     ],
 
     /*
@@ -212,6 +212,7 @@
         'Html' => 'Collective\Html\HtmlFacade',
         'Form' => 'Collective\Html\FormFacade',
         'Guzzle' => 'CorpusParole\Facades\GuzzleFacade',
+        'RdfHelper' => 'CorpusParole\Facades\RdfHelperFacade',
     ],
 
 ];
--- a/server/src/gulpfile.js	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/gulpfile.js	Mon Oct 31 14:24:23 2016 +0100
@@ -9,9 +9,9 @@
 var options = minimist(process.argv.slice(2));
 
 var buildOption = "--prod";
-if(options.prod) {
+if(options.production) {
     buildOption = "--prod";
-} else if(options.dev) {
+} else if(options.development) {
     buildOption = "--dev";
 }
 
@@ -52,9 +52,15 @@
 
 elixir(function(mix) {
     mix.sass('app.scss', 'public/css/app.css', {includePaths: [paths['bootstrap']+'stylesheets/']})
+        .scripts(['sparqlclient.js'], 'public/js/sparqlclient.js')
+        .copy('resources/assets/sass/img', 'public/css/img')
         .copy(paths.bootstrap + 'fonts/bootstrap', 'public/fonts')
         .copy(paths.bootstrap + 'javascripts/bootstrap.js', 'public/js/vendor/bootstrap.js')
+        .copy(paths.bower_base_path + 'yasqe/dist/yasqe.bundled.min.js', 'public/js/vendor/yasqe.bundled.min.js')
+        .copy(paths.bower_base_path + 'yasr/dist/yasr.bundled.min.js', 'public/js/vendor/yasr.bundled.min.js')
         .copy(paths.bower_base_path + 'jquery/dist/jquery.min.js', 'public/js/vendor/jquery.js')
+        .copy(paths.bower_base_path + 'yasqe/dist/yasqe.min.css', 'public/css/vendor/yasqe.min.css')
+        .copy(paths.bower_base_path + 'yasr/dist/yasr.min.css', 'public/css/vendor/yasr.min.css')
         .copy(paths.bower_base_path + 'font-awesome/css/font-awesome.min.css', 'public/css/vendor/font-awesome.css')
         .task('build-ember')
         .task('copy-bo-ember');
--- a/server/src/public/css/app.css	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/public/css/app.css	Mon Oct 31 14:24:23 2016 +0100
@@ -1131,43 +1131,165 @@
   cursor: pointer; }
 
 h1, h2, h3, h4, h5, h6,
-.h1, .h2, .h3, .doc_details_title, .h4, .content-main-title, .data-audio-title, .h5, .content-title, .h6 {
+.h1, .h2, .h3, .doc_details_title, .corpus-rdf-boolean, .h4, .content-main-title, .data-audio-title, .h5, .content-title, .h6 {
   font-family: inherit;
   font-weight: 500;
   line-height: 1.1;
   color: inherit; }
   h1 small,
-  h1 .small, h2 small,
-  h2 .small, h3 small,
-  h3 .small, h4 small,
-  h4 .small, h5 small,
-  h5 .small, h6 small,
+  h1 .small,
+  h1 .corpus-rdf-literal-type-separator,
+  h1
+  .corpus-rdf-literal-type,
+  h1 .corpus-rdf-literal-lang,
+  h1
+  .corpus-rdf-literal-lang-separator, h2 small,
+  h2 .small,
+  h2 .corpus-rdf-literal-type-separator,
+  h2
+  .corpus-rdf-literal-type,
+  h2 .corpus-rdf-literal-lang,
+  h2
+  .corpus-rdf-literal-lang-separator, h3 small,
+  h3 .small,
+  h3 .corpus-rdf-literal-type-separator,
+  h3
+  .corpus-rdf-literal-type,
+  h3 .corpus-rdf-literal-lang,
+  h3
+  .corpus-rdf-literal-lang-separator, h4 small,
+  h4 .small,
+  h4 .corpus-rdf-literal-type-separator,
+  h4
+  .corpus-rdf-literal-type,
+  h4 .corpus-rdf-literal-lang,
+  h4
+  .corpus-rdf-literal-lang-separator, h5 small,
+  h5 .small,
+  h5 .corpus-rdf-literal-type-separator,
+  h5
+  .corpus-rdf-literal-type,
+  h5 .corpus-rdf-literal-lang,
+  h5
+  .corpus-rdf-literal-lang-separator, h6 small,
   h6 .small,
+  h6 .corpus-rdf-literal-type-separator,
+  h6
+  .corpus-rdf-literal-type,
+  h6 .corpus-rdf-literal-lang,
+  h6
+  .corpus-rdf-literal-lang-separator,
   .h1 small,
-  .h1 .small, .h2 small,
-  .h2 .small, .h3 small, .doc_details_title small,
-  .h3 .small, .doc_details_title .small, .h4 small, .content-main-title small, .data-audio-title small,
-  .h4 .small, .content-main-title .small, .data-audio-title .small, .h5 small, .content-title small,
-  .h5 .small, .content-title .small, .h6 small,
-  .h6 .small {
+  .h1 .small,
+  .h1 .corpus-rdf-literal-type-separator,
+  .h1
+  .corpus-rdf-literal-type,
+  .h1 .corpus-rdf-literal-lang,
+  .h1
+  .corpus-rdf-literal-lang-separator, .h2 small,
+  .h2 .small,
+  .h2 .corpus-rdf-literal-type-separator,
+  .h2
+  .corpus-rdf-literal-type,
+  .h2 .corpus-rdf-literal-lang,
+  .h2
+  .corpus-rdf-literal-lang-separator, .h3 small, .doc_details_title small, .corpus-rdf-boolean small,
+  .h3 .small, .doc_details_title .small, .corpus-rdf-boolean .small,
+  .h3 .corpus-rdf-literal-type-separator, .doc_details_title .corpus-rdf-literal-type-separator, .corpus-rdf-boolean .corpus-rdf-literal-type-separator,
+  .h3
+  .corpus-rdf-literal-type, .doc_details_title
+  .corpus-rdf-literal-type, .corpus-rdf-boolean
+  .corpus-rdf-literal-type,
+  .h3 .corpus-rdf-literal-lang, .doc_details_title .corpus-rdf-literal-lang, .corpus-rdf-boolean .corpus-rdf-literal-lang,
+  .h3
+  .corpus-rdf-literal-lang-separator, .doc_details_title
+  .corpus-rdf-literal-lang-separator, .corpus-rdf-boolean
+  .corpus-rdf-literal-lang-separator, .h4 small, .content-main-title small, .data-audio-title small,
+  .h4 .small, .content-main-title .small, .data-audio-title .small,
+  .h4 .corpus-rdf-literal-type-separator, .content-main-title .corpus-rdf-literal-type-separator, .data-audio-title .corpus-rdf-literal-type-separator,
+  .h4
+  .corpus-rdf-literal-type, .content-main-title
+  .corpus-rdf-literal-type, .data-audio-title
+  .corpus-rdf-literal-type,
+  .h4 .corpus-rdf-literal-lang, .content-main-title .corpus-rdf-literal-lang, .data-audio-title .corpus-rdf-literal-lang,
+  .h4
+  .corpus-rdf-literal-lang-separator, .content-main-title
+  .corpus-rdf-literal-lang-separator, .data-audio-title
+  .corpus-rdf-literal-lang-separator, .h5 small, .content-title small,
+  .h5 .small, .content-title .small,
+  .h5 .corpus-rdf-literal-type-separator, .content-title .corpus-rdf-literal-type-separator,
+  .h5
+  .corpus-rdf-literal-type, .content-title
+  .corpus-rdf-literal-type,
+  .h5 .corpus-rdf-literal-lang, .content-title .corpus-rdf-literal-lang,
+  .h5
+  .corpus-rdf-literal-lang-separator, .content-title
+  .corpus-rdf-literal-lang-separator, .h6 small,
+  .h6 .small,
+  .h6 .corpus-rdf-literal-type-separator,
+  .h6
+  .corpus-rdf-literal-type,
+  .h6 .corpus-rdf-literal-lang,
+  .h6
+  .corpus-rdf-literal-lang-separator {
     font-weight: normal;
     line-height: 1;
     color: #777777; }
 
 h1, .h1,
 h2, .h2,
-h3, .h3, .doc_details_title {
+h3, .h3, .doc_details_title, .corpus-rdf-boolean {
   margin-top: 20px;
   margin-bottom: 10px; }
   h1 small,
-  h1 .small, .h1 small,
+  h1 .small,
+  h1 .corpus-rdf-literal-type-separator,
+  h1
+  .corpus-rdf-literal-type,
+  h1 .corpus-rdf-literal-lang,
+  h1
+  .corpus-rdf-literal-lang-separator, .h1 small,
   .h1 .small,
+  .h1 .corpus-rdf-literal-type-separator,
+  .h1
+  .corpus-rdf-literal-type,
+  .h1 .corpus-rdf-literal-lang,
+  .h1
+  .corpus-rdf-literal-lang-separator,
   h2 small,
-  h2 .small, .h2 small,
+  h2 .small,
+  h2 .corpus-rdf-literal-type-separator,
+  h2
+  .corpus-rdf-literal-type,
+  h2 .corpus-rdf-literal-lang,
+  h2
+  .corpus-rdf-literal-lang-separator, .h2 small,
   .h2 .small,
+  .h2 .corpus-rdf-literal-type-separator,
+  .h2
+  .corpus-rdf-literal-type,
+  .h2 .corpus-rdf-literal-lang,
+  .h2
+  .corpus-rdf-literal-lang-separator,
   h3 small,
-  h3 .small, .h3 small, .doc_details_title small,
-  .h3 .small, .doc_details_title .small {
+  h3 .small,
+  h3 .corpus-rdf-literal-type-separator,
+  h3
+  .corpus-rdf-literal-type,
+  h3 .corpus-rdf-literal-lang,
+  h3
+  .corpus-rdf-literal-lang-separator, .h3 small, .doc_details_title small, .corpus-rdf-boolean small,
+  .h3 .small, .doc_details_title .small, .corpus-rdf-boolean .small,
+  .h3 .corpus-rdf-literal-type-separator, .doc_details_title .corpus-rdf-literal-type-separator, .corpus-rdf-boolean .corpus-rdf-literal-type-separator,
+  .h3
+  .corpus-rdf-literal-type, .doc_details_title
+  .corpus-rdf-literal-type, .corpus-rdf-boolean
+  .corpus-rdf-literal-type,
+  .h3 .corpus-rdf-literal-lang, .doc_details_title .corpus-rdf-literal-lang, .corpus-rdf-boolean .corpus-rdf-literal-lang,
+  .h3
+  .corpus-rdf-literal-lang-separator, .doc_details_title
+  .corpus-rdf-literal-lang-separator, .corpus-rdf-boolean
+  .corpus-rdf-literal-lang-separator {
     font-size: 65%; }
 
 h4, .h4, .content-main-title, .data-audio-title,
@@ -1176,14 +1298,56 @@
   margin-top: 10px;
   margin-bottom: 10px; }
   h4 small,
-  h4 .small, .h4 small, .content-main-title small, .data-audio-title small,
+  h4 .small,
+  h4 .corpus-rdf-literal-type-separator,
+  h4
+  .corpus-rdf-literal-type,
+  h4 .corpus-rdf-literal-lang,
+  h4
+  .corpus-rdf-literal-lang-separator, .h4 small, .content-main-title small, .data-audio-title small,
   .h4 .small, .content-main-title .small, .data-audio-title .small,
+  .h4 .corpus-rdf-literal-type-separator, .content-main-title .corpus-rdf-literal-type-separator, .data-audio-title .corpus-rdf-literal-type-separator,
+  .h4
+  .corpus-rdf-literal-type, .content-main-title
+  .corpus-rdf-literal-type, .data-audio-title
+  .corpus-rdf-literal-type,
+  .h4 .corpus-rdf-literal-lang, .content-main-title .corpus-rdf-literal-lang, .data-audio-title .corpus-rdf-literal-lang,
+  .h4
+  .corpus-rdf-literal-lang-separator, .content-main-title
+  .corpus-rdf-literal-lang-separator, .data-audio-title
+  .corpus-rdf-literal-lang-separator,
   h5 small,
-  h5 .small, .h5 small, .content-title small,
+  h5 .small,
+  h5 .corpus-rdf-literal-type-separator,
+  h5
+  .corpus-rdf-literal-type,
+  h5 .corpus-rdf-literal-lang,
+  h5
+  .corpus-rdf-literal-lang-separator, .h5 small, .content-title small,
   .h5 .small, .content-title .small,
+  .h5 .corpus-rdf-literal-type-separator, .content-title .corpus-rdf-literal-type-separator,
+  .h5
+  .corpus-rdf-literal-type, .content-title
+  .corpus-rdf-literal-type,
+  .h5 .corpus-rdf-literal-lang, .content-title .corpus-rdf-literal-lang,
+  .h5
+  .corpus-rdf-literal-lang-separator, .content-title
+  .corpus-rdf-literal-lang-separator,
   h6 small,
-  h6 .small, .h6 small,
-  .h6 .small {
+  h6 .small,
+  h6 .corpus-rdf-literal-type-separator,
+  h6
+  .corpus-rdf-literal-type,
+  h6 .corpus-rdf-literal-lang,
+  h6
+  .corpus-rdf-literal-lang-separator, .h6 small,
+  .h6 .small,
+  .h6 .corpus-rdf-literal-type-separator,
+  .h6
+  .corpus-rdf-literal-type,
+  .h6 .corpus-rdf-literal-lang,
+  .h6
+  .corpus-rdf-literal-lang-separator {
     font-size: 75%; }
 
 h1, .h1 {
@@ -1192,7 +1356,7 @@
 h2, .h2 {
   font-size: 30px; }
 
-h3, .h3, .doc_details_title {
+h3, .h3, .doc_details_title, .corpus-rdf-boolean {
   font-size: 24px; }
 
 h4, .h4, .content-main-title, .data-audio-title {
@@ -1217,7 +1381,11 @@
       font-size: 21px; } }
 
 small,
-.small {
+.small,
+.corpus-rdf-literal-type-separator,
+.corpus-rdf-literal-type,
+.corpus-rdf-literal-lang,
+.corpus-rdf-literal-lang-separator {
   font-size: 85%; }
 
 mark,
@@ -1405,14 +1573,26 @@
     margin-bottom: 0; }
   blockquote footer,
   blockquote small,
-  blockquote .small {
+  blockquote .small,
+  blockquote .corpus-rdf-literal-type-separator,
+  blockquote
+  .corpus-rdf-literal-type,
+  blockquote .corpus-rdf-literal-lang,
+  blockquote
+  .corpus-rdf-literal-lang-separator {
     display: block;
     font-size: 80%;
     line-height: 1.42857;
     color: #777777; }
     blockquote footer:before,
     blockquote small:before,
-    blockquote .small:before {
+    blockquote .small:before,
+    blockquote .corpus-rdf-literal-type-separator:before,
+    blockquote
+    .corpus-rdf-literal-type:before,
+    blockquote .corpus-rdf-literal-lang:before,
+    blockquote
+    .corpus-rdf-literal-lang-separator:before {
       content: '\2014 \00A0'; }
 
 .blockquote-reverse,
@@ -1425,16 +1605,40 @@
   .blockquote-reverse footer:before,
   .blockquote-reverse small:before,
   .blockquote-reverse .small:before,
+  .blockquote-reverse .corpus-rdf-literal-type-separator:before,
+  .blockquote-reverse
+  .corpus-rdf-literal-type:before,
+  .blockquote-reverse .corpus-rdf-literal-lang:before,
+  .blockquote-reverse
+  .corpus-rdf-literal-lang-separator:before,
   blockquote.pull-right footer:before,
   blockquote.pull-right small:before,
-  blockquote.pull-right .small:before {
+  blockquote.pull-right .small:before,
+  blockquote.pull-right .corpus-rdf-literal-type-separator:before,
+  blockquote.pull-right
+  .corpus-rdf-literal-type:before,
+  blockquote.pull-right .corpus-rdf-literal-lang:before,
+  blockquote.pull-right
+  .corpus-rdf-literal-lang-separator:before {
     content: ''; }
   .blockquote-reverse footer:after,
   .blockquote-reverse small:after,
   .blockquote-reverse .small:after,
+  .blockquote-reverse .corpus-rdf-literal-type-separator:after,
+  .blockquote-reverse
+  .corpus-rdf-literal-type:after,
+  .blockquote-reverse .corpus-rdf-literal-lang:after,
+  .blockquote-reverse
+  .corpus-rdf-literal-lang-separator:after,
   blockquote.pull-right footer:after,
   blockquote.pull-right small:after,
-  blockquote.pull-right .small:after {
+  blockquote.pull-right .small:after,
+  blockquote.pull-right .corpus-rdf-literal-type-separator:after,
+  blockquote.pull-right
+  .corpus-rdf-literal-type:after,
+  blockquote.pull-right .corpus-rdf-literal-lang:after,
+  blockquote.pull-right
+  .corpus-rdf-literal-lang-separator:after {
     content: '\00A0 \2014'; }
 
 address {
@@ -2074,10 +2278,10 @@
   .table-bordered > thead > tr > td {
     border-bottom-width: 2px; }
 
-.table-striped > tbody > tr:nth-of-type(odd) {
+.table-striped > tbody > tr:nth-of-type(odd), table.resultsTable > tbody > tr:nth-of-type(odd) {
   background-color: #f9f9f9; }
 
-.table-hover > tbody > tr:hover {
+.table-hover > tbody > tr:hover, table.resultsTable > tbody > tr:hover {
   background-color: #f5f5f5; }
 
 table col[class*="col-"] {
@@ -2105,11 +2309,11 @@
 .table > tfoot > tr.active > th {
   background-color: #f5f5f5; }
 
-.table-hover > tbody > tr > td.active:hover,
-.table-hover > tbody > tr > th.active:hover,
-.table-hover > tbody > tr.active:hover > td,
-.table-hover > tbody > tr:hover > .active,
-.table-hover > tbody > tr.active:hover > th {
+.table-hover > tbody > tr > td.active:hover, table.resultsTable > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover, table.resultsTable > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td, table.resultsTable > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active, table.resultsTable > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th, table.resultsTable > tbody > tr.active:hover > th {
   background-color: #e8e8e8; }
 
 .table > thead > tr > td.success,
@@ -2126,11 +2330,11 @@
 .table > tfoot > tr.success > th {
   background-color: #dff0d8; }
 
-.table-hover > tbody > tr > td.success:hover,
-.table-hover > tbody > tr > th.success:hover,
-.table-hover > tbody > tr.success:hover > td,
-.table-hover > tbody > tr:hover > .success,
-.table-hover > tbody > tr.success:hover > th {
+.table-hover > tbody > tr > td.success:hover, table.resultsTable > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover, table.resultsTable > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td, table.resultsTable > tbody > tr.success:hover > td,
+.table-hover > tbody > tr:hover > .success, table.resultsTable > tbody > tr:hover > .success,
+.table-hover > tbody > tr.success:hover > th, table.resultsTable > tbody > tr.success:hover > th {
   background-color: #d0e9c6; }
 
 .table > thead > tr > td.info,
@@ -2147,11 +2351,11 @@
 .table > tfoot > tr.info > th {
   background-color: #d9edf7; }
 
-.table-hover > tbody > tr > td.info:hover,
-.table-hover > tbody > tr > th.info:hover,
-.table-hover > tbody > tr.info:hover > td,
-.table-hover > tbody > tr:hover > .info,
-.table-hover > tbody > tr.info:hover > th {
+.table-hover > tbody > tr > td.info:hover, table.resultsTable > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover, table.resultsTable > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td, table.resultsTable > tbody > tr.info:hover > td,
+.table-hover > tbody > tr:hover > .info, table.resultsTable > tbody > tr:hover > .info,
+.table-hover > tbody > tr.info:hover > th, table.resultsTable > tbody > tr.info:hover > th {
   background-color: #c4e3f3; }
 
 .table > thead > tr > td.warning,
@@ -2168,11 +2372,11 @@
 .table > tfoot > tr.warning > th {
   background-color: #fcf8e3; }
 
-.table-hover > tbody > tr > td.warning:hover,
-.table-hover > tbody > tr > th.warning:hover,
-.table-hover > tbody > tr.warning:hover > td,
-.table-hover > tbody > tr:hover > .warning,
-.table-hover > tbody > tr.warning:hover > th {
+.table-hover > tbody > tr > td.warning:hover, table.resultsTable > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover, table.resultsTable > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td, table.resultsTable > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr:hover > .warning, table.resultsTable > tbody > tr:hover > .warning,
+.table-hover > tbody > tr.warning:hover > th, table.resultsTable > tbody > tr.warning:hover > th {
   background-color: #faf2cc; }
 
 .table > thead > tr > td.danger,
@@ -2189,11 +2393,11 @@
 .table > tfoot > tr.danger > th {
   background-color: #f2dede; }
 
-.table-hover > tbody > tr > td.danger:hover,
-.table-hover > tbody > tr > th.danger:hover,
-.table-hover > tbody > tr.danger:hover > td,
-.table-hover > tbody > tr:hover > .danger,
-.table-hover > tbody > tr.danger:hover > th {
+.table-hover > tbody > tr > td.danger:hover, table.resultsTable > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover, table.resultsTable > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td, table.resultsTable > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr:hover > .danger, table.resultsTable > tbody > tr:hover > .danger,
+.table-hover > tbody > tr.danger:hover > th, table.resultsTable > tbody > tr.danger:hover > th {
   background-color: #ebcccc; }
 
 .table-responsive {
@@ -3903,23 +4107,23 @@
       margin-right: 0; } }
 
 .navbar-default {
-  background-color: #f8f8f8;
-  border-color: #e7e7e7; }
+  background-color: #0085cb;
+  border-color: #006faa; }
   .navbar-default .navbar-brand {
-    color: #777; }
+    color: white; }
     .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus {
-      color: #5e5e5e;
+      color: #e6e6e6;
       background-color: transparent; }
   .navbar-default .navbar-text {
-    color: #777; }
+    color: white; }
   .navbar-default .navbar-nav > li > a {
-    color: #777; }
+    color: white; }
     .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus {
-      color: #333;
+      color: #e6e6e6;
       background-color: transparent; }
   .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus {
     color: #555;
-    background-color: #e7e7e7; }
+    background-color: #006faa; }
   .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus {
     color: #ccc;
     background-color: transparent; }
@@ -3931,30 +4135,30 @@
       background-color: #888; }
   .navbar-default .navbar-collapse,
   .navbar-default .navbar-form {
-    border-color: #e7e7e7; }
+    border-color: #006faa; }
   .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
-    background-color: #e7e7e7;
+    background-color: #006faa;
     color: #555; }
   @media (max-width: 767px) {
     .navbar-default .navbar-nav .open .dropdown-menu > li > a {
-      color: #777; }
+      color: white; }
       .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
-        color: #333;
+        color: #e6e6e6;
         background-color: transparent; }
     .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
       color: #555;
-      background-color: #e7e7e7; }
+      background-color: #006faa; }
     .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
       color: #ccc;
       background-color: transparent; } }
   .navbar-default .navbar-link {
-    color: #777; }
+    color: white; }
     .navbar-default .navbar-link:hover {
-      color: #333; }
+      color: #e6e6e6; }
   .navbar-default .btn-link {
-    color: #777; }
+    color: white; }
     .navbar-default .btn-link:hover, .navbar-default .btn-link:focus {
-      color: #333; }
+      color: #e6e6e6; }
     .navbar-default .btn-link[disabled]:hover, .navbar-default .btn-link[disabled]:focus,
     fieldset[disabled] .navbar-default .btn-link:hover,
     fieldset[disabled] .navbar-default .btn-link:focus {
@@ -4524,11 +4728,29 @@
   border-color: #337ab7; }
   .list-group-item.active .list-group-item-heading,
   .list-group-item.active .list-group-item-heading > small,
-  .list-group-item.active .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading,
+  .list-group-item.active .list-group-item-heading > .small,
+  .list-group-item.active .list-group-item-heading > .corpus-rdf-literal-type-separator,
+  .list-group-item.active .list-group-item-heading >
+  .corpus-rdf-literal-type,
+  .list-group-item.active .list-group-item-heading > .corpus-rdf-literal-lang,
+  .list-group-item.active .list-group-item-heading >
+  .corpus-rdf-literal-lang-separator, .list-group-item.active:hover .list-group-item-heading,
   .list-group-item.active:hover .list-group-item-heading > small,
-  .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading,
+  .list-group-item.active:hover .list-group-item-heading > .small,
+  .list-group-item.active:hover .list-group-item-heading > .corpus-rdf-literal-type-separator,
+  .list-group-item.active:hover .list-group-item-heading >
+  .corpus-rdf-literal-type,
+  .list-group-item.active:hover .list-group-item-heading > .corpus-rdf-literal-lang,
+  .list-group-item.active:hover .list-group-item-heading >
+  .corpus-rdf-literal-lang-separator, .list-group-item.active:focus .list-group-item-heading,
   .list-group-item.active:focus .list-group-item-heading > small,
-  .list-group-item.active:focus .list-group-item-heading > .small {
+  .list-group-item.active:focus .list-group-item-heading > .small,
+  .list-group-item.active:focus .list-group-item-heading > .corpus-rdf-literal-type-separator,
+  .list-group-item.active:focus .list-group-item-heading >
+  .corpus-rdf-literal-type,
+  .list-group-item.active:focus .list-group-item-heading > .corpus-rdf-literal-lang,
+  .list-group-item.active:focus .list-group-item-heading >
+  .corpus-rdf-literal-lang-separator {
     color: inherit; }
   .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text {
     color: #c7ddef; }
@@ -4664,8 +4886,20 @@
   .panel-title > a,
   .panel-title > small,
   .panel-title > .small,
+  .panel-title > .corpus-rdf-literal-type-separator,
+  .panel-title >
+  .corpus-rdf-literal-type,
+  .panel-title > .corpus-rdf-literal-lang,
+  .panel-title >
+  .corpus-rdf-literal-lang-separator,
   .panel-title > small > a,
-  .panel-title > .small > a {
+  .panel-title > .small > a,
+  .panel-title > .corpus-rdf-literal-type-separator > a,
+  .panel-title >
+  .corpus-rdf-literal-type > a,
+  .panel-title > .corpus-rdf-literal-lang > a,
+  .panel-title >
+  .corpus-rdf-literal-lang-separator > a {
     color: inherit; }
 
 .panel-footer {
@@ -5736,4 +5970,95 @@
 .doc_details_title {
   margin-top: 0; }
 
+.navbar-default .navbar-collapse {
+  background: #0085cb url("img/banner_img_reloaded.jpg") no-repeat 0 -89px; }
+
+.navbar > .container .navbar-brand {
+  font-weight: bold;
+  display: inline-block;
+  width: 120px;
+  margin-top: 8px;
+  margin-left: 0px;
+  height: auto; }
+
+.navbar-nav {
+  font-weight: bold;
+  color: white; }
+
+footer {
+  margin-top: 15px; }
+
+footer section.partenariat .row > div:nth-child(2) {
+  text-align: right;
+  line-height: 50px; }
+
+footer section.partenariat {
+  border-radius: 5px;
+  box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
+  padding: 20px 15px;
+  background: #fff; }
+
+section.partenariat label {
+  font-weight: bold; }
+
+footer .region-footer #block-menu-menu-footer-menu {
+  font-size: 90%;
+  color: #8A8A8A; }
+
+footer .region-footer #block-menu-menu-footer-menu .row {
+  padding: 0 15px; }
+
+footer .region-footer #block-menu-menu-footer-menu .menu li {
+  display: inline-block;
+  margin-top: 5px;
+  margin-bottom: 5px;
+  border-right: 1px solid #DEDDDD; }
+
+footer .region-footer #block-menu-menu-footer-menu .menu li a {
+  padding-top: 4px;
+  padding-bottom: 4px;
+  color: #8A8A8A; }
+
+footer .region-footer #block-menu-menu-footer-menu .menu li:first-child a {
+  padding-left: 0; }
+
+footer .region-footer #block-menu-menu-footer-menu div.row div:nth-child(1) {
+  padding-left: 0; }
+
+footer .region-footer #block-menu-menu-footer-menu div.row div:nth-child(2) {
+  padding-right: 0;
+  text-align: right;
+  line-height: 39px; }
+
+#query-form #timeout {
+  max-width: 150px; }
+
+.corpus-rdf-resource-prefix,
+.result-bindings-namespaces-prefix {
+  color: #f50; }
+
+.corpus-rdf-resource-link, .corpus-rdf-resource-delimiter {
+  color: #085; }
+
+.corpus-rdf-literal-value, .corpus-rdf-literal-value-str {
+  color: #a11; }
+
+.corpus-rdf-literal-type-separator,
+.corpus-rdf-literal-type {
+  color: #777777; }
+
+.corpus-rdf-literal-lang,
+.corpus-rdf-literal-lang-separator {
+  color: #219; }
+
+.result-bindings-heading {
+  margin-top: 0;
+  margin-bottom: 0; }
+
+.corpus-rdf-boolean-true {
+  color: #085; }
+
+.corpus-rdf-boolean-false {
+  color: #a11; }
+
 /*# sourceMappingURL=app.css.map */
Binary file server/src/public/img/favicon.ico has changed
Binary file server/src/public/img/logoCNRS.jpg has changed
Binary file server/src/public/img/logoIRI.jpg has changed
Binary file server/src/public/img/logo_corpus.png has changed
Binary file server/src/public/img/marianne.jpg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/resources/assets/js/sparqlclient.js	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,103 @@
+// sparql edit function
+
+// ASK, null
+const ASK_RESULT_FORMAT = [
+    ["HTML", "text/html"]
+];
+
+// SELECT
+const SELECT_RESULT_FORMAT = [
+    ["HTML", "text/html"],
+    ["SPARQL/CSV", "text/csv"],
+    ["SPARQL/JSON", "application/sparql-results+json"],
+    ["SPARQL/XML", "application/sparql-results+xml"],
+    ["SPARQL/TSV", "text/tab-separated-values"],
+    ["BINARY", "application/x-binary-rdf-results-table"]
+];
+
+// DESCRIBE,CONSTRUCT
+const GRAPH_RESULT_FORMAT = [
+    ["HTML", "text/html"],
+    ["N-Triples", "application/n-triples"],
+    ["RDF/XML", "application/rdf+xml"],
+    ["Turtle", "text/turtle"],
+    ["N3", "text/n3"],
+    ["RDF/JSON", "application/rdf+json"],
+    ["TriG", "application/trig"],
+    ["N-Quads", "application/n-quads"],
+    ["BinaryRDF", "application/x-binary-rdf"],
+    ["TriX", "application/trix"],
+    ["JSON-LD", "application/ld+json"]
+];
+
+function setOutputFormatSelect(selectElt, optionsList) {
+    var keys = $.map(optionsList, function(o) { return o[0];});
+    var selectedKey = $(":selected",selectElt).text();
+    if(!selectedKey || $.inArray(selectedKey, keys) === -1) {
+        selectedKey = "HTML";
+    }
+    selectElt.empty();
+    var innerHtml = "";
+    $.each(optionsList, function(i, o) {
+        innerHtml += "<option value=\""+o[1]+"\""+ ((o[0]==selectedKey)?" selected=\"selected\" ":"") +">"+o[0]+"<options>";
+    });
+
+    selectElt.append(innerHtml);
+}
+
+var currentQueryType = null;
+
+function updateQueryType(queryType) {
+    var resultFormats = ASK_RESULT_FORMAT;
+    if(currentQueryType != queryType) {
+        if(queryType == "SELECT") {
+            resultFormats = SELECT_RESULT_FORMAT;
+        } else if (queryType == "DESCRIBE" || queryType == "CONSTRUCT") {
+            resultFormats = GRAPH_RESULT_FORMAT;
+        }
+        setOutputFormatSelect($("#format"),resultFormats);
+        currentQueryType = queryType;
+    }
+}
+
+function getSubmitState(editor) {
+    var parseErrors = $(".parseErrorIcon").length;
+    return editor.getQueryMode() === 'query' && parseErrors == 0 && !/^\s*$/.test(editor.getValue());
+}
+
+function initSparqlEditor() {
+    var yasqe = YASQE.fromTextArea($('#query').get(0), {
+        sparql: {
+            showQueryButton: false,
+            //endpoint: "{{ route('sparql_proxy') }}",
+            //requestMethod: "GET",
+            //acceptHeaderGraph: "application/rdf+json,/;q=0.9"
+            //acceptHeaderGraph: "text/turtle,/;q=0.9",
+            //acceptHeaderSelect: "application/x-turtle,/;q=0.9",
+        }
+    });
+    yasqe.on("update", function(instance) {
+        var queryType = instance.getQueryType();
+        updateQueryType(queryType);
+        var submitState = getSubmitState(instance);
+        $("#submit-query-form").prop('disabled', !submitState );
+        $("#format").prop('disabled', !submitState );
+        $("#timeout").prop('disabled', !submitState );
+    });
+
+    $("#query-form").submit(function() {
+        return getSubmitState(yasqe);
+    });
+
+    $("#limits-choices a").click(function(e) {
+        e.preventDefault();
+        $('#limit').val($(e.target).text());
+    });
+
+    // var yasr = YASR($('#results').get(0), {
+    //     getUsedPrefixes: yasqe.getPrefixesFromQuery,
+    //     useGoogleCharts: false,
+    //     outputPlugins: ["table", "error", "boolean", "rawResponse", "pivot"],
+    // });
+    // yasqe.options.sparql.callbacks.complete = yasr.setResponse;
+}
--- a/server/src/resources/assets/sass/_app-core.scss	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/resources/assets/sass/_app-core.scss	Mon Oct 31 14:24:23 2016 +0100
@@ -1,7 +1,3 @@
-$font-family-sans-serif: "Roboto", Helvetica, Arial, sans-serif !default;
-$btn-font-weight: 300;
-@import "bootstrap";
-
 
 body, label, .checkbox label {
 	font-weight: 300;
@@ -22,3 +18,136 @@
 .content-title {
 	@extend .h5;
 }
+
+table.resultsTable {
+    @extend .table-striped;
+    @extend .table-hover;
+}
+
+.navbar-default .navbar-collapse {
+    background: $navbar-default-bg url('img/banner_img_reloaded.jpg') no-repeat 0 -89px;
+}
+
+.navbar > .container .navbar-brand {
+    font-weight: bold;
+    display: inline-block;
+    width: 120px;
+    margin-top: 8px;
+    margin-left: 0px;
+    height: auto;
+}
+
+.navbar-nav {
+    font-weight: bold;
+    color: $navbar-default-color;
+}
+
+footer {
+    margin-top: 15px;
+}
+
+footer section.partenariat .row > div:nth-child(2) {
+    text-align: right;
+    line-height: 50px;
+}
+
+footer section.partenariat {
+    border-radius: 5px;
+    box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
+    padding: 20px 15px;
+    background: #fff;
+}
+
+section.partenariat label {
+    font-weight: bold;
+}
+
+footer .region-footer #block-menu-menu-footer-menu {
+    font-size: 90%;
+    color: #8A8A8A;
+}
+
+footer .region-footer #block-menu-menu-footer-menu .row {
+    padding: 0 15px;
+}
+
+footer .region-footer #block-menu-menu-footer-menu .menu li {
+    display: inline-block;
+    margin-top: 5px;
+    margin-bottom: 5px;
+    border-right: 1px solid #DEDDDD;
+}
+
+footer .region-footer #block-menu-menu-footer-menu .menu li a {
+    padding-top: 4px;
+    padding-bottom: 4px;
+    color: #8A8A8A;
+}
+
+footer .region-footer #block-menu-menu-footer-menu .menu li:first-child a {
+    padding-left: 0;
+}
+
+footer .region-footer #block-menu-menu-footer-menu div.row div:nth-child(1) {
+    padding-left: 0;
+}
+
+footer .region-footer #block-menu-menu-footer-menu div.row div:nth-child(2) {
+    padding-right: 0;
+    text-align: right;
+    line-height: 39px;
+}
+
+#query-form #timeout {
+    max-width: 150px;
+}
+
+// corpus-rdf-blank-value
+// corpus-rdf-blank-node
+// corpus-rdf-blank-node-prefix
+// corpus-rdf-resource
+// corpus-rdf-resource-short-separator
+.corpus-rdf-resource-prefix,
+.result-bindings-namespaces-prefix {
+    color: #f50;
+}
+
+.corpus-rdf-resource-link, .corpus-rdf-resource-delimiter {
+    color: #085;
+}
+// corpus-rdf-literal
+//
+.corpus-rdf-literal-value, .corpus-rdf-literal-value-str {
+    color: #a11;
+}
+
+// corpus-rdf-literal-uri
+
+.corpus-rdf-literal-type-separator,
+.corpus-rdf-literal-type {
+    @extend .small;
+    color: $gray-light;
+}
+
+.corpus-rdf-literal-lang,
+.corpus-rdf-literal-lang-separator {
+    @extend .small;
+    color: #219;
+}
+
+.result-bindings-heading {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+.corpus-rdf-boolean {
+    @extend .h3;
+}
+
+.corpus-rdf-boolean-true {
+    color: #085;
+}
+
+.corpus-rdf-boolean-false {
+    color: #a11;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/resources/assets/sass/_variables.scss	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,8 @@
+
+$font-family-sans-serif: "Roboto", Helvetica, Arial, sans-serif !default;
+$btn-font-weight: 300;
+$navbar-default-color: white;
+$navbar-default-brand-color: white;
+$navbar-default-bg: #0085cb;
+$navbar-default-link-color: white;
+$navbar-default-link-hover-color: darken($navbar-default-link-color, 10%);
--- a/server/src/resources/assets/sass/app.scss	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/resources/assets/sass/app.scss	Mon Oct 31 14:24:23 2016 +0100
@@ -1,1 +1,6 @@
+
+@import "variables";
+
+@import "bootstrap";
+
 @import "app-core";
Binary file server/src/resources/assets/sass/img/banner_img_reloaded.jpg has changed
--- a/server/src/resources/views/app.blade.php	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/resources/views/app.blade.php	Mon Oct 31 14:24:23 2016 +0100
@@ -1,77 +1,21 @@
-@inject('version', 'version')
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    @section('meta')
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    @show
-    <title>@yield('title','Laravel')</title>
-
-    @section('css-assets')
-    <link href="{{ asset('/css/app.css') }}" rel="stylesheet">
-
-    <!-- Fonts -->
-    <link href="{{ asset('/css/fonts.css')}}" rel='stylesheet' type='text/css'>
-    @show
+@extends('base')
 
-    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
-    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
-    <!--[if lt IE 9]>
-        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
-        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
-    <![endif]-->
-</head>
-<body>
-    <nav class="navbar navbar-default">
-        <div class="container-fluid">
-            <div class="navbar-header">
-                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
-                    <span class="sr-only">Toggle Navigation</span>
-                    <span class="icon-bar"></span>
-                    <span class="icon-bar"></span>
-                    <span class="icon-bar"></span>
-                </button>
-                <a class="navbar-brand" href="/">Corpus de la Parole</a>
-            </div>
+@section('nav')
+<ul class="nav navbar-nav">
+    <li><a href="{{ url('/bo/docs/') }}">Accueil B.O.</a></li>
+</ul>
 
-            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
-                <ul class="nav navbar-nav">
-                    @section('nav-bar')
-                    <li><a href="{{ url('/bo/docs/') }}">Accueil B.O.</a></li>
-                    @show
-                </ul>
+<ul class="nav navbar-nav navbar-right">
+    @if (Auth::guest())
+        <li><a href="{{ url('/auth/login') }}">Connexion</a></li>
+    @else
+        <li class="dropdown">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ Auth::user()->name }} <span class="caret"></span></a>
+            <ul class="dropdown-menu" role="menu">
+                <li><a href="{{ url('/auth/logout') }}">Déconnexion</a></li>
+            </ul>
+        </li>
+    @endif
+</ul>
 
-                <ul class="nav navbar-nav navbar-right">
-                    @if (Auth::guest())
-                        <li><a href="{{ url('/auth/login') }}">Connexion</a></li>
-                    @else
-                        <li class="dropdown">
-                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ Auth::user()->name }} <span class="caret"></span></a>
-                            <ul class="dropdown-menu" role="menu">
-                                <li><a href="{{ url('/auth/logout') }}">Déconnexion</a></li>
-                            </ul>
-                        </li>
-                    @endif
-                </ul>
-            </div>
-        </div>
-    </nav>
-
-    @yield('content')
-
-    <footer class="footer">
-      <div class="container-fluid">
-        @yield('footer')
-        <p class="text-muted small text-right">Réalisé par <a href="http://www.iri.centrepompidou.fr" target="_blank">IRI</a> - version {{ $version->version() }}</p>
-      </div>
-    </footer>
-
-    @section('js-assets')
-    <!-- Scripts -->
-    <script src="{{ asset('/js/vendor/jquery.js') }}"></script>
-    <script src="{{ asset('/js/vendor/bootstrap.js') }}"></script>
-    @show
-</body>
-</html>
+@endsection
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/resources/views/base.blade.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,88 @@
+@inject('version', 'version')
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    @section('meta')
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    @show
+    <title>@yield('title','Corpus de la Parole')</title>
+
+    <link rel="shortcut icon" href="{{ asset('/img/favicon.ico') }}" type="image/vnd.microsoft.icon" />
+
+    @section('css-assets')
+    <link href="{{ asset('/css/app.css') }}" rel="stylesheet">
+
+    <!-- Fonts -->
+    <link href="{{ asset('/css/fonts.css')}}" rel='stylesheet' type='text/css'>
+    @show
+
+</head>
+<body>
+    <header id="navbar" role="banner" class="navbar container-fluid navbar-default col-lg-12">
+    @section('header')
+        <div class="container">
+            <div class="navbar-header">
+                <a class="logo navbar-btn pull-left" href="{{ route('root') }}" title="Accueil">
+                    <img src="{{ asset('/img/logo_corpus.png') }}" alt="Accueil">
+                </a>
+                <a class="name navbar-brand" href="{{ route('root') }}" title="Accueil">Corpus de la parole</a>
+            </div>
+            <div class="navbar-collapse collapse">
+                @yield('nav')
+            </div>
+        </div>
+    @show
+    </header>
+
+    <div class="container-fluid">
+    @yield('content')
+    </div>
+
+    <footer class="footer container-fluid">
+        @section('footer')
+        <section class="partenariat">
+            <div class="row">
+                <div class="col-xs-6">
+                    <a href="http://www.culturecommunication.gouv.fr/" title="Ministère de la Culture et de la communication">
+                        <img src="{{ asset('/img/marianne.jpg') }}" alt="Logo du Ministère de la Culture et de la communication" width="39" height="50">
+                    </a>
+                </div>
+                <div class="col-xs-6">
+                    <label>En partenariat avec</label>
+                    <a href="http://www.iri.centrepompidou.fr/" title="IRI">
+                        <img src="{{ asset('/img/logoIRI.jpg') }}" alt="Logo dde l'IRI" width="75" height="50">
+                    </a>
+                    <a href="http://www.cnrs.fr/" title="CNRS">
+                        <img src="{{ asset('/img/logoCNRS.jpg') }}" alt="Logo du CNRS" width="75" height="50">
+                    </a>
+                </div>
+            </div>
+        </section>
+        <div class="region region-footer">
+        <section id="block-menu-menu-footer-menu" class="block block-menu contextual-links-region clearfix">
+            <div class="row">
+                <div class="col-sm-12 col-md-8">
+                    <ul id="footer_menu" class="menu nav">
+                        <li class="first leaf"><a href="/contactez-nous" title="">Contact</a></li>
+                        <li class="leaf"><a href="/credits">Crédits</a></li>
+                        <li class="last leaf"><a href="/mentions-legales">Mentions légales</a></li>
+                        <li><a href="/sitemap">Plan du site</a></li>
+                    </ul>
+                </div>
+                <div class="col-sm-12 col-md-4">version {{ $version->version() }} ©&nbsp;Ministère de la culture et de la communication</div>
+            </div>
+        </section>
+        </div>
+        @show
+    </footer>
+
+    @section('js-assets')
+    <!-- Scripts -->
+    <script src="{{ asset('/js/vendor/jquery.js') }}"></script>
+    <script src="{{ asset('/js/vendor/bootstrap.js') }}"></script>
+    @show
+
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/resources/views/sparql/sparqlClientForm.blade.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,98 @@
+@extends('base')
+
+@section('css-assets')
+<link href="{{ asset('/css/vendor/yasqe.min.css')}}" rel='stylesheet' type='text/css'>
+<link href="{{ asset('/css/vendor/yasr.min.css')}}" rel='stylesheet' type='text/css'>
+@parent
+@endsection
+
+
+@section('nav')
+<ul class="nav navbar-nav">
+    <li><a href="{{ route('sparql_form') }}">Client SPARQL</a></li>
+</ul>
+@endsection
+
+@section('js-assets')
+@parent
+    <script src="{{ asset('/js/vendor/yasqe.bundled.min.js') }}"></script>
+    <script src="{{ asset('/js/sparqlclient.js') }}"></script>
+    <script>
+    $(function() {
+        initSparqlEditor();
+        // var yasqe = YASQE.fromTextArea($('#query').get(0), {
+        //     sparql: {
+        //         showQueryButton: false,
+        //         //endpoint: "{{ route('sparql_proxy') }}",
+        //         //requestMethod: "GET",
+        //         //acceptHeaderGraph: "application/rdf+json,/;q=0.9"
+        //         //acceptHeaderGraph: "text/turtle,/;q=0.9",
+        //         //acceptHeaderSelect: "application/x-turtle,/;q=0.9",
+        //     }
+        // });
+        // // var yasr = YASR($('#results').get(0), {
+        // //     getUsedPrefixes: yasqe.getPrefixesFromQuery,
+        // //     useGoogleCharts: false,
+        // //     outputPlugins: ["table", "error", "boolean", "rawResponse", "pivot"],
+        // // });
+        // // yasqe.options.sparql.callbacks.complete = yasr.setResponse;
+    });
+    </script>
+@endsection
+
+@section('content')
+<div class="row">
+    <div class="col-md-12">
+        <form action="{{ route('sparql_query') }}" method="get" id="query-form">
+            <fieldset>
+                <div class="form-group row">
+                    <div class="col-md-12">
+                    <label for="query">Requête</label><br />
+                    <textarea rows="18" cols="80" name="query" id="query" class="form-control">select distinct ?Concept where {[] a ?Concept} LIMIT 100</textarea>
+                    </div>
+                </div>
+                <div class="row">
+                    <div class="form-group col-md-2">
+                        <label for="format" class="n control-label">Format du résultat</label>
+                        <select name="format" id="format" class="form-control">
+                            <option value="text/html" selected="selected">HTML</option>
+                        </select>
+                    </div>
+                    <div class="form-group col-md-2">
+                        <label for="timeout" class="n control-label">Timeout</label>
+                        <input name="timeout" id="timeout" class="form-control" type="text" value="0" />
+                        <span class=" help-block"> milliseconds <i>(values less than 1000 are ignored)</i></span>
+                    </div>
+                    <div class="form-group col-md-2">
+                        <label for="limit" class="n control-label">Limite</label>
+                        <div class="input-group">
+                            <input name="limit" id="limit" class="form-control" type="text" value="0" />
+                            <div class="input-group-btn">
+                                <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span></button>
+                                <ul id="limits-choices" class="dropdown-menu dropdown-menu-right">
+                                    <li><a href="#">0</a></li>
+                                    <li><a href="#">100</a></li>
+                                    <li><a href="#">200</a></li>
+                                    <li><a href="#">500</a></li>
+                                </ul>
+                            </div>
+                        </div>
+                        <span class=" help-block"> résultats/pages <i>(0 = tous sur une seule page)</i></span>
+                    </div>
+                </div>
+                <div class="form-group row">
+                    <div class="col-md-12">
+                    <input id="submit-query-form" type="submit" class="btn btn-primary" value="Lancer la requête"/>
+                    <input type="reset" class="btn" value="URL de la requête" id="get-query-url"/>
+                    <input type="reset" class="btn" value="Nouvelle requête" id="reset-query"/>
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+    </div>
+</div>
+
+<div class="row">
+    <div id="results" class="col-md-12">
+</div>
+@endsection
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/resources/views/sparql/sparqlClientResultBase.blade.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,11 @@
+@extends('base')
+
+@section('content')
+<div class="row">
+    <div class="col-md-12">
+        <div class="result-bindings-panel panel panel-default">
+            @yield('result-content')
+        </div>
+    </div>
+</div>
+@endsection
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/resources/views/sparql/sparqlClientResultBoolean.blade.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,12 @@
+@extends('sparql/sparqlClientResultBase')
+
+@section('result-content')
+<div class="result-bindings-heading panel-heading h3">Résultat de la requête</div>
+<div class="result-bindings-body panel-body">
+    @if($results->isTrue())
+    <span class="corpus-rdf-boolean corpus-rdf-boolean-true">VRAI</span>
+    @else
+    <span class="corpus-rdf-boolean corpus-rdf-boolean-false">FAUX</span>
+    @endif
+</div>
+@endsection
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/resources/views/sparql/sparqlClientResultList.blade.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,49 @@
+@extends('sparql/sparqlClientResultBase')
+
+@section('result-content')
+<div class="result-bindings-heading panel-heading h3">Résultat de la requête ({{$count}})</div>
+<div class="result-bindings-body panel-body">
+    <div class="col-md-6">
+    @if(count($namespaces)>0)
+        <table class="result-bindings-namespaces table table-striped">
+        <caption>Namespaces</caption>
+        @foreach ($namespaces as $short => $uri)
+            <tr><td class="result-bindings-namespaces-prefix-cell"><span class="result-bindings-namespaces-prefix">{{$short}}</span>:</td><td>{{$uri}}</td></tr>
+        @endforeach
+        </table>
+    @endif
+    </div>
+    <div class="col-md-6">
+        <form action="{{ route('sparql_query') }}" method="get" class="form-inline">
+        <input type="hidden" name="query" value="{{ $query }}"/>
+        <div class="form-group">
+        <label for="format" class="n control-label">Download format</label>
+        <select name="format" id="format" class="form-control">
+            @foreach($downloadFormats as $t => $v)
+            <option value="{{ $v }}">{{ $t }}</option>
+            @endforeach
+        </select>
+        </div>
+        <input id="submit-query-form" type="submit" class="btn btn-primary" value="download"/>
+        <form>
+    </div>
+</div>
+<table class='sparql-results table table-striped table-hover'>
+<thead>
+<tr>
+    @foreach ($fields as $field)
+    <th>{{ $fieldPrefix }}{{ $field }}</th>
+    @endforeach
+</tr>
+</thead>
+<tbody>
+    @foreach ($results as $row)
+    <tr>
+        @foreach ($fields as $field)
+        <td>{!! $row[$field] !!}</td>
+        @endforeach
+    </tr>
+    @endforeach
+</tbody>
+</table>
+@endsection
--- a/server/src/routes/web.php	Sun Oct 23 01:07:09 2016 +0200
+++ b/server/src/routes/web.php	Mon Oct 31 14:24:23 2016 +0100
@@ -8,7 +8,7 @@
 |
 */
 
-Route::get('/', 'WelcomeController@index');
+Route::get('/', 'WelcomeController@index')->name('root');
 
 Route::get('home', 'HomeController@index');
 
@@ -21,9 +21,12 @@
         ->where('docs', '.+');
     Route::resource('bo/docs', 'Bo\DocumentListController');
 
+    Route::get('sparql/client', 'Sparql\SparqlClientController@index')->name('sparql_form');
+    Route::get('sparql/query', 'Sparql\SparqlClientController@show')->name('sparql_query');
+    Route::get('sparql/proxy', 'Sparql\SparqlClientController@query')->name('sparql_proxy');
 
     //Route::controllers([
     //    'auth' => 'Auth\AuthController',
     //    'password' => 'Auth\PasswordController',
     //]);
-});
\ No newline at end of file
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/SparqlQueryAnalyserTest.php	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,81 @@
+<?php
+
+use CorpusParole\Libraries\Sparql\SparqlQueryAnalyser;
+
+class SparqlQueryAnalyserTest extends TestCase
+{
+    const QUERIES = [
+        "BASE" => "base.sparql",
+        "ASK" => "ask.sparql",
+        "GRAPH" => "graph.sparql",
+        "LIMIT_OFFSET" => "limit_offset.sparql",
+        "PREFIXES" => "prefixes.sparql",
+        "SELECT" => "select.sparql",
+        "UNKNOWN" => "unknown.sparql",
+    ];
+
+
+    public function getTestQuery($key) {
+        return file_get_contents(__DIR__.'/files/SparqlQueryAnalyserTest/'.self::QUERIES[$key]);
+    }
+    /**
+     * A basic test jsut test object creation.
+     *
+     * @return void
+     */
+    public function testCreation() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("BASE"));
+        $this->assertNotNull($analyser);
+    }
+
+    public function testQuerySelect() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("SELECT"));
+        $this->assertEquals(SparqlQueryAnalyser::SELECT_QUERY, $analyser->getQueryType());
+    }
+
+    public function testQueryGraph() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("GRAPH"));
+        $this->assertEquals(SparqlQueryAnalyser::GRAPH_QUERY, $analyser->getQueryType());
+    }
+
+    public function testQueryAsk() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("ASK"));
+        $this->assertEquals(SparqlQueryAnalyser::ASK_QUERY, $analyser->getQueryType());
+    }
+
+    public function testQueryUnkown() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("UNKNOWN"));
+        $this->assertEquals(SparqlQueryAnalyser::UNKNOWN_QUERY, $analyser->getQueryType());
+    }
+
+    public function testLimitOffset() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("LIMIT_OFFSET"));
+        $this->assertEquals(20, $analyser->getOffset());
+        $this->assertEquals(10, $analyser->getLimit());
+    }
+
+    public function testPrefixes() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("PREFIXES"));
+        $this->assertCount(5, $analyser->getPrefixes());
+        $this->assertEquals([
+            "" => "http://www.google.com/",
+            "rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+            "rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
+            "foaf" => "http://xmlns.com/foaf/0.1/",
+            "dbpedia-owl" => "http://dbpedia.org/ontology/"
+        ], $analyser->getPrefixes());
+    }
+
+    public function testRawPrefixes() {
+        $analyser = new SparqlQueryAnalyser($this->getTestQuery("PREFIXES"));
+        $this->assertCount(5, $analyser->getRawPrefixes());
+        $this->assertEquals([
+            "BASE <http://www.google.com/>",
+            "PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>",
+            "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>",
+            "prefix foaf: <http://xmlns.com/foaf/0.1/>",
+            "PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>"
+        ], $analyser->getRawPrefixes());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/ask.sparql	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,2 @@
+PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
+ASK  { ?x foaf:name  "Alice" }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/base.sparql	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,7 @@
+
+select (?o as ?RES) (COUNT(?s) as ?COUNT) where {
+  ?s a <http://www.europeana.eu/schemas/edm/ProvidedCHO>.
+  ?s <http://purl.org/dc/elements/1.1/subject> ?o
+}
+GROUP BY ?o
+ORDER BY DESC(?COUNT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/graph.sparql	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,2 @@
+PREFIX edm: <http://www.europeana.eu/schemas/edm/>
+CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <https://hdl.handle.net/11280.100/crdo-CFPP2000_11_SOUND> { ?s ?p ?o } }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/limit_offset.sparql	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,21 @@
+SELECT
+    ?uri
+    ?doc
+    ?title
+    ?issued
+    ?modified
+    (group_concat(distinct ?language;separator=", ") as ?lang)
+    (group_concat(distinct ?publisher;separator=", ") as ?publishers)
+WHERE {
+  GRAPH ?uri {
+        ?doc a <http://www.europeana.eu/schemas/edm/ProvidedCHO>.
+        ?doc <http://purl.org/dc/elements/1.1/title> ?title.
+        OPTIONAL {?doc <http://purl.org/dc/elements/1.1/language> ?language.}
+        OPTIONAL {?doc <http://purl.org/dc/terms/issued> ?issued.}
+        OPTIONAL {?doc <http://purl.org/dc/terms/modified> ?modified.}
+        OPTIONAL {?doc <http://purl.org/dc/elements/1.1/publisher> ?publisher.}
+    }.
+}
+GROUP BY ?uri ?doc ?title ?issued ?modified
+LIMIT 10 OFFSET 20
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/prefixes.sparql	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,16 @@
+BASE <http://www.google.com/>
+PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+prefix foaf: <http://xmlns.com/foaf/0.1/>
+PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>
+select  ?Nom ?resource ?url (count( distinct (?o) as ?nb))
+ where {
+   ?resource rdfs:label ?Nom.
+   ?resource foaf:isPrimaryTopicOf ?url.
+   ?resource rdf:type ?p.
+   ?resource dbpedia-owl:wikiPageExternalLink  ?o
+FILTER ( langMatches( lang(?Nom), "EN" )).
+?Nom <bif:contains> "Apple".
+MINUS { ?resource dbo:wikiPageRedirects|dbo:wikiPageDisambiguates ?dis   }
+}
+Group By  ?Nom ?resource ?url
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/select.sparql	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,7 @@
+PREFIX olac: <http://www.language-archives.org/OLAC/1.0/>
+select (?o as ?RES) (COUNT(?s) as ?COUNT) where {
+  ?s a <http://www.europeana.eu/schemas/edm/ProvidedCHO>.
+  ?s <http://purl.org/dc/elements/1.1/subject> ?o
+}
+GROUP BY ?o
+ORDER BY DESC(?COUNT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/tests/Libraries/Sparql/files/SparqlQueryAnalyserTest/unknown.sparql	Mon Oct 31 14:24:23 2016 +0100
@@ -0,0 +1,6 @@
+PREFIX dc: <http://purl.org/dc/elements/1.1/>
+INSERT DATA
+{
+  <http://example/book1> dc:title "A new book" ;
+                         dc:creator "A.N.Other" .
+}