├── README.md ├── background.js ├── content.css ├── content.js ├── html2canvas.min.js ├── icons ├── icon128.png ├── icon16.png └── icon48.png ├── manifest.json └── qrcode.min.js /README.md: -------------------------------------------------------------------------------- 1 | # HighlightShare Chrome Extension 2 | 3 | 一个简单优雅的 Chrome 扩展,用于将网页中选中的文字和图片生成精美的分享卡片。 4 | ![Demo](https://huazispace.s3.bitiful.net/HightlightShare%2Fintroduce.png?no-wait=on) 5 | 6 | ## 功能特点 7 | 8 | - 🎯 一键选择:支持选中网页中的文字和图片 9 | - 🎨 主题切换:提供明暗两种主题风格 10 | - 📱 二维码分享:自动生成当前页面二维码 11 | - 💾 便捷保存:支持复制到剪贴板或下载为图片 12 | - 🎭 优雅界面:简洁美观的卡片设计 13 | 14 | ## 使用方法 15 | 16 | 1. 选择内容 17 | - 选中网页中的文字 18 | - 右键点击网页中的图片 19 | 20 | 2. 生成卡片 21 | - 点击右键菜单中的"生成分享卡片" 22 | 23 | 3. 自定义卡片 24 | - 点击"切换"按钮更换主题风格 25 | - 点击"二维码"按钮显示/隐藏页面二维码 26 | 27 | 4. 分享卡片 28 | - 点击"复制"按钮复制到剪贴板 29 | - 点击"下载"按钮保存为图片 30 | 31 | ## 安装方法 32 | 33 | 1. 下载扩展 34 | - 克隆或下载本仓库到本地 35 | 36 | 2. 安装扩展 37 | - 打开 Chrome 浏览器 38 | - 访问 `chrome://extensions/` 39 | - 开启"开发者模式" 40 | - 点击"加载已解压的扩展程序" 41 | - 选择项目目录 42 | 43 | ## 技术实现 44 | 45 | - 使用原生 JavaScript 开发,无需额外框架 46 | - 基于 Chrome Extension Manifest V3 47 | - 使用 html2canvas 实现卡片导出 48 | - 使用 QRCode.js 生成二维码 49 | 50 | ## 更新记录 51 | 52 | - V0.2 增加二维码显示控制 53 | - 新增二维码生成功能 54 | - 支持二维码显示/隐藏 55 | 56 | - V0.1 预览版 57 | - 基础卡片生成功能 58 | - 支持文字和图片选择 59 | - 明暗两种主题切换 60 | - 复制和下载功能 61 | 62 | ## 贡献指南 63 | 64 | 欢迎提交 Issue 和 Pull Request 来帮助改进这个项目! 65 | 66 | ## 注意事项 67 | 68 | - 确保允许扩展程序访问网页内容 69 | - 下载功能需要允许浏览器下载权限 70 | - 部分网站可能会限制内容选择或图片下载 -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(() => { 2 | chrome.contextMenus.create({ 3 | id: "highlightShare", 4 | title: "生成分享卡片", 5 | contexts: ["selection", "image"] 6 | }); 7 | }); 8 | 9 | chrome.contextMenus.onClicked.addListener((info, tab) => { 10 | if (info.menuItemId === "highlightShare") { 11 | chrome.tabs.sendMessage(tab.id, { 12 | action: "createCard", 13 | selection: info.selectionText, 14 | imageUrl: info.srcUrl 15 | }); 16 | } 17 | }); 18 | 19 | // 处理下载请求 20 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 21 | if (request.action === 'downloadCard') { 22 | chrome.downloads.download({ 23 | url: request.dataUrl, 24 | filename: request.filename, 25 | saveAs: true 26 | }); 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /content.css: -------------------------------------------------------------------------------- 1 | .highlight-share-modal { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background: rgba(0, 0, 0, 0.5); 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | justify-content: center; 12 | z-index: 999999; 13 | } 14 | 15 | .highlight-share-card { 16 | background: white; 17 | border-radius: 12px; 18 | box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); 19 | width: 600px; 20 | max-width: 90vw; 21 | padding: 24px; 22 | margin-bottom: 16px; 23 | } 24 | 25 | /* 默认风格 */ 26 | .highlight-share-card[data-style="1"] { 27 | background: white; 28 | color: #333; 29 | } 30 | 31 | /* 暗色风格 */ 32 | .highlight-share-card[data-style="2"] { 33 | background: #2c2c2c; 34 | color: #fff; 35 | } 36 | 37 | .card-header { 38 | display: flex; 39 | align-items: center; 40 | margin-bottom: 16px; 41 | } 42 | 43 | .favicon { 44 | width: 32px; 45 | height: 32px; 46 | margin-right: 12px; 47 | } 48 | 49 | .page-info { 50 | flex: 1; 51 | } 52 | 53 | .page-title { 54 | font-size: 16px; 55 | font-weight: 600; 56 | margin-bottom: 2px; 57 | } 58 | 59 | .page-url { 60 | font-size: 14px; 61 | color: #666; 62 | word-break: break-all; 63 | } 64 | 65 | .card-content { 66 | margin: 16px 0; 67 | } 68 | 69 | .selected-text { 70 | font-size: 16px; 71 | line-height: 1.6; 72 | margin-bottom: 16px; 73 | white-space: pre-wrap; 74 | word-wrap: break-word; 75 | word-break: break-word; 76 | } 77 | 78 | .selected-text p { 79 | margin: 0 0 1em; 80 | } 81 | 82 | .selected-text p:last-child { 83 | margin-bottom: 0; 84 | } 85 | 86 | .selected-image { 87 | max-width: 100%; 88 | border-radius: 8px; 89 | } 90 | 91 | .card-toolbar { 92 | display: flex; 93 | gap: 12px; 94 | justify-content: center; 95 | } 96 | 97 | .card-toolbar button { 98 | display: inline-flex; 99 | align-items: center; 100 | gap: 6px; 101 | padding: 8px 16px; 102 | border: none; 103 | border-radius: 6px; 104 | background: #007AFF; 105 | color: white; 106 | cursor: pointer; 107 | font-size: 14px; 108 | transition: all 0.2s ease; 109 | } 110 | 111 | .card-toolbar button svg { 112 | display: inline-block; 113 | width: 16px; 114 | height: 16px; 115 | margin-right: 4px; 116 | vertical-align: middle; 117 | transition: transform 0.2s ease; 118 | } 119 | 120 | .card-toolbar button:hover { 121 | background: #0056b3; 122 | } 123 | 124 | .card-toolbar button:hover svg { 125 | transform: scale(1.1); 126 | } 127 | 128 | .close-modal { 129 | background: #666 !important; 130 | } 131 | 132 | .close-modal:hover { 133 | background: #444 !important; 134 | } 135 | 136 | /* 右键菜单样式 */ 137 | .card-context-menu { 138 | position: fixed; 139 | background: white; 140 | border-radius: 8px; 141 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); 142 | padding: 8px 0; 143 | z-index: 1000000; 144 | min-width: 160px; 145 | } 146 | 147 | .card-context-menu .menu-item { 148 | display: flex; 149 | align-items: center; 150 | gap: 8px; 151 | padding: 8px 16px; 152 | cursor: pointer; 153 | transition: background-color 0.2s; 154 | font-size: 14px; 155 | color: #333; 156 | } 157 | 158 | .card-context-menu .menu-item:hover { 159 | background-color: #f5f5f5; 160 | } 161 | 162 | .card-context-menu .menu-item svg { 163 | transition: transform 0.2s ease; 164 | } 165 | 166 | .card-context-menu .menu-item:hover svg { 167 | transform: scale(1.1); 168 | } 169 | 170 | /* 提示框样式 */ 171 | .highlight-share-toast { 172 | position: fixed; 173 | bottom: 20px; 174 | left: 50%; 175 | transform: translateX(-50%) translateY(100px); 176 | background: rgba(0, 0, 0, 0.8); 177 | color: white; 178 | padding: 12px 24px; 179 | border-radius: 4px; 180 | font-size: 14px; 181 | z-index: 1000000; 182 | transition: transform 0.3s ease-out; 183 | } 184 | 185 | .highlight-share-toast.show { 186 | transform: translateX(-50%) translateY(0); 187 | } 188 | 189 | /* 暗色主题样式优化 */ 190 | .highlight-share-card[data-style="2"] .page-url { 191 | color: #999; 192 | } 193 | 194 | .highlight-share-card[data-style="2"] .card-context-menu { 195 | background: #2c2c2c; 196 | border: 1px solid #444; 197 | } 198 | 199 | .highlight-share-card[data-style="2"] .card-context-menu .menu-item { 200 | color: #fff; 201 | } 202 | 203 | .highlight-share-card[data-style="2"] .card-context-menu .menu-item:hover { 204 | background-color: #3c3c3c; 205 | } 206 | 207 | .highlight-share-card[data-style="2"] .card-toolbar button { 208 | background: #444; 209 | } 210 | 211 | .highlight-share-card[data-style="2"] .card-toolbar button:hover { 212 | background: #555; 213 | } 214 | 215 | .highlight-share-card[data-style="2"] .close-modal { 216 | background: #333 !important; 217 | } 218 | 219 | .highlight-share-card[data-style="2"] .close-modal:hover { 220 | background: #222 !important; 221 | } 222 | 223 | /* 卡片底部样式 */ 224 | .card-footer { 225 | display: flex; 226 | justify-content: flex-end; 227 | margin-top: 16px; 228 | padding-top: 16px; 229 | /* border-top: 1px solid #eee; */ 230 | } 231 | 232 | .highlight-share-card[data-style="2"] .card-footer { 233 | border-top-color: #444; 234 | } 235 | 236 | /* 二维码容器样式 */ 237 | .qrcode-container { 238 | display: none; 239 | margin-left: 8px; 240 | overflow: hidden; 241 | background: #fff; 242 | } 243 | 244 | .qrcode-container.show { 245 | display: block; 246 | } 247 | 248 | .qrcode-container canvas { 249 | display: block; 250 | width: 40px !important; 251 | height: 40px !important; 252 | } 253 | 254 | /* 二维码按钮激活状态 */ 255 | .qrcode-toggle.active { 256 | background: #005299 !important; 257 | color: #fff !important; 258 | } 259 | 260 | /* 适配深色主题 */ 261 | .highlight-share-card[data-style="2"] .qrcode-container { 262 | background: #2c2c2c; 263 | } 264 | -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | let cardModal = null; 2 | 3 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 4 | if (request.action === "createCard") { 5 | createShareCard(request); 6 | } 7 | }); 8 | 9 | function createShareCard(data) { 10 | if (cardModal) { 11 | document.body.removeChild(cardModal); 12 | } 13 | 14 | // 创建模态框 15 | cardModal = document.createElement('div'); 16 | cardModal.className = 'highlight-share-modal'; 17 | 18 | // 创建卡片 19 | const card = document.createElement('div'); 20 | card.className = 'highlight-share-card'; 21 | card.setAttribute('data-style', '1'); 22 | 23 | // 获取页面信息 24 | const pageTitle = document.title; 25 | const pageUrl = window.location.href; 26 | const favicon = document.querySelector('link[rel*="icon"]')?.href || '/favicon.ico'; 27 | 28 | // 处理选中的文本,保留段落格式 29 | const formattedText = data.selection ? formatSelectedText(data.selection) : ''; 30 | 31 | // 构建卡片内容 32 | card.innerHTML = ` 33 |
34 | favicon 35 |
36 |
${pageTitle}
37 |
${pageUrl}
38 |
39 |
40 |
41 | ${formattedText ? `
${formattedText}
` : ''} 42 | ${data.imageUrl ? `selected image` : ''} 43 |
44 | 47 | `; 48 | 49 | // 创建工具栏 50 | const toolbar = document.createElement('div'); 51 | toolbar.className = 'card-toolbar'; 52 | toolbar.innerHTML = ` 53 | 60 | 70 | 77 | 85 | 92 | `; 93 | 94 | // 将卡片和工具栏添加到模态框 95 | cardModal.appendChild(card); 96 | cardModal.appendChild(toolbar); 97 | document.body.appendChild(cardModal); 98 | 99 | // 添加事件监听 100 | const styleSwitch = toolbar.querySelector('.style-switch'); 101 | const qrcodeToggle = toolbar.querySelector('.qrcode-toggle'); 102 | const downloadBtn = toolbar.querySelector('.download-card'); 103 | const copyBtn = toolbar.querySelector('.copy-card'); 104 | const closeBtn = toolbar.querySelector('.close-modal'); 105 | const qrcodeContainer = card.querySelector('.qrcode-container'); // 从卡片中查找二维码容器 106 | 107 | console.log('Found buttons:', { 108 | styleSwitch: !!styleSwitch, 109 | qrcodeToggle: !!qrcodeToggle, 110 | downloadBtn: !!downloadBtn, 111 | copyBtn: !!copyBtn, 112 | closeBtn: !!closeBtn, 113 | qrcodeContainer: !!qrcodeContainer 114 | }); 115 | 116 | if (styleSwitch) { 117 | styleSwitch.addEventListener('click', () => switchStyle(card)); 118 | } 119 | 120 | if (qrcodeToggle && qrcodeContainer) { 121 | qrcodeToggle.addEventListener('click', () => toggleQRCode(card, qrcodeContainer)); 122 | } else { 123 | console.error('QR code toggle button or container not found', { 124 | toggle: !!qrcodeToggle, 125 | container: !!qrcodeContainer 126 | }); 127 | } 128 | 129 | if (downloadBtn) { 130 | downloadBtn.addEventListener('click', () => downloadCard(card)); 131 | } 132 | 133 | if (copyBtn) { 134 | copyBtn.addEventListener('click', () => copyCard(card)); 135 | } 136 | 137 | if (closeBtn) { 138 | closeBtn.addEventListener('click', closeModal); 139 | } 140 | 141 | // 添加卡片右键菜单 142 | card.addEventListener('contextmenu', (e) => { 143 | e.preventDefault(); 144 | const contextMenu = document.createElement('div'); 145 | contextMenu.className = 'card-context-menu'; 146 | contextMenu.innerHTML = ` 147 | 154 | 162 | `; 163 | 164 | contextMenu.style.top = `${e.clientY}px`; 165 | contextMenu.style.left = `${e.clientX}px`; 166 | 167 | document.body.appendChild(contextMenu); 168 | 169 | const removeMenu = () => { 170 | if (document.body.contains(contextMenu)) { 171 | document.body.removeChild(contextMenu); 172 | } 173 | document.removeEventListener('click', removeMenu); 174 | }; 175 | 176 | contextMenu.querySelector('.copy').addEventListener('click', () => { 177 | copyCard(card); 178 | removeMenu(); 179 | }); 180 | 181 | contextMenu.querySelector('.download').addEventListener('click', () => { 182 | downloadCard(card); 183 | removeMenu(); 184 | }); 185 | 186 | document.addEventListener('click', removeMenu); 187 | }); 188 | 189 | // 预加载图片 190 | const images = card.querySelectorAll('img'); 191 | Promise.all(Array.from(images).map(img => { 192 | return new Promise((resolve, reject) => { 193 | if (img.complete) { 194 | resolve(); 195 | } else { 196 | img.onload = resolve; 197 | img.onerror = resolve; // 即使加载失败也继续 198 | } 199 | }); 200 | })).then(() => { 201 | // 所有图片加载完成后,调整卡片大小 202 | card.style.maxHeight = '80vh'; 203 | card.style.overflow = 'auto'; 204 | }); 205 | } 206 | 207 | // 切换主题样式 208 | function switchStyle(cardElement) { 209 | const currentStyle = cardElement.getAttribute('data-style') || '1'; 210 | const nextStyle = currentStyle === '1' ? '2' : '1'; 211 | cardElement.setAttribute('data-style', nextStyle); 212 | 213 | // 如果二维码正在显示,更新二维码颜色 214 | const qrcodeContainer = cardElement.querySelector('.qrcode-container'); 215 | const button = cardElement.querySelector('.qrcode-toggle'); 216 | 217 | if (!qrcodeContainer) { 218 | console.error('QR code container not found'); 219 | return; 220 | } 221 | 222 | if (!button) { 223 | console.error('QR code toggle button not found'); 224 | return; 225 | } 226 | 227 | if (qrcodeContainer.style.display === 'block') { 228 | qrcodeContainer.innerHTML = ''; 229 | generateQRCode(cardElement, qrcodeContainer); 230 | } 231 | } 232 | 233 | // 切换二维码显示/隐藏 234 | function toggleQRCode(cardElement, qrcodeContainer) { 235 | console.log('toggleQRCode called'); 236 | const button = cardModal.querySelector('.qrcode-toggle'); 237 | 238 | console.log('Found elements:', { 239 | qrcodeContainer: !!qrcodeContainer, 240 | button: !!button 241 | }); 242 | 243 | if (!qrcodeContainer || !button) { 244 | console.error('Required elements not found'); 245 | return; 246 | } 247 | 248 | if (qrcodeContainer.style.display === 'none') { 249 | qrcodeContainer.style.display = 'block'; 250 | generateQRCode(cardElement, qrcodeContainer); 251 | button.classList.add('active'); 252 | } else { 253 | qrcodeContainer.style.display = 'none'; 254 | button.classList.remove('active'); 255 | } 256 | } 257 | 258 | // 生成二维码 259 | function generateQRCode(cardElement, qrcodeContainer) { 260 | console.log('Generating QR code'); 261 | const isDarkTheme = cardElement.getAttribute('data-style') === '2'; 262 | 263 | try { 264 | // 清除旧的二维码 265 | qrcodeContainer.innerHTML = ''; 266 | 267 | // 创建新的二维码 268 | const qrcode = new QRCode(qrcodeContainer, { 269 | text: window.location.href, 270 | width: 70, 271 | height: 70, 272 | colorDark: isDarkTheme ? "#FFFFFF" : "#000000", 273 | colorLight: isDarkTheme ? "#000000" : "#FFFFFF", 274 | correctLevel: QRCode.CorrectLevel.H 275 | }); 276 | 277 | console.log('QR code generated successfully'); 278 | } catch (error) { 279 | console.error('Failed to generate QR code:', error); 280 | } 281 | } 282 | 283 | async function downloadCard(cardElement) { 284 | try { 285 | showToast('正在生成卡片...'); 286 | 287 | // 创建一个容器来保持样式隔离 288 | const container = document.createElement('div'); 289 | container.style.position = 'fixed'; 290 | container.style.left = '-9999px'; 291 | container.style.top = '0'; 292 | container.style.width = cardElement.offsetWidth + 'px'; 293 | container.style.height = 'auto'; 294 | container.style.transform = 'none'; 295 | container.style.zIndex = '-1'; 296 | 297 | // 克隆卡片元素 298 | const clonedCard = cardElement.cloneNode(true); 299 | 300 | // 复制计算后的样式 301 | const computedStyle = window.getComputedStyle(cardElement); 302 | for (const prop of computedStyle) { 303 | clonedCard.style[prop] = computedStyle.getPropertyValue(prop); 304 | } 305 | 306 | // 确保背景色正确 307 | const isDarkMode = cardElement.getAttribute('data-style') === '2'; 308 | clonedCard.style.backgroundColor = isDarkMode ? '#2c2c2c' : '#ffffff'; 309 | clonedCard.style.position = 'relative'; 310 | clonedCard.style.left = '0'; 311 | clonedCard.style.top = '0'; 312 | clonedCard.style.transform = 'none'; 313 | clonedCard.style.margin = '0'; 314 | clonedCard.style.width = '100%'; 315 | 316 | // 复制子元素样式 317 | const sourceElements = cardElement.getElementsByTagName('*'); 318 | const clonedElements = clonedCard.getElementsByTagName('*'); 319 | for (let i = 0; i < sourceElements.length; i++) { 320 | const computedStyle = window.getComputedStyle(sourceElements[i]); 321 | for (const prop of computedStyle) { 322 | clonedElements[i].style[prop] = computedStyle.getPropertyValue(prop); 323 | } 324 | } 325 | 326 | container.appendChild(clonedCard); 327 | document.body.appendChild(container); 328 | 329 | const options = { 330 | useCORS: true, 331 | allowTaint: true, 332 | backgroundColor: isDarkMode ? '#2c2c2c' : '#ffffff', 333 | scale: 2, 334 | logging: false, 335 | width: cardElement.offsetWidth, 336 | height: cardElement.offsetHeight, 337 | removeContainer: true, 338 | foreignObjectRendering: false 339 | }; 340 | 341 | const canvas = await html2canvas(clonedCard, options); 342 | document.body.removeChild(container); 343 | 344 | const dataUrl = canvas.toDataURL('image/png'); 345 | const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); 346 | 347 | chrome.runtime.sendMessage({ 348 | action: 'downloadCard', 349 | dataUrl: dataUrl, 350 | filename: `highlight-share-${timestamp}.png` 351 | }); 352 | 353 | showToast('卡片已生成,请选择保存位置'); 354 | } catch (error) { 355 | console.error('Failed to download card:', error); 356 | showToast('生成卡片失败,请重试'); 357 | } 358 | } 359 | 360 | async function copyCard(cardElement) { 361 | try { 362 | showToast('正在生成卡片...'); 363 | 364 | // 创建一个容器来保持样式隔离 365 | const container = document.createElement('div'); 366 | container.style.position = 'fixed'; 367 | container.style.left = '-9999px'; 368 | container.style.top = '0'; 369 | container.style.width = cardElement.offsetWidth + 'px'; 370 | container.style.height = 'auto'; 371 | container.style.transform = 'none'; 372 | container.style.zIndex = '-1'; 373 | 374 | // 克隆卡片元素 375 | const clonedCard = cardElement.cloneNode(true); 376 | 377 | // 复制计算后的样式 378 | const computedStyle = window.getComputedStyle(cardElement); 379 | for (const prop of computedStyle) { 380 | clonedCard.style[prop] = computedStyle.getPropertyValue(prop); 381 | } 382 | 383 | // 确保背景色正确 384 | const isDarkMode = cardElement.getAttribute('data-style') === '2'; 385 | clonedCard.style.backgroundColor = isDarkMode ? '#2c2c2c' : '#ffffff'; 386 | clonedCard.style.position = 'relative'; 387 | clonedCard.style.left = '0'; 388 | clonedCard.style.top = '0'; 389 | clonedCard.style.transform = 'none'; 390 | clonedCard.style.margin = '0'; 391 | clonedCard.style.width = '100%'; 392 | 393 | // 复制子元素样式 394 | const sourceElements = cardElement.getElementsByTagName('*'); 395 | const clonedElements = clonedCard.getElementsByTagName('*'); 396 | for (let i = 0; i < sourceElements.length; i++) { 397 | const computedStyle = window.getComputedStyle(sourceElements[i]); 398 | for (const prop of computedStyle) { 399 | clonedElements[i].style[prop] = computedStyle.getPropertyValue(prop); 400 | } 401 | } 402 | 403 | container.appendChild(clonedCard); 404 | document.body.appendChild(container); 405 | 406 | const options = { 407 | useCORS: true, 408 | allowTaint: true, 409 | backgroundColor: isDarkMode ? '#2c2c2c' : '#ffffff', 410 | scale: 2, 411 | logging: false, 412 | width: cardElement.offsetWidth, 413 | height: cardElement.offsetHeight, 414 | removeContainer: true, 415 | foreignObjectRendering: false 416 | }; 417 | 418 | const canvas = await html2canvas(clonedCard, options); 419 | document.body.removeChild(container); 420 | 421 | canvas.toBlob(async (blob) => { 422 | try { 423 | const item = new ClipboardItem({ 'image/png': blob }); 424 | await navigator.clipboard.write([item]); 425 | showToast('卡片已复制到剪贴板'); 426 | } catch (error) { 427 | console.error('Failed to copy card:', error); 428 | showToast('复制失败,请重试'); 429 | } 430 | }); 431 | } catch (error) { 432 | console.error('Failed to create card image:', error); 433 | showToast('生成卡片失败,请重试'); 434 | } 435 | } 436 | 437 | // 格式化选中的文本,保留段落格式 438 | function formatSelectedText(text) { 439 | if (!text) return ''; 440 | 441 | // 分割文本为段落 442 | const paragraphs = text.split(/\n\s*\n/); 443 | 444 | // 处理每个段落 445 | return paragraphs 446 | .map(p => { 447 | // 处理单个段落中的换行 448 | const lines = p.trim().split('\n'); 449 | const processedParagraph = lines 450 | .map(line => line.trim()) 451 | .filter(line => line.length > 0) 452 | .join('
'); 453 | 454 | return processedParagraph ? `

