summaryrefslogtreecommitdiff
path: root/templater_common.php
diff options
context:
space:
mode:
authorNiklas Olmes <niklas@olmes.de>2026-04-24 19:30:00 +0200
committerNiklas Olmes <niklas@olmes.de>2026-04-24 19:30:00 +0200
commitcdea8caa5617f0cb77bcbc9803759abd2df50644 (patch)
tree2f7f1bd3af3b2396baf5403ad1a7ad00bcb7fae9 /templater_common.php
stipcrmHEADmain
Diffstat (limited to 'templater_common.php')
-rw-r--r--templater_common.php567
1 files changed, 567 insertions, 0 deletions
diff --git a/templater_common.php b/templater_common.php
new file mode 100644
index 0000000..7f6a684
--- /dev/null
+++ b/templater_common.php
@@ -0,0 +1,567 @@
+<?php
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+?>
+<?
+/* vim: set ts=4 sw=4 et : */
+
+require_once __DIR__ . "/lookup.php";
+require_once __DIR__ . "/lookup_more.php";
+
+(function () use ($mysqli, $sql, $payload_sql, $independent_payload_sql, $payload_sql_order, $independent_payload_sql_order, $isemail, $post_sql) {
+global $abschlusskombis, $studiengaenge, $signaturen;
+
+$stips_sql = "SELECT Person FROM Stipendien WHERE Jahr >= 2001 AND Förderbeginn > '2000-01-01' GROUP BY Person;";
+$stmt = $mysqli->prepare($stips_sql);
+$stmt->bind_result($stips_persid);
+$stmt->execute();
+$stips = [];
+while ($stmt->fetch()) {
+ $stips[$stips_persid] = 1;
+}
+$stmt->reset();
+?>
+<script>
+var Stips = {};
+try {
+ Stips = JSON.parse('<? echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($stips))); ?>');
+} catch (e) { ; }
+</script>
+<?
+
+$prefs_sql = "SELECT persid, salutation FROM Personen_Prefs WHERE userid=?;";
+$stmt = $mysqli->prepare($prefs_sql);
+$stmt->bind_param("i", $_SESSION['auth_userid']);
+$stmt->bind_result($prefs_persid, $prefs_salutation);
+$stmt->execute();
+$prefs = [];
+while ($stmt->fetch()) {
+ $prefs[$prefs_persid] = $prefs_salutation;
+}
+$stmt->reset();
+?>
+<script>
+var Prefs = {};
+try {
+ Prefs = JSON.parse('<? echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($prefs))); ?>');
+} catch (e) { ; }
+</script>
+<?
+
+$ids = explode(',', $_POST['ids']);
+
+$ids_in = "(";
+$first = true;
+foreach ($ids as $i) {
+ if (!$first) $ids_in .= ', ';
+ $first = false;
+ $ids_in .= "'" . $i . "'";
+}
+$ids_in .= ")";
+
+$sql = $sql . ' ' . $ids_in;
+$sql = $sql . ' ' . $post_sql;
+
+if ($isemail && isset($_SESSION['singleemail'])) {
+?>
+ <div style="border: 1px solid grey; padding: .2em; margin-top: .5em; max-width: 700px;">
+ <b>Einzel-E-Mail-Modul ist aktiv. Alle Adressaten werden zusammengefasst und sind direkter Adressat (To) der E-Mail.</b> Da sich damit alle Adressaten gegenseitig sehen können, sollte diese Funktion nur verwendet werden, wenn dies datenschutzrechtlich unbedenklich ist. Individualisierung durch Templateauszeichnungen ist selbstredent nicht möglich.
+ <br />
+ <button class="small" style="background: lightcoral;" onclick="$.ajaxSetup({async:false}); $.post('/db/main/nosingleemail.php'); location.reload(); return false;">Einzel-E-Mail-Modus beenden</button>
+ </div>
+<?
+ $sql = "SELECT GROUP_CONCAT(q.Email) AS Email FROM (" . $sql . ") q";
+}
+
+$stmt = $mysqli->prepare($sql);
+$stmt->execute();
+$r = $stmt->get_result();
+echo $mysqli->error;
+
+$data = [];
+$i = 0;
+while (($l = $r->fetch_object())) {
+ $e = [];
+ foreach ($l as $k => $v) {
+ $e[$k] = $v;
+ if ($k == 's2' || $k == 's3' || $k == 's4' || $k == 's5' || $k == 's6' || $k == 'data')
+ $e[$k] = json_decode($v);
+ }
+ $e['index'] = $i;
+ $data[] = $e;
+ $i++;
+}
+$stmt->reset();
+
+if ($payload_sql) {
+ $payload_sql = $payload_sql . $ids_in;
+ if ($payload_sql_order)
+ $payload_sql .= " ORDER BY " . $payload_sql_order;
+ $stmt = $mysqli->prepare($payload_sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ echo $mysqli->error;
+
+ $payload = [];
+ $i = 0;
+ while (($l = $r->fetch_object())) {
+ $e = [];
+ foreach ($l as $k => $v) {
+ $e[$k] = $v;
+ }
+ $e['index'] = $i;
+ $payload[] = $e;
+ $i++;
+ }
+ $stmt->reset();
+}
+
+if ($independent_payload_sql) {
+ if ($independent_payload_sql_order)
+ $independent_payload_sql .= " ORDER BY " . $independent_payload_sql_order;
+ $stmt = $mysqli->prepare($independent_payload_sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ echo $mysqli->error;
+
+ $independent_payload = [];
+ $i = 0;
+ while (($l = $r->fetch_object())) {
+ $e = [];
+ foreach ($l as $k => $v) {
+ $e[$k] = $v;
+ }
+ $e['index'] = $i;
+ $independent_payload[] = $e;
+ $i++;
+ }
+ $stmt->reset();
+}
+?>
+
+<h3 style="margin-bottom: 0;">Template <i style="cursor: pointer;" onclick="$('#template_info').toggle();" class="fas fa-info-circle"></i></h3>
+
+<p id="template_info" style="display: none;" class="explain">
+ Felder mit <code class="explain">{{ d.pfad.feld }}</code> auszeichnen, z.B. <code class="example">{{ d.s2.Nachname }}</code> &ndash; oder unten aktuell verfügbare Felder klicken zum Einf&uuml;gen an Cursorposition
+ <br />
+ Zeilenumbruch statt Absatz mit <code class="explain">Umschalt-Enter</code> oder besser nachvollziehbar mit <code class="example">[br]</code>
+ <br />
+ Seitenumbruch im PDF mit <code class="explain">[formfeed]</code>
+ <br />
+ HTML-Tags mit <code class="explain">[]</code> umschlie&szlig;en, z.B. <code class="example">[hr]</code> (f&uuml;r horizontale Trennlinie) oder <code class="example">[div style="border: 1px solid black;"]Box[/div]</code> (für div-Element mit 1px Rand)
+ <br />
+ Fallunterscheidung: <code class="explain">{{ d.Jahr == '2013' ? 'Ja, 2013.' : 'Nein, nicht das Jahr 2013' }}</code>
+ <br />
+ Signatur: <code class="explain">{{ 'Hagemann' | signatur }}</code>
+ <br />
+ Automatische Anrede: <code class="explain">{{ 'informal' | autoanrede }}</code> oder <code class="explain">{{ 'formal' | autoanrede }}</code>
+ <br />
+ Filter: <code class="explain">{{ d.Feld | filter }}</code>. Verfügbar:
+ <br />
+ - <code class="explain">filter</code> (generischer Filter, filtert Liste auf <span style="color: lightcoral;">Wert</span> in <span style="color: darkgoldenrod;">Attribut</span>. Bsp.: <code class="example">{{ p | filter:<span style="color: darkgoldenrod;">'persid'</span>:<span style="color: lightcoral;">d.ID</span> }}</code>)
+ <br />
+ - <code class="explain">event</code> (Teilnahmelink zum Event, <code class="example">{{ d.token | event }}</code>)
+ <br />
+ - <code class="explain">eventlist</code> (Ausgabe Eventliste samt Anmeldelink f&uuml;r Person aus Menge gew&auml;hlter Events, bei der die jew. Person eingetragen ist; <code class="example">{{ d.ID | eventlist }}</code>, optionale Parameter: minPartID, CatID, Reihenfolge umdrehen (<code class="example">{{ d.ID | eventlist:0:0:1 }}</code>)
+ <br />
+ - <code class="explain">studiengang</code> (Studiengang anhand ID aus Abschlusskombis, z.B. <code class="example">{{ d.s3.studiengang | studiengang }}</code>, für alte Studiengänge: <code class="example">studiengang_alt</code>)
+ <br />
+ - <code class="explain">my</code> (Link zum Pers&ouml;nlichen Bereich DS anhand UID)
+ <br />
+ - <code class="explain">mysocial</code> (Link zum Pers&ouml;nlichen Bereich Sozialstipendium anhand UID)
+ <br />
+ - <code class="explain">mylue</code> (Link zum Pers&ouml;nlichen Bereich Leistungs&uuml;berpr&uuml;er anhand PersID)
+ <br />
+ - <code class="explain">lue24</code> (Teilnahmelink Leistungs&uuml;berpr&uuml;fung 2024 anhand UID)
+ <br />
+ - <code class="explain">dscontinue</code> (Fortf&uuml;hrlink DS anhand UID)
+ <br />
+ - <code class="explain">socialcontinue</code> (Fortf&uuml;hrlink Sozialstipendium anhand UID)
+ <br />
+ - <code class="explain">dsnachreichung</code> (Link Nachreichungsbereich DS anhand UID)
+ <br />
+ - <code class="explain">alumni21</code> (Link Alumni-Umfrage 2021 anhand PersID)
+ <br />
+ - <code class="explain">komm21</code> (Link Kommissionsmitglieder-Umfrage 2021 anhand PersID)
+ <br />
+ - <code class="explain">{{ x | stiplist:y:z:fb }}</code> (Stipendiatenliste. Parameter: x&rarr;FoerdID, y&rarr;Förderart (0=alle,5=Sozial), z&rarr;Art der Ausgabe (0/1=kompakt, 2=mit Förderzeitraum, 3=mit Förderzeitraum und Förderart, 4=mit Förderzeitraum und vereinfachter Förderart (DS/Sozial), 5=mit Teilnahme bei Event (Niklas muss das vorher explizit einstellen!), fb&rarr;erst ab Förderbeginn JJJJ-MM-TT Bsp.: <code class="example">{{ d.FoerdID | stiplist:0:4:'2023-04-01' }}</code>
+ <br />
+ Verfügbare Felder:
+<?
+foreach ($data[0] as $k => $v) {
+?>
+ <a style="text-decoration: underline; color: #0c8ab3 !important; cursor: pointer;" onclick="quill.focus(); quill.insertText(quill.getSelection(true), '{{ d[\'<?=$k?>\'] }}');">d.<?=$k?></a>
+<?
+ if (is_object($data[0][$k])) {
+ foreach ($data[0][$k] as $kk => $v) {
+?>
+ <small>.<a style="text-decoration: underline; cursor: pointer;" onclick="quill.focus(); quill.insertText(quill.getSelection(true), '{{ d[\'<?=$k?>\'][\'<?=$kk?>\'] }}');"><?=$kk?></a></small>
+<?
+ }
+ }
+}
+?>
+</p>
+
+<div class="editorcontainer" style="background: white; color: black;">
+ <div id="editor"><? if ($isemail) {?>{{ '<?=$_SESSION['auth_user']?>' | signatur }}<? } ?></div>
+</div>
+
+<?
+(function () use ($mysqli) {
+ $sql = "
+SELECT id, title
+FROM pages
+WHERE visible = 1 AND oid = 2
+ORDER BY title
+";
+ $stmt = $mysqli->prepare($sql);
+ $stmt->bind_result($pid, $ptitle);
+ $stmt->execute();
+?>
+ <textarea style="display: none;" class="htmlcontainer" style="background: darkbeige !important; color: grey; height: 1em; width: 3em;"></textarea>
+
+<?
+})();
+?>
+
+<script>
+<? minStart(); ?>
+
+var Delta = Quill.import('delta');
+var Break = Quill.import('blots/break');
+var Embed = Quill.import('blots/embed');
+
+function lineBreakMatcher() {
+ var newDelta = new Delta();
+ newDelta.insert({'break': ''});
+ return newDelta;
+}
+
+class SmartBreak extends Break {
+ length () {
+ return 1;
+ }
+ value () {
+ return '\n';
+ }
+ insertInto(parent, ref) {
+ Embed.prototype.insertInto.call(this, parent, ref);
+ }
+}
+SmartBreak.blotName = 'break';
+SmartBreak.tagName = 'BR'
+Quill.register(SmartBreak);
+
+var quill = new Quill('#editor', {
+ theme: 'snow',
+ modules: {
+ toolbar: [
+ [{ header: [1, 2, 3, false] }],
+ [{'size': ['small', false, 'large', 'huge']}],
+ ['bold', 'italic', 'underline', 'strike'],
+ [{ 'color': [] }, { 'background': [] }],
+ ['link', 'blockquote', 'image'],
+ [{'list': 'ordered'}, {'list': 'bullet'}],
+ [{'script': 'sub'}, {'script': 'super'}],
+ [{'indent': '-1'}, {'indent': '+1'}],
+ [{'align': []}],
+ ['clean']
+ ],
+ clipboard: {
+ matchers: [
+ ['BR', lineBreakMatcher]
+ ]
+ },
+ keyboard: {
+ bindings: {
+ linebreak: {
+ key: 13,
+ shiftKey: true,
+ handler: function (range) {
+ var currentLeaf = this.quill.getLeaf(range.index)[0];
+ var nextLeaf = this.quill.getLeaf(range.index + 1)[0];
+
+ this.quill.insertEmbed(range.index, 'break', true, 'user');
+
+ if (nextLeaf === null || (currentLeaf.parent !== nextLeaf.parent)) {
+ this.quill.insertEmbed(range.index, 'break', true, 'user');
+ }
+
+ this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
+ }
+ }
+ }
+ }
+ }
+});
+
+function redoRows(row, d, pum, index, dindex) {
+ if (typeof row === 'undefined')
+ return;
+
+ if (typeof data !== 'object')
+ return;
+
+ if (data[dindex].__done) {
+ row.classList.add('email_success');
+ row.classList.remove('email_failed');
+ }
+ if (data[dindex].__failed) {
+ row.classList.add('email_failed');
+ row.classList.remove('email_success');
+ }
+ if (data[dindex].__skip) {
+ row.classList.add('validated');
+ row.classList.remove('email_failed');
+ row.classList.remove('email_success');
+ }
+}
+
+<? minEnd(); ?>
+</script>
+
+<h3 style="margin-bottom: 0;">Vorschau</h3>
+
+<style>
+ #preview h1, #preview h2, #preview h3, #preview h4, #preview p, #preview * { font-family: "Arial Narrow", "Arial", sans-serif; }
+ #preview ul, #preview ol, #preview li, #preview strong, #preview em, #preview i, #preview b, #preview u, #preview s, #preview span, #preview sub, #preview sup { font-family: "Arial Narrow", "Arial", sans-serif; }
+ #preview blockquote { font-family: "Arial Narrow", "Arial", sans-serif; }
+
+ #preview .ql-size-small { font-size: 70%; }
+ #preview .ql-size-normal { font-size: 100%; }
+ #preview .ql-size-large { font-size: 130%; }
+ #preview .ql-size-huge { font-size: 180%; }
+
+ #preview a { color: blue; text-color: blue; }
+ .ql-editor { padding: 0; }
+</style>
+
+<div id="previewcontainer" style="background: white; color: black; border: 1px solid lightgrey; padding: .5em;">
+ <div id="preview" ng-controller="previewCtrl"></div>
+</div>
+
+<h3 style="margin-bottom: 0;">Adressaten <i onclick="$('#recipient_info').toggle();" class="fas fa-info-circle"></i></h3>
+
+<div id="recipient_info" style="display: none;">
+<p>Mit Klick auf blaue RUID/UID/ID die Vorschau zum entsprechenden Adressaten wechseln.</p>
+<p>Bei Mehrfachverknüpfungen (bspw. Person bei Orga) RUID/UID verwenden, ansonsten wird die Vorschau nur für das angeklickte Merkmal angepasst.</p>
+</div>
+
+<?
+(function () use ($mysqli, $sql) {
+ $stmt = $mysqli->prepare($sql);
+ $stmt->execute();
+ $r = $stmt->get_result();
+ $id = "email";
+ $order = '[]';
+ $noautolinks = true;
+ $nodefaultlinks = true;
+ $links = [
+ '#' => '#',
+ 'RUID' => '#',
+ 'ruid' => '#',
+ 'UID' => '#',
+ 'uid' => '#',
+ 'ID' => '#',
+ 'id' => '#',
+ ];
+ $rowcallback = "redoRows";
+ $norefresh = true;
+ include __DIR__ . '/autotable.php';
+ $stmt->reset();
+})();
+?>
+
+<script>
+
+window.data = JSON.parse('<?php echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($data))); ?>');
+window.payload = JSON.parse('<?php echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($payload))); ?>');
+window.independent_payload = JSON.parse('<?php echo str_replace("'", "\'", str_replace("\\", "\\\\", json_encode($independent_payload))); ?>');
+window.pivot = data[0];
+window.count_s = 0;
+window.count_f = 0;
+
+function bP() {
+ $('.consignee .changeable').parent().parent().css('background', 'unset');
+
+ var dd = $('#editor .ql-editor').html();
+ dd = dd.replace(/┌/g, '<').replace(/┐/g, '>');
+ $('#previewcontainer').html('<div id="preview">' + dd + '</div>');
+ angular.bootstrap($('#preview'), ['previewApp']);
+ angular.element('#preview').scope().d = pivot;
+ angular.element('#preview').scope().p = payload;
+ angular.element('#preview').scope().ip = independent_payload;
+ angular.element('#preview').scope().$apply();
+
+ var a = $('#preview').html();
+ a = a.replace(/\[/g, '<').replace(/\]/g, '>');
+ $('#preview').html(a);
+
+ try { $('input[name=organisation]').val(pivot.organisation || pivot.Organisation); } catch (e) { }
+ try { $('input[name=anrede]').val(pivot.anrede || pivot.Anrede); } catch(e) { }
+ try { $('input[name=anrede_briefkopf]').val(pivot.anrede_briefkopf); } catch (e) { }
+ try { $('input[name=titel]').val(pivot.titel || pivot.Titel); } catch (e) { }
+ try { $('input[name=vorname]').val(pivot.vorname || pivot.Vorname); } catch (e) { }
+ try { $('input[name=nachname]').val(pivot.nachname || pivot.Nachname); } catch (e) { }
+ try { $('input[name=strasse]').val(pivot.strasse || pivot.Strasse || pivot.Straße); } catch (e) { }
+ try { $('input[name=adresszusatz]').val(pivot.adresszusatz || pivot.Adresszusatz); } catch (e) { }
+ try { $('input[name=plz]').val(pivot.plz || pivot.PLZ); } catch (e) { }
+ try { $('input[name=template]').val(pivot.hochschule || pivot.Hochschule); } catch (e) { }
+ try { $('input[name=ort]').val(pivot.ort || pivot.Ort); } catch (e) { }
+ try { $('input[name=uid]').val(pivot.genuid || pivot.RUID || pivot.ruid || pivot.ID || pivot.id || pivot.UID || pivot.uid); } catch (e) { }
+ try { $('input[name=email_to]').val(pivot.email || pivot.Email); } catch (e) { }
+ try { $('input[name=email_id]').val(pivot.uid || pivot.ID || pivot.id); } catch (e) { }
+ try { $('input[name=data]').val(pivot.data || JSON.stringify(pivot)); } catch (e) { }
+}
+
+$(document).ready(function () {
+ window.preview = angular.module('previewApp', []);
+ window.previewCtrl = preview.controller('previewCtrl', ['$scope', function ($scope) {
+ $scope.d = data[0];
+ }]);
+
+ $('textarea.htmlcontainer').on('input change', function () {
+ if ($('textarea.htmlcontainer').val().length > 10) {
+ $('.editorcontainer').hide();
+ $('#previewcontainer').hide();
+ return;
+ }
+ $('.editorcontainer').show();
+ $('#previewcontainer').show();
+ });
+
+ $('.consignee .changeable').on('input change', function () {
+ $(this).parent().parent().css('background', 'yellow');
+ });
+
+ preview.filter('autoanrede', function () {
+ return function (x) {
+ var persid = "";
+
+ if (typeof pivot.PersID !== 'undefined') persid = pivot.PersID;
+ else if (typeof pivot.persid !== 'undefined') persid = pivot.persid;
+ else if (typeof pivot.ID !== 'undefined') persid = pivot.ID;
+ else if (typeof pivot.id !== 'undefined') persid = pivot.id;
+
+ var titel = pivot.Titel;
+ if (titel == null) titel = "";
+
+ var anrede = pivot.Anrede;
+ if (anrede == null) anrede = "";
+
+ var r = "";
+
+ try {
+
+ if ((x === 'informal' && Prefs[persid] !== 1) || Prefs[persid] === 2 || Prefs[persid] === 3 || (Prefs[persid] !== 1 && Stips[persid] === 1)) {
+ if (pivot.Geschlecht.charAt(0) === 'm') {
+ r += "Lieber ";
+ } else if (pivot.Geschlecht.charAt(0) === 'w') {
+ r += "Liebe ";
+ } else {
+ r += "Hallo ";
+ }
+ if (Prefs[persid] !== 3 && Stips[persid] !== 1) {
+ if (pivot.Geschlecht.charAt(0) === 'm') {
+ r += "Herr ";
+ } else if (pivot.Geschlecht.charAt(0) === 'w') {
+ r += "Frau ";
+ }
+ }
+ if (Prefs[persid] === 3 || Stips[persid] === 1) {
+ r += pivot.Vorname;
+ } else {
+ if (titel.indexOf('Prof') !== -1) {
+ if (pivot.Geschlecht.charAt(0) == 'w') {
+ r += "Professorin ";
+ } else {
+ r += "Professor ";
+ }
+ } else if (titel.indexOf('Dr.') !== -1) {
+ r += "Dr. ";
+ } else if (titel != ""){
+ r += titel + " ";
+ }
+ r += pivot.Nachname;
+ }
+ r += ',';
+
+ if (anrede.indexOf("Eheleute") !== -1 || anrede.indexOf("Ehepaar") !== -1) {
+ r = pivot['informale Ansprache'] + " " + pivot.Nachname + ",";
+ }
+ } else if (x === 'formal' || Prefs[persid] == 1) {
+ if (pivot.Geschlecht.charAt(0) === 'm') {
+ r += "Sehr geehrter Herr ";
+ } else if (pivot.Geschlecht.charAt(0) === 'w') {
+ r += "Sehr geehrte Frau ";
+ }
+
+ if (titel.indexOf('Prof') !== -1) {
+ r += "Professor ";
+ } else if (titel.indexOf('Dr.') !== -1) {
+ r += "Dr. ";
+ } else if (titel != ""){
+ r += titel + " ";
+ }
+ r += pivot.Nachname;
+ r += ',';
+
+ if (anrede.indexOf("Eheleute") !== -1 || anrede.indexOf("Ehepaar") !== -1) {
+ r = pivot.Ansprache + " " + pivot.Nachname + ",";
+ }
+ }
+
+ } catch (e) {
+ r = "Hallo " + pivot.Vorname + " " + pivot.Nachname + ",";
+ }
+
+ return r;
+ }
+ });
+
+ preview.filter('filter', function () {
+ return function (x, y, z) {
+ var d = $.grep(x, function (v, i) {
+ return v[y] == z;
+ });
+ return d;
+ }
+ });
+
+ quill.on('text-change', bP);
+ bP();
+});
+
+$(window).on('hashchange', function () {
+ var r = $.grep(data, function (e) {
+ return (e.ruid == location.hash.substr(1) || e.RUID == location.hash.substr(1) || e.uid == location.hash.substr(1) || e.UID == location.hash.substr(1) || e.ID == location.hash.substr(1) || e.id == location.hash.substr(1));
+ });
+ pivot = r[0];
+ bP();
+});
+
+</script>
+
+<style>
+::placeholder {
+ color: lightgrey;
+}
+.mand:placeholder-shown {
+ border: 1px solid red;
+ padding: 1px;
+}
+</style>
+
+<?
+})();
+?>