A tool for creating feminine form of masculine-by-default nouns. https://feminitives.ru/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

429 lines
14 KiB

// Copyright (C) 2016-2017, Maxim Lihachev, <envrm@yandex.ru>
//
// 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 3 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// [ ] TODO: валидация
//------------------------------------------------------------------------------
'use strict';
//------------------------------------------------------------------------------
//Иерархия элементов на странице
var HTML = {
container: "",
_select: function(element) { return document.getElementById(this.container + "-" + element); },
input: function() { return this._select("word"); },
button: function() { return this._select("convert"); },
dict: function() { return this._select("dict"); },
content: function() { return this._select("content"); },
full: function() { return this._select("full"); },
help: function() { return this._select("help"); },
image: function() { return this._select("image"); },
};
//Вывод справки с примерами использования
function show_help() {
HTML.vis(HTML.help());
HTML.vis(HTML.content());
}
HTML.vis = function(e, v) {
if (v !== undefined) {
e.style.visibility = v;
} else {
e.style.visibility = e.style.visibility === "visible" ? "hidden" : "visible";
}
}
//Инициализация документа
HTML.init = function(root) {
//Задание базового id для всех элементов
this.container = root;
//Конвертирование по нажатию <Enter>
this.input().addEventListener('keyup', event => {
event.preventDefault();
event.keyCode == 13 && tr();
});
//Конвертирование по нажатию кнопки
this.button().onclick = () => tr();
};
//------------------------------------------------------------------------------
//Правила создания феминитивов
var FEM = {};
FEM.endings = {
'ка' : [
['[аео]р', 0],
['ан', 0],
['рг', 1],
['ст', 0], //специалист -> специалистка
['ль', 1], //создатель -> создателка
['ец', 2], //канадец -> канадка
['ет', 0], //авторитет -> авторитетка
['от', 0] //гот -> готка
],
'ина' : [
['ас', 0]
],
'иня' : [
['[аео]р', 0],
['[ои]к', 0],
['со', 1], //колесо -> колесиня
['ог', 0], //биолог -> биологиня
['рг', 0],
['ач', 0], //врач -> врачиня
['ль', 1], //создатель -> создателиня
['ст', 0], //специалист -> специалистиня
['од', 0], //метод -> методиня
['ет', 2], //авторитет -> авторитиня
['ец', 2], //канадец -> канадиня
['аф', 0] //граф -> графиня
],
'киня' : [
['[аео]р', 0],
['ок', 0],
['ст', 0], //специалист -> специалисткиня
['ан', 0]
],
'есса' : [
['[аео]р', 0],
['[ои]к', 0],
['ог', 0], //биолог -> биологесса
['ан', 0],
['ль', 1], //создатель -> создателесса
['рг', 0],
['ач', 0], //врач -> врачесса
['ый', 2], //учёный -> учёнесса
['ст', 0], //специалист -> специалистесса
['од', 0], //метод -> методесса
['ет', 2], //авторитет -> авторитесса
['ец', 2], //канадец -> канадесса
['от', 0], //гот -> готесса
['эт', 0] //поэт -> поэтесса
],
'ица' : [
['[аео]р', 0],
['уч', 0],
['ик', 2],
['ог', 0], //биолог -> биологиня
['ан', 0],
['ив', 0],
['рг', 0],
['ас', 0],
['ач', 0], //врач -> врачица
['ст', 3], //специалист -> специалица
['од', 0], //метод -> методица
['ет', 2], //авторитет -> авторитица
['ец', 2] //канадец -> канадица
],
'ница' : [
['ль', 0], //создатель -> создательница
['ас', 0],
['ет', 0], //авторитет -> авторитетица
['ец', 2] //канадец -> канадница
],
'ая' : [
['[ыио]й', 2], //учёный -> учёная, знающий -> знающая
['ое', 2]
],
//----- ДАЛЬШЕ ИДЁТ ШИЗА -----
'ии' : [
['и[ия]', 2], //металлургии -> металлург_ии, произведения -> произведении
],
'ца' : [
['ие', 1], //металлургии -> металлург_ии, произведения -> произведении
],
'ми' : [
['ми', 2] //знаниями -> знания_ми
],
'ой' : [
['го', 3] //художественного -> художественн_ой
],
'инь' : [
['ей', 2] //людей -> люд_ей
],
'ти' : [
['ти', 2] //области -> облас_ти
],
'ю' : [
['ью', 1] //матерью -> матерь_ю
],
'ны' : [
['на', 2] //бассейна -> бассей_ны
],
};
//Слова со специфичными определениями
FEM.exceptions = {
'феминист' : [ ['профеминист', 'союзник'],
"Мифическое создание, якобы поддерживающее феминизм. В реальности не встречается."
],
'жена' : [ ['мужерабка', 'подстилка патриархата'],
"Женщина, прогнувшаяся под патриархат и получающая удовольствие от угнетения."
]
};
//Проверка на исключение
FEM.exceptions.contains = function(word) {
return Object.keys(this).includes(word);
};
//Список значений
FEM.exceptions.feminitives = function(word) {
return [random_word(this[word][0]), this[word][0]];
};
//Дефиниция слова-исключения
FEM.exceptions.definition = function(word) {
return this[word][1];
};
//Слова для замены
FEM.words = {
'тот' : 'т_а',
'того' : 'т_у',
'кто' : 'котор_ая',
'её' : 'е_ё',
'ее' : 'е_е',
'ий' : 'ая',
'человек' : 'человека',
'муж' : 'жен'
};
FEM.words.convert = function(string) {
for (var fem_w in this) {
string = string.replace(new RegExp(fem_w, "ig"), this[fem_w])
.replace(/(.)/, s => s.toUpperCase());
}
return string;
};
//------------------------------------------------------------------------------
//Первый элемент списка - окончание (в виде регулярного выражения)
let ending = tuple => new RegExp("^.*" + tuple[0] + "$", "i");
//Второй элемент списка - смещение
let offset = tuple => tuple[1];
//Случайный элемент списка
let random_word = wordlist => wordlist[Math.floor(Math.random() * wordlist.length)];
//Оборачивание в <span> с указанным классом
let html_wrap = (str, cl) => `<span class="${cl}">${str}</span>`;
//Цветовое выделение текста
let css_end = ending => html_wrap(ending, "ending");
//Символ gender gap
let css_gender_gap = html_wrap(' \u26A7 ', "queer");
//------------------------------------------------------------------------------
//Конструирование феминитива с gender_gap
function construct_feminitive(stem, ending, gap) {
return gap ? stem + css_gender_gap + css_end(ending) : stem + "_" + ending;
}
//Отправка адреса страницы в vk.com
function share_page() {
let vk_url = "http://vk.com/share.php"
+ "?url=" + URL.href
+ "&title=" + URL.title
+ "&description=" + URL.description.text;
let new_tab = window.open(vk_url,'_blank');
new_tab.focus();
}
//Сохранение изображения с феминитивом
function download_image() {
html2canvas(HTML.image(), {
onrendered: canvas => {
let a = document.createElement('a');
a.href = canvas.toDataURL();
a.download = HTML.content().innerHTML + '.png';
a.click();
}
});
}
//Создание феминитива
function make_feminitives(word) {
//Обрабатываем только слова длиннее трёх символов
if (word.length < 3) return word;
var stem = ""; //Основа слова
var current_ending = word.slice(-2); //Текущее окончание
var feminitives = []; //Массив феминитивов
var femicards = []; //Массив феминитивов для карточки
for (let fem_ending in FEM.endings) {
FEM.endings[fem_ending].forEach(end => {
if (ending(end).test(current_ending)) {
//Удаление лишних букв из основы
stem = offset(end) === 0 ? word : word.slice(0, -offset(end));
//Добавление фем-варианта слова в массив
feminitives.push(construct_feminitive(stem, fem_ending, 1));
femicards.push(construct_feminitive(stem, fem_ending, 0));
}
});
}
//При отсутствии феминитивов считать корректным исходное слово
return [random_word(femicards) || word, feminitives];
}
//Поиск и феминизация дефиниции в викистранице
function parseWikiPage(page) {
var wiki = page.split("\\n");
var definition = "";
wiki.some((line, n) => {
if (line.match(/^.*==== Значение ====.*$/)) {
console.log(wiki[n+1]); //DEBUG
definition = wiki[n+1]
.replace(/^# ?/, "") //# дефиниция
.replace(/\[{2}([^\]\|]*)\]{2}/g, "$1") //[[1]]
.replace(/\[{2}[^\|]*\|([^\]]*)\]{2}/g, "$1") //[[1|2]]
.replace(/\[{2}([^\]\|]*)\}{2}/g, "$1") //{{1}}
.replace(/\{{2}[^\{\}]*\}{2} ?/g, "") //{{1|2}}
.replace(/\{{2}[^\{\}]*\}{2} ?/g, "") //~ : возможна вложенность
.replace(/\[[0-9]{1,}\]/g, "") //ссылки [n]
.replace(/^ *, */g, "") //^, ...
.replace(/ ?$/,"."); //Точка в конце предложения
return true;
}
});
//Разделение дефиниции на массив слов и знаков препинания и феминизация слов
var tokens = definition.match(/[\wа-яА-Яё]+|\d+| +|[^ \w\d\t]+/ig) || [];
//Замена местоимений, предлогов и проч.
HTML.full().innerHTML = FEM.words.convert(tokens.map(w => make_feminitives(w)[0]).join(""));
//DEBUG
console.log(definition);
console.log(tokens);
}
//Запрос значения слова в викисловаре
function get_wiktionary(term) {
var cors_url = "https://cors.now.sh/";
var wiki_url = cors_url + "https://ru.wiktionary.org/w/index.php?action=raw&title=" + term;
var xmlhttp = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
parseWikiPage(xmlhttp.responseText);
}
};
xmlhttp.open("GET", wiki_url, true);
xmlhttp.send();
}
//Создание и вывод феминитива
function tr(word) {
//Исходное слово
var wd = word || HTML.input().value.trim().toLowerCase().replace(/<\/?[^>]+(>|$)/g, "").split(" ")[0];
var feminitives = "";
//Состояние по умолчанию
HTML.content().innerHTML = "Введите слово";
HTML.dict().innerHTML = "";
HTML.full().innerHTML = "";
HTML.vis(HTML.help(), "hidden");
HTML.vis(HTML.content(), "visible");
//Изменение адреса
URL.set(wd);
//Вывод информации
if (!wd) {
show_help();
return;
} else if (FEM.exceptions.contains(wd)) {
HTML.full().innerHTML = FEM.exceptions.definition(wd);
feminitives = FEM.exceptions.feminitives(wd);
} else {
get_wiktionary(wd);
feminitives = make_feminitives(wd);
}
//Вывод информации
HTML.input().value = wd;
HTML.content().innerHTML = feminitives[0].replace(/(.)/, s => s.toUpperCase());
HTML.dict().innerHTML = feminitives[1].join(" | ")
|| "Это слово и так прекрасно. Оставим его как есть.";
}
//------------------------------------------------------------------------------
//Параметры URL
var URL = {
opt: {},
description: {
clear: function() { this.text = "Как феминистки пишут разные слова."; },
set: function(wd) { this.text = 'Как феминистки пишут слово "' +wd+ '".'; },
},
set: function(wd) {
if (!wd) {
window.history.pushState({}, null, window.location.href.split('?')[0]);
this.description.clear();
} else {
window.history.pushState({}, null, window.location.href.split('?')[0]+'?word='+wd);
this.description.set(wd);
}
this.href = encodeURIComponent(window.location.href);
}
};
//Разбор параметров URL
URL.parse = function() {
var gy = window.location.search.substring(1).split("&");
gy.forEach(arg => {
let ft = arg.split("=");
this.opt[ft[0]] = this.opt[ft[0]] || decodeURIComponent(ft[1]);
});
URL.description.clear();
this.title = document.title;
this.opt.href = encodeURIComponent(window.location.href);
};
//Инициализация с разбором адресной строки
function init(container) {
HTML.init(container);
URL.parse();
if (URL.opt.word) {
HTML.input().value = URL.opt.word.replace(/\+/g," ");
tr();
} else {
show_help();
}
}