Kontent qismiga oʻtish

Andoza:whatlinkshere

Vikilug‘atdan olingan

local export = {}


-- Constants local ADD_PADDING_HOR = 10.0 -- Minimum padding for the annotated main image, horizontal and vertical. local ADD_PADDING_VER = 10.0

local LDR_LINE_PATTERN = '

'

local IMG_SRC_SPAN = 'Image source.'

local DIST_BTWN_LEADER_AND_TEXT = 3 -- In pixels. local FONT_SIZE_SEC_MARGIN = 8 -- In pixels.


-- Module imports local m_parameters = require("Module:parameters")


-- Parses widths specified in pixels. "300.0px" -> 300 or nil iff invalid format. local function parse_width(width_string) local result = nil local match = mw.ustring.match(mw.ustring.lower(width_string), "^ *([%d\\.]+) *px *$") if match then result = math.abs(tonumber(match)) end

return result end


-- Utility function to dump object into a code block to inspect. local function debug_dump(object) return "" .. mw.getCurrentFrame():extensionTag('nowiki', mw.dumpObject(object)) .. "" end


-- Parses annotation specs and returns a table containing them. local function parse_annotations_specs(annots) local result = {}

local parts = mw.text.split(annots, ",", true) for i, cur_annot_spec in ipairs(parts) do cur_annot_spec = mw.text.trim(cur_annot_spec) if mw.ustring.len(cur_annot_spec) > 0 and mw.ustring.sub(cur_annot_spec, 1, 1) ~= '#' then local annot_parts = {mw.ustring.match(cur_annot_spec, "^ *(sta) *from *([%d%.-]+) *([%d%.-]+) *to *([%d%.-]+) *([%d%.-]+) *(.+) *$")} assert(annot_parts ~= nil, "Invalid annotation specification: " .. cur_annot_spec)

local cur_annot = {["type"] = annot_parts[1], ["ldr_start_x"] = tonumber(annot_parts[2]), ["ldr_start_y"] = tonumber(annot_parts[3]), ["ldr_end_x"] = tonumber(annot_parts[4]), ["ldr_end_y"] = tonumber(annot_parts[5]), ["label"] = mw.text.trim(annot_parts[6]), }

cur_annot["ldr_rises"] = cur_annot["ldr_end_y"] <= cur_annot["ldr_start_y"] cur_annot["ldr_goesright"] = cur_annot["ldr_end_x"] >= cur_annot["ldr_start_x"] guess_label_position(cur_annot)

table.insert(result, cur_annot) end end

return result end


-- Converts bare links like label to something like label local function augment_wikilinks(annots, lang_fragment) for _, cur_annot in ipairs(annots) do cur_annot["label"] = mw.ustring.gsub(cur_annot["label"], "%[%[([^|%]]*)%]%]", "%1") end end


local function escape_regex_literal(word)

   local result = mw.ustring.gsub(word, "([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1")
   return result

end


-- Attempts to find the current term (page title) in the labels so as to tag them -- for focus later in processing. Can highlight multiple labels. local function hilite_current_term(annots, current_term) for _, cur_annot in ipairs(annots) do cur_annot["focus"] = false -- by default

-- Find the start and end position of the term in the text local start_pos, end_pos = mw.ustring.find(cur_annot["label"], current_term)

if start_pos and end_pos then -- Get the characters immediately before the start and immediately after the end local char_before = start_pos > 1 and mw.ustring.sub(cur_annot['label'], start_pos - 1, start_pos - 1) or ' ' local char_after = end_pos < mw.ustring.len(cur_annot['label']) and mw.ustring.sub(cur_annot['label'], end_pos + 1, end_pos + 1) or ' '

-- Check if these characters are whitespace characters using a regular expression if (mw.ustring.match(char_before, '[%s%-%[%]<>#%|,%.]') and mw.ustring.match(char_after, '[%s%-%[%]<>#%|,%.]')) then cur_annot["focus"] = true end end end end


-- Computes the paddings for the div containing the image. local function compute_img_div_paddings(annots, img_width, img_height) local min_x = 1E9 local min_y = 1E9 local max_x = -1E9 local max_y = -1E9

for _, cur_annot in ipairs(annots) do min_x = math.min(min_x, cur_annot["ldr_start_x"], cur_annot["ldr_end_x"], cur_annot["lbl_start_x"], cur_annot["lbl_end_x"]) min_y = math.min(min_y, cur_annot["ldr_start_y"], cur_annot["ldr_end_y"], cur_annot["lbl_start_y"], cur_annot["lbl_end_y"]) max_x = math.max(max_x, cur_annot["ldr_start_x"], cur_annot["ldr_end_x"], cur_annot["lbl_start_x"], cur_annot["lbl_end_x"]) max_y = math.max(max_y, cur_annot["ldr_start_y"], cur_annot["ldr_end_y"], cur_annot["lbl_start_y"], cur_annot["lbl_end_y"]) end

