|
|
(٣٢ مراجعات متوسطة بواسطة نفس المستخدم غير معروضة) |
سطر ١: |
سطر ١: |
| /* الجافاسكريبت الموضوع هنا سيتم تحميله لكل المستخدمين مع كل تحميل للصفحة. */ | | /* الجافاسكريبت الموضوع هنا سيتم تحميله لكل المستخدمين مع كل تحميل للصفحة. */ |
| | | |
− | // =============================================================================
| + | console.log("Entering Common.js"); |
− | // Formate a poem
| + | |
− | // 2024-02-27: Created by Abdalla G. M. Ahmed
| + | |
− | // =============================================================================
| + | |
| | | |
− | // -----------------------------------------------------------------------------
| + | $(function () { |
− | // Measure displayed width of an HTML text
| + | if (document.querySelectorAll('.abyat').length > 0) { |
− | // -----------------------------------------------------------------------------
| + | var script = document.createElement('script'); |
− | | + | script.src = "https://abdallagafar.github.io/poemFormat/poemFormat.js"; |
− | function measureTextWidth(text, font) { | + | script.addEventListener('load', function() { // Now the external script is loaded and its functions are ready to be used |
− | var tmpSpan = document.createElement("span"); | + | document.querySelectorAll('.abyat').forEach(function(poem) { // Search for poems |
− | tmpSpan.style.font = font; // Apply the font style
| + | poem.innerHTML = poemFormat(poem); |
− | tmpSpan.style.position = "absolute";
| + | }); |
− | tmpSpan.style.visibility = "hidden";
| + | console.log('Processed ' + poemFormatGrossCounter + ' beit.'); |
− | tmpSpan.style.whiteSpace = "nowrap";
| + | }); |
− | tmpSpan.innerHTML = text;
| + | document.head.appendChild(script); // Append the script element to the document's head to start loading the script |
− | | + | |
− | 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
| + | |
− | .replace(/\s*<hr[^>]*>\s*/gi, '\n----\n')
| + | |
− | .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="bGap" style="width:'+bGap+'px"> </span>' : // Will be placed at the being
| + | |
− | '' // Default to none
| + | |
− | );
| + | |
− | line = line.trim(); // Remove leading and trailing spaces | + | |
− | if (line === "" || /<br>/.test(line)) { // Empty line
| + | |
− | formattedLines.push('<br>'); // is replaced by a line break
| + | |
− | continue; // Done with this line
| + | |
− | }
| + | |
− | if (line === "----") { // Separator line
| + | |
− | formattedLines.push('<hr style="width:%bWidth%px">');
| + | |
− | 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
| + | |
− | }
| + | |
− | var closingHalf = false;
| + | |
− | /*if (line.startsWith('..') && line.endsWith('..')) { // Lines delimited by two dots are treated as closing halves.
| + | |
− | line = line.substring(2, line.length - 2).trim(); // Remove delimiters and any white space
| + | |
− | line = '* ' + line.replace(/(\s+)/g, ' '); // Insert a minimal-width dummy half, and remove gaps, if any, to process as two halves
| + | |
− | closingHalf = true;
| + | |
− | }*/
| + | |
− | 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
| + | |
− | var parts = (
| + | |
− | 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="bJustified" style="width:%pWidth%px">'
| + | |
− | + item +
| + | |
− | '</span>'
| + | |
− | );
| + | |
− | }
| + | |
− | else { // White space separating parts
| + | |
− | return '<span class="bGap"> </span>'; // Enclose in a span, typically stylized to preserve spaces
| + | |
− | }
| + | |
− | })
| + | |
− | ); | + | |
− | //if (closingHalf) { parts.shift(); } // Dispense with dummy
| + | |
− | line = parts.join('').replace(/%pWidth%/g, '%pWidth' + nParts + '%'); // The number of parts is known now
| + | |
− | pWidth[nParts] = Math.max(pWidth[nParts], partWidth); | + | |
− | /*if (closingHalf) {
| + | |
− | formattedLines.push(
| + | |
− | '<p class="bMid" style="width:%bWidth%px">'
| + | |
− | + line +
| + | |
− | '</p>'
| + | |
− | );
| + | |
− | }*/
| + | |
− | //else {
| + | |
− | formattedLines.push(
| + | |
− | '<p class="beit">'
| + | |
− | + leadingSpace
| + | |
− | + '<span class="bJustified" 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);
| |
− | });
| |
| | | |
| var toggler = document.getElementsByClassName("treeBranch"); | | var toggler = document.getElementsByClassName("treeBranch"); |