Модуль:Isomer
|
Этот модуль оценён как готовый к использованию. Предполагается, что все баги устранены и он готов для широкого использования. Его можно указывать на справочных страницах и рекомендовать к использованию новым участникам. Для его изменения и тестирования, пожалуйста, используйте песочницу. |
Содержание
Функции
Section
{{#invoke:Isomer|Section|full-width|half-width|title-background|desc-background|data-background|border-color|shortnames|title|mandatory-title|wide-column|collapsed|nocat|1|2|3|…}} — отображает несколько строк (секцию) таблицы. Перед первым вызовом функции в тексте должно быть начало таблицы ({|) или начало новой строки (|-), после последнего — окончание таблицы (|}) или следующая строка. Параметры вызова:
- half-width
- Ширина блока, расположенного в правой ячейке.
- full-width
- Ширина блока, занимающего две объединённые ячейки.
- title-background
- Цвет фона заголовка секции.
- desc-background
- Цвет фона левой ячейки.
- data-background
- Цвет фона правой ячейки.
- border-color
- Цвет границы заголовка секции.
- shortnames
- Краткое имя, список, элементы которого идентифицируют отображаемое значение как относящееся к определённому изомеру.
- title
- Текст заголовка секции, при отсутствии заголовок не отображается.
- mandatory-title
- Заголовок отображается даже для пустой секции (любое непустое значение).
- wide-column
- Таблица должна иметь три столбца (см. например {{drugbox}}), если параметр равен 1 — объединяются первый и второй столбец, если 2 — второй и третий.
- collapsed
- Секция свёрнута (любое непустое значение).
- nocat
- Автоматическая категоризация (см. ниже category) отключена (любое непустое значение).
Далее идут тройки безымянных параметров:
- 1
- Описание параметра, текст левой ячейки. При пустом значении ячейки объединяются.
- 2
- Величина параметра, текст правой ячейки. Если все параметры 2 в секции пустые, секция не отображается.
- 3
- Дополнительные параметры.
Строка может иметь несколько дополнительных параметров, которые отделяются друг от друга точкой с запятой. Значения параметра, если они имеются, отделяются от его имени двоеточием. Значение параметра не должно содержать точку с запятой, знаки равенства должны экранироваться. Дополнительные параметры:
- unit:единица\минимальная степень\максимальная степень\используемая степень
- Отображается физическая величина с нормализацией значения.
- единица — единица физической величины, обычно используется без приставок, например «г»;
- минимальная и максимальная степень — степени десятичных множителей, используемые для данной величины, например «г\-6\3» указывает, что могут использоваться микрограммы, миллиграммы, граммы, килограммы, но не мегаграммы («0\0» запрещает модификацию значения, в этом случае можно указывать величину с приставкой, например «нм\0\0»);
- используемая степень — множитель, применяющийся для величины, например «Дж\\\3» означает, что энергия указывается в килоджоулях.
- Параметр 2 может быть диапазоном значений.
- link:ссылка
- Отображается указанная ссылка, параметр 2 добавляется к ссылке и используется как её заголовок.
- columns:колонки
- Указывает число колонок, используется для вывода изображений. Параметр 2 интерпретируется как имя файла, если файл не найден, то значение параметра выводится без изменений.
- templates:шаблон
- Параметр 2 передаётся в указанный шаблон.
- word-wrap
- Длинные слова могут разбиваться для переноса.
- image-width:ширина
- Ограничивает ширину изображения по сравнению с расчётной по ширине поля или колонки.
- category:категория
- Если параметр 2 пустой, страница включается в указанную категорию.
- multivalue-separator:входной\выходной
- Разделитель нескольких значений параметров внутри изомера, по умолчанию «; \<br />». Входной используется для разделения входных значений, выходной добавляется к обработанным строкам. Если указан пустой входной разделитель, строка будет обрабатываться целиком, без разделения.
- extension:расширение
- Используется дополнительная обработка значения параметра 2.
- GHS — коды разделённые произвольным, но одинаковым для всей строки символом передаются в шаблон {{H-фраза}} или {{P-фраза}}.
- NFPA704 — четыре значения, разделённые запятыми, используются в качестве соответствующих параметров шаблона {{NFPA 704}}.
- ICD10 — каждое значение из списка, разделённого запятыми, используется в шаблоне {{ICD10}} (с необходимым разделением на части).
- capitalise — заменяет первый символ на заглавный.
IsomerButtons
{{#invoke:Isomer|IsomerButtons|1}} — отображает строку — список изомеров. Параметры вызова:
- title-background
- Цвет фона заголовка.
- border-color
- Цвет границы заголовка.
- dark-color
- Цвет тёмного фона.
- light-color
- Цвет светлого фона.
- title
- Текст заголовка, см. выше.
- 1
- Краткое имя, см. shortnames выше.
Isomers
{{#invoke:Isomer|Isomers|1|2|unit|link|columns|width}} — отображает значения параметра для каждого изомера. Функция в шаблоне {{Изомеры}} непосредственно больше не используется (заменена на Section) и будет исключена из модуля. Параметры вызова:
- 1
- Краткое имя, см. выше.
- 2
- Отображаемая величина, ссылка, файл.
- unit
- Отображается физическая величина с нормализацией значения, см. выше.
- link
- Отображается указанная ссылка, см. выше.
- columns
- Указывает число колонок, см. выше.
- width
- Ширина поля или общая ширина всех колонок.
Во избежание поломок страниц, использующих данный модуль, желательно экспериментировать в Песочнице для модулей.
local I={} -- Формирует содержимое ячейки для шаблона Изомеры local IsDigit IsDigit=function(char) return mw.ustring.find('0123456789⁰¹²³⁴⁵⁶⁷⁸⁹',char,1,true) end local IsExclus IsExclus=function(char) return mw.ustring.find('%°℃K',char,1,true) end local physval physval=function(text,physunit) local s=mw.text.trim(text); local lastchar=mw.ustring.sub(s,-1); if (IsDigit(lastchar)) then local prefs={ [-24]='и', [-21]='з', [-18]='а', [-15]='ф', [-12]='п', [-9]='н', [-6]='мк', [-3]='м', [0]='', [3]='к', [6]='М', [9]='Г', [12]='Т', [15]='П', [18]='Э', [21]='З', [24]='И' } local p=mw.text.split(physunit,'\\',true); -- например: г\-6\3\0 local punit=p[1]; -- граммы (г) local pmin=tonumber(p[2]) or -24; -- допустимые приставки и множители: от мк (10⁻⁶) local pmax=tonumber(p[3]) or 24; -- до к (10³) local pval=tonumber(p[4]) or 0; -- текущее значение: граммы (10⁰) local v=mw.text.split(s,'[—…]',false); -- если диапазон значений local vmin=v[1]; local vmax=v[2]; local firstchar=mw.ustring.sub(vmin,1,1); -- знаки <, > и т. д. перед числом, но не минус if ((firstchar~='-') and (firstchar~='−') and not IsDigit(firstchar)) then vmin=mw.ustring.sub(vmin,2); else firstchar=nil; end local lang=mw.language.new('ru'); vmin=lang:parseFormattedNumber(vmin); vmax=lang:parseFormattedNumber(vmax); if ((vmin) and (not v[2] or vmax)) then -- распознаны оба числа диапазона или единственное local pow, p; if (not IsExclus(mw.ustring.sub(punit,1,1))) then if (vmax and ((math.abs(math.floor(math.log10(math.abs(vmax)))) % 3) < (math.abs(math.floor(math.log10(math.abs(vmin)))) % 3))) then p=math.log10(math.abs(vmax)); -- напр.: 120—1400 Ед → 0,12—1,4 кЕд else p=math.log10(math.abs(vmin)); if ((p<0) and (p>=-1)) then p=0; -- напр.: 120 мЕд → 0,12 Ед end; end; pow=math.floor(p / 3) * 3 +pval; pow=math.max(pow,pmin); pow=math.min(pow,pmax); else pow=0 -- к указанным ед. приставки не применяем end vmin=vmin/(10^(pow-pval)); s=firstchar or ''; -- начало форматирования if (firstchar and (vmin<0)) then s=s..' '; end s=s..lang:formatNum(vmin); if (vmax) then vmax=vmax/(10^(pow-pval)); if (vmin<0) then s=s..'…'; else s=s..'—'; end if (vmax<0) then s=s..' '; else if (vmin<0) then s=s..'+'; end end s=s..lang:formatNum(vmax); end punit=prefs[pow]..punit; else punit=prefs[pval]..punit; -- похоже на число, но не обработано end if (punit~='°') then s=s..' '; end s=s..punit; end return s end local formatimg formatimg=function(text,width) local s=text; if (mw.ustring.sub(text,1,1)~='[') then local o=mw.title.new(text,'Media'); if (o) and (o.exists) then s='[[File:'..text..'|'..tostring(width)..'px]]'; end end return s end local splitPrefValSuff splitPrefValSuff=function(text) local p,s; local pref=''; local val; local suff=''; p=mw.ustring.find(text,':'); s=mw.ustring.find(text,'(',p or 2,true); if (p) then pref=mw.ustring.sub(text,1,p)..' '; p=p+1; end if (s) then suff=' '..mw.ustring.sub(text,s,-1); s=s-1; end val=mw.ustring.sub(text,p or 1,s); return pref,val,suff end local extGHS extGHS=function(text) local s=''; local v={}; local firstchar=mw.ustring.sub(text,1,1); if ((firstchar=='P') or (firstchar=='H')) then local f=mw.getCurrentFrame(); local pos=1; local delimiter; repeat -- разделитель может быть любой, но + используется внутри фразы pos=mw.ustring.find(text,firstchar,pos+1,true); if (pos) then delimiter=mw.ustring.sub(text,pos-1,pos-1); end until ((not pos) or (delimiter~='+')) if (pos) then v=mw.text.split(text,delimiter,true); else v={text}; end for i=1,#v do if (firstchar=='P') then s=s..f:expandTemplate{title='P-фраза',args={v[i]}}; else s=s..f:expandTemplate{title='H-фраза',args={v[i]}}; end if (i<#v) then s=s..', '; end end else s=text; end return s end local extNFPA extNFPA=function(text) local s=''; local v={}; local f=mw.getCurrentFrame(); if (mw.ustring.find(text,',',1,true)) then v=mw.text.split(text,',',true); else -- викиданные без разд. дабы не вывод. пустое поле) v[1]=mw.ustring.sub(text,1,1); v[2]=mw.ustring.sub(text,2,2); v[3]=mw.ustring.sub(text,3,3); v[4]=mw.ustring.sub(text,4,-1); end s=f:expandTemplate{title='NFPA 704',args={['опасность для здоровья'] = v[1], ['огнеопасность'] = v[2], ['реакционоспособность'] = v[3], ['прочее'] = v[4]}} return s end local extICD10 extICD10=function(text) local s=''; if (mw.ustring.find(text,']]',1,true)) then s=text; -- есть викификация, не трогаем else local v=mw.text.split(text,',',true); local str,sA,sB; local f=mw.getCurrentFrame(); for i=1,#v do str=mw.text.trim(v[i]); sA=mw.ustring.sub(str,1,1); sB=mw.ustring.sub(str,2,-1); s=s..f:expandTemplate{title='ICD10',args={sA,sB}}; if (i<#v) then s=s..', '; end end end return s end local capitalise capitalise=function(text) local pos=(mw.ustring.find(text,'|') or 0) + 1; -- если викифицированно local s=mw.ustring.sub(text,1,pos-1)..mw.ustring.upper(mw.ustring.sub(text,pos,pos))..mw.ustring.sub(text,pos+1,-1); return s end local processvalue processvalue=function(text,opt) local s; if (opt.ext=='GHS') then --extensions text=extGHS(text); elseif (opt.ext=='NFPA704') then text=extNFPA(text); elseif (opt.ext=='ICD10') then text=extICD10(text); end if (opt.physunit) then s=physval(text,opt.physunit); elseif (opt.link) then s=[[<span class="reflink plainlinksneverexpand">[]]..opt.link..text..' '..text..[[]</span>]]; elseif (opt.columns) then s=formatimg(text,opt.imgwidth); elseif (opt.temps) then local f=mw.getCurrentFrame(); s=f:expandTemplate{title=opt.temps,args={text}}; else s=text; end return s end local getmultivalues getmultivalues=function(text,opt) local multivalues={}; local prefvalsuff={}; local strsepin='; '; if (opt.mvsep) then -- разделители множественных значений (на входе и на выходе) local sep=mw.text.split(opt.mvsep,'\\',true) strsepin=sep[1]; end local pref,val,suff; local hasHtml = mw.ustring.find(text,'<%a.->'); -- ((mw.ustring.sub(text,1,1)=='<') and (mw.ustring.sub(text,2,2)~=' ') and (not IsDigit(mw.ustring.sub(text,2,2)))) if (hasHtml or (#strsepin==0)) then if (hasHtml) then opt.ext=nil; --на входе html, ничего не трогаем end prefvalsuff={ pref='', val=text, suff='' } table.insert(multivalues,prefvalsuff); else local v=mw.text.split(text,strsepin,true); -- значения можно разделять '; ' for i=1,#v do if (opt.columns) then pref,val,suff='',v[i],''; --имя файла, не трогаем else pref,val,suff=splitPrefValSuff(v[i]); -- с собственными пояснениями end val=mw.text.trim(val); prefvalsuff={ pref=pref, val=val, suff=suff } table.insert(multivalues,prefvalsuff); end end return multivalues end local processmultivalues processmultivalues=function(values,opt) local s=''; local strsepout=[[<br />]]; if (opt.mvsep) then -- разделители множественных значений (на входе и на выходе) local sep=mw.text.split(opt.mvsep,'\\',true) if (sep[2]) then strsepout=sep[2]; end end if (opt.capitalise) then values[1].val=capitalise(values[1].val); end for i=1, #values do s=s..values[i].pref..processvalue(values[i].val,opt)..values[i].suff; if (i<#values) then s=s..strsepout; end end return s end local getlocalisomers getlocalisomers=function(shortnamelist,textlist,opt) local isomers={}; local shortnames, text; if ((#shortnamelist>0) and (mw.ustring.find(textlist,'\\'))) then shortnames=mw.text.split(shortnamelist,'\\'); text=mw.text.split(textlist,'\\'); n=#shortnames; else shortnames={}; text={textlist}; n=1; end for i=1,n do if (text[i]) and (#text[i]~=0) then local isomer={ shortname = shortnames[i], values = getmultivalues(text[i],opt) } table.insert(isomers,isomer); end end return isomers end local getproperty getproperty=function(property,firstonly) local propertyvalues={}; local entity=mw.wikibase.getEntityObject(); if (entity and entity.claims and entity.claims[string.upper(property)]) then local rank = 'normal' for i, statement in pairs( entity.claims[string.upper(property)] ) do if (statement.rank == 'preferred') then rank = 'preferred' break end end for i, statement in pairs( entity.claims[string.upper(property)] ) do if (statement.rank == rank) then if (statement.mainsnak.snaktype == 'value') then local val; if (statement.mainsnak.datavalue.type == 'wikibase-entityid') then local id = 'Q'..statement.mainsnak.datavalue.value['numeric-id']; local link = mw.wikibase.sitelink(id) local label = mw.wikibase.label(id); if link then if label then val='[['..link..'|'..label..']]'; else val='[['..link..']]'; end else if label then val=label; else val='[[d:'..id..'|'..id..']]'; end end elseif statement.mainsnak.datavalue.type == 'quantity' then val=statement.mainsnak.datavalue.value['amount']; else val=statement.mainsnak.datavalue.value; end table.insert(propertyvalues,val); if (firstonly) then break; end end end end end return propertyvalues end local getwikidataisomers getwikidataisomers=function(shortnamelist,textlist,opt) local isomers={}; local multivalues={}; local prefvalsuff={}; local val={}; if (mw.ustring.find(opt.property,'\\')) then local s=''; --several prop. in one cell (NFPA 704) local properties=mw.text.split(opt.property,'\\'); for i=1, #properties do s = s..(getproperty(properties[i],true)[1] or ''); end if (#s>0) then val={s}; end else val = getproperty(opt.property,false); end for i=1, #val do prefvalsuff={ pref='', val=val[i], suff='' } table.insert(multivalues,prefvalsuff); end if (#multivalues > 0) then local isomer={ shortname = nil, values = multivalues } table.insert(isomers,isomer); end return isomers end local processisomers processisomers=function(row) local s=''; local str, str2, colwidth; if (row.options.columns) then colwidth=math.floor(row.options.width/row.options.columns); else colwidth=row.options.width; end if ((not row.options.imgwidth) or (row.options.imgwidth > colwidth)) then row.options.imgwidth=colwidth; end if (row.options.width and row.isomers[1].shortname) then row.options.imgwidth=row.options.imgwidth-20; end for i=1,#row.isomers do -- if (text[i]) and (#text[i]~=0) then if ((row.isomers[i].shortname) or row.options.width) then -- оборачиваем рез-т в div и (или) подписываем кратким названием str='<div'; if (row.isomers[i].shortname) then str=str..[[ class="mw-collapsible" id="mw-customcollapsible-isomer]]..tostring(i)..[=["]=]; end if (row.options.width) then str=str..[[ style="width:]]..tostring(colwidth)..[=[px; word-wrap:break-word;]=]; if (row.options.columns) then str=str..' display:inline-block; vertical-align:top'; end str=str..[["]]; --end style end str=str..'>'; --end div if (row.isomers[i].shortname) then if (row.options.columns) then str=str..[[<span style="vertical-align:top; letter-spacing:normal;">]]; end str=str..[[''']]..row.isomers[i].shortname..[=[''': ]=]; if (row.options.columns) then str=str..[[</span>]]; end end str2=[[</div>]]; else str=''; str2=''; end s=s..str..processmultivalues(row.isomers[i].values,row.options)..str2; -- end end if (row.options.columns) then s=string.format([[<div style="width:%ipx; text-align:center; letter-spacing:-5px; margin: 0 auto">%s</div>]],row.options.width,s); end return s end --[=[function I.Isomers(frame) local shortnamelist=frame.args[1]; local textlist=frame.args[2]; local opt={}; opt.physunit=frame.args['unit']; opt.link=frame.args['link']; opt.width=tonumber(frame.args['width']); opt.columns=tonumber(frame.args['columns']); return isomers(shortnamelist,textlist,opt) end]=] local getrow getrow=function(shortnames,desc,value,opt) local row; local isomers={}; if (opt.property) then isomers=getwikidataisomers(shortnames,value,opt); end if (#isomers == 0) then opt.property = nil; --данные получены не из викиданных isomers=getlocalisomers(shortnames,value,opt); end if (#isomers > 0) then row={ isomers = isomers, description = desc, options = opt } end return row end -- Формирует строку - список изомеров function I.IsomerButtons(frame) local titlebg=frame.args['title-background'] or '#F8EABA'; local bordercolor=frame.args['border-color'] or '#C0C090'; local darkcolor=frame.args['dark-color'] or '#F0F0C0'; local lightcolor=frame.args['light-color'] or '#F8F8C8'; local title=frame.args['title']; local shortnamelist=frame.args[1]; local s=''; local bgcolor; if (#shortnamelist>0) then if ((title) and (#title>0)) then s=s..string.format([[!colspan="2" align="center" cellspacing="3" style="border:1px solid %s;background:%s;margin-bottom:3px"|%s]],bordercolor,titlebg,title); s=s..'\n|-\n|colspan="2" style="padding:0"|\n'; end; local shortname=mw.text.split(shortnamelist,'\\'); local n=#shortname; local width=math.floor(100 / n + 0.5); s=s..[[{| style="border: 0px; border-collapse:collapse; text-align:center; width:100%"]]..'\n'; for i=1,n do if ((i % 2) ==0) then bgcolor=darkcolor; else bgcolor=lightcolor; end s=s..string.format([[|bgcolor="%s" width="%i%%"|<div class="mw-customtoggle-isomer%i">%s</div>]],bgcolor,width,i,shortname[i])..'\n'; end s=s..'|}'; if ((title) and (#title>0)) then s=s..'\n|-\n'; end end return s end -- Формирует секцию: заголовок и поля local hash hash=function(text) local h={0,0,0,0,0,0} local s='' for i = 1, mw.ustring.len(text) do h[(i % 6) + 1]=h[(i % 6) + 1]+mw.ustring.codepoint(text,i); end for i = 1, 6 do s=s..mw.ustring.char((h[i] % 26)+mw.ustring.codepoint('a',1)); end return s end local splitfirst splitfirst=function(s,pattern,plain) local s1,s2; local i=mw.ustring.find(s,pattern,1,plain); if (i) then s1=mw.ustring.sub(s,1,i-1); s2=mw.ustring.sub(s,i+1,-1); else s1=s; s2=''; end return s1,s2 end function I.Section(frame) local fullwidth=tonumber(frame.args['full-width']) or 225; local halfwidth=tonumber(frame.args['half-width']) or math.floor(fullwidth * 0.6); local titlebg=frame.args['title-background'] or '#F8EABA'; local bordercolor=frame.args['border-color'] or '#C0C090'; local descbg=frame.args['desc-background'] or '#F0F0C0'; local databg=frame.args['data-background'] or '#FFFFFF'; local shortnames=frame.args['shortnames'] or ''; local title=frame.args['title']; local mand=frame.args['mandatory-title']; local widecol=tonumber(frame.args['wide-column']) or 0; local strcolspan1=''; local strcolspan2=''; local strcolspan3='colspan="2"'; if (widecol > 0) then strcolspan3='colspan="3"'; if (widecol == 1) then strcolspan1='colspan="2"'; else strcolspan2='colspan="2"'; end end local strcollapsed=''; if (frame.args['collapsed']) then strcollapsed='mw-collapsed'; end local nocat=frame.args['nocat']; local rows={}; local s=''; local r=2; local strid=''; local desc,value,options; local physunit,link,width,columns,ext; local w,optname,optval; r=1; while (frame.args[r]) do desc=mw.text.trim(frame.args[r]); value=mw.text.trim(frame.args[r+1] or ''); options=mw.text.trim(frame.args[r+2]); r=r+3; if (#desc>0) then w=halfwidth; else w=fullwidth; end opt={}; if (options) then for j,u in ipairs(mw.text.split(options,';',true)) do optname,optval=splitfirst(u,'[:=]',false); optname=mw.ustring.lower(mw.text.trim(optname)); optval=optval or ''; optval=mw.ustring.gsub(optval,'&sem&',';') if (optname=='unit') then opt.physunit=optval; elseif (optname=='link') then opt.link=optval; elseif (optname=='capitalise') then opt.capitalise=true; elseif (optname=='word-wrap') then opt.width=w; elseif (optname=='columns') then opt.columns=optval; opt.width=w; elseif (optname=='image-width') then opt.imgwidth=tonumber(optval); opt.width=w; elseif (optname=='extension') then opt.ext=optval; elseif (optname=='templates') then opt.temps=optval; elseif (optname=='multivalue-separator') then opt.mvsep=optval; elseif (optname=='category') then if ((#value==0) and not nocat) then local o=mw.title.getCurrentTitle(); if (o.namespace==0) then s=s..string.format('[[Category:%s]]\n',optval); end end elseif (optname=='property') then opt.property=optval; end end end table.insert(rows,getrow(shortnames,desc,value,opt)); end if ((title) and (#title>0)) then if ((#rows>0) or mand) then local h=hash(title); strid=string.format([[id="mw-customcollapsible-%s" class="mw-collapsible %s"]],h,strcollapsed) s=string.format([[!%s align="center" cellspacing="3" style="border:1px solid %s;background:%s;margin-bottom:3px"|<div class="mw-customtoggle-%s">%s</div>]],strcolspan3,bordercolor,titlebg,h,title)..'\n'; end end for i=1, #rows do if (#rows[i].isomers > 0) then s=s..string.format('|-%s\n',strid); if ((rows[i].description) and (#rows[i].description>0)) then s=s..string.format([[|%s bgcolor="%s"|%s||%s bgcolor="%s"|]],strcolspan1,descbg,rows[i].description,strcolspan2,databg); else s=s..string.format([[|%s bgcolor="%s"|]],strcolspan3,databg); end s=s..processisomers(rows[i])..'\n'; end end s=s..'|-\n'; return s end return I