local max_hor_offset = math.max(-min_x, max_x - img_width, 0) + ADD_PADDING_HOR local max_ver_offset = math.max(-min_y, max_y - img_height, 0) + ADD_PADDING_VER

local result = {["left"] = max_hor_offset, ["right"] = max_hor_offset, ["top"] = max_ver_offset, ["bottom"] = max_ver_offset}

return result end


-- Gets the image height given its width, keeping aspect ratio. Expensive. local function get_image_height(image_filename, image_width) local result = nil

local title = mw.title.new("File:" .. image_filename) local file = title.file assert(file.exists, "Image does not exist: " .. image_filename) local width = file.width local height = file.height result = (image_width / width) * height

return result end


-- A simple mapping function returning a new table. function fn_map(tbl, func)

   local newtbl = {}
   for i, v in ipairs(tbl) do
       newtbl[i] = func(v)
   end
   return newtbl

end


-- Computes the position attributes of the label for a given annotation, -- when the position are not specified. Heuristic. Positions relative to img. function guess_label_position(annot) local delta_x = 0 local delta_y = 0 local lbl_alignment = nil

delta_x = annot["ldr_goesright"] and DIST_BTWN_LEADER_AND_TEXT or -DIST_BTWN_LEADER_AND_TEXT delta_y = annot["ldr_rises"] and -DIST_BTWN_LEADER_AND_TEXT or DIST_BTWN_LEADER_AND_TEXT lbl_alignment = annot["ldr_goesright"] and "left" or "right"

annot["lbl_start_x"] = annot["ldr_end_x"] + delta_x annot["lbl_start_y"] = annot["ldr_end_y"] + delta_y

-- TODO: Find a way to get the size of the font. This is hacky at best. -- 30 characters are 171 pixel wide and 12px high for the specified font and size and line-height. local annot_label_lines = extract_label_lines(annot["label"]) local line_chars = fn_map(annot_label_lines, function(txt) return mw.ustring.len(txt) end) local annot_nb_chars = math.max(unpack(line_chars))

annot["lbl_width"] = (171.0 / 30.0) * annot_nb_chars annot["lbl_height"] = 12.0 * #line_chars + (#line_chars - 1) * 6 -- line-height: 12px, + fudge

-- A small rectification in case the leader line is almost vertical or horizontal local is_almost_vertical = math.abs(annot["ldr_start_x"] - annot["ldr_end_x"]) < 6 local is_almost_horizontal = math.abs(annot["ldr_start_y"] - annot["ldr_end_y"]) < 10

if is_almost_vertical then lbl_alignment = "center" annot["lbl_start_x"] = annot["lbl_start_x"] + (annot["ldr_goesright"] and -1 or 1) * annot["lbl_width"] / 2.0 end

if is_almost_horizontal then annot["lbl_start_y"] = annot["ldr_end_y"] - annot["lbl_height"] / 2.0 end

-- Finalize end positions annot["lbl_end_x"] = annot["lbl_start_x"] + (annot["ldr_goesright"] and 1 or -1) * (annot["lbl_width"] + FONT_SIZE_SEC_MARGIN) annot["lbl_end_y"] = annot["lbl_start_y"] + ((annot["ldr_rises"] and not is_almost_horizontal) and -annot["lbl_height"] or annot["lbl_height"]) annot["lbl_alignment"] = lbl_alignment end


-- Computes the HTML attributes to position annotations relative to their -- container div, and not the image they annotate anymore. local function compute_position_attributes(annotations, pad_left, pad_right, pad_top, image_width) for i, cur_annot in ipairs(annotations) do local delta_x = cur_annot['ldr_end_x'] - cur_annot['ldr_start_x'] local delta_y = cur_annot['ldr_end_y'] - cur_annot['ldr_start_y']

-- leader lines cur_annot['ldr_length'] = math.sqrt(delta_x ^ 2 + delta_y ^ 2) cur_annot['ldr_div_top'] = cur_annot['ldr_start_y'] + delta_y/2.0 + pad_top cur_annot['ldr_div_left'] = cur_annot['ldr_start_x'] + delta_x/2.0 - cur_annot['ldr_length']/2.0 + pad_left cur_annot['ldr_div_rotation'] = math.deg(math.atan(delta_y / delta_x))

if not cur_annot["ldr_goesright"] then cur_annot['ldr_div_rotation'] = cur_annot['ldr_div_rotation'] + 180.0 end

