Added resource description
This commit is contained in:
parent
1b4d94a8de
commit
d7298e0c02
8 changed files with 470 additions and 1 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -13,4 +13,5 @@
|
|||
*.iml
|
||||
*.ipr
|
||||
.internal/Tools/*
|
||||
.internal/Sources/config.ini
|
||||
.internal/Sources/config.ini
|
||||
*.bbcode
|
26
.idea/runConfigurations/Convert_Description_for_SpigotMC.xml
generated
Normal file
26
.idea/runConfigurations/Convert_Description_for_SpigotMC.xml
generated
Normal file
|
@ -0,0 +1,26 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Convert Description for SpigotMC" type="PythonConfigurationType" factoryName="Python">
|
||||
<module name="VehiclesPlus" />
|
||||
<option name="ENV_FILES" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="SDK_NAME" value="Python 3.10" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Description" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/Description/html_to_bbcode.py" />
|
||||
<option name="PARAMETERS" value="vp.html" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
357
Description/html_to_bbcode.py
Normal file
357
Description/html_to_bbcode.py
Normal file
|
@ -0,0 +1,357 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
from bs4 import BeautifulSoup, NavigableString, Tag, Comment
|
||||
|
||||
|
||||
class HTMLToBBCode:
|
||||
def __init__(self):
|
||||
self.conversion_map = {
|
||||
'b': ['[b]', '[/b]'],
|
||||
'i': ['[i]', '[/i]'],
|
||||
'u': ['[u]', '[/u]'],
|
||||
'strong': ['[b]', '[/b]'],
|
||||
'em': ['[i]', '[/i]'],
|
||||
'strike': ['[s]', '[/s]'],
|
||||
'h1': ['[size=7]', '[/size]'],
|
||||
'h2': ['[size=6]', '[/size]'],
|
||||
'h3': ['[size=5]', '[/size]'],
|
||||
'h4': ['[size=4]', '[/size]'],
|
||||
'h5': ['[size=3]', '[/size]'],
|
||||
'h6': ['[size=2]', '[/size]'],
|
||||
'center': ['[center]', '[/center]'],
|
||||
'div': ['', ''], # Handle div attributes separately
|
||||
}
|
||||
|
||||
# URL replacements (from -> to)
|
||||
self.url_replacements = {
|
||||
'https://polymart.org/resource/vehiclesplus-1-12-1-20-2.633?purchase=1':
|
||||
'https://www.spigotmc.org/resources/vehiclesplus-1-12-1-20-2.70523/purchase',
|
||||
'https://polymart.org/resource?spigot_id=1997':
|
||||
'https://www.spigotmc.org/resources/protocollib.1997/',
|
||||
'https://polymart.org/resource?spigot_id=34315':
|
||||
'https://www.spigotmc.org/resources/vault.34315/',
|
||||
'https://polymart.org/resource?spigot_id=9089':
|
||||
'https://www.spigotmc.org/resources/essentialsx.9089/',
|
||||
'https://sbdevelopment.tech/images/buttons/buy2.png':
|
||||
'https://sbdevelopment.tech/images/buttons/buy.png',
|
||||
'https://sbdevelopment.tech/images/buttons/wiki2.png':
|
||||
'https://sbdevelopment.tech/images/buttons/wiki.png',
|
||||
'https://sbdevelopment.tech/images/buttons/discord2.png':
|
||||
'https://sbdevelopment.tech/images/buttons/discord.png',
|
||||
'https://sbdevelopment.tech/images/buttons/website2.png':
|
||||
'https://sbdevelopment.tech/images/buttons/website.png'
|
||||
}
|
||||
|
||||
def _convert_font_size(self, size, unit):
|
||||
"""Convert font size to BBCode size scale 1-7"""
|
||||
if unit == 'em':
|
||||
# Convert em to scale 1-7
|
||||
# em values: 1=normal, 2=large, 3=larger
|
||||
size = float(size)
|
||||
if size <= 0.8:
|
||||
return 1
|
||||
elif size <= 1:
|
||||
return 2
|
||||
elif size <= 1.5:
|
||||
return 3
|
||||
elif size <= 2:
|
||||
return 4
|
||||
elif size <= 2.5:
|
||||
return 5
|
||||
elif size <= 3:
|
||||
return 6
|
||||
else:
|
||||
return 7
|
||||
elif unit == 'pt':
|
||||
# Convert pt to scale 1-7
|
||||
size = int(size)
|
||||
if size <= 8:
|
||||
return 1
|
||||
elif size <= 10:
|
||||
return 2
|
||||
elif size <= 12:
|
||||
return 3
|
||||
elif size <= 14:
|
||||
return 4
|
||||
elif size <= 16:
|
||||
return 5
|
||||
elif size <= 20:
|
||||
return 6
|
||||
else:
|
||||
return 7
|
||||
return 3 # Default to normal size
|
||||
|
||||
@staticmethod
|
||||
def _convert_youtube_url(url):
|
||||
# Extract video ID from YouTube URL
|
||||
if 'youtube.com' in url:
|
||||
video_id = url.split('v=')[-1].split('&')[0]
|
||||
return f'[MEDIA=youtube]{video_id}[/MEDIA]'
|
||||
elif 'youtu.be' in url:
|
||||
video_id = url.split('/')[-1].split('?')[0]
|
||||
return f'[MEDIA=youtube]{video_id}[/MEDIA]'
|
||||
return None
|
||||
|
||||
def convert(self, html_content):
|
||||
# Create BeautifulSoup object
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
|
||||
# Remove script, style elements and comments
|
||||
for element in soup(['script', 'style']):
|
||||
element.decompose()
|
||||
|
||||
# Remove HTML comments
|
||||
for comment in soup.find_all(string=lambda text: isinstance(text, Comment)):
|
||||
comment.extract()
|
||||
|
||||
# Convert the HTML to BBCode
|
||||
bbcode = self._process_tag(soup)
|
||||
|
||||
# Fix [CENTER] tag formatting - ensure single newline before [CENTER] and after content
|
||||
bbcode = re.sub(r'\n+\[CENTER\]', '\n[CENTER]', bbcode)
|
||||
bbcode = re.sub(r'([^\n])\[CENTER\]', r'\1\n[CENTER]', bbcode)
|
||||
|
||||
# Ensure [/CENTER] is on its own line
|
||||
bbcode = re.sub(r'\[/CENTER\]([^\n])', r'[/CENTER]\n\1', bbcode)
|
||||
|
||||
# Clean up multiple consecutive newlines, but preserve single newlines
|
||||
bbcode = re.sub(r'\n{3,}', '\n\n', bbcode)
|
||||
|
||||
# Remove any trailing whitespace
|
||||
return bbcode.strip()
|
||||
|
||||
def _process_tag(self, element):
|
||||
if isinstance(element, NavigableString):
|
||||
# Clean up text content but preserve trailing space
|
||||
text = element.string if element.string else ''
|
||||
# Keep trailing space if it exists
|
||||
has_trailing_space = text.endswith(' ')
|
||||
# Normalize internal spaces
|
||||
text = ' '.join(text.split())
|
||||
# Restore trailing space if it existed
|
||||
if has_trailing_space:
|
||||
text += ' '
|
||||
return text
|
||||
|
||||
# Skip elements with hidden-bbcode class
|
||||
if isinstance(element, Tag) and 'hidden-bbcode' in element.get('class', []):
|
||||
return ''
|
||||
|
||||
# Handle br tags immediately
|
||||
if element.name == 'br':
|
||||
return '\n'
|
||||
|
||||
# Handle p tags
|
||||
if element.name == 'p':
|
||||
content = self._get_inner_content(element)
|
||||
style = element.get('style', '')
|
||||
|
||||
if style:
|
||||
tags = []
|
||||
# Handle font size
|
||||
size_match = re.search(r'font-size:\s*(\d+(?:\.\d+)?)(pt|em)', style)
|
||||
if size_match:
|
||||
size = size_match.group(1)
|
||||
unit = size_match.group(2)
|
||||
bbcode_size = self._convert_font_size(size, unit)
|
||||
tags.append(f'[SIZE={bbcode_size}]')
|
||||
|
||||
# Handle color
|
||||
color_match = re.search(r'color:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)', style)
|
||||
if color_match:
|
||||
rgb = f'#{int(color_match.group(1)):02x}{int(color_match.group(2)):02x}{int(color_match.group(3)):02x}'
|
||||
tags.append(f'[COLOR={rgb}]')
|
||||
|
||||
# Apply all opening tags
|
||||
for tag in tags:
|
||||
content = tag + content
|
||||
|
||||
# Apply all closing tags in reverse order
|
||||
for tag in reversed(tags):
|
||||
content += f'[/{tag[1:tag.index("=")]}]' if '=' in tag else f'[/{tag[1:]}]'
|
||||
|
||||
# Only add newlines if there's actual content and we're not in a special container
|
||||
if content.strip() and not element.find_parent(['blockquote', 'center']):
|
||||
# Check if the content is just a single element (like an IMG or MEDIA tag)
|
||||
if re.match(r'^\[(?:IMG|MEDIA)[^\]]*\][^\[]*\[/(?:IMG|MEDIA)\]$', content.strip()):
|
||||
return f'{content}\n'
|
||||
return f'{content}\n\n'
|
||||
return content
|
||||
|
||||
# Handle styled elements (span)
|
||||
if element.name == 'span':
|
||||
style = element.get('style', '')
|
||||
if style:
|
||||
content = self._get_inner_content(element)
|
||||
tags = []
|
||||
|
||||
# Handle font size
|
||||
size_match = re.search(r'font-size:\s*(\d+(?:\.\d+)?)(pt|em)', style)
|
||||
if size_match:
|
||||
size = size_match.group(1)
|
||||
unit = size_match.group(2)
|
||||
bbcode_size = self._convert_font_size(size, unit)
|
||||
tags.append(f'[SIZE={bbcode_size}]')
|
||||
|
||||
# Handle color
|
||||
color_match = re.search(r'color:\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)', style)
|
||||
if color_match:
|
||||
rgb = f'#{int(color_match.group(1)):02x}{int(color_match.group(2)):02x}{int(color_match.group(3)):02x}'
|
||||
tags.append(f'[COLOR={rgb}]')
|
||||
|
||||
# Apply all opening tags
|
||||
for tag in tags:
|
||||
content = tag + content
|
||||
|
||||
# Apply all closing tags in reverse order
|
||||
for tag in reversed(tags):
|
||||
content += f'[/{tag[1:tag.index("=")]}]' if '=' in tag else f'[/{tag[1:]}]'
|
||||
|
||||
return content
|
||||
else:
|
||||
content = self._get_inner_content(element)
|
||||
return content
|
||||
|
||||
# Handle warning paragraph
|
||||
if element.name == 'p' and element.find('a', {'class': 'externalLink ProxyLink'}):
|
||||
warning_text = self._get_inner_content(element)
|
||||
return f'[SIZE=3][COLOR=rgb(255, 128, 0)]{warning_text}[/COLOR][/SIZE]\n\n'
|
||||
|
||||
# Handle text alignment in divs
|
||||
if element.name == 'div':
|
||||
style = element.get('style', '')
|
||||
if 'text-align: center' in style:
|
||||
inner_content = self._get_inner_content(element).strip()
|
||||
if inner_content:
|
||||
# Both opening and closing tags on their own lines
|
||||
return f'[CENTER]\n{inner_content}\n[/CENTER]'
|
||||
return ''
|
||||
|
||||
# Handle blockquotes - strip all tags inside
|
||||
if element.name == 'blockquote':
|
||||
# Get raw text without any formatting
|
||||
content = ''
|
||||
for text in element.stripped_strings:
|
||||
content += text + '\n'
|
||||
return f'[QUOTE]\n{content.strip()}\n[/QUOTE]'
|
||||
|
||||
# Handle links
|
||||
if element.name == 'a':
|
||||
href = element.get('href', '')
|
||||
if href:
|
||||
# Check if it's a YouTube URL first
|
||||
youtube_bbcode = self._convert_youtube_url(href)
|
||||
if youtube_bbcode:
|
||||
return youtube_bbcode
|
||||
|
||||
# Otherwise handle as normal URL
|
||||
href = self.url_replacements.get(href, href)
|
||||
content = self._get_inner_content(element)
|
||||
# Check if there's space after the link
|
||||
next_sibling = element.next_sibling
|
||||
has_space = next_sibling and isinstance(next_sibling, NavigableString) and next_sibling.startswith(' ')
|
||||
# Use single quotes for URLs and preserve space
|
||||
return f"[URL='{href}']{content}[/URL]" + (' ' if has_space else '')
|
||||
return ''
|
||||
|
||||
# Handle images
|
||||
if element.name == 'img':
|
||||
src = element.get('src', '')
|
||||
if src:
|
||||
src = self.url_replacements.get(src, src)
|
||||
return f'[IMG]{src}[/IMG]'
|
||||
return ''
|
||||
|
||||
# Handle lists
|
||||
if element.name in ['ul', 'ol']:
|
||||
result = '[LIST]' if element.name == 'ul' else '[LIST=1]'
|
||||
result += '\n'
|
||||
for item in element.find_all('li', recursive=False):
|
||||
result += '[*]' + self._get_inner_content(item).strip() + '\n'
|
||||
result += '[/LIST]\n'
|
||||
return result
|
||||
|
||||
# Handle pre/code blocks
|
||||
if element.name == 'pre':
|
||||
if element.find('code'):
|
||||
# Get the raw content preserving original formatting
|
||||
code_content = ''
|
||||
for string in element.find('code').strings:
|
||||
code_content += string
|
||||
if code_content:
|
||||
# Preserve original formatting for HTML content
|
||||
if 'language-markup' in element.get('class', []):
|
||||
return f'[html]\n{code_content}\n[/html]'
|
||||
return f'[code]\n{code_content}\n[/code]'
|
||||
return ''
|
||||
|
||||
# Handle basic formatting
|
||||
if element.name in self.conversion_map:
|
||||
content = self._get_inner_content(element)
|
||||
if content.strip():
|
||||
return f"{self.conversion_map[element.name][0]}{content}{self.conversion_map[element.name][1]}"
|
||||
|
||||
# Process all other tags
|
||||
return self._get_inner_content(element)
|
||||
|
||||
def _get_inner_content(self, element):
|
||||
return ''.join(self._process_tag(child) for child in element.children)
|
||||
|
||||
|
||||
def convert_html_to_bbcode(html_content):
|
||||
"""
|
||||
Convert HTML content to BBCode.
|
||||
|
||||
Args:
|
||||
html_content (str): The HTML content to convert
|
||||
|
||||
Returns:
|
||||
str: The converted BBCode content
|
||||
"""
|
||||
converter = HTMLToBBCode()
|
||||
return converter.convert(html_content)
|
||||
|
||||
|
||||
def convert_file(input_file):
|
||||
"""
|
||||
Convert an HTML file to BBCode and save it with .bbcode extension
|
||||
|
||||
Args:
|
||||
input_file (str): Path to the HTML file
|
||||
"""
|
||||
if not input_file.endswith('.html'):
|
||||
print(f"Error: Input file '{input_file}' must have .html extension")
|
||||
return False
|
||||
|
||||
output_file = input_file[:-5] + '.bbcode' # Replace .html with .bbcode
|
||||
|
||||
try:
|
||||
with open(input_file, 'r', encoding='utf-8') as f:
|
||||
html_content = f.read()
|
||||
|
||||
bbcode = convert_html_to_bbcode(html_content)
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(bbcode)
|
||||
|
||||
print(f"Successfully converted '{input_file}' to '{output_file}'")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error converting file: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python html_to_bbcode.py <input_file.html>")
|
||||
sys.exit(1)
|
||||
|
||||
input_file = sys.argv[1]
|
||||
if not os.path.exists(input_file):
|
||||
print(f"Error: File '{input_file}' does not exist")
|
||||
sys.exit(1)
|
||||
|
||||
success = convert_file(input_file)
|
||||
sys.exit(0 if success else 1)
|
3
Description/index.html
Normal file
3
Description/index.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<body style="background: #232323;">
|
||||
<iframe width="1150" height="99%" src="vp.html" style="display: block; margin: 0 auto; background: white;"></iframe>
|
||||
</body>
|
1
Description/requirements.txt
Normal file
1
Description/requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
beautifulsoup4==4.12.2
|
81
Description/vp.html
Normal file
81
Description/vp.html
Normal file
|
@ -0,0 +1,81 @@
|
|||
<!-- v2 WARNING -->
|
||||
<p style="font-size: 16px; color: rgb(255, 128, 0); display: inline;">NOTE FOR LEGACY USERS: v3 will create a new datafolder. You can convert the data of v2 using the <a class="externalLink ProxyLink" href="https://git.sbdevelopment.tech/SBDevelopment/VehiclesPlusConverter/releases/tag/0.2" target="_blank" rel="nofollow noopener" data-proxy-href="https://git.sbdevelopment.tech/SBDevelopment/VehiclesPlusConverter/releases/tag/0.2">VehiclesPlusConverter</a>.</p>
|
||||
<!-- Name header -->
|
||||
<p>
|
||||
<img src="https://sbdevelopment.tech/images/headers/vp.png"/>
|
||||
</p>
|
||||
<!-- Button array -->
|
||||
<div style="text-align: center;">
|
||||
<a href="https://polymart.org/resource/vehiclesplus-1-12-1-20-2.633?purchase=1">
|
||||
<img src="https://sbdevelopment.tech/images/buttons/buy2.png"
|
||||
width="220" height="88"/></a>
|
||||
<a href="https://docs.sbdevelopment.tech/vehiclesplus-v3/about">
|
||||
<img src="https://sbdevelopment.tech/images/buttons/wiki2.png"
|
||||
width="220" height="88"/></a>
|
||||
<a href="https://discord.gg/z26ZGrrFWB">
|
||||
<img src="https://sbdevelopment.tech/images/buttons/discord2.png"
|
||||
width="220" height="88"/></a>
|
||||
<a href="https://sbdevelopment.tech">
|
||||
<img src="https://sbdevelopment.tech/images/buttons/website2.png"
|
||||
width="220" height="88"/></a>
|
||||
</div>
|
||||
<!-- New line -->
|
||||
<div style="text-align: center;"> </div>
|
||||
<!-- Polymart only note: Discount for Turkey, Brazil and Ukraine -->
|
||||
<div style="text-align: center;">
|
||||
<em class="hidden-bbcode">Buyers from Turkey, Brazil or Ukraine will receive a currency-based discount!</em>
|
||||
</div>
|
||||
<!-- USAGE -->
|
||||
<p><img src="https://sbdevelopment.tech/images/banners/usage.png" width="100%"/></p>
|
||||
<p>The plugin is pretty easy to start with!<br/>Follow our <a href="https://docs.sbdevelopment.tech/vehiclesplus-v3/setup">Getting Started</a> wiki page to find out more.</p>
|
||||
<!-- FUNCTIONALITIES -->
|
||||
<p><img src="https://sbdevelopment.tech/images/banners/functionalities.png" width="100%"/></p>
|
||||
<p>The plugin has a lot of features to make your server more fun!<br/>You can find out more about the features <a href="https://docs.sbdevelopment.tech/vehiclesplus-v3/vehicles">on our Wiki</a>.</p>
|
||||
<!-- Tutorials -->
|
||||
<p><img src="https://sbdevelopment.tech/images/banners/tutorials.png" width="100%"/></p>
|
||||
<p><a href="https://www.youtube.com/watch?v=i3o2s4g-vBs">https://www.youtube.com/watch?v=i3o2s4g-vBs</a></p>
|
||||
<p><a href="https://youtu.be/07H_CQcgBrY">https://youtu.be/07H_CQcgBrY</a></p>
|
||||
<!-- For developers -->
|
||||
<p><img src="https://sbdevelopment.tech/images/banners/fordevelopers.png" width="100%"/></p>
|
||||
<p>The plugin has a big API available for developers to create cool add-ons!<br/>Follow <a href="https://docs.sbdevelopment.tech/vehiclesplus-v3/api/gettingstarted">the API Usage instructions on our Wiki</a>.</p>
|
||||
<!-- Terms of Service -->
|
||||
<p><img src="https://sbdevelopment.tech/images/banners/termsofservice.png" width="100%"/></p>
|
||||
<ul>
|
||||
<li>By purchasing this plugin, you affirm that you have thoroughly read and comprehended the complete description of
|
||||
the plugin.
|
||||
</li>
|
||||
<li>You are expressly prohibited from modifying, reselling, or redistributing this plugin without explicit
|
||||
authorization from the plugin's rights holder.
|
||||
</li>
|
||||
<li>Decompiling or reverse engineering of this plugin is strictly prohibited, and you shall not engage in such
|
||||
activities without prior written consent from the plugin's rights holder.
|
||||
</li>
|
||||
<li>It is forbidden to request support through the reviews section. Please utilize the designated channels such as
|
||||
the Discord server or GitHub issues page for seeking support.
|
||||
</li>
|
||||
<li>By clicking on the download link, you expressly waive any right to withdraw this product as per the provisions
|
||||
of European Directive 2011/83/EU on Consumer Rights.
|
||||
</li>
|
||||
<li>These Terms of Service are subject to modification without prior notice.</li>
|
||||
</ul>
|
||||
<!-- DISCLAIMER -->
|
||||
<blockquote>
|
||||
<p><span style="font-size: 8pt;">Copyright (C) SBDevelopment - All Rights Reserved.</span></p>
|
||||
<p>
|
||||
<span style="font-size: 8pt;">THE CONTENTS OF THIS PROJECT ARE PROPRIETARY AND CONFIDENTIAL.</span><br/>
|
||||
<span style="font-size: 8pt;">UNAUTHORIZED COPYING, TRANSFERRING OR REPRODUCTION OF THE CONTENTS OF THIS PROJECT, VIA ANY MEDIUM, IS STRICTLY PROHIBITED.</span>
|
||||
</p>
|
||||
<p>
|
||||
<span style="font-size: 8pt;">The receipt or possession of the source code and/or any parts thereof does not convey or imply any right to use them</span><br/>
|
||||
<span style="font-size: 8pt;">for any purpose other than the purpose for which they were provided to you.</span>
|
||||
</p>
|
||||
<p>
|
||||
<span style="font-size: 8pt;">The software is provided "AS IS", without warranty of any kind, express or implied, including but not limited to</span><br/>
|
||||
<span style="font-size: 8pt;">the warranties of merchantability, fitness for a particular purpose and non infringement.</span><br/>
|
||||
<span style="font-size: 8pt;">In no event shall the authors or copyright holders be liable for any claim, damages or other liability,</span><br/>
|
||||
<span style="font-size: 8pt;">whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software</span><br/>
|
||||
<span style="font-size: 8pt;">or the use or other dealings in the software.</span>
|
||||
</p>
|
||||
<p><span style="font-size: 8pt;">The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</span>
|
||||
</p>
|
||||
</blockquote>
|
Binary file not shown.
Binary file not shown.
Loading…
Add table
Reference in a new issue