1(function(){ 2 /* ============================================= 3 æè¿è¦ãååã®ã¬ã³ã¡ã³ãï¼sps_creative_ver1.0ï¼ 4 ============================================= */ 5 6 const DISPLAY_ITEMS = 8; //ååã®æå¤§è¡¨ç¤ºæ°(åå) 7 const EXPIRE_DAYS = 7; //é²è¦§å±¥æ´ã®ä¿ææé(æ¥) 8 9 const SECTION_TITLE = "æè¿ãã§ãã¯ããåå"; //ã¿ã¤ãã«å 10 const TRACKING_PARAM = "from=recently_viewed"; //URLãã©ã¡ã¼ã¿ 11 12 const DEFAULT_SELECTOR = "#wrapper"; //表示ä½ç½® 13 const DEFAULT_POSITION = "after"; //å(before) å¾(after) 14 15 const STORAGE_KEY = "recently_viewed"; //ãã¼å 16 const HIDE_URL = ['order.php','regi.php']; //é表示 17 18 const PAGE_SETTINGS = [ //ãã¼ã¸ãã¨ã®è¡¨ç¤ºä½ç½®èª¿æ´ 19 { //ã¹ããå°ç¨ã«ã¼ã 20 match: (url) => url.includes("smp_cart.php"), 21 selector: "#contents", 22 position: "after" 23 }, 24 { //ã¹ãã¼ããã©ã³ãµã¤ã 25 match: (url) => url.includes("/smp"), 26 selector: "#contents", 27 position: "after" 28 }, 29 { //PCã«ã¼ã 30 match: (url) => url.includes("/cart.php"), 31 selector: '#EST_SPS_cart div[align="left"]:has(form[name="SiteSealReference"])', //SSLãã¼ã¯ 32 position: "before" 33 } 34 ]; 35 36 /* ============================================= 37 ãã¹ãã¢ã¼ãéå§ 38 ============================================= */ 39 40 const TEST_MODE_ONLY = false; //å ¬éæã¯false 41 42 function isTestMode() { 43 if (!TEST_MODE_ONLY) return true; 44 const params = new URLSearchParams(location.search); 45 return params.get("mode") === "recently_viewed"; 46 } 47 48 function loadTestCSS() { 49 if (!TEST_MODE_ONLY) return; 50 if (!isTestMode()) return; 51 if (document.querySelector('link[data-recently-viewed-css]')) return; 52 53 const link = document.createElement("link"); 54 link.rel = "stylesheet"; 55 link.href = "/css/recently_viewed.css"; 56 link.setAttribute("data-recently-viewed-css", "true"); 57 document.head.appendChild(link); 58 } 59 60 /* ============================================= 61 ãã¹ãã¢ã¼ãçµäº 62 ============================================= */ 63 64 let INITIAL_IMAGE_SRC = ""; 65 66 (function(){ 67 const img = 68 document.querySelector(".mainImg img") || 69 document.querySelector(".mainImg") || 70 document.querySelector("#mainImg img"); 71 72 if (!img) return; 73 74 let src = 75 img.getAttribute && ( 76 img.getAttribute("src") || 77 img.getAttribute("data-src") || 78 img.getAttribute("data-original") 79 ); 80 81 if (!src && img.src) src = img.src; 82 83 if (src) { 84 INITIAL_IMAGE_SRC = src; 85 } 86 })(); 87 88 89 const MAX_ITEMS = DISPLAY_ITEMS + 1; 90 91 function normalizeUrl(url) { 92 try { 93 const u = new URL(url, location.origin); 94 return u.origin + u.pathname; 95 } catch (e) { 96 return url.split('?')[0]; 97 } 98 } 99 100 function appendTrackingParam(url) { 101 if (!TRACKING_PARAM) return url; 102 const separator = url.includes("?") ? "&" : "?"; 103 return url + separator + TRACKING_PARAM; 104 } 105 106 function shouldHide() { 107 return HIDE_URL.some(p => location.href.includes(p)); 108 } 109 110 function normalizeImagePath(path) { 111 if (!path) return ""; 112 return path.replace(/\/simg\//g, "/"); 113 } 114 115 function toAbsolute(url) { 116 if (!url) return ""; 117 try { 118 return new URL(url, location.href).href; 119 } catch (e) { 120 return url; 121 } 122 } 123 124 function cleanPriceText(text){ 125 if(!text) return ""; 126 return text.replace(/\(ç¨è¾¼\)/g,"").trim(); 127 } 128 129 function getViewed() { 130 const now = Date.now(); 131 const expire = EXPIRE_DAYS * 24 * 60 * 60 * 1000; 132 133 let viewed = JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]"); 134 viewed = viewed.filter(v => !v.ts || now - v.ts <= expire); 135 136 localStorage.setItem(STORAGE_KEY, JSON.stringify(viewed)); 137 return viewed; 138 } 139 140 function saveProductIfProductPage() { 141 142 if (!document.querySelector("#itemDetail-wrap")) return; 143 if (!document.querySelector("#pi_cart h1")) return; 144 if (!document.querySelector("table.price")) return; 145 146 let retry = 0; 147 const maxRetry = 30; 148 149 const timer = setInterval(function(){ 150 151 const nameEl = document.querySelector("#pi_cart h1"); 152 153 if (!nameEl) { 154 retry++; 155 if (retry >= maxRetry) clearInterval(timer); 156 return; 157 } 158 159 clearInterval(timer); 160 161 const productName = nameEl.textContent.trim(); 162 if (!productName) return; 163
164 let imageSrc = INITIAL_IMAGE_SRC; 165 166 if (!imageSrc) { 167 const imgEl = 168 document.querySelector(".mainImg img") || 169 document.querySelector(".mainImg"); 170 171 if (imgEl) { 172 imageSrc = 173 imgEl.getAttribute("src") || 174 imgEl.getAttribute("data-src") || 175 imgEl.src; 176 } 177 } 178 179 if (!imageSrc) return; 180 181 imageSrc = toAbsolute(normalizeImagePath(imageSrc)); 182 183 let priceText = ""; 184 let priceClass = ""; 185 let discountRateText = ""; 186 187 const priceTable = document.querySelector("table.price"); 188 if (!priceTable) return; 189 190 const specialPriceEl = priceTable.querySelector(".special_price"); 191 const firstSelling = priceTable.querySelector(".selling_price"); 192 const firstDiscount = priceTable.querySelector(".discount_rate, .discount-rate"); 193 194 if (specialPriceEl) { 195 priceText = cleanPriceText(specialPriceEl.textContent.trim()); 196 priceClass = "special_price"; 197 } 198 else if (firstSelling) { 199 priceText = cleanPriceText(firstSelling.textContent.trim()); 200 priceClass = "selling_price"; 201 } 202 203 if (firstDiscount) { 204 discountRateText = firstDiscount.textContent.trim(); 205 } else { 206 discountRateText = ""; 207 } 208 209 const baseUrl = normalizeUrl(location.href); 210 211 let viewed = getViewed(); 212 viewed = viewed.filter(v => v.url !== baseUrl); 213 214 viewed.unshift({ 215 name: productName, 216 image: imageSrc, 217 price_text: priceText, 218 price_class: priceClass, 219 discount_rate: discountRateText, 220 url: baseUrl, 221 ts: Date.now() 222 }); 223 224 if (viewed.length > MAX_ITEMS) { 225 viewed = viewed.slice(0, MAX_ITEMS); 226 } 227 228 localStorage.setItem(STORAGE_KEY, JSON.stringify(viewed)); 229 230 }, 100); 231 } 232 233 function resolveInsertSetting() { 234 235 const url = location.href; 236 237 for (let i = 0; i < PAGE_SETTINGS.length; i++) { 238 if (PAGE_SETTINGS[i].match(url)) { 239 return PAGE_SETTINGS[i]; 240 } 241 } 242 243 return { 244 selector: DEFAULT_SELECTOR, 245 position: DEFAULT_POSITION 246 }; 247 } 248 249 function insertSection(section) { 250 251 const setting = resolveInsertSetting(); 252 const target = document.querySelector(setting.selector); 253 if (!target || !target.parentNode) return; 254 255 if (setting.position === "before") { 256 target.parentNode.insertBefore(section, target); 257 } else { 258 target.parentNode.insertBefore(section, target.nextSibling); 259 } 260 } 261 262 function render() { 263 264 if (!isTestMode()) return; 265 if (shouldHide()) return; 266 267 loadTestCSS(); 268 269 const existing = document.querySelector(".recentlyViewed"); 270 if (existing) existing.remove(); 271 272 const currentUrl = normalizeUrl(location.href); 273 const viewed = getViewed(); 274 275 const displayItems = viewed 276 .filter(v => v.url !== currentUrl) 277 .slice(0, DISPLAY_ITEMS); 278 279 if (!displayItems.length) return; 280 281 const section = document.createElement("section"); 282 section.className = "recentlyViewed"; 283 section.innerHTML = ` 284 <div class="recentlyViewed__inner"> 285 <h2 class="section__title">${SECTION_TITLE}</h2> 286 <div class="product__lists"></div> 287 </div> 288 `; 289 290 const listsDiv = section.querySelector(".product__lists"); 291 292 displayItems.forEach(item => { 293 294 const linkUrl = appendTrackingParam(item.url); 295 296 const priceHtml = item.price_text 297 ? ` 298 <p class="item-price"> 299 <span class="${item.price_class}"> 300 ${item.price_text}(ç¨è¾¼) 301 </span> 302 ${item.discount_rate 303 ? `<span class="discount-icon">${item.discount_rate}</span>` 304 : ""} 305 </p> 306 ` 307 : ""; 308 309 const div = document.createElement("div"); 310 div.className = "recent-item"; 311 div.innerHTML = ` 312 <a href="${linkUrl}"> 313 <p class="item-img"> 314 <img src="${item.image}" alt="${item.name}"> 315 </p> 316 <p class="item-name">${item.name}</p> 317 ${priceHtml} 318 </a> 319 `; 320 321 listsDiv.appendChild(div); 322 }); 323 324 insertSection(section); 325 } 326 327 document.addEventListener("DOMContentLoaded", function () { 328 saveProductIfProductPage(); 329 render(); 330 }); 331 332})(); 333 334// æåçç¥å¯¾å¿ 335document.addEventListener('DOMContentLoaded', function () { 336 337 // æåå¹ ãèæ ®ãã¦ã«ãã 338 function truncateByWidth(str, maxWidth) { 339 let width = 0; 340 let result = ''; 341 342 for (let char of str) { 343 // å ¨è§å¤å®ï¼ãã£ããï¼ 344 const charWidth = (char.match(/[^\x01-\x7E]/)) ? 2 : 1; 345 346 if (width + charWidth > maxWidth) { 347 return result + '...'; 348 } 349 350 width += charWidth; 351 result += char; 352 } 353 354 return result; 355 } 356 357 function truncateText() { 358 const targets = document.querySelectorAll('.column-set h3 a, section.recentlyViewed p.item-name'); 359 360 if (window.innerWidth <= 768) {
361 targets.forEach(function(el) { 362 363 // å ããã¹ãä¿æ 364 if (!el.dataset.originalText) { 365 el.dataset.originalText = el.textContent.trim(); 366 } 367 368 const text = el.dataset.originalText; 369 370 // æåæ°èª¿æ´ 371 el.textContent = truncateByWidth(text, 42); 372 373 }); 374 } else { 375 // PCæ»ã 376 targets.forEach(function(el) { 377 if (el.dataset.originalText) { 378 el.textContent = el.dataset.originalText; 379 } 380 }); 381 } 382 } 383 384 truncateText(); 385 window.addEventListener('resize', truncateText); 386 387});
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.