1 2/** 3 * Markdown handler 4 * 5 * Handles content with markdown 6 * 7 * @author Pætur Mortensen 8 */ 9class MarkdownHandler{ 10 11 // The processed output 12 output; 13 // Markdown markers on the system 14 markers = { 15 image: { markdown: '#image#', methodName: 'process_image_markdown' }, 16 bold: { markdown: '#bold#', methodName: 'process_bold_markdown' }, 17 italic: { markdown: '#italic#', methodName: 'process_italic_markdown' }, 18 }; 19 // Currently handled markdown instance 20 currentMarkdown = { type: null, startPosition: 0, endPosition: 0 }; 21 22 constructor(input){ 23 // Set the initial output as the input 24 this.output = input; 25 26 // Handle all markdown 27 this.handle_markdown(); 28 } 29 30 /** 31 * Handle all markdown in the content 32 * 33 * @author Pætur Mortensen 34 */ 35 handle_markdown(){ 36 37 // For each type of markdown marker 38 for(const type in this.markers){ 39 const marker = this.markers[type]; 40 // Set the marker type currently being worked 41 this.currentMarkdown.type = type; 42 // Extract markdown identifier and method name from the marker 43 const markdown = marker.markdown; 44 const methodName = marker.methodName; 45 46 // Find position of marker if set and set as currently worked markdown 47 this.currentMarkdown.startPosition = this.output.indexOf(markdown); 48 49 // While there is an instance of the markdown marker, process it 50 while(this.currentMarkdown.startPosition !== -1){ 51 // Call the marker method for this type of marker and get the string for injection 52 const injectString = this[methodName](); 53 54 // Inject the formatted string if there is one to inject 55 if(injectString) this.inject_formatted_string(injectString); 56 // There is no injection string, clear the marker so we don't end up with infinite loop 57 else this.clear_markdown_marker(); 58 59 // Once the above marker has been processed, search again to see if there are more 60 this.currentMarkdown.startPosition = this.output.indexOf(markdown); 61 } 62 } 63 } 64 65 /** 66 * Inject the formatted string in place of the markdown 67 * 68 * @param {string} injectString String to inject in place of markdown 69 * 70 * @author Pætur Mortensen 71 */ 72 inject_formatted_string(injectString){ 73 // Get strings before and after the markdown 74 const beforeString = this.output.substring(0, this.currentMarkdown.startPosition); 75 const afterString = this.output.substring(this.currentMarkdown.endPosition + 1); 76 // Build new output with formatted markdown 77 this.output = beforeString + injectString + afterString; 78 } 79 80 /** 81 * Get the content from the markdown 82 * 83 * @author Pætur Mortensen 84 */ 85 get_markdown_content() 86 { 87 // Set the type for this markdown 88 const type = this.currentMarkdown.type; 89 // Identify the marker itself and set string positions for it 90 const marker = this.markers[type].markdown; 91 const markerLength = marker.length; 92 // Set the marker end position 93 const markerEndPos = this.currentMarkdown.startPosition + markerLength; 94 95 // Get the delimiter positions (delimiters for the markdown content) 96 const delimiterPositions = this.get_delimiter_positions(markerEndPos); 97 98 // If delimiters were not found, return NULL 99 if(!delimiterPositions) return null; 100 101 // Extract the content from the output 102 const content = this.output.substring(delimiterPositions.start + 1, delimiterPositions.end); 103 104 // If there is no content, just return null 105 if(!content) return null; 106 107 return content; 108 } 109 110 /** 111 * Get the delimiter positions 112 * 113 * @author Pætur Mortensen 114 */ 115 get_delimiter_positions(startPosition) 116 { 117 // If the delimiter is not found here, return FALSE 118 if(this.output[startPosition] !== '(') return false; 119 // Get the position for the end delimiter 120 const endPosition = this.output.indexOf(')', startPosition); 121 122 // If no end position was found, return FALSE 123 if(!endPosition) return false; 124 125 // Set the current markdown end position 126 this.currentMarkdown.endPosition = endPosition; 127 128 // Return start and end delimiter positions 129 return {start: startPosition, end: endPosition}; 130 } 131 132 /** 133 * Clear the first instance of the current markdown marker 134 * 135 * This is done to ensure this marker isn't identified again and we get an infinite loop when 136 * there is some issue with the markdown 137 * 138 * @author Pætur Mortensen 139 */ 140 clear_markdown_marker() 141 { 142 // Get the current type and the markdown for it 143 const type = this.currentMarkdown.type; 144 const marker = this.markers[type].markdown; 145 // Find the first instance of the marker in the string 146 const position = this.output.indexOf(marker); 147 // Find the end position for the marker by its length 148 const markerEndPos = position + marker.length; 149 150 // Now remove the marker from the string 151 this.output = this.output.substring(0, position) + this.output.substring(markerEndPos); 152 } 153 154/*************************************************************************************************** 155 * 156 * MARKDOWN PROCESSORS 157 * 158 **************************************************************************************************/ 159 160 /** 161 * Process markdown for an image #image#() 162 * 163 * @author Pætur Mortensen 164 */ 165 process_image_markdown(){ 166 // Get the image source from the markdown content 167 const imgSrc = this.get_markdown_content(); 168 169 // Image source not found. Return NULL for cleanup 170 if(!imgSrc) return null; 171 172 // Build the string to inject in place of the markdown 173 return '<img src="' + imgSrc + '" alt="markdown image" class="markdown-image" />'; 174 } 175 176 /** 177 * Process markdown for bold text #bold#() 178 * 179 * @author Pætur Mortensen 180 */ 181 process_bold_markdown(){ 182 // Get the content from the markdown content 183 const content = this.get_markdown_content(); 184 185 // Content not found. Return NULL for cleanup 186 if(!content) return null; 187 188 // Build the string to inject in place of the markdown 189 return '<b>' + content + '</b>'; 190 } 191 192 /** 193 * Process markdown for italic text #italic#() 194 * 195 * @author Pætur Mortensen 196 */ 197 process_italic_markdown(){ 198 // Get the content from the markdown content 199 const content = this.get_markdown_content(); 200 201 // Content not found. Return NULL for cleanup 202 if(!content) return null; 203 204 // Build the string to inject in place of the markdown 205 return '<i>' + content + '</i>'; 206 } 207 208 /** 209 * Return the formatted result 210 * 211 * @author Pætur Mortensen 212 */ 213 result(){ 214 return this.output; 215 } 216}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.