モジュール:File clip

require('strict')
local function showImage(filename, caption, options)
	--=
		画像を表示するウィキテキスト構文を生成して返す
		filename: ファイル名（文字列型、必須）
		caption（）: 画像のキャプション（文字列型、省略可）
		options: その他のオプション（テーブル型、省略可）
	=--
	if type(filename) ~= 'string' or filename ==  then
		error('ファイル名が正しく指定されていません。', 2)
	end
	local t = { filename }
	if options then
		for k, v in pairs(options) do
			table.insert(
				t,
				(type(k) == 'string') and mw.ustring.format('%s=%s', k, v) or v
			)
		end
	end
	if caption then table.insert(t, caption) end
	return mw.ustring.format('File:%s', table.concat(t, '|'))
end
local function isValign(value)
	for _, v in ipairs({'baseline', 'sub', 'super', 'top', 'text-top',
	'middle', 'bottom', 'text-bottom'}) do
		if v == value then return true end
	end
	return false
end
--------------------------------------------------------------------------------
-- fileClipクラス
-- 	
-- fileClipをテーブルのメタテーブルに設定することで、
-- そのテーブルはfileClipクラスのインスタンスであるかのように振る舞う
--------------------------------------------------------------------------------
local fileClip = {}
fileClip.__index = fileClip
function fileClip:_setError(message)
	self._error = mw.html.create('strong')
		:addClass('error')
		:wikitext('エラー：' .. message)
	return self
end
function fileClip._new(args)
	local obj = {}
	setmetatable(obj, fileClip)
	
	-- 配置指定
	obj._align = args.align
	
	if not args1 then
		return obj:_setError('ファイル名が指定されていません。')
	end
	
	-- 表示サイズ（横幅）
	obj._width = tonumber(args.width)
	if not obj._width then
		obj._width = 200
		return obj:_setError('表示サイズが指定されていません。')
	end
	
	-- 切り抜き範囲（百分率）
	-- 指定省略時は0%
	local crop_top = tonumber(args2 or args.t) or 0
	local crop_right = tonumber(args3 or args.r) or 0
	local crop_bottom = tonumber(args4 or args.b) or 0
	local crop_left = tonumber(args5 or args.l) or 0
	
	-- 画像の表示範囲（百分率）を計算
	-- 縦方向・横方向のどちらか一方でも0%以下ならエラー
	local v_rest = 100 - (crop_top + crop_bottom)
	local h_rest = 100 - (crop_right + crop_left)
	if v_rest  1 and filemetadata.pages then
			filemetadata = filemetadata.pagespage
		end
		orig_width = filemetadata.width
		orig_height = filemetadata.height
	end
	
	-- 切り抜き画像の表示サイズ（縦幅）を計算
	obj._height = math.floor(
		(obj._width * orig_height * v_rest) /
		(orig_width * h_rest)
	)
	
	-- 画像を拡大後、上方向および左方向に何pxずらせばよいか計算
	obj._shift_up = math.floor(
		(obj._width * orig_height * crop_top) / 
		(orig_width * h_rest)
	)
	obj._shift_left = math.floor((obj._width * crop_left) / h_rest)
	
	-- 画像のキャプション
	-- 指定省略時は指定されたファイル名（名前空間プレフィックスを含まない）
	obj._caption = args6 or args.c or args.caption or obj._filename
	
	-- 拡大後の画像の横幅、その他のオプション
	obj._image_options = {
		math.floor((obj._width * 100) / h_rest) .. 'px',
		alt = args.alt,
		link = args.link,
		page = args.page,
		class = args.class,
		lang = args.lang
	}
	
	return obj
end
function fileClip:thumb()
	local center = mw.html.create('div'):addClass('center')
	local thumb = mw.html.create('div')
	if self._align == 'center' or self._align == 'none' then
		thumb:addClass('thumb fileclip-thumb tnone')
	elseif self._align == 'left' then
		thumb:addClass('thumb fileclip-thumb tleft')
	else
		thumb:addClass('thumb fileclip-thumb tright')
	end
	local thumbinner = mw.html.create('div')
		:addClass('thumbinner fileclip-thumbinner')
		:css('width', (self._width + 2) .. 'px')
	if self._error then
		thumb:node(thumbinner:node(self._error))
		return (self._align == 'center') and center:node(thumb) or thumb
	end
	local clipper = mw.html.create('div')
		:addClass('fileclip-clipper thumbimage')
		:css({ width = self._width .. 'px', height = self._height .. 'px' })
		:cssText('position: relative; overflow: hidden;')
	local shifting = mw.html.create('div')
		:addClass('fileclip-shifting')
		:css({ top = -self._shift_up .. 'px', left = -self._shift_left .. 'px' })
		:cssText('position: absolute;')
		:wikitext(showImage(self._filename, nil, self._image_options))
	clipper:node(shifting)
	local thumbcaption = mw.html.create('div'):addClass('thumbcaption')
	local magnify = mw.html.create('div')
		:addClass('magnify fileclip-magnify')
		:wikitext(
			showImage(
				'Scissors icon black.svg',
				'拡大',
				{ 'text-top|16px', link = 'File:' .. self._filename }
			)
		)
	thumbcaption:node(magnify):wikitext(self._caption)
	thumb:node(thumbinner:node(clipper):node(thumbcaption))
	return (self._align == 'center') and center:node(thumb) or thumb
		
end
function fileClip:nothumb()
	if self._error then return self._error end
	local center = mw.html.create('div'):addClass('center')
	local aligndiv
	local clipper = mw.html.create('span')
		:addClass('fileclip-clipper')
		:css({ width = (self._width .. 'px'), height = (self._height .. 'px') })
		:cssText('position: relative; overflow: hidden; display: inline-block;')
	
	if self._align == 'center' or self._align == 'none' then
		aligndiv = mw.html.create('div'):addClass('floatnone')
	elseif self._align == 'right' or self._align == 'left' then
		aligndiv = mw.html.create('div'):addClass('float' .. self._align)
	elseif self._align and isValign(self._align) then
		clipper:css({ 'vertical-align' = self._align })
	else
		clipper:css('vertical-align', 'middle')
	end
	
	local shifting = mw.html.create('span')
		:addClass('fileclip-shifting')
		:css({ top = -self._shift_up .. 'px', left = -self._shift_left .. 'px' })
		:cssText('position: absolute;')
		:wikitext(showImage(self._filename, self._caption, self._image_options))
	clipper:node(shifting)
	if not aligndiv then return clipper end
	aligndiv:node(clipper)
	return (self._align == 'center') and center:node(aligndiv) or aligndiv
end
local getArgs
local function _main(mode, frame)
	if not getArgs then getArgs = require('モジュール:Arguments').getArgs end
	local args = getArgs(frame, {
		valueFunc = function (key, value)
			-- alt引数とlink引数については、省略した場合と
			-- 空文字列を指定した場合を区別する
			
			if value then
				value = mw.text.trim(value)
			end
			if value ==  and key ~= 'alt' and key ~= 'link' then
				return nil
			end
			return value
		end,
		wrappers = { 'Template:File clip', 'Template:File clip2' }
	})
	local thumb = fileClip._new(args)
	thumb._export = thumbmode
	return frame:extensionTag{
			name = 'templatestyles',
			args = { src = 'File clip/styles.css' }
		}
		.. tostring(thumb:_export())
		.. 
end
local p = {
	thumb = function(frame) return _main('thumb', frame) end,
	nothumb = function(frame) return _main('nothumb', frame) end
}
return p