«ميدياويكي:Common.js»: الفرق بين المراجعتين
من Sudan Memory
سطر ١: | سطر ١: | ||
/* الجافاسكريبت الموضوع هنا سيتم تحميله لكل المستخدمين مع كل تحميل للصفحة. */ | /* الجافاسكريبت الموضوع هنا سيتم تحميله لكل المستخدمين مع كل تحميل للصفحة. */ | ||
− | + | // ============================================================================= | |
− | + | // Formate a poem | |
− | var | + | // 2024-02-27: Created by Abdalla G. M. Ahmed |
− | + | // ============================================================================= | |
− | + | ||
− | + | // ----------------------------------------------------------------------------- | |
− | + | // Measure displayed width of an HTML text | |
− | + | // ----------------------------------------------------------------------------- | |
− | + | ||
− | return | + | function measureTextWidth(text, font) { |
+ | var tmpSpan = document.createElement("span"); | ||
+ | tmpSpan.style.font = font; // Apply the font style | ||
+ | tmpSpan.style.position = "absolute"; | ||
+ | tmpSpan.style.visibility = "hidden"; | ||
+ | tmpSpan.style.whiteSpace = "nowrap"; | ||
+ | tmpSpan.innerHTML = text; | ||
+ | |||
+ | document.body.appendChild(tmpSpan); | ||
+ | var textWidth = tmpSpan.offsetWidth; | ||
+ | document.body.removeChild(tmpSpan); | ||
+ | |||
+ | return textWidth; | ||
} | } | ||
− | function | + | // ----------------------------------------------------------------------------- |
− | var | + | // Retrieve font specs of an element |
− | var | + | // ----------------------------------------------------------------------------- |
− | + | ||
− | + | function getFontStyle(node) { | |
− | + | var computedStyle = window.getComputedStyle(node); | |
− | + | var fontSize = computedStyle.fontSize; | |
− | + | var fontFamily = computedStyle.fontFamily; | |
− | + | var fontWeight = computedStyle.fontWeight; | |
− | + | var fontStyle = computedStyle.fontStyle; | |
− | + | var fontVariant = computedStyle.fontVariant; | |
− | + | return ( | |
− | + | fontStyle + " " + fontVariant + " " + | |
− | + | fontWeight + " " + fontSize + " " + fontFamily | |
− | + | ); | |
− | + | } | |
− | + | ||
+ | // ----------------------------------------------------------------------------- | ||
+ | // Trim empty leading and trailing entries of an array | ||
+ | // ----------------------------------------------------------------------------- | ||
+ | |||
+ | function arrayTrim(arr) { | ||
+ | var result = arr.slice(); // Clone the array to avoid modifying the original array | ||
+ | while (result.length > 0 && !result[0]) { result.shift(); } // Remove empty elements from the beginning | ||
+ | while (result.length > 0 && !result[result.length - 1]) { result.pop(); } // Remove empty elements from the end | ||
+ | return result; | ||
+ | } | ||
+ | |||
+ | // ----------------------------------------------------------------------------- | ||
+ | // Format contents of a container (e.g., div) as a poem | ||
+ | // ----------------------------------------------------------------------------- | ||
+ | |||
+ | function poemFormat(poem) { | ||
+ | var bWidth = parseFloat(poem.getAttribute('data-bWidth') || 0); // Overall with of beit | ||
+ | var bGap = parseFloat(poem.getAttribute('data-bGap') || 50); | ||
+ | var pWidth = [0, 0, 0, 0, 0]; // Widths of part for different number of parts, index 0 is used for centered verses | ||
+ | var nGaps = [2, 0, 1, 2, 3]; // Number of gaps for different number of parts, initially assuming no leading gaps | ||
+ | var fontStyle = getFontStyle(poem); | ||
+ | var lines = ( | ||
+ | poem | ||
+ | .innerHTML // We get the HTML to retain some tags like <a,i,b> | ||
+ | .replace(/\s*(<p[^>]*>|<div[^>]*>|<pre[^>]*>)/gi, '') // Remove whitespace before opening container tags and the tags | ||
+ | .replace(/<\/p>|<\/div>|<\/pre>/gi, '') // Remove closing tags | ||
+ | .split('\n') // Break into lines, assuming one beit per line | ||
+ | ); | ||
+ | lines = arrayTrim(lines); // Remove leading and trailing empty lines so that the enclosing <div> tag may be placed in separate lines | ||
+ | var formattedLines = []; | ||
+ | var tagPlaceholder = '<a><i></i></a>'; // Unlikely to be present and does not affect text size | ||
+ | var tags = []; // To store extracted tags | ||
+ | for (var i = 0; i < lines.length; i++) { | ||
+ | var line = lines[i]; | ||
+ | var leadingSpace = ( | ||
+ | /^ {2,}/.test(line) ? // Starts with two or more spaces? | ||
+ | '<span class="gap" style="width:'+bGap+'px"> </span>' : // Will be placed at the being | ||
+ | '' // Default to none | ||
+ | ); | ||
+ | line = line.trim(); // Remove leading and trailing spaces | ||
+ | if (line === "") { // Empty line | ||
+ | formattedLines.push('<br>'); // is replaced by a line break | ||
+ | continue; // Done with this line | ||
} | } | ||
+ | if (/^!/.test(line)) { // A line starting with an exclamation mark is a comment | ||
+ | formattedLines.push( | ||
+ | '<p class="bComment" style="width:%cWidth%px">' // "cWidth" is a placeholder until line widths are computed | ||
+ | + line.substring(1) + // Remove the exclamation mark | ||
+ | '</p>' | ||
+ | ); | ||
+ | continue; // Done with this line | ||
+ | } | ||
+ | if (line.startsWith('.') && line.endsWith('.')) { // Lines delimited by dots are centered. | ||
+ | line = line.substring(1, line.length - 1).trim(); // Remove delimiters and any white space | ||
+ | pWidth[0] = Math.max(pWidth[0], measureTextWidth(line)); | ||
+ | formattedLines.push( | ||
+ | '<p class="bMid" style="width:%bWidth%px">' + line + '</p>' | ||
+ | ); | ||
+ | continue; // Done with this line | ||
+ | } | ||
+ | // --------------------------------------------------------------------- | ||
+ | // From here we assume a regular classic column poem | ||
+ | // --------------------------------------------------------------------- | ||
+ | // --------------------------------------------------------------------- | ||
+ | // Distribute tags over words so that they can be split into parts, | ||
+ | // then save tags and replace them with no-space placeholders | ||
+ | // --------------------------------------------------------------------- | ||
+ | line = line.replace( | ||
+ | /(<([abi]+)(?:\s[^>]*)?>)(.*?)<\/\2>/gi, // Match the <a/i/b> tags and capture parameters into positional variables | ||
+ | function(match, openingTag, tagName, content) { // Compute replacements based on captured parameters | ||
+ | return content // Content inside tag | ||
+ | .split(/(\s+)/) // Split at white space and include white spaces in the list | ||
+ | .map(function(item) { | ||
+ | if (/\S/.test(item)) { // If an item is non-white space | ||
+ | tags.push(openingTag, '</' + tagName + '>'); // Save opening and closing tags | ||
+ | item = tagPlaceholder + item + tagPlaceholder; // Enclose text item in a dummy tag placeholder | ||
+ | } | ||
+ | return item; // Whitespace is returned as is | ||
+ | }).join(''); // Join all items back together | ||
+ | } | ||
+ | ); | ||
+ | // --------------------------------------------------------- | ||
+ | // Extract trailing symbols to align at "قافية" | ||
+ | // --------------------------------------------------------- | ||
+ | var trailingSymbols = ''; // Default to none | ||
+ | line = line.replace( | ||
+ | /^(.*?[\u0620-\u064Aa-zA-Z])([^\u0620-\u064Aa-zA-Z]*)$/, // Match trailing non-letter symbols and capture parameters | ||
+ | function(match, alphabeticPart, nonAlphabeticPart) { // A function to handle parameters | ||
+ | if (match.endsWith(tagPlaceholder)) return match; // Too complex to handle, may consider later | ||
+ | trailingSymbols = ( // Extract trailing symbols, if any | ||
+ | '<span class="bTrailingSymbols">' // Enclose in a span element to enable separate formatting, if desirable | ||
+ | + nonAlphabeticPart + | ||
+ | '</span>' | ||
+ | ); | ||
+ | return alphabeticPart; // Return alphabetic part to replace the original line | ||
+ | } | ||
+ | ); | ||
+ | // --------------------------------------------------------------------- | ||
+ | // Split parts | ||
+ | // --------------------------------------------------------------------- | ||
+ | var nParts = 0; // Number of parts | ||
+ | var partWidth = 0; // Maximum part length | ||
+ | line = ( | ||
+ | line | ||
+ | .split(/(\s{2,})/) // Split at two or more spaces and include white spaces in the list | ||
+ | .map(function(item) { | ||
+ | if (/\S/.test(item)) { // An item containing non-space contents is treated as a part | ||
+ | nParts++; // Increment number of parts to count this one | ||
+ | // --------------------------------------------------------- | ||
+ | // Enclose words in spans to enable justification | ||
+ | // --------------------------------------------------------- | ||
+ | item = ( | ||
+ | item | ||
+ | .split(/(\s+)/) // Split at white space and include white spaces in the list | ||
+ | .map(function(item) { | ||
+ | if ( | ||
+ | /\S/.test(item) | ||
+ | && !item.startsWith(tagPlaceholder) | ||
+ | ) { // If an item is non-white space and not already in a tag | ||
+ | return '<span>' + item + '</span>'; // Enclose in a span to enable justification | ||
+ | } | ||
+ | return item; // Return whitespace and tagged items as is | ||
+ | }) | ||
+ | .join('') // Join all items back together | ||
+ | ); | ||
+ | // --------------------------------------------------------- | ||
+ | partWidth = Math.max( | ||
+ | partWidth, measureTextWidth(item, fontStyle) // Update longest part length | ||
+ | ); | ||
+ | return ( | ||
+ | '<span class="vJustified" style="width:%pWidth%px">' | ||
+ | + item + | ||
+ | '</span>' | ||
+ | ); | ||
+ | } | ||
+ | else { // White space separating parts | ||
+ | return '<span class="gap"> </span>'; // Enclose in a span, typically stylized to preserve spaces | ||
+ | } | ||
+ | }) | ||
+ | .join('') // Join all items back together | ||
+ | .replace(/%pWidth%/g, '%pWidth' + nParts + '%') // The number of parts is known now | ||
+ | ); | ||
+ | pWidth[nParts] = Math.max(pWidth[nParts], partWidth); | ||
+ | formattedLines.push( | ||
+ | '<p class="beit">' | ||
+ | + leadingSpace | ||
+ | + '<span class="vJustified" style="width:%bWidth%px">' | ||
+ | + line | ||
+ | + '</span>' | ||
+ | + trailingSymbols | ||
+ | + '</p>' | ||
+ | ); | ||
+ | } | ||
+ | for (var i = 0; i < pWidth.length; i++) { | ||
+ | var bWidth_i = pWidth[i] ? pWidth[i] * i + nGaps[i] * bGap : 0; // Width to accommodate this number/width of parts | ||
+ | bWidth = Math.max(bWidth, bWidth_i); // Accommodate the widest | ||
+ | } | ||
+ | pWidth = pWidth.map(function(w, i) { | ||
+ | return (bWidth - nGaps[i] * bGap) / (i ? i : 1); | ||
}); | }); | ||
− | return formattedLines.join(''); | + | var tagIndex = 0; // To track the current tag being replaced |
+ | return ( | ||
+ | formattedLines | ||
+ | .join('') // Concatenate | ||
+ | .replace(new RegExp(tagPlaceholder, "g"), function() { // Restore saved tags | ||
+ | return tags[tagIndex++]; | ||
+ | }) | ||
+ | .replace(/%pWidth(\d+)%/g, function(match, i) { // Replace computed part widths | ||
+ | return pWidth[i].toFixed(1); | ||
+ | }) | ||
+ | .replace(/%bWidth%/g, bWidth) // Replace computed overall beit width | ||
+ | .replace(/%cWidth%/g, (0.9 * bWidth).toFixed(1)) // Replace width of comments | ||
+ | ); | ||
} | } | ||
$(function () { | $(function () { | ||
− | + | document.querySelectorAll('.abyat').forEach(function(poem) { | |
− | + | poem.innerHTML = poemFormat(poem); | |
− | + | ||
− | poem.innerHTML = | + | |
− | + | ||
}); | }); | ||
+ | |||
+ | document.querySelectorAll('.abyat').forEach(function(poem) { | ||
+ | poemFormat(poem); | ||
+ | }); | ||
+ | |||
var toggler = document.getElementsByClassName("treeBranch"); | var toggler = document.getElementsByClassName("treeBranch"); |
مراجعة ١٢:٢٤، ٢٧ فبراير ٢٠٢٤
/* الجافاسكريبت الموضوع هنا سيتم تحميله لكل المستخدمين مع كل تحميل للصفحة. */ // ============================================================================= // Formate a poem // 2024-02-27: Created by Abdalla G. M. Ahmed // ============================================================================= // ----------------------------------------------------------------------------- // Measure displayed width of an HTML text // ----------------------------------------------------------------------------- function measureTextWidth(text, font) { var tmpSpan = document.createElement("span"); tmpSpan.style.font = font; // Apply the font style tmpSpan.style.position = "absolute"; tmpSpan.style.visibility = "hidden"; tmpSpan.style.whiteSpace = "nowrap"; tmpSpan.innerHTML = text; document.body.appendChild(tmpSpan); var textWidth = tmpSpan.offsetWidth; document.body.removeChild(tmpSpan); return textWidth; } // ----------------------------------------------------------------------------- // Retrieve font specs of an element // ----------------------------------------------------------------------------- function getFontStyle(node) { var computedStyle = window.getComputedStyle(node); var fontSize = computedStyle.fontSize; var fontFamily = computedStyle.fontFamily; var fontWeight = computedStyle.fontWeight; var fontStyle = computedStyle.fontStyle; var fontVariant = computedStyle.fontVariant; return ( fontStyle + " " + fontVariant + " " + fontWeight + " " + fontSize + " " + fontFamily ); } // ----------------------------------------------------------------------------- // Trim empty leading and trailing entries of an array // ----------------------------------------------------------------------------- function arrayTrim(arr) { var result = arr.slice(); // Clone the array to avoid modifying the original array while (result.length > 0 && !result[0]) { result.shift(); } // Remove empty elements from the beginning while (result.length > 0 && !result[result.length - 1]) { result.pop(); } // Remove empty elements from the end return result; } // ----------------------------------------------------------------------------- // Format contents of a container (e.g., div) as a poem // ----------------------------------------------------------------------------- function poemFormat(poem) { var bWidth = parseFloat(poem.getAttribute('data-bWidth') || 0); // Overall with of beit var bGap = parseFloat(poem.getAttribute('data-bGap') || 50); var pWidth = [0, 0, 0, 0, 0]; // Widths of part for different number of parts, index 0 is used for centered verses var nGaps = [2, 0, 1, 2, 3]; // Number of gaps for different number of parts, initially assuming no leading gaps var fontStyle = getFontStyle(poem); var lines = ( poem .innerHTML // We get the HTML to retain some tags like <a,i,b> .replace(/\s*(<p[^>]*>|<div[^>]*>|<pre[^>]*>)/gi, '') // Remove whitespace before opening container tags and the tags .replace(/<\/p>|<\/div>|<\/pre>/gi, '') // Remove closing tags .split('\n') // Break into lines, assuming one beit per line ); lines = arrayTrim(lines); // Remove leading and trailing empty lines so that the enclosing <div> tag may be placed in separate lines var formattedLines = []; var tagPlaceholder = '<a><i></i></a>'; // Unlikely to be present and does not affect text size var tags = []; // To store extracted tags for (var i = 0; i < lines.length; i++) { var line = lines[i]; var leadingSpace = ( /^ {2,}/.test(line) ? // Starts with two or more spaces? '<span class="gap" style="width:'+bGap+'px"> </span>' : // Will be placed at the being '' // Default to none ); line = line.trim(); // Remove leading and trailing spaces if (line === "") { // Empty line formattedLines.push('<br>'); // is replaced by a line break continue; // Done with this line } if (/^!/.test(line)) { // A line starting with an exclamation mark is a comment formattedLines.push( '<p class="bComment" style="width:%cWidth%px">' // "cWidth" is a placeholder until line widths are computed + line.substring(1) + // Remove the exclamation mark '</p>' ); continue; // Done with this line } if (line.startsWith('.') && line.endsWith('.')) { // Lines delimited by dots are centered. line = line.substring(1, line.length - 1).trim(); // Remove delimiters and any white space pWidth[0] = Math.max(pWidth[0], measureTextWidth(line)); formattedLines.push( '<p class="bMid" style="width:%bWidth%px">' + line + '</p>' ); continue; // Done with this line } // --------------------------------------------------------------------- // From here we assume a regular classic column poem // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Distribute tags over words so that they can be split into parts, // then save tags and replace them with no-space placeholders // --------------------------------------------------------------------- line = line.replace( /(<([abi]+)(?:\s[^>]*)?>)(.*?)<\/\2>/gi, // Match the <a/i/b> tags and capture parameters into positional variables function(match, openingTag, tagName, content) { // Compute replacements based on captured parameters return content // Content inside tag .split(/(\s+)/) // Split at white space and include white spaces in the list .map(function(item) { if (/\S/.test(item)) { // If an item is non-white space tags.push(openingTag, '</' + tagName + '>'); // Save opening and closing tags item = tagPlaceholder + item + tagPlaceholder; // Enclose text item in a dummy tag placeholder } return item; // Whitespace is returned as is }).join(''); // Join all items back together } ); // --------------------------------------------------------- // Extract trailing symbols to align at "قافية" // --------------------------------------------------------- var trailingSymbols = ''; // Default to none line = line.replace( /^(.*?[\u0620-\u064Aa-zA-Z])([^\u0620-\u064Aa-zA-Z]*)$/, // Match trailing non-letter symbols and capture parameters function(match, alphabeticPart, nonAlphabeticPart) { // A function to handle parameters if (match.endsWith(tagPlaceholder)) return match; // Too complex to handle, may consider later trailingSymbols = ( // Extract trailing symbols, if any '<span class="bTrailingSymbols">' // Enclose in a span element to enable separate formatting, if desirable + nonAlphabeticPart + '</span>' ); return alphabeticPart; // Return alphabetic part to replace the original line } ); // --------------------------------------------------------------------- // Split parts // --------------------------------------------------------------------- var nParts = 0; // Number of parts var partWidth = 0; // Maximum part length line = ( line .split(/(\s{2,})/) // Split at two or more spaces and include white spaces in the list .map(function(item) { if (/\S/.test(item)) { // An item containing non-space contents is treated as a part nParts++; // Increment number of parts to count this one // --------------------------------------------------------- // Enclose words in spans to enable justification // --------------------------------------------------------- item = ( item .split(/(\s+)/) // Split at white space and include white spaces in the list .map(function(item) { if ( /\S/.test(item) && !item.startsWith(tagPlaceholder) ) { // If an item is non-white space and not already in a tag return '<span>' + item + '</span>'; // Enclose in a span to enable justification } return item; // Return whitespace and tagged items as is }) .join('') // Join all items back together ); // --------------------------------------------------------- partWidth = Math.max( partWidth, measureTextWidth(item, fontStyle) // Update longest part length ); return ( '<span class="vJustified" style="width:%pWidth%px">' + item + '</span>' ); } else { // White space separating parts return '<span class="gap"> </span>'; // Enclose in a span, typically stylized to preserve spaces } }) .join('') // Join all items back together .replace(/%pWidth%/g, '%pWidth' + nParts + '%') // The number of parts is known now ); pWidth[nParts] = Math.max(pWidth[nParts], partWidth); formattedLines.push( '<p class="beit">' + leadingSpace + '<span class="vJustified" style="width:%bWidth%px">' + line + '</span>' + trailingSymbols + '</p>' ); } for (var i = 0; i < pWidth.length; i++) { var bWidth_i = pWidth[i] ? pWidth[i] * i + nGaps[i] * bGap : 0; // Width to accommodate this number/width of parts bWidth = Math.max(bWidth, bWidth_i); // Accommodate the widest } pWidth = pWidth.map(function(w, i) { return (bWidth - nGaps[i] * bGap) / (i ? i : 1); }); var tagIndex = 0; // To track the current tag being replaced return ( formattedLines .join('') // Concatenate .replace(new RegExp(tagPlaceholder, "g"), function() { // Restore saved tags return tags[tagIndex++]; }) .replace(/%pWidth(\d+)%/g, function(match, i) { // Replace computed part widths return pWidth[i].toFixed(1); }) .replace(/%bWidth%/g, bWidth) // Replace computed overall beit width .replace(/%cWidth%/g, (0.9 * bWidth).toFixed(1)) // Replace width of comments ); } $(function () { document.querySelectorAll('.abyat').forEach(function(poem) { poem.innerHTML = poemFormat(poem); }); document.querySelectorAll('.abyat').forEach(function(poem) { poemFormat(poem); }); var toggler = document.getElementsByClassName("treeBranch"); var i; for (i = 0; i < toggler.length; i++) { toggler[i].addEventListener("click", function() { this.parentElement.querySelector(".treeBranch-nested").classList.toggle("treeBranch-active"); this.classList.toggle("treeBranch-down"); }); } }());