-- labels, either positioned from the left or the right cur_annot["lbl_div_top"] = pad_top + math.min(cur_annot["lbl_start_y"], cur_annot["lbl_end_y"]) if cur_annot["ldr_goesright"] then cur_annot["lbl_div_left"] = math.min(cur_annot["lbl_start_x"], cur_annot["lbl_end_x"]) + pad_left cur_annot["lbl_div_right"] = nil else cur_annot["lbl_div_left"] = nil cur_annot["lbl_div_right"] = pad_right + image_width - math.max(cur_annot["lbl_start_x"], cur_annot["lbl_end_x"]) end end end


-- Returns either a pixel value or auto if the position is nil. local function render_nilable_pos(position) return position and mw.ustring.format('%.1fpx', position) or 'auto' end


-- Main loop to render each annotation, without containing divs. local function render_annotations(annotations) local result = ""

for i, cur_annot in ipairs(annotations) do result = result .. tostring(mw.html.create("div") :addClass("label") :addClass("hiliter") :addClass(cur_annot["focus"] and "focus" or "") :css("left", render_nilable_pos(cur_annot['lbl_div_left'])) :css("right", render_nilable_pos(cur_annot['lbl_div_right'])) :css("top", render_nilable_pos(cur_annot['lbl_div_top'])) :css("text-align", cur_annot["lbl_alignment"]) :wikitext(cur_annot["label"])) .. '\n' .. mw.ustring.format(LDR_LINE_PATTERN, cur_annot['ldr_div_rotation'], cur_annot['ldr_div_left'], cur_annot['ldr_div_top'], cur_annot['ldr_length']) .. '\n' end

return result end


-- Extracts the label part of a wikitext link. -- TODO: Do this using the API, but expandTemplate and preprocess don't work. -- See export.remove_links https://en.wiktionary.org/wiki/Module:links -- In the meantime, this is a heuristic. -- Returns a list of rendered text strings, where the strings are split according -- to the
tag in the original label. function extract_label_lines(wikitext_link) local rendered_result = wikitext_link

-- normalize rendered_result = mw.ustring.gsub(rendered_result, "\n", " ") rendered_result = mw.text.trim(mw.ustring.gsub(rendered_result, " +", " "))

-- remove wikilinks rendered_result = mw.ustring.gsub(rendered_result, "%[%[([^|]*)%]%]", "%1") rendered_result = mw.ustring.gsub(rendered_result, "%[%]*|([^%+)%]%]", "%1")

-- protect new lines rendered_result = mw.ustring.gsub(rendered_result, "
", "\n")

-- remove tags, like span for culture rendered_result = mw.ustring.gsub(rendered_result, "<[^>]+>", "")

-- split at br local result = mw.text.split(rendered_result, "\n")

return result end


-- Display an image with simple text annotations. function export.labeled_image(frame) -- read arguments local params_specs = { ["image"] = {required = true}, ["caption"] = {required = false, default = "Annotated image."}, ["width"] = {required = true}, ["annotations"] = {required = false}, ["colorscheme"] = {required = false, default = "cornflowerblue"}, }

local args = m_parameters.process(frame.args, params_specs, false)

local image_filename = args["image"] local image_width_string = args["width"] local annotations_specs = args['annotations'] or "" local caption_text = args['caption'] local colorscheme = mw.ustring.lower(args['colorscheme'])

local image_width = parse_width(image_width_string) local image_height = get_image_height(image_filename, image_width)

-- retrieve some configuration settings local lang_fragment = mw.language.fetchLanguageName(mw.language.getContentLanguage():getCode()) local current_term = mw.title.getCurrentTitle().text

-- parse and compute annotation elements local annotations = parse_annotations_specs(annotations_specs) augment_wikilinks(annotations, lang_fragment) hilite_current_term(annotations, current_term) local img_div_paddings = compute_img_div_paddings(annotations, image_width, image_height) compute_position_attributes(annotations, img_div_paddings['left'], img_div_paddings['right'], img_div_paddings['top'], image_width)

-- rendering in HTML local inner_width = img_div_paddings['left'] + img_div_paddings['right'] + image_width + 1.0 * 2 -- with border local outer_width = inner_width + 3.0 * 2 + 1 * 2

local result = frame:extensionTag('templatestyles', , { src = "Module:visual-dict/styles.css" }) ..

mw.ustring.format('

', colorscheme, outer_width) .. mw.ustring.format( '
', inner_width) .. mw.ustring.format( '
', img_div_paddings['top'], img_div_paddings['right'], img_div_paddings['bottom'], img_div_paddings['left'] ) ..

render_annotations(annotations) ..

mw.ustring.format( "
Fayl:%s
", image_filename, image_width) .. '
' .. '
' .. mw.ustring.format( '
Image source.
', image_filename) .. '

' .. caption_text .. '

' .. '
' .. '
' .. '

'

   return result

end

return export