diff --git a/modules/app.xqm b/modules/app.xqm index 03acf24..3bc81e9 100644 --- a/modules/app.xqm +++ b/modules/app.xqm @@ -1,331 +1,330 @@ xquery version "3.1"; (:~ This is the default application library module of the exist_test app. : : @author Christian Steiner : @version 1.0.0 : @see https://nietzsche.philhist.unibas.ch :) (: Module for app-specific template functions :) module namespace app="http://exist-db.org/apps/topoTEI/templates"; declare namespace array="http://www.w3.org/2005/xpath-functions/array"; declare namespace map="http://www.w3.org/2005/xpath-functions/map"; import module namespace templates="http://exist-db.org/xquery/html-templating"; import module namespace lib="http://exist-db.org/xquery/html-templating/lib"; import module namespace config="http://exist-db.org/apps/topoTEI/config" at "config.xqm"; declare namespace request="http://exist-db.org/xquery/request"; import module namespace console="http://exist-db.org/xquery/console"; declare namespace system="http://exist-db.org/xquery/system"; import module namespace transform="http://exist-db.org/xquery/transform"; import module namespace functx="http://www.functx.com"; declare namespace xmldb="http://exist-db.org/xquery/xmldb"; declare namespace tei="http://www.tei-c.org/ns/1.0"; declare %templates:wrap function app:uploadDialog($node as node(), $model as map(*)) { let $files := local:getTeiFiles($model('newest-first')) return <p> { if(count($files) > 0) then (<div class="col-md-6"> <form action="/exist/restxq/transform" method="get"> <select id="fileSelection" name="file" onChange="enableVersionButton(file.value, 'versionButton')"> {for $resource in $files return <option> {$resource}</option> }</select> <input type="submit" value="auswählen"/> <input id="newest" class="newest" type="checkbox" onChange="updateOrderBy(this)"/><label class="newest" for="newest"> Neueste zuerst</label> </form> <span> <button title="Ausgewählte Datei runterladen" class="fbutton" onclick="exportFile('fileSelection')">Download ...</button> <button title="Ausgewählte Datei löschen" class="fbutton"onClick="deleteFile('fileSelection')">Datei löschen</button> </span> </div>) else () } <div class="col-md-6"> <form method="POST" action="/exist/restxq/posttransform" enctype="multipart/form-data"> <input type="file" name="content" accept=".xml"/> <input type="submit" value="upload"/> </form> </div> <div>{ if(count($files) > 0) then (<a hidden="true" id="downloadLink" href="/exist/restxq/download?file={$files[1]}" download="{$files[1]}">Download</a>) else () } </div> </p> }; declare %templates:wrap function app:checkStatus ($node as node(), $model as map(*), $msg as xs:string?, $newest as xs:string?) as map(*) { let $default := map { "newest-first": ($newest = 'true' or empty($newest))} return if ($msg) then ( switch($msg) case ("422") return map { "error": "Falscher Dateityp" } default return map:merge(($default, map { 'status' : $msg })) ) else ( $default ) }; declare function app:title($node as node(), $model as map(*)) as element(h1) { let $msg := "Datei auswählen:" return if (map:contains($model, 'error')) then ( <h1>Fehler: {$model('error')}! Bitte neue {$msg}</h1> ) else ( <h1>{$msg}</h1> ) }; declare function local:getTeiFiles($newest as xs:boolean) as xs:string* { if ($newest) then ( for $resource in xmldb:get-child-resources($config:data-root) where local:isTeiFile(doc(concat($config:data-root, '/', $resource))) order by xmldb:last-modified($config:data-root, $resource) descending return $resource ) else ( for $resource in xmldb:get-child-resources($config:data-root) where local:isTeiFile(doc(concat($config:data-root, '/', $resource))) order by $resource return $resource ) }; declare function local:isTeiFile($document as node()) { $document/tei:TEI }; declare function local:getVersions($resource as xs:string, $date_suffix as xs:string?) as map(*)* { let $bakDir := concat($config:data-root, '/bak') return if (xmldb:collection-available($bakDir)) then ( for $bakFile in xmldb:get-child-resources($bakDir) where starts-with($bakFile, $resource) order by xmldb:last-modified($bakDir, $bakFile) descending return map { 'name': $bakFile, 'selected': (not(empty($date_suffix)) and ends-with($bakFile, $date_suffix))} ) else () }; declare function app:versions($node as node(), $model as map(*)) as element(div){ let $file := $model('filename') let $mainVersion := if (contains($file, '.xml_')) then (concat(substring-before($file, '.xml_'), '.xml')) else ($file) let $suffix := if (contains($file, '.xml_')) then (substring-after($file, '.xml_')) else () return <div id="versionPanel" class="input"> <h2>Versionen:</h2> <form id="versions"> <fieldset> { for $bakMap in local:getVersions($mainVersion, $suffix) return if($bakMap('selected')) then ( <div><input type="radio" name="file" value="{$bakMap('name')}" checked="true"/><label for="{$bakMap('name')}">{$bakMap('name')}</label></div>) else (<div><input type="radio" name="file" value="{$bakMap('name')}" onChange="enableButtons(['revertVersionButton','showVersionButton', 'deleteVersionButton'])"/> <label for="{$bakMap('name')}">{$bakMap('name')}</label></div>) } </fieldset> </form> { if (not(empty($suffix))) then(<span> <button id="revertVersionButton" onClick="revertVersion()" title="Ausgewählte Version wiederherstellen"><i class="fa fa-undo"></i></button> <button id="showVersionButton" onClick="showVersion()">Anzeigen</button> <button title="Ausgewählte Version löschen" id="deleteVersionButton" onClick="deleteVersion(false)">Löschen</button> </span>) else (<span> <button id="revertVersionButton" onClick="revertVersion()" title="Ausgewählte Version wiederherstellen" disabled="true"><i class="fa fa-undo"></i></button> <button id="showVersionButton" onClick="showVersion()" disabled="true">Anzeigen</button> <button title="Ausgewählte Version löschen" id="deleteVersionButton" onClick="deleteVersion(false)" disabled="true">Löschen</button> </span>) } <button onClick="showDefaultVersion('{$mainVersion}')">Abbrechen</button> <button title="Alle alten Versionen löschen" onClick="deleteVersion(true)"><i class="fa fa-trash-o"></i></button> </div> }; declare function local:createHiddenInput($map as map(*)) as element(input){ let $id := $map('id') let $value := $map('value') return <input type="hidden" id="{$id}" name="{$id}" value="{$value}"/> }; declare function app:hiddenData($node as node(), $model as map(*)) as element(div) { let $collection := $config:data-root let $filename := $model('filename') let $maps := [ map { 'id': 'collection', 'name':'collection','value': $collection}, map {'id': 'filename','name':'file', 'value': $filename}] return <div class="input"> { for $index in 1 to array:size($maps) let $map := array:get($maps, $index) return local:createHiddenInput($map) } <a hidden="true" id="downloadLink" href="/exist/restxq/download?file={$filename}" download="{$filename}">Download</a> </div> }; declare function local:createFontFaces($current_font as xs:string?) as element(option)* { let $fontDir := concat(replace($config:data-root, "data", "resources/"), 'fonts/') return if (xmldb:collection-available($fontDir)) then ( for $fontFile in xmldb:get-child-resources($fontDir) order by xmldb:last-modified($fontDir, $fontFile) descending return if ($fontFile = $current_font) then ( <option selected="true">{$fontFile}</option> ) else ( <option>{$fontFile}</option> ) ) else () }; declare function local:createWebFonts($document) as element(option)* { for $localFont in $document/config/fonts/webfont/text() return if ($localFont = $document/config/fonts/current/text()) then ( <option selected="true">{$localFont}</option> ) else ( <option>{$localFont}</option> ) }; declare function local:createFontFace($fontName) as xs:string { if (contains($fontName, '.')) then ( '@font-face { font-family: "MyFont"; src: url("../apps/topoTEI/resources/fonts/' || $fontName || '");} #transkription { font-family: MyFont; } ') else ( '#transkription { font-family: ' || $fontName ||'; }' ) }; declare function app:fontLink($node as node(), $model as map(*)) as element(link)* { let $configFile := doc(concat($config:app-root, '/config/gui_config.xml')) for $link in $configFile/config/fonts/link return <link href='{$link}' rel='stylesheet' type='text/css'/> }; declare function app:fontFace($node as node(), $model as map(*)) as element(style)* { let $configFile := doc(concat($config:app-root, '/config/gui_config.xml')) return if ($configFile/config/fonts/current/text()) then ( <style>{local:createFontFace($configFile/config/fonts/current/text())} </style> ) else () }; declare function app:createConfig($node as node(), $model as map(*)) as element(div) { let $configFile := doc(concat($config:app-root, '/config/gui_config.xml')) return <div id="editorInput" class="input"> <h2>Konfiguration</h2> <form name="config"> { for $p in $configFile/config/param let $label := $p/@label return <div><label class="config" for="{$p/@name}">{string($p/@label)}:</label><input type="number" id="{$p/@name}" value="{$p/text()}" step="any"/></div> } <div> <label class="config" for="font">Schrift:</label> <select id="fontSelection" name="font"> { local:createFontFaces($configFile/config/fonts/current/text())} { local:createWebFonts($configFile) } </select> </div> </form> <button onClick="saveConfig('fontSelection', [{string-join( for $item in $configFile/config/param/@name return concat("'", $item, "'"), ',')}])">Speichern</button> </div> }; declare function app:lineInput($node as node(), $model as map(*)) as element(div) { <div id="lineInput" class="input"> <h2>Zeilenposition</h2> <form name="line"> - <span id="param">Bottom</span> <input type="number" value="3" id="line" step="0.1" onChange="setLineHeight(line.value, false, param.innerText, false)"/> em + <span id="param">bottom</span> <input type="number" value="3" id="linePosition" step="0.1" onChange="setNewValue(this)" data-unit="em" data-param="bottom" data-css="bottom"/> em </form> </div> }; -declare function app:defaultLineInput($node as node(), $model as map(*)) as element(div) { - <div id="defaultLineInput" class="input"> +declare function app:textBlockInput($node as node(), $model as map(*)) as element(div) { + <div id="textBlockInput" class="input"> <h2>Settings für Textblock</h2> <form name="line"> - Zeilenhöhe: <input type="number" value="3" id="line" step="0.1" onChange="setLineHeight(line.value, true, 'lineHeight', false)"/> em<br/> - padding-top: <input type="number" value="0" id="paddingTop" step="0.1" onChange="setStyleToElement(this.parentElement, paddingTop.value, PADDING_TOP)"/> em<br/> - padding-bottom: <input type="number" value="0" id="paddingBottom" step="0.1" onChange="setStyleToElement(this.parentElement, paddingBottom.value, PADDING_BOTTOM)"/> em<br/> - <button onClick="getStyleFromElement(this.parentElement, [])">test</button> + Zeilenhöhe: <input type="number" value="3" id="line" step="0.1" onChange="setNewValue(this)" data-unit="em" data-param="lineHeight" data-css="line-height"/> em<br/> + padding-top: <input type="number" value="0" id="paddingTop" step="0.1" onChange="setNewValue(this)" data-unit="em" data-param="paddingTop" data-css="padding-top"/> em<br/> + padding-bottom: <input type="number" value="0" id="paddingBottom" step="0.1" onChange="setNewValue(this)" data-unit="em" data-param="paddingBottom" data-css="padding-bottom"/> em </form> </div> }; declare function app:transform($node as node(), $model as map(*)) { let $file := $model('file') let $node-tree := doc($file) let $stylesheet := doc(concat($config:app-root, "/xslt/sourceDoc.xsl")) let $param := <parameters> <param name="fullpage" value="false"/> </parameters> return transform:transform($node-tree, $stylesheet, $param, (), "method=html5 media-type=text/html") }; (:~ : This is a sample templating function. It will be called by the templating module if : it encounters an HTML element with an attribute: data-template="app:test" or class="app:test" (deprecated). : The function has to take 2 default parameters. Additional parameters are automatically mapped to : any matching request or function parameter. : : @param $node the HTML node with the attribute which triggered this call : @param $model a map containing arbitrary data - used to pass information between template calls :) declare %templates:wrap %templates:default ("name", "default") function app:hello($node as node(), $model as map(*), $name as xs:string?) { let $log := console:log('hello: ') return <p> Hello World, and so! {$model('name')}</p> }; declare function app:divs($node as node(), $model as map(*), $file as xs:string*) as element(div) { <div id="myTest" class="datacontainer"> {if ($file) then ( for $entry in doc(concat("/db/apps/topoTEI/data/", $file))/data/entry return <div id="{$entry/id}" class="data" style="{$entry/style/text()}" draggable="true">{$entry/id/text()} {$entry/style/text()}</div> ) else ( <div id="data1" class="data" draggable="true"> <div class="mydivheader">Click here to move</div> A </div>)} </div> }; declare %templates:default ("file", "default.xml") function app:files($node as node(), $model as map(*), $file as xs:string?) as element(select){ <select name="file"> {for $resource in xmldb:get-child-resources("/db/apps/topoTEI/data") return if ($resource = $file) then ( <option selected="selected"> {$resource}</option> ) else ( <option> {$resource}</option> ) }</select> }; declare function app:test($node as node(), $model as map(*)) { <p>Hello World at <strong>{format-dateTime(current-dateTime(), "[Y0001]-[M01]-[D01]_[H01]:[m01]:[s01]")}</strong>. The templating function was triggered by the class attribute <code>class="app:test"</code>.</p> }; diff --git a/modules/rest-api.xq b/modules/rest-api.xq index 33f0b83..c03abb4 100644 --- a/modules/rest-api.xq +++ b/modules/rest-api.xq @@ -1,474 +1,474 @@ xquery version "3.1"; module namespace myrest="http://exist-db.org/apps/restxq/myrest"; declare namespace rest="http://exquery.org/ns/restxq"; declare namespace http="http://expath.org/ns/http-client"; declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization"; declare namespace util="http://exist-db.org/xquery/util"; declare namespace map="http://www.w3.org/2005/xpath-functions/map"; import module namespace console="http://exist-db.org/xquery/console"; import module namespace transform="http://exist-db.org/xquery/transform"; declare namespace system="http://exist-db.org/xquery/system"; import module namespace config="http://exist-db.org/apps/topoTEI/config" at "config.xqm"; import module namespace templates="http://exist-db.org/xquery/html-templating"; import module namespace req="http://exquery.org/ns/request"; import module namespace functx="http://www.functx.com"; import module namespace app="http://exist-db.org/apps/topoTEI/templates" at "app.xqm"; import module namespace myparsedata="http://exist-db.org/apps/myapp/myparsedata" at "myparsedata.xqm"; declare namespace tei="http://www.tei-c.org/ns/1.0"; declare namespace array="http://www.w3.org/2005/xpath-functions/array"; declare %rest:path("/save") %rest:POST %rest:form-param("file", "{$file}", "unknown.xml") %rest:form-param("elements", "{$elements}", "[]") function myrest:saveData($file as xs:string*,$elements as xs:string*) { let $newfile := local:updateFile($file, $elements) - + let $log := console:log($file) return <rest:response> <http:response status="200" message="OK"> <response name="location" value="/exist/restxq/transform?file={$file}"/> </http:response> </rest:response> }; declare %rest:path("/fix") %rest:GET function myrest:fixD($file as xs:string*,$elements as xs:string*) { let $bakDir := concat($config:data-root, '/bak') let $output-collection := xmldb:login($bakDir, 'test', 'test') (: :let $output := for $bakFile in xmldb:get-child-resources($bakDir) where not(ends-with($bakFile, '.xml')) let $new := xmldb:remove($bakDir, $bakFile) return $new :) return <rest:response> <http:response status="200" message="OK"> </http:response> </rest:response> }; declare function local:getLocalPath() as xs:string { let $path := substring-before(substring-after(system:get-module-load-path(), '/db/'), '/modules') return $path }; declare %rest:path("/transform") %rest:GET %rest:query-param("file", "{$file}", "default.xml") %output:media-type("text/html") %output:method("html5") function myrest:transform($file as xs:string*) { let $filepath := concat($config:data-root,'/', $file) - let $log := console:log($file) + return local:showTransformation($filepath) }; declare function local:storeFile($data, $type as xs:string, $targetType as xs:string, $collection as xs:string) as map(*) { let $output-collection := xmldb:login($collection, 'test', 'test') let $parsedData := myparsedata:parseData($data, $type, $targetType) return if (map:contains($parsedData, $targetType)) then ( let $filename := $parsedData('filename') let $content := $parsedData($targetType) return if (contains($targetType, 'xml')) then ( let $document := local:prepareDocument($content) let $backup := local:backupFile($filename, $collection) let $localUri := xmldb:store($collection, $filename, $document) return map:merge(($parsedData, map{ 'localUri': $localUri })) ) else ( let $localUri := xmldb:store($collection, $filename, $content) return map:merge(($parsedData, map{ 'localUri': $localUri })) ) ) else ( $parsedData ) }; declare function local:prepareDocument($xmlContent as xs:string) as node() { let $document := parse-xml($xmlContent) let $stylesheet := config:resolve("xslt/createIDs4sourceDoc.xsl") let $fix := config:resolve("xslt/remove_namespaces.xsl") let $sourceDoc := config:resolve("xslt/updateSourceDoc.xsl") let $new-document := transform:transform($document, $stylesheet, (), (), "method=xml media-type=text/xml") let $fix-document := transform:transform($new-document, $fix, (), (), "method=xml media-type=text/xml") let $source-document := transform:transform($fix-document, $sourceDoc, (), (), "method=xml media-type=text/xml") return $source-document }; declare %rest:path("/revertVersion") %rest:GET %rest:query-param("file", "{$file}", "none") function myrest:revertVersion($file) { let $collection := concat($config:data-root, '/') let $output-collection := xmldb:login($collection, 'test', 'test') let $originalFile := concat(substring-after(substring-before($file, '.xml_'), 'bak/'), '.xml') let $bakfile := local:backupFile($originalFile, $collection ) let $out-file := xmldb:store($collection, $originalFile, doc(concat($collection, $file))) return <rest:response> <http:response status="302" message="Temporary Redirect"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> <http:header name="location" value="/exist/restxq/deleteBak?file={$file}"/> </http:response> </rest:response> }; declare %rest:path("/deleteBak") %rest:GET %rest:query-param("file", "{$file}", "none") function myrest:deleteBakFile($file) { let $refFile := if (not(contains($file, '.xml_'))) then ( let $collection := concat($config:data-root, '/') let $output := local:deleteAllVersions($file, $collection) return $file ) else ( let $originalFile := concat(substring-after(substring-before($file, '.xml_'), 'bak/'), '.xml') let $collection := concat($config:data-root, '/bak/') let $output-collection := xmldb:login($collection, 'test', 'test') let $deleted := xmldb:remove($collection, $file) return $originalFile ) return <rest:response> <http:response status="302" message="Temporary Redirect"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> <http:header name="location" value="/exist/restxq/transform?file={$refFile}"/> </http:response> </rest:response> }; declare function local:deleteAllVersions($file, $collection) { let $output-collection := xmldb:login($collection, 'test', 'test') let $bakDir := concat($collection, 'bak/') let $output := for $bakFile in local:getVersions($file) return xmldb:remove($bakDir, $bakFile) return $output }; declare function local:getVersions($resource as xs:string) as xs:string* { let $bakDir := concat($config:data-root, '/bak') for $bakFile in xmldb:get-child-resources($bakDir) where starts-with($bakFile, $resource) order by xmldb:last-modified($bakDir, $bakFile) descending return $bakFile }; declare %rest:path("/delete") %rest:GET %rest:query-param("file", "{$file}", "none") %rest:header-param("Referer", "{$referer}", "none") function myrest:deleteFile($file, $referer) { let $collection := concat($config:data-root, '/') let $output-collection := xmldb:login($collection, 'test', 'test') let $deleted := xmldb:remove($collection, $file) let $output := local:deleteAllVersions($file, $collection) return <rest:response> <http:response status="302" message="Temporary Redirect"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> <http:header name="location" value="{$referer}#reload"/> </http:response> </rest:response> }; declare %rest:path("/postfont") %rest:POST("{$data}") %rest:header-param("Content-Type", "{$type}") %output:media-type("text/html") %output:method("html5") %rest:header-param("Referer", "{$referer}", "none") function myrest:uploadFont($data, $type, $referer) { let $targetType := 'font' let $collection := concat(replace($config:data-root, "data", "resources/"), 'fonts/') let $response := local:storeFile($data, $type, $targetType, $collection) let $location := concat(replace($referer, '(\?.*$)',''), '?msg=', $response('status')) return <rest:response> <http:response status="302" message="Temporary Redirect"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> <http:header name="location" value="{$location}"/> </http:response> </rest:response> }; declare %rest:path("/old-posttransform") %rest:POST("{$data}") %rest:header-param("Content-Type", "{$type}") %output:media-type("text/html") %output:method("html5") %rest:header-param("Referer", "{$referer}", "none") function myrest:OLDuploadTransform($data, $type, $referer) { let $targetType := "text/xml" let $collection := concat($config:data-root, "/") let $response := local:storeFile($data, $type, $targetType, $collection) let $status := $response('status') return if ($status = '200') then ( local:showTransformation($response('localUri')) ) else ( let $location := concat(replace($referer, '(\?.*$)',''), '?msg=', $status) return <rest:response> <http:response status="302" message="Temporary Redirect"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> <http:header name="location" value="{$location}"/> </http:response> </rest:response> ) }; declare %rest:path("/posttransform") %rest:POST("{$data}") %rest:header-param("Content-Type", "{$type}") %output:media-type("text/html") %output:method("html5") %rest:header-param("Referer", "{$referer}", "none") function myrest:uploadTransform($data, $type, $referer) { let $targetType := "text/xml" let $collection := concat($config:data-root, "/") let $response := local:storeFile($data, $type, $targetType, $collection) let $status := $response('status') return if ($status = '200') then ( local:showTransformation($response('localUri')) ) else ( let $location := concat(replace($referer, '(\?.*$)',''), '?msg=', $status) return <rest:response> <http:response status="302" message="Temporary Redirect"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> <http:header name="location" value="{$location}"/> </http:response> </rest:response> ) }; declare function local:showTransformation($file as xs:string){ let $content := doc('transform.html') let $config := map { (: The following function will be called to look up template parameters :) $templates:CONFIG_APP_ROOT: $config:app-root, $templates:CONFIG_PARAM_RESOLVER : function($param as xs:string) as xs:string* { req:parameter($param) } } let $lookup := function($functionName as xs:string, $arity as xs:int) { try { function-lookup(xs:QName($functionName), $arity) } catch * { () } } let $filename := functx:substring-after-last($file, '/') let $map := map { 'file': $file, 'filename': $filename} return templates:apply($content, $lookup, $map, $config) }; declare %rest:path("/download") %rest:GET %rest:query-param("file", "{$file}", "D20_a28r_check2.xml") %rest:produces("application/xml") function myrest:donwload($file as xs:string*) { let $doc-uri := concat($config:data-root, '/', $file) let $mimetype := 'application/xml' let $method := 'xml' let $data := doc($doc-uri) (: :let $stylesheet := config:resolve('xslt/recreate_hierarchies.xsl') let $export-data := transform:transform($data, $stylesheet, (), (), "method=xml media-type=text/xml") :) return ( <rest:response> <http:response> <http:header name="Content-Type" value="{$mimetype}"/> </http:response> <output:serialization-parameters> <output:method value="{$method}"/> <output:media-type value="{$mimetype}"/> </output:serialization-parameters> </rest:response> , $data (: :<serverinfo accept="{req:header("Accept")}" method="{$method}" mimetype="{$mimetype}"> <desc language="en-US"/> <database version="{system:get-version()}"/> </serverinfo>:) ) }; declare function local:createCollection($collection as xs:string, $child as xs:string) as xs:string { let $new-collection := concat($collection, $child) return if (not(xmldb:collection-available($new-collection))) then ( let $target-collection := xmldb:create-collection($collection, $child) return $target-collection ) else ( $new-collection ) }; declare function local:getBackupFileName($file as xs:string) as xs:string { let $damyrestring := format-dateTime(current-dateTime(), "[Y0001]-[M01]-[D01]_[H01][m01][s01]") let $fileName := concat($file, "_", $damyrestring, '.xml') return string($fileName) }; declare function local:backupFile($file as xs:string*, $collection as xs:string*) as xs:string* { let $output-collection := xmldb:login($collection, 'test', 'test') return if (doc(concat($collection, $file))) then ( let $backup-collection := local:createCollection($collection, "bak") let $backup := xmldb:store($backup-collection, local:getBackupFileName($file), doc(concat($collection, $file))) (: :let $remove := xmldb:remove($collection, $file):) return $backup ) else () }; declare function local:parseHeader($data, $boundary){ let $head := replace(substring-before(substring-after($data, $boundary), "Content-Type:"), '(\r?\n|\r)', '') let $content-type := concat("content-type: ", replace(tokenize(substring-after($data, "Content-Type:"), '\n')[1], '(\s+)', '')) let $full-header := concat($head, ";", $content-type) let $header := map:merge( for $item in tokenize($full-header, ";") return if (contains($item, '="')) then ( let $keyValue := tokenize($item, "=") let $out := map { replace($keyValue[1], '(^\s)', '') : replace($keyValue[2], '"', '') } return $out ) else ( let $keyValue := tokenize($item, ": ") let $out := map { $keyValue[1] : replace($keyValue[2], '"', '') } return $out ) ) return $header }; declare %rest:path("/upload") %rest:POST("{$data}") %rest:header-param("Content-Type", "{$type}") %rest:header-param("Referer", "{$referer}", "none") function myrest:uploadFile($data, $type, $referer as xs:string*) { let $collection := concat($config:data-root, "/data/") let $output-collection := xmldb:login($collection, 'test', 'test') let $boundary := substring-after($type, "boundary=") let $content := substring-before(substring-after($data, $boundary), concat("--", $boundary)) let $header := local:parseHeader($data, $boundary) let $xmlContent := replace(substring-after($content, $header('content-type')), '(^\s+)', '') let $filename := $header('filename') let $backup := local:backupFile($filename, $collection) let $response := xmldb:store($collection, $filename, $xmlContent) return <rest:response> <http:response status="302" message="Temporary Redirect"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> <http:header name="location" value="{$referer}"/> </http:response> </rest:response> }; declare function local:update($item, $document){ if ($item('style')) then( update insert attribute style { $item('style')} into $document//*[@xml:id = $item('id')] ) else ( update insert attribute n { $item('n')} into $document//*[@xml:id = $item('id')] ) }; declare function local:updateFile($file as xs:string, $elements as xs:string*) as xs:string* { let $array := parse-json($elements) let $collection := concat($config:data-root, "/") let $output-collection := xmldb:login($collection, 'test', 'test') let $backup := local:backupFile($file, $collection) let $document := doc(concat($collection, $file)) for $index in 1 to array:size($array) let $item := array:get($array, $index) let $out := local:update($item, $document) return $file }; declare function local:saveConfigItems($configArray, $document) { for $index in 1 to array:size($configArray) let $item := array:get($configArray, $index) let $out := local:updateConfigFile($item, $document) let $name := $item("name") return 0 }; declare function local:processConfig($configuration as xs:string*) as xs:boolean { let $config := parse-json($configuration) let $log := console:log($config) let $configArray := $config('config') let $collection := replace($config:data-root, "data", "config/") let $output-collection := xmldb:login($collection, 'test', 'test') let $document := doc(concat($collection, "gui_config.xml")) let $oldFontSameAsNew := ($document/config/fonts/current/text() = $config('font')) let $update := local:updateConfigFontValue($config('font'), $document) let $configItemsChanged := local:saveConfigItems($configArray, $document) return $oldFontSameAsNew }; declare %rest:path("/config") %rest:POST %rest:form-param("configuration", "{$configuration}", "[]") function myrest:updateConfig($configuration as xs:string*) { let $oldFontSameAsNew := local:processConfig($configuration) return if ($oldFontSameAsNew) then ( <rest:response> <http:response status="200" message="OK"/> </rest:response> ) else ( <rest:response> <http:response status="205" message="Reset Content"> <http:header name="Cache-Control" value="no-cache, no-store, must-revalidate"/> <http:header name="Pragma" value="no-cache"/> <http:header name="Expires" value="0"/> <http:header name="X-XQuery-Cached" value="false"/> </http:response> </rest:response>) }; declare function local:updateConfigFontValue($font, $document){ update value $document/config/fonts/current with $font }; declare function local:updateConfigFile($item, $document){ update value $document/config/param[@name= $item("name") ] with $item("value") }; diff --git a/modules/transform.html b/modules/transform.html index d871c84..9ad62a0 100644 --- a/modules/transform.html +++ b/modules/transform.html @@ -1,90 +1,90 @@ <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/> <meta http-equiv="Pragma" content="no-cache"/> <meta http-equiv="Expires" content="Sat, 12 Oct 1991 05:00:00 GMT"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/> <link rel="shortcut icon" href="../apps/topoTEI/resources/images/exist_icon_16x16.ico"/> <link rel="stylesheet" type="text/css" href="../apps/topoTEI/resources/css/exist-2.2.css"/> <link rel="stylesheet" type="text/css" href="../apps/topoTEI/resources/css/style.css"/> <link rel="stylesheet" type="text/css" href="../apps/topoTEI/resources/css/gui_style.css"/> <link data-template="app:fontLink"/> <!-- <script type="text/javascript" src="../apps/topoTEI/resources/scripts/bootstrap.bundle.min.js"/>--> <script type="text/javascript" src="../apps/topoTEI/resources/scripts/topoTEI.js"/> <script type="text/javascript" src="../apps/topoTEI/resources/scripts/gui_transcription.js"/> <style data-template="app:fontFace"/> </head> <!-- <body id="grey-top" onload="updatePositions()"> --> <body id="grey-top"> <div class="grey-bot"> <div class="container" id="main-container"> <div class="row"> <div class="col-md-12" id="header"> <a href="../apps/topoTEI/" id="logo">eXistDB</a> </div> </div> <div class="row"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-navbar" role="navigation"> <div class="container-fluid g-0"> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item"> <a id="homeButton" class="nav-link active" aria-current="page" href="../apps/topoTEI/index.html#reload">Home</a> </li> <li class="nav-item"> <a id="undoButton" class="nav-link" onClick="undo()" disabled="true">Undo</a> </li> <li class="nav-item"> <a id="redoButton" class="nav-link" onClick="redo()" disabled="true">Redo</a> </li> <li class="nav-item"> <a id="saveButton" class="nav-link" onClick="myPost(this)" disabled="true">Speichern</a> </li> <li class="nav-item"> <a id="editButton" class="nav-link active" title="Quelldatei in eXide öffnen" onClick="openFile(this)">Edit</a> </li> <li class="nav-item"> <a id="exportButton" class="nav-link active" title="Quelldatei runterladen" onClick="downloadFile(this)">Download</a> </li> <li class="nav-item"> <a id="versionButton" class="nav-link active" title="Versionen verwalten" onClick="checkVersions(this)">Versionen</a> </li> <li class="nav-item"> <a class="nav-link active" onClick="toggleConfig()">Konfiguration</a> </li> </ul> </div> </div> </nav> </div> <div id="content" class="row" style="padding-top: 4.5rem"> <div data-template="app:hiddenData"/> <div data-template="app:createConfig"/> <div data-template="app:lineInput"/> - <div data-template="app:defaultLineInput"/> + <div data-template="app:textBlockInput"/> <div data-template="app:versions"/> <div data-template="app:transform"/> </div> </div> </div> <div class="container"> <div class="text-end"> <a id="poweredby" href="http://exist-db.org"/> </div> <div id="footer"> <ul> <li> <a href="../apps/topoTEI/index.html#reload">topoTEI</a> </li> </ul> <div id="copyright"> <p>Copyright Universität Basel 2023</p> </div> </div> </div> </body> </html> \ No newline at end of file diff --git a/resources/css/style.css b/resources/css/style.css index ed1cbcd..764b43d 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -1,50 +1,59 @@ /*Application styles could go here */ .data { width: 100px; height: 100px; position: absolute; left: 10px; top: 10px; background-color: red; } .datacontainer { position: relative; color: blue; background: green; height: 400px; } .mydivheader { padding: 10px; cursor: move; z-index: 10; background-color: #2196F3; color: #fff; } .fixed-navbar { position: fixed; } .selected { background-color: yellow; } .input { position: fixed; left : 10px; visibility: hidden; } .active { cursor: pointer; } .fbutton { margin: 3px; } label.config { width: 150px; } #versions { height: 200px; width: 350px; overflow: scroll; } .newest { margin-left: 5px; +} +.newvalue { + color: red; +} +.newvalue::before { + content: attr(data-label); +} +.newvalue::after { + content: attr(data-unit); } \ No newline at end of file diff --git a/resources/scripts/topoTEI.js b/resources/scripts/topoTEI.js index 790b8bf..2afff7b 100644 --- a/resources/scripts/topoTEI.js +++ b/resources/scripts/topoTEI.js @@ -1,729 +1,741 @@ /** **/ +const OBJ_PARAMS = [{targetName: 'id', dataName: 'data-id'}, + {targetName: 'isClass', dataName: 'data-is-class', type: 'boolean'}, + {targetName: 'paramName', dataName: 'data-param'}, + {targetName: 'cssName', dataName: 'data-css'}, + {targetName: 'unit', dataName: 'data-unit'}]; var PADDING_TOP = { paramName: 'paddingTop', cssName: 'padding-top'}; var PADDING_BOTTOM = { paramName: 'paddingBottom', cssName: 'padding-bottom'}; +var LINE_PARAM = { paramName: 'lineHeight', cssName: 'line-height'}; var runsOnBakFile = false; var NEWEST = "newest"; var FILENAME = 'filename'; var COLLECTION = 'collection'; var DOWNLOAD_LINK = 'downloadLink'; var VERSIONS = 'versions'; var MARGIN_LEFT = 'marginLeft'; var POSITION_CHANGED = 'positionChanged'; var LINE_CHANGED = 'lineChanged'; var VALUE_CHANGED = 'valueChanged'; var ZONE_LINE = 'zoneLine'; +var TEXT_BLOCK = 'textBlockInput'; +var LINE_INPUT = 'lineInput'; +var LINE_POSITION = 'linePosition'; var LINE = 'line'; const INSERTION_MARK_REGEX = /(insM|Ez)/g; var fileIsOpenedInEditor = false; var currentLine = null; var currentInput = null; var undoStack = []; var redoStack = []; var tester = [ FILENAME, COLLECTION, DOWNLOAD_LINK]; function test (){ tester.forEach(test =>{ console.log(document.getElementById(test)) }); } function updateOrderBy(checkbox){ location.href = (location.search) ? location.href.substring(0, location.href.indexOf('?')) + '?newest=' + String(checkbox.checked) : location.href + '?newest=' + String(checkbox.checked); } function revertVersion(){ let form = document.getElementById(VERSIONS); if (form && form.elements.file.value){ let currentFile = 'bak/' + form.elements.file.value; location.href = '/exist/restxq/revertVersion?file=' + currentFile; } } function showVersion(){ let form = document.getElementById(VERSIONS); if (form && form.elements.file.value){ let currentFile = 'bak/' + form.elements.file.value; location.href = '/exist/restxq/transform?file=' + currentFile; } } function deleteVersion(all){ if (all){ let file = document.getElementById(FILENAME); let dialogText = 'Alle alten Versionen wirklich löschen?' if (file && confirm(dialogText) == true){ location.href = '/exist/restxq/deleteBak?file=' + file.value; } } else { let form = document.getElementById(VERSIONS); if (form && form.elements.file.value){ let currentFile = 'bak/' + form.elements.file.value; location.href = '/exist/restxq/deleteBak?file=' + currentFile; } } } function showDefaultVersion(defaultFile){ location.href = '/exist/restxq/transform?file=' + defaultFile; } function enableButtons(buttonIds){ buttonIds.forEach(buttonId =>{ let button = document.getElementById(buttonId); if (button){ button.removeAttribute('disabled'); } }); } function enableVersionButton(file, versionButtonId){ let versionButton = document.getElementById(versionButtonId); let fileInput = document.getElementById(file); if (versionButton && fileInput){ if (fileInput.value == 'true'){ versionButton.removeAttribute('disabled'); } else { versionButton.setAttribute('disabled', 'true'); } } } function checkVersions(button){ if (!button.getAttribute('disabled')){ let versionPanel = document.getElementById("versionPanel"); versionPanel.style.visibility = (versionPanel.style.visibility == 'visible') ? 'hidden' : 'visible'; } } function deleteFile(selectName){ let select = document.getElementById(selectName); if (select){ let currentFile = select.options[select.selectedIndex].text; let dialogText = 'Datei "' + currentFile + '" und alle Versionen davon wirklich löschen?' if (confirm(dialogText) == true){ location.href = '/exist/restxq/delete?file=' + currentFile; } } } function exportFile(selectName){ let select = document.getElementById(selectName); let link = document.getElementById(DOWNLOAD_LINK); if (select && link){ let currentFile = select.options[select.selectedIndex].text; link.setAttribute('download', currentFile); let newHref = link.href.substring(0, link.href.indexOf('?')) + "?file=" + currentFile; link.setAttribute('href', newHref) link.click(); } } function downloadFile(button){ if (!button.getAttribute('disabled')){ let link = document.getElementById(DOWNLOAD_LINK); if (link){ if(runsOnBakFile){ let currentFile = link.href.substring(link.href.indexOf('?')).replace('?file=',''); let filename = currentFile.substring(0, currentFile.indexOf('.')) + '_' + currentFile.substring(currentFile.indexOf('.')).replace('.xml_','') + '.xml'; link.setAttribute('download', filename); console.log(currentFile, filename) let newHref = link.href.substring(0, link.href.indexOf('?')) + "?file=bak/" + currentFile; link.setAttribute('href', newHref) } link.click(); } } } function openFile(button){ if (!button.getAttribute('disabled')){ let collection = document.getElementById(COLLECTION); let file = document.getElementById(FILENAME); if (collection && file){ redoStack = []; handleButtons(); Array.from(document.getElementsByClassName('selected')).forEach(selected =>selected.classList.remove("selected")); let filepath = (runsOnBakFile) ? collection.value + '/bak/' + file.value : collection.value + '/' + file.value; fileIsOpenedInEditor = true; window.open('/exist/apps/eXide/index.html?open=' + filepath, '_blank'); } } } function undo(){ let button = document.getElementById("undoButton"); if(!button.getAttribute('disabled') && undoStack.length > 0){ Array.from(document.getElementsByClassName('selected')).forEach(selected =>selected.classList.remove("selected")); let lastEvent = undoStack.pop(); lastEvent.undo(true); } } function redo(){ let button = document.getElementById("redoButton"); if(!button.getAttribute('disabled') && redoStack.length > 0){ Array.from(document.getElementsByClassName('selected')).forEach(selected =>selected.classList.remove("selected")); let lastEvent = redoStack.pop(); lastEvent.undo(false); } } class Change { constructor(element, offsetX, offsetY){ this.element = element; this.offsetX = offsetX; this.offsetY = offsetY; } undo(isRedoing) { this.element.classList.add("selected"); repositionElement(this.element, this.offsetX*-1, this.offsetY*-1, isRedoing); } }; class LineChange { constructor(line, value, isDefault, paramName){ this.line = line; this.value = value; this.isDefault = isDefault; this.paramName = paramName; } undo(isRedoing) { currentLine = this.line; setLineHeight(this.value, this.isDefault, this.paramName, isRedoing); } }; class ParamChange { - constructor(idObject, oldValue, paramObject){ - this.idObject = idObject; + constructor(input, oldValue){ + this.input = input; this.oldValue = oldValue; - this.paramObject = paramObject; } undo(isRedoing) { - setNewValue(this.idObject, this.oldValue, this.paramObject, isRedoing); + setNewValue(this.input, this.oldValue, isRedoing); } }; - - - window.onload = function() { if(window.location.hash == '#reload') { console.log('reloading .........') history.replaceState(null, null, ' '); window.location.reload(true); } } function recordLineChange(line, isDefault, paramName, isRedoing){ let oldValue = Number(currentLine.parentElement.style[paramName].replace('em','')); let change = new LineChange(line, oldValue, isDefault, paramName); let currentStack = (isRedoing) ? redoStack : undoStack; currentStack.push(change); } -function recordNewValueChange(idObject, paramObject, isRedoing){ - let currentElement = (idObject.isClass) ? Array.from(document.getElementsByClassName(idObject.id))[0] : document.getElementById(idObject.id); - let oldValue = currentElement.style[paramObject.paramName]; - let change = new ParamChange(idObject, oldValue, paramObject); +function getObject(input, dataParams){ + const obj = {}; + dataParams.forEach(param =>{ + if(input.getAttribute(param.dataName)){ + obj[param.targetName] = (param.type == 'boolean') ? input.getAttribute(param.dataName) == 'true' : input.getAttribute(param.dataName); + } + }); + return obj; +} + +function recordNewValueChange(input, isRedoing){ + let inputObject = getObject(input, OBJ_PARAMS); + let currentElement = (inputObject.isClass) ? Array.from(document.getElementsByClassName(inputObject.id))[0] : document.getElementById(inputObject.id); + let oldValue = currentElement.style[inputObject.paramName]; + let change = new ParamChange(input, oldValue); let currentStack = (isRedoing) ? redoStack : undoStack; currentStack.push(change); } -function setNewValue(idObject, newValue, paramObject, isRedoing){ - recordNewValueChange(idObject, paramObject, isRedoing); +function setNewValue(input, isRedoing){ + let inputObject = getObject(input, OBJ_PARAMS); + recordNewValueChange(input, isRedoing); + let newValue = (input.type == 'number') ? Number(input.value) : input.value; handleButtons(); - if (idObject.isClass){ - Array.from(document.getElementsByClassName(idObject.id)).forEach(element =>{ - element.style[paramObject.paramName] = newValue; - element.classList.add(VALUE_CHANGED); + if (inputObject.isClass){ + Array.from(document.getElementsByClassName(inputObject.id)).forEach(element =>{ + setStyleToElement(element, newValue, inputObject) }); } else { - let element = document.getElementById(idObject.id); - element.style[paramObject.paramName] = newValue; - element.classList.add(VALUE_CHANGED); - } + let element = document.getElementById(inputObject.id); + setStyleToElement(element, newValue, inputObject); + } } function setStyleToElement(element, newValue, paramObject){ - element.style[paramObject.paramName] = newValue + 'em';//TODO fix this!! + element.style[paramObject.paramName] = newValue + paramObject.unit; element.classList.add(VALUE_CHANGED); if (element.dataset.index) { if (!containsValue(element, 'data-param', paramObject.paramName)){ element.setAttribute('data-param' + element.dataset.index, paramObject.paramName); element.setAttribute('data-css' + element.dataset.index, paramObject.cssName); element.setAttribute('data-index', Number(element.dataset.index)+1); } } else { element.setAttribute('data-param' + 0, paramObject.paramName); element.setAttribute('data-css' + 0, paramObject.cssName); element.setAttribute('data-index', 1); } console.log(element.dataset, element.style); } function containsValue(element, param, value){ let length = (element.dataset.index) ? element.dataset.index : 0; let data = []; for (var i = 0; i < length; i++){ data[i] = element.getAttribute(param + i); } return data.filter(name => name == value).length > 0; } function getStyleFromElement(element, targetArray){ let length = (element.dataset.index) ? Number(element.dataset.index) : 0; let style = ''; for (var i = 0; i < length; i++){ style = style + element.getAttribute('data-css' + i) + ':' + element.style[element.getAttribute('data-param' + i)] + ';'; } console.log(style); targetArray.push({id: element.id, style: style}); } function setLineHeight(newValue, isDefault, paramName, isRedoing){ recordLineChange(currentLine, isDefault, paramName, isRedoing); handleButtons(); if (isDefault){ Array.from(document.getElementsByClassName(LINE)).forEach(line =>{ line.style.lineHeight = newValue + 'em'; line.classList.add(LINE_CHANGED); }); } else { currentLine.parentElement.style[paramName] = newValue + 'em'; currentLine.parentElement.classList.add(LINE_CHANGED); } } -function myTestOK(newValue, id){ - console.log(newValue, id); -} -function createNewFormInput(element, input){ - if ( element.classList.contains('firstBlock') || element.classList.contains('singleBlock')) { - let oldValue = (element.style['paddingTop']) ? Number(element.style['paddingTop'].replace('em','')) : 5; - var newField = document.createElement('input'); - newField.setAttribute('type','number'); - newField.setAttribute('id','paddingTop'); - newField.setAttribute('value',oldValue); - newField.setAttribute('step','0.1'); - newField.setAttribute('onChange','myTestOK(paddingTop.value, \"' + element.id + '\")'); - let label = document.createElement('span'); - label.innerText = 'padding-top: ' - input.appendChild(label) - let em = document.createElement('span'); - em.innerText = 'em' - - input.appendChild(newField); - input.appendChild(em) - } - if ( element.classList.contains('lastBlock') || element.classList.contains('singleBlock')) { - let oldValue = (element.style['paddingBottom']) ? Number(element.style['paddingBottom'].replace('em','')) : 5; - var newField = document.createElement('input'); - newField.setAttribute('type','number'); - newField.setAttribute('id','paddingBottom'); - newField.setAttribute('value',oldValue); - newField.setAttribute('step','0.1'); - newField.setAttribute('onChange','myTestOK(paddingBottom.value, \"' + element.id + '\")'); - let label = document.createElement('span'); - label.innerText = 'padding-bottom: ' - input.appendChild(label) - let em = document.createElement('span'); - em.innerText = 'em' - - input.appendChild(newField); - input.appendChild(em) - } -} -function getLineHeightInput(element, id, paramName){ +function setInputValue(input, styleValue, id, isClass, label){ + if (styleValue) { + input.value = Number(styleValue.replace(input.dataset.unit, '')) + } + input.setAttribute('data-is-class', String(isClass)); + input.setAttribute('data-id', id); +} +function showLinePositionDialog(element, paramName){ + if (!runsOnBakFile){ + let input = document.getElementById(LINE_INPUT); + let textBlock = document.getElementById(TEXT_BLOCK); + let id = element.parentElement.id; + if (textBlock){ + textBlock.style.visibility = 'hidden'; + } + if (input.dataset.id == id){ + input.removeAttribute('data-id'); + input.style.visibility = 'hidden'; + } else { + let lineInput = Array.from(input.lastElementChild.children).filter(child =>child.id == LINE_POSITION)[0]; + lineInput.setAttribute('data-param', paramName); + lineInput.setAttribute('data-css', paramName); + setInputValue(lineInput, element.parentElement.style[paramName], id, false); + input.firstElementChild.innerText = "Zeilenposition für Zeile " + element.innerText; + let label = Array.from(input.lastElementChild.children).filter(child =>child.id == 'param')[0]; + label.innerText = paramName; + input.style.visibility = 'visible'; + } + } +} +function getLineHeightInput(element, id, paramName){ //DEPRECATED if (!runsOnBakFile){ let input = document.getElementById(id); if (currentInput && currentLine && input != currentInput){ currentInput.style.visibility = 'hidden'; } currentInput = input; + let lineInput = Array.from(input.lastElementChild.children).filter(child =>child.value)[0]; + let isClass = element.parentElement.classList.contains(LINE) + let lineInputId = (isClass) ? LINE : element.parentElement.id; + setInputValue(lineInput, element.parentElement.style[paramName], lineInputId, isClass); if( currentLine === element || (currentLine && currentLine.parentElement.classList.contains(LINE) && element.parentElement.classList.contains(LINE))){ input.style.visibility = 'hidden'; currentLine = null; } else { currentLine = element; - console.log(element.parentElement.parentElement.classList) if (element.parentElement.classList.contains(ZONE_LINE)){ input.firstElementChild.innerText = "Zeilenposition für Zeile " + element.innerText; let label = Array.from(input.lastElementChild.children).filter(child =>child.id == 'param')[0]; label.innerText = paramName; } else { let currentElement = element.parentElement.parentElement; - let currentParams = [ 'paddingTop', 'paddingBottom']; + let currentParams = [ PADDING_TOP.paramName, PADDING_BOTTOM.paramName ]; currentParams.forEach(param =>{ - Array.from(input.lastElementChild.children).filter(child =>child.id == param)[0].value = (currentElement.style[param]) ? Number(currentElement.style[param].replace('em','')) : 0; + let paramInput = Array.from(input.lastElementChild.children).filter(child =>child.id == param)[0]; + paramInput.value = (currentElement.style[param]) ? Number(currentElement.style[param].replace(paramInput.dataset.unit,'')) : 0; }) } if (element.parentElement.style[paramName]) { let lineInput = Array.from(input.lastElementChild.children).filter(child =>child.value)[0]; - lineInput.value = Number(element.parentElement.style[paramName].replace('em','')); + //lineInput.value = Number(element.parentElement.style[paramName].replace('em','')); + setInputValue(lineInput, element.parentElement.style[paramName], element.parentElement.id, element.parentElement.classList.contains(LINE)); } input.style.visibility = 'visible'; } } } function showFixLineNumberButtonIfNeeded(element){ let lines = Array.from(document.getElementsByClassName('lnr')).filter(line =>line.innerText == element.innerText); if (lines.length > 1){ let button = document.getElementById('lineInputButton'); button.removeAttribute('hidden'); } } function fixLineNumbering(){ let lines = Array.from(document.getElementsByClassName('lnr')).map(line =>new Object({ id: line.parentElement.id, n: line.innerText.substring(0, line.innerText.indexOf(':')) })); let data = adjustLineNumbers(lines); mySend(data); } function adjustLineNumbers(lines){ if (lines.length < 2){ return lines } else { if (lines[0].n == lines[1].n){ for (var i = 1; i < lines.length; i++){ lines[i].n = String( Number(lines[i].n) + 2); } } return [lines[0]].concat(adjustLineNumbers(lines.slice(1))); } } function saveStyleGet(element, attribute){ return (element.style[attribute]) ? element.style[attribute] : "0px"; } function createAddPositionInfo (element, isChild, targetArray){ if (isChild){ let style = (element.className.includes('below') || !(element.parentElement && element.parentElement.className.search(INSERTION_MARK_REGEX) > -1)) ? "left:" + saveStyleGet(element, 'left') + "; top:" + saveStyleGet(element, 'top') : "left:" + saveStyleGet(element, 'left'); targetArray.push({id: element.id, style: style}); if (element.parentElement && element.parentElement.className.search(INSERTION_MARK_REGEX) > -1){ createAddPositionInfo(element.parentElement, false, targetArray) } } else { let style = (element.className.includes('below')) ? "height:" + saveStyleGet(element.parentElement, 'height') : "top:" + saveStyleGet(element, 'top') + "; height:" + saveStyleGet(element, 'height'); targetArray.push({id: element.id, style: style}); } } function createInfo (element, targetArray){ if (element.className.includes(MARGIN_LEFT)){ let style = "margin-left:" + element.style.marginLeft; targetArray.push({id: element.id, style: style}); } else { createAddPositionInfo(element, true, targetArray); } } function createLineInfo (element, targetArray){ if (element.classList.contains(LINE)){ let style = 'line-height:' + element.style.lineHeight; targetArray.push({id: element.id, style: style}); } else { let style = (element.style.bottom) ? 'bottom:' + element.style.bottom : 'top:' + element.style.top; targetArray.push({id: element.id, style: style}); } } function createStyleObject(element){ let style = ''; if (element.style.paddingTop){ style = 'padding-top: ' + element.style.paddingTop + ';'; } if (element.style.paddingBottom){ style = style + 'padding-bottom: ' + element.style.paddingBottom + ';'; } return { id: element.id, style: style} } function toggleConfig(){ let config = document.getElementById("editorInput"); config.style.visibility = (config.style.visibility == 'visible') ? 'hidden' : 'visible'; } function createConfigObject(object){ return { name: object.id, value: String(object.value)} } function updateFont(font) { console.log(font) let xhr = new XMLHttpRequest() xhr.open('POST', "/exist/restxq/font", true) xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') xhr.send('font=' + font); xhr.onload = function () { //TODO } } function saveConfig(fontId, dataNameArray) { let fontSelector = document.getElementById(fontId); let font = fontSelector.options[fontSelector.selectedIndex].text; let configData = dataNameArray.map(id =>createConfigObject(document.getElementById(id))); let data = { font: font, config: configData } let jsonData = JSON.stringify(data); console.log(jsonData) let xhr = new XMLHttpRequest() xhr.open('POST', "/exist/restxq/config", true) xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') xhr.send('configuration=' + jsonData); xhr.onload = function () { if(this.status == '205'){ window.location.reload(true); } toggleConfig(); } } function myPost(button) { if (!button.getAttribute('disabled')){ let elements = Array.from(document.querySelectorAll("*[draggable]")).filter(element =>element.classList.contains(POSITION_CHANGED)); let elementInfos = []; elements.forEach(element =>{ createInfo(element, elementInfos) }); Array.from(document.getElementsByClassName(LINE_CHANGED)).forEach(line =>{ createLineInfo(line, elementInfos) }); mySend(elementInfos); } } function mySend(data){ let filename = document.getElementById(FILENAME); if (filename && data.length > 0) { let file = filename.value; let xhr = new XMLHttpRequest() xhr.open('POST', "/exist/restxq/save", true) xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') xhr.send("file=" + file + "&elements=" + JSON.stringify(data)); xhr.onload = function () { undoStack = []; location.href = '/exist/restxq/transform?file=' + file } } } var currentItems = []; var currentItem = null; var offset = 1; var modOffset = 10; var clickOffset = 10; function clickItem(item, event){ if (!runsOnBakFile){ event.stopPropagation(); if (modifierPressed){ if (shiftPressed){ item.classList.add("selected") currentItems.push(item) if (currentItem){ currentItems.push(currentItem); currentItem = null; } }else{ currentItem = item; currentItem.classList.add("selected"); let classList = Array.from(currentItem.classList) if (document.getElementById('toggleOffset') && Number(document.getElementById('toggleOffset').value)) { clickOffset = Number(document.getElementById('toggleOffset').value); } let currentOffset = ((currentItem.parentElement.className.includes('below') && !classList.includes('clicked')) || classList.includes('clicked')) ? clickOffset : clickOffset*-1; if (!classList.includes('clicked')){ currentItem.classList.add('clicked'); } else { currentItem.classList.remove('clicked'); } repositionElement(currentItem, 0, currentOffset, false); } } else { currentItems.forEach(selected =>selected.classList.remove("selected")) currentItems = []; if (currentItem){ currentItem.classList.remove("selected"); } if (currentItem === item){ currentItem = null; } else { currentItem = item; currentItem.classList.add("selected"); } } } } var modifierPressed = false; var shiftPressed = false; document.onkeyup = function(e) { if (e.key == 'Shift' || e.key == 'Control') { modifierPressed = false; if (e.key == 'Shift'){ shiftPressed = false; } } }; document.onkeydown = checkKey; function checkKey(e) { if (!runsOnBakFile){ e = e || window.event; if (redoStack.length > 0){ e.preventDefault(); } if(e.getModifierState(e.key)){ modifierPressed = true; shiftPressed = (e.key == 'Shift'); } if (modifierPressed && (e.key == 'z' || e.key == 'r')){ let execFunction = (e.key == 'z') ? undo : redo; execFunction(); } else { let selectedElements = Array.from(document.getElementsByClassName('selected')); if (selectedElements.length > 0){ e.preventDefault(); if (document.getElementById('offset') && Number(document.getElementById('offset').value)) { offset = Number(document.getElementById('offset').value); } if (document.getElementById('modOffset') && Number(document.getElementById('modOffset').value)) { modOffset = Number(document.getElementById('modOffset').value); } selectedElements.forEach(item =>{ let currentOffset = (modifierPressed) ? modOffset : offset; if (e.keyCode == '38') { repositionElement(item, 0, currentOffset*-1, false) // up arrow } else if (e.keyCode == '40') { repositionElement(item, 0, currentOffset, false) // down arrow } else if (e.keyCode == '37') { // left arrow repositionElement(item, currentOffset*-1, 0, false); } else if (e.keyCode == '39') { // right arrow repositionElement(item, currentOffset, 0, false); } else if (e.key == 'Enter'){ item.classList.remove("selected"); currentItem = null; } }); } } } } function recordChange(currentElement, offsetX, offsetY, isRedoing){ if (!currentElement.classList.contains(POSITION_CHANGED)){ currentElement.classList.add(POSITION_CHANGED); } let change = new Change(currentElement, offsetX, offsetY); let currentStack = (isRedoing) ? redoStack : undoStack; currentStack.push(change); } function handleButtons(){ let onChangeActiveButtons = [ document.getElementById('undoButton'), document.getElementById('saveButton') ]; let onChangeDisabledButtons = [ document.getElementById('editButton'), document.getElementById('exportButton'), document.getElementById('versionButton')]; let redoButton = document.getElementById('redoButton'); onChangeActiveButtons.forEach(button =>{ setDisabledStatus(button, (undoStack.length == 0)) }); onChangeDisabledButtons.forEach(button =>{ setDisabledStatus(button, !(undoStack.length == 0)) }); setDisabledStatus(redoButton, (redoStack.length == 0)) } function setDisabledStatus(button, disable){ if(!button.getAttribute('disabled') && disable){ button.setAttribute('disabled','true'); button.classList.remove('active'); } if(button.getAttribute('disabled') && !disable){ button.removeAttribute('disabled'); button.classList.add('active'); } } function repositionElement(currentElement, offsetX, offsetY, isRedoing){ recordChange(currentElement, offsetX, offsetY, isRedoing); handleButtons(); if (currentElement.className.includes(MARGIN_LEFT)){ let oldLeft = (currentElement.style.marginLeft) ? Number(currentElement.style.marginLeft.replace('px','')) : currentElement.offsetLeft; currentElement.style.marginLeft = (oldLeft + offsetX) + 'px'; } else { let oldLeft = (currentElement.style.left) ? Number(currentElement.style.left.replace('px','')) : currentElement.offsetLeft; currentElement.style.left = (oldLeft + offsetX) + 'px'; if(currentElement.parentElement && currentElement.parentElement.className.search(INSERTION_MARK_REGEX) > -1) { if (currentElement.parentElement.className.includes('below')){ let oldHeight = (currentElement.parentElement.style.height) ? Number(currentElement.parentElement.style.height.replace('px','')) : currentElement.parentElement.offsetHeight; let newHeight = oldHeight + offsetY; currentElement.parentElement.style.height = newHeight + "px"; currentElement.style.top = (currentElement.offsetTop + offsetY) + "px"; } else { let oldTop = Number(currentElement.parentElement.style.top.replace('px','')); if (offsetY == 0 && !currentElement.parentElement.style.top){ oldTop = -2 } let newTop = oldTop + offsetY; currentElement.parentElement.style.top = newTop + "px"; currentElement.parentElement.style.height = ((currentElement.parentElement.offsetHeight-2) + newTop*-1) + "px"; } } else { let oldTop = (currentElement.style.top) ? Number(currentElement.style.top.replace('px','')) : currentElement.offsetTop; currentElement.style.top = (oldTop + offsetY) + "px"; } } } var dragStartPosX = null; var dragStartPosY = null; window.addEventListener("dragenter", (event) => { event.preventDefault(); }); window.addEventListener("dragover", (event) => { event.preventDefault(); }); window.addEventListener( 'dragstart', (event) => { if (event && !runsOnBakFile){ dragStartPosX = event.clientX; dragStartPosY = event.clientY; event.dataTransfer.effectAllowed = "move"; event.dataTransfer.setData("text/plain", event.target.id); } }); window.addEventListener( 'dragend', (event) => { if (event && !runsOnBakFile){ let dragEndPosX = dragStartPosX - event.clientX; let dragEndPosY = dragStartPosY - event.clientY; repositionElement(event.target, dragEndPosX*-1, dragEndPosY*-1, false); event.preventDefault(); } }); window.onbeforeunload = function(event){ if (undoStack.length > 0){ return confirm("Confirm refresh"); } }; window.addEventListener("load", (event) => { let versions = document.getElementById(VERSIONS); if(versions){ if (versions.elements.file == undefined){ let button = document.getElementById('versionButton') setDisabledStatus(button, true); } } runsOnBakFile = window.location.search.replace('?file=', '').startsWith('bak/'); if (runsOnBakFile){ let button = document.getElementById('versionButton') checkVersions(button); setDisabledStatus(button, true); } let newest = document.getElementById(NEWEST); if (newest){ let checkNewest = (location.search && location.search.includes('newest=')) ? location.search.includes('newest=true') : true; //newest.style.setProperty('checked', String(checkNewest)); if (checkNewest) { newest.setAttribute('checked', 'true'); } else { newest.removeAttribute('checked'); } console.log('checked', String(checkNewest)); } }); document.addEventListener("visibilitychange", (event) =>{ if (document.visibilityState != 'visible' && fileIsOpenedInEditor){ console.log('file opended') } if (document.visibilityState == 'visible' && fileIsOpenedInEditor){ window.location.reload(true); } }); diff --git a/xslt/elementTemplates.xsl b/xslt/elementTemplates.xsl index 4c8650f..e92e220 100644 --- a/xslt/elementTemplates.xsl +++ b/xslt/elementTemplates.xsl @@ -1,355 +1,356 @@ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tei="http://www.tei-c.org/ns/1.0" version="2.0"> <xsl:import href="functions.xsl"/> <xsl:output method="html" encoding="UTF-8"/> <xsl:variable name="apos">'</xsl:variable> <!-- process forme work by using a dictionary that translates @hand keys to human readable information --> <xsl:template match="tei:fw"> <xsl:variable name="dict"> <tei:entry key="#XXX_red" value="unbekannte fremde Hand"/> <tei:entry key="#N-Archiv_red" value="fremde Hand: Nietzsche Archiv"/> <tei:entry key="#GSA_pencil" value="fremde Hand: GSA, Bleistift"/> </xsl:variable> <span class="{@place} {replace(@hand, '#', '')}" title="{$dict/tei:entry[@key = current()/@hand]/@value}"> <xsl:apply-templates/> </span> </xsl:template> <!-- Write the line number, if in editor mode write also a call to a javascript function onClick --> <xsl:template name="writeLineNumber"> <xsl:param name="n"/> <xsl:param name="lineType"/> + <xsl:param name="zoneId"/> <xsl:if test="$n"> <xsl:choose> <xsl:when test="$fullpage = 'true'"> <span class="lnr"> <xsl:value-of select="$n"/>: </span> </xsl:when> <xsl:otherwise> - <xsl:variable name="lineInputType" select="if ($lineType = 'line') then ('defaultLineInput') else ('lineInput')"/> - <xsl:variable name="paramName" select="if ($lineType = 'line') then ('lineHeight') else (if (number(replace($n, '[a-z]','')) lt 12) then ('top') else ('bottom'))"/> + <xsl:variable name="paramName" select="if (number(replace($n, '[a-z]','')) lt 12) then ('top') else ('bottom')"/> <xsl:variable name="className" select="if ($lineType = 'line') then ('lnr') else ('zlnr')"/> + <xsl:variable name="function" select="if ($lineType = 'line') then (concat('showTextBlockDialog(',$apos,$zoneId,$apos,')')) else (concat('showLinePositionDialog(this, ',$apos,$paramName,$apos,')'))"/> <xsl:element name="span"> <xsl:attribute name="class"> <xsl:value-of select="$className"/> </xsl:attribute> <xsl:attribute name="onClick"> - <xsl:value-of select="concat('getLineHeightInput(this, ',$apos,$lineInputType,$apos,', ',$apos,$paramName,$apos,')')"/> + <xsl:value-of select="$function"/> </xsl:attribute> <xsl:value-of select="$n"/>: </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:if> </xsl:template> <!-- Match text between addSpan and lb: display it depending on param 'show' --> <xsl:template match="text()[preceding-sibling::tei:addSpan[1]/following-sibling::tei:lb[1]/@n = following-sibling::tei:lb[1]/@n]|text()[preceding-sibling::tei:addSpan[@spanTo = concat('#',current()/following-sibling::tei:anchor[1]/@xml:id)]]"> <xsl:param name="show"/> <xsl:param name="debug"/> <xsl:if test="$show = 'true'"> <xsl:copy-of select="."/> </xsl:if> </xsl:template> <xsl:template match="*[preceding-sibling::tei:addSpan[@spanTo = concat('#',current()/following-sibling::tei:anchor[1]/@xml:id)]]|*[preceding-sibling::tei:addSpan[1]/following-sibling::tei:lb[1]/@n = following-sibling::tei:lb[1]/@n]"> <xsl:param name="show"/> <xsl:param name="debug"/> <xsl:if test="$show = 'true'"> <xsl:call-template name="showSelected"> <xsl:with-param name="localName" select="local-name()"/> </xsl:call-template> </xsl:if> </xsl:template> <xsl:template name="showSelected"> <xsl:param name="localName"/> <xsl:choose> <xsl:when test="$localName = 'head'"> <xsl:call-template name="head"/> </xsl:when> <xsl:when test="$localName = 'pc'"> <xsl:apply-templates/> </xsl:when> <xsl:when test="$localName = 'add'"> <xsl:call-template name="add"/> </xsl:when> <xsl:when test="$localName = 'del'"> <xsl:call-template name="del"/> </xsl:when> <xsl:when test="($localName = 'hi' or $localName = 'restore') and not(@spanTo)"> <xsl:call-template name="hi"/> </xsl:when> <xsl:when test="$localName = 'subst' and tei:add/@place = 'superimposed' and tei:del"> <xsl:call-template name="superimposed"/> </xsl:when> <xsl:when test="$localName = 'subst'"> <xsl:apply-templates/> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="."/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- Match addSpan: apply templates to following nodes until the corresponding anchor --> <xsl:template match="tei:addSpan"> <xsl:variable name="nextLineNumber" select="following-sibling::tei:lb[1]/@n"/> <xsl:variable name="anchorId" select="replace(@spanTo, '#', '')"/> <xsl:variable name="hand" select="replace(@hand, '#', '')"/> <span class="addSpan {$hand} {@rend}"> <xsl:choose> <xsl:when test="$nextLineNumber"> <xsl:apply-templates select="following-sibling::*[following-sibling::tei:lb/@n = $nextLineNumber]|following-sibling::text()[following-sibling::tei:lb/@n = $nextLineNumber]"> <xsl:with-param name="show" select="'true'"/> <xsl:with-param name="debug" select="$nextLineNumber"/> </xsl:apply-templates> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="following-sibling::*[following-sibling::tei:anchor/@xml:id = $anchorId]|following-sibling::text()[following-sibling::tei:anchor/@xml:id = $anchorId]"> <xsl:with-param name="show" select="'true'"/> <xsl:with-param name="debug" select="$anchorId"/> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> </span> </xsl:template> <xsl:template match="tei:anchor[@xml:id = substring-after(preceding-sibling::tei:lb[1]/preceding-sibling::tei:addSpan[1]/@spanTo, '#')]"> <xsl:variable name="hand" select="substring-after(preceding-sibling::tei:lb[1]/preceding-sibling::tei:addSpan[1]/@hand, '#')"/> <xsl:variable name="previousLineNumber" select="preceding-sibling::tei:lb[1]/@n"/> <xsl:variable name="numberAfterAddSpan" select="preceding-sibling::tei:addSpan[@spanTo = concat('#', current()/@xml:id)]/following-sibling::tei:lb[1]/@n"/> <xsl:if test="$previousLineNumber = $numberAfterAddSpan"> <span class="{$hand}"> <xsl:apply-templates select="preceding-sibling::*[preceding-sibling::tei:lb[1]/@n = $previousLineNumber]|preceding-sibling::text()[preceding-sibling::tei:lb[1]/@n = $previousLineNumber]"> <xsl:with-param name="show" select="'true'"/> <xsl:with-param name="debug" select="$previousLineNumber"/> </xsl:apply-templates> </span> </xsl:if> </xsl:template> <!-- Process overwritten text in case of substitution with @spanTo --> <xsl:template match="tei:subst[@spanTo and (following-sibling::tei:del[1]/@rend = 'overwritten' or following-sibling::tei:add[1]/@place = 'superimposed')]"> <xsl:variable name="hand" select="replace(following-sibling::tei:add/@hand,'#','')"/> <span class="box {$hand}" title="{following-sibling::tei:del[@rend='overwritten'][1]/text()} (überschrieben)"> <xsl:value-of select="following-sibling::tei:add[@place='superimposed'][1]/text()"/> </span> </xsl:template> <!-- Process overwritten text in case of normal substitution, also for forme work --> <xsl:template name="superimposed" match="tei:subst[tei:add/@place = 'superimposed' and tei:del and not(preceding-sibling::tei:addSpan[1]/following-sibling::tei:lb[1]/@n = following-sibling::tei:lb[1]/@n)]"> <xsl:variable name="dict"> <tei:entry key="erased" value="radiert:"/> <tei:entry key="overwritten" value="überschrieben:"/> </xsl:variable> <span class="{if (parent::tei:fw) then ('fw-box') else ('box')}"> <xsl:choose> <xsl:when test="current()/tei:del/node()"> <xsl:apply-templates select="current()/tei:add[@place = 'superimposed']/(*|text())"/> <span class="tooltip"> <xsl:value-of select="$dict/tei:entry[@key = current()/tei:del/@rend]/@value"/> <span class="transkriptionField small"> <xsl:apply-templates select="./tei:del/(*|text())"/> </span> </span> </xsl:when> <xsl:otherwise> <span class="{if (parent::tei:fw) then ('fw-box') else ('box')}" title="{current()/tei:del/text()} {$dict/tei:entry[@key = current()/tei:del/@rend]/@value}"> <xsl:apply-templates select="current()/tei:add[@place = 'superimposed']/(*|text())"/> </span> </xsl:otherwise> </xsl:choose> <xsl:apply-templates select="current()/tei:add[empty(@place) or not(@place = 'superimposed')]"/> </span> </xsl:template> <!-- Process deletions --> <xsl:template name="del" match="tei:del"> <xsl:variable name="deleted" select="concat('deleted',replace(replace(@hand, '@', '0'),'#','-'))"/> <xsl:choose> <xsl:when test="@rend != ''"> <span class="{@rend} {replace(@hand,'#','')}" title="{text()}"> <xsl:apply-templates/> </span> </xsl:when> <xsl:otherwise> <span class="{$deleted}" title="{text()}"> <xsl:apply-templates> </xsl:apply-templates> </span> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- Process space --> <xsl:template match="tei:space[@unit='char']"> <xsl:call-template name="insertSpace"> <xsl:with-param name="counter" select="@quantity"/> </xsl:call-template> </xsl:template> <!-- Create empty space output --> <xsl:template name="insertSpace"> <xsl:param name="counter"/> <xsl:text> </xsl:text> <xsl:if test="$counter > 0"> <xsl:call-template name="insertSpace"> <xsl:with-param name="counter" select="$counter - 1"/> </xsl:call-template> </xsl:if> </xsl:template> <!-- Process head --> <xsl:template name="head" match="tei:head"> <span class="head"> <xsl:apply-templates/> </span> </xsl:template> <!-- Process all text that is contained between one or several <hi spanTo="..."/> and <anchor xml:id="..."/> --> <xsl:template match="text()[tei:seqContains(preceding-sibling::tei:hi/@spanTo, following-sibling::tei:anchor/@xml:id)=1] |text()[tei:seqContains(../preceding-sibling::tei:hi/@spanTo, ../following-sibling::tei:anchor/@xml:id)=1]"> <span class="{distinct-values(preceding-sibling::tei:hi[@spanTo and index-of(current()/following-sibling::tei:anchor/@xml:id, replace(@spanTo, '#','')) gt 0]/@rend |../preceding-sibling::tei:hi[@spanTo and index-of(current()/../following-sibling::tei:anchor/@xml:id, replace(@spanTo, '#','')) gt 0]/@rend)}"> <xsl:copy-of select="."/> </span> </xsl:template> <!-- Process highlights --> <xsl:template name="hi" match="tei:hi[not(@spanTo) and not(preceding-sibling::tei:addSpan[1]/following-sibling::tei:lb[1]/@n = following-sibling::tei:lb[1]/@n)]"> <xsl:choose> <xsl:when test="parent::tei:restore/@type = 'strike'"> <span class="deleted-{@rend}"> <xsl:apply-templates/> </span> </xsl:when> <xsl:otherwise> <span class="{@rend}"> <xsl:apply-templates/> </span> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- Write content span --> <xsl:template name="writeContentSpanAttributes"> <xsl:param name="parentZoneId"/> <xsl:param name="isZone"/> <xsl:param name="spanClass"/> <xsl:param name="spanStyle"/> <xsl:variable name="class" select="if ($isZone = 'true') then (concat('marginLeft', ' ', $spanClass)) else ($spanClass)"/> <xsl:attribute name="id"> <xsl:value-of select="$parentZoneId"/> </xsl:attribute> <xsl:if test="$spanStyle"> <xsl:attribute name="style"> <xsl:value-of select="$spanStyle"/> </xsl:attribute> </xsl:if> <xsl:if test="$class"> <xsl:attribute name="class"> <xsl:value-of select="$class"/> </xsl:attribute> </xsl:if> <xsl:if test="$isZone = 'true' and $fullpage != 'true'"> <xsl:attribute name="draggable"> <xsl:value-of select="'true'"/> </xsl:attribute> <xsl:attribute name="onClick"> <xsl:value-of select="'clickItem(this, event)'"/> </xsl:attribute> </xsl:if> </xsl:template> <!-- Write addition, in editor mode make text draggable and create a call to javascript function onClick --> <xsl:template name="writeAdd"> <xsl:param name="childId"/> <xsl:param name="parentId"/> <xsl:param name="childClass"/> <xsl:param name="parentClass"/> <xsl:param name="childStyle"/> <xsl:param name="parentStyle"/> <xsl:choose> <xsl:when test="$fullpage = 'true'"> <span id="{$parentId}" class="{$parentClass}" style="{$parentStyle}"> <span id="{$childId}" class="{$childClass}" style="{$childStyle}"> <xsl:apply-templates/> </span> </span> </xsl:when> <xsl:otherwise> <span id="{$parentId}" class="{$parentClass}" style="{$parentStyle}"> <span id="{$childId}" class="{$childClass}" onClick="clickItem(this, event)" draggable="true" style="{$childStyle}"> <xsl:apply-templates/> </span> </span> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- Write zoneItems, in editor mode make text draggable and create a call to javascript function onClick --> <xsl:template name="zoneItems"> <xsl:param name="id"/> <xsl:if test="$fullpage != 'true'"> <xsl:attribute name="draggable"> <xsl:value-of select="'true'"/> </xsl:attribute> <xsl:attribute name="onClick"> <xsl:value-of select="'clickItem(this, event)'"/> </xsl:attribute> </xsl:if> <xsl:apply-templates select="//*[@xml:id = $id ]"> <xsl:with-param name="id" select="$id"/> </xsl:apply-templates> </xsl:template> <!-- Process additions --> <xsl:template name="add" match="tei:add"> <xsl:variable name="hand" select="replace(@hand,'#','')"/> <xsl:choose> <xsl:when test="@place and (contains(@place,'above') or contains(@place,'below'))"> <xsl:variable name="addId" select="concat('#', @xml:id)"/> <xsl:variable name="childId" select="//tei:sourceDoc//tei:add[@corresp=$addId]/@xml:id"/> <xsl:variable name="childStyle" select="//tei:sourceDoc//tei:add[@corresp=$addId]/@style"/> <xsl:variable name="parentId" select="//tei:sourceDoc//tei:metamark[@target=$addId]/@xml:id"/> <xsl:variable name="parentStyle" select="//tei:sourceDoc//tei:metamark[@target=$addId]/@style"/> <xsl:call-template name="writeAdd"> <xsl:with-param name="childId" select="$childId"/> <xsl:with-param name="parentId" select="$parentId"/> <!-- <xsl:with-param name="childClass" select="concat(@place, ' ', $hand, ' centerLeft')"/> --> <xsl:with-param name="childClass" select="@place"/> <xsl:with-param name="parentClass" select="if (@rend) then (concat(@rend, 'insertion-', @place, ' ', $hand)) else (concat('insertion-', @place, ' ', $hand))"/> <xsl:with-param name="childStyle" select="$childStyle"/> <xsl:with-param name="parentStyle" select="$parentStyle"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <span class="inline {$hand}"> <xsl:apply-templates/> </span> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- Process notes --> <xsl:template match="tei:note[@type = 'authorial']"> <xsl:param name="id"/> <xsl:if test="preceding-sibling::tei:lb[1][@xml:id = $id]"> <span class="{@place} {replace(@hand, '#', '')}"> <xsl:apply-templates/> </span> </xsl:if> </xsl:template> <!-- Process metamarks --> <xsl:template match="tei:metamark"> <xsl:param name="id"/> <xsl:choose> <xsl:when test="@target"> <xsl:variable name="target" select="if (contains(@target, ' ')) then (substring-before(substring-after(@target, '#'), ' ')) else (substring-after(@target, '#'))"/> <span id="{@xml:id}" class="metamark {replace(replace(@rend, '#', ''), '\*','')}" onmouseover="toggleHighlight('{$target}', true)" onmouseout="toggleHighlight('{$target}', false)"> <xsl:apply-templates/> </span> </xsl:when> <xsl:otherwise> <span id="{@xml:id}" class="{replace(replace(@rend, '#', ''), '*','')}"> <xsl:apply-templates/> </span> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- unprocessed tags ...--> <xsl:template match="tei:note[@type = 'private']"/> <xsl:template match="tei:certainty"/> <xsl:template match="tei:noteGrp|tei:note[empty(@type) or not(@type = 'authorial')]"/> <xsl:template match="tei:pb"/> <xsl:template match="tei:del[@rend='overwritten']|tei:add[@place='superimposed']"/> </xsl:stylesheet> \ No newline at end of file diff --git a/xslt/sourceDoc.xsl b/xslt/sourceDoc.xsl index e1ec717..1d7b62e 100644 --- a/xslt/sourceDoc.xsl +++ b/xslt/sourceDoc.xsl @@ -1,141 +1,142 @@ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tei="http://www.tei-c.org/ns/1.0" version="2.0"> <xsl:import href="elementTemplates.xsl"/> <xsl:output method="html" encoding="UTF-8"/> <!-- Param 'resources' specifies the root folder for css and scripts --> <xsl:param name="resources" select="'resources'"/> <!-- Param 'fullpage' specifies whether output should be a standalone html page or just a transkription as part of a <div> --> <xsl:param name="fullpage" select="'true'"/> <xsl:variable name="TITLE" select="//tei:titleStmt/tei:title"/> <!-- Transform root to html either as standalone or as part of a page depending on param 'fullpage' --> <xsl:template match="/"> <xsl:choose> <xsl:when test="$fullpage = 'true'"> <html> <head> <title> <xsl:value-of select="$TITLE"/> </title> <link rel="stylesheet" href="{concat($resources, '/css/gui_style.css')}"/> <script src="{concat($resources, '/scripts/gui_transcription.js')}"/> </head> <!--<body onload="updatePositions()">--> <body> <h1>Diplomatische Transkription: <xsl:value-of select="$TITLE"/> </h1> <xsl:apply-templates select="/tei:TEI/tei:text/tei:body"/> </body> </html> </xsl:when> <xsl:otherwise> <div> <h1>Diplomatische Transkription: <xsl:value-of select="$TITLE"/> </h1> <xsl:apply-templates select="/tei:TEI/tei:text/tei:body"/> </div> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- Process tei:div1: produce top forme work container, transkription and bottom forme work container --> <xsl:template match="tei:body/tei:div1"> <!--<div class="fw-container"> <xsl:apply-templates select="tei:fw[@place='top-left' or @place='top-right']"/> </div>--> <xsl:apply-templates select="//tei:sourceDoc/tei:surface[@start = concat('#', //tei:pb/@xml:id)]"/> <!--<div class="fw-container"> <xsl:apply-templates select="tei:fw[@place='bottom-left']"/> </div>--> </xsl:template> <xsl:template match="tei:surface"> <xsl:variable name="style" select="@style"/> <div id="transkription" class="transkriptionField" style="{$style}"> <xsl:apply-templates/> </div> </xsl:template> <xsl:template match="tei:zone"> <xsl:variable name="zone" select="substring-after(@start, '#')"/> <xsl:element name="div"> <xsl:if test="@xml:id and (empty(@type) or ends-with(@type, 'Block') or not(tei:line))"> <xsl:attribute name="id"> <xsl:value-of select="@xml:id"/> </xsl:attribute> </xsl:if> <xsl:if test="@style and (empty(@type) or ends-with(@type, 'Block') or not(tei:line))"> <xsl:attribute name="style"> <xsl:value-of select="@style"/> </xsl:attribute> </xsl:if> <xsl:if test="@type"> <xsl:attribute name="class"> <xsl:value-of select="@type"/> </xsl:attribute> </xsl:if> <xsl:choose> <xsl:when test="tei:line"> <xsl:apply-templates> <xsl:with-param name="zoneId" select="$zone"/> </xsl:apply-templates> </xsl:when> <xsl:otherwise> <xsl:call-template name="zoneItems"> <xsl:with-param name="id" select="$zone"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:element> </xsl:template> <xsl:template match="tei:line"> <xsl:param name="zoneId"/> <xsl:variable name="lineClass" select="if (empty(parent::tei:zone/@type) or ends-with(parent::tei:zone/@type, 'Block')) then ('line') else ('zoneLine')"/> <xsl:variable name="startId" select="substring-after(@start, '#')"/> <xsl:variable name="endId" select="if (following-sibling::tei:line) then (substring-after(following-sibling::tei:line[1]/@start, '#')) else (if (parent::tei:zone/following-sibling::tei:*[1]/local-name() = 'line') then (substring-after(parent::tei:zone/following-sibling::tei:line[1]/@start, '#')) else (substring-after(parent::tei:zone/following-sibling::tei:zone[1]/tei:line[1]/@start,'#')))"/> <xsl:variable name="isZone" select="if (contains(parent::tei:zone/@type, 'zone') or tei:zone/@type = 'head') then ('true') else ('false')"/> <xsl:variable name="spanType" select="concat(@hand, ' ', @rend,' ',tei:zone/@type)"/> <xsl:variable name="spanStyle" select="if ($isZone = 'true') then ( if (tei:zone/@xml:id) then (tei:zone/@style) else (parent::tei:zone/@style)) else ()"/> <div id="{@xml:id}" class="{$lineClass}" style="{@style}"> <xsl:call-template name="writeLineNumber"> <xsl:with-param name="lineType" select="$lineClass"/> <xsl:with-param name="n" select="//tei:lb[@xml:id = $startId]/@n"/> + <xsl:with-param name="zoneId" select="parent::tei:zone/@xml:id"/> </xsl:call-template> <xsl:element name="span"> <xsl:call-template name="writeContentSpanAttributes"> <xsl:with-param name="parentZoneId" select="if (tei:zone/@xml:id) then (tei:zone/@xml:id) else (parent::tei:zone/@xml:id)"/> <xsl:with-param name="isZone" select="$isZone"/> <xsl:with-param name="spanClass" select="$spanType"/> <xsl:with-param name="spanStyle" select="$spanStyle"/> </xsl:call-template> <xsl:choose> <!-- Simple case: nodes/text between two lb --> <xsl:when test="$endId and count(//(*|text())[preceding-sibling::tei:lb[@xml:id = $startId] and following-sibling::tei:lb[@xml:id = $endId]]) gt 0"> <xsl:apply-templates select="//(*|text())[preceding-sibling::tei:lb[@xml:id = $startId] and following-sibling::tei:lb[@xml:id = $endId]]"/> </xsl:when> <!-- Hierarchical case 1: nodes/text between two lb, second lb inside a tag --> <xsl:when test="$endId and count(//(*|text())[preceding-sibling::tei:lb[@xml:id = $startId]]/../node()/tei:lb[@xml:id = $endId]) gt 0"> <xsl:apply-templates select="//(*|text())[(preceding-sibling::tei:lb[@xml:id = $startId] or ancestor::*/preceding-sibling::tei:lb[@xml:id = $startId]) and (following-sibling::*//tei:lb[@xml:id = $endId] or following-sibling::tei:lb[@xml:id = $endId])]"/> </xsl:when> <!-- Hierarchical case 2: first lb inside a tag --> <xsl:when test="$endId"> <xsl:choose> <!-- Hierarchical case 2a: only one lb inside a tag --> <xsl:when test="count(//(*|text())[preceding-sibling::tei:lb[@xml:id = $startId]]/../tei:lb) eq 1"> <xsl:apply-templates select="//(*|text())[preceding-sibling::tei:lb[@xml:id = $startId]]"/> </xsl:when> <!-- Hierarchical case 2b: several lbs inside a tag, current lb is last lb inside tag --> <xsl:otherwise> <xsl:apply-templates select="//(*|text())[preceding-sibling::tei:lb[@xml:id = $startId]]/../(*|text())[preceding-sibling::tei:lb[@xml:id = $startId]]"/> </xsl:otherwise> </xsl:choose> </xsl:when> <!-- Nodes/text after last lb in div2 --> <xsl:otherwise> <xsl:apply-templates select="//(*|text())[preceding-sibling::tei:lb[@xml:id = $startId]]"> <xsl:with-param name="id" select="$startId"/> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> </xsl:element> </div> </xsl:template> </xsl:stylesheet> \ No newline at end of file