Commit b6d04a04 authored by Sylvain Coulange's avatar Sylvain Coulange
Browse files

createSerie en cours

parent 38c28ce6
# Generated by Django 3.0.5 on 2020-11-05 10:21
# Generated by Django 3.0.5 on 2020-11-09 14:55
from django.conf import settings
from django.db import migrations, models
......@@ -20,9 +20,12 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('dateCreation', models.DateTimeField(auto_now_add=True)),
('dateModification', models.DateTimeField(auto_now=True)),
('nom', models.CharField(max_length=100, null=True)),
('description', models.TextField(max_length=300)),
('auteur', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
('nom', models.CharField(default='nouvel utilisateur', max_length=100)),
('description', models.CharField(blank=True, max_length=500)),
('mots', models.CharField(default='[]', max_length=10000)),
('listemots', models.CharField(blank=True, default='[]', max_length=500)),
('contenuSerie', models.CharField(blank=True, default='[]', max_length=100000)),
('auteur', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
],
),
]
......@@ -4,10 +4,13 @@ from django.contrib.auth.models import User
class Serie(models.Model):
dateCreation = models.DateTimeField(auto_now_add=True, auto_now=False)
dateModification = models.DateTimeField(auto_now_add=False, auto_now=True)
nom = models.CharField(max_length=100)
description = models.TextField(max_length=300)
auteur = models.ForeignKey(User, on_delete=models.PROTECT)
mots = models.CharField(max_length=10000)
nom = models.CharField(default='nouvel utilisateur', max_length=100)
description = models.CharField(blank=True, max_length=500)
auteur = models.OneToOneField(User, on_delete=models.PROTECT)
mots = models.CharField(default='[]', max_length=10000)
listemots = models.CharField(blank=True, default='[]', max_length=500)
contenuSerie =models.CharField(blank=True, default='[]', max_length=100000)
def __str__(self):
return self.nom
......
......@@ -2,12 +2,11 @@ from django.shortcuts import render, HttpResponseRedirect
from django.http import JsonResponse
from .models import Serie
from django.forms.models import model_to_dict
from django.views.generic import ListView, DetailView
import subprocess, json, os, tempfile
### Chargement de la base de données
dbFile = '../db/db_phonographe.json'
dbActivitiesFile = '../db/db_activites_phonographe.json'
dbActivitiesSwitchStressFile = '../db/db_activites_switchstress.json'
dbPhonographe = {}
dbActivities = {}
......@@ -15,14 +14,6 @@ with open(dbFile, "r") as f:
dbPhonographe = json.load(f)
print('La base de données contient',len(dbPhonographe),'entrées.')
with open(dbActivitiesFile, "r") as f:
dbActivities = json.load(f)
print('Il y a',len(dbActivities),'activités de pointage phonologique enregistrées.')
with open(dbActivitiesSwitchStressFile, "r") as f:
dbActivitiesSwitchStress = json.load(f)
print('Il y a',len(dbActivitiesSwitchStress),'activités SwitchStress enregistrées.')
with open('static/json/fidel_do.json') as json_data:
fidelDo = json.load(json_data)
with open('static/json/fidel_pronsci.json') as json_data:
......@@ -108,34 +99,33 @@ def save2db(request):
def openPlayerHome(request):
updateTimeStr = updateTime()
#allseries = json.dumps(dbActivities)
series = Serie.objects.all()
seriesJson = {}
for serie in Serie.objects.all():
seriesJson[serie.id] = model_to_dict(serie)
seriesJson = json.dumps(seriesJson)
return render(request, 'playerHome.html', {'updateTime': updateTimeStr, 'series': series})
return render(request, 'playerHome.html', {'updateTime': updateTimeStr, 'series': series, 'seriesJson': seriesJson})
# Réécriture de openPlayerHome en ListView
class SerieListView(ListView):
model = Serie
template_name = 'playerHome.html'
context_object_name = 'series'
#ordering = ['-dateCreation']
def openPlayerPhono(request, serieId):
updateTimeStr = updateTime()
#serieContent = json.dumps(dbActivities[serieId])
serieContent = json.dumps(model_to_dict(Serie.objects.filter(id=serieId).first()))
print(serieContent)
print("Demande ouverture série",serieId)
return render(request, 'playerPhono.html', {'updateTime': updateTimeStr, 'serieContent':serieContent})
class SerieDetailView(DetailView):
model = Serie
template_name = 'playerSeriePage.html'
# def getActivity(request):
# colis = json.loads(request.body)
# serieIdreq = colis[serieId-request]
# print(serieIdreq)
# serieContent = dbActivities[serieIdreq]
# return JsonResponse(serieContent)
# def openSwitchStress(request):
# def openPlayerPhono(request, serieId):
# updateTimeStr = updateTime()
# return render(request, 'playerSwitchStress.html', {'updateTime': updateTimeStr})
# serieContent = json.dumps(model_to_dict(Serie.objects.filter(id=serieId).first()))
# print(serieContent)
# print("Demande ouverture série",serieId)
# return render(request, 'playerPhono.html', {'updateTime': updateTimeStr, 'serieContent':serieContent})
# def getActivitySwitchStress(request):
# activity = dbActivitiesSwitchStress["serie0"]
# return JsonResponse(activity)
\ No newline at end of file
def openPlayerPhono(request, pk):
updateTimeStr = updateTime()
serieContent = Serie.objects.filter(id=pk).first()
print(serieContent)
print("Demande ouverture série",pk)
return render(request, 'playerPhono.html', {'updateTime': updateTimeStr, 'serieContent':serieContent})
\ No newline at end of file
......@@ -141,14 +141,18 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
STATIC_ROOT = os.path.join(BASE_DIR, 'assets')
#STATIC_ROOT = os.path.join(BASE_DIR, 'static')
#STATIC_ROOT = os.path.join(BASE_DIR, 'assets')
MEDIA_ROOT = os.path.join(BASE_DIR, '../media') # dir où seront sauvegardés les médias
MEDIA_URL = '/media/'
CRISPY_TEMPLATE_PACK = 'bootstrap4'
LOGIN_REDIRECT_URL = 'home'
LOGIN_URL = 'login'
\ No newline at end of file
LOGIN_URL = '/login/'
\ No newline at end of file
......@@ -20,12 +20,17 @@ from clavier import views as clavier_views
from users import views as user_views
from django.views.decorators.csrf import csrf_exempt
# Pour accéder aux média
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('register/', user_views.register, name='register'),
path('login/', auth_views.LoginView.as_view(template_name="users/login.html"), name='login'),
path('logout/', auth_views.LogoutView.as_view(template_name="users/logout.html"), name='logout'),
path('profile/', user_views.profile, name='profile'),
path('player/createSerie/', user_views.createSerie, name='createSerie'),
path('', clavier_views.newPage),
path('fr/', clavier_views.newPage),
......@@ -35,11 +40,15 @@ urlpatterns = [
path('<str:pageLang>/id-<str:pageId>/', clavier_views.loadPage),
path('export/', csrf_exempt(clavier_views.save2db)),
path('player/', clavier_views.openPlayerHome, name='home'),
path('player/<str:serieId>', clavier_views.openPlayerPhono),
path('player/createSerie/', user_views.createSerie),
path('player/', clavier_views.openPlayerHome, name='home'), # ou SerieListView.as_view()
path('player/serie-<int:pk>', clavier_views.SerieDetailView.as_view(), name='serie-page'),
path('player/serie-<int:pk>/play', clavier_views.openPlayerPhono),
# path('getActivity/', csrf_exempt(clavier_views.getActivity)),
# path('switchstress/', clavier_views.openSwitchStress),
# path('getActivitySwitchStress/', csrf_exempt(clavier_views.getActivitySwitchStress))
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
......@@ -11,20 +11,20 @@ setKeyboards('black');
function setKeyboards(bgColor) {
if (bgColor == 'black') {
// Clavier par défaut :
pngPochoir.src="../static/png/01.png";
pngCalq.src='../static/png/04.png';
pngPochoir.src="/static/png/01.png";
pngCalq.src="/static/png/04.png";
rien.addEventListener('click', function(){pngCalq.src='';btnFocus(rien);});
vsm.addEventListener('click', function(){pngCalq.src='../static/png/03.png';btnFocus(vsm);});
bch.addEventListener('click', function(){pngCalq.src='../static/png/04.png';btnFocus(bch);});
cpsg.addEventListener('click', function(){pngCalq.src='../static/png/06.png';btnFocus(cpsg);});
vsm.addEventListener('click', function(){pngCalq.src="/static/png/03.png";btnFocus(vsm);});
bch.addEventListener('click', function(){pngCalq.src="/static/png/04.png";btnFocus(bch);});
cpsg.addEventListener('click', function(){pngCalq.src="/static/png/06.png";btnFocus(cpsg);});
} else if (bgColor == 'white') {
// Clavier par défaut :
pngPochoir.src="../static/png/02.png";
pngCalq.src='../static/png/05.png';
pngPochoir.src="/static/png/02.png";
pngCalq.src="/static/png/05.png";
rien.addEventListener('click', function(){pngCalq.src='';btnFocus(rien);});
vsm.addEventListener('click', function(){pngCalq.src='../static/png/03.png';btnFocus(vsm);});
bch.addEventListener('click', function(){pngCalq.src='../static/png/05.png';btnFocus(bch);});
cpsg.addEventListener('click', function(){pngCalq.src='../static/png/06.png';btnFocus(cpsg);});
vsm.addEventListener('click', function(){pngCalq.src="/static/png/03.png";btnFocus(vsm);});
bch.addEventListener('click', function(){pngCalq.src="/static/png/05.png";btnFocus(bch);});
cpsg.addEventListener('click', function(){pngCalq.src="/static/png/06.png";btnFocus(cpsg);});
};
};
......@@ -46,7 +46,7 @@ function btnFocus(btn){
//////////////////////////////////////////////////////////
// Modifier l'encrage et la taille du clavier
if ($('#btn-min').length) {
if (document.getElementById('btn-min').length) {
var btnmin = document.getElementById('btn-min');
btnmin.addEventListener('click', function(){
var clavSize = getComputedStyle(document.documentElement).getPropertyValue('--clavSize');
......@@ -56,7 +56,7 @@ if ($('#btn-min').length) {
});
}
if ($('#btn-max').length) {
if (document.getElementById('btn-max').length) {
var btnmax = document.getElementById('btn-max');
btnmax.addEventListener('click', function(){
var clavSize = getComputedStyle(document.documentElement).getPropertyValue('--clavSize');
......
var serieMots = []
function printSerieMot(){
console.log(serieMots);
}
function addSerieCode(){
makeSerieMots();
document.getElementById('mots').value = JSON.stringify(serieMots);
}
// Exemple serieMots :
[
{
"motGenerique":"chat",
"phono": [
["phon_s_maj","phon_a"],
],
"audio":"",
"image":""
},
{
"motGenerique":"pas",
"phono": [
["phon_p", "phon_a"],
["phon_p", "phon_a_maj"],
],
"audio":"",
"image":""
}
]
function generate(){
// récupération de la liste de mots (split sur "," et trim)
......@@ -7,29 +37,43 @@ function generate(){
// pour chaque mot, on crée une entrée (divMot) et on requête Wikicolor pour récupérer les phono possibles
for(i=0; i<listemots.length; i++){
var mot = listemots[i].trim();
var mot = listemots[i].trim().toLowerCase();
var divMot = document.createElement('div');
divMot.id = 'divMot-' + i;
divMot.classList = 'd-flex flex-row bd-highlight divMot';
var divMotInfo = document.createElement('div');
divMotInfo.classList = "d-flex flex-column bd-highlight mb-3";
divMot.appendChild(divMotInfo);
var divMotGen = document.createElement('div');
divMotGen.id = 'divMotGen-' + i;
divMotGen.classList = 'p-2 bd-highlight align-self-stretch divMotCellMot';
divMotGen.innerHTML = mot;
divMot.appendChild(divMotGen);
divMotInfo.appendChild(divMotGen);
var divMotAudio = document.createElement('div');
divMotAudio.id = 'divMotAudio-' + i;
divMotAudio.classList = 'p-2 bd-highlight align-self-stretch divMotCellMedia';
divMotAudio.innerHTML = '<i class="fa fa-volume-up" aria-hidden="true"></i>';
divMot.appendChild(divMotAudio);
divMotAudio.innerHTML = '<i class="fa fa-volume-up" aria-hidden="true"></i><small><input type="file" class="form-control-file" id="audioControlFile-'+ i +'"></small>';
divMotInfo.appendChild(divMotAudio);
var divMotAudioPath = document.createElement('div');
divMotAudioPath.id = 'divMotAudioPath-' + i;
divMotAudioPath.style.display = 'none';
divMotAudio.appendChild(divMotAudioPath);
var divMotIm = document.createElement('div');
divMotIm.id = 'divMotIm-' + i;
divMotIm.classList = 'p-2 bd-highlight align-self-stretch divMotCellMedia';
divMotIm.innerHTML = '<i class="fa fa-picture-o" aria-hidden="true"></i>';
divMot.appendChild(divMotIm);
divMotIm.innerHTML = '<i class="fa fa-picture-o" aria-hidden="true"></i><small><input type="file" class="form-control-file" id="imControlFile-'+ i +'"></small>';
divMotInfo.appendChild(divMotIm);
var divMotImPath = document.createElement('div');
divMotImPath.id = 'divMotImPath-' + i;
divMotImPath.style.display = 'none';
divMotIm.appendChild(divMotImPath);
var divMotPhono = document.createElement('div');
divMotPhono.id = 'divMotPhono-' + i;
......@@ -59,7 +103,7 @@ async function getPhono(word, targetDiv) {
};
// ENVOI
const response = await fetch('http://127.0.0.1:9000/getPhonoOf/', options); // http://wikicolor.alem-app.fr
const response = await fetch('http://wikicolor.alem-app.fr/getPhonoOf/', options); //'http://127.0.0.1:9000/getPhonoOf/', options);
const data = await response.json();
console.log(data);
......@@ -87,19 +131,25 @@ function makePhonoliste(data, targetDiv){
var phono_i_check = document.createElement('div');
phono_i_check.classList = "bd-highlight d-flex align-items-center";
phono_i_check.innerHTML = '<input type="checkbox" class="form-check-input" checked>';
phono_i_check.innerHTML = '<input id="check-'+ targetDiv + '-' + imot + '-' + jphono +'" type="checkbox" class="form-check-input" checked>';
phono_i.appendChild(phono_i_check);
var phono_i_phono = document.createElement('div');
phono_i_phono.id = targetDiv + '-' + imot + '-' + jphono + '-phons';
phono_i_phono.classList = "bd-highlight";
phono_i.appendChild(phono_i_phono);
var phono_i_phonoText = document.createElement('div');
phono_i_phonoText.id = targetDiv + '-' + imot + '-' + jphono + '-phonsText';
phono_i_phonoText.style.display = 'none';
phono_i.appendChild(phono_i_phonoText);
divMotPhonoTable.appendChild(phono_i);
for(kphon=0; kphon < data['outText'][imot][jphono].length; kphon++){
if (data['outText'][imot][jphono][kphon] != 'phon_echec' && data['outText'][imot][jphono][kphon] != 'phon_neutre'){
appendPhonCard(data['outText'][imot][jphono][kphon], phono_i_phono.id);
phono_i_phonoText.innerHTML = phono_i_phonoText.innerHTML + data['outText'][imot][jphono][kphon] + ',' ;
}
};
}
......@@ -151,4 +201,88 @@ async function getAllPhonographiesOf(phono) {
const data = await response.json();
console.log(data);
return data;
}
function makeSerieMots(){
serieMots = []; // réinitialisation de serieMots
var listeDivMots = document.getElementsByClassName('divMot');
for (idivMot=0; idivMot<listeDivMots.length; idivMot++){
iMot = listeDivMots[idivMot].id.split('-')[1];
var entree = {
"motGenerique": document.getElementById('divMotGen-'+iMot).innerHTML,
"phono": [],
"audio": document.getElementById('divMotAudioPath-'+iMot).innerHTML,
"image": document.getElementById('divMotImPath-'+iMot).innerHTML
};
var entreeTable = []; // contiendra la liste des mots avec leurs phono sélectionnées (pour générer toutes les combi à la fin)
var liste_mots = document.getElementById('divMotPhono-' + iMot).children;
// POUR CHAQUE MOT DE CETTE DIVMOT
for(jPhonoMot=0; jPhonoMot<liste_mots.length; jPhonoMot++){
entreeTable.push([]); // création d'un nouveau mot
var liste_phono = document.getElementById('divMotPhono-'+iMot+'-'+jPhonoMot).children;
// POUR CHAQUE PHONO DE CE MOT
for(kPhonoMotPhono=0; kPhonoMotPhono<liste_phono.length; kPhonoMotPhono++){
// SI CETTE PHONO EST SELECTIONNÉE (checked)
if(document.getElementById('check-divMotPhono-'+iMot+'-'+jPhonoMot+'-'+kPhonoMotPhono).checked){
var phonsText = document.getElementById('divMotPhono-'+iMot+'-'+jPhonoMot+'-'+kPhonoMotPhono+'-phonsText').innerHTML;
entreeTable[entreeTable.length-1].push(phonsText);
}
}
// Suppression du dernier mot si celui-ci est vide (que des phon_echec ou phon_neutre)
if(entreeTable[entreeTable.length-1].length==0){
entreeTable.pop();
}
}
for(ee=0; ee<entreeTable.length; ee++){
console.log(entreeTable[ee]);
for(ii=0; ii<entreeTable[ee].length; ii++){
console.log(entreeTable[ee][ii]);
}
}
// GENERATION DE TOUTES LES COMBINAISONS POSSIBLE POUR CETTE DIVMOT
// ex. "le chat" → l,@,S,a ; l,@,tS,a,t
var ancienneListe = []; // contiendra toutes les combinaisons des mots précédents entre eux et seront combinés à chaque phono du mot courant
var nouvelleListe = []; // à chaque nouveau mot, la nouvelleListe est passée à l'ancienneListe (mémoire de un mot)
for(xmot=0; xmot<entreeTable.length; xmot++){ // itération sur les mots de entreeTable
ancienneListe = nouvelleListe; // on bascule la liste vers la mémoire
nouvelleListe = []
if(ancienneListe.length == 0){ // si la liste des précombinaisons est vide (c'est donc le premier mot)
for(yphono=0; yphono<entreeTable[xmot].length; yphono++){ // itération sur les phonos du mot courant
nouvelleListe.push(entreeTable[xmot][yphono]);
}
} else {
for(yphono=0; yphono<entreeTable[xmot].length; yphono++){ // itération sur les phonos du mot courant
for(zprecombi=0; zprecombi<ancienneListe.length; zprecombi++){ // itération sur les précombinaisons déjà constituées
nouvelleListe.push((ancienneListe[zprecombi]+entreeTable[xmot][yphono]));
}
}
}
}
entree["phono"] = nouvelleListe;
console.log("Génération des combinaisons...");
console.log(entree);
serieMots.push(entree);
}
}
\ No newline at end of file
......@@ -116,14 +116,9 @@ var btnValider = document.getElementById("btnValider");
// LANCEMENT DE L'ACTIVITÉ PAR DEFAUT (TRANSMISE AVEC LE TEMPLATE cf. views.py)
mots = JSON.parse(serieContent["mots"]);
nbmots = mots.length;
document.getElementById('nomSerie').innerHTML = serieContent["nom"];
document.getElementById('nbMots').innerHTML = nbmots;
document.getElementById('fenetreDeLancement').style.display = "block";
......@@ -141,7 +136,7 @@ function loadNext() {
document.getElementById("bravo").style.display = "none";
if (cptitem < nbmots) {
rep.innerHTML = "";
currentAudio = '/static/audio/s0/'+mots[cptitem]["audio"];
currentAudio = mots[cptitem]["audio"];
playAudio();
cptitem = cptitem+1;
document.getElementById('cptitem').innerHTML = cptitem;
......@@ -155,23 +150,25 @@ function loadNext() {
// vérifie la réponse
function checkAnswer() {
var phono = mots[cptitem-1]["phono"]; // cptitem-1 parce qu'on a déjà incrémenté cptitem lors de loadNext()
var erreur = false;
if (rep.children.length == phono.length) {
for (i=0; i<rep.children.length; i++) {
var repphon = rep.children[i].classList[1].replace("rect","phon");
if (repphon != phono[i]) {
erreur = true;
console.log("ERREUR",repphon,phono[i]);
}
var correct = false;
var reponse = "";
for (i=0; i<rep.children.length; i++) {
reponse = reponse + rep.children[i].classList[1].replace("rect","phon")+',';
}
console.log(reponse);
for (j=0; j<phono.length; j++){
if(reponse == phono[j]){
correct = true;
}
} else erreur = true
}
if (erreur) {
playEffect("wrong");
} else {
if (correct) {
playEffect("correct");
document.getElementById("bravo").style.display = "block";
} else {
playEffect("wrong");
}
}
......
var spanNbmots = document.getElementsByClassName("nbmots");
for (var i = 0; i < spanNbmots.length; i++) {
console.log(spanNbmots[i].id);
var serieDecoupe = spanNbmots[i].id.split('-');
var serieId = serieDecoupe[1];
mots = JSON.parse(series[serieId].mots);
spanNbmots[i].innerHTML = mots.length;
var listmots = document.getElementById("listmots-"+serieId);
for (var j = 0; j < mots.length; j++) {
listmots.innerHTML += mots[j]["motGenerique"]+"<br/>";
}
}
\ No newline at end of file
......@@ -60,4 +60,20 @@
.createSerieCarte {
cursor: pointer;
}
.account-img {
height: 125px;
width: 125px;
margin-right: 20px;
margin-bottom: 16px;
}
.account-heading {
font-size: 2.5rem;
}
.serieAuteurImage {
width: 20px;
height: 20px;
}
\ No newline at end of file
/* CSS ALeM-APP v3 */
/* https://alem.hypotheses.org/ */
/* Last update: 2020/10/30 */
/* Last update: 2020/11/12 */
:root {
--phon_bicolor1: #ff0000;
......@@ -22,6 +22,7 @@
--phon_4: #c3ffff; /*ɾ*/
--phon_6: lightgray; /*ɐ*/
--phon_7: #ad9501; /*ɤ*/
--phon_glottstop: #f36c85; /*ʔ*/
--phon_8: lightgray; /*ɵ*/
--phon_9: #ea6b76; /*œ*/
--phon_9_maj: lightgray; /*ɶ*/
......@@ -90,6 +91,8 @@
--phon_schwi: #f8b3ba; /* pour l'anglais i/ɪ */
--phon_schwa: #e7dd87; /* pour l'anglais ə */
--phon_schwu: #b9dbc5; /* pour l'anglais u/ʊ */
--phon_nas: #acc6ee; /*̃ */
}
......@@ -120,6 +123,7 @@ svg {
.phon_4 { fill: var(--phon_4); color:var(--phon_4); stop-color:var(--phon_4);} /*ɾ*/
.phon_6 { fill: var(--phon_6); color:var(--phon_6); stop-color:var(--phon_6);} /*ɐ*/
.phon_7 { fill: var(--phon_7); color:var(--phon_7); stop-color:var(--phon_7);} /*ɤ*/
.phon_glottstop { fill: var(--phon_glottstop); color:var(--phon_glottstop); stop-color:var(--phon_glottstop);} /*ʔ*/
.phon_8 { fill: var(--phon_8); color:var(--phon_8); stop-color:var(--phon_8);} /*ɵ*/
.phon_9 { fill: var(--phon_9); color:var(--phon_9); stop-color:var(--phon_9);} /*œ*/
.phon_9_maj { fill: var(--phon_9_maj); color:var(--phon_9_maj); stop-color:var(--phon_9_maj);} /*ɶ*/
......@@ -199,12 +203,14 @@ svg {
.phon_ts_maj { background:-webkit-linear-gradient(var(--phon_t) 52%, var(--phon_s_maj) 50%);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;}
.phon_dz {background:-webkit-linear-gradient(var(--phon_d) 52%, var(--phon_z) 50%);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;}
.phon_dz_maj { background:-webkit-linear-gradient(var(--phon_d) 52%, var(--phon_z_maj) 50%);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;}
.phon_dz_slash {background:-webkit-linear-gradient(var(--phon_d) 52%, var(--phon_z_slash) 50%);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;}
.phon_ks { background:-webkit-linear-gradient(var(--phon_k) 52%, var(--phon_s) 50%);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;}
.phon_ks_maj {background:-webkit-linear-gradient(var(--phon_k) 52%, var(--phon_s_maj) 50%);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;}
.phon_kw { background: -webkit-linear-gradient(var(--phon_k) 52%, var(--phon_w) 50%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;}
.phon_gz { background:-webkit-linear-gradient(var(--phon_g) 52%, var(--phon_z) 50%);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;}
.phon_gz_maj { background: -webkit-linear-gradient(var(--phon_g) 52%, var(--phon_z_maj) 50%); -webkit-background-clip: text