diff --git a/js/feminitives-functions.js b/js/feminitives-functions.js new file mode 100755 index 0000000..b0cecd2 --- /dev/null +++ b/js/feminitives-functions.js @@ -0,0 +1,416 @@ +// +// Copyright (C) 2016-2019, Maxim Lihachev, +// +// 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, version 3. +// +// 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 . +// + +//------------------------------------------------------------------------------ + +'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()); +} + +//Смена названия по клику +function swap_title(id) { + var e = document.getElementById(id); + if(e.style.display == 'inline') { + e.style.display = 'none'; + } else { + e.style.display = 'inline'; + } +} + +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; + + //Конвертирование по нажатию + this.input().addEventListener('keyup', event => { + event.preventDefault(); + event.keyCode == 13 && tr(); + }); + + //Конвертирование по нажатию кнопки + this.button().onclick = () => tr(); +}; + +//------------------------------------------------------------------------------ + +FEM.words.convert = function(string) { + for (var fem_w in this) { + string = string.replace(new RegExp('(^|\\s)+' + fem_w, "ig"), '$1' + 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)]; + +//Оборачивание в с указанным классом +let html_wrap = (str, cl) => `${str}`; + +//Цветовое выделение текста +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; +} + +//Длина шаблона, подпадающего под регулярное выражение r +function regex_len(r) { + return r.replace(/\[[^\[\]]*\]/g, "x").length; +} + +//Отправка адреса страницы в 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, visibleGaps) { + //Обрабатываем только слова длиннее трёх символов + if (word.length < 3) return [word, word]; + + if (FEM.words[word] != undefined) { + let wrd = visibleGaps ? FEM.words[word] : FEM.words[word].replace("_", ""); + + return [wrd, wrd]; + } + + var stem = ""; //Основа слова + // var current_ending = word.slice(-2); //Текущее окончание + var current_endings = [word.slice(-4), word.slice(-3), word.slice(-2)]; + var feminitives = []; //Массив феминитивов + var femicards = []; //Массив феминитивов для карточки + + var found = false; + + current_endings.forEach(current_ending => { + if (!found) { + for (let fem_ending in FEM.endings) { + FEM.endings[fem_ending].forEach(end => { + if (regex_len(end[0]) === current_ending.length + && ending(end).test(current_ending)) { + //Удаление лишних букв из основы + stem = offset(end) === 0 ? word : word.slice(0, -offset(end)); + + let rule = fem_ending.split("+"); + + let prefix, all_fem_endings; + + if (rule.length > 1) { + prefix = rule[0]; + all_fem_endings = rule[1]; + } else { + prefix = ""; + all_fem_endings = fem_ending; + } + + all_fem_endings.split("|").forEach(e => { + //Добавление фем-варианта слова в массив + feminitives.push(construct_feminitive(stem + prefix, e, 1, visibleGaps)); + femicards.push(construct_feminitive(stem + prefix, e, 0, visibleGaps)); + + }); + + found = true; + } + }); + } + } + }); + + //При отсутствии феминитивов считать корректным исходное слово + return [FEM.random_word(femicards) || word, feminitives]; +} + +//Создание феминитива +function make_feminitives_old(word) { + //Обрабатываем только слова длиннее трёх символов + if (word.length < 3) return [word, 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(/^.*==== Значение ====.*$/)) { + let definition_line = n + 1; + + //После заголовка возможна пустая строка или помета (например, {{воен.}}) + if (wiki[n+1].trim().length === 0 + || wiki[n+1].trim().match(/^{{[^}]*}}$/)) { + definition_line = n + 2; + } + + definition = wiki[definition_line] + //# дефиниция + .replace(/^# ?/, "") + + //[[1]] + .replace(/\[{2}([^\]|]*)\]{2}/g, "$1") + + //[[1|2]] + .replace(/\[{2}[^|]*\|([^\]]*)\]{2}/g, "$1") + + //{{сокр.|слово.*$ + .replace(/\{{2}[а-яА-Я]+\.[^{}]*\|[^{}]*\}{2}\s*/g, "") + + //~ : возможна вложенность + .replace(/\{{2}[а-яА-Я]+\.[^{}]*\|[^{}]*\}{2}\s*/g, "") + + //{{-}}$ + .replace(/\{\{-\}\}/g, "") + + //{{помета|...}}$ + .replace(/\{{2}помета\s*\|[^}]+\}{2}/g, "") + + //{{действие|(глагол)#...lang=LC}}$ + .replace(/\{{2}(действие)\s*\|([^#|]+)([#|].*lang=\w{2})?\}{2}/g, "$1 «$2»") + + //{{пример/семантика|.*$ или {{пример/семантика}} + .replace(/\{{2}(пример|семантика)(\}{2}|\s*\|.*$)/g, "") + + //{{1|слово.*$ + .replace(/\{{2}[^{}]*\|(lang=\w{2})?/g, "") + + //{{1}} + .replace(/\{{2}([^\]|]*)\}{2}/g, "$1") + + //'''ударение''' + .replace(/'''([^']*)'''/g, "«$1»") + + // + .replace(//g, "") + + //}} + .replace(/\}{2}/g, "") + + //ссылки [n] + .replace(/\[[0-9]{1,}\]/g, "") + + //^,; ... + .replace(/^\s*[,;]\s*/g, "") + + //{{.*$ + .replace(/\{{2}[^}]*$/g, "") + + //HTML + .replace(/(<([^>]+)>)/ig, "") + + //Неразрывный пробел + .replace(/ /g, " ") + + //Точка в конце предложения + .replace(/ ?$/,"."); + + return true; + } + }); + + //Разделение дефиниции на массив слов и знаков препинания и феминизация слов + var tokens = definition.match(/[\wа-яА-Яё]+|\d+| +|[.;,]|[^ \w\d\t.;,]+/ig) || []; + console.log(tokens); + + //Замена местоимений, предлогов и проч. + 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://coors.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(); + } +} diff --git a/js/feminitives-rules.js b/js/feminitives-rules.js new file mode 100755 index 0000000..0cfd1df --- /dev/null +++ b/js/feminitives-rules.js @@ -0,0 +1,449 @@ +// +// Copyright (C) 2016-2019, Maxim Lihachev, +// +// 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, version 3. +// +// 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 . +// + +//------------------------------------------------------------------------------ + +var FEM = {}; + +//Слова для замены и исключения +//Имеют высший приоритет и не обрабатываются остальными правилами +FEM.words = { + 'без' : 'без', + 'как' : 'как', + 'либо' : 'либо', + 'например' : 'например', + 'них' : 'них', + 'под' : 'под', + + 'два' : 'две', + + 'весь' : 'вся', + 'всё' : 'вся', + + 'чего' : 'чегой', + + 'ее' : 'е_е', + 'её' : 'е_ё', + 'кто' : 'котор_ая', + 'каким' : 'как_ой', + 'муж.' : 'жен.', + 'муж' : 'жена', + 'то' : 'т_а', + 'того' : 'т_у', + 'тот' : 'т_а', + 'мужчина' : 'женщи_на', + 'мужчины' : 'женщи_ны' +}; + +//Слова со специфичными определениями +FEM.exceptions = { + 'феминист' : [ ['профеминист', 'союзник'], + "Мифическое создание, якобы поддерживающее феминизм. В реальности не встречается." + ], + 'жена' : [ ['мужерабка', 'подстилка патриархата'], + "Женщина, прогнувшаяся под патриархат и получающая удовольствие от угнетения." + ] +}; + +// Формат: +// '<префикс>+окончание1|окончание2|...|окончаниеN' : { +// ['<шаблон>', <сколько удалять символов на конце слова>] +// } +// +// Окончания отсортированы от наиболее специфичных к наиболее общим +// +FEM.endings = { + //Исключения, которые не нужно феминизировать + 'бо' : [ + ['либо', 2] // -либо + ], + 'их' : [ + ['их', 2] // нескольких + ], + 'ых' : [ + ['ых', 2] // грубых + ], + 'ной' : [ + ['ной', 3] // платёжной + ], + 'ных' : [ + ['ных', 3] // засушенных + ], + 'тей' : [ + ['тей', 3] // обязанностей + ], + 'ть' : [ + ['[аеи]ть', 2] // глаголы в инфинитиве + ], + 'ёт' : [ + ['[её]т', 2] // глаголы в форме 3 л. ед.ч. + ], + 'ют' : [ + ['ают', 2] // глаголы в форме мн.ч. + ], + + // имена прилагательные + 'ая' : [ + ['[дкнш]ое', 2], // твёрдое, офицерское, полное, большое + ['[дн]ый', 2], // твёрдый, полный + ['[бвлмпт][оы][ей]', 2], // новый, круглый, знакомый, глупый, принятый + //['[бвклмнтш]ое', 2], // новое, сладкое, круглое, знакомое, полное, принятое, прошлое + ['[шщ]ее', 2], // пешее, вещее + ['[кнпщщ]ий', 2] // сладкий, дольний, глупый, пеший, пещий + ], + + 'яя' : [ + ['[нчщ]ий', 2], // верхний, ползучий, знающий + ['[нчщ]ее', 2] // верхнее, ползучее, знающее + ], + + 'аяся' : [ + ['ееся', 4], // являющееся + ['ийся', 4] // занимающийся + ], + + // + // ----- ФОРМА РОДИТЕЛЬНОГО ПАДЕЖА (преимущественно) ----- + // + + // форма мн.ч. + 'ок|есс|ин|иц' : [ + ['вий', 2], // действий + ['лей', 2], // родителей + ['ств', 0], // свойств + ['тра', 0], // метра + ['еров', 2] // размеров + ], + + // форма ед.ч. + 'ч+ки|ессы|ини|ицы' : [ + ['века', 2] // человека + ], + + 'ки|ессы|ини|ицы' : [ + ['[ае]за', 1], // газа + ['[ай]на', 1], // капитана, бассейна + ['[ас]та', 1], // заката, роста + ['[её]ма', 1], // водоёма + ['[коя][тдя]а', 1], // отряда, объекта, города, живота + ['[мн]ца', 2], // самца, солнца + ['алла', 1], // металла + ['дке', 2], // порядке + ['е[жт]а', 1], // платежа, света + ['зда', 1], // переезда + ['ены', 1], // члены (организации) + ['еста', 1], // теста + ['и[дтщ]а', 1], // вида, кредита, туловища + ['иала', 1], // материала + ['инка', 1], // клинка + ['кты', 1], // объекты + ['лива', 1], // полива + ['нта', 1], // варианта + ['о[лрс]а', 1], // пола, надзора, космоса + ['рева', 1], // дерева + ['ства', 1], // государства + ['те[лр][аия]', 1], // руководителя + ['[оу]ви', 1], // крови, обуви + ['фона', 1], // телефона + ['я[дс]а', 1] // отряда, мяса + ], + + 'ессы|ини|ицы' : [ + // действия, равновесия, предприятия, метталургии + ['[вгнст]и[ия]', 2], + + ['бега', 1], // побега + ['[бк]оя', 1], // покоя, боя + ['иги', 1], // подвиги + ['[врт]ца', 1], // продавца, кварца, отца + ['ени', 1], // времени + ['есы', 1], // интересы + ['[исы]ка', 1], // дворника, песка, языка + ['изма', 1], // организма + ['лия', 2], // изделия + ['сьма', 1], // письма + ['йма', 1], // займа + ['нка', 1], // банка + ['ья', 1], // несчастья + ['уки', 1], // звуки + ['юди', 1], // люди + ['у[сх]а', 1] // вкуса, воздуха + ], + + 'ии' : [ + ['[гн]и[ия]', 2] // металлургия + ], + + 'есс|иц|инь' : [ + ['вых', 2] // паслёновых + ], + + 'й+ки' : [ + ['боя', 1], // боя + ['ви[йя]', 1] // действия + ], + + // имена прилагательные + 'ей' : [ + ['[шщ]им', 2], // общим + ['его', 3] // высшего + ], + + 'ой' : [ + // другого, молодого, женского, целого, художественного, которого, этого, большого + ['[вгдклнртш]ого', 3], + + ['[ео]му', 3], // судебному + ['[гд][иы]м', 2], // твёрдым, другим + ['[клмнр][оы]м', 2] // музыкальном, прямым + ], + + // + // ----- ФОРМЫ ДАТЕЛЬНОГО И ПРЕДЛОЖНОГО ПАДЕЖА ----- + // + + // форма ед.ч. + 'ке|ессе|ине|ице' : [ + ['[ия]де', 1], // виде, ряде + ['[оя]ду', 1], // периоду, взгляду + ['ане', 1], // плане + ['е[нс]те', 1], // инструменте -> инструментессе, месте -> местессе + ['[ар]те', 1], // результате, транспорте + ['ессе', 1], // процессе + ['[её]ме', 1], // объёме + ['ксу', 1], // боксу -> боксессе + ['лу', 1], // делу + ['нию', 2], // отношению + ['нке', 1], // ребёнке + ['оду', 1], // период + ['ету', 1], // предмету + ['сту', 1], // росту + ['усе', 1] // соусе + ], + 'ессе|ине|ице' : [ + ['еге', 1], // беге + ['тве', 1], // государстве + ['дне', 1], // судне -> суднессе + ['[иы]ке', 1], // крике, языке + ['нцу', 1], // концу -> концессе + ['чае', 1] // случае + ], + 'ей|це' : [ + ['н[иь]ем', 2] // давлением -> давленией, вареньем -> вареньей + ], + + // форма мн.ч. + 'ессам' : [ + ['кам', 2] // потомкам -> потомкессам + ], + 'ессам|иням|ицам' : [ + ['[дт]ям', 2] // людям, детям + ], + + // + // ----- ФОРМА ВИНИТЕЛЬНОГО ПАДЕЖА ----- + // + + 'ую' : [ + ['шой', 2], // большой + ['чий', 2], // горячий + ['[вг]ое', 2] // готовое, другое + ], + + // + // ----- ФОРМА ТВОРИТЕЛЬНОГО ПАДЕЖА ----- + // + + 'ессой|иней|ицей' : [ + ['[вгдл]ом', 2], // клювом, ремеслом, лозунгом, трудом + ['ью', 2], // осенью + ['[илнт][её]м', 2] // носителем, лезвием, остриём, временем, путём + ], + + 'ками|ессами|ицами' : [ + ['[вдлн][ая]ми', 3], // островами, спортсменами, представителями, плодами + ['[иы]ми', 3], // мелкими, продолговатыми + ['тами', 3], // продуктами + ['ьями', 4] // листьями + ], + + 'ессами|ицами' : [ + ['[ая]ми', 3] // лицами, отношениями + ], + + // --------------------------------- + // ----- ИМЕНА СУЩЕСТВИТЕЛЬНЫЕ ----- + // --------------------------------- + + // Слова с заменой согласного основы + 'ч+ка|есса|ина|иня|ица' : [ + ['ёнок', 1], // ребёнок + ['ыка', 2], // владыка + ['[ае]к', 1] // левак, человек + ], + + 'ч+есса|ина|иня|ица' : [ + ['цо', 2] // лицо + ], + + 'ц+есса|иня|ица' : [ + ['[бнр]ец', 2] // конец, творец, рубец + ], + + 'к+иня' : [ + ['ст', 0], // специалист + ['нт', 0] // агент + ], + + 'н+ица' : [ + ['ль', 0] // создатель + ], + + 'адья' : [ + ['поп', 0] // поп + ], + + 'ка|есса|иня|ица' : [ + // [а] прораб, нрав, раж, лаз, зал, кран, этап, нагар, ужас, закат, граф, врач, палаш, плащ + // [е] хлеб, лев, манеж, вырез, удел, овен, зацеп, характер, лес, авторитет, блеф, меч, флеш, клещ + // [ё] небоскрёб, рёв, ёж, -ёз, орёл, клён, поклёп, сапёр, овёс, зачёт, -ёф, -ёч, клёш, -ёщ + // [и] гриб, залив, чиж, бриз, спил, блин, хрип, жир, рис, визит, гриф, клич, мякиш, товарищ + // [н] -нб, -нв, -нж, ценз, -нл, джинн, -нп, жанр, анонс, бант, аканф, ленч, реванш, -нщ + // [о] сноб, ров, нож, воз, пол, район, клоп, узор, снос, азот, штоф, светоч, грош, овощ + // [р] ущерб, нерв, морж, -рз, жерл, сатурн, карп, -рр, барс, торт, торф, грач, фарш, борщ + // [y] сруб, обдув, уж, арбуз, гул, врун, закуп, амур, трус, жгут, пуф, луч, душ, хрущ + // [э] дзэн, кэб, дэв, -эж, -эз, -эл, нэп, мэр, аэс, поэт, -эф, -эч, -эш, -эщ + // [ю] книголюб, клюв, -юж, союз, -юл, вьюн, шлюп, бордюр, полюс, уют, -юф, ключ, плюш, плющ + + ['[аеёиноруэю][бвжзлнпрстфчшщ]', 0], + + ['[аеи][дн]ец', 2], // канадец, уроженец + ['тец', 0], // отец + ['[лн]ь', 1], // вождь, создатель, создателесса, камень + ['л[блнпт]', 0], // металл, столб, челн, залп, болт + ['льм', 0], // фильм + ['мп', 0], // темп + ['оша', 1], // юноша + ['рин', 2], // татарин + ['с[лст]', 0], // смысл, процесс, специалист + ['ы[нсш]', 0], // сын, мыс, ландыш + ['я[жнпрсчшщ]', 0] // пляж, кляп, маляр, пояс, мяч, гуляш, хрящ + ], + + + 'ка' : [ + ['яд',0], // отряд + ['[аеёуы]м',0], // хам, джем, вотум, дым + ['рий',0], // гербарий + ['[аеоу]й',0], // бугай, лакей, зной, буй + ['[рст]ь', 0] // фонарь, карась, гость + ], + + 'есса|иня|ица' : [ + // [а] маг, клад, знак, хам, страх, плац + // [е] берег, бред, человек, джем, цех, боец + // [ё] -ёг, лёд, отёк, -ёх, -ём, -ец + // [и] подвиг, вид, медик, грим, вывих, шприц + // [н] ринг, фонд, банк, сонм, бронх, принц + // [о] рог, сброд, срок, дом, вдох, -оц + // [р] торг, аккорд, кварк, шторм, стерх, кварц + // [с] пеласг, -сд, спуск, микрокосм, -сх, -сц + // [у] супруг, пуд, жук, вотум, пух, кибуц + // [ы] припрыг, стыд, клык, дым, жмых, -ыц + // [я] стяг, отряд, тюфяк, -ям, лях, -паяц + + ['[аеёинорсуыя][гдкмхц]', 0], + + ['[аеи]лец', 2], // жилец + + ['[бвдт]р', 0], // бобр, кентавр, театр, кадр + ['з[дм]', 0], // съезд + ['ол[гк]', 0], // долг, полк + ['офе', 1], // кофе + ['ыв', 0], // взрыв + ['[аеиоу]й',1], // бугай, лакей, гербарий, зной, буй + ['[дрст]ь', 1], // вождь, фонарь, карась, путь + ['ядя', 1], // дядя + ['дья', 2], // судья + + ['[кы]т', 0], // реликт, опыт + ['к[сх]', 1], // бокс, сикх + + // ----- слова среднего рода ----- + + // небо, существо, чудо, яблоко, стекло, письмо, окно, ведро, колесо, пальто, ухо, плечо + ['[бвдклмнрстхч]о', 1], + + // действие, оружие, приспособление, кушанье, участие, отличие, жильё, согласие + ['[вжлнстч][иь][её]', 2], + + ['мя', 1], // время + ['[лрцщ]е', 1] // поле, море, солнце, вместилище + ], + + 'есса' : [ + ['[длн]ие', 1] // правосудие, изделие, мнение + ], + + 'ыня' : [ + ['рин', 2], // барин + ['уз', 0] // арбуз + ], + + 'ица' : [ + ['[дл]ие', 2], // правосудие, изделие + ['ще', 3] // вместилище + ], + + 'ша' : [ + ['у[нт]', 0], // врун, лилипут + ['ир', 0] // командир + ], + + 'ь+я' : [ + ['ун', 0] // врун + ], + + // формы мн.ч. + 'ессы|ыни|ицы' : [ + ['нга', 1], // ранга + ['о[дс]ы', 1], // выводы, матросы + ['усы', 2] // вкусы + ], + + // ----- РАЗНОЕ ----- + + 'инь' : [ + ['дей', 2] //людей -> людинь + ], + + 'на' : [ + ['обно', 2] //способно -> способна + ] +}; + +//Случайный элемент списка +FEM.random_word = wordlist => wordlist[Math.floor(Math.random() * wordlist.length)]; + +//Проверка на исключение +FEM.exceptions.contains = word => Object.keys(FEM.exceptions).includes(word); + +//Список значений +FEM.exceptions.feminitives = word => [FEM.random_word(FEM.exceptions[word][0]), FEM.exceptions[word][0]]; + +//Дефиниция слова-исключения +FEM.exceptions.definition = word => FEM.exceptions[word][1]; + diff --git a/js/feminitives.js b/js/feminitives.js deleted file mode 100644 index a5b829d..0000000 --- a/js/feminitives.js +++ /dev/null @@ -1,497 +0,0 @@ -// Copyright (C) 2016-2017, Maxim Lihachev, -// -// 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 . - -// [ ] 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()); -} - -//Смена названия по клику -function swap_title(id) { - var e = document.getElementById(id); - if(e.style.display == 'inline') { - e.style.display = 'none'; - } else { - e.style.display = 'inline'; - } -} - -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; - - //Конвертирование по нажатию - this.input().addEventListener('keyup', event => { - event.preventDefault(); - event.keyCode == 13 && tr(); - }); - - //Конвертирование по нажатию кнопки - this.button().onclick = () => tr(); -}; - -//------------------------------------------------------------------------------ - -//Правила создания феминитивов -var FEM = {}; - -FEM.endings = { - 'ка' : [ - ['[аеёо]р', 0], - ['[её]т', 0], //авторитет -> авторитетка - ['а[ншб]', 0], //кран -> кранка, краб -> крабка - ['ец', 2], //канадец -> канадка - ['и[лр]', 0], //библиофил -> библиофилка, командир -> командирка - ['ин', 2], //татарин -> татарка - ['ль', 1], //создатель -> создателка - ['о[тн]', 0], //гот -> готка, адрон -> адронка - ['рг', 1], - ['[ус]т', 0], //специалист -> специалистка - ['у[нйп]', 0] //колдун -> колдунка - ], - 'чка' : [ - ['[ае]к', 1] //левак -> левачка, человек -> человечка - ], - 'ина' : [ - ['ас', 0], - ['уй', 1], //буй -> буина - ['уд', 0], //сосуд -> сосудина - ['ил', 0] //библиофил -> библиофилина - ], - 'иха' : [ - ['[аеёов]р', 0], //автор -> авториха - ['а[чб]', 0], //врач -> врачиха, краб -> крабиха - ['ец', 2], //канадец -> канадиха - ['и[лр]', 0], //библиофил -> библиофилиха, командир -> командириха - ['ль', 1], //создатель -> создателиха - ['рк', 0], //кварк -> кваркиха - ['о[гдтнп]', 0], //гот -> готиха, адрон -> адрониха, биолог -> биологиха, метод -> методиха, клоп -> клопиха - ['у[дпт]', 0], //труп -> трупиха, сосуд -> сосудиха - ['уй', 1] //буй -> буиха - ], - 'иня' : [ - ['[аеёов]р', 0], - ['[её]т', 2], //авторитет -> авторитиня - ['[оиы]к', 0], //язык -> языкиня - ['а[вбкфч]', 0], //левак -> левакиня, граф -> графиня, врач -> врачиня, краб -> крабиня, состав -> составиня - ['во', 1], //существо -> существиня - ['ек', 0], //человек -> человекиня - ['ец', 2], //канадец -> канадиня - ['ил', 0], //библиофил -> библиофилиня - ['ль', 1], //создатель -> создателиня - ['о[дг]', 0], //биолог -> биологиня - ['од', 0], //метод -> методиня - ['р[гк]', 0], //кварк -> кваркиня - ['ро', 1], //ведро -> ведриня - ['со', 1], //колесо -> колесиня - ['сс', 0], //процесс -> процессиня - ['[кс]т', 0], //специалист -> специалистиня, объект -> объектиня - ['уй', 1], //буй -> буиня - ['у[тпх]', 0] //труп -> трупиня - ], - 'ыня' : [ - ['ин', 2], // боярин -> боярыня - ], - 'киня' : [ - ['[аеёо]р', 0], - ['ок', 0], - ['ст', 0], //специалист -> специалисткиня - ['ан', 0], - ['ил', 0], //библиофил -> библиофилкиня - ['уй', 0] //буй -> буйкиня - ], - 'есса' : [ - ['[аеёов]р', 0], - ['[её]т', 2], //авторитет -> авторитесса - ['[лн]ь', 1], //создатель -> создателесса, камень -> каменесса - ['[оиы]к', 0], //язык -> языкесса - ['[оэ]т', 0], //гот -> готесса, поэт -> поэтесса - ['а[вчнб]', 0], //врач -> врачесса, баран -> баранесса, краб -> крабесса, состав -> составесса - ['во', 1], //существо -> существесса - ['е[кнц]', 0], //член -> членесса, канадец -> канадесса, человек -> человекесса - ['и[лр]', 0], //библиофил -> библиофилесса, командир -> командиресса - ['[лр]о', 1], //тело -> телесса, ведро -> ведресса - ['о[ндгп]', 0], //метод -> методесса, адрон -> адронесса, биолог -> биологесса, клоп -> клопесса - ['р[гк]', 0], //кварк -> кваркесса - ['ст', 0], //специалист -> специалистесса - ['у[дпнхт]', 0], //сосуд -> сосудесса, колдун -> колдунесса, олух -> олухесса - ['уй', 1], //буй -> буесса - ['ще', 0], //вместилище -> вместилищесса - ['ый', 2], //учёный -> учёнесса - ['яд', 0] //взгляд -> взглядесса - ], - 'ица' : [ - ['[аеёов]р', 0], - ['[её]т', 2], //авторитет -> авторитица - ['а[вбнсч]', 0], //врач -> врачица, краб -> крабица, состав -> составица - ['во', 1], //существо -> существица - ['ен', 0], //член -> членица - ['ец', 2], //канадец -> канадица - ['ик', 2], - ['и[влр]', 0], //библиофил -> библиофилица, командир -> командирица - ['нь', 1], //камень -> каменица - ['о[гд]', 0], //биолог -> биологиня, метод -> методица - ['рг', 0], - ['ст', 3], //специалист -> специалица - ['сс', 0], //процесс -> процессица - ['у[днпчт]', 0], //сосуд -> сосудица, колдун -> колдуница, труп -> трупица - ['уй', 1], //буй -> буица - ['ще', 3], //вместилище -> вместилищесса - ['яд', 0] //взгляд -> взглядица - ], - 'ница' : [ - ['ль', 0], //создатель -> создательница - ['ас', 0], - ['[её]т', 0], //авторитет -> авторитетица - ['ец', 2], //канадец -> канадница - ['уй', 0] //буй -> буйница - ], - 'ая' : [ - ['[ыио]й', 2], //учёный -> учёная, знающий -> знающая - ['ое', 2] - ], - 'ша' : [ - ['[её]т', 1], //вертолёт -> вертолёша - ['ут', 1], //лилипут -> лилипутша - ['ир', 0] //командир -> командирша - ], - 'адья' : [ - ['оп', 0] // поп -> попадья - ], - //----- ДАЛЬШЕ ИДЁТ ШИЗА ----- - 'ии' : [ - ['и[ия]', 2], //металлургии -> металлург_ии, произведения -> произведении - ], - 'ца' : [ - ['ие', 1], //металлургии -> металлург_ии, произведения -> произведении - ], - 'ми' : [ - ['ми', 2] //знаниями -> знания_ми - ], - 'ой' : [ - ['го', 3] //художественного -> художественн_ой - ], - 'инь' : [ - ['ей', 2] //людей -> люд_ей - ], - 'ти' : [ - ['ти', 2] //области -> облас_ти - ], - 'ю' : [ - ['ью', 1] //матерью -> матерь_ю - ], - 'ны' : [ - ['на', 2] //бассейна -> бассей_ны - ], - 'на' : [ - ['но', 2] //способно -> способна - ], - 'ки' : [ - ['ца', 2] //самца -> самки - ], - 'аяся' : [ - ['ся', 4], //занимающийся -> занимающ_аяся - ], -}; - -//Слова со специфичными определениями -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('(^|\\s)+' + fem_w, "ig"), '$1' + 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)]; - -//Оборачивание в с указанным классом -let html_wrap = (str, cl) => `${str}`; - -//Цветовое выделение текста -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, 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}[^\{\}]*\|.*/g, "") //{{1|слово.*$ - .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) || []; - console.log(tokens); - - //Замена местоимений, предлогов и проч. - 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://coors.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(); - } -} diff --git a/js/html2canvas.js b/js/html2canvas.js old mode 100644 new mode 100755