├── .gitignore ├── bin ├── background.js ├── icon_128.png ├── icon_128_pressed.png ├── icon_32.png ├── inject.js └── manifest.json ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── addStyle.js ├── app.js ├── background.js ├── dom-pick │ ├── element-overlay.d.ts │ ├── element-overlay.js │ ├── element-picker.d.ts │ ├── element-picker.js │ ├── index.d.ts │ ├── index.js │ ├── utils.d.ts │ └── utils.js ├── icon_128.png ├── icon_128_pressed.png ├── icon_32.png ├── index.js ├── info.js ├── manifest.json └── scrap.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /bin/background.js: -------------------------------------------------------------------------------- 1 | const updateIcon = async isPressed => { 2 | await chrome.action.setIcon({ path: `icon_128${isPressed ? "_pressed" : ""}.png` }); 3 | } 4 | 5 | const sendMessage = data => { 6 | return new Promise(resolve => { 7 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 8 | if (tabs.length === 0) resolve(); 9 | chrome.tabs.sendMessage(tabs[0].id, data, function(response) { 10 | resolve(response); 11 | }); 12 | }); 13 | }); 14 | } 15 | 16 | const generateCeID = () => { 17 | return Date.now() + '_' + parseInt(Math.random() * 10000) + '_' + parseInt(Math.random() * 10000); 18 | } 19 | 20 | const getCeID = () => { 21 | return new Promise(resolve => { 22 | chrome.storage.local.get(["ce_id"], async function(result){ 23 | if (result.ce_id && result.ce_id.length > 10) return resolve(result.ce_id); 24 | const ce_id = generateCeID(); 25 | chrome.storage.local.set({ ce_id }, async function(result){ 26 | resolve(ce_id) 27 | }); 28 | }); 29 | }); 30 | } 31 | 32 | const getCurrentState = async () => { 33 | return await sendMessage({ type: 'get_state' }); 34 | } 35 | 36 | const toggleState = async () => { 37 | return await sendMessage({ type: 'toggle_state' }); 38 | } 39 | 40 | const currentTabChanged = async () => { 41 | await updateIcon(await getCurrentState()); 42 | const ce_id = await getCeID(); 43 | await sendMessage({ type: 'ce_id', ce_id }); 44 | } 45 | 46 | chrome.tabs.onUpdated.addListener(async function(tabId, changeInfo, tab) { 47 | if (changeInfo.status === 'complete') { 48 | await currentTabChanged(); 49 | } 50 | }); 51 | 52 | chrome.tabs.onActivated.addListener(async tab => { 53 | await currentTabChanged(); 54 | }); 55 | 56 | chrome.runtime.onInstalled.addListener(async ({ reason, version }) => { 57 | if (reason === chrome.runtime.OnInstalledReason.INSTALL) { 58 | } 59 | }); 60 | 61 | chrome.action.onClicked.addListener(async (tab) => { 62 | const state = await toggleState(); 63 | await updateIcon(state); 64 | }); 65 | 66 | chrome.runtime.onMessage.addListener( 67 | async function(request, sender, sendResponse) { //sender.tab 68 | switch(request.type) { 69 | case 'init': 70 | sendResponse({ type: "init" }); 71 | break; 72 | case 'close': 73 | sendResponse({ type: "close" }); 74 | await updateIcon(false); 75 | break; 76 | } 77 | } 78 | ); 79 | 80 | -------------------------------------------------------------------------------- /bin/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AAA0109/DomPicker_Extension/563b131f70fa977fe29ffc16e414c3ec75c98afc/bin/icon_128.png -------------------------------------------------------------------------------- /bin/icon_128_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AAA0109/DomPicker_Extension/563b131f70fa977fe29ffc16e414c3ec75c98afc/bin/icon_128_pressed.png -------------------------------------------------------------------------------- /bin/icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AAA0109/DomPicker_Extension/563b131f70fa977fe29ffc16e414c3ec75c98afc/bin/icon_32.png -------------------------------------------------------------------------------- /bin/inject.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | /** 5 | * Checks if `value` is the 6 | * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) 7 | * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) 8 | * 9 | * @static 10 | * @memberOf _ 11 | * @since 0.1.0 12 | * @category Lang 13 | * @param {*} value The value to check. 14 | * @returns {boolean} Returns `true` if `value` is an object, else `false`. 15 | * @example 16 | * 17 | * _.isObject({}); 18 | * // => true 19 | * 20 | * _.isObject([1, 2, 3]); 21 | * // => true 22 | * 23 | * _.isObject(_.noop); 24 | * // => true 25 | * 26 | * _.isObject(null); 27 | * // => false 28 | */ 29 | 30 | var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 31 | 32 | /** Detect free variable `global` from Node.js. */ 33 | var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; 34 | 35 | var _freeGlobal = freeGlobal; 36 | 37 | /** Detect free variable `self`. */ 38 | var freeSelf = typeof self == 'object' && self && self.Object === Object && self; 39 | 40 | /** Used as a reference to the global object. */ 41 | var root = _freeGlobal || freeSelf || Function('return this')(); 42 | 43 | var _root = root; 44 | 45 | /** Used to match a single whitespace character. */ 46 | 47 | /** Built-in value references. */ 48 | var Symbol = _root.Symbol; 49 | 50 | var _Symbol = Symbol; 51 | 52 | /** Built-in value references. */ 53 | var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined; 54 | 55 | /** Used for built-in method references. */ 56 | 57 | /** Built-in value references. */ 58 | var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined; 59 | 60 | /** 61 | * Checks if `value` is object-like. A value is object-like if it's not `null` 62 | * and has a `typeof` result of "object". 63 | * 64 | * @static 65 | * @memberOf _ 66 | * @since 4.0.0 67 | * @category Lang 68 | * @param {*} value The value to check. 69 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`. 70 | * @example 71 | * 72 | * _.isObjectLike({}); 73 | * // => true 74 | * 75 | * _.isObjectLike([1, 2, 3]); 76 | * // => true 77 | * 78 | * _.isObjectLike(_.noop); 79 | * // => false 80 | * 81 | * _.isObjectLike(null); 82 | * // => false 83 | */ 84 | 85 | class ElementOverlay { 86 | constructor(options) { 87 | var _a, _b, _c, _d, _e, _f, _g, _h, _j; 88 | this.overlay = document.createElement("div"); 89 | this.overlay.className = options.className || "_ext-element-overlay"; 90 | this.overlay.style.background = ((_a = options.style) === null || _a === void 0 ? void 0 : _a.background) || "rgba(250, 240, 202, 0.2)"; 91 | this.overlay.style.borderColor = ((_b = options.style) === null || _b === void 0 ? void 0 : _b.borderColor) || "#F95738"; 92 | this.overlay.style.borderStyle = ((_c = options.style) === null || _c === void 0 ? void 0 : _c.borderStyle) || "solid"; 93 | this.overlay.style.borderRadius = ((_d = options.style) === null || _d === void 0 ? void 0 : _d.borderRadius) || "1px"; 94 | this.overlay.style.borderWidth = ((_e = options.style) === null || _e === void 0 ? void 0 : _e.borderWidth) || "1px"; 95 | this.overlay.style.boxSizing = ((_f = options.style) === null || _f === void 0 ? void 0 : _f.boxSizing) || "border-box"; 96 | this.overlay.style.cursor = ((_g = options.style) === null || _g === void 0 ? void 0 : _g.cursor) || "crosshair"; 97 | this.overlay.style.position = ((_h = options.style) === null || _h === void 0 ? void 0 : _h.position) || "absolute"; 98 | this.overlay.style.zIndex = ((_j = options.style) === null || _j === void 0 ? void 0 : _j.zIndex) || "2147483647"; 99 | // this.overlay.style.transition = "all .2s linear"; 100 | this.shadowContainer = document.createElement("div"); 101 | this.shadowContainer.className = "_ext-element-overlay-container"; 102 | this.shadowContainer.style.position = "absolute"; 103 | this.shadowContainer.style.top = "0px"; 104 | this.shadowContainer.style.left = "0px"; 105 | this.shadowRoot = this.shadowContainer.attachShadow({ mode: "open" }); 106 | } 107 | addToDOM(parent, useShadowDOM) { 108 | this.usingShadowDOM = useShadowDOM; 109 | if (useShadowDOM) { 110 | parent.insertBefore(this.shadowContainer, parent.firstChild); 111 | this.shadowRoot.appendChild(this.overlay); 112 | } 113 | else { 114 | parent.appendChild(this.overlay); 115 | } 116 | } 117 | removeFromDOM() { 118 | this.setBounds({ x: 0, y: 0, width: 0, height: 0 }); 119 | this.overlay.remove(); 120 | if (this.usingShadowDOM) { 121 | this.shadowContainer.remove(); 122 | } 123 | } 124 | captureCursor() { 125 | this.overlay.style.pointerEvents = "auto"; 126 | } 127 | ignoreCursor() { 128 | this.overlay.style.pointerEvents = "none"; 129 | } 130 | setBounds({ x, y, width, height }) { 131 | this.overlay.style.left = x + "px"; 132 | this.overlay.style.top = y + "px"; 133 | this.overlay.style.width = width + "px"; 134 | this.overlay.style.height = height + "px"; 135 | } 136 | getBounds() { 137 | return { 138 | x: parseFloat(this.overlay.style.left), 139 | y: parseFloat(this.overlay.style.top), 140 | width: parseFloat(this.overlay.style.width), 141 | height: parseFloat(this.overlay.style.height), 142 | } 143 | } 144 | } 145 | 146 | const getElementBounds = (el) => { 147 | const rect = el.getBoundingClientRect(); 148 | return { 149 | x: window.pageXOffset + rect.left, 150 | y: window.pageYOffset + rect.top, 151 | width: el.offsetWidth, 152 | height: el.offsetHeight, 153 | }; 154 | }; 155 | 156 | const checkSimilarBounds = (b1, b2) => { 157 | const keys = ['x', 'y', 'width', 'height']; 158 | for (let i = 0 ; i < keys.length; i ++) { 159 | if (Math.abs(b1[keys[i]] - b2[keys[i]]) > 0.1 ) 160 | return false; 161 | } 162 | return true; 163 | }; 164 | 165 | class ElementPicker { 166 | constructor(overlayOptions) { 167 | this.handleMouseMove = (event) => { 168 | this.mouseX = event.clientX; 169 | this.mouseY = event.clientY; 170 | }; 171 | this.handleClick = (event) => { 172 | var _a; 173 | if (this.target && this.target.getAttribute('gsallow')) return; 174 | if (this.target && ((_a = this.options) === null || _a === void 0 ? void 0 : _a.onClick)) { 175 | this.options.onClick(this.target); 176 | } 177 | event.preventDefault(); 178 | }; 179 | this.tick = () => { 180 | this.updateTarget(); 181 | this.tickReq = window.requestAnimationFrame(this.tick); 182 | }; 183 | this.active = false; 184 | this.overlay = new ElementOverlay(overlayOptions !== null && overlayOptions !== void 0 ? overlayOptions : {}); 185 | } 186 | start(options) { 187 | var _a, _b; 188 | if (this.active) { 189 | return false; 190 | } 191 | this.active = true; 192 | this.options = options; 193 | document.addEventListener("mousemove", this.handleMouseMove, true); 194 | document.addEventListener("click", this.handleClick, true); 195 | this.overlay.addToDOM((_a = options.parentElement) !== null && _a !== void 0 ? _a : document.body, (_b = options.useShadowDOM) !== null && _b !== void 0 ? _b : true); 196 | this.tick(); 197 | return true; 198 | } 199 | stop() { 200 | this.active = false; 201 | this.options = undefined; 202 | document.removeEventListener("mousemove", this.handleMouseMove, true); 203 | document.removeEventListener("click", this.handleClick, true); 204 | this.overlay.removeFromDOM(); 205 | this.target = undefined; 206 | this.mouseX = undefined; 207 | this.mouseY = undefined; 208 | if (this.tickReq) { 209 | window.cancelAnimationFrame(this.tickReq); 210 | } 211 | } 212 | updateTarget() { 213 | var _a, _b; 214 | if (this.mouseX === undefined || this.mouseY === undefined) { 215 | return; 216 | } 217 | // Peek through the overlay to find the new target 218 | this.overlay.ignoreCursor(); 219 | const elAtCursor = document.elementFromPoint(this.mouseX, this.mouseY); 220 | const newTarget = elAtCursor; 221 | this.overlay.captureCursor(); 222 | // If the target hasn't changed, there's nothing to do 223 | if (!newTarget) return; 224 | // If we have an element filter and the new target doesn't match, 225 | // clear out the target 226 | if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.elementFilter) { 227 | if (!this.options.elementFilter(newTarget)) { 228 | this.target = undefined; 229 | this.overlay.setBounds({ x: 0, y: 0, width: 0, height: 0 }); 230 | return; 231 | } 232 | } 233 | 234 | const bounds = getElementBounds(newTarget); 235 | if (newTarget === this.target) { 236 | const ori_bounds = this.overlay.getBounds(); 237 | if (checkSimilarBounds(bounds, ori_bounds)) 238 | return ; 239 | } 240 | 241 | this.target = newTarget; 242 | this.overlay.setBounds(bounds); 243 | if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.onHover) { 244 | this.options.onHover(newTarget); 245 | } 246 | } 247 | } 248 | 249 | const addStyle = style => { 250 | const styleTag = document.createElement('style'); 251 | styleTag.innerHTML = style; 252 | document.head.appendChild(styleTag); 253 | }; 254 | 255 | const Constant = { 256 | title: ['title', 'name'], 257 | price: ['price'], 258 | description: ['description', 'detail', 'info'] 259 | }; 260 | 261 | const checkIfSimilarProductContainer = (el, attrs = []) => { 262 | const area_limit = 80 * 80, txt_limit_ct = 2; 263 | var txt_ct = 0; 264 | const itms = el.getElementsByTagName('*'); 265 | for (let i = 0; i < itms.length; i ++) { 266 | if (getText(itms[i])) txt_ct ++; 267 | if (txt_ct > txt_limit_ct) break; 268 | } 269 | if (txt_ct < txt_limit_ct) return false; 270 | 271 | const imgs = el.getElementsByTagName('img'); 272 | let i = 0; 273 | for (i = 0; i < imgs.length; i ++) { 274 | const img = imgs[i]; 275 | const area = img.width * img.height; 276 | if (area < area_limit) continue; 277 | break; 278 | } 279 | if (i === imgs.length) return false; 280 | if (!attrs.length) return true; 281 | 282 | const htmlStr = (el.innerHTML || '').toLocaleLowerCase(); 283 | i = 0; 284 | for (i = 0; i < attrs.length; i ++) { 285 | let j = 0; 286 | for (j = 0; j < attrs[i].length; j ++) 287 | if(htmlStr.includes(attrs[i][j])) 288 | break; 289 | if (j === attrs[i].length) break; 290 | } 291 | 292 | if (i && i === attrs.length) return true; 293 | return false; 294 | }; 295 | 296 | const checkIfSimilarItem = (a, b) => { 297 | if (!checkIfSimilarProductContainer(a) || !checkIfSimilarProductContainer(b)) return 0; 298 | const tag1 = a.tagName, tag2 = b.tagName; 299 | if (tag1.toLocaleLowerCase() !== tag2.toLocaleLowerCase()) return 0; 300 | const attr1 = a.attributes, attr2 = b.attributes; 301 | let ct = 0; 302 | for (let i = 0; i < attr1.length; i ++) { 303 | const attr = attr1[i].name || ''; 304 | if (!attr) continue; 305 | let j = 0; 306 | for (j = 0; j < attr2.length; j ++) { 307 | if (attr2[j].name == attr) break; 308 | } 309 | if (j === attr2.length) { 310 | continue; 311 | } 312 | ct ++; 313 | } 314 | let rate = Math.min(((ct * 2) / (attr1.length + attr2.length)) * 1.5, 1); 315 | if (attr1.length + attr2.length === 0) rate = 1; 316 | return rate; 317 | }; 318 | 319 | const checkIfListContainer = el => { 320 | const t = 0.9; 321 | let p = el.parentNode; 322 | while (p && p.parentNode) { 323 | const pp = p.parentNode; 324 | const chs = pp.children; 325 | let ct = 0; 326 | for (let i = 0; i < chs.length; i ++) { 327 | let max = 0; 328 | for (let j = 0; j < chs.length; j ++) { 329 | if (i === j) continue; 330 | const a = chs[i], b = chs[j]; 331 | const ret = checkIfSimilarItem(a, b); 332 | max = Math.max(max, ret); 333 | if (max >= t) break; 334 | } 335 | if (max < t) ct ++; 336 | if (ct > 1) break; 337 | } 338 | if (ct < 2 && chs.length > 2) return p; 339 | p = p.parentNode; 340 | } 341 | return null; 342 | }; 343 | 344 | const getProductRootElement = el => { 345 | const check_list = checkIfListContainer(el); 346 | if (check_list) return check_list; 347 | if (checkIfSimilarProductContainer(el, [Constant.title])) return el; 348 | let p = el.parentNode; 349 | while (p && p.tagName.toLocaleLowerCase() !== 'html') { 350 | if (checkIfSimilarProductContainer(p, [Constant.title])) return p; 351 | p = p.parentNode; 352 | } 353 | return el; 354 | }; 355 | 356 | const isVisible = el => { 357 | const style = window.getComputedStyle(el); 358 | if (style.opacity === '0') return false; 359 | if (style.visibility == 'hidden') return false; 360 | const r = el.getBoundingClientRect(); 361 | if (r.width < 10 || r.height < 10) return false; 362 | return true; 363 | }; 364 | 365 | const checkIfHasAttribute = (el, attr) => { 366 | const keys = el.attributes; 367 | for (let i = 0; i < keys.length; i ++) { 368 | const key = keys[i].name || ''; 369 | const value = el.getAttribute(key) || ''; 370 | if (key.toLocaleLowerCase().includes(attr) || value.toLocaleLowerCase().includes(attr)) return true; 371 | } 372 | return false; 373 | }; 374 | 375 | const checkIfDescendOf = (ch, p, signs) => { 376 | while(ch && ch !== p) { 377 | for (let i = 0; i < signs.length; i ++) 378 | if (checkIfHasAttribute(ch, signs[i])) 379 | return true; 380 | ch = ch.parentNode; 381 | } 382 | return false; 383 | }; 384 | 385 | const isHovered = (r, e) => { 386 | const x = e.x, y = e.y; 387 | if (r.left <= x && r.right >= x && r.top <= y && r.bottom >= y) return true; 388 | return false; 389 | }; 390 | 391 | const checkIfBetterImg = (a, b, mouse, excepts = []) => { 392 | if (!isVisible(a)) return false; 393 | if (!isVisible(b)) return true; 394 | const a_src = getSrcFromImgTag(a), b_src = getSrcFromImgTag(b); 395 | if (!a_src) return false; 396 | if (!b_src) return true; 397 | const excepts_src = excepts.map(itm => getSrcFromImgTag(itm)); 398 | if (excepts_src.includes(a_src)) return false; 399 | if (excepts_src.includes(b_src)) return true; 400 | 401 | const offset = 2; 402 | const r1 = a.getBoundingClientRect(), r2 = b.getBoundingClientRect(); 403 | const h1 = isHovered(r1, mouse), h2 = isHovered(r2, mouse); 404 | if (h1 && !h2) return true; 405 | if (!h1 && h2) return false; 406 | 407 | const area1 = r1.width * r1.height, area2 = r2.width * r2.height; 408 | if (Math.abs(area1 - area2) < offset * offset) { 409 | if (Math.abs(r1.x - r2.x) < offset && Math.abs(r1.y - r2.y) < offset) return true; 410 | } 411 | if (area1 > area2) return true; 412 | return false; 413 | }; 414 | 415 | const containsAnyLetters = str => { 416 | return /[a-zA-Z0-9]/.test(str); 417 | }; 418 | 419 | const getText = el => { 420 | if (!el) return ''; 421 | if (['noscript', 'img'].indexOf(el.nodeName.toLocaleLowerCase()) > -1) return ''; 422 | if (!isVisible(el)) return false; 423 | try { 424 | const childNodes = el.childNodes; 425 | var hasText = false; 426 | for (let i = 0; i < childNodes.length; i++) { 427 | if (childNodes[i].nodeType === Node.TEXT_NODE) { 428 | var txt = childNodes[i].textContent; 429 | if (!containsAnyLetters(txt)) continue; 430 | hasText = true; 431 | break; 432 | } 433 | } 434 | if (hasText) return (el.innerText || el.textContent || '').replace(/\n/g, ''); 435 | return '' 436 | } catch (e) { 437 | return ''; 438 | } 439 | }; 440 | 441 | const getFText = el => { 442 | if (!el) return ''; 443 | return (el.innerText || el.textContent || '').replace(/\n\n/g, '\n'); 444 | }; 445 | 446 | const getEnteredText = el => { 447 | if (!el) return ''; 448 | return (el.innerText || el.textContent || '').replace(/\n\n/g, '\n').replace(/\n/g, '\n'); //• 449 | }; 450 | 451 | const checkIfBetterTitle = (a, b, p) => { 452 | const txt1 = getText(a), txt2 = getText(b); 453 | if (txt1 && !txt2) return true; 454 | if (!txt1) return false; 455 | 456 | const des1 = checkIfDescendOf(a, p, Constant.title), des2 = checkIfDescendOf(b, p, Constant.title); 457 | if (des1 && !des2) return true; 458 | if (!des1 && des2) return false; 459 | 460 | const fontSize1 = parseFloat(window.getComputedStyle(a).fontSize) || 0, 461 | fontSize2 = parseFloat(window.getComputedStyle(b).fontSize) || 0; 462 | 463 | if (fontSize1 > fontSize2 * 1.2) return true; 464 | return false; 465 | }; 466 | 467 | const getCurrencyNumber = (str) => { 468 | try { 469 | return parseFloat(str.replace(/[^0-9.]+/g,"")) || 0; 470 | } catch (ex) { 471 | return 0; 472 | } 473 | }; 474 | 475 | const checkIfBetterPrice = (a, b, p) => { 476 | const txt1 = getText(a), txt2 = getText(b); 477 | const isPrice1 = checkIfPrice(txt1), isPrice2 = checkIfPrice(txt2); 478 | if (isPrice1 && !isPrice2) return true; 479 | if (!isPrice1) return false; 480 | 481 | const des1 = checkIfDescendOf(a, p, Constant.price), des2 = checkIfDescendOf(b, p, Constant.price); 482 | if (des1 && !des2) return true; 483 | if (!des1 && des2) return false; 484 | 485 | return false; 486 | }; 487 | 488 | const checkIfBetterDescription = (a, b, p) => { 489 | const txt1 = getText(a), txt2 = getText(b); 490 | if (txt1 && !txt2) return true; 491 | if (!txt1) return false; 492 | 493 | // const des1 = checkIfDescendOf(a, p, Constant.description), des2 = checkIfDescendOf(b, p, Constant.description) 494 | // if (des1 && !des2) return true; 495 | // if (!des1 && des2) return false; 496 | 497 | if (txt1.length > txt2.length) return true; 498 | return false; 499 | }; 500 | 501 | const findHref = el => { 502 | var p = el; 503 | while(p && p.tagName.toLocaleLowerCase() !== 'html') { 504 | if ((p.tagName.toLocaleLowerCase() === 'a' || p.tagName.toLocaleLowerCase === 'button') && p.href) return p.href; 505 | p = p.parentNode; 506 | } 507 | return ''; 508 | }; 509 | 510 | const getImgUrl = (el, mouse, excepts = []) => { 511 | if (!el) return ''; 512 | if (el.tagName.toLocaleLowerCase() === 'img') return el; 513 | const imgs = el.getElementsByTagName('img'); 514 | if (!imgs.length) return ''; 515 | 516 | var ret = imgs[0]; 517 | for (let i = 1; i < imgs.length; i ++) { 518 | if (checkIfBetterImg(imgs[i], ret, mouse, excepts)) ret = imgs[i]; 519 | } 520 | return ret; 521 | }; 522 | 523 | const getManualImgUrl = (el, mouse) => { 524 | while(el.tagName !== 'html') { 525 | const img = getImgUrl(el, mouse); 526 | if (img) return img; 527 | el = el.parentNode; 528 | } 529 | return null; 530 | }; 531 | 532 | const getName = (el) => { 533 | const itms = el.getElementsByTagName("*"); 534 | var ret = itms[0]; 535 | for (let i = 1; i < itms.length; i ++) { 536 | if (checkIfBetterTitle(itms[i], ret, el)) ret = itms[i]; 537 | } 538 | return ret; 539 | }; 540 | 541 | const checkIfPrice = (p) => { 542 | if (!p) return false; 543 | let d = p.replace(/ |\n|,/g, ''); 544 | d = d.replace('$', ''); 545 | if (!d) return false; 546 | for (let i = 0; i < d.length; i ++) if (d[i] !== '.' && !(d[i] >= '0' && d[i] <= '9')) return false; 547 | return true; 548 | }; 549 | 550 | const getPrice = (el) => { 551 | const itms = el.getElementsByTagName("*"); 552 | var ret = itms[0]; 553 | for (let i = 1; i < itms.length; i ++) { 554 | if (checkIfBetterPrice(itms[i], ret, el)) ret = itms[i]; 555 | } 556 | return ret; 557 | }; 558 | 559 | const getDescriptin = (el) => { 560 | const itms = el.getElementsByTagName("*"); 561 | var ret = itms[0]; 562 | for (let i = 1; i < itms.length; i ++) { 563 | if (checkIfBetterDescription(itms[i], ret, el)) ret = itms[i]; 564 | } 565 | return ret; 566 | }; 567 | 568 | const getPhotos = (el, mouse, photo) => { 569 | const ret = [photo]; 570 | for (let i = 0; i < 4; i ++) { 571 | const img = getImgUrl(el, mouse, ret); 572 | if (ret.findIndex(itm => getSrcFromImgTag(itm).split('?')[0] === getSrcFromImgTag(img).split('?')[0]) > -1) break; 573 | ret.push(img); 574 | } 575 | ret.shift(); 576 | return ret; 577 | }; 578 | 579 | const getUrl = (el) => { 580 | if (!el) return ''; 581 | return findHref(el); 582 | }; 583 | 584 | const getSrcFromImgTag = (el) => { 585 | if (!el) return ''; 586 | return (el.currentSrc || el.src || '').split(' ')[0] 587 | }; 588 | 589 | const getProductInfo = (el, picker) => { 590 | const p = getProductRootElement(el); 591 | 592 | const e_img = getImgUrl(p, { x: picker.mouseX, y: picker.mouseY }); 593 | const e_name = getName(p); 594 | const e_price = getPrice(p); 595 | const e_description = getDescriptin(p); 596 | const e_photos = getPhotos(p, { x: picker.mouseX, y: picker.mouseY }, e_img); 597 | const name = getText(e_name); 598 | const img = getSrcFromImgTag(e_img); 599 | const url = getUrl(el); 600 | const price = getCurrencyNumber(getText(e_price)); 601 | const description = getEnteredText(e_description); 602 | const r_photos = {}; 603 | const photos = e_photos.map((p, idx) => { 604 | r_photos['photo' + idx] = p; 605 | return getSrcFromImgTag(p); 606 | }); 607 | return { 608 | name, 609 | img, 610 | color: img, 611 | url, 612 | description, 613 | price, 614 | photos, 615 | elements: { e_name, e_img, e_price, e_description, ...r_photos } 616 | } 617 | }; 618 | 619 | const getProductInfoIndividual = (el, picker, global) => { 620 | if (!global.productInfo) global.productInfo = {}; 621 | const productInfo = global.productInfo; 622 | if (!productInfo.elements) productInfo.elements = {}; 623 | if (!productInfo.photos) productInfo.photos = []; 624 | if (!productInfo.photos.length) { 625 | productInfo.photos.push(''); 626 | productInfo.elements.photo0 = null; 627 | } 628 | 629 | switch(global.selectMode) { 630 | case 'img': 631 | productInfo.elements.e_img = getManualImgUrl(el, { x: picker.mouseX, y: picker.mouseY }); 632 | productInfo.img = getSrcFromImgTag(productInfo.elements.e_img); 633 | break; 634 | case 'color': 635 | productInfo.elements.e_color = getManualImgUrl(el, { x: picker.mouseX, y: picker.mouseY }); 636 | productInfo.color = getSrcFromImgTag(productInfo.elements.e_color); 637 | break; 638 | case 'name': 639 | productInfo.elements.e_name = el; 640 | productInfo.name = getFText(el); 641 | break; 642 | case 'description': 643 | productInfo.elements.e_description = el; 644 | productInfo.description = getEnteredText(el); 645 | break; 646 | case 'price': 647 | productInfo.elements.e_price = el; 648 | productInfo.price = getCurrencyNumber(getFText(el)); 649 | break; 650 | case 'photos': 651 | const e_photo = getManualImgUrl(el, { x: picker.mouseX, y: picker.mouseY }); 652 | const photo = (e_photo.currentSrc || e_photo.src || '').split(' ')[0]; 653 | productInfo.elements['temp_photo'] = e_photo; 654 | productInfo.temp_photo = photo; 655 | break; 656 | } 657 | }; 658 | 659 | const STYLES = ` 660 | .gs_confirm_container, .gs_message, .gs_tooltip { 661 | box-sizing: border-box !important; 662 | } 663 | .gs_confirm_container *, .gs_message *, .gs_tooltip * { 664 | color: black !important; 665 | box-sizing: border-box !important; 666 | font-size: 16px !important; 667 | appearance: unset !important; 668 | position: unset !important; 669 | margin: unset !important; 670 | opacity: unset !important; 671 | visibility: unset !important; 672 | } 673 | .gs_confirm_container input[type=checkbox] { 674 | width: 13px !important; 675 | height: 13px !important; 676 | } 677 | .gs_confirm_container input, .gs_message input { 678 | border: 1px solid black !important; 679 | } 680 | .gs_hidden { visibility: hidden; } 681 | .gs_d-none { display: none !important; } 682 | .gs_tooltip { 683 | position: fixed !important; 684 | z-index: 99999999999999 !important; 685 | max-width: 500px !important; 686 | width: 80% !important; 687 | pointer-events: none !important; 688 | display: none !important; 689 | 690 | background-color: #ffa778 !important; 691 | padding: 5px 10px !important; 692 | box-shadow: 1px 1px 5px 2px #260101 !important; 693 | line-height: 16px !important; 694 | font-size: 16px !important; 695 | color: black !important; 696 | } 697 | .gs_checkbox { 698 | width: 18px !important; 699 | height: 18px !important; 700 | cursor: pointer !important; 701 | } 702 | .gs_tooltip.gs_show { 703 | display: block !important; 704 | } 705 | .gs_confirm_container { 706 | position: fixed !important; 707 | left: 0 !important; 708 | top: 0 !important; 709 | width: 100vw !important; 710 | height: 100vh !important; 711 | background-color: #ff450040 !important; 712 | z-index: 9999999999999 !important; 713 | display: none !important; 714 | } 715 | .gs_confirm_container.gs_hide { 716 | opacity: 0 !important; 717 | transition: opacity 2s !important; 718 | transition-delay: 4s !important; 719 | } 720 | .gs_message, .gs_confirm { 721 | position: fixed !important; 722 | box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.25) !important; 723 | padding: 30px 10px 8px !important; 724 | background-color: #fff !important; 725 | border: 4px solid #eee !important; 726 | } 727 | .gs_confirm { 728 | left: calc(50vw - 350px) !important; 729 | top: 80px !important; 730 | } 731 | .gs_hide .gs_confirm { 732 | display: none !important; 733 | } 734 | .gs_confirm_content { 735 | width: 730px !important; 736 | max-width: 100% !important; 737 | max-height: calc(100vh - 150px) !important; 738 | overflow-y: auto !important; 739 | display: flex !important; 740 | gap: 20px !important; 741 | flex-wrap: wrap !important; 742 | align-items: flex-start !important; 743 | } 744 | @media screen and (max-width: 768px) { 745 | .gs_confirm { 746 | width: 290px !important; 747 | left: calc(50vw - 150px) !important; 748 | } 749 | } 750 | .gs_message { 751 | display: none !important; 752 | left: 10px !important; 753 | bottom: 10px !important; 754 | z-index: 999999999999 !important; 755 | width: 300px !important; 756 | } 757 | .gs_message_content { 758 | display: flex !important; 759 | max-height: calc(100vh - 100px) !important; 760 | overflow-y: auto !important; 761 | min-height: 250px !important; 762 | flex-direction: column !important; 763 | } 764 | 765 | .gs_confirm_container.gs_show, .gs_message.gs_show { 766 | display: inline-block !important; 767 | } 768 | .gs_ollacart_img img { 769 | width: 100% !important; 770 | } 771 | .gs_confirm .gs_ollacart_img { 772 | width: 350px !important; 773 | /* position: sticky; */ 774 | /* top: 0; */ 775 | } 776 | .gs_name_price { 777 | display: flex !important; 778 | justify-content: space-between !important; 779 | font-size: 16px !important; 780 | color: black !important; 781 | gap: 10px !important; 782 | } 783 | .gs_confirm .gs_name_price { 784 | font-size: 20px !important; 785 | font-weight: bold !important; 786 | color: #303030 !important; 787 | } 788 | .gs_confirm .gs_price { 789 | color: #004400 !important; 790 | } 791 | .gs_description { 792 | font-size: 14px !important; 793 | margin-top: 10px !important; 794 | white-space: break-spaces !important; 795 | } 796 | .gs_message_over, .gs_message_finish { 797 | position: absolute !important; 798 | left: 0 !important; 799 | right: 0 !important; 800 | top: -20px !important; 801 | background-color: orangered !important; 802 | color: white !important; 803 | text-align: center !important; 804 | padding: 8px 0 !important; 805 | font-size: 20px !important; 806 | font-weight: bold !important; 807 | white-space: nowrap !important; 808 | cursor: pointer !important; 809 | } 810 | .gs_message_mask { 811 | position: absolute !important; 812 | left: -4px !important; 813 | right: -4px !important; 814 | top: -4px !important; 815 | bottom: -4px !important; 816 | background-color: #ff450040 !important; 817 | } 818 | .gs_message_finish { 819 | font-size: 30px !important; 820 | top: calc(50% - 100px) !important; 821 | padding: 50px 0 !important; 822 | } 823 | .gs_addtional_photos { 824 | margin-top: 5px !important; 825 | display: flex !important; 826 | flex-wrap: wrap !important; 827 | gap: 10px !important; 828 | } 829 | .gs_addtional_photos>div { 830 | position: relative !important; 831 | width: 46px !important; 832 | height: 60px !important; 833 | overflow: hidden !important; 834 | display: flex !important; 835 | justify-content: center !important; 836 | align-items: center !important; 837 | border: 1px solid blue !important; 838 | } 839 | .gs_addtional_photos .gs_remove_photo { 840 | transform: translateY(100%) !important; 841 | opacity: 0 !important; 842 | transition: all .3s !important; 843 | position: absolute !important; 844 | width: 100% !important; 845 | height: 100% !important; 846 | background-color: #000000A0 !important; 847 | display: flex !important; 848 | justify-content: center !important; 849 | align-items: center !important; 850 | } 851 | .gs_addtional_photos>div:hover .gs_remove_photo { 852 | transform: translateY(0) !important; 853 | opacity: 1 !important; 854 | } 855 | .gs_addtional_photos .gs_remove_photo .gs_remove_btn { 856 | width: 30px !important; 857 | height: 30px !important; 858 | border-radius: 50% !important; 859 | cursor: pointer !important; 860 | display: flex !important; 861 | justify-content: center !important; 862 | align-items: center !important; 863 | font-size: 30px !important; 864 | font-weight: bold !important; 865 | background-color: rgb(200, 200, 200) !important; 866 | color: black !important; 867 | } 868 | .gs_addtional_photos img { 869 | width: 100% !important; 870 | } 871 | 872 | .gs_manual_select_tools { 873 | flex-grow: 1 !important; 874 | display: flex !important; 875 | align-items: flex-end !important; 876 | justify-content: space-between !important; 877 | margin-top: 10px !important; 878 | } 879 | .gs_confirm_tools { 880 | display: flex !important; 881 | gap: 15px !important; 882 | justify-content: flex-end !important; 883 | align-items: flex-end !important; 884 | flex-grow: 1 !important; 885 | margin-top: 10px !important; 886 | } 887 | .gs_btn { 888 | padding: 4px 10px !important; 889 | background-color: orangered !important; 890 | color: white !important; 891 | font-size: 16px !important; 892 | font-weight: bold !important; 893 | cursor: pointer !important; 894 | border-radius: 7px !important; 895 | } 896 | .gs_btn:hover { 897 | opacity: 0.8 !important; 898 | } 899 | .gs_btn:active { 900 | opacity: 0.7 !important; 901 | } 902 | .gs_btn.gs_direct { 903 | padding: 4px 14px !important; 904 | } 905 | 906 | .gs_confirm_right { 907 | display: flex !important; 908 | flex-direction: column !important; 909 | flex-grow: 1 !important; 910 | width: 1px !important; 911 | } 912 | .gs_text_center { 913 | text-align: center !important; 914 | } 915 | .gs_go_ollacart { 916 | margin-top: 20px !important; 917 | font-size: 20px !important; 918 | line-height: 25px !important; 919 | cursor: pointer !important; 920 | color: lightseagreen !important; 921 | } 922 | .gs_textarea { 923 | width: 100% !important; 924 | height: 300px !important; 925 | min-height: 300px !important; 926 | max-height: 300px !important; 927 | font-size: 16px !important; 928 | } 929 | 930 | .gs_close { 931 | position: absolute !important; 932 | top: 5px !important; 933 | right: 5px !important; 934 | cursor: pointer !important; 935 | } 936 | .gs_close:hover { 937 | opacity: 0.8 !important; 938 | } 939 | .gs_close img { 940 | width: 20px !important; 941 | } 942 | 943 | .gs_addtional_picker { 944 | margin-top: 15px !important; 945 | margin-left: auto !important; 946 | } 947 | .gs_addtional_picker>div { 948 | width: 200px !important; 949 | margin-top: 5px !important; 950 | display: flex !important; 951 | align-items: center !important; 952 | justify-content: space-between !important; 953 | } 954 | .gs_addtional_picker>div>div { 955 | display: flex !important; 956 | align-items: center !important; 957 | gap: 5px !important; 958 | } 959 | .gs_addtional_picker>div>*:nth-child(2) { 960 | width: 70px !important; 961 | } 962 | .gs_addtional_picker .gs_color-img { 963 | aspect-ratio: 1 !important; 964 | text-align: center !important; 965 | border: 1px solid orangered !important; 966 | object-fit: contain !important; 967 | padding: 4px !important; 968 | border-radius: 8px !important; 969 | cursor: pointer !important; 970 | } 971 | .gs_addtional_picker .gs_color-img:hover { 972 | opacity: 0.8 !important; 973 | } 974 | 975 | .gs_confirm_content::-webkit-scrollbar, .gs_message_content::-webkit-scrollbar { 976 | width: 7px !important; 977 | } 978 | .gs_confirm_content::-webkit-scrollbar-track, .gs_message_content::-webkit-scrollbar-track { 979 | background: #f1f1f1 !important; 980 | } 981 | .gs_confirm_content::-webkit-scrollbar-thumb, .gs_message_content::-webkit-scrollbar-thumb { 982 | background: #e19b9b !important; 983 | } 984 | .gs_confirm_content::-webkit-scrollbar-thumb:hover, .gs_message_content::-webkit-scrollbar-thumb:hover { 985 | background: #e19b9bd0 !important; 986 | } 987 | `; 988 | 989 | const manualSelect = { 990 | img: 'Main Logo', 991 | name: 'Title', 992 | price: 'Price', 993 | description: 'Description', 994 | photos: 'Images' 995 | }; 996 | 997 | const showMessage = (global) => { 998 | const info = global.productInfo; 999 | let html = '
'; 1000 | if (!global.selectMode || global.selectMode === 'img') html += `
`; 1001 | if (!global.selectMode) { 1002 | html += `
${info.name}${info.price || ''}
`; 1003 | } 1004 | if (!global.selectMode) html += `
${info.description}
`; 1005 | if (global.selectMode === 'name' || global.selectMode === 'price' || global.selectMode === 'description') { 1006 | html += ``; 1007 | } 1008 | if (!global.selectMode || global.selectMode === 'photos') { 1009 | html += `
`; 1010 | for (let i = 0; info.photos && (i < info.photos.length); i ++ ) { 1011 | if (info.photos[i]) 1012 | html += `
-
`; 1013 | } 1014 | html += `
`; 1015 | } 1016 | if (global.selectMode === 'photos' && info.temp_photo) { 1017 | html += `
`; 1018 | } 1019 | 1020 | if (global.selectMode) { 1021 | html += `
1022 |
<
1023 |
Finish
1024 |
>
1025 |
`; 1026 | } 1027 | html += `
`; 1028 | 1029 | if (global.selectMode) { 1030 | html += `
Select ${manualSelect[global.selectMode]}
`; 1031 | } else { 1032 | html += `
Auto Select
`; 1033 | } 1034 | 1035 | global.popup.innerHTML = html; 1036 | global.popup.classList.toggle("gs_show", true); 1037 | }; 1038 | 1039 | const showColorModal = (global) => { 1040 | const info = global.productInfo; 1041 | let html = '
'; 1042 | html += `
`; 1043 | 1044 | if (global.selectMode) { 1045 | html += `
1046 | 1047 |
Finish
1048 | 1049 |
`; 1050 | } 1051 | html += `
`; 1052 | 1053 | html += `
Specify Color
`; 1054 | 1055 | global.colormodal.innerHTML = html; 1056 | global.colormodal.classList.toggle("gs_show", true); 1057 | }; 1058 | 1059 | const showConfirm = global => { 1060 | hideMessage(global); 1061 | hideColorModal(global); 1062 | hideTooltip(global); 1063 | 1064 | const info = global.productInfo; 1065 | let html = `
close
`; 1066 | html += `

Go to OllaCart

`; 1067 | html += `
${info.name}$${info.price || '0'}
`; 1068 | html += `
1069 |
1070 |
c Size notes
1071 | 1072 |
1073 | 1077 |
`; 1078 | if (info.description) html += `
${info.description}
`; 1079 | for (let i = 0; info.photos && (i < info.photos.length); i ++ ) { 1080 | if (i === 0) html += `
`; 1081 | if (info.photos[i]) 1082 | html += `
-
`; 1083 | if (i === info.photos.length - 1) html += `
`; 1084 | } 1085 | 1086 | html += `
1087 |
Looks Correct
1088 |
Manual Select
1089 |
`; 1090 | 1091 | html += '
'; 1092 | // html += `
You selected item
`; 1093 | html += `
`; 1094 | 1095 | if (global.finish) html += `
Added to OllaCart
`; 1096 | 1097 | global.confirm.innerHTML = html; 1098 | global.confirm.classList.toggle("gs_show", true); 1099 | global.showConfirm = true; 1100 | 1101 | if (global.finish) global.confirm.classList.toggle("gs_hide", true); 1102 | else global.confirm.classList.toggle("gs_hide", false); 1103 | }; 1104 | 1105 | const showTooltip = global => { 1106 | global.tooltip.classList.toggle("gs_show", true); 1107 | }; 1108 | 1109 | const hideTooltip = global => { 1110 | global.tooltip.classList.toggle("gs_show", false); 1111 | }; 1112 | 1113 | const hideMessage = global => { 1114 | global.popup.classList.toggle("gs_show", false); 1115 | }; 1116 | 1117 | const hideColorModal = global => { 1118 | global.colormodal.classList.toggle("gs_show", false); 1119 | }; 1120 | 1121 | const hideConfirm = global => { 1122 | global.confirm.classList.toggle("gs_show", false); 1123 | global.showConfirm = false; 1124 | }; 1125 | 1126 | const initMessage = global => { 1127 | addStyle(STYLES); 1128 | global.popup = document.createElement("div"); 1129 | global.popup.className = "gs_message"; 1130 | document.body.appendChild(global.popup); 1131 | 1132 | global.colormodal = document.createElement("div"); 1133 | global.colormodal.className = "gs_message"; 1134 | document.body.appendChild(global.colormodal); 1135 | 1136 | global.confirm = document.createElement("div"); 1137 | global.confirm.className = "gs_confirm_container"; 1138 | document.body.appendChild(global.confirm); 1139 | 1140 | global.tooltip = document.createElement("div"); 1141 | global.tooltip.innerHTML = 'Click whenever OllaCart shows appropriate product information. You can manually select and edit information after.'; 1142 | global.tooltip.className = "gs_tooltip"; 1143 | document.body.appendChild(global.tooltip); 1144 | }; 1145 | 1146 | // const API_URL = 'https://www.ollacart.com/api/' 1147 | const API_URL = 'https://ollacart-dev.herokuapp.com/api/'; 1148 | // const API_URL2 = 'http://localhost:5000/api/' 1149 | 1150 | const clearClass = (cl) => { 1151 | const itms = document.getElementsByClassName(cl); 1152 | for (let i = itms.length - 1 ; i >= 0; i --) itms[i].classList.remove(cl); 1153 | }; 1154 | const addClass = (obj, cl) => { 1155 | const itms = Object.keys(obj).map(key => obj[key]); 1156 | for (let i = 0 ; i < itms.length; i ++) { 1157 | if (!itms[i]) continue; 1158 | itms[i].classList.add(cl); 1159 | } 1160 | }; 1161 | 1162 | const copyToTemp = (global) => { 1163 | global.productInfo.temp_photo = ''; 1164 | global.tempInfo = { 1165 | ...global.productInfo, 1166 | elements: {...(global.productInfo.elements || {})}, 1167 | photos: [...(global.productInfo.photos || [])] 1168 | }; 1169 | }; 1170 | const copyFromTemp = (global) => { 1171 | const keys = Object.keys(global.productInfo); 1172 | let i = 0; 1173 | for (i = 0; i < keys.length; i ++) 1174 | if (global.productInfo[keys[i]] !== global.tempInfo[keys[i]]) 1175 | break; 1176 | if (i === keys.length) return; 1177 | global.productInfo = { 1178 | ...global.tempInfo, 1179 | temp_photo: '' 1180 | }; 1181 | if (global.selectMode === 'color') showColorModal(global); 1182 | else showMessage(global); 1183 | }; 1184 | 1185 | const toggle = global => { 1186 | const state = !global.state; 1187 | global.state = state; 1188 | global.selectMode = null; 1189 | 1190 | if (state) { 1191 | global.picker.start({ 1192 | onHover: global.selectElement, 1193 | onClick: global.domPick 1194 | }); 1195 | document.addEventListener('input', global.inputValueChanged); 1196 | document.addEventListener('mousemove', global.onMouseMove); 1197 | } else { 1198 | global.picker.stop(); 1199 | document.removeEventListener('input', global.inputValueChanged); 1200 | document.removeEventListener('mousemove', global.onMouseMove); 1201 | } 1202 | 1203 | if (!state) { 1204 | clearClass('gs_copied'); 1205 | hideMessage(global); 1206 | hideConfirm(global); 1207 | hideTooltip(global); 1208 | } 1209 | }; 1210 | 1211 | const init = global => { 1212 | global.init = true; 1213 | global.state = false; 1214 | global.selectMode = null; 1215 | global.productInfo = {}; 1216 | global.tempInfo = {}; 1217 | global.picker = new ElementPicker({ 1218 | style: { 1219 | background: "rgba(153, 235, 255, 0.5)", 1220 | borderColor: "yellow" 1221 | }, 1222 | }); 1223 | global.items = ['img', 'name', 'price', 'description', 'photos']; 1224 | 1225 | global.sendAPI = () => { 1226 | const productInfo = global.productInfo; 1227 | if (!productInfo.img || !productInfo.name) return; 1228 | 1229 | const { name, price, description, photos } = productInfo; 1230 | const photo = productInfo.img; 1231 | const url = productInfo.url || findHref(productInfo.elements.e_img) || findHref(productInfo.elements.e_name) || location.href; 1232 | const original_url = location.href; 1233 | const size = productInfo.chooseSize ? productInfo.size : ''; 1234 | // console.log('url', url); 1235 | 1236 | fetch(API_URL + 'product/create', { 1237 | method: 'POST', 1238 | headers: { 1239 | 'Accept': 'application/json', 1240 | 'Content-Type': 'application/json' 1241 | }, 1242 | body: JSON.stringify({ photo, original_url, url, name, price, description, photos, size, ce_id: localStorage.getItem('ce_id') || '' }) 1243 | }); 1244 | }; 1245 | 1246 | global.popupBtnClicked = (attr, target) => { 1247 | if(!global.showConfirm) copyFromTemp(global); 1248 | if (attr === 'gs__close') { 1249 | toggle(global); 1250 | global.sendClose(); 1251 | return; 1252 | } 1253 | if (attr === 'gs__goollacart') { 1254 | window.open('https://www.ollacart.com', '_blank'); 1255 | return; 1256 | } 1257 | if (attr === 'gs__confirm') { 1258 | global.sendAPI(); 1259 | global.finish = true; 1260 | global.picker.stop(); 1261 | showConfirm(global); 1262 | setTimeout(() => { 1263 | global.finish = false; 1264 | toggle(global); 1265 | global.sendClose(); 1266 | }, 5000); 1267 | return; 1268 | } 1269 | if (attr === 'gs__color') { 1270 | copyToTemp(global); 1271 | hideConfirm(global); 1272 | global.selectMode = 'color'; 1273 | showColorModal(global); 1274 | return; 1275 | } 1276 | if (attr === 'gs__togglecolor') { 1277 | global.productInfo.chooseColor = !global.productInfo.chooseColor; 1278 | showConfirm(global); 1279 | return; 1280 | } 1281 | if (attr === 'gs__togglesize') { 1282 | global.productInfo.chooseSize = !global.productInfo.chooseSize; 1283 | showConfirm(global); 1284 | return; 1285 | } 1286 | if (attr === 'gs__manual') { 1287 | copyToTemp(global); 1288 | hideConfirm(global); 1289 | global.selectMode = 'img'; 1290 | showMessage(global); 1291 | return; 1292 | } 1293 | if (attr === 'gs__finish') { 1294 | global.selectMode = ''; 1295 | showConfirm(global); 1296 | return; 1297 | } 1298 | if (attr === 'gs__remove') { 1299 | const t = parseInt(target) || 0; 1300 | for (let i = t; i < global.productInfo.photos.length - 1; i ++) { 1301 | global.productInfo.photos[i] = global.productInfo.photos[i + 1]; 1302 | global.productInfo.elements['photo' + i] = global.productInfo.elements['photo' + (i + 1)]; 1303 | } 1304 | if (global.productInfo.photos.length) { 1305 | global.productInfo.photos.pop(); 1306 | delete global.productInfo.elements['photo' + global.productInfo.photos.length]; 1307 | } 1308 | copyToTemp(global); 1309 | if (global.showConfirm) showConfirm(global); 1310 | else showMessage(global); 1311 | return; 1312 | } 1313 | let idx = global.items.indexOf(global.selectMode); 1314 | if (attr === 'gs__prev') idx --; 1315 | if (attr === 'gs__next') idx ++; 1316 | if (idx < 0) idx = 0; 1317 | if (idx >= global.items.length) return idx = global.items.length - 1; 1318 | 1319 | global.selectMode = global.items[idx]; 1320 | 1321 | if (global.selectMode === 'photos') { 1322 | global.productInfo.elements['temp_photo'] = null; 1323 | global.productInfo.temp_photo = ''; 1324 | } 1325 | showMessage(global); 1326 | }; 1327 | 1328 | global.selectElement = el => { 1329 | if (!el) return; 1330 | if (el.tagName.toLocaleLowerCase() === 'html') return; 1331 | if (global.finish || !global.popup || global.confirm.contains(el)) return; 1332 | if (global.popup.contains(el) || global.colormodal.contains(el)) { 1333 | if(global.selectMode !== 'photos') copyFromTemp(global); 1334 | return; 1335 | } 1336 | 1337 | if (!global.selectMode) { 1338 | global.productInfo = getProductInfo(el, global.picker); 1339 | } else { 1340 | getProductInfoIndividual(el, global.picker, global); 1341 | } 1342 | if (global.selectMode === 'color') showColorModal(global); 1343 | else showMessage(global); 1344 | }; 1345 | 1346 | global.domPick = (el) => { 1347 | console.log('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', el); 1348 | if (!el) return ; 1349 | if (el.tagName.toLocaleLowerCase() === 'html') return; 1350 | if (!global.popup) return; 1351 | if (global.popup.contains(el) || global.confirm.contains(el) || global.colormodal.contains(el)) { 1352 | const attr = el.getAttribute('tag'); 1353 | const target = el.getAttribute('target') || ''; 1354 | if (attr && attr !== 'gs__text') 1355 | global.popupBtnClicked(attr, target); 1356 | return ; 1357 | } 1358 | 1359 | clearClass('gs_copied'); 1360 | if (!global.selectMode) global.productInfo = getProductInfo(el, global.picker); 1361 | addClass(global.productInfo.elements, 'gs_copied'); 1362 | 1363 | if (global.selectMode) { 1364 | if (global.selectMode === 'color') { 1365 | copyToTemp(global); 1366 | showColorModal(global); 1367 | return; 1368 | } 1369 | let idx = global.items.indexOf(global.selectMode) + 1; 1370 | if (global.selectMode === 'photos') { 1371 | global.productInfo.elements['photo' + global.productInfo.photos.length] = global.productInfo.elements['temp_photo']; 1372 | global.productInfo.photos.push(global.productInfo.temp_photo); 1373 | idx --; 1374 | } 1375 | global.selectMode = global.items[idx]; 1376 | copyToTemp(global); 1377 | 1378 | showMessage(global); 1379 | return ; 1380 | } 1381 | copyToTemp(global); 1382 | showConfirm(global); 1383 | }; 1384 | 1385 | global.inputValueChanged = (e) => { 1386 | const tag = e.target.getAttribute("tag"); 1387 | const target = e.target.getAttribute("target"); 1388 | if (tag === 'gs__text' || !target) { 1389 | global.productInfo[target] = e.target.value; 1390 | global.tempInfo[target] = e.target.value; 1391 | console.log(global.productInfo); 1392 | } 1393 | }; 1394 | 1395 | global.onMouseMove = (e) => { 1396 | if(global.selectMode || global.showConfirm) { 1397 | hideTooltip(global); 1398 | return ; 1399 | } 1400 | global.tooltip.style.left = e.clientX + 'px'; 1401 | global.tooltip.style.top = e.clientY + 'px'; 1402 | showTooltip(global); 1403 | }; 1404 | 1405 | addStyle(` 1406 | .gs_hover { 1407 | border: 2px solid #cdcdcd !important; 1408 | background: repeating-linear-gradient( 135deg, rgba(225, 225, 226, 0.3), rgba(229, 229, 229, 0.3) 10px, rgba(173, 173, 173, 0.3) 10px, rgba(172, 172, 172, 0.3) 20px ) !important; 1409 | box-shadow: inset 0px 0px 0px 1px #d7d7d7; 1410 | } 1411 | 1412 | .gs_copied { 1413 | border: 3px solid #ff0a00 !important; 1414 | background: repeating-linear-gradient( 135deg, rgba(183, 240, 200, 0.3), rgba(192, 231, 194, 0.3) 10px, rgba(124, 189, 126, 0.3) 10px, rgba(137, 180, 129, 0.3) 20px ) !important; 1415 | box-shadow: inset 0px 0px 0px 1px #c4d9c2 !important; 1416 | } 1417 | `); 1418 | initMessage(global); 1419 | }; 1420 | 1421 | !(() => { 1422 | const global = window.__gs = window.__gs || {}; 1423 | // console.log('[Ollacart] Init', global); 1424 | if (!global.init) { 1425 | // console.log("[Ollacart Selector]: Started"); 1426 | init(global); 1427 | 1428 | global.sendClose = () => { 1429 | chrome.runtime.sendMessage({type: "close"}, function(response) { 1430 | // console.log(response); 1431 | }); 1432 | }; 1433 | 1434 | chrome.runtime.sendMessage({type: "init"}, function(response) { 1435 | // console.log(response); 1436 | }); 1437 | 1438 | chrome.runtime.onMessage.addListener( 1439 | function(request, sender, sendResponse) { 1440 | // console.log(request); 1441 | switch(request.type) { 1442 | case 'get_state': 1443 | sendResponse(global.state); 1444 | break; 1445 | case 'toggle_state': 1446 | toggle(global); 1447 | sendResponse(global.state); 1448 | case 'ce_id': 1449 | if (request.ce_id) 1450 | localStorage.setItem('ce_id', request.ce_id); 1451 | } 1452 | } 1453 | ); 1454 | } 1455 | })(); 1456 | 1457 | }()); 1458 | -------------------------------------------------------------------------------- /bin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OllaCart", 3 | "version": "1.0.0", 4 | "manifest_version": 3, 5 | "description": "Chrome Extension for Ollacart. Add your items to OllaCart.", 6 | "permissions": ["storage", "tabs", "activeTab"], 7 | "content_scripts": [{ 8 | "matches": [""], 9 | "js": ["inject.js"], 10 | "all_frames": false 11 | }], 12 | "background": { 13 | "service_worker": "background.js" 14 | }, 15 | "action": { }, 16 | "icons": { 17 | "32": "icon_32.png", 18 | "128": "icon_128.png" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getselector", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@medv/finder": { 8 | "version": "1.1.0", 9 | "resolved": "https://registry.npmjs.org/@medv/finder/-/finder-1.1.0.tgz", 10 | "integrity": "sha512-UPjX+hWYTxYQioAMG2xk7Cm/kh/NbPNQaQWjO4a1lS04ns4xMINDxlXljrCt2ODGrtFkGSsU6iVm3+gUK8KT4A==", 11 | "requires": { 12 | "cssesc": "2.0.0" 13 | } 14 | }, 15 | "acorn": { 16 | "version": "5.4.1", 17 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", 18 | "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==", 19 | "dev": true 20 | }, 21 | "arr-diff": { 22 | "version": "2.0.0", 23 | "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", 24 | "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", 25 | "dev": true, 26 | "requires": { 27 | "arr-flatten": "1.1.0" 28 | } 29 | }, 30 | "arr-flatten": { 31 | "version": "1.1.0", 32 | "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", 33 | "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", 34 | "dev": true 35 | }, 36 | "array-unique": { 37 | "version": "0.2.1", 38 | "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", 39 | "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", 40 | "dev": true 41 | }, 42 | "asynckit": { 43 | "version": "0.4.0", 44 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 45 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 46 | }, 47 | "axios": { 48 | "version": "1.6.0", 49 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", 50 | "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", 51 | "requires": { 52 | "follow-redirects": "^1.15.0", 53 | "form-data": "^4.0.0", 54 | "proxy-from-env": "^1.1.0" 55 | } 56 | }, 57 | "braces": { 58 | "version": "1.8.5", 59 | "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", 60 | "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", 61 | "dev": true, 62 | "requires": { 63 | "expand-range": "1.8.2", 64 | "preserve": "0.2.0", 65 | "repeat-element": "1.1.2" 66 | } 67 | }, 68 | "builtin-modules": { 69 | "version": "1.1.1", 70 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 71 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 72 | "dev": true 73 | }, 74 | "combined-stream": { 75 | "version": "1.0.8", 76 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 77 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 78 | "requires": { 79 | "delayed-stream": "~1.0.0" 80 | } 81 | }, 82 | "cssesc": { 83 | "version": "2.0.0", 84 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", 85 | "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" 86 | }, 87 | "delayed-stream": { 88 | "version": "1.0.0", 89 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 90 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 91 | }, 92 | "estree-walker": { 93 | "version": "0.5.1", 94 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.1.tgz", 95 | "integrity": "sha512-7HgCgz1axW7w5aOvgOQkoR1RMBkllygJrssU3BvymKQ95lxXYv6Pon17fBRDm9qhkvXZGijOULoSF9ShOk/ZLg==", 96 | "dev": true 97 | }, 98 | "expand-brackets": { 99 | "version": "0.1.5", 100 | "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", 101 | "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", 102 | "dev": true, 103 | "requires": { 104 | "is-posix-bracket": "0.1.1" 105 | } 106 | }, 107 | "expand-range": { 108 | "version": "1.8.2", 109 | "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", 110 | "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", 111 | "dev": true, 112 | "requires": { 113 | "fill-range": "2.2.3" 114 | } 115 | }, 116 | "extglob": { 117 | "version": "0.3.2", 118 | "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", 119 | "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", 120 | "dev": true, 121 | "requires": { 122 | "is-extglob": "1.0.0" 123 | } 124 | }, 125 | "filename-regex": { 126 | "version": "2.0.1", 127 | "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", 128 | "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", 129 | "dev": true 130 | }, 131 | "fill-range": { 132 | "version": "2.2.3", 133 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", 134 | "integrity": "sha512-P1WnpaJQ8BQdSEIjEmgyCHm9ESwkO6sMu+0Moa4s0u9B+iQ5M9tBbbCYvWmF7vRvqyMO2ENqC+w4Hev8wErQcg==", 135 | "dev": true, 136 | "requires": { 137 | "is-number": "^2.1.0", 138 | "isobject": "^2.0.0", 139 | "randomatic": "^1.1.3", 140 | "repeat-element": "^1.1.2", 141 | "repeat-string": "^1.5.2" 142 | } 143 | }, 144 | "follow-redirects": { 145 | "version": "1.15.6", 146 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", 147 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" 148 | }, 149 | "for-in": { 150 | "version": "1.0.2", 151 | "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", 152 | "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", 153 | "dev": true 154 | }, 155 | "for-own": { 156 | "version": "0.1.5", 157 | "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", 158 | "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", 159 | "dev": true, 160 | "requires": { 161 | "for-in": "1.0.2" 162 | } 163 | }, 164 | "form-data": { 165 | "version": "4.0.0", 166 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 167 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 168 | "requires": { 169 | "asynckit": "^0.4.0", 170 | "combined-stream": "^1.0.8", 171 | "mime-types": "^2.1.12" 172 | } 173 | }, 174 | "glob-base": { 175 | "version": "0.3.0", 176 | "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", 177 | "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", 178 | "dev": true, 179 | "requires": { 180 | "glob-parent": "2.0.0", 181 | "is-glob": "2.0.1" 182 | } 183 | }, 184 | "glob-parent": { 185 | "version": "2.0.0", 186 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", 187 | "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", 188 | "dev": true, 189 | "requires": { 190 | "is-glob": "2.0.1" 191 | } 192 | }, 193 | "is-buffer": { 194 | "version": "1.1.6", 195 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 196 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", 197 | "dev": true 198 | }, 199 | "is-dotfile": { 200 | "version": "1.0.3", 201 | "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", 202 | "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", 203 | "dev": true 204 | }, 205 | "is-equal-shallow": { 206 | "version": "0.1.3", 207 | "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", 208 | "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", 209 | "dev": true, 210 | "requires": { 211 | "is-primitive": "2.0.0" 212 | } 213 | }, 214 | "is-extendable": { 215 | "version": "0.1.1", 216 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", 217 | "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", 218 | "dev": true 219 | }, 220 | "is-extglob": { 221 | "version": "1.0.0", 222 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", 223 | "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", 224 | "dev": true 225 | }, 226 | "is-glob": { 227 | "version": "2.0.1", 228 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", 229 | "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", 230 | "dev": true, 231 | "requires": { 232 | "is-extglob": "1.0.0" 233 | } 234 | }, 235 | "is-module": { 236 | "version": "1.0.0", 237 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 238 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 239 | "dev": true 240 | }, 241 | "is-number": { 242 | "version": "2.1.0", 243 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", 244 | "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", 245 | "dev": true, 246 | "requires": { 247 | "kind-of": "3.2.2" 248 | } 249 | }, 250 | "is-posix-bracket": { 251 | "version": "0.1.1", 252 | "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", 253 | "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", 254 | "dev": true 255 | }, 256 | "is-primitive": { 257 | "version": "2.0.0", 258 | "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", 259 | "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", 260 | "dev": true 261 | }, 262 | "isarray": { 263 | "version": "1.0.0", 264 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 265 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 266 | "dev": true 267 | }, 268 | "isobject": { 269 | "version": "2.1.0", 270 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", 271 | "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", 272 | "dev": true, 273 | "requires": { 274 | "isarray": "1.0.0" 275 | } 276 | }, 277 | "kind-of": { 278 | "version": "3.2.2", 279 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 280 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 281 | "dev": true, 282 | "requires": { 283 | "is-buffer": "1.1.6" 284 | } 285 | }, 286 | "lodash": { 287 | "version": "4.17.21", 288 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 289 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 290 | }, 291 | "magic-string": { 292 | "version": "0.22.4", 293 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz", 294 | "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==", 295 | "dev": true, 296 | "requires": { 297 | "vlq": "0.2.3" 298 | } 299 | }, 300 | "micromatch": { 301 | "version": "2.3.11", 302 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", 303 | "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", 304 | "dev": true, 305 | "requires": { 306 | "arr-diff": "2.0.0", 307 | "array-unique": "0.2.1", 308 | "braces": "1.8.5", 309 | "expand-brackets": "0.1.5", 310 | "extglob": "0.3.2", 311 | "filename-regex": "2.0.1", 312 | "is-extglob": "1.0.0", 313 | "is-glob": "2.0.1", 314 | "kind-of": "3.2.2", 315 | "normalize-path": "2.1.1", 316 | "object.omit": "2.0.1", 317 | "parse-glob": "3.0.4", 318 | "regex-cache": "0.4.4" 319 | } 320 | }, 321 | "mime-db": { 322 | "version": "1.52.0", 323 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 324 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 325 | }, 326 | "mime-types": { 327 | "version": "2.1.35", 328 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 329 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 330 | "requires": { 331 | "mime-db": "1.52.0" 332 | } 333 | }, 334 | "normalize-path": { 335 | "version": "2.1.1", 336 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", 337 | "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", 338 | "dev": true, 339 | "requires": { 340 | "remove-trailing-separator": "1.1.0" 341 | } 342 | }, 343 | "object.omit": { 344 | "version": "2.0.1", 345 | "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", 346 | "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", 347 | "dev": true, 348 | "requires": { 349 | "for-own": "0.1.5", 350 | "is-extendable": "0.1.1" 351 | } 352 | }, 353 | "parse-glob": { 354 | "version": "3.0.4", 355 | "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", 356 | "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", 357 | "dev": true, 358 | "requires": { 359 | "glob-base": "0.3.0", 360 | "is-dotfile": "1.0.3", 361 | "is-extglob": "1.0.0", 362 | "is-glob": "2.0.1" 363 | } 364 | }, 365 | "path-parse": { 366 | "version": "1.0.5", 367 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 368 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", 369 | "dev": true 370 | }, 371 | "preserve": { 372 | "version": "0.2.0", 373 | "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", 374 | "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", 375 | "dev": true 376 | }, 377 | "proxy-from-env": { 378 | "version": "1.1.0", 379 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 380 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 381 | }, 382 | "randomatic": { 383 | "version": "1.1.7", 384 | "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", 385 | "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", 386 | "dev": true, 387 | "requires": { 388 | "is-number": "3.0.0", 389 | "kind-of": "4.0.0" 390 | }, 391 | "dependencies": { 392 | "is-number": { 393 | "version": "3.0.0", 394 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", 395 | "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", 396 | "dev": true, 397 | "requires": { 398 | "kind-of": "3.2.2" 399 | }, 400 | "dependencies": { 401 | "kind-of": { 402 | "version": "3.2.2", 403 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 404 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 405 | "dev": true, 406 | "requires": { 407 | "is-buffer": "1.1.6" 408 | } 409 | } 410 | } 411 | }, 412 | "kind-of": { 413 | "version": "4.0.0", 414 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", 415 | "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", 416 | "dev": true, 417 | "requires": { 418 | "is-buffer": "1.1.6" 419 | } 420 | } 421 | } 422 | }, 423 | "regex-cache": { 424 | "version": "0.4.4", 425 | "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", 426 | "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", 427 | "dev": true, 428 | "requires": { 429 | "is-equal-shallow": "0.1.3" 430 | } 431 | }, 432 | "remove-trailing-separator": { 433 | "version": "1.1.0", 434 | "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", 435 | "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", 436 | "dev": true 437 | }, 438 | "repeat-element": { 439 | "version": "1.1.2", 440 | "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", 441 | "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", 442 | "dev": true 443 | }, 444 | "repeat-string": { 445 | "version": "1.6.1", 446 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 447 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 448 | "dev": true 449 | }, 450 | "resolve": { 451 | "version": "1.5.0", 452 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", 453 | "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", 454 | "dev": true, 455 | "requires": { 456 | "path-parse": "1.0.5" 457 | } 458 | }, 459 | "rollup": { 460 | "version": "0.56.2", 461 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.56.2.tgz", 462 | "integrity": "sha512-vZIn0P+xA1glmc4DwRsUC9ce6SSE5gZT2YKaRiSYHwJXHcRtXWHOvrY2NtUR8Gk+EoszyyoXMfw9OrYCCKCYCA==", 463 | "dev": true 464 | }, 465 | "rollup-plugin-commonjs": { 466 | "version": "8.3.0", 467 | "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.3.0.tgz", 468 | "integrity": "sha512-PYs3OiYgENFYEmI3vOEm5nrp3eY90YZqd5vGmQqeXmhJsAWFIrFdROCvOasqJ1HgeTvqyYo9IGXnFDyoboNcgQ==", 469 | "dev": true, 470 | "requires": { 471 | "acorn": "5.4.1", 472 | "estree-walker": "0.5.1", 473 | "magic-string": "0.22.4", 474 | "resolve": "1.5.0", 475 | "rollup-pluginutils": "2.0.1" 476 | } 477 | }, 478 | "rollup-plugin-node-resolve": { 479 | "version": "3.0.3", 480 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.3.tgz", 481 | "integrity": "sha512-qJLXJ1aASV6p8SrEfRdQdHmb5OQmqXyIWIdVGcju8QFzftSsHcuL554Vy+n8mr0fZCC+ksO6aWJ7TAVl2F+Qwg==", 482 | "dev": true, 483 | "requires": { 484 | "builtin-modules": "1.1.1", 485 | "is-module": "1.0.0", 486 | "resolve": "1.5.0" 487 | } 488 | }, 489 | "rollup-pluginutils": { 490 | "version": "2.0.1", 491 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", 492 | "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=", 493 | "dev": true, 494 | "requires": { 495 | "estree-walker": "0.3.1", 496 | "micromatch": "2.3.11" 497 | }, 498 | "dependencies": { 499 | "estree-walker": { 500 | "version": "0.3.1", 501 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz", 502 | "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=", 503 | "dev": true 504 | } 505 | } 506 | }, 507 | "vlq": { 508 | "version": "0.2.3", 509 | "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", 510 | "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", 511 | "dev": true 512 | } 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getselector", 3 | "version": "1.0.0", 4 | "description": "Get Selector Browser Extension", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "copy": "del /f /q .\\bin && rd /q .\\bin && mkdir .\\bin && copy src\\manifest.json bin\\manifest.json && copy src\\background.js bin\\background.js && copy src\\*.png bin\\", 9 | "build": "npm run copy && ./node_modules/.bin/rollup -c rollup.config.js" 10 | }, 11 | "keywords": [ 12 | "get", 13 | "css", 14 | "selector", 15 | "find", 16 | "unique", 17 | "browser", 18 | "extension" 19 | ], 20 | "author": "AAA", 21 | "devDependencies": { 22 | "rollup": "^0.56.2", 23 | "rollup-plugin-commonjs": "^8.3.0", 24 | "rollup-plugin-node-resolve": "^3.0.3" 25 | }, 26 | "dependencies": { 27 | "@medv/finder": "^1.1.0", 28 | "axios": "^1.6.0", 29 | "lodash": "^4.17.21" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | 4 | export default { 5 | input: 'src/index.js', 6 | output: { 7 | format: 'iife', 8 | file: 'bin/inject.js', 9 | name: 'getselector' 10 | }, 11 | plugins: [ 12 | resolve(), 13 | commonjs({ 14 | exclude: [], 15 | include: [ 16 | 'node_modules/**' 17 | ] 18 | }) 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /src/addStyle.js: -------------------------------------------------------------------------------- 1 | export const addStyle = style => { 2 | const styleTag = document.createElement('style'); 3 | styleTag.innerHTML = style; 4 | document.head.appendChild(styleTag); 5 | }; 6 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import debounce from "lodash/debounce"; 2 | import { ElementPicker } from "./dom-pick"; 3 | import { addStyle } from "./addStyle"; 4 | import { findHref, getProductInfo, getProductInfoIndividual } from "./scrap"; 5 | import { initMessage, showMessage, showColorModal, showConfirm, showTooltip, hideMessage, hideColorModal, hideConfirm, hideTooltip } from "./info"; 6 | 7 | // const API_URL = 'https://www.ollacart.com/api/' 8 | const API_URL = 'https://ollacart-dev.herokuapp.com/api/' 9 | // const API_URL2 = 'http://localhost:5000/api/' 10 | 11 | const clearClass = (cl) => { 12 | const itms = document.getElementsByClassName(cl); 13 | for (let i = itms.length - 1 ; i >= 0; i --) itms[i].classList.remove(cl); 14 | } 15 | const addClass = (obj, cl) => { 16 | const itms = Object.keys(obj).map(key => obj[key]); 17 | for (let i = 0 ; i < itms.length; i ++) { 18 | if (!itms[i]) continue; 19 | itms[i].classList.add(cl); 20 | } 21 | } 22 | 23 | const copyToTemp = (global) => { 24 | global.productInfo.temp_photo = ''; 25 | global.tempInfo = { 26 | ...global.productInfo, 27 | elements: {...(global.productInfo.elements || {})}, 28 | photos: [...(global.productInfo.photos || [])] 29 | } 30 | } 31 | const copyFromTemp = (global) => { 32 | const keys = Object.keys(global.productInfo); 33 | let i = 0; 34 | for (i = 0; i < keys.length; i ++) 35 | if (global.productInfo[keys[i]] !== global.tempInfo[keys[i]]) 36 | break; 37 | if (i === keys.length) return; 38 | global.productInfo = { 39 | ...global.tempInfo, 40 | temp_photo: '' 41 | } 42 | if (global.selectMode === 'color') showColorModal(global); 43 | else showMessage(global); 44 | } 45 | 46 | export const toggle = global => { 47 | const state = !global.state; 48 | global.state = state; 49 | global.selectMode = null; 50 | 51 | if (state) { 52 | global.picker.start({ 53 | onHover: global.selectElement, 54 | onClick: global.domPick 55 | }); 56 | document.addEventListener('input', global.inputValueChanged); 57 | document.addEventListener('mousemove', global.onMouseMove); 58 | } else { 59 | global.picker.stop(); 60 | document.removeEventListener('input', global.inputValueChanged); 61 | document.removeEventListener('mousemove', global.onMouseMove); 62 | } 63 | 64 | if (!state) { 65 | clearClass('gs_copied'); 66 | hideMessage(global); 67 | hideConfirm(global); 68 | hideTooltip(global); 69 | } 70 | }; 71 | 72 | export const init = global => { 73 | global.init = true; 74 | global.state = false; 75 | global.selectMode = null; 76 | global.productInfo = {}; 77 | global.tempInfo = {}; 78 | global.picker = new ElementPicker({ 79 | style: { 80 | background: "rgba(153, 235, 255, 0.5)", 81 | borderColor: "yellow" 82 | }, 83 | }); 84 | global.items = ['img', 'name', 'price', 'description', 'photos']; 85 | 86 | global.sendAPI = () => { 87 | const productInfo = global.productInfo; 88 | if (!productInfo.img || !productInfo.name) return; 89 | 90 | const { name, price, description, photos } = productInfo; 91 | const photo = productInfo.img; 92 | const url = productInfo.url || findHref(productInfo.elements.e_img) || findHref(productInfo.elements.e_name) || location.href; 93 | const original_url = location.href; 94 | const size = productInfo.chooseSize ? productInfo.size : ''; 95 | // console.log('url', url); 96 | 97 | fetch(API_URL + 'product/create', { 98 | method: 'POST', 99 | headers: { 100 | 'Accept': 'application/json', 101 | 'Content-Type': 'application/json' 102 | }, 103 | body: JSON.stringify({ photo, original_url, url, name, price, description, photos, size, ce_id: localStorage.getItem('ce_id') || '' }) 104 | }); 105 | } 106 | 107 | global.popupBtnClicked = (attr, target) => { 108 | if(!global.showConfirm) copyFromTemp(global); 109 | if (attr === 'gs__close') { 110 | toggle(global); 111 | global.sendClose(); 112 | return; 113 | } 114 | if (attr === 'gs__goollacart') { 115 | window.open('https://www.ollacart.com', '_blank'); 116 | return; 117 | } 118 | if (attr === 'gs__confirm') { 119 | global.sendAPI(); 120 | global.finish = true; 121 | global.picker.stop(); 122 | showConfirm(global); 123 | setTimeout(() => { 124 | global.finish = false; 125 | toggle(global); 126 | global.sendClose(); 127 | }, 5000); 128 | return; 129 | } 130 | if (attr === 'gs__color') { 131 | copyToTemp(global); 132 | hideConfirm(global); 133 | global.selectMode = 'color'; 134 | showColorModal(global); 135 | return; 136 | } 137 | if (attr === 'gs__togglecolor') { 138 | global.productInfo.chooseColor = !global.productInfo.chooseColor; 139 | showConfirm(global); 140 | return; 141 | } 142 | if (attr === 'gs__togglesize') { 143 | global.productInfo.chooseSize = !global.productInfo.chooseSize; 144 | showConfirm(global); 145 | return; 146 | } 147 | if (attr === 'gs__manual') { 148 | copyToTemp(global); 149 | hideConfirm(global); 150 | global.selectMode = 'img'; 151 | showMessage(global); 152 | return; 153 | } 154 | if (attr === 'gs__finish') { 155 | global.selectMode = ''; 156 | showConfirm(global); 157 | return; 158 | } 159 | if (attr === 'gs__remove') { 160 | const t = parseInt(target) || 0; 161 | for (let i = t; i < global.productInfo.photos.length - 1; i ++) { 162 | global.productInfo.photos[i] = global.productInfo.photos[i + 1] 163 | global.productInfo.elements['photo' + i] = global.productInfo.elements['photo' + (i + 1)]; 164 | } 165 | if (global.productInfo.photos.length) { 166 | global.productInfo.photos.pop(); 167 | delete global.productInfo.elements['photo' + global.productInfo.photos.length]; 168 | } 169 | copyToTemp(global); 170 | if (global.showConfirm) showConfirm(global); 171 | else showMessage(global); 172 | return; 173 | } 174 | let idx = global.items.indexOf(global.selectMode); 175 | if (attr === 'gs__prev') idx --; 176 | if (attr === 'gs__next') idx ++; 177 | if (idx < 0) idx = 0; 178 | if (idx >= global.items.length) return idx = global.items.length - 1; 179 | 180 | global.selectMode = global.items[idx]; 181 | 182 | if (global.selectMode === 'photos') { 183 | global.productInfo.elements['temp_photo'] = null; 184 | global.productInfo.temp_photo = ''; 185 | } 186 | showMessage(global); 187 | } 188 | 189 | global.selectElement = el => { 190 | if (!el) return; 191 | if (el.tagName.toLocaleLowerCase() === 'html') return; 192 | if (global.finish || !global.popup || global.confirm.contains(el)) return; 193 | if (global.popup.contains(el) || global.colormodal.contains(el)) { 194 | if(global.selectMode !== 'photos') copyFromTemp(global); 195 | return; 196 | } 197 | 198 | if (!global.selectMode) { 199 | global.productInfo = getProductInfo(el, global.picker); 200 | } else { 201 | getProductInfoIndividual(el, global.picker, global); 202 | } 203 | if (global.selectMode === 'color') showColorModal(global); 204 | else showMessage(global); 205 | } 206 | 207 | global.domPick = (el) => { 208 | console.log('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', el); 209 | if (!el) return ; 210 | if (el.tagName.toLocaleLowerCase() === 'html') return; 211 | if (!global.popup) return; 212 | if (global.popup.contains(el) || global.confirm.contains(el) || global.colormodal.contains(el)) { 213 | const attr = el.getAttribute('tag') 214 | const target = el.getAttribute('target') || ''; 215 | if (attr && attr !== 'gs__text') 216 | global.popupBtnClicked(attr, target); 217 | return ; 218 | } 219 | 220 | clearClass('gs_copied'); 221 | if (!global.selectMode) global.productInfo = getProductInfo(el, global.picker); 222 | addClass(global.productInfo.elements, 'gs_copied') 223 | 224 | if (global.selectMode) { 225 | if (global.selectMode === 'color') { 226 | copyToTemp(global); 227 | showColorModal(global); 228 | return; 229 | } 230 | let idx = global.items.indexOf(global.selectMode) + 1; 231 | if (global.selectMode === 'photos') { 232 | global.productInfo.elements['photo' + global.productInfo.photos.length] = global.productInfo.elements['temp_photo']; 233 | global.productInfo.photos.push(global.productInfo.temp_photo); 234 | idx --; 235 | } 236 | global.selectMode = global.items[idx]; 237 | copyToTemp(global); 238 | 239 | showMessage(global); 240 | return ; 241 | } 242 | copyToTemp(global); 243 | showConfirm(global); 244 | }; 245 | 246 | global.inputValueChanged = (e) => { 247 | const tag = e.target.getAttribute("tag"); 248 | const target = e.target.getAttribute("target"); 249 | if (tag === 'gs__text' || !target) { 250 | global.productInfo[target] = e.target.value; 251 | global.tempInfo[target] = e.target.value; 252 | console.log(global.productInfo) 253 | } 254 | } 255 | 256 | global.onMouseMove = (e) => { 257 | if(global.selectMode || global.showConfirm) { 258 | hideTooltip(global); 259 | return ; 260 | } 261 | global.tooltip.style.left = e.clientX + 'px'; 262 | global.tooltip.style.top = e.clientY + 'px'; 263 | showTooltip(global); 264 | } 265 | 266 | addStyle(` 267 | .gs_hover { 268 | border: 2px solid #cdcdcd !important; 269 | background: repeating-linear-gradient( 135deg, rgba(225, 225, 226, 0.3), rgba(229, 229, 229, 0.3) 10px, rgba(173, 173, 173, 0.3) 10px, rgba(172, 172, 172, 0.3) 20px ) !important; 270 | box-shadow: inset 0px 0px 0px 1px #d7d7d7; 271 | } 272 | 273 | .gs_copied { 274 | border: 3px solid #ff0a00 !important; 275 | background: repeating-linear-gradient( 135deg, rgba(183, 240, 200, 0.3), rgba(192, 231, 194, 0.3) 10px, rgba(124, 189, 126, 0.3) 10px, rgba(137, 180, 129, 0.3) 20px ) !important; 276 | box-shadow: inset 0px 0px 0px 1px #c4d9c2 !important; 277 | } 278 | `); 279 | initMessage(global); 280 | }; 281 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | const updateIcon = async isPressed => { 2 | await chrome.action.setIcon({ path: `icon_128${isPressed ? "_pressed" : ""}.png` }); 3 | } 4 | 5 | const sendMessage = data => { 6 | return new Promise(resolve => { 7 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 8 | if (tabs.length === 0) resolve(); 9 | chrome.tabs.sendMessage(tabs[0].id, data, function(response) { 10 | resolve(response); 11 | }); 12 | }); 13 | }); 14 | } 15 | 16 | const generateCeID = () => { 17 | return Date.now() + '_' + parseInt(Math.random() * 10000) + '_' + parseInt(Math.random() * 10000); 18 | } 19 | 20 | const getCeID = () => { 21 | return new Promise(resolve => { 22 | chrome.storage.local.get(["ce_id"], async function(result){ 23 | if (result.ce_id && result.ce_id.length > 10) return resolve(result.ce_id); 24 | const ce_id = generateCeID(); 25 | chrome.storage.local.set({ ce_id }, async function(result){ 26 | resolve(ce_id) 27 | }); 28 | }); 29 | }); 30 | } 31 | 32 | const getCurrentState = async () => { 33 | return await sendMessage({ type: 'get_state' }); 34 | } 35 | 36 | const toggleState = async () => { 37 | return await sendMessage({ type: 'toggle_state' }); 38 | } 39 | 40 | const currentTabChanged = async () => { 41 | await updateIcon(await getCurrentState()); 42 | const ce_id = await getCeID(); 43 | await sendMessage({ type: 'ce_id', ce_id }); 44 | } 45 | 46 | chrome.tabs.onUpdated.addListener(async function(tabId, changeInfo, tab) { 47 | if (changeInfo.status === 'complete') { 48 | await currentTabChanged(); 49 | } 50 | }); 51 | 52 | chrome.tabs.onActivated.addListener(async tab => { 53 | await currentTabChanged(); 54 | }); 55 | 56 | chrome.runtime.onInstalled.addListener(async ({ reason, version }) => { 57 | if (reason === chrome.runtime.OnInstalledReason.INSTALL) { 58 | } 59 | }); 60 | 61 | chrome.action.onClicked.addListener(async (tab) => { 62 | const state = await toggleState(); 63 | await updateIcon(state); 64 | }); 65 | 66 | chrome.runtime.onMessage.addListener( 67 | async function(request, sender, sendResponse) { //sender.tab 68 | switch(request.type) { 69 | case 'init': 70 | sendResponse({ type: "init" }); 71 | break; 72 | case 'close': 73 | sendResponse({ type: "close" }); 74 | await updateIcon(false); 75 | break; 76 | } 77 | } 78 | ); 79 | 80 | -------------------------------------------------------------------------------- /src/dom-pick/element-overlay.d.ts: -------------------------------------------------------------------------------- 1 | import { BoundingBox, ElementOverlayOptions } from "./utils"; 2 | export default class ElementOverlay { 3 | overlay: HTMLDivElement; 4 | shadowContainer: HTMLDivElement; 5 | shadowRoot: ShadowRoot; 6 | usingShadowDOM?: boolean; 7 | constructor(options: ElementOverlayOptions); 8 | addToDOM(parent: Node, useShadowDOM: boolean): void; 9 | removeFromDOM(): void; 10 | captureCursor(): void; 11 | ignoreCursor(): void; 12 | setBounds({ x, y, width, height }: BoundingBox): void; 13 | getBounds(): BoundingBox 14 | } 15 | -------------------------------------------------------------------------------- /src/dom-pick/element-overlay.js: -------------------------------------------------------------------------------- 1 | export default class ElementOverlay { 2 | constructor(options) { 3 | var _a, _b, _c, _d, _e, _f, _g, _h, _j; 4 | this.overlay = document.createElement("div"); 5 | this.overlay.className = options.className || "_ext-element-overlay"; 6 | this.overlay.style.background = ((_a = options.style) === null || _a === void 0 ? void 0 : _a.background) || "rgba(250, 240, 202, 0.2)"; 7 | this.overlay.style.borderColor = ((_b = options.style) === null || _b === void 0 ? void 0 : _b.borderColor) || "#F95738"; 8 | this.overlay.style.borderStyle = ((_c = options.style) === null || _c === void 0 ? void 0 : _c.borderStyle) || "solid"; 9 | this.overlay.style.borderRadius = ((_d = options.style) === null || _d === void 0 ? void 0 : _d.borderRadius) || "1px"; 10 | this.overlay.style.borderWidth = ((_e = options.style) === null || _e === void 0 ? void 0 : _e.borderWidth) || "1px"; 11 | this.overlay.style.boxSizing = ((_f = options.style) === null || _f === void 0 ? void 0 : _f.boxSizing) || "border-box"; 12 | this.overlay.style.cursor = ((_g = options.style) === null || _g === void 0 ? void 0 : _g.cursor) || "crosshair"; 13 | this.overlay.style.position = ((_h = options.style) === null || _h === void 0 ? void 0 : _h.position) || "absolute"; 14 | this.overlay.style.zIndex = ((_j = options.style) === null || _j === void 0 ? void 0 : _j.zIndex) || "2147483647"; 15 | // this.overlay.style.transition = "all .2s linear"; 16 | this.shadowContainer = document.createElement("div"); 17 | this.shadowContainer.className = "_ext-element-overlay-container"; 18 | this.shadowContainer.style.position = "absolute"; 19 | this.shadowContainer.style.top = "0px"; 20 | this.shadowContainer.style.left = "0px"; 21 | this.shadowRoot = this.shadowContainer.attachShadow({ mode: "open" }); 22 | } 23 | addToDOM(parent, useShadowDOM) { 24 | this.usingShadowDOM = useShadowDOM; 25 | if (useShadowDOM) { 26 | parent.insertBefore(this.shadowContainer, parent.firstChild); 27 | this.shadowRoot.appendChild(this.overlay); 28 | } 29 | else { 30 | parent.appendChild(this.overlay); 31 | } 32 | } 33 | removeFromDOM() { 34 | this.setBounds({ x: 0, y: 0, width: 0, height: 0 }); 35 | this.overlay.remove(); 36 | if (this.usingShadowDOM) { 37 | this.shadowContainer.remove(); 38 | } 39 | } 40 | captureCursor() { 41 | this.overlay.style.pointerEvents = "auto"; 42 | } 43 | ignoreCursor() { 44 | this.overlay.style.pointerEvents = "none"; 45 | } 46 | setBounds({ x, y, width, height }) { 47 | this.overlay.style.left = x + "px"; 48 | this.overlay.style.top = y + "px"; 49 | this.overlay.style.width = width + "px"; 50 | this.overlay.style.height = height + "px"; 51 | } 52 | getBounds() { 53 | return { 54 | x: parseFloat(this.overlay.style.left), 55 | y: parseFloat(this.overlay.style.top), 56 | width: parseFloat(this.overlay.style.width), 57 | height: parseFloat(this.overlay.style.height), 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/dom-pick/element-picker.d.ts: -------------------------------------------------------------------------------- 1 | import { ElementOverlayOptions } from "./utils"; 2 | declare type ElementCallback = (el: HTMLElement) => T; 3 | declare type ElementPickerOptions = { 4 | parentElement?: Node; 5 | useShadowDOM?: boolean; 6 | onClick?: ElementCallback; 7 | onHover?: ElementCallback; 8 | elementFilter?: ElementCallback; 9 | }; 10 | export default class ElementPicker { 11 | private overlay; 12 | private active; 13 | private options?; 14 | private target?; 15 | private mouseX?; 16 | private mouseY?; 17 | private tickReq?; 18 | constructor(overlayOptions?: ElementOverlayOptions); 19 | start(options: ElementPickerOptions): boolean; 20 | stop(): void; 21 | private handleMouseMove; 22 | private handleClick; 23 | private tick; 24 | private updateTarget; 25 | } 26 | export {}; 27 | -------------------------------------------------------------------------------- /src/dom-pick/element-picker.js: -------------------------------------------------------------------------------- 1 | import ElementOverlay from "./element-overlay"; 2 | import { getElementBounds, checkSimilarBounds } from "./utils"; 3 | export default class ElementPicker { 4 | constructor(overlayOptions) { 5 | this.handleMouseMove = (event) => { 6 | this.mouseX = event.clientX; 7 | this.mouseY = event.clientY; 8 | }; 9 | this.handleClick = (event) => { 10 | var _a; 11 | if (this.target && this.target.getAttribute('gsallow')) return; 12 | if (this.target && ((_a = this.options) === null || _a === void 0 ? void 0 : _a.onClick)) { 13 | this.options.onClick(this.target); 14 | } 15 | event.preventDefault(); 16 | }; 17 | this.tick = () => { 18 | this.updateTarget(); 19 | this.tickReq = window.requestAnimationFrame(this.tick); 20 | }; 21 | this.active = false; 22 | this.overlay = new ElementOverlay(overlayOptions !== null && overlayOptions !== void 0 ? overlayOptions : {}); 23 | } 24 | start(options) { 25 | var _a, _b; 26 | if (this.active) { 27 | return false; 28 | } 29 | this.active = true; 30 | this.options = options; 31 | document.addEventListener("mousemove", this.handleMouseMove, true); 32 | document.addEventListener("click", this.handleClick, true); 33 | this.overlay.addToDOM((_a = options.parentElement) !== null && _a !== void 0 ? _a : document.body, (_b = options.useShadowDOM) !== null && _b !== void 0 ? _b : true); 34 | this.tick(); 35 | return true; 36 | } 37 | stop() { 38 | this.active = false; 39 | this.options = undefined; 40 | document.removeEventListener("mousemove", this.handleMouseMove, true); 41 | document.removeEventListener("click", this.handleClick, true); 42 | this.overlay.removeFromDOM(); 43 | this.target = undefined; 44 | this.mouseX = undefined; 45 | this.mouseY = undefined; 46 | if (this.tickReq) { 47 | window.cancelAnimationFrame(this.tickReq); 48 | } 49 | } 50 | updateTarget() { 51 | var _a, _b; 52 | if (this.mouseX === undefined || this.mouseY === undefined) { 53 | return; 54 | } 55 | // Peek through the overlay to find the new target 56 | this.overlay.ignoreCursor(); 57 | const elAtCursor = document.elementFromPoint(this.mouseX, this.mouseY); 58 | const newTarget = elAtCursor; 59 | this.overlay.captureCursor(); 60 | // If the target hasn't changed, there's nothing to do 61 | if (!newTarget) return; 62 | // If we have an element filter and the new target doesn't match, 63 | // clear out the target 64 | if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.elementFilter) { 65 | if (!this.options.elementFilter(newTarget)) { 66 | this.target = undefined; 67 | this.overlay.setBounds({ x: 0, y: 0, width: 0, height: 0 }); 68 | return; 69 | } 70 | } 71 | 72 | const bounds = getElementBounds(newTarget); 73 | if (newTarget === this.target) { 74 | const ori_bounds = this.overlay.getBounds(); 75 | if (checkSimilarBounds(bounds, ori_bounds)) 76 | return ; 77 | } 78 | 79 | this.target = newTarget; 80 | this.overlay.setBounds(bounds); 81 | if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.onHover) { 82 | this.options.onHover(newTarget); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/dom-pick/index.d.ts: -------------------------------------------------------------------------------- 1 | import ElementPicker from "./element-picker"; 2 | export { ElementPicker }; 3 | -------------------------------------------------------------------------------- /src/dom-pick/index.js: -------------------------------------------------------------------------------- 1 | import ElementPicker from "./element-picker"; 2 | export { ElementPicker }; 3 | -------------------------------------------------------------------------------- /src/dom-pick/utils.d.ts: -------------------------------------------------------------------------------- 1 | export interface BoundingBox { 2 | x: number; 3 | y: number; 4 | width: number; 5 | height: number; 6 | } 7 | export interface ElementOverlayStyleOptions { 8 | background?: string; 9 | borderColor?: string; 10 | borderStyle?: string; 11 | borderRadius?: string; 12 | borderWidth?: string; 13 | boxSizing?: string; 14 | cursor?: string; 15 | position?: string; 16 | zIndex?: string; 17 | } 18 | export declare type ElementOverlayOptions = { 19 | className?: string; 20 | style?: ElementOverlayStyleOptions; 21 | }; 22 | export declare const getElementBounds: (el: HTMLElement) => BoundingBox; 23 | export declare const checkSimilarBounds: (b1: BoundingBox, b2: BoundingBox) => boolean 24 | -------------------------------------------------------------------------------- /src/dom-pick/utils.js: -------------------------------------------------------------------------------- 1 | ; 2 | export const getElementBounds = (el) => { 3 | const rect = el.getBoundingClientRect(); 4 | return { 5 | x: window.pageXOffset + rect.left, 6 | y: window.pageYOffset + rect.top, 7 | width: el.offsetWidth, 8 | height: el.offsetHeight, 9 | }; 10 | }; 11 | 12 | export const checkSimilarBounds = (b1, b2) => { 13 | const keys = ['x', 'y', 'width', 'height']; 14 | for (let i = 0 ; i < keys.length; i ++) { 15 | if (Math.abs(b1[keys[i]] - b2[keys[i]]) > 0.1 ) 16 | return false; 17 | } 18 | return true; 19 | } -------------------------------------------------------------------------------- /src/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AAA0109/DomPicker_Extension/563b131f70fa977fe29ffc16e414c3ec75c98afc/src/icon_128.png -------------------------------------------------------------------------------- /src/icon_128_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AAA0109/DomPicker_Extension/563b131f70fa977fe29ffc16e414c3ec75c98afc/src/icon_128_pressed.png -------------------------------------------------------------------------------- /src/icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AAA0109/DomPicker_Extension/563b131f70fa977fe29ffc16e414c3ec75c98afc/src/icon_32.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { init, toggle } from "./app"; 2 | 3 | !(() => { 4 | const global = window.__gs = window.__gs || {}; 5 | // console.log('[Ollacart] Init', global); 6 | if (!global.init) { 7 | // console.log("[Ollacart Selector]: Started"); 8 | init(global); 9 | 10 | global.sendClose = () => { 11 | chrome.runtime.sendMessage({type: "close"}, function(response) { 12 | // console.log(response); 13 | }); 14 | } 15 | 16 | chrome.runtime.sendMessage({type: "init"}, function(response) { 17 | // console.log(response); 18 | }); 19 | 20 | chrome.runtime.onMessage.addListener( 21 | function(request, sender, sendResponse) { 22 | // console.log(request); 23 | switch(request.type) { 24 | case 'get_state': 25 | sendResponse(global.state); 26 | break; 27 | case 'toggle_state': 28 | toggle(global); 29 | sendResponse(global.state); 30 | case 'ce_id': 31 | if (request.ce_id) 32 | localStorage.setItem('ce_id', request.ce_id); 33 | } 34 | } 35 | ); 36 | } 37 | })(); -------------------------------------------------------------------------------- /src/info.js: -------------------------------------------------------------------------------- 1 | import { addStyle } from "./addStyle"; 2 | 3 | const STYLES = ` 4 | .gs_confirm_container, .gs_message, .gs_tooltip { 5 | box-sizing: border-box !important; 6 | } 7 | .gs_confirm_container *, .gs_message *, .gs_tooltip * { 8 | color: black !important; 9 | box-sizing: border-box !important; 10 | font-size: 16px !important; 11 | appearance: unset !important; 12 | position: unset !important; 13 | margin: unset !important; 14 | opacity: unset !important; 15 | visibility: unset !important; 16 | } 17 | .gs_confirm_container input[type=checkbox] { 18 | width: 13px !important; 19 | height: 13px !important; 20 | } 21 | .gs_confirm_container input, .gs_message input { 22 | border: 1px solid black !important; 23 | } 24 | .gs_hidden { visibility: hidden; } 25 | .gs_d-none { display: none !important; } 26 | .gs_tooltip { 27 | position: fixed !important; 28 | z-index: 99999999999999 !important; 29 | max-width: 500px !important; 30 | width: 80% !important; 31 | pointer-events: none !important; 32 | display: none !important; 33 | 34 | background-color: #ffa778 !important; 35 | padding: 5px 10px !important; 36 | box-shadow: 1px 1px 5px 2px #260101 !important; 37 | line-height: 16px !important; 38 | font-size: 16px !important; 39 | color: black !important; 40 | } 41 | .gs_checkbox { 42 | width: 18px !important; 43 | height: 18px !important; 44 | cursor: pointer !important; 45 | } 46 | .gs_tooltip.gs_show { 47 | display: block !important; 48 | } 49 | .gs_confirm_container { 50 | position: fixed !important; 51 | left: 0 !important; 52 | top: 0 !important; 53 | width: 100vw !important; 54 | height: 100vh !important; 55 | background-color: #ff450040 !important; 56 | z-index: 9999999999999 !important; 57 | display: none !important; 58 | } 59 | .gs_confirm_container.gs_hide { 60 | opacity: 0 !important; 61 | transition: opacity 2s !important; 62 | transition-delay: 4s !important; 63 | } 64 | .gs_message, .gs_confirm { 65 | position: fixed !important; 66 | box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.25) !important; 67 | padding: 30px 10px 8px !important; 68 | background-color: #fff !important; 69 | border: 4px solid #eee !important; 70 | } 71 | .gs_confirm { 72 | left: calc(50vw - 350px) !important; 73 | top: 80px !important; 74 | } 75 | .gs_hide .gs_confirm { 76 | display: none !important; 77 | } 78 | .gs_confirm_content { 79 | width: 730px !important; 80 | max-width: 100% !important; 81 | max-height: calc(100vh - 150px) !important; 82 | overflow-y: auto !important; 83 | display: flex !important; 84 | gap: 20px !important; 85 | flex-wrap: wrap !important; 86 | align-items: flex-start !important; 87 | } 88 | @media screen and (max-width: 768px) { 89 | .gs_confirm { 90 | width: 290px !important; 91 | left: calc(50vw - 150px) !important; 92 | } 93 | } 94 | .gs_message { 95 | display: none !important; 96 | left: 10px !important; 97 | bottom: 10px !important; 98 | z-index: 999999999999 !important; 99 | width: 300px !important; 100 | } 101 | .gs_message_content { 102 | display: flex !important; 103 | max-height: calc(100vh - 100px) !important; 104 | overflow-y: auto !important; 105 | min-height: 250px !important; 106 | flex-direction: column !important; 107 | } 108 | 109 | .gs_confirm_container.gs_show, .gs_message.gs_show { 110 | display: inline-block !important; 111 | } 112 | .gs_ollacart_img img { 113 | width: 100% !important; 114 | } 115 | .gs_confirm .gs_ollacart_img { 116 | width: 350px !important; 117 | /* position: sticky; */ 118 | /* top: 0; */ 119 | } 120 | .gs_name_price { 121 | display: flex !important; 122 | justify-content: space-between !important; 123 | font-size: 16px !important; 124 | color: black !important; 125 | gap: 10px !important; 126 | } 127 | .gs_confirm .gs_name_price { 128 | font-size: 20px !important; 129 | font-weight: bold !important; 130 | color: #303030 !important; 131 | } 132 | .gs_confirm .gs_price { 133 | color: #004400 !important; 134 | } 135 | .gs_description { 136 | font-size: 14px !important; 137 | margin-top: 10px !important; 138 | white-space: break-spaces !important; 139 | } 140 | .gs_message_over, .gs_message_finish { 141 | position: absolute !important; 142 | left: 0 !important; 143 | right: 0 !important; 144 | top: -20px !important; 145 | background-color: orangered !important; 146 | color: white !important; 147 | text-align: center !important; 148 | padding: 8px 0 !important; 149 | font-size: 20px !important; 150 | font-weight: bold !important; 151 | white-space: nowrap !important; 152 | cursor: pointer !important; 153 | } 154 | .gs_message_mask { 155 | position: absolute !important; 156 | left: -4px !important; 157 | right: -4px !important; 158 | top: -4px !important; 159 | bottom: -4px !important; 160 | background-color: #ff450040 !important; 161 | } 162 | .gs_message_finish { 163 | font-size: 30px !important; 164 | top: calc(50% - 100px) !important; 165 | padding: 50px 0 !important; 166 | } 167 | .gs_addtional_photos { 168 | margin-top: 5px !important; 169 | display: flex !important; 170 | flex-wrap: wrap !important; 171 | gap: 10px !important; 172 | } 173 | .gs_addtional_photos>div { 174 | position: relative !important; 175 | width: 46px !important; 176 | height: 60px !important; 177 | overflow: hidden !important; 178 | display: flex !important; 179 | justify-content: center !important; 180 | align-items: center !important; 181 | border: 1px solid blue !important; 182 | } 183 | .gs_addtional_photos .gs_remove_photo { 184 | transform: translateY(100%) !important; 185 | opacity: 0 !important; 186 | transition: all .3s !important; 187 | position: absolute !important; 188 | width: 100% !important; 189 | height: 100% !important; 190 | background-color: #000000A0 !important; 191 | display: flex !important; 192 | justify-content: center !important; 193 | align-items: center !important; 194 | } 195 | .gs_addtional_photos>div:hover .gs_remove_photo { 196 | transform: translateY(0) !important; 197 | opacity: 1 !important; 198 | } 199 | .gs_addtional_photos .gs_remove_photo .gs_remove_btn { 200 | width: 30px !important; 201 | height: 30px !important; 202 | border-radius: 50% !important; 203 | cursor: pointer !important; 204 | display: flex !important; 205 | justify-content: center !important; 206 | align-items: center !important; 207 | font-size: 30px !important; 208 | font-weight: bold !important; 209 | background-color: rgb(200, 200, 200) !important; 210 | color: black !important; 211 | } 212 | .gs_addtional_photos img { 213 | width: 100% !important; 214 | } 215 | 216 | .gs_manual_select_tools { 217 | flex-grow: 1 !important; 218 | display: flex !important; 219 | align-items: flex-end !important; 220 | justify-content: space-between !important; 221 | margin-top: 10px !important; 222 | } 223 | .gs_confirm_tools { 224 | display: flex !important; 225 | gap: 15px !important; 226 | justify-content: flex-end !important; 227 | align-items: flex-end !important; 228 | flex-grow: 1 !important; 229 | margin-top: 10px !important; 230 | } 231 | .gs_btn { 232 | padding: 4px 10px !important; 233 | background-color: orangered !important; 234 | color: white !important; 235 | font-size: 16px !important; 236 | font-weight: bold !important; 237 | cursor: pointer !important; 238 | border-radius: 7px !important; 239 | } 240 | .gs_btn:hover { 241 | opacity: 0.8 !important; 242 | } 243 | .gs_btn:active { 244 | opacity: 0.7 !important; 245 | } 246 | .gs_btn.gs_direct { 247 | padding: 4px 14px !important; 248 | } 249 | 250 | .gs_confirm_right { 251 | display: flex !important; 252 | flex-direction: column !important; 253 | flex-grow: 1 !important; 254 | width: 1px !important; 255 | } 256 | .gs_text_center { 257 | text-align: center !important; 258 | } 259 | .gs_go_ollacart { 260 | margin-top: 20px !important; 261 | font-size: 20px !important; 262 | line-height: 25px !important; 263 | cursor: pointer !important; 264 | color: lightseagreen !important; 265 | } 266 | .gs_textarea { 267 | width: 100% !important; 268 | height: 300px !important; 269 | min-height: 300px !important; 270 | max-height: 300px !important; 271 | font-size: 16px !important; 272 | } 273 | 274 | .gs_close { 275 | position: absolute !important; 276 | top: 5px !important; 277 | right: 5px !important; 278 | cursor: pointer !important; 279 | } 280 | .gs_close:hover { 281 | opacity: 0.8 !important; 282 | } 283 | .gs_close img { 284 | width: 20px !important; 285 | } 286 | 287 | .gs_addtional_picker { 288 | margin-top: 15px !important; 289 | margin-left: auto !important; 290 | } 291 | .gs_addtional_picker>div { 292 | width: 200px !important; 293 | margin-top: 5px !important; 294 | display: flex !important; 295 | align-items: center !important; 296 | justify-content: space-between !important; 297 | } 298 | .gs_addtional_picker>div>div { 299 | display: flex !important; 300 | align-items: center !important; 301 | gap: 5px !important; 302 | } 303 | .gs_addtional_picker>div>*:nth-child(2) { 304 | width: 70px !important; 305 | } 306 | .gs_addtional_picker .gs_color-img { 307 | aspect-ratio: 1 !important; 308 | text-align: center !important; 309 | border: 1px solid orangered !important; 310 | object-fit: contain !important; 311 | padding: 4px !important; 312 | border-radius: 8px !important; 313 | cursor: pointer !important; 314 | } 315 | .gs_addtional_picker .gs_color-img:hover { 316 | opacity: 0.8 !important; 317 | } 318 | 319 | .gs_confirm_content::-webkit-scrollbar, .gs_message_content::-webkit-scrollbar { 320 | width: 7px !important; 321 | } 322 | .gs_confirm_content::-webkit-scrollbar-track, .gs_message_content::-webkit-scrollbar-track { 323 | background: #f1f1f1 !important; 324 | } 325 | .gs_confirm_content::-webkit-scrollbar-thumb, .gs_message_content::-webkit-scrollbar-thumb { 326 | background: #e19b9b !important; 327 | } 328 | .gs_confirm_content::-webkit-scrollbar-thumb:hover, .gs_message_content::-webkit-scrollbar-thumb:hover { 329 | background: #e19b9bd0 !important; 330 | } 331 | `; 332 | 333 | const manualSelect = { 334 | img: 'Main Logo', 335 | name: 'Title', 336 | price: 'Price', 337 | description: 'Description', 338 | photos: 'Images' 339 | } 340 | 341 | export const showMessage = (global) => { 342 | const info = global.productInfo; 343 | let html = '
'; 344 | if (!global.selectMode || global.selectMode === 'img') html += `
`; 345 | if (!global.selectMode) { 346 | html += `
${info.name}${info.price || ''}
`; 347 | } 348 | if (!global.selectMode) html += `
${info.description}
` 349 | if (global.selectMode === 'name' || global.selectMode === 'price' || global.selectMode === 'description') { 350 | html += `` 351 | } 352 | if (!global.selectMode || global.selectMode === 'photos') { 353 | html += `
` 354 | for (let i = 0; info.photos && (i < info.photos.length); i ++ ) { 355 | if (info.photos[i]) 356 | html += `
-
`; 357 | } 358 | html += `
` 359 | } 360 | if (global.selectMode === 'photos' && info.temp_photo) { 361 | html += `
`; 362 | } 363 | 364 | if (global.selectMode) { 365 | html += `
366 |
<
367 |
Finish
368 |
>
369 |
` 370 | } 371 | html += `
`; 372 | 373 | if (global.selectMode) { 374 | html += `
Select ${manualSelect[global.selectMode]}
` 375 | } else { 376 | html += `
Auto Select
`; 377 | } 378 | 379 | global.popup.innerHTML = html; 380 | global.popup.classList.toggle("gs_show", true); 381 | }; 382 | 383 | export const showColorModal = (global) => { 384 | const info = global.productInfo; 385 | let html = '
'; 386 | html += `
`; 387 | 388 | if (global.selectMode) { 389 | html += `
390 | 391 |
Finish
392 | 393 |
` 394 | } 395 | html += `
`; 396 | 397 | html += `
Specify Color
` 398 | 399 | global.colormodal.innerHTML = html; 400 | global.colormodal.classList.toggle("gs_show", true); 401 | }; 402 | 403 | export const showConfirm = global => { 404 | hideMessage(global); 405 | hideColorModal(global); 406 | hideTooltip(global); 407 | 408 | const info = global.productInfo; 409 | let html = `
close
` 410 | html += `

Go to OllaCart

`; 411 | html += `
${info.name}$${info.price || '0'}
`; 412 | html += `
413 |
414 |
c Size notes
415 | 416 |
417 | 421 |
`; 422 | if (info.description) html += `
${info.description}
` 423 | for (let i = 0; info.photos && (i < info.photos.length); i ++ ) { 424 | if (i === 0) html += `
` 425 | if (info.photos[i]) 426 | html += `
-
`; 427 | if (i === info.photos.length - 1) html += `
` 428 | } 429 | 430 | html += `
431 |
Looks Correct
432 |
Manual Select
433 |
` 434 | 435 | html += '
'; 436 | // html += `
You selected item
`; 437 | html += `
` 438 | 439 | if (global.finish) html += `
Added to OllaCart
`; 440 | 441 | global.confirm.innerHTML = html; 442 | global.confirm.classList.toggle("gs_show", true); 443 | global.showConfirm = true; 444 | 445 | if (global.finish) global.confirm.classList.toggle("gs_hide", true); 446 | else global.confirm.classList.toggle("gs_hide", false); 447 | } 448 | 449 | export const showTooltip = global => { 450 | global.tooltip.classList.toggle("gs_show", true); 451 | } 452 | 453 | export const hideTooltip = global => { 454 | global.tooltip.classList.toggle("gs_show", false); 455 | }; 456 | 457 | export const hideMessage = global => { 458 | global.popup.classList.toggle("gs_show", false); 459 | }; 460 | 461 | export const hideColorModal = global => { 462 | global.colormodal.classList.toggle("gs_show", false); 463 | } 464 | 465 | export const hideConfirm = global => { 466 | global.confirm.classList.toggle("gs_show", false); 467 | global.showConfirm = false; 468 | } 469 | 470 | export const initMessage = global => { 471 | addStyle(STYLES); 472 | global.popup = document.createElement("div"); 473 | global.popup.className = "gs_message"; 474 | document.body.appendChild(global.popup); 475 | 476 | global.colormodal = document.createElement("div"); 477 | global.colormodal.className = "gs_message"; 478 | document.body.appendChild(global.colormodal); 479 | 480 | global.confirm = document.createElement("div"); 481 | global.confirm.className = "gs_confirm_container"; 482 | document.body.appendChild(global.confirm); 483 | 484 | global.tooltip = document.createElement("div"); 485 | global.tooltip.innerHTML = 'Click whenever OllaCart shows appropriate product information. You can manually select and edit information after.'; 486 | global.tooltip.className = "gs_tooltip"; 487 | document.body.appendChild(global.tooltip); 488 | }; 489 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OllaCart", 3 | "version": "1.0.0", 4 | "manifest_version": 3, 5 | "description": "Chrome Extension for Ollacart. Add your items to OllaCart.", 6 | "permissions": ["storage", "tabs", "activeTab"], 7 | "content_scripts": [{ 8 | "matches": [""], 9 | "js": ["inject.js"], 10 | "all_frames": false 11 | }], 12 | "background": { 13 | "service_worker": "background.js" 14 | }, 15 | "action": { }, 16 | "icons": { 17 | "32": "icon_32.png", 18 | "128": "icon_128.png" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/scrap.js: -------------------------------------------------------------------------------- 1 | const Constant = { 2 | title: ['title', 'name'], 3 | price: ['price'], 4 | description: ['description', 'detail', 'info'] 5 | } 6 | 7 | const checkIfSimilarProductContainer = (el, attrs = []) => { 8 | const area_limit = 80 * 80, txt_limit_ct = 2; 9 | var txt_ct = 0; 10 | const itms = el.getElementsByTagName('*'); 11 | for (let i = 0; i < itms.length; i ++) { 12 | if (getText(itms[i])) txt_ct ++; 13 | if (txt_ct > txt_limit_ct) break; 14 | } 15 | if (txt_ct < txt_limit_ct) return false; 16 | 17 | const imgs = el.getElementsByTagName('img'); 18 | let i = 0; 19 | for (i = 0; i < imgs.length; i ++) { 20 | const img = imgs[i]; 21 | const area = img.width * img.height; 22 | if (area < area_limit) continue; 23 | break; 24 | } 25 | if (i === imgs.length) return false; 26 | if (!attrs.length) return true; 27 | 28 | const htmlStr = (el.innerHTML || '').toLocaleLowerCase(); 29 | i = 0; 30 | for (i = 0; i < attrs.length; i ++) { 31 | let j = 0; 32 | for (j = 0; j < attrs[i].length; j ++) 33 | if(htmlStr.includes(attrs[i][j])) 34 | break; 35 | if (j === attrs[i].length) break; 36 | } 37 | 38 | if (i && i === attrs.length) return true; 39 | return false; 40 | } 41 | 42 | const checkIfSimilarItem = (a, b) => { 43 | if (!checkIfSimilarProductContainer(a) || !checkIfSimilarProductContainer(b)) return 0; 44 | const tag1 = a.tagName, tag2 = b.tagName; 45 | if (tag1.toLocaleLowerCase() !== tag2.toLocaleLowerCase()) return 0; 46 | const attr1 = a.attributes, attr2 = b.attributes; 47 | let ct = 0; 48 | for (let i = 0; i < attr1.length; i ++) { 49 | const attr = attr1[i].name || ''; 50 | if (!attr) continue; 51 | let j = 0; 52 | for (j = 0; j < attr2.length; j ++) { 53 | if (attr2[j].name == attr) break; 54 | } 55 | if (j === attr2.length) { 56 | continue; 57 | } 58 | ct ++; 59 | } 60 | let rate = Math.min(((ct * 2) / (attr1.length + attr2.length)) * 1.5, 1); 61 | if (attr1.length + attr2.length === 0) rate = 1; 62 | return rate; 63 | } 64 | 65 | const checkIfListContainer = el => { 66 | const t = 0.9; 67 | let p = el.parentNode; 68 | while (p && p.parentNode) { 69 | const pp = p.parentNode; 70 | const chs = pp.children; 71 | const rets = []; 72 | let ct = 0; 73 | for (let i = 0; i < chs.length; i ++) { 74 | let max = 0; 75 | for (let j = 0; j < chs.length; j ++) { 76 | if (i === j) continue; 77 | const a = chs[i], b = chs[j]; 78 | const ret = checkIfSimilarItem(a, b); 79 | max = Math.max(max, ret); 80 | if (max >= t) break; 81 | } 82 | if (max < t) ct ++; 83 | if (ct > 1) break; 84 | } 85 | if (ct < 2 && chs.length > 2) return p; 86 | p = p.parentNode; 87 | } 88 | return null; 89 | } 90 | 91 | const getProductRootElement = el => { 92 | const check_list = checkIfListContainer(el); 93 | if (check_list) return check_list; 94 | if (checkIfSimilarProductContainer(el, [Constant.title])) return el; 95 | let p = el.parentNode; 96 | while (p && p.tagName.toLocaleLowerCase() !== 'html') { 97 | if (checkIfSimilarProductContainer(p, [Constant.title])) return p; 98 | p = p.parentNode; 99 | } 100 | return el; 101 | } 102 | 103 | const isVisible = el => { 104 | const style = window.getComputedStyle(el); 105 | if (style.opacity === '0') return false; 106 | if (style.visibility == 'hidden') return false; 107 | const r = el.getBoundingClientRect(); 108 | if (r.width < 10 || r.height < 10) return false; 109 | return true; 110 | } 111 | 112 | const checkIfHasAttribute = (el, attr) => { 113 | const keys = el.attributes; 114 | for (let i = 0; i < keys.length; i ++) { 115 | const key = keys[i].name || ''; 116 | const value = el.getAttribute(key) || ''; 117 | if (key.toLocaleLowerCase().includes(attr) || value.toLocaleLowerCase().includes(attr)) return true; 118 | } 119 | return false; 120 | } 121 | 122 | const checkIfDescendOf = (ch, p, signs) => { 123 | while(ch && ch !== p) { 124 | for (let i = 0; i < signs.length; i ++) 125 | if (checkIfHasAttribute(ch, signs[i])) 126 | return true; 127 | ch = ch.parentNode; 128 | } 129 | return false; 130 | } 131 | 132 | const isHovered = (r, e) => { 133 | const x = e.x, y = e.y; 134 | if (r.left <= x && r.right >= x && r.top <= y && r.bottom >= y) return true; 135 | return false; 136 | } 137 | 138 | const checkIfBetterImg = (a, b, mouse, excepts = []) => { 139 | if (!isVisible(a)) return false; 140 | if (!isVisible(b)) return true; 141 | const a_src = getSrcFromImgTag(a), b_src = getSrcFromImgTag(b); 142 | if (!a_src) return false; 143 | if (!b_src) return true; 144 | const excepts_src = excepts.map(itm => getSrcFromImgTag(itm)); 145 | if (excepts_src.includes(a_src)) return false; 146 | if (excepts_src.includes(b_src)) return true; 147 | 148 | const offset = 2; 149 | const r1 = a.getBoundingClientRect(), r2 = b.getBoundingClientRect(); 150 | const h1 = isHovered(r1, mouse), h2 = isHovered(r2, mouse); 151 | if (h1 && !h2) return true; 152 | if (!h1 && h2) return false; 153 | 154 | const area1 = r1.width * r1.height, area2 = r2.width * r2.height; 155 | if (Math.abs(area1 - area2) < offset * offset) { 156 | if (Math.abs(r1.x - r2.x) < offset && Math.abs(r1.y - r2.y) < offset) return true; 157 | } 158 | if (area1 > area2) return true; 159 | return false; 160 | } 161 | 162 | const containsAnyLetters = str => { 163 | return /[a-zA-Z0-9]/.test(str); 164 | } 165 | 166 | const getText = el => { 167 | if (!el) return ''; 168 | if (['noscript', 'img'].indexOf(el.nodeName.toLocaleLowerCase()) > -1) return ''; 169 | if (!isVisible(el)) return false; 170 | try { 171 | const childNodes = el.childNodes; 172 | var hasText = false; 173 | for (let i = 0; i < childNodes.length; i++) { 174 | if (childNodes[i].nodeType === Node.TEXT_NODE) { 175 | var txt = childNodes[i].textContent; 176 | if (!containsAnyLetters(txt)) continue; 177 | hasText = true; 178 | break; 179 | } 180 | } 181 | if (hasText) return (el.innerText || el.textContent || '').replace(/\n/g, ''); 182 | return '' 183 | } catch (e) { 184 | return ''; 185 | } 186 | } 187 | 188 | const getFText = el => { 189 | if (!el) return ''; 190 | return (el.innerText || el.textContent || '').replace(/\n\n/g, '\n'); 191 | } 192 | 193 | const getEnteredText = el => { 194 | if (!el) return ''; 195 | return (el.innerText || el.textContent || '').replace(/\n\n/g, '\n').replace(/\n/g, '\n'); //• 196 | } 197 | 198 | const checkIfBetterTitle = (a, b, p) => { 199 | const txt1 = getText(a), txt2 = getText(b); 200 | if (txt1 && !txt2) return true; 201 | if (!txt1) return false; 202 | 203 | const des1 = checkIfDescendOf(a, p, Constant.title), des2 = checkIfDescendOf(b, p, Constant.title) 204 | if (des1 && !des2) return true; 205 | if (!des1 && des2) return false; 206 | 207 | const fontSize1 = parseFloat(window.getComputedStyle(a).fontSize) || 0, 208 | fontSize2 = parseFloat(window.getComputedStyle(b).fontSize) || 0; 209 | 210 | if (fontSize1 > fontSize2 * 1.2) return true; 211 | return false; 212 | } 213 | 214 | const getCurrencyNumber = (str) => { 215 | try { 216 | return parseFloat(str.replace(/[^0-9.]+/g,"")) || 0; 217 | } catch (ex) { 218 | return 0; 219 | } 220 | } 221 | 222 | const checkIfBetterPrice = (a, b, p) => { 223 | const txt1 = getText(a), txt2 = getText(b); 224 | const isPrice1 = checkIfPrice(txt1), isPrice2 = checkIfPrice(txt2); 225 | if (isPrice1 && !isPrice2) return true; 226 | if (!isPrice1) return false; 227 | 228 | const des1 = checkIfDescendOf(a, p, Constant.price), des2 = checkIfDescendOf(b, p, Constant.price) 229 | if (des1 && !des2) return true; 230 | if (!des1 && des2) return false; 231 | 232 | return false; 233 | } 234 | 235 | const checkIfBetterDescription = (a, b, p) => { 236 | const txt1 = getText(a), txt2 = getText(b); 237 | if (txt1 && !txt2) return true; 238 | if (!txt1) return false; 239 | 240 | // const des1 = checkIfDescendOf(a, p, Constant.description), des2 = checkIfDescendOf(b, p, Constant.description) 241 | // if (des1 && !des2) return true; 242 | // if (!des1 && des2) return false; 243 | 244 | if (txt1.length > txt2.length) return true; 245 | return false; 246 | } 247 | 248 | export const findHref = el => { 249 | var p = el; 250 | while(p && p.tagName.toLocaleLowerCase() !== 'html') { 251 | if ((p.tagName.toLocaleLowerCase() === 'a' || p.tagName.toLocaleLowerCase === 'button') && p.href) return p.href; 252 | p = p.parentNode; 253 | } 254 | return ''; 255 | } 256 | 257 | const getImgUrl = (el, mouse, excepts = []) => { 258 | if (!el) return ''; 259 | if (el.tagName.toLocaleLowerCase() === 'img') return el; 260 | const imgs = el.getElementsByTagName('img') 261 | if (!imgs.length) return ''; 262 | 263 | var ret = imgs[0]; 264 | for (let i = 1; i < imgs.length; i ++) { 265 | if (checkIfBetterImg(imgs[i], ret, mouse, excepts)) ret = imgs[i]; 266 | } 267 | return ret; 268 | } 269 | 270 | const getManualImgUrl = (el, mouse) => { 271 | while(el.tagName !== 'html') { 272 | const img = getImgUrl(el, mouse); 273 | if (img) return img; 274 | el = el.parentNode; 275 | } 276 | return null; 277 | } 278 | 279 | const getName = (el) => { 280 | const itms = el.getElementsByTagName("*"); 281 | var ret = itms[0]; 282 | for (let i = 1; i < itms.length; i ++) { 283 | if (checkIfBetterTitle(itms[i], ret, el)) ret = itms[i]; 284 | } 285 | return ret; 286 | } 287 | 288 | const checkIfPrice = (p) => { 289 | if (!p) return false; 290 | let d = p.replace(/ |\n|,/g, ''); 291 | d = d.replace('$', ''); 292 | if (!d) return false; 293 | for (let i = 0; i < d.length; i ++) if (d[i] !== '.' && !(d[i] >= '0' && d[i] <= '9')) return false; 294 | return true; 295 | } 296 | 297 | const getPrice = (el) => { 298 | const itms = el.getElementsByTagName("*"); 299 | var ret = itms[0]; 300 | for (let i = 1; i < itms.length; i ++) { 301 | if (checkIfBetterPrice(itms[i], ret, el)) ret = itms[i]; 302 | } 303 | return ret; 304 | } 305 | 306 | const getDescriptin = (el) => { 307 | const itms = el.getElementsByTagName("*"); 308 | var ret = itms[0]; 309 | for (let i = 1; i < itms.length; i ++) { 310 | if (checkIfBetterDescription(itms[i], ret, el)) ret = itms[i]; 311 | } 312 | return ret; 313 | } 314 | 315 | const getPhotos = (el, mouse, photo) => { 316 | const ret = [photo]; 317 | for (let i = 0; i < 4; i ++) { 318 | const img = getImgUrl(el, mouse, ret); 319 | if (ret.findIndex(itm => getSrcFromImgTag(itm).split('?')[0] === getSrcFromImgTag(img).split('?')[0]) > -1) break; 320 | ret.push(img); 321 | } 322 | ret.shift(); 323 | return ret; 324 | } 325 | 326 | const getUrl = (el) => { 327 | if (!el) return ''; 328 | return findHref(el); 329 | } 330 | 331 | const getSrcFromImgTag = (el) => { 332 | if (!el) return ''; 333 | return (el.currentSrc || el.src || '').split(' ')[0] 334 | } 335 | 336 | export const getProductInfo = (el, picker) => { 337 | const p = getProductRootElement(el); 338 | 339 | const e_img = getImgUrl(p, { x: picker.mouseX, y: picker.mouseY }); 340 | const e_name = getName(p); 341 | const e_price = getPrice(p); 342 | const e_description = getDescriptin(p); 343 | const e_photos = getPhotos(p, { x: picker.mouseX, y: picker.mouseY }, e_img); 344 | const name = getText(e_name); 345 | const img = getSrcFromImgTag(e_img); 346 | const url = getUrl(el); 347 | const price = getCurrencyNumber(getText(e_price)); 348 | const description = getEnteredText(e_description); 349 | const r_photos = {}; 350 | const photos = e_photos.map((p, idx) => { 351 | r_photos['photo' + idx] = p; 352 | return getSrcFromImgTag(p); 353 | }) 354 | return { 355 | name, 356 | img, 357 | color: img, 358 | url, 359 | description, 360 | price, 361 | photos, 362 | elements: { e_name, e_img, e_price, e_description, ...r_photos } 363 | } 364 | } 365 | 366 | export const getProductInfoIndividual = (el, picker, global) => { 367 | if (!global.productInfo) global.productInfo = {}; 368 | const productInfo = global.productInfo; 369 | if (!productInfo.elements) productInfo.elements = {}; 370 | if (!productInfo.photos) productInfo.photos = []; 371 | if (!productInfo.photos.length) { 372 | productInfo.photos.push(''); 373 | productInfo.elements.photo0 = null; 374 | } 375 | 376 | switch(global.selectMode) { 377 | case 'img': 378 | productInfo.elements.e_img = getManualImgUrl(el, { x: picker.mouseX, y: picker.mouseY }); 379 | productInfo.img = getSrcFromImgTag(productInfo.elements.e_img); 380 | break; 381 | case 'color': 382 | productInfo.elements.e_color = getManualImgUrl(el, { x: picker.mouseX, y: picker.mouseY }); 383 | productInfo.color = getSrcFromImgTag(productInfo.elements.e_color); 384 | break; 385 | case 'name': 386 | productInfo.elements.e_name = el; 387 | productInfo.name = getFText(el); 388 | break; 389 | case 'description': 390 | productInfo.elements.e_description = el; 391 | productInfo.description = getEnteredText(el); 392 | break; 393 | case 'price': 394 | productInfo.elements.e_price = el; 395 | productInfo.price = getCurrencyNumber(getFText(el)); 396 | break; 397 | case 'photos': 398 | const e_photo = getManualImgUrl(el, { x: picker.mouseX, y: picker.mouseY }); 399 | const photo = (e_photo.currentSrc || e_photo.src || '').split(' ')[0]; 400 | productInfo.elements['temp_photo'] = e_photo 401 | productInfo.temp_photo = photo; 402 | break; 403 | } 404 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@medv/finder@^1.1.0": 6 | version "1.1.3" 7 | resolved "https://registry.yarnpkg.com/@medv/finder/-/finder-1.1.3.tgz#01d90511b3cf1420276c5fa7a5cc4ac5fd7e1527" 8 | integrity sha512-hq6O7/4YpnBdfOzTFRF/0cNIg3IuJcF55LSOWq67lG/JQVtTwEu5ydv6L3fleoiwEOpQxF/zZq+NupVqgDVy6Q== 9 | dependencies: 10 | cssesc "^2.0.0" 11 | 12 | acorn@^5.2.1: 13 | version "5.7.4" 14 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" 15 | integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== 16 | 17 | asynckit@^0.4.0: 18 | version "0.4.0" 19 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 20 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 21 | 22 | axios@^1.6.0: 23 | version "1.6.0" 24 | resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" 25 | integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== 26 | dependencies: 27 | follow-redirects "^1.15.0" 28 | form-data "^4.0.0" 29 | proxy-from-env "^1.1.0" 30 | 31 | builtin-modules@^2.0.0: 32 | version "2.0.0" 33 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-2.0.0.tgz#60b7ef5ae6546bd7deefa74b08b62a43a232648e" 34 | integrity sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg== 35 | 36 | combined-stream@^1.0.8: 37 | version "1.0.8" 38 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 39 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 40 | dependencies: 41 | delayed-stream "~1.0.0" 42 | 43 | cssesc@^2.0.0: 44 | version "2.0.0" 45 | resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" 46 | integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== 47 | 48 | delayed-stream@~1.0.0: 49 | version "1.0.0" 50 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 51 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 52 | 53 | estree-walker@^0.5.0: 54 | version "0.5.2" 55 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" 56 | integrity sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig== 57 | 58 | estree-walker@^0.6.1: 59 | version "0.6.1" 60 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" 61 | integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== 62 | 63 | follow-redirects@^1.15.0: 64 | version "1.15.6" 65 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" 66 | integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== 67 | 68 | form-data@^4.0.0: 69 | version "4.0.0" 70 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" 71 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 72 | dependencies: 73 | asynckit "^0.4.0" 74 | combined-stream "^1.0.8" 75 | mime-types "^2.1.12" 76 | 77 | function-bind@^1.1.1: 78 | version "1.1.1" 79 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 80 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 81 | 82 | has@^1.0.3: 83 | version "1.0.3" 84 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 85 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 86 | dependencies: 87 | function-bind "^1.1.1" 88 | 89 | is-core-module@^2.9.0: 90 | version "2.11.0" 91 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" 92 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== 93 | dependencies: 94 | has "^1.0.3" 95 | 96 | is-module@^1.0.0: 97 | version "1.0.0" 98 | resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" 99 | integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== 100 | 101 | lodash@^4.17.21: 102 | version "4.17.21" 103 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 104 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 105 | 106 | magic-string@^0.22.4: 107 | version "0.22.5" 108 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" 109 | integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w== 110 | dependencies: 111 | vlq "^0.2.2" 112 | 113 | mime-db@1.52.0: 114 | version "1.52.0" 115 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 116 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 117 | 118 | mime-types@^2.1.12: 119 | version "2.1.35" 120 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 121 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 122 | dependencies: 123 | mime-db "1.52.0" 124 | 125 | path-parse@^1.0.7: 126 | version "1.0.7" 127 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 128 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 129 | 130 | proxy-from-env@^1.1.0: 131 | version "1.1.0" 132 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" 133 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 134 | 135 | resolve@^1.1.6, resolve@^1.4.0: 136 | version "1.22.1" 137 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 138 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 139 | dependencies: 140 | is-core-module "^2.9.0" 141 | path-parse "^1.0.7" 142 | supports-preserve-symlinks-flag "^1.0.0" 143 | 144 | rollup-plugin-commonjs@^8.3.0: 145 | version "8.4.1" 146 | resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.4.1.tgz#5c9cea2b2c3de322f5fbccd147e07ed5e502d7a0" 147 | integrity sha512-mg+WuD+jlwoo8bJtW3Mvx7Tz6TsIdMsdhuvCnDMoyjh0oxsVgsjB/N0X984RJCWwc5IIiqNVJhXeeITcc73++A== 148 | dependencies: 149 | acorn "^5.2.1" 150 | estree-walker "^0.5.0" 151 | magic-string "^0.22.4" 152 | resolve "^1.4.0" 153 | rollup-pluginutils "^2.0.1" 154 | 155 | rollup-plugin-node-resolve@^3.0.3: 156 | version "3.4.0" 157 | resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.4.0.tgz#908585eda12e393caac7498715a01e08606abc89" 158 | integrity sha512-PJcd85dxfSBWih84ozRtBkB731OjXk0KnzN0oGp7WOWcarAFkVa71cV5hTJg2qpVsV2U8EUwrzHP3tvy9vS3qg== 159 | dependencies: 160 | builtin-modules "^2.0.0" 161 | is-module "^1.0.0" 162 | resolve "^1.1.6" 163 | 164 | rollup-pluginutils@^2.0.1: 165 | version "2.8.2" 166 | resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" 167 | integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== 168 | dependencies: 169 | estree-walker "^0.6.1" 170 | 171 | rollup@^0.56.2: 172 | version "0.56.5" 173 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.56.5.tgz#40fe3cf0cd1659d469baad11f4d5b6336c14ce84" 174 | integrity sha512-IGPk5vdWrsc4vkiW9XMeXr5QMtxmvATTttTi59w2jBQWe9G/MMQtn8teIBAj+DdK51TrpVT6P0aQUaQUlUYCJA== 175 | 176 | supports-preserve-symlinks-flag@^1.0.0: 177 | version "1.0.0" 178 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 179 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 180 | 181 | vlq@^0.2.2: 182 | version "0.2.3" 183 | resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" 184 | integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== 185 | --------------------------------------------------------------------------------