From 027bc66344fcca84fe879b11dc01eab7e2c50700 Mon Sep 17 00:00:00 2001 From: Pavel Kityan Date: Mon, 4 Nov 2013 18:51:57 +0400 Subject: [PATCH] Code refactoring. Minor bug fixes. --- LICENSE | 2 +- README.md | 36 ++++- morfana.js | 346 +++++++++++++++++++--------------------- morfana.min.js | 12 ++ tests/simple.long.html | 13 +- tests/simple.short.html | 1 + 6 files changed, 222 insertions(+), 188 deletions(-) create mode 100644 morfana.min.js diff --git a/LICENSE b/LICENSE index 27627e6..ad9a838 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Pavel Kityan (http://morfana.ru) +Copyright (c) 2013 Pavel Kityan (pavel@kityan.ru) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 60ca718..0d15ab5 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,38 @@ Morfana JavaScript display engine for morphemic analysis in russian language -[Official website](http://morfana.ru/) \ No newline at end of file +[Official website](http://morfana.ru/) + +Demo HTML document +----- + +``` html + + + + + + + + + + десятиэтажный + + +``` +For more demos visit [official website](http://morfana.ru/) + +##Changelog +`1.0.3a` / `04.11.2013` + +- Code refactoring +- Minor bug fixes + +`1.0.2a` / `04.11.2013` + +- Code refactoring +- Minor bug fixes + +`1.0.1a` / `04.11.2013` + +- Library release \ No newline at end of file diff --git a/morfana.js b/morfana.js index 7e6fef0..55fab11 100644 --- a/morfana.js +++ b/morfana.js @@ -5,33 +5,38 @@ Copyright 2013, Pavel Kityan (pavel@kityan.ru) Licensed under the MIT license. - Version: 1.0.2a + Version: 1.0.3a Build date: 4 November 2013 */ (function( window ) { -var debug = true; +var debug = false; +var config = {} ; -// умолчания -var config = { -autostart: true, // запуск разметки сразу -freezeWord: false // добавлять вертикальное смещение слов (для случая, когда слова в тексте и разметка может залезть на другие строки) или нет (т.е. заморозить слово, что удобно для интерактива) -}; +// default values +configure({ +autoStart: true, // start Morfana after loading complete +freezeWord: false // add vertical padding to word's span or "freeze" word in its inital place +}); -// массив для асинхронной обработки +// array for processing words with setInterval() var queue = new Array(); -// описание морфем +// order of processing +var processingOrder = ['ok', 'pr','ko', 'su', 'os', 'nullok']; + +// morphemes' descriptions var morfemsDescription = {pr: {name: 'приставка'}, ko: {name: 'корень'}, su: {name: 'суффикс'}, os: {name: 'основа'}, ok: {name: 'окончание'}} +// check for dependencies if (!window.jQuery) {return;} if (!window.rangy) {return;} -jQuery( document ).ready(function() { - - // чтение конфига +// DOM ready +jQuery(document).ready(function(){ + // reading user config var scripts = document.getElementsByTagName("script"); for (var i = 0, qty = scripts.length; i < qty; i++) { @@ -43,9 +48,10 @@ jQuery( document ).ready(function() { } } - // инициализация rangy + // rangy init rangy.init(); + // autostart if not denied by user if (config['autostart']){draw();} }); @@ -82,43 +88,41 @@ function preprocess(el) var out = new Object(); out['obj'] = obj; out['morfems'] = new Array(); - var morfems = obj.attr('data-morfana-markup').split(";"); + + // cleaning data-morfana-markup value, splitting + var morfems = obj.attr('data-morfana-markup').replace(/\s/g, "").split(";"); var tmparr = new Object(); for (var i=0, qty = morfems.length; i< qty; i++) { - // добавить trim-ы! var tmp = morfems[i].split(":"); if (tmp[1]) { var arr = tmp[1].split("-"); var m = tmp[0]; + // because of the start value for zero ending is 0, we should mark as "nullok" it for making correct order of processing if (m == 'ok' && arr[0] == 0){m = 'nullok';} - // чтобы обработать отдельно - в самую последнюю очередь, после окончаний (если основа включает окончание в сложном слове) - // таким образом мы полчим массив массивов, обрабатывая который имеем упорядоченный набор (т.е. окончания пойдут в порядке следования) - + if (!tmparr[m]) { - // созадем массив для морфемы + // array for current morpheme tmparr[m] = new Array(); } if (!tmparr[m][arr[0]]) { - // созадем массив для морфемы для номера буквы в слове (могут несколько одинаковых морфемы (хотя не должны) и неодинаковых (например основа слова и приставка) начинаться с одного символа) + // array for start for current morpheme tmparr[m][arr[0]] = new Array(); } - // здесь именно tmp[0], а не m, поскольку 'nullok' нам нужен только для упорядочивания + // here we use strictly tmp[0] but not m, because "nullok" is only for making correct order of processing tmparr[m][arr[0]].push({name: tmp[0], range: arr}); } } - // порядок обработки морфем - var order = ['ok', 'pr','ko', 'su', 'os', 'nullok']; - - for (i=0, qty = order.length; i< qty; i++) + // creating correct order of processing + for (i=0, qty = processingOrder.length; i< qty; i++) { - m = order[i]; + m = processingOrder[i]; if (tmparr[m]) { for (var k=0, qty2 = tmparr[m].length; k< qty2; k++) @@ -134,13 +138,13 @@ function preprocess(el) } } - // собственно, сама разметка + // process(out); } /** - Установка для элемента и всех вложенных свойства CSS + Setting element's and all its children's CSS property */ function setAllChildren(obj, param, value) { @@ -153,11 +157,11 @@ function setAllChildren(obj, param, value) } /** - * Оборачиваем символы морфемы "окончание" в span с padding-left и padding-right - * @param {number} start - первый символ морфемы по порядку в слове (нумерация - с единицы) - * @param {number} stop - последний символ морфемы по порядку в слове (нумерация - с единицы) - * @param {boolean} left - Отступы и слева и справа. Если нет, тогда только справа - увеличенный. Это значит, что окончание "нулевое", а следовательно надо дать отступ побольше, перед следующим словом - * @param {array} map - Карта символов для работы rangy + * Wrapping symbols in morpheme "ok" with spans with padding-left and padding-right + * @param {number} start - number of first morpheme's symbol (starting with 1) + * @param {number} stop - number of last morpheme's symbol (starting with 1) + * @param {boolean} left - if false, padding-right should be bigger than padding-left + * @param {array} map - symbol map of word for rangy */ function wrapMorfanaPadding(start, stop, left, map, obj) @@ -166,15 +170,14 @@ function wrapMorfanaPadding(start, stop, left, map, obj) if (!left) { - // [2do] высоту брать в одном месте! в process - var metrics = getMetrics(obj,start,stop); + var h = getMetrics(obj,start,stop, true); } var rng = rangy.createRange(); rng.setStart(map[stop-1]['obj'], map[stop-1]['index']); rng.setEnd(map[stop-1]['obj'], map[stop-1]['index']+1); var newNode = document.createElement('span'); - jQuery(newNode).css('padding-right',(left)?'5px':((metrics.h)+'px')); + jQuery(newNode).css('padding-right',(left)?'5px':((h)+'px')); jQuery(newNode).addClass('morfana-paddings'); rng.surroundContents(newNode); @@ -194,55 +197,39 @@ function wrapMorfanaPadding(start, stop, left, map, obj) /** - Установка для всех родительских элементов obj вплоть до stopParent - */ -function setAllParents(obj, param, value, stopParent) -{ - obj.css(param, value); - if (obj.parent()[0] != stopParent[0]) - { - setAllParents(obj.parent(), param, value, stopParent); - } -} - - -/** - * Функция расчета метрик слова целиком или морфемы: высоты, ширины, смещения морфемы относительно начала слова + * Calculating word's height and morpheme's x and width */ - function getMetrics(obj,start,stop, savePaddings) + function getMetrics(obj,start,stop, returnHeight) { var objHTML = obj.html(); - // временый элемент внутри элемента со словом, для метрик - var tmpdv = $('
') + // creating temporary div inside word's element + var tmpdv = $('
') obj.append(tmpdv); - // определяем высоту всего слова, принудительно установив line-height в normal + // setting line-height to normal, calculating words's height tmpdv.html(objHTML); var h2 = tmpdv.height(); setAllChildren(tmpdv, 'line-height', 'normal'); var h = tmpdv.height(); - + if (returnHeight){tmpdv.remove(); return h;} - // определяем смещение и ширину участка слова, заданного start/stop - //alert(objHTML) + // calculationg morpheme's width and x tmpdv.html(objHTML); var map = getLettersMap(tmpdv); var li = map.length - 1; var newNode; var rng = rangy.createRange(); - - // отсекаем лишнее от слова, если нужно + // cutting word's part we don't need if ((stop - 1) < li) { rng.setStart(map[stop]['obj'], map[stop]['index']); - rng.setEnd(map[li]['obj'], map[li]['index']+1); + rng.setEnd(map[li]['obj'], map[li]['index']+1); rng.deleteContents(); - // зачищаем остатки, которые не убирает rng.deleteContents() + // cleaning up after rng.deleteContents() tmpdv.find('.morfana-paddings').each(function(){var obj = jQuery(this); if (obj.text() == ''){obj.remove()}}); } - // сейчас в tmpdv содерджится фрагмент слова, ширина которого дает нам x+w // мы сбрасываем letter-spacing и padding-right последнего символа, чтобы значок морфемы заканчивался на символе, а не после него @@ -252,17 +239,17 @@ function setAllParents(obj, param, value, stopParent) jQuery(newNode).css('letter-spacing','normal'); rng.surroundContents(newNode); - // здесь в w пока содержится x + // this w value has x value inside var w = tmpdv.width(); - // ищем x + // calcaulating x value if (start == 1) { var x = 0; } else { - // требуется новая карта + // rebuilding map var map = getLettersMap(tmpdv); rng.setStart(map[start-1]['obj'], map[start-1]['index']); rng.setEnd(map[stop-1]['obj'], map[stop-1]['index']+1); @@ -270,6 +257,8 @@ function setAllParents(obj, param, value, stopParent) var x = tmpdv.width(); } tmpdv.remove(); + + // clean w value w-=x; var hDiff = h2-h; @@ -279,145 +268,138 @@ function setAllParents(obj, param, value, stopParent) function process(p) { -var obj = p['obj']; - -// зачищаем от предыдущей отрисовки -// [!] нужно заменить на удалению добавленных "обёрток" вообще, чтобы не росло дерево при многократных обработках + var obj = p['obj']; -obj.find(".morfana-graphics").remove(); -obj.find(".morfana-paddings").css('padding-right', '0px'); -obj.find(".morfana-paddings").css('padding-left', '0px'); -obj.find(".morfana-paddings").removeClass('morfana-paddings'); + // cleaning up after previous draw() + obj.find(".morfana-graphics").remove(); + obj.find(".morfana-paddings").contents().unwrap(); -var map = getLettersMap(obj); + var map = getLettersMap(obj); + var prependElements = new Array(); + var h = getMetrics(obj, 1, map.length + 1, true); -var prependElements = new Array(); -var h; - -for (var i=0; i 0 && !nullOk){w += 10; x-=5;} + var h = metrics.h*1.4; + var w = (!nullOk)?(metrics.w + ((stop == start)?10:0)):(h * 0.3+10); + var x = (!nullOk)?(metrics.x + ((start!=stop)?5:0)):(metrics.w - h*.5 + 3); + var hDiff = metrics.hDiff; -// почему не rect? -// "z-index: -1" в стиле svg позволяет сделать буквы внутри окончания доступными для мыши, но в ряде случаев окончания пропадают (см. wordpress). Решить проблему. -return {h: metrics.h, str:'\ -\ -'}; + // компенсируем паддинги, поскольку будучи примененными к разным символам теряются, при таком методе рассчете ширины как сейчас + if ((stop - start) > 0 && !nullOk){w += 10; x-=5;} + // почему не rect? + // "z-index: -1" в стиле svg позволяет сделать буквы внутри окончания доступными для мыши, но в ряде случаев окончания пропадают (см. wordpress). Решить проблему. + return '\ + \ + '; } - -// остальные морфемы +// all morphemes except "ok" function createImage(morphemeType, obj, start, stop, map) { -var metrics = getMetrics(obj,start,stop); -var w = metrics.w; var h = metrics.h; var x = metrics.x; var hDiff = metrics.hDiff; -var hm = 0.34; -switch (morphemeType) -{ - case 'ko': return {h: h, str: ''}; - case 'su': return {h: h, str: ''}; - case 'os': return {h: h, str: ''}; - case 'pr': return {h: h, str: ''}; -} - + var metrics = getMetrics(obj,start,stop); + var w = metrics.w; var h = metrics.h; var x = metrics.x; var hDiff = metrics.hDiff; + var hm = 0.34; + switch (morphemeType) + { + case 'ko': return ''; + case 'su': return ''; + case 'os': return ''; + case 'pr': return ''; + } } function getLettersMap(obj) { -var map = new Array(); + var map = new Array(); - (function createLettersMap(obj, shift) - { - var qty = obj[0].childNodes.length; - for (var i=0; i < qty; i++) - { - var data = obj[0].childNodes[i].data; - if (data == undefined) + (function createLettersMap(obj, shift) { - shift = createLettersMap(jQuery(obj[0].childNodes[i]), shift); - } - else + var qty = obj[0].childNodes.length; + for (var i=0; i < qty; i++) { - for (var j=0;j < data.length; j++) - { - map[shift] = {obj: obj[0].childNodes[i], index: j}; - shift++; - } - } - - } - return shift; - })(obj, 0); + var data = obj[0].childNodes[i].data; + if (data == undefined) + { + shift = createLettersMap(jQuery(obj[0].childNodes[i]), shift); + } + else + { + for (var j=0;j < data.length; j++) + { + map[shift] = {obj: obj[0].childNodes[i], index: j}; + shift++; + } + } + } + return shift; + })(obj, 0); -return map; + return map; } -function draw (el,markup){ +function draw (el,markup) +{ if (el) { if (markup != undefined) @@ -431,24 +413,26 @@ function draw (el,markup){ jQuery('[data-morfana-markup]').each(enqueue); } - // разворачиваем массив очереди для FIFO queue.reverse(); doQueue(); } -function enqueue() {queue.push(this);} +function enqueue() +{ + queue.push(this); +} function doQueue() { -var qty = queue.length; -if (qty > 0) - { - preprocess(queue.pop()) - } -if (qty > 1) - { - setTimeout(doQueue, 10); - } + var qty = queue.length; + if (qty > 0) + { + preprocess(queue.pop()) + } + if (qty > 1) + { + setTimeout(doQueue, 10); + } } @@ -457,14 +441,16 @@ function getMorfemDescription(m) return morfemsDescription[m]; } -function configure (obj){ +function configure (obj) +{ for(prop in obj) { - config[prop] = obj[prop]; + var lc = prop.toLowerCase(); + config[lc] = obj[prop]; } } -// create global object, export API +// API var Morfana = {}; Morfana.draw = draw; Morfana.configure = configure; diff --git a/morfana.min.js b/morfana.min.js new file mode 100644 index 0000000..99ff7a2 --- /dev/null +++ b/morfana.min.js @@ -0,0 +1,12 @@ +/* + Morfana. JavaScript display engine for morphemic analysis in russian language + http://morfana.ru + http://github.com/kityan/morfana + + Copyright 2013, Pavel Kityan (pavel@kityan.ru) + Licensed under the MIT license. + Version: 1.0.3a + Build date: 4 November 2013 +*/ + +(function(window){var debug=false;var config={};configure({autoStart:true,freezeWord:false});var queue=new Array();var processingOrder=["ok","pr","ko","su","os","nullok"];var morfemsDescription={pr:{name:""},ko:{name:""},su:{name:""},os:{name:""},ok:{name:""}};if(!window.jQuery){return}if(!window.rangy){return}jQuery(document).ready(function(){var scripts=document.getElementsByTagName("script");for(var i=0,qty=scripts.length;i');obj.append(tmpdv);tmpdv.html(objHTML);var h2=tmpdv.height();setAllChildren(tmpdv,"line-height","normal");var h=tmpdv.height();if(returnHeight){tmpdv.remove();return h}tmpdv.html(objHTML);var map=getLettersMap(tmpdv);var li=map.length-1;var newNode;var rng=rangy.createRange();if((stop-1)0&&!nullOk){w+=10;x-=5}return' '}function createImage(morphemeType,obj,start,stop,map){var metrics=getMetrics(obj,start,stop);var w=metrics.w;var h=metrics.h;var x=metrics.x;var hDiff=metrics.hDiff;var hm=0.34;switch(morphemeType){case"ko":return'';case"su":return'';case"os":return'';case"pr":return''}}function getLettersMap(obj){var map=new Array();(function createLettersMap(obj,shift){var qty=obj[0].childNodes.length;for(var i=0;i0){preprocess(queue.pop())}if(qty>1){setTimeout(doQueue,10)}}function getMorfemDescription(m){return morfemsDescription[m]}function configure(obj){for(prop in obj){var lc=prop.toLowerCase();config[lc]=obj[prop]}}var Morfana={};Morfana.draw=draw;Morfana.configure=configure;Morfana.getLettersMap=getLettersMap;Morfana.getMorfemDescription=getMorfemDescription;window.Morfana=Morfana})(window); diff --git a/tests/simple.long.html b/tests/simple.long.html index 582770a..602dce3 100644 --- a/tests/simple.long.html +++ b/tests/simple.long.html @@ -5,18 +5,19 @@ Morfana simple.long test - + - +
- Авиапо́чта + Авиапо́чта двумя́ста́ми / слова без разметки / слова без разметки / дв-умя́-ст-а́ми десятиэтажных @@ -32,7 +33,7 @@

десятиэтажный
-
+
diff --git a/tests/simple.short.html b/tests/simple.short.html index b92e746..4650775 100644 --- a/tests/simple.short.html +++ b/tests/simple.short.html @@ -9,6 +9,7 @@ десятиэтажный +десятиэтажный