The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Documentation for this module may be created at Module:Television Rotten Tomatoes scores/doc

-- This module implements {{Television Rotten Tomatoes scores}}.

--------------------------------------------------------------------------------
-- TVRT class
-- The main class.
--------------------------------------------------------------------------------

local TVRT = {}

function TVRT.upper(str)
    return (str:gsub("^%l", string.upper))
end

function TVRT.round(value)
	return math.floor(value + 0.5)
end

-- Create the graph and table
function TVRT.new(frame,args)
	args = args or {}
	
	-- Variables
	local timeline = ""
	local episodes = 0
	local graph_width = args.width and tonumber(args.width) or 600
	local graph_height = args.height and tonumber(args.height) or 300
	local force_gap = args.forcegap or nil

	-- Create table and top caption
	local roottable = mw.html.create('table')
	roottable	
		:addClass('wikitable')
		:addClass(args.align and 'float'..args.align or '')
		:css('text-align', 'center')
		:css('width', (args.width or 600)..'px')
		:tag('caption'):wikitext('<i>'..(args.title or '')..'</i>'..(args.label and ' '..string.lower(args.label) or '')..': Critical reception by episode')
	
	local rootcell = roottable:tag('tr'):tag('td')
	
	-- Border widths 
	local g_l = 50
	local g_r = 50
	local g_t = 30
	local g_b = 40
	local g_lr_border = g_l + g_r
	local g_tb_border = g_t + g_b
	
	local score_min = 100
	local score_max = 0

	-- Number of episodes
	for k,v in pairs(args) do 
		if (string.lower(v) == 'n/a') or (not string.match(k,'[^%d]+') and not string.match(v,'[^%d%.]+')) then
			episodes = episodes + 1
			if v ~= "" and v ~= nil then
				score_min = math.min(score_min, tonumber(v))
				score_max = math.max(score_max, tonumber(v))
			end
		end
	end
	
	-- Position varibles for each entry 
	local old_x = g_l
	local old_y = 0
	local new_x = old_x
	local new_y = old_y
	local increment_x = TVRT.round((graph_width-g_lr_border)/(episodes-1))
	
	local period_min = 0
	local period_max = 100
	
	if args.minimize_y then
		period_min = math.floor(score_min/10)*10
		period_max = math.ceil(score_max/10)*10
	end
	
	-- One percentage on the y axis is this many pixels 
	local percent_pixel = (graph_height-g_tb_border)/(period_max-period_min)
	
	-- Create the timeline
	timeline = timeline .. "\nImageSize  = width:"..graph_width.." height:"..graph_height.."\n"
	timeline = timeline .. "\nPlotArea   = left:"..g_l.." bottom:"..g_b.." top:"..g_t.." right:"..g_r.."\n"
	timeline = timeline .. "\nTimeAxis   = orientation:vertical format:y\n"
	timeline = timeline .. "\nAlignBars  = justify\n"
	timeline = timeline .. "\nColors =\n"
	timeline = timeline .. "\n id:grayH value:gray(0.95)\n"
	timeline = timeline .. "\n id:grayV value:gray(0.85)\n"

	-- Colour from hex to 0-1 RGB
	local hex = args["color"]:gsub("#","")
	if #hex == 3 then
		hex = hex:sub(1,1) .. hex:sub(1,1) .. hex:sub(2,2) .. hex:sub(2,2) .. hex:sub(3,3) .. hex:sub(3,3) -- If it's 3 hex chars then make it six instead
	end
	rgbR = tonumber("0x"..hex:sub(1,2))/256
	rgbG = tonumber("0x"..hex:sub(3,4))/256
	rgbB = tonumber("0x"..hex:sub(5,6))/256
	timeline = timeline .. "\n id:graph value:rgb("..rgbR..", "..rgbG..", "..rgbB..")\n"
	
	-- Further the timeline 
	timeline = timeline .. "\nPeriod     = from:"..period_min.." till:"..period_max.."\n"
	timeline = timeline .. "\nScaleMajor = unit:year start:"..period_min.." increment:10\n"
	timeline = timeline .. "\nScaleMinor = unit:year start:"..period_min.." increment:2\n"

	-- X-axis position for each entry 
	timeline = timeline .. "\nBarData=\n"
	for episode = 1,episodes do 
		timeline = timeline .. "\n bar:"..episode.." text:"..episode.."\n"
	end
	
	-- Faint line at each 10% on the y-axis 
	timeline = timeline .. "\nLineData=\n"
	timeline = timeline .. "\n layer:front\n"
	for variance = period_min,period_max,10 do 
		timeline = timeline .. "\n at:"..variance.." frompos:"..old_x.." tillpos:"..(graph_width-g_r).." color:grayH width:1 layer:back\n"
	end
	
	-- Draw the lines between each entry 
	local all_positions = {}

	local last_x = nil
	local last_y = nil
	local current_x = g_l

	for episode = 1, episodes do
		local score = tonumber(args[episode])

		if score then
			local y = TVRT.round(g_b + (score-period_min) * percent_pixel)

			if last_x then
				timeline = timeline .. "\n points:("..last_x..","..last_y..")("..current_x..","..y..") color:graph width:1.5\n"
			end

			-- Draw square marker
			timeline = timeline .. "\n at:"..score.." frompos:"..(current_x-2).." tillpos:"..(current_x+2).." color:graph width:4\n"

			all_positions[#all_positions+1] = {x = current_x, y = y, episode = episode}

			last_x = current_x
			last_y = y
		elseif force_gap then
			last_x = nil
			last_y = nil
		end

		current_x = current_x + increment_x
	end

	-- Dud entry to allow positioning of final value
	all_positions[#all_positions+1] = {x = 1000, y = 1000, episode = nil}
	
	-- Draw the values on the graph
	local fontsize = 8
	local old_slope = 0
	local new_slope = old_slope
	
	timeline = timeline .. "\nTextData=\n"
	timeline = timeline .. "\n pos:("..(graph_width/2-20)..",15) fontsize:S text:Episode\n"
	timeline = timeline .. "\n pos:(20,"..(graph_height-15)..") fontsize:S text:Percent\n"
	for k,v in pairs(all_positions) do 
		-- Determine the position of the text based on the slope of each line 
		local nextv = all_positions[k+1]
		if nextv ~= nil then
			new_slope = (nextv.y-v.y) / (nextv.x-v.x)
			if new_slope > 0 then
				v.y = v.y - fontsize*1.5
			else
				v.y = v.y + fontsize*0.5
			end
			old_slope = new_slope
		end
		
		local label = v.episode and args[v.episode] or ""
		timeline = timeline .. "\n pos:("..v.x..","..v.y..") textcolor:black fontsize:"..fontsize.." text:"..label.."\n"
	end
	
	-- Add the timeline itself to the base table
	local timeline_base = frame:preprocess("<timeline>" .. timeline .. "</timeline>")
	rootcell:node(timeline_base)
	rootcell:tag('div'):css('clear','both')
	
	-- Create bottom caption
	local lower_caption_text1 = (args.label and TVRT.upper(string.lower(args.label)) or "<i>"..args.title.."</i>") .. " (" .. (args.year or "Year?") .. "):&nbsp;"
	local lower_caption_text2 = "Percentage of positive critics' reviews tracked by the website [[Rotten Tomatoes]]" .. (args.ref or "Ref?")
	
	local lower_caption1 = mw.html.create('span')
	local lower_caption2 = mw.html.create('span')
	
	lower_caption1
		:css('color', '#'..hex)
		:css('font-weight', 'bold')
		:wikitext(lower_caption_text1)
	lower_caption2
		:wikitext(lower_caption_text2)
	
	if not args.nolower then rootcell:node(lower_caption1) end
	rootcell:node(lower_caption2)

	return roottable
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p = {}

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		removeBlanks = false,
		wrappers = 'Template:Television Rotten Tomatoes scores'
	})
	return TVRT.new(frame,args)
end

return p