${processedParagraph}

` : ''; 455 | }) 456 | .filter(p => p.length > 0) 457 | .join(''); 458 | } 459 | 460 | function showToast(message) { 461 | const toast = document.createElement('div'); 462 | toast.className = 'highlight-share-toast'; 463 | toast.textContent = message; 464 | document.body.appendChild(toast); 465 | 466 | setTimeout(() => { 467 | toast.classList.add('show'); 468 | setTimeout(() => { 469 | toast.classList.remove('show'); 470 | setTimeout(() => { 471 | document.body.removeChild(toast); 472 | }, 300); 473 | }, 2000); 474 | }, 100); 475 | } 476 | 477 | // 关闭模态框 478 | function closeModal() { 479 | console.log('Closing modal'); 480 | if (cardModal && document.body.contains(cardModal)) { 481 | document.body.removeChild(cardModal); 482 | cardModal = null; 483 | } 484 | } 485 | 486 | // 动态加载html2canvas 487 | const script = document.createElement('script'); 488 | script.src = '/html2canvas.min.js'; 489 | document.head.appendChild(script); 490 | -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeyHuazi/HighlightShare/ac2c93929cc66050b2018a77faf7f9633f64888a/icons/icon128.png -------------------------------------------------------------------------------- /icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeyHuazi/HighlightShare/ac2c93929cc66050b2018a77faf7f9633f64888a/icons/icon16.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeyHuazi/HighlightShare/ac2c93929cc66050b2018a77faf7f9633f64888a/icons/icon48.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "HighlightShare", 4 | "version": "1.0", 5 | "description": "快速将浏览器选中的文字和图片生成精美卡片", 6 | "permissions": [ 7 | "contextMenus", 8 | "activeTab", 9 | "downloads", 10 | "clipboardWrite" 11 | ], 12 | "host_permissions": [ 13 | "" 14 | ], 15 | "web_accessible_resources": [{ 16 | "resources": ["html2canvas.min.js"], 17 | "matches": [""] 18 | }], 19 | "action": { 20 | "default_icon": { 21 | "16": "icons/icon16.png", 22 | "48": "icons/icon48.png", 23 | "128": "icons/icon128.png" 24 | } 25 | }, 26 | "icons": { 27 | "16": "icons/icon16.png", 28 | "48": "icons/icon48.png", 29 | "128": "icons/icon128.png" 30 | }, 31 | "background": { 32 | "service_worker": "background.js" 33 | }, 34 | "content_scripts": [ 35 | { 36 | "matches": [""], 37 | "js": ["html2canvas.min.js", "content.js","qrcode.min.js"], 38 | "css": ["content.css"] 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /qrcode.min.js: -------------------------------------------------------------------------------- 1 | var QRCode; !function () { function a(a) { this.mode = c.MODE_8BIT_BYTE, this.data = a, this.parsedData = []; for (var b = [], d = 0, e = this.data.length; e > d; d++) { var f = this.data.charCodeAt(d); f > 65536 ? (b[0] = 240 | (1835008 & f) >>> 18, b[1] = 128 | (258048 & f) >>> 12, b[2] = 128 | (4032 & f) >>> 6, b[3] = 128 | 63 & f) : f > 2048 ? (b[0] = 224 | (61440 & f) >>> 12, b[1] = 128 | (4032 & f) >>> 6, b[2] = 128 | 63 & f) : f > 128 ? (b[0] = 192 | (1984 & f) >>> 6, b[1] = 128 | 63 & f) : b[0] = f, this.parsedData = this.parsedData.concat(b) } this.parsedData.length != this.data.length && (this.parsedData.unshift(191), this.parsedData.unshift(187), this.parsedData.unshift(239)) } function b(a, b) { this.typeNumber = a, this.errorCorrectLevel = b, this.modules = null, this.moduleCount = 0, this.dataCache = null, this.dataList = [] } function i(a, b) { if (void 0 == a.length) throw new Error(a.length + "/" + b); for (var c = 0; c < a.length && 0 == a[c];)c++; this.num = new Array(a.length - c + b); for (var d = 0; d < a.length - c; d++)this.num[d] = a[d + c] } function j(a, b) { this.totalCount = a, this.dataCount = b } function k() { this.buffer = [], this.length = 0 } function m() { return "undefined" != typeof CanvasRenderingContext2D } function n() { var a = !1, b = navigator.userAgent; return /android/i.test(b) && (a = !0, aMat = b.toString().match(/android ([0-9]\.[0-9])/i), aMat && aMat[1] && (a = parseFloat(aMat[1]))), a } function r(a, b) { for (var c = 1, e = s(a), f = 0, g = l.length; g >= f; f++) { var h = 0; switch (b) { case d.L: h = l[f][0]; break; case d.M: h = l[f][1]; break; case d.Q: h = l[f][2]; break; case d.H: h = l[f][3] }if (h >= e) break; c++ } if (c > l.length) throw new Error("Too long data"); return c } function s(a) { var b = encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g, "a"); return b.length + (b.length != a ? 3 : 0) } a.prototype = { getLength: function () { return this.parsedData.length }, write: function (a) { for (var b = 0, c = this.parsedData.length; c > b; b++)a.put(this.parsedData[b], 8) } }, b.prototype = { addData: function (b) { var c = new a(b); this.dataList.push(c), this.dataCache = null }, isDark: function (a, b) { if (0 > a || this.moduleCount <= a || 0 > b || this.moduleCount <= b) throw new Error(a + "," + b); return this.modules[a][b] }, getModuleCount: function () { return this.moduleCount }, make: function () { this.makeImpl(!1, this.getBestMaskPattern()) }, makeImpl: function (a, c) { this.moduleCount = 4 * this.typeNumber + 17, this.modules = new Array(this.moduleCount); for (var d = 0; d < this.moduleCount; d++) { this.modules[d] = new Array(this.moduleCount); for (var e = 0; e < this.moduleCount; e++)this.modules[d][e] = null } this.setupPositionProbePattern(0, 0), this.setupPositionProbePattern(this.moduleCount - 7, 0), this.setupPositionProbePattern(0, this.moduleCount - 7), this.setupPositionAdjustPattern(), this.setupTimingPattern(), this.setupTypeInfo(a, c), this.typeNumber >= 7 && this.setupTypeNumber(a), null == this.dataCache && (this.dataCache = b.createData(this.typeNumber, this.errorCorrectLevel, this.dataList)), this.mapData(this.dataCache, c) }, setupPositionProbePattern: function (a, b) { for (var c = -1; 7 >= c; c++)if (!(-1 >= a + c || this.moduleCount <= a + c)) for (var d = -1; 7 >= d; d++)-1 >= b + d || this.moduleCount <= b + d || (this.modules[a + c][b + d] = c >= 0 && 6 >= c && (0 == d || 6 == d) || d >= 0 && 6 >= d && (0 == c || 6 == c) || c >= 2 && 4 >= c && d >= 2 && 4 >= d ? !0 : !1) }, getBestMaskPattern: function () { for (var a = 0, b = 0, c = 0; 8 > c; c++) { this.makeImpl(!0, c); var d = f.getLostPoint(this); (0 == c || a > d) && (a = d, b = c) } return b }, createMovieClip: function (a, b, c) { var d = a.createEmptyMovieClip(b, c), e = 1; this.make(); for (var f = 0; f < this.modules.length; f++)for (var g = f * e, h = 0; h < this.modules[f].length; h++) { var i = h * e, j = this.modules[f][h]; j && (d.beginFill(0, 100), d.moveTo(i, g), d.lineTo(i + e, g), d.lineTo(i + e, g + e), d.lineTo(i, g + e), d.endFill()) } return d }, setupTimingPattern: function () { for (var a = 8; a < this.moduleCount - 8; a++)null == this.modules[a][6] && (this.modules[a][6] = 0 == a % 2); for (var b = 8; b < this.moduleCount - 8; b++)null == this.modules[6][b] && (this.modules[6][b] = 0 == b % 2) }, setupPositionAdjustPattern: function () { for (var a = f.getPatternPosition(this.typeNumber), b = 0; b < a.length; b++)for (var c = 0; c < a.length; c++) { var d = a[b], e = a[c]; if (null == this.modules[d][e]) for (var g = -2; 2 >= g; g++)for (var h = -2; 2 >= h; h++)this.modules[d + g][e + h] = -2 == g || 2 == g || -2 == h || 2 == h || 0 == g && 0 == h ? !0 : !1 } }, setupTypeNumber: function (a) { for (var b = f.getBCHTypeNumber(this.typeNumber), c = 0; 18 > c; c++) { var d = !a && 1 == (1 & b >> c); this.modules[Math.floor(c / 3)][c % 3 + this.moduleCount - 8 - 3] = d } for (var c = 0; 18 > c; c++) { var d = !a && 1 == (1 & b >> c); this.modules[c % 3 + this.moduleCount - 8 - 3][Math.floor(c / 3)] = d } }, setupTypeInfo: function (a, b) { for (var c = this.errorCorrectLevel << 3 | b, d = f.getBCHTypeInfo(c), e = 0; 15 > e; e++) { var g = !a && 1 == (1 & d >> e); 6 > e ? this.modules[e][8] = g : 8 > e ? this.modules[e + 1][8] = g : this.modules[this.moduleCount - 15 + e][8] = g } for (var e = 0; 15 > e; e++) { var g = !a && 1 == (1 & d >> e); 8 > e ? this.modules[8][this.moduleCount - e - 1] = g : 9 > e ? this.modules[8][15 - e - 1 + 1] = g : this.modules[8][15 - e - 1] = g } this.modules[this.moduleCount - 8][8] = !a }, mapData: function (a, b) { for (var c = -1, d = this.moduleCount - 1, e = 7, g = 0, h = this.moduleCount - 1; h > 0; h -= 2)for (6 == h && h--; ;) { for (var i = 0; 2 > i; i++)if (null == this.modules[d][h - i]) { var j = !1; g < a.length && (j = 1 == (1 & a[g] >>> e)); var k = f.getMask(b, d, h - i); k && (j = !j), this.modules[d][h - i] = j, e--, -1 == e && (g++, e = 7) } if (d += c, 0 > d || this.moduleCount <= d) { d -= c, c = -c; break } } } }, b.PAD0 = 236, b.PAD1 = 17, b.createData = function (a, c, d) { for (var e = j.getRSBlocks(a, c), g = new k, h = 0; h < d.length; h++) { var i = d[h]; g.put(i.mode, 4), g.put(i.getLength(), f.getLengthInBits(i.mode, a)), i.write(g) } for (var l = 0, h = 0; h < e.length; h++)l += e[h].dataCount; if (g.getLengthInBits() > 8 * l) throw new Error("code length overflow. (" + g.getLengthInBits() + ">" + 8 * l + ")"); for (g.getLengthInBits() + 4 <= 8 * l && g.put(0, 4); 0 != g.getLengthInBits() % 8;)g.putBit(!1); for (; ;) { if (g.getLengthInBits() >= 8 * l) break; if (g.put(b.PAD0, 8), g.getLengthInBits() >= 8 * l) break; g.put(b.PAD1, 8) } return b.createBytes(g, e) }, b.createBytes = function (a, b) { for (var c = 0, d = 0, e = 0, g = new Array(b.length), h = new Array(b.length), j = 0; j < b.length; j++) { var k = b[j].dataCount, l = b[j].totalCount - k; d = Math.max(d, k), e = Math.max(e, l), g[j] = new Array(k); for (var m = 0; m < g[j].length; m++)g[j][m] = 255 & a.buffer[m + c]; c += k; var n = f.getErrorCorrectPolynomial(l), o = new i(g[j], n.getLength() - 1), p = o.mod(n); h[j] = new Array(n.getLength() - 1); for (var m = 0; m < h[j].length; m++) { var q = m + p.getLength() - h[j].length; h[j][m] = q >= 0 ? p.get(q) : 0 } } for (var r = 0, m = 0; m < b.length; m++)r += b[m].totalCount; for (var s = new Array(r), t = 0, m = 0; d > m; m++)for (var j = 0; j < b.length; j++)m < g[j].length && (s[t++] = g[j][m]); for (var m = 0; e > m; m++)for (var j = 0; j < b.length; j++)m < h[j].length && (s[t++] = h[j][m]); return s }; for (var c = { MODE_NUMBER: 1, MODE_ALPHA_NUM: 2, MODE_8BIT_BYTE: 4, MODE_KANJI: 8 }, d = { L: 1, M: 0, Q: 3, H: 2 }, e = { PATTERN000: 0, PATTERN001: 1, PATTERN010: 2, PATTERN011: 3, PATTERN100: 4, PATTERN101: 5, PATTERN110: 6, PATTERN111: 7 }, f = { PATTERN_POSITION_TABLE: [[], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]], G15: 1335, G18: 7973, G15_MASK: 21522, getBCHTypeInfo: function (a) { for (var b = a << 10; f.getBCHDigit(b) - f.getBCHDigit(f.G15) >= 0;)b ^= f.G15 << f.getBCHDigit(b) - f.getBCHDigit(f.G15); return (a << 10 | b) ^ f.G15_MASK }, getBCHTypeNumber: function (a) { for (var b = a << 12; f.getBCHDigit(b) - f.getBCHDigit(f.G18) >= 0;)b ^= f.G18 << f.getBCHDigit(b) - f.getBCHDigit(f.G18); return a << 12 | b }, getBCHDigit: function (a) { for (var b = 0; 0 != a;)b++, a >>>= 1; return b }, getPatternPosition: function (a) { return f.PATTERN_POSITION_TABLE[a - 1] }, getMask: function (a, b, c) { switch (a) { case e.PATTERN000: return 0 == (b + c) % 2; case e.PATTERN001: return 0 == b % 2; case e.PATTERN010: return 0 == c % 3; case e.PATTERN011: return 0 == (b + c) % 3; case e.PATTERN100: return 0 == (Math.floor(b / 2) + Math.floor(c / 3)) % 2; case e.PATTERN101: return 0 == b * c % 2 + b * c % 3; case e.PATTERN110: return 0 == (b * c % 2 + b * c % 3) % 2; case e.PATTERN111: return 0 == (b * c % 3 + (b + c) % 2) % 2; default: throw new Error("bad maskPattern:" + a) } }, getErrorCorrectPolynomial: function (a) { for (var b = new i([1], 0), c = 0; a > c; c++)b = b.multiply(new i([1, g.gexp(c)], 0)); return b }, getLengthInBits: function (a, b) { if (b >= 1 && 10 > b) switch (a) { case c.MODE_NUMBER: return 10; case c.MODE_ALPHA_NUM: return 9; case c.MODE_8BIT_BYTE: return 8; case c.MODE_KANJI: return 8; default: throw new Error("mode:" + a) } else if (27 > b) switch (a) { case c.MODE_NUMBER: return 12; case c.MODE_ALPHA_NUM: return 11; case c.MODE_8BIT_BYTE: return 16; case c.MODE_KANJI: return 10; default: throw new Error("mode:" + a) } else { if (!(41 > b)) throw new Error("type:" + b); switch (a) { case c.MODE_NUMBER: return 14; case c.MODE_ALPHA_NUM: return 13; case c.MODE_8BIT_BYTE: return 16; case c.MODE_KANJI: return 12; default: throw new Error("mode:" + a) } } }, getLostPoint: function (a) { for (var b = a.getModuleCount(), c = 0, d = 0; b > d; d++)for (var e = 0; b > e; e++) { for (var f = 0, g = a.isDark(d, e), h = -1; 1 >= h; h++)if (!(0 > d + h || d + h >= b)) for (var i = -1; 1 >= i; i++)0 > e + i || e + i >= b || (0 != h || 0 != i) && g == a.isDark(d + h, e + i) && f++; f > 5 && (c += 3 + f - 5) } for (var d = 0; b - 1 > d; d++)for (var e = 0; b - 1 > e; e++) { var j = 0; a.isDark(d, e) && j++, a.isDark(d + 1, e) && j++, a.isDark(d, e + 1) && j++, a.isDark(d + 1, e + 1) && j++, (0 == j || 4 == j) && (c += 3) } for (var d = 0; b > d; d++)for (var e = 0; b - 6 > e; e++)a.isDark(d, e) && !a.isDark(d, e + 1) && a.isDark(d, e + 2) && a.isDark(d, e + 3) && a.isDark(d, e + 4) && !a.isDark(d, e + 5) && a.isDark(d, e + 6) && (c += 40); for (var e = 0; b > e; e++)for (var d = 0; b - 6 > d; d++)a.isDark(d, e) && !a.isDark(d + 1, e) && a.isDark(d + 2, e) && a.isDark(d + 3, e) && a.isDark(d + 4, e) && !a.isDark(d + 5, e) && a.isDark(d + 6, e) && (c += 40); for (var k = 0, e = 0; b > e; e++)for (var d = 0; b > d; d++)a.isDark(d, e) && k++; var l = Math.abs(100 * k / b / b - 50) / 5; return c += 10 * l } }, g = { glog: function (a) { if (1 > a) throw new Error("glog(" + a + ")"); return g.LOG_TABLE[a] }, gexp: function (a) { for (; 0 > a;)a += 255; for (; a >= 256;)a -= 255; return g.EXP_TABLE[a] }, EXP_TABLE: new Array(256), LOG_TABLE: new Array(256) }, h = 0; 8 > h; h++)g.EXP_TABLE[h] = 1 << h; for (var h = 8; 256 > h; h++)g.EXP_TABLE[h] = g.EXP_TABLE[h - 4] ^ g.EXP_TABLE[h - 5] ^ g.EXP_TABLE[h - 6] ^ g.EXP_TABLE[h - 8]; for (var h = 0; 255 > h; h++)g.LOG_TABLE[g.EXP_TABLE[h]] = h; i.prototype = { get: function (a) { return this.num[a] }, getLength: function () { return this.num.length }, multiply: function (a) { for (var b = new Array(this.getLength() + a.getLength() - 1), c = 0; c < this.getLength(); c++)for (var d = 0; d < a.getLength(); d++)b[c + d] ^= g.gexp(g.glog(this.get(c)) + g.glog(a.get(d))); return new i(b, 0) }, mod: function (a) { if (this.getLength() - a.getLength() < 0) return this; for (var b = g.glog(this.get(0)) - g.glog(a.get(0)), c = new Array(this.getLength()), d = 0; d < this.getLength(); d++)c[d] = this.get(d); for (var d = 0; d < a.getLength(); d++)c[d] ^= g.gexp(g.glog(a.get(d)) + b); return new i(c, 0).mod(a) } }, j.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]], j.getRSBlocks = function (a, b) { var c = j.getRsBlockTable(a, b); if (void 0 == c) throw new Error("bad rs block @ typeNumber:" + a + "/errorCorrectLevel:" + b); for (var d = c.length / 3, e = [], f = 0; d > f; f++)for (var g = c[3 * f + 0], h = c[3 * f + 1], i = c[3 * f + 2], k = 0; g > k; k++)e.push(new j(h, i)); return e }, j.getRsBlockTable = function (a, b) { switch (b) { case d.L: return j.RS_BLOCK_TABLE[4 * (a - 1) + 0]; case d.M: return j.RS_BLOCK_TABLE[4 * (a - 1) + 1]; case d.Q: return j.RS_BLOCK_TABLE[4 * (a - 1) + 2]; case d.H: return j.RS_BLOCK_TABLE[4 * (a - 1) + 3]; default: return void 0 } }, k.prototype = { get: function (a) { var b = Math.floor(a / 8); return 1 == (1 & this.buffer[b] >>> 7 - a % 8) }, put: function (a, b) { for (var c = 0; b > c; c++)this.putBit(1 == (1 & a >>> b - c - 1)) }, getLengthInBits: function () { return this.length }, putBit: function (a) { var b = Math.floor(this.length / 8); this.buffer.length <= b && this.buffer.push(0), a && (this.buffer[b] |= 128 >>> this.length % 8), this.length++ } }; var l = [[17, 14, 11, 7], [32, 26, 20, 14], [53, 42, 32, 24], [78, 62, 46, 34], [106, 84, 60, 44], [134, 106, 74, 58], [154, 122, 86, 64], [192, 152, 108, 84], [230, 180, 130, 98], [271, 213, 151, 119], [321, 251, 177, 137], [367, 287, 203, 155], [425, 331, 241, 177], [458, 362, 258, 194], [520, 412, 292, 220], [586, 450, 322, 250], [644, 504, 364, 280], [718, 560, 394, 310], [792, 624, 442, 338], [858, 666, 482, 382], [929, 711, 509, 403], [1003, 779, 565, 439], [1091, 857, 611, 461], [1171, 911, 661, 511], [1273, 997, 715, 535], [1367, 1059, 751, 593], [1465, 1125, 805, 625], [1528, 1190, 868, 658], [1628, 1264, 908, 698], [1732, 1370, 982, 742], [1840, 1452, 1030, 790], [1952, 1538, 1112, 842], [2068, 1628, 1168, 898], [2188, 1722, 1228, 958], [2303, 1809, 1283, 983], [2431, 1911, 1351, 1051], [2563, 1989, 1423, 1093], [2699, 2099, 1499, 1139], [2809, 2213, 1579, 1219], [2953, 2331, 1663, 1273]], o = function () { var a = function (a, b) { this._el = a, this._htOption = b }; return a.prototype.draw = function (a) { function g(a, b) { var c = document.createElementNS("http://www.w3.org/2000/svg", a); for (var d in b) b.hasOwnProperty(d) && c.setAttribute(d, b[d]); return c } var b = this._htOption, c = this._el, d = a.getModuleCount(); Math.floor(b.width / d), Math.floor(b.height / d), this.clear(); var h = g("svg", { viewBox: "0 0 " + String(d) + " " + String(d), width: "100%", height: "100%", fill: b.colorLight }); h.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"), c.appendChild(h), h.appendChild(g("rect", { fill: b.colorDark, width: "1", height: "1", id: "template" })); for (var i = 0; d > i; i++)for (var j = 0; d > j; j++)if (a.isDark(i, j)) { var k = g("use", { x: String(i), y: String(j) }); k.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template"), h.appendChild(k) } }, a.prototype.clear = function () { for (; this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild) }, a }(), p = "svg" === document.documentElement.tagName.toLowerCase(), q = p ? o : m() ? function () { function a() { this._elImage.src = this._elCanvas.toDataURL("image/png"), this._elImage.style.display = "block", this._elCanvas.style.display = "none" } function d(a, b) { var c = this; if (c._fFail = b, c._fSuccess = a, null === c._bSupportDataURI) { var d = document.createElement("img"), e = function () { c._bSupportDataURI = !1, c._fFail && _fFail.call(c) }, f = function () { c._bSupportDataURI = !0, c._fSuccess && c._fSuccess.call(c) }; return d.onabort = e, d.onerror = e, d.onload = f, d.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", void 0 } c._bSupportDataURI === !0 && c._fSuccess ? c._fSuccess.call(c) : c._bSupportDataURI === !1 && c._fFail && c._fFail.call(c) } if (this._android && this._android <= 2.1) { var b = 1 / window.devicePixelRatio, c = CanvasRenderingContext2D.prototype.drawImage; CanvasRenderingContext2D.prototype.drawImage = function (a, d, e, f, g, h, i, j) { if ("nodeName" in a && /img/i.test(a.nodeName)) for (var l = arguments.length - 1; l >= 1; l--)arguments[l] = arguments[l] * b; else "undefined" == typeof j && (arguments[1] *= b, arguments[2] *= b, arguments[3] *= b, arguments[4] *= b); c.apply(this, arguments) } } var e = function (a, b) { this._bIsPainted = !1, this._android = n(), this._htOption = b, this._elCanvas = document.createElement("canvas"), this._elCanvas.width = b.width, this._elCanvas.height = b.height, a.appendChild(this._elCanvas), this._el = a, this._oContext = this._elCanvas.getContext("2d"), this._bIsPainted = !1, this._elImage = document.createElement("img"), this._elImage.style.display = "none", this._el.appendChild(this._elImage), this._bSupportDataURI = null }; return e.prototype.draw = function (a) { var b = this._elImage, c = this._oContext, d = this._htOption, e = a.getModuleCount(), f = d.width / e, g = d.height / e, h = Math.round(f), i = Math.round(g); b.style.display = "none", this.clear(); for (var j = 0; e > j; j++)for (var k = 0; e > k; k++) { var l = a.isDark(j, k), m = k * f, n = j * g; c.strokeStyle = l ? d.colorDark : d.colorLight, c.lineWidth = 1, c.fillStyle = l ? d.colorDark : d.colorLight, c.fillRect(m, n, f, g), c.strokeRect(Math.floor(m) + .5, Math.floor(n) + .5, h, i), c.strokeRect(Math.ceil(m) - .5, Math.ceil(n) - .5, h, i) } this._bIsPainted = !0 }, e.prototype.makeImage = function () { this._bIsPainted && d.call(this, a) }, e.prototype.isPainted = function () { return this._bIsPainted }, e.prototype.clear = function () { this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height), this._bIsPainted = !1 }, e.prototype.round = function (a) { return a ? Math.floor(1e3 * a) / 1e3 : a }, e }() : function () { var a = function (a, b) { this._el = a, this._htOption = b }; return a.prototype.draw = function (a) { for (var b = this._htOption, c = this._el, d = a.getModuleCount(), e = Math.floor(b.width / d), f = Math.floor(b.height / d), g = [''], h = 0; d > h; h++) { g.push(""); for (var i = 0; d > i; i++)g.push(''); g.push("") } g.push("
"), c.innerHTML = g.join(""); var j = c.childNodes[0], k = (b.width - j.offsetWidth) / 2, l = (b.height - j.offsetHeight) / 2; k > 0 && l > 0 && (j.style.margin = l + "px " + k + "px") }, a.prototype.clear = function () { this._el.innerHTML = "" }, a }(); QRCode = function (a, b) { if (this._htOption = { width: 256, height: 256, typeNumber: 4, colorDark: "#000000", colorLight: "#ffffff", correctLevel: d.H }, "string" == typeof b && (b = { text: b }), b) for (var c in b) this._htOption[c] = b[c]; "string" == typeof a && (a = document.getElementById(a)), this._android = n(), this._el = a, this._oQRCode = null, this._oDrawing = new q(this._el, this._htOption), this._htOption.text && this.makeCode(this._htOption.text) }, QRCode.prototype.makeCode = function (a) { this._oQRCode = new b(r(a, this._htOption.correctLevel), this._htOption.correctLevel), this._oQRCode.addData(a), this._oQRCode.make(), this._el.title = a, this._oDrawing.draw(this._oQRCode), this.makeImage() }, QRCode.prototype.makeImage = function () { "function" == typeof this._oDrawing.makeImage && (!this._android || this._android >= 3) && this._oDrawing.makeImage() }, QRCode.prototype.clear = function () { this._oDrawing.clear() }, QRCode.CorrectLevel = d }(); --------------------------------------------------------------------------------