Перейти к содержанию

Модуль:Isomer

Википедия — эркенаб энциклопедия сайталдасан материал

Для документации этого модуля может быть создана страница Модуль:Isomer/doc

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..'&nbsp;';
			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..'&nbsp;';
				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..'&nbsp;';
		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)..'&nbsp;';
		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..[=[''':&nbsp;]=];
					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