You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
14 KiB
PHP
369 lines
14 KiB
PHP
<?php
|
|
#ini_set('display_errors','1'); error_reporting(E_ALL ^ (E_NOTICE | E_WARNING));
|
|
if (!defined("_ECRIRE_INC_VERSION")) return;
|
|
#---------------------------------------------------#
|
|
# Plugin : AScore #
|
|
# Auteur : Patrice Vanneufville, 2021 #
|
|
# Contact : patrice¡.!vanneufville¡@!laposte¡.!net #
|
|
# Licence : GPL #
|
|
#--------------------------------------------------------------------------#
|
|
# Documentation : https://contrib.spip.net/ #
|
|
#--------------------------------------------------------------------------#
|
|
|
|
defined('_VEROVIO_SCRIPT') || define('_VEROVIO_SCRIPT',
|
|
'<script src="https://www.verovio.org/javascript/latest/verovio-toolkit.js" type="text/javascript"></script>'
|
|
// '<script src="https://www.verovio.org/javascript/latest/verovio-toolkit-hum.js" type="text/javascript"></script>'
|
|
// '<script src="https://www.verovio.org/javascript/develop/verovio-toolkit-light.js" type="text/javascript"></script>'
|
|
// '<script src="' . find_in_path('javascript/verovio-toolkit-hum.js') . '" type="text/javascript" id="verovio"></script>'
|
|
. '<script src="' . find_in_path('javascript/svg2bitmap.js') . '" type="text/javascript" id="svg2bitmap"></script>');
|
|
|
|
|
|
defined('_VEROVIO_CSS') || define('_VEROVIO_CSS', '<style>
|
|
.ascore_msg {
|
|
color:darkred;
|
|
font-weight:bold;
|
|
}
|
|
.ascore_download {
|
|
margin-top: 0.8em;
|
|
}
|
|
pre[rel]:not([rel=""]):before {
|
|
font-family: Gotham Narrow SSm A,Gotham Narrow SSm B,Rubik,Lato,Lucida Grande,Lucida Sans Unicode,Tahoma,Sans-Serif;
|
|
font-style: normal;
|
|
font-weight: 800;
|
|
font-size: .6rem;
|
|
content: attr(rel);
|
|
color: #fff;
|
|
position: absolute;
|
|
top: 0.1rem;
|
|
right: 0.4rem;
|
|
padding: 0;
|
|
color: #3c78a7!important;
|
|
}
|
|
pre.ascore_code {
|
|
width: 98%;
|
|
padding: 0.8em;
|
|
position:relative;
|
|
text-align: left;
|
|
line-height: normal;
|
|
font-size: .7rem;
|
|
max-height: 250px;
|
|
overflow: auto;
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
word-wrap: break-word;
|
|
}
|
|
pre.ascore_code code {
|
|
background: transparent;
|
|
}
|
|
.ascore_center {
|
|
text-align: center;
|
|
}
|
|
.ascore_right {
|
|
text-align: right;
|
|
}
|
|
.ascore_left {
|
|
text-align: left;
|
|
}
|
|
</style>');
|
|
|
|
// Format JSON : g.pgHead tspan.text
|
|
defined('_VEROVIO_CSS_TITRE') || define('_VEROVIO_CSS_TITRE', '"font-weight": "bold", "font-family": "sans-serif"');
|
|
|
|
|
|
// fonction pre-traitement, pipeline pre_propre
|
|
function ascore_pre_typo($texte) {
|
|
if(!strlen($texte)) return '';
|
|
$listeBalises = array('abc', 'pae');
|
|
// ne sachant pas quelle balise est la premiere...
|
|
$texte = str_replace(
|
|
array(_SCORE_ABC_DEBUT, _SCORE_ABC_FIN, _SCORE_PAE_DEBUT, _SCORE_PAE_FIN),
|
|
array(_SCORE_DEBUT."(abc)", _SCORE_FIN, _SCORE_DEBUT."(pae)", _SCORE_FIN),
|
|
$texte
|
|
);
|
|
// puis recherche des balises
|
|
if(strpos($texte, _SCORE_DEBUT)===false || strpos($texte, _SCORE_FIN)===false)
|
|
return $texte;
|
|
// isolation du code de la partition
|
|
list($texteAvant, $suite) = explode(_SCORE_DEBUT, $texte, 2);
|
|
list($texte, $texteApres) = explode(_SCORE_FIN, $suite, 2);
|
|
$optionsJS = $xOptionsJS = array();
|
|
$format = $options = $msg = '';
|
|
// autre balise que _SCORE_DEBUT ?
|
|
foreach($listeBalises as $b)
|
|
if(strncmp($texte, "($b)", $l = strlen($b)+2) === 0) {
|
|
$format = $b;
|
|
$texte = substr($texte, $l);
|
|
}
|
|
// le xml est-il protégé par <html/>?
|
|
if(strncmp($texte, '<span class="base64"', 20)===0 || strncmp($texte, '<div class="base64"', 19)===0)
|
|
$texte = echappe_retour($texte);
|
|
// grrrr si SPIP a tout pete
|
|
if(strpos($texte, 'base64')!==false || strpos($texte, '<img ')!==false) {
|
|
$msg = "\n_ ".htmlentities(($format?"<$format>":_SCORE_DEBUT).'<html>'._T('ascore:vos_donnees').'</html>'.($format?"<$format>":_SCORE_FIN));
|
|
$msg = _T('ascore:donnees_alterees', array('syntaxe' => $msg));
|
|
}
|
|
// des options de configuration ?
|
|
if(strpos($texte, '[data]')!==false) {
|
|
list($options, $texte) = explode('[data]', $texte, 2);
|
|
$options = ascore_parse_options($options);
|
|
foreach ($options as $opt) if($opt[2]) {
|
|
$val = ascore_check_boolean($opt[3]);
|
|
$key = ascore_aliases($opt[2]);
|
|
if(substr($key, 0, 1)==='x') $xOptionsJS[$key] = $val;
|
|
else $optionsJS[$key] = $val;
|
|
}
|
|
}
|
|
$texte = trim($texte);
|
|
if(!isset($xOptionsJS['xTitreCSS']))
|
|
$xOptionsJS['xTitreCSS'] = _VEROVIO_CSS_TITRE;
|
|
$mini = isset($xOptionsJS['xMini']) && $xOptionsJS['xMini'];
|
|
// syntaxe simplifiee : mini = 35 500 (zoom largeur)
|
|
if($mini && preg_match(',(\d+)\s+(\d+),', $xOptionsJS['xMini'], $regs)) {
|
|
$xOptionsJS['xMini'] = $regs[1];
|
|
if(!isset($xOptionsJS['xLargeur'])) $xOptionsJS['xLargeur'] = $regs[2];
|
|
}
|
|
// syntaxe simplifiee : inline = 35 500 1em (zoom largeur declageY)
|
|
$inline = isset($xOptionsJS['xInline']) && $xOptionsJS['xInline'];
|
|
if($inline && preg_match(',(\d+)\s+(\d+)\s*([^\s]*),', $xOptionsJS['xInline'], $regs)) {
|
|
$xOptionsJS['xMini'] = $regs[1];
|
|
if(!isset($xOptionsJS['xLargeur'])) $xOptionsJS['xLargeur'] = $regs[2];
|
|
$xOptionsJS['xStyle'] = 'display:inline-block; ';
|
|
if($regs[3]) $xOptionsJS['xStyle'] .= "transform: translateY($regs[3]); ";
|
|
$xOptionsJS['xBalise'] = 'span';
|
|
}
|
|
if( (isset($xOptionsJS[$zoom='xZoom']) && $xOptionsJS['xZoom'])
|
|
|| (isset($OptionsJS[$zoom='scale']) && $OptionsJS['scale']) ) {
|
|
if($mini)
|
|
$msg = ($msg?"<br/>":'') . typo(_T('ascore:ignore_zoom', array('oups'=>'mini', 'zoom' => ascore_aliases($zoom, true))));
|
|
elseif($inline)
|
|
$msg = ($msg?"<br/>":'') . typo(_T('ascore:ignore_zoom', array('oups'=>'inline', 'zoom' => ascore_aliases($zoom, true))));
|
|
else
|
|
$optionsJS['scale'] = $xOptionsJS['xZoom'];
|
|
}
|
|
if(isset($xOptionsJS['xMarges']) && preg_match(',(\d+)(?:[^\d]+(\d+))?(?:[^\d]+(\d+))?(?:[^\d]+(\d+))?,', $xOptionsJS['xMarges'], $regs)) {
|
|
// syntaxe similaire au margin CSS
|
|
switch(count($regs)) {
|
|
case 2: $regs[2] = $regs[3] = $regs[4] = $regs[1]; break;
|
|
case 3: $regs[3] = $regs[1]; $regs[4] = $regs[2]; break;
|
|
case 4: $regs[4] = $regs[2]; break;
|
|
}
|
|
list(, $optionsJS['pageMarginTop'], $optionsJS['pageMarginRight'], $optionsJS['pageMarginBottom'], $optionsJS['pageMarginLeft']) = $regs;
|
|
}
|
|
if($format == 'abc') {
|
|
$optionsJS['inputFrom'] = 'abc';
|
|
$texte = ascore_corrige_abc($texte, $xOptionsJS, $mini);
|
|
} elseif($format == 'pae') {
|
|
$optionsJS['inputFrom'] = 'pae';
|
|
$texte = ascore_corrige_pae($texte, $xOptionsJS, $mini);
|
|
}
|
|
$balise = isset($xOptionsJS['xBalise']) ? $xOptionsJS['xBalise'] : 'div';
|
|
$data = $texte ? base64_encode(unicode2charset(charset2unicode($texte),'iso-8859-1')) : '';
|
|
$optionsJS = json_encode($optionsJS, JSON_FORCE_OBJECT + JSON_NUMERIC_CHECK);
|
|
$xOptionsJS = json_encode($xOptionsJS, JSON_FORCE_OBJECT + JSON_NUMERIC_CHECK);
|
|
$source = hash('sha256', $data . $optionsJS . $xOptionsJS);
|
|
// recherche du cache
|
|
$dir = sous_repertoire(_DIR_CACHE, 'ascore');
|
|
$recalcul = in_array(_request('var_mode'), array(/*'calcul',*/ 'recalcul', 'preview'));
|
|
if(!$recalcul && lire_fichier($dir . $source . '.html', $c)) {
|
|
// utilisation du cache puisqu'il existe
|
|
$c = unicode2charset(charset2unicode($c,'iso-8859-1'));
|
|
$score = code_echappement($c, '', true);
|
|
} else {
|
|
// recuperation du fond 'modele/score.html'
|
|
$score = trim(recuperer_fond('modeles/score', array(
|
|
'balise' => $balise,
|
|
'data' => $data,
|
|
'source' => $source,
|
|
'id_score' => 0,
|
|
'msg' => $msg,
|
|
'options' => $optionsJS,
|
|
'x_options' => $xOptionsJS,
|
|
)));
|
|
}
|
|
if($inline)
|
|
$score = code_echappement($score, '', true, 'span');
|
|
return $texteAvant . $score . ascore_pre_typo($texteApres, true);
|
|
}
|
|
|
|
// fonction qui retire les commentaires simples (un seul "%")
|
|
// les pseudos-commentaires (ligne debutant par "%%") sont preserves
|
|
// le resultat est passe par trim()
|
|
function acsore_scan_comments_abc($ligne) {
|
|
$i = strpos($ligne, '%');
|
|
if($i === false) return $ligne;
|
|
// protection pseudos-commentaire
|
|
if(strncmp($ligne = trim($ligne), '%%', 2) === 0) return $ligne . ' %';
|
|
// suppression du commentaire
|
|
$ligne = substr($ligne, 0, $i);
|
|
return trim($ligne);
|
|
}
|
|
|
|
// fonction qui ordonne et complete correctement les entetes
|
|
// rappel section d'entete ; "X:" en premier, "T:" en second, "K:" en dernier
|
|
// $mini est un mode d'affichage simplifie
|
|
function ascore_corrige_abc($texte, &$xOptionsJS, $mini = false) {
|
|
if(!($texte = trim($texte))) return '';
|
|
$texte = preg_split('/[\r\n]+/', $texte);
|
|
$x = 0; $t = 100 ; $e = 200; $k = 800; $z = 900;
|
|
$chiffrage = $tonalite = '';
|
|
$out = array(); $entete = true;
|
|
foreach($texte as $i=>$ligne) {
|
|
// syntaxe servant a decaler horizontalement des annotations placees au-dessus de la portee
|
|
// Exemple "-2.9%|"zAB
|
|
// Ici on protege le "%" qui est une marque de commentaire en format ABC
|
|
if(strpos($ligne, '"') !== false)
|
|
$ligne = preg_replace(',"(\-?[\d\.]+)%([^"]+)"z,', '"$1pc$2"z', $ligne);
|
|
// suppression des commentaires
|
|
$ligne = acsore_scan_comments_abc($ligne);
|
|
// est-ce une ligne d'entete ?
|
|
$entete = $entete && preg_match(',^([A-Za-z]):,', $ligne, $regs);
|
|
if($entete) {
|
|
if($regs[1]=='X') $out[$x++] = $ligne; // section
|
|
elseif($regs[1]=='T') $out[$t++] = $ligne; // titre
|
|
elseif($regs[1]=='K') $out[$k++] = $tonalite // clef & tonalite
|
|
= str_replace(array('rythme', 'rhythm', 'percussion'), 'perc stafflines=1', $ligne);
|
|
elseif($regs[1]=='M') $out[$e++] = $chiffrage = $ligne; // chiffrage
|
|
else $out[$e++] = $ligne;
|
|
}
|
|
else $out[$z++] = $ligne;
|
|
}
|
|
$nb0 = count($out);
|
|
// insertion des entetes necessaires
|
|
if(!$x) $out[0] = 'X: 1';
|
|
if($t == 100) $out[$t] = 'T:';
|
|
if($k == 800) $out[$k] = $mini ? 'K: C' : 'K:';
|
|
//if($z == 900) $out[$z] = 'x';
|
|
ksort($out);
|
|
if($nb0==1 && strlen($tonalite))
|
|
// raccourci pour une cle et/ou une armure seule "K: bla bla"
|
|
$out = "X: 1\nT:\n$tonalite\nx";
|
|
elseif($nb0==1 && strlen($chiffrage))
|
|
// raccourci pour un chiffrage seul "M: bla bla"
|
|
$out = "X: 1\nT:\n$chiffrage\nK: perc stafflines=0\nx";
|
|
elseif($nb0==1 && isset($out[900]) && preg_match(',^\d+/\d+$,', $chiffrage=$out[900]))
|
|
// raccourci pour un chiffrage seul type "12/8"
|
|
$out = "X: 1\nT:\nM: $chiffrage\nK: perc stafflines=0\nx";
|
|
else
|
|
$out = trim(implode("\n", $out));
|
|
// barre de mesure si absente, car le toolkit n'aime pas ça pour l'instant...
|
|
if(/*$mini && */substr($out, -1)!=='|' && substr($out, -2)!=='|]') {
|
|
$out .= '|';
|
|
$xOptionsJS['xClass'] = (isset($xOptionsJS['xClass']) && $xOptionsJS['xClass'])
|
|
? $xOptionsJS['xClass'] . ' sansBarreDeFin' : 'sansBarreDeFin';
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
// fonction qui complete correctement les entetes
|
|
function ascore_corrige_pae($texte, &$xOptionsJS, $mini = false) {
|
|
if(!($texte = trim($texte))) return '';
|
|
if(strpos($texte, '@data:')===false)
|
|
$texte = '@data:' . $texte;
|
|
return $texte;
|
|
}
|
|
|
|
|
|
/*
|
|
// pipeline ramasse-miettes permet de recuperer le HTML propre *après* la reinsertion des modeles
|
|
// les scripts de partitions (base ou inline) sont empeches en partie privee, il a fallu les echapper.
|
|
// textwheel >= 1.3.5
|
|
function ascore_post_echappe_html_propre($texte) {
|
|
return echappe_retour($texte, 'VEROVIO_SCRIPTS');
|
|
}
|
|
*/
|
|
|
|
// pour inserer un js
|
|
function ascore_javascript($b) {
|
|
$f = find_in_path("javascript/$b.js");
|
|
$out = array();
|
|
foreach(array('option_inconnue', 'doc_introuvable', 'log', 'inline_introuvable' ) as $i => $l)
|
|
$out[] = $i . ': "' . addslashes(typo(_T('ascore:erreur_'.$l))) . '"';
|
|
return "<script type=\"text/javascript\">\nconst ascore_errors = {\n\t"
|
|
. implode(",\n\t", $out)
|
|
. "\n};\n</script>"
|
|
. ($f ? '<script type="text/javascript" src="'.$f."\"></script>\n" : '');
|
|
}
|
|
|
|
// pipeline header_prive
|
|
function ascore_header_prive($flux) {
|
|
$flux .= _VEROVIO_CSS . _VEROVIO_SCRIPT . ascore_javascript('verovio');
|
|
return $flux;
|
|
}
|
|
|
|
// pipeline insert_head
|
|
function ascore_insert_head($flux) {
|
|
if(_request('var_mode'))
|
|
return ascore_header_prive($flux);
|
|
if(!defined('_VEROVIO_HEAD'))
|
|
include_spip('ascore_fonctions');
|
|
return $flux . _VEROVIO_CSS . _VEROVIO_HEAD;
|
|
}
|
|
|
|
// Ajouter le modele score a la liste des modeles de documents
|
|
function ascore_declarer_tables_objets_sql($tables) {
|
|
$tables['spip_documents']['modeles'][] = 'score';
|
|
return $tables;
|
|
}
|
|
|
|
// Le pipeline affichage_final, execute a chaque hit sur toute la page
|
|
// Incorporation du js dans <head> a la place de _VEROVIO_HEAD, si un score
|
|
// est present dans la page
|
|
function ascore_affichage_final($flux) {
|
|
if(!defined('_VEROVIO_HEAD')) include_spip('ascore_fonctions');
|
|
return str_replace(_VEROVIO_HEAD,
|
|
strpos($flux, 'modele_ascore')!==false ? _VEROVIO_SCRIPT . ascore_javascript('verovio') : '',
|
|
$flux);
|
|
}
|
|
|
|
/*
|
|
// Afficher la configuration trouvee
|
|
function ascore_affiche_milieu($flux){
|
|
if ($flux['args']['exec'] == 'article') {
|
|
$flux['data'].= ascore_configuration_inline($flux['args']['id_article']);
|
|
}
|
|
return $flux;
|
|
}*/
|
|
|
|
// decoder une ligne de config (retrait des comentaires facon PHP et mise en forme du parametre)
|
|
// retour : array(param, valeur, param original, ligne epuree)
|
|
// code issu du plugin Jeux
|
|
function ascore_parse_ligne_options($ligne) {
|
|
$ligne = preg_replace(',\/\*(.*?)\*\/,', '', $ligne);
|
|
$ligne = trim(preg_replace(',\/\/(.*)$,', '', $ligne));
|
|
$arr = strpos($ligne, '=')!==false ? explode('=', $ligne, 2) : explode(':', $ligne, 2);
|
|
if(count($arr) != 2) return array(false, false, false, strlen($ligne) ? '/* ' . $ligne . '*/' : false);
|
|
$arr[0] = trim($arr[0]); $arr[1] = trim($arr[1]);
|
|
return array($arr[0], $arr[1], $arr[0], $arr[0] . ': ' . $arr[1]);
|
|
}
|
|
|
|
// decoder une section d'optionis
|
|
// retourne le tableau des parametres : array[] = array($ligne epuree, $param_original, $param, $valeur, $ligne_originale)
|
|
// code issu du plugin Jeux
|
|
function ascore_parse_options($texte_config) {
|
|
$arr_config = array();
|
|
// s'il existe un commentaire on abandonne le séparateur "," : chaque option doit être sur une ligne differente
|
|
$regexp = strpos($texte_config, '/')!==false ? '/[\r\n]+/' : '/[\r\n]+|,/';
|
|
$texte_config = preg_split($regexp, trim($texte_config));
|
|
foreach ($texte_config as $ligne) {
|
|
// array(param, valeur, param original, ligne epuree)
|
|
$arr = ascore_parse_ligne_options($ligne);
|
|
if(strlen($arr[3])) {
|
|
if($arr[0])
|
|
$arr_config[] = array($arr[3], $arr[2], $arr[0], $arr[1], $ligne);
|
|
else
|
|
$arr_config[] = array($arr[3], false, false, false, $ligne);
|
|
}
|
|
}
|
|
return $arr_config;
|
|
}
|
|
|
|
function ascore_check_boolean($value) {
|
|
if(in_array($value, array('true', 'vrai', 'oui', 'yes', 'on', /*'1', */'si', 'ja', strtolower(_T('item_oui')))))
|
|
return true;
|
|
if(in_array($value, array('false', 'faux', 'non', 'no', 'off', /*'0', */'nein', strtolower(_T('item_non')))))
|
|
return false;
|
|
return $value;
|
|
}
|
|
|
|
?>
|