From 1dd48de3edbc8f546a78d0425ba3c7e88cdef485 Mon Sep 17 00:00:00 2001 From: YNWA Fawzy <38886749+Cybrarist@users.noreply.github.com> Date: Fri, 23 Aug 2024 10:28:03 +0400 Subject: [PATCH 1/4] V2.0 --- Product.js | 170 +++++++++++ Stores/Amazon.js | 69 +++++ Stores/Argos.js | 88 ++++++ Stores/Ebay.js | 71 +++++ Stores/Noon.js | 72 +++++ functions.js | 148 +++++++++ manifest.json | 31 +- options.html | 16 + resources/css/style.css | 194 ++++++++++-- resources/images/bandit_square.png | Bin 0 -> 30186 bytes resources/images/stores/amazon.png | Bin 0 -> 2170 bytes resources/images/stores/amazon_ae.png | Bin 0 -> 2492 bytes resources/images/stores/amazon_ca.png | Bin 0 -> 2451 bytes resources/images/stores/amazon_co_jp.png | Bin 0 -> 2717 bytes resources/images/stores/amazon_co_uk.png | Bin 0 -> 2706 bytes resources/images/stores/amazon_com_au.png | Bin 0 -> 2874 bytes resources/images/stores/amazon_com_br.png | Bin 0 -> 2839 bytes resources/images/stores/amazon_com_mx.png | Bin 0 -> 2885 bytes resources/images/stores/amazon_com_tr.png | Bin 0 -> 2787 bytes resources/images/stores/amazon_de.png | Bin 0 -> 18374 bytes resources/images/stores/amazon_eg.png | Bin 0 -> 2510 bytes resources/images/stores/amazon_es.png | Bin 0 -> 2471 bytes resources/images/stores/amazon_fr.png | Bin 0 -> 2378 bytes resources/images/stores/amazon_in.png | Bin 0 -> 2325 bytes resources/images/stores/amazon_it.png | Bin 0 -> 2335 bytes resources/images/stores/amazon_nl.png | Bin 0 -> 2336 bytes resources/images/stores/amazon_pl.png | Bin 0 -> 12462 bytes resources/images/stores/amazon_sa.png | Bin 0 -> 2460 bytes resources/images/stores/amazon_se.png | Bin 0 -> 2483 bytes resources/images/stores/amazon_sg.png | Bin 0 -> 2473 bytes resources/images/stores/argos.png | Bin 0 -> 13668 bytes resources/images/stores/costco.png | Bin 0 -> 36274 bytes resources/images/stores/currys.png | Bin 0 -> 2937 bytes resources/images/stores/diy_com.svg | 1 + resources/images/stores/ebay.png | Bin 0 -> 6903 bytes resources/images/stores/noon.svg | 20 ++ resources/images/stores/walmart.png | Bin 0 -> 3908 bytes resources/js/options.js | 2 + stores.js | 350 ++++++++++++++++++++++ 39 files changed, 1197 insertions(+), 35 deletions(-) create mode 100644 Product.js create mode 100644 Stores/Amazon.js create mode 100644 Stores/Argos.js create mode 100644 Stores/Ebay.js create mode 100644 Stores/Noon.js create mode 100644 resources/images/bandit_square.png create mode 100644 resources/images/stores/amazon.png create mode 100644 resources/images/stores/amazon_ae.png create mode 100644 resources/images/stores/amazon_ca.png create mode 100644 resources/images/stores/amazon_co_jp.png create mode 100644 resources/images/stores/amazon_co_uk.png create mode 100644 resources/images/stores/amazon_com_au.png create mode 100644 resources/images/stores/amazon_com_br.png create mode 100644 resources/images/stores/amazon_com_mx.png create mode 100644 resources/images/stores/amazon_com_tr.png create mode 100644 resources/images/stores/amazon_de.png create mode 100644 resources/images/stores/amazon_eg.png create mode 100644 resources/images/stores/amazon_es.png create mode 100644 resources/images/stores/amazon_fr.png create mode 100644 resources/images/stores/amazon_in.png create mode 100644 resources/images/stores/amazon_it.png create mode 100644 resources/images/stores/amazon_nl.png create mode 100644 resources/images/stores/amazon_pl.png create mode 100644 resources/images/stores/amazon_sa.png create mode 100644 resources/images/stores/amazon_se.png create mode 100644 resources/images/stores/amazon_sg.png create mode 100644 resources/images/stores/argos.png create mode 100644 resources/images/stores/costco.png create mode 100644 resources/images/stores/currys.png create mode 100644 resources/images/stores/diy_com.svg create mode 100644 resources/images/stores/ebay.png create mode 100644 resources/images/stores/noon.svg create mode 100644 resources/images/stores/walmart.png create mode 100644 stores.js diff --git a/Product.js b/Product.js new file mode 100644 index 0000000..196b165 --- /dev/null +++ b/Product.js @@ -0,0 +1,170 @@ +class Product { + name; + image; + price; + rate; + number_of_rates; + url; + update_product; + #token; + + storage_promise; + + constructor() { + this.storage_promise= this.get_storage_data() + } + + update_url () { + return `${this.url}/api/products/update` + }; + get_product () { + return `${this.url}/api/products/get` + }; + create_product () { + return `${this.url}/api/products/create` + }; + + async get_storage_data(){ + var result= await browser.storage.sync.get() + this.#token= result.token; + this.url=result.url; + this.update_product=result.update_product; + + console.log(this.#token) + } + + async update_server_product(){ + await this.storage_promise + if (!self.update_product) + return + + fetch(this.update_url() ,{ + method: 'POST', + headers:{ + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': `Bearer ${this.#token}`, + }, + body:JSON.stringify({ + url : window.location.href, + current_price: self.price + }) + }) + .catch(error => { + // Handle errors + console.log("Error:", error); + }); + } + + async get_product_data(){ + await this.storage_promise + + return fetch(this.get_product() ,{ + method: 'POST', + headers:{ + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': `Bearer ${this.#token}`, + }, + body:JSON.stringify({ + url : window.location.href + }) + + }) + .then(response => { + return response.json(); + }) + .catch(error => { + // Handle errors + console.log("Error:", error); + }); + } + + submit_form(){ + document.body.querySelector("#gray_layout").classList.add("show_flex") + + fetch(this.create_product(), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': `Bearer ${this.#token}`, + }, + body: JSON.stringify({ + url: window.location.href , + name: this.name, + image: this.image, + notify_price: document.getElementById("notify_price").value, + official_seller: document.getElementById("official_seller").checked, + favourite: document.getElementById("favourite").checked, + stock_available: document.getElementById("stock_available").checked, + lowest_within: document.getElementById("lowest_within").value, + number_of_rates:this.number_of_rates, + price:this?.price ?? 0, + }) + }) + .then(response => { + return response.json(); + }) + .then(data => { + console.log(data); + if (data.errors) + add_notification_to_page('danger' , 'Something wrong Happened') + else + add_notification_to_page('success' , + `

${ data.message}

+

You can check it from the following link

+

+ ${data.link} +

` + ) + + }) + .catch(error => { + add_notification_to_page('danger' , 'Something wrong Happened') + }); + } + + get_dom_product_details(){} + + populate_dom_with_charts(){} + + + refresh_dom_all(){ + product.get_dom_product_details() + product.update_server_product().then(response => { + console.log("updated server") + }) + document + .querySelectorAll("#gray_layout , #discount_bandit_show, #chart , #all_stores_cards") + ?.forEach((elem)=>{ + elem.remove() + }) + + product.populate_dom_with_charts() + + document.getElementById("submit_discount_form") + .addEventListener("click" , function (){ + product.submit_form() + }) + + product.get_product_data().then(response => { + + }) + } + +} + + +let previousUrl = ''; +const observer = new MutationObserver(function(mutations) { + if (location.href !== previousUrl) { + previousUrl = location.href; + product.refresh_dom_all() + } +}); +const config = {subtree: true, childList: true}; +observer.observe(document, config); + + + diff --git a/Stores/Amazon.js b/Stores/Amazon.js new file mode 100644 index 0000000..a0d28cf --- /dev/null +++ b/Stores/Amazon.js @@ -0,0 +1,69 @@ +if (window.location.href.includes('amazon.') ){ + + class Amazon extends Product { + constructor() { + super(); + } + + get_dom_product_details(){ + this.name= document.getElementById("productTitle").textContent.trim() + this.price= document.getElementById("twister-plus-price-data-price").value + this.image= document.querySelector(".imgTagWrapper img").src + this.number_of_rates=document.getElementById("acrCustomerReviewText") + .textContent.split(" ")[0] + .replaceAll(",","") + .replaceAll("." , "") + } + + populate_dom_with_charts() { + + document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) + + document.body.querySelector("#title_feature_div") + .insertAdjacentHTML('afterend', ``) + + //add the chart and stores + var main_body=document.body.querySelector("#ppd") + main_body.insertAdjacentHTML("afterend" , "
") + main_body.insertAdjacentHTML("afterend" , "
") + } + + async get_product_data() { + + super.get_product_data().then(data => { + + + insert_chart_into_dom(data.series) + //add highest and lowest prices + document.getElementById("buybox").insertAdjacentHTML("afterbegin" , + `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
+
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` + ) + + //add open in Discount Bandit + document.getElementById("submit.buy-now").insertAdjacentHTML("afterend" , + ` + + Discount Bandit` + ) + //add other stores available. + var stores_elements=document.getElementById("all_stores_cards"); + for (const [store, value] of Object.entries(data.prices)) + if (store != data.current_store_id) + stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) + }) + } + + } + + var product= new Amazon() + + product.get_storage_data().then(r => {}) +} diff --git a/Stores/Argos.js b/Stores/Argos.js new file mode 100644 index 0000000..b6c8ad0 --- /dev/null +++ b/Stores/Argos.js @@ -0,0 +1,88 @@ +if (window.location.href.includes('argos.co.uk') ){ + + class Argos extends Product { + constructor() { + super(); + } + + get_dom_product_details(){ + this.name= document.querySelectorAll('[data-test="product-title"]')[0].textContent.trim() + this.price= document.querySelectorAll('[data-test="product-price-primary"]')[0].getAttribute('content') + this.image= document.querySelectorAll('[data-test="component-media-gallery-thumbnails_thumbnail-0"] img')[0].src + this.number_of_rates= document.querySelectorAll('[itemProp="ratingCount"]')[0].textContent + } + + populate_dom_with_charts() { + + document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) + + + + //add the chart and stores + setTimeout(() => { + document.querySelectorAll('[data-test="product-name"]')[0] + .insertAdjacentHTML('afterend', ``) + + + var main_body=document.getElementById("pdp-description") + main_body.insertAdjacentHTML("beforebegin" , "
") + main_body.insertAdjacentHTML("beforebegin" , "
") + }, 2000); + + + } + + async get_product_data() { + + super.get_product_data().then(data => { + + if (data){ + //add highest and lowest prices + document.querySelectorAll("[data-test='product-price-primary']")[0] + .insertAdjacentHTML("afterbegin" , + `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
+
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` + ) + + + //add open in Discount Bandit + document.querySelectorAll("[data-test='add-trolley-button-wrapper']")[0] + .insertAdjacentHTML("afterend" , + ` + + Discount Bandit` + ) + setTimeout(() => { + + + insert_chart_into_dom(data.series) + + var stores_elements=document.getElementById("all_stores_cards"); + for (const [store, value] of Object.entries(data.prices)) + if (store != data.current_store_id) + stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) + + }, 3000); + + } + + + + + //add other stores available. + }) + } + + } + + var product= new Argos() + + product.get_storage_data().then(r => {}) +} diff --git a/Stores/Ebay.js b/Stores/Ebay.js new file mode 100644 index 0000000..01d70df --- /dev/null +++ b/Stores/Ebay.js @@ -0,0 +1,71 @@ +if (window.location.href.includes('ebay.') ){ + + class Ebay extends Product { + constructor() { + super(); + } + + get_dom_product_details(){ + this.name = document.querySelector('[data-testid="x-item-title"]').textContent.trim() + this.price= document.querySelector(".x-price-primary span").textContent.trim().replace(/US|\$/g,"").trim() + this.image= document.querySelector(".ux-image-carousel-item.image-treatment.active.image img").src + this.number_of_rates=0 + } + + populate_dom_with_charts() { + + document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) + + document.querySelector('[data-testid="x-item-title"]') + .insertAdjacentHTML('afterend', ``) + + //add the chart and stores + var main_body=document.body.querySelector("[data-testid='x-evo-atf-left-river']") + main_body.insertAdjacentHTML("afterend" , "
") + main_body.insertAdjacentHTML("afterend" , "
") + } + + async get_product_data() { + + super.get_product_data().then(data => { + + + console.log(data) + + insert_chart_into_dom(data.series) + //add highest and lowest prices + document.querySelector('[data-testid="x-price-primary"]') + .insertAdjacentHTML("afterbegin" , + `
Lowest Price ${data.prices[data.current_store_id]?.lowest_price.toLocaleString()}
+
Highest Price ${data.prices[data.current_store_id]?.highest_price.toLocaleString()}
` + ) + + //add open in Discount Bandit + document.querySelector("[data-testid='x-buybox-cta']") + .insertAdjacentHTML("afterend" , + ` + + Discount Bandit` + ) + //add other stores available. + var stores_elements=document.getElementById("all_stores_cards"); + for (const [store, value] of Object.entries(data.prices)) + if (store != data.current_store_id) + stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) + }) + } + + } + + var product= new Ebay() + + product.get_storage_data().then(r => {}) +} diff --git a/Stores/Noon.js b/Stores/Noon.js new file mode 100644 index 0000000..056e8fd --- /dev/null +++ b/Stores/Noon.js @@ -0,0 +1,72 @@ +if (window.location.href.includes('noon.') ){ + class Noon extends Product { + + constructor() { + super(); + } + + get_dom_product_details(){ + + document.querySelectorAll("[type='application/ld+json']").forEach((elem)=>{ + if(elem.textContent.trim().includes('"@type":"Product"')){ + var json_string= JSON.parse(elem.textContent); + this.price=json_string.offers[0].price; + this.image= json_string.image[0]; + this.number_of_rates=json_string.aggregateRating.reviewCount; + } + + }) + this.name= document.querySelector("title").textContent.trim().split('|' )[0] + } + + populate_dom_with_charts() { + + document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) + + document.body.querySelector("h1") + .insertAdjacentHTML('afterend', ``) + + //add the chart and stores + var main_body=document.body.querySelector(".noGap") + main_body.insertAdjacentHTML("afterend" , "
") + main_body.insertAdjacentHTML("afterend" , "
") + } + + async get_product_data() { + + super.get_product_data().then(data => { + // + insert_chart_into_dom(data.series) + //add highest and lowest prices + document.querySelector(".priceNow[data-qa='div-price-now']").insertAdjacentHTML("beforebegin" , + `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
+
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` + ) + + //add open in Discount Bandit + document.querySelector("[data-qa^='pdp-quantity-']").insertAdjacentHTML("afterend" , + ` + + Discount Bandit` + ) + //add other stores available. + var stores_elements=document.getElementById("all_stores_cards"); + for (const [store, value] of Object.entries(data.prices)) + if (store != data.current_store_id) + stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) + }) + } + + } + + var product= new Noon() + + product.get_storage_data().then(r => {}) +} diff --git a/functions.js b/functions.js index e69de29..340561a 100644 --- a/functions.js +++ b/functions.js @@ -0,0 +1,148 @@ +function product_per_store_price_template(store_id, data){ + + var current_store=stores.data[store_id-1]; + + return ` + +
+
+
+
+
+ +
+
${current_store.name}
+
+ +

${current_store.currency} ${data.current_price.toLocaleString() }

+ +
+

+ + ${current_store.currency} ${data.highest_price.toLocaleString()} +

+

+ + ${current_store.currency} ${data.lowest_price.toLocaleString()} +

+
+
Seller${data.seller} +
+
+
+
+
+` +} + +function add_notification_to_page(type , message){ + document.body.insertAdjacentHTML("beforebegin" , ` +
+ ${message} +
+ `) +} + + +function insert_chart_into_dom(series){ + //Generate the apex chart + var options = { + chart: { + type:'area', + height:300 + }, + theme:{ + palette: "pallet1" + }, + series: series, + xaxis: { + type:'datetime', + categories:[ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec' + ], + labels:{ + style:{ + fontFamily:'inherit' + } + } + }, + yaxis: { + labels: { + formatter: function (val, index) { + return val.toLocaleString('en-US'); + } + } + }, + stroke : { + curve:'smooth' + }, + + dataLabels: { + enabled: false, + }, + legend: { + position: 'top' + } + } + + var chart = new ApexCharts(document.querySelector("#chart"), options); + chart.render(); + +} + +function get_global_form(){ + + return`
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+` + + + +} + + + +function get_numbers_with_dots(string) { + return string.replace(/[^0-9.]/g, ''); +} \ No newline at end of file diff --git a/manifest.json b/manifest.json index 9d58aa1..d216dac 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "manifest_version": 2, - "version": "1.0", + "version": "2.0", "name": "Discount Bandit", @@ -47,20 +47,35 @@ "https://*.amazon.nl/*" , "https://*.amazon.sg/*" , "https://*.amazon.se/*" , - "https://*.amazon.com.be/*" + "https://*.amazon.com.be/*", + "https://*.argos.co.uk/product/*", + "https://*.ebay.com/itm/*", + "https://*.noon.com/*/p/*" ], - "js" : [ "resources/apexcharts.js" , "amazon.js"], + "js" : [ "resources/apexcharts.js" ,"Product.js", + + "Stores/Amazon.js", + "Stores/Argos.js", + "Stores/Ebay.js", + "Stores/Noon.js", + + + "functions.js" , "stores.js"], "css": ["resources/css/style.css"] } ], + "web_accessible_resources": [ + "resources/images/stores/*", + "resources/images/bandit.png" + ], "icons": { - "16": "resources/images/bandit.png", - "32": "resources/images/bandit.png", - "48": "resources/images/bandit.png", - "64": "resources/images/bandit.png", - "96": "resources/images/bandit.png" + "16": "resources/images/bandit_square.png", + "32": "resources/images/bandit_square.png", + "48": "resources/images/bandit_square.png", + "64": "resources/images/bandit_square.png", + "96": "resources/images/bandit_square.png" } } \ No newline at end of file diff --git a/options.html b/options.html index cc9b36f..472bfc9 100644 --- a/options.html +++ b/options.html @@ -59,6 +59,22 @@ + + +
+
+ + +
+
+ + diff --git a/resources/css/style.css b/resources/css/style.css index 32ca27d..ac36f2a 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -1,4 +1,4 @@ -.gray_layout{ +#gray_layout{ position: fixed; background: #99999969; z-index: 200; @@ -9,6 +9,10 @@ align-items: center; } +.show_flex{ + display: flex !important; +} + .form_background{ background: white; z-index: 300; @@ -69,11 +73,12 @@ background: #00ed64; } -#discount_bandit_show:hover { +#discount_bandit_show { cursor: pointer !important; + max-width: 50px; } -.submit_amazon{ +#submit_discount_form{ width: 50%; height: 40px; background-color: rgb(239 68 68 / 1); @@ -96,7 +101,7 @@ -webkit-appearance: button; } -.submit_amazon:hover{ +#submit_discount_form:hover{ background-color: rgb(248 113 113 / 1 ); } @@ -107,35 +112,32 @@ } .success-message{ - position: sticky; - display: none; - background: forestgreen; - color: white; - z-index: 1000; - text-align: center; - min-height: 20px; - padding: 10px; - width: 100%; - flex-direction: column; - -} -.success-message a{ - color: white; + color: #270; + background-color: #DFF2BF; } + + .danger-message{ - width: 100%; - display: none; - text-align: center; - background: #dc3545; - color: white; - z-index: 1000; - text-align: center; - min-height: 20px; - padding: 10px; + color: #D8000C; + background-color: #FFBABA; +} + + +.notification_message{ + position: absolute; + right: 0; + top: 100px; + border-radius: 8px; + padding: 20px 30px ; + text-align: left; + flex-direction: column; + z-index:1000; } + #chart { display: flex; + width: 100%; } .lowest_price{ @@ -148,4 +150,142 @@ .lowest_price , .max_price{ width: 100%; display: flex; +} + + +.stores{ + display: flex; + justify-content: start; + align-items: center; + flex-direction: row; + flex-wrap: wrap; + gap: 10px; + padding: 20px; + + +} +#all_stores_cards{ + width: 100%; + display: flex; + flex-direction: row; + gap:10px; + margin: 20px; + flex-wrap: wrap; + a:hover, a:visited , a:focus , a:active, a { + text-decoration: none !important; + } + +} +.single_store{ + border-radius: 20px; + width:23%; + min-width: 300px; + + &:hover{ + opacity: 0.8; + } + + .card-block{ + display: flex; + flex-direction: column; + + div{ + display: flex; + justify-content: space-between; + flex-direction: row; + gap: 10px; + } + } + + .prices, .current{ + width: 50%; + height: 30px; + float: left; + margin:0; + } + + .name{ + font-weight: bold; + } + + .highest{ + color: #dc3545; + } + .lowest{ + color: #60a220; + } + .seller{ + padding-top: 10px; + } +} + +.card { + width: 100%; + border-radius: 5px; + -webkit-box-shadow: 0 1px 2.94px 0.06px rgba(4,26,55,0.16); + box-shadow: 0 1px 2.94px 0.06px rgba(4,26,55,0.16); + border: none; + -webkit-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; +} + +.card .card-block { + padding: 25px; +} + +.order-card i { + font-size: 26px; +} + +.f-left { + float: left; +} + +.f-right { + float: right; +} +.order-card { + color: #fff; +} + +.bg-c-blue { + background: linear-gradient(45deg,#4099ff,#73b4ff); +} + +.bg-c-green { + background: linear-gradient(45deg,#2ed8b6,#59e0c5); +} + +.bg-c-yellow { + background: linear-gradient(45deg,#FFB64D,#ffcb80); +} +.bg-c-orange { + background: linear-gradient(45deg, rgba(255,143,22,1) 0%, rgba(255,209,0,1) 100%); +} + +.bg-c-pink { + background: linear-gradient(45deg,#FF5370,#ff869a); +} + +.bg-c-red { + background: linear-gradient(45deg,#FF5370,#ff869a); +} + +a.discount_bandit_button{ + display: flex; + justify-content: space-evenly; + align-items: center; + width: 100%; + background: #dc3545; + text-align: center; + line-height: 28px; + margin: auto; + border-radius: 20px; + color: #fff; + padding:5px 0; + + &:hover{ + text-decoration: none; + opacity: 0.9; + } } \ No newline at end of file diff --git a/resources/images/bandit_square.png b/resources/images/bandit_square.png new file mode 100644 index 0000000000000000000000000000000000000000..81b41c4fd474d183bd8b0814a5de09f22e7b190f GIT binary patch literal 30186 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 literal 0 HcmV?d00001 diff --git a/resources/images/stores/amazon_ae.png b/resources/images/stores/amazon_ae.png new file mode 100644 index 0000000000000000000000000000000000000000..fcf15815ee43abe315e3f8a7557b01c37eb845a0 GIT binary patch literal 2492 zcmV;t2}AaYP)et-NL-1<--OiD1VBC2U8x-!9oW&~zlXi|W>>kVfWNCXbG+PX)w;X8Yx@)M`Zc_Z zbP9Gl-Y4;VeU^TZ%>d!#1jx2HKWE&K!0-ttw%zlTL+QqBhvQv}*G9$0z~R_?tHze5 z93&l5GY5X3)l!7vO_w!o1Y6vf2QmP>E!9!TgKg25xkkCC8eIcBVq%7oxoqty`{PFn ze> z@iEGQ$zAIGCY)d8u$@r{G3{C@_6?SJoCA^f-KOP_#k>DydaWle5pk{sY~Dwrw!!@` znclZ%>|~NHaL&&okAFxTJuUAWEdjZsj&mK*VK)D*v7V2_#0t{RHIp?$SP3|8wde3CvJrH{i zqR7_$9Ghbs9Q%MYkP!i{V0IZuv)k^uVB9Wy+D8F?4GL_E??Uga%$;2JW-wzckB3At zpFQtobKKIDgTkPl6I_cs3FOgCmhQOU((JO{7|{bk)I^_JY<=YZ1dtW~m{gF}vqm(r z#ba4%s#T%87L*B+`M!}(PO?GfB>@?o<^1o&*A+iwb1c>AM@}d6p)Gy~9bd`FZ>bGFMl?<8Ll7pH+tJb*d( z?ZW|?h#>O0M|`JH?KxaqYElHU{OMGokbc2Hs##e$m4pPli@dqhc8|jShE{?cG)>Y$ z#{Ik`3#JOdn?oD|y8fyekg?8Bnyt&h15Qae&qUwk{e^^Hj<_TZWQs()0ckj|i~AvaJngBf|_+Xb>>!P{8w`d&`Be^j4}#11ykbH=;7s{X;S=^a#QggY8dpnf8_rsMk&PO(l_D)`6K zte#t>X?E;j3V(ICfsFjkn;oT6X&=^+TFi{~SS-vTT0w{B`8;6pM;=;M~TDbc?2ZN0$UBTraT(pULJUC z?45PdHY1f@IXQd{^^j$25%WdHtzx_Mr&&D)vZZBxYQI*1O!Lwi0ofjUoJ$R#(5W!4 zqV#(}brVCOGl)&ePz+)c8Yln|2MozlVnw){S|6^H%@k_Tmlg#_5@hOS0NfX7eHFES3wb64nIe)}|BmFL3o03EOlbSd#CR?7 zY&ydZLPN`UGEQ$a>#w860@c$QEz-5*{S=Oe0dSz7%5(BSAz7-eR-LYY@?9Kd-y;b! zF?Jc69p7o#%yCZ$K@QiVQ!jmNN1kco!3_SL)ytPRs zga+ib)a?p--q;m*kDr)D%M+EZw&$$BgW8pOIei!)kD@3N+DSR|(3t_HDAg7XIe==c ztmS21c`e!=9QscOVy;>7rX(5x0Yg4RF~$WHnxeTmGS7*v zXc)X5z?C$Wpb?4mFuxLDoL~ScOdn#In2SuvRp9ejrj-4{) zy<`tMz!_>PO&ufchm$l#yuNErm{v62DOY{5v0uI4I2p32-e)X>tc3gn_Yc>5jhpHV zjhfNEn9=%-dm-o6ml*r(@w)aJXG7OlAoUDc)<%^Z?mEMTVb(*Q$8I2IGMLvxszy(tkl4 z(d=BeC=&KI;Qr@ufa)Hl=P!)?;e=m=ZudaPHORqZ6v$6Xz59(b+GytIy<=glwJgs) z4Y`oQP*z9V&b0^-N zFT*HG7=-vvP)T{gQcQ)i?`x^ zUz7)0UKA?c7jh(^BI1dP7biiiO~Tf*=MB>-)VuVWy{NW&@ju{ZS8s)%Q*|rrb4D?O#9xH<@9DTS9i

sRtd4^+;FJiy%;k#2H zSEQ@?4!ZH%)z)rIYxU`6U%Fpj-LG5P;x}W>ujBqZ7&9hQ-H>MOcy$sVG*ndAFPgO{gj_uO0>kotTjKsrA{xdWh(pA**iOWJVv2^pW^ z+!XhoGRI4y_f7jsJX;a*&N4D?^_`62?xu{JMurffmnBJtz1mmWbcPkWdGg}GCi?W* zOc}ErAUqfOzIf)0kNL*mH+EO03z6!fkFa3<^Y4S{R$r=q{> zd{lH`cdE6w#pHh~ADfaf?8C6&QxQ7ms0%@O>>dN|N-1&5h<%Q4UK&gnK`$3;f--Uc zaIL~D=Mu#@2I2nK8d;_R_l8y8XZIpwQ=UIA)5#>&gUhoGJ4J$*s>E&jM>$R4B zg7T5KaJzG>=7I>XAd^(GW}N+{$R_E=wdD^uQG z(W9n56-X&2pB)6(ekf_*HVq+;D7VNM{jh1Ipur62QICdtTPJ??#k|<3c;; zdp`Kw8Sg!-s;XKaNN+(m62}T!S|Rb9&IT z2&e>bCKR$jB4*{)wh=ZCKDEsp!?Lea#_SRDJcZS!47;B?rlJ?$ITBozOPG8?V19$2 z+-_*nf=KZcn&Suzc~Ts>sTy7r1do5HSm>EbDK$>n_; zK1B#c@u!Et#a;uYS&iFMqnkq_!*Zto2Ff=+3BGHeQFKs6XwKK01u@DbaH(DnhA-!% zIA)3YMkatudG#6%@oN*fhOyTsc=kn@-MiJ{P|t^ih-7znL)F+qdk2Sz0QawuGbwsW z0++r|B##V~_To>SNGZEew5h1!ocaN)a*M@3Ru-}d3LZhpU)F0&+7nzNB5~jngVhkS zC*i%v6Gy-P5w$QmUr=^2%IexszSg-B_> z(G;wkp`U|w7W%8gMp-!Lb{x2pasRD+<8%dx`;x$==0=FXpxZ=IL7SqG|H57wte@rD z3JuGv4X$Y506#HpZ8RBEREm4WT-N7}T ztJHeYU#dytdnT?CL2XHuyg+`VP^N}j@2W8B`>6X;D+#dWy~B){R}>puYE|O-;?{7R z#GNdAG+`F)?Ylp=@YJfg9>(S@u_I9#Yc=|bMoM9s3$i@y+H$e5Dw?L`5w>1j+f@;N% z@mzklN=*DE33aoyq%Z64>8n>;3MPv_C5Bl%pY0JDafo5?ZbkP!`#BjX_|wz)_k0iBX_`9qb?V1r!kLl1Q0cwx@45Cln(xn}gAUC5Yz$Cy$pE#a zCqcYWO@|!e1`JN4D949@}`m@O!epDef z%LNT^Y zs3wM23l>}op)DBEv{;FaJRmoeC6eUzty^;+86T{k_DKEYsJvBSxmModomuL+HJwo_ zWk?`G_}i8-gseFUT(1g{#nI2%$fOGP|V>*?q`3{+Vu6Gsp@!SbzAEWlDAHp*DmHbzQ4b%wh67 zg;@5*su2-D-l2H0FIqDxUvhxSLQjT$Bvli-@zn96I(*f>o&CcdK^U!kPA72m9~`{i zH|@{g#uX{Xat6|WfS~_SK?gowgpsAvE!4XvK0+A5aE@dDmn%1#ZIK(xPRcd0iIDdp z-#}(T-o*V0xiKuuN|#jDSoRteb2enXwR)XHS!b;EGOY7=NG?(u*|Q~7J*71=4yF7E z`5MYQCO3(#vsSM2ezpnLcrn&^0)F?yZ*NEy*1s!$PsP2Z0^G6eu@b6&#f@WSGQ^Sf z&w;{*04wnssZg17I`F5jQT)|Xu1YRDTtKs}iSR$SLQzKLBR@842Fukf;? z5(;}VmYwFq;S|V?kU@}oNE)(n?A&1Ixshxuc%6a22SP5F<;`@+vWRajfSg0%D|5|& z{DQfvFdJ6 zi RLPh`p002ovPDHLkV1o1hry&3U literal 0 HcmV?d00001 diff --git a/resources/images/stores/amazon_co_jp.png b/resources/images/stores/amazon_co_jp.png new file mode 100644 index 0000000000000000000000000000000000000000..3f1b43b55b654fbd5fc9c2998990b6343d004eef GIT binary patch literal 2717 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 Y0a!y`emKLfK`|OY69lomq)bR67y@^4$>ox; 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 literal 0 HcmV?d00001 diff --git a/resources/images/stores/amazon_com_tr.png b/resources/images/stores/amazon_com_tr.png new file mode 100644 index 0000000000000000000000000000000000000000..655b1dc69a6b40b759908aa9568d862d84a8151e GIT binary patch literal 2787 zcmV<93LN!`P)ZY6_ zZ}z?ayYJ1cE-<^yobx|3^X`53>;Jp|kKesQ_&yC|Oe2{wXC<@tGs%p#9?}EZlr+pI zl3DZ8d_I4O(0ovTS|VfJNJ91GGvm|~QW@(myuTQ-l-f|MpL?w zKtH$BT42n}X$;h7gVEZot6wY@4^C#SFM91Jvet$4d_>Fm<1qF+@c%b%J7#BNwwM(| z^i!Y6*!N(udLd4X{JA;TI^6%_i1FhOXLWj%64q+o-;l@@no{G&9gW|=)Aj*n=mwa| zm>)#>#xmpUP8sI;A>e1sTgtrGCx-P$P2bm{Y)8a9yL4ktjxDBw_3YJ6`QqE?Thqo) z0PV~nhbk=QS1Vq4>{S1|!}IyKa$sR6E{ajyuVJC)#`OVkj15?-T=Bhl{(6Pudksoz z94KHV4C`~1y=&f54p>;1c`=p~&S%1Iy}`)-7h<4TfzF9czV{h)r+_xyC*>VCDD%kfO=}YZJLH#={QhQmP3&ny23RBW<~v{!u1;j_uSEpVb>vZqGI!%Vi*&T;NGXXx%H@0XpuADlQfDS^2d`6V@^8}M&F zPD&S!_1dI$ppeH4eI%bqg+ws2MRCa(*>=@d zom$^ZWG0@qZ@~9|0(#C^A7}Aqua=i{y~=AmE>$t~3O7EtrmH%=LjCbq-b~pXYqWcR z0ZV0XyI2f&U7Rb`0gK`rb&?RwfU$$4Vu_F|LrfL062zj1E#{R24J@^}dx&GB_*J^| zw0(wj#W*QYbMBNeu|MO?+E)avuh~g+Aj?gOh~n*Dq|BVw*gfD|=X-EA|zzc%V((6DJa}aNlKPSjvxr zy0js395U^bCj$)|(0E@UpK7g%k0hW16QPygSMj30o9kSOg79EOn*4`+N@s(c_ zLBO8lO(D|}vA=z7-nQnKV^B=!riRJKuWu@|y9YX|W>`vO=h zYu!+0d@FfoOIf2#t;4`thyH#}u8)FQ`pq(>60lUv@i9%%&!vaos(6#YswWbet`)hP z-@Mj41`7iSyd#TyvHla)hPyW^#;Oolb(E14cj#U}nlRE!0?Go*ce_Ei^SkMEp9Rqy z{l`9tC$4k=F9Y_ClzkCE)c;;}FOAlE zH}Q2|0RBdSr7kGQM5h#1xOep&l?-)_9(~&Jk-+j8Y2sigPLOT}JPy6Yty{BUHE7K7 zxwKJTuza-_L@=aN#ri-IJLMqylt#D(Qj3q*(7_YqCP(|<|)d9l?%A7#Mu z$#k1;H5k@U)c3_4gnm+&F8B4Vi#1tGBdBr`KQm98k7c2_GgtU9SuNrig4M=UIy*n02Ut?{^?BvB>L}@)h?cdTt|X>Tw9tV1+Gfse#yVdRe5#H0AW8*&MP=~h^cgmaHLJhpAJG_Wps zXEfv-8P~Oo`H-cM?ufqrCj)raHpcllp1%q(Hq^8>xB!+_ywQ#_Amg$XvL5mrR7k~b`7|9a>7(l0;D%KdxJX0TuJ%0Mn@iJx_B*t>4}(|8DF3|`6E?48-W=buK^7Y4gbF; z=zrJ%!2dh>|1ZJ8EaGZ(2?>MldY_PIE7iW*{>#CE?k@Rvel(FVDGS&g4^bVAc*K|( z9taxXfE%Bqn$S*v^Hx@#p>e_l02aSFI$oO*5)uwxF0X0W{PU=b;!;Uj#QEfTJcCB6 zRdoIJ9BHcK@cGzI;F^Dly`b3dZg|2F3$Z(W?U2Jp=dw3|zQY&RJ0jKGj!(kcC9Hc< z_H<61xv3Ka`arGiB1U{-r7b&qEBw&VagzMjot1NNdRLR{rpK1UeXAm}qm$8l5ZRoy zo;`8&mQ%pj+xrDkcF%!Q>qq+lXG%unQ`BCN0GKp);H!5cC zg#Fc9S)Yp!w+pSb`xeag3&nggzW4I%=h0a_t@Y>6`a-Bs=2L%mY^tlpECG@I={V)U z@DI!c*SU{x;b#~o6!@*B7eYqLXl3#nMybH((SjSE|CVQ3W+QyLkXb(eC~deR7kRpMAxiMv zdZ`tNUJB1l$G&+V66jCTCR)yX3@FQC%)4WL(k-0YeL}%u;BcENv~|V9kMb>mIZmMK zT;pZ;v&ruNd*5b%F%Y|OEm6qL;Iz|!r|HoEqx$uY@aDv~@SZ>Hr&`7YGjir+m5}%; zYV~AMNvF#4XdBaHK?C(hfx!S!x9+#}Xe4s)n_)L0!Um~Z+Z5`40bVLe?4eIYLxg9e`*AYz$@mVrfd+w>6q0YD)HsG;q31M^JY*vz4x8sMsHZ0b{yp^YZR~*dr)6F zn=Ey~7u{4Vsq7id-`r#LWIg?05H^xF74o!tE0t(#w^E8ae+ES+WozMDe^tK}9xQBD zW5)1eC4p|Obx<>}qw&hLrHzkJ5ctZ>Yv@dcUxuDIV@su$Igi~5?3Hpw@${A& z{J-zFI5xm!U8gUHW;5q@!|gFBKq!y1^Wrncf9p;(b7gfCVvU5|*i-9e2dOfJneGfL zL{)A4;<{b|SpRx=TVw^^1lSvVhWtC{p@%d!^cneAXE}cdpIctt8FmM3@eP6HE9%T^ zuLMS>V(=}lxyQu0$kdur>xz0UMoz5QPnEtMZaXdq7x}s@@N{UV{-CUF^^E_d7g_Udx$#oclt#|T(5>+*BM6J zfhOOC_ZwFVD$dhG5c{b)Lu=FYM*GU{y@AoB3zxyKN3XVnNQ&4O+8F7(q= zIrV{YWOjA4w4!x~li~*!ZHn8Vx_Rw~=EOypaa3T-e1w z)(L)o3$X2z{@pAo3g=}?#HJlNMS$QX%|xCeCUR>7U08WZI;?X=w~pyBI@ zbTO9t<9Gg)9b<;E8RC8lw-FfsOe4rdcy=#wnHi)D4*KCrJq&HpkF%15dT*5sH~QC8 z0!C67t7$@y9jBuI#)`|Hvpfa;qaxKWuY`t%0l%O$0!E463)2A5uC0g_N@l^EkI1<9 z#_!wN7|thR^MLfY>xZM<#ZaR%oR4YFOfIt8hyT3xwACDlM;~F0LC}(u6Xw(T&-^HN zALK`P)XX0Ul^oz|$%;W^iQ$puYd&s|3Z1D&VzDQ#?mppMi+q)v`->rqP+gpjuD(|W zE(w;PKx^HU2~_SXstHs1-#y{cG<-?PfX=TgGIG5d^q@fbSo5lN!(sDb^I~~V|4qTv z4;Gvk)^<{w7S+);0GJshPW4tEO(jX5 z3`IO@5*rUh;RoHRC;6KtfKZDZ?t0%Rl3#r83S<%<^d>x#a+yzI&Bae7M|MaKg5Zd% zvpYI?6v-)lfqMH74khxX6}!0%PHI#ElksSzU#sH-CitL3+5lTvi483RA6g6Dx+|r5 z`h#}K1i3lKd)y}R)2fRMsFsE(pyEy&=ii8v@Vc;pJ;htrGLuSsEsjBva5>4EmN$nJ z8jjy>e0qrJTBMS0Og@T&C(obDBa@v=n*G88CbFZsL6Rt-yz%e6TQChqVGh^bFXxjFzNU)5^l_o#H^`fOCgt?-eHr>BZEb8` z%RcejEz7A7k6E3QuY*OgznpSxZs3pDrqJ&eFi`vsf8(My9kQ&b`PCZ=XEBP0;tK~< zk^~CZtNAM<&6+hjtm^)PfhXzGuz}atfKFv7@!Vej;*B(6z5)aP7Pam6_%O)pd!1dK zmHMrmy0}IQ4~Wq^x%ZnzdN`6cOf!_;k@r^)dm*`r^G$1IX?OF8GlGjhfHoe0-%afX z)292DYAmE91;oIf=1>oiGP|qL)oB=RDyjkdePxpx-N@ApBr>c8goY)hgfs530`L3R z5neBM$-S{QHmT=kGFkVS#wr#oqHfU>ik_^kZK`yfW5chp#lSIJPEDAN@+xMAXj~B+ zyrw^N;(LoVJdp|eYV)VjguzL;q*OD5`#wX$Owvkc8nK{26rhdMu8Nz3WrAv}b*@rH znIYBnCtSu4@|#%G-*6B=zU%Td8~zUGP>Zu7yu<6}*v3c@MR9WO3a>Hj#r@t*SW@JaP(OXpgV=f6LV{E!V zHOb-mXgLp~E^>$It3wg<_BjO9jTVBL{>&XZHDiPNbb%PZdu^{$v_DmUgF}Lk370{N z>8rnYAAIt)7_MdzN{Jhcal2?#bpPl=N%86NAO+dg9@sO$k1IE8!hGdQP=ew0*DN=c zR`yJ@@wdasK%$3%W|BDb1pUf4T^v$HST+H-iE92#8C^Ek3O6hJ`BKAGf9QYhOR`rV z|4j9ZuZf_M+90C2WINiXE6GkBTc154!xQ72`t>5#+JSH)FFAboRE7yo`Lyt_?+!j?BiQB=^}gXt@%hNTUH(~o5$Q@MXICiy)!&kkR?qmhCNe-W^?+mlVFoJs^-k{0MzVI=aXFNC^Y7mdYk z3Azz7a(?K)NpXD)m;1ZnBn0j|ll>K$?J?vgTdJT0#T1&&B%DM3^Y?(yL$@Xm^9Qf7 z2b_9bjLzxg-&PnC-0o2Rzg<%eZ7RfH8{Ior{)qzGiOduc)*HxdBG%kw2yoCy6-hda z7|%X^@D9c>PGSvev{wXAPIPwf`5d%p{63CGw+~a>Tgj+}S?dQycFznz!;dxZuv4%# zX{xtL)Z3NU_3@9Vkp}CmEY!(+#h_MreY8cXAcI;(y3g%5IPH$7@HJ4I&)f9P7eErd z9g8Q#ftKvn7Ri^oPYT_x`<~6DYr6xK%N=T7g`+PSxK6C`EguIqNR14ntH6OPvr>cf)=5t znTnd}eI&uhnaZP*!*ndlxHb;YWTR1Tmch>(O)`b465n9^YvCPaz*s_(`qu6z!GHaZ zKSU7^?Fpv(|8u5}nW7XtY}LdgpV;?Ic^9;CBEcv`qAQD!Nsljd5RMlYmB9x<>uHBjIH-g z+6C;}!6%)ngOdk3V}JS+DOn-N9JrY?B_4|@Vb098kN^3|J{}$a z;d6opS9234Nx|-vLrJt_{s7^Kjc1UsCo+OnB{mIw`p6)$v1nf8%gqan|_k4|xu z&qmBWZ&f)iWlyyv_y@N_)B0}~;pb~^7v@#uA*}^E9sgkxx4h4^k}{Fscs^HzIVF8C)ZwN0 z%DrxubNi){V_8&47P;`IlxBeD4;#8?(t>Oh6Hu6`do0ZUn=TQ(^_(6W^uh9BNt%nj z_xOeATVin>?63;Bj>oG)1Jsu(liKaqo947Y>FrgTpJPP#Y{_idy%!;A-*GN!?Aq!x z#vJ3mFRM)bNihg3>jEt{nx2IFdKfk7B^;RMKiWPfW&cdU9oIZkZt&( z6$e3}a6GjZy+Bzxwc_D< z(d|-%ha(AF{mSPnkZsRCK)1it2x#VBV=;#4e|wEXDzz_}@JFXs7MD;(BeBAP6PttY zpIm|W@R+=Y-1CuX#~l&0WTrHMtdoSPrgXgoJ zsD+HHigEo?=+ylalZ2wgA<35cPzF5}+J&$)lwIX(LsTsop)c~Z%%p9XLU3`7u5?i3 zdzs5{|KU$+ZH~bmtZKS*r}Rk6L=<&*Y@qOQcSl%bG4cEvl3`PR&w~KlfuAAgi;XhR zYWD+N$hF)zXsq+27QmE!q$Fr_k_cufwS@X>npmXIxIlh}W+Kq+%L@}O7dxN2ws?IR z&DJ%e;-k7#derorc>hPSa9PgZgbQ;|A1OXi+7Qb&jSdvvD2j8<*c-X>&d`Xr{$dQ+)@oq|8Rc^5AY$%HR+#NY23qy=|LKFKx09p}Gh_ z=F01hWYXk{f}bxp`PKB}FE<;xg_0fj)YMdgQK9>S)k_amCE-0oJXL!VzL}$Hv`wOntFOsm1$0(p&m}gw_ z3Ar=2mW*UJ6-ptq_7tw|on5(W$`7C3riE9%M5)iVLrK;T0%)Ncx#1ALKFiD$cBPp} zZt|VE$be|xU#MKgwFP~=G3?m>w$=GkbcR`H2)lF&5+AdF z%G4GISDn}R!Ccd^xS~OI`(yFewe;1ZCI+!0m18lCYqtN0vJt#?Oniu^{u=7Df!&rB zGjZ&z6cS$?i_ahU7e#lg^lL|A$N zBCQAj&Qa{OUFgD=^Yfyv=v8(g4!tXkq{vRn^0Uq)MgfsXFuvf zA8D?G=hJoJ$-uxZI`?FPk^e)cJAb&1sNN(j@cV1uXQSuRU#_9HGGl{Bw8jsm9&5QR z*uu%H@n7MgVTM5bAEwqPSjE)MR#-Qh^-{)qw0qkg+oT!9aez60=_-1$l`agsW_A&=P(R4m4lizM<_ zb2giRzgO0<^Z|~FT35f+rgD+)!@!$OvP>OFDHMxlZKm(rjQ_!{&hybiV7oB_?P{!o z83;R24)Op@>b&|M>1-+1_Vz6@vYM|YJn3((g6+BLI%uQ?)2`IXaWVqFmB@}6nA9}w zaC0FJ#;2VkDqscTPYGo@Zn!pUU|C;rCHeT6 zyNbfebw~9g55)xSX!6Y3J_Ib;jx+fVY#4>Dnj6_g^|>nAN5>a)PYBaGDlS^wCE{0L zg{flIA)cAOro|exm2WTmX2b)w@aXbyT7B#)zdZ+S`(3DLP(j8Q5l%wTCgisKfS`>= zWMj9gWB&B0i+JG=7}oUa^GosW0qwf?ChLjAn_RVMTYaF%jVSdvea+b8BE9DB7~Fa> z$;P(K!PUvL>vHR4J5Kd~w$=g)+jsNQfAq>YTD(&%M6=r{)Dpx3L4%AnQy1r9l~7_jE~3CEd4dgwG0-kt{~w zV=kn6pP$+bHnQW*D1Q(Ok0On`m_#Z|>BOmli_X{zbdZoI68~@o83k;XR>>*#3=o^( z)6G!)OSSxYkp~?z>_-{bKe{oQ#p0FG1w0z zjgwpC1`b`ugEosum}u%GuL;vWJ-Ofz_E?qB?E-;DrbG-NxiqHu=(CxPAWeT`64GxH zJMYPzt*eS31=&Ugn6%;WWJgrh&+$Sk;5jg61YxDh|Lj6+Rg9i;2shh z-_H=>h5_aMjhsH{1ys~`g(7eNaoiFKZPNHLr{gRMreKmLkyPhBIHz^0bqtLGpz~E@ zg;^e1Lc+{B{UBr1TG-OZlIt(@O=eGWLRB3p;WdpdA|PXJ1Rw@+2yFpNp^vrw1?=wB z$qV7X;wtF?D4lI~!>wOZBdfkj$JO%2SaRS$Tx|%Mwk;d7utN@aBtYQysx@r$sdqrc zgUC{d6bdxIlSM_#CqZtziw>fa^mE20T7Gh;+}h$QKr+>(-tJj_MLDR?DRaReV2EyZ z3{3aqsVxo!7+z)x>5z!kbKJR>JiZ$LbnPhr>>r>UeHDsT7@v3dp|I8Qn^g!x%*qE_ zG)J9Ia%TKL{fxW*CLC?RV7&)MIoGtbteYcr@zQ&iL&0t*P4BeIN2=sm#m+qM{u z)v@{drJEzqNO-gaj;s~RR)xI=>IA@6s>i{pto`%daO`LwuwV=QhV_0|p&u7ye8D+$ zNL3cYKN%#fNFg|0BnIp$G_@Cxr3n>O-`X#M0tLp2*byjibtI2pBKcSH2%i+BuDbt( z2^ktgSqQ1RgUwUYT)KGj@|c2wFYl(N{rYyyv=lNm)&d4>O+%q!6{D6G@=$+S)D4jP z(GbnngthfM*N#cU77vFMm(b@G^+RS=x~2#VsdN0VA4YP!3X)S>F@UXQgd5^l-8_qf z05ZHXYxX5^xnPO#zohlro>0@XH#`fdAr82t7A#84Lf{XacQ3hQG}8+=w9Ocm1Tc@H z{AS%Z1T8rZQ!th(Tgcl=Gu<^oFtbY>c2G)#^(t<)CCSYet$<5^<^1PBOs1jE!WL#1 zL?gHOqAf`ynI8=LR;2(Iovs=x8$s^U!k#oA0uV)}y>g$aH%reO?nhRj8ycw#5}Fnk zldBfEuMsGQP0H}KA=;?Q!m2`xK`=+9|4Or0MxB?$%NyQbt=JXIg%MMe7X!DfbA?V& z3)xho3>%me+Z?h6AQOa5PgNID$g|b?&}jTP@MwVycO(dAc=@N!%_wM$h`wJvsqE$V zCOS5*`V9sipO?ORqruMiMrmZN-qC@1N$Vu2u=?`4CMb87*0Nz!0SKHnTQ&(8m@wxc z70gg}du@gEa5|$+M#Fk@nN$FK42ztZMp?+`&5=Rb)sGi*6xB+GhfRv%EE8CGJXB<0 zwzX5eibV?Y_pWk29$H;bWF?bsH=FmsUs^!9<_N@D)6;BeU_`?c@TKVh3}<{vSZgE| zgw-`W56Y?%_%(PDu3@ypokBXXy|W1m4eGN2#;VM=<6h6o0dtgKG_tujpeX$;4o1Dq z`U&7TpP6TtmuYWoDz!YIK){?q*rbxros~fV{5pA(0sf>doaStM*>If-8l$N5&Tv#H zAPD%q5l{><(I?M6kR2gNSt}r(1b*N-!hL{7{WTf%P{+@9;H7`4@cD1rI5OxEHqb63 zJlZK}m-Zw8zlMT9t~7nW&Zbouiy{Ev2cT^NY=NwJ@EXxb=X&cu+aykS?42jyhsXIj zfoGo`F15D^V|I)Y8DP(az*t(~KYzXs25eHn#|S;&@SFsg--lm-=wYQGR(y8SPiQhh zQo%kL$;_z(W-g!fXL!dMJ)m%M778vZ)lIKuIrk7o8<0ge?t2Vjj8Wh;^+dgsbpT+> z2&yet;nO|}1ZjVx#l8g2Pi2cdzCWHSrCSj$=CG299O}aMaBIZ4#|ZLSGwYd3W~Y*8 zhT1Xq?VA190}_lChaVtRQDOt-LE?H(kG9&z5Ap{|0W=(rXik%h-0^Z-uKUddV+&%A zb4cUG5Ld+cvs&T7(eGE3XEQSFgK`0+Yv>82jKmagVCGRgJ)c-e>m>6j+h;79V%vmM%J2`FJPRZC47>;-PHr z0_$8?@9`on`3bb_SE%Ym& zk#4p&p#*j;Hh;(xfRKb-Q`=B5Ra$lKeqY%enR@UiF6pjCgQ8QeHq_jB@Z)?cLW@B; z-IL9UDNUU8;|3aOKSbKeu`+xSp@&GlrMn-)8rILV5CicJ+#`GJN!Gk}6sMCiGko?F z-Ve#ea7l@NlpeCBtuw|Bw9ECX^*K{Y0DF!MRKvoX)lR)=9+LB(z!9mHh=fsvL#q}h zjGGp-gj|2?oRG$I-pXDMq057epsN1jK-?2 zaTk#a?`IQL+3)OpixqR8bd!uop`cJSpCcShyRHK~f+W zFNWB(M#czL5xR8cs$kqP6-|8 z(6p`Pz%(mVZ6jb>3SEK1oY!OdE-qmVg3z`C``1zlLZ8~(LqLJ0L;Eo}kKSK^w`J=_ zMX`-Cr-^nXTlX!h`=7f62tavVT%=)etaTX;UmQ1y7n0aWpmH)^pFN78W&#Xllmlju zuwbgrEZo?d#ub4MypVhZN$f_F%mUlTiWvIQN{9;WtObLN17#R<@>d=2s$OHlr#Wz^ zkRxp~tZ>vIHXuA2MiLyhH!|J@pKD@qU`5J5axZwq?uUWRH*w>*8~;wEH1Rc9AZroX zoqq3&PyQ$eH<}RYP-1KZgQvseT5*##8EljwlES%c??LSKti!=?C=pz^1 zrITKqJ3e^f&9qrUK_U3i5HgcD?@)iegfQ_=XQ8-y%VUOoNH}j$jUR9qW{*+f{4L<=zAyjSE_`hN`3Z0>zfw5h}-kIS-q3q#^Vm zGFa&|S9N7WU43`U0uQMse%FJ}5UP1sWL4o>^UJI^JDw>R@@4a-S!7*+E;w%F;%rr2LP_Nj(oHAr( zGg*C7Ma*e_%H>iCLj+(52`v;FNl~cO9`tQO65W**(TnpDHC=de01^|+o3|FPoKGqJQv(DSHd4U~WP+;KRK964$G}{Z*SYj@yT|vxZ zuTg_~K=zmp&Ci=CVb5`VjB~d4O%Pi05(XYgR%~ULt6?R*#3tMv2Htxi0qb;CA`9n3 zSK$v~AwN_EYnx8YFtcWOh$kef1crxg?Km9}*S-zlY#lw%2NgjIz$8_mIFNHrCduk zt|bG+h1jISL~d$(3gaBE>eP~)3Zb%>YT09(AG41yQa^lsm&*`jE?DN#KdL(~?L~aH z+mC~D(a)?eCyghVBy;_eePY036eMiu9pO49Pn3;m__dY|nW%IiFr#{im6Ym2dT@ z^L;lVrxhH^&zmy|@II2|OrpEtzm~BYQo7ZEUQv-&cgJ~-Np;yF&wVlXBaGoG*a(P4 zwW7duTzs&Gk4Zr%ZEMge@_74=Q3Z3E`MDk(2_^8X6tECnQC?*4L{|u&C)*f86K%3F{7v&*7cLSnm zc2^zhI*_N|J8Vhuh|JeM75eJ0N6q_4(DK3`i^~2u$!`mEh@V6r>Wu6}3MCqrZJD~} z42Eoc=&iuZo^!;=Q=4n9%I=>%LMw2G9xif@w5pHGv7G5_*LFcd)|SqY=!;lU9Jym0 zrp{3pMB;NCb&4mVeKjrd=v(wnut`HB9bA!(@r=aPlC5KO4KoW@(vb#WySW?`a<^=M za{BYE(hk5nqm^Y!P!SYD!x!uKf_4!HVTRl}kfKV^ZZ5@Z-~EG zNP2mFtxAuzr4Ty#B6=7u+~_WTmtNh`nKC4lcy0?hA=ZYle$Y=w*&X*|W`kz`F6=Hf z0_znzsh(X?&X{Wg(%(vz98P@aFz!iopjS_cfATW=GxuFKjFWTR4m^&lxn<$G=yUvj#gjv8f6Bf{EztMQ^e9urhJ~p5OZ|;5yoU z(qI|}G0u8CbA4sGZzg~q)Z!n|wer?na3(O~{YLC_B;Bt=t3IzVTA=LQTA?=236G|H z&Y%qNSajF3(NQ-$C4rX}>KEnO9e0!?xDw3Pzu1bd!$U3Vm#;xk~`6Nv} zz>zF`jD5yJ(H4%QTD}tPisN(k-))?Xq!|EeMqEeD0VqUsuFehF?=SY{`a8MBA-Zri zMx>#!_8b0o+Jh4>EqCK&Lvu>K0`6@;JsM#E?x^xtvUDaK?#yoHj1Q3sFt(jfJq{1D z@M!QchgGVI%PcB7|`e)}_{IY)$5`3EAx9#bbFl(-LESHM-Q%?b0-5jkr`N z$?_fZZYN&e*3yyd+xy$jp+9BeqNY*LmymZyA`QuHTZ(+5iMJX&+ zT4&t9UFz{ZBFt3wH5#kf16nR-l{Azl=a`!)#jp6PMAUD~gG97tT#VM%5GXbf1xtCKVVu8;+(K<^l9UUZySt;< zQ;7vIoxfSmwPhJgx;HhO|2<%e`xzX39~@4Pxw1*c!d&r+&p*uHKNabpiXFh#-}LT1 zeUQMYc9}}>rUTU#oQq@*cf;Fe4ovdvj^5}ki{xen7gubD&0Tqs~caXzxdY*U2 z(s6Bn`#i{wSd8Tth(-#@E12$7pRamwn~w*60Fr+ad;rrh;SpvCK0o$5#kt_WTubfp zzv|9K_@_Bb&2aPfCUGIR7xr~mGXZ1W052*#wX)_`KXhiA9zu5fTb$g0cBY-HFz_%Y zuOCv2I>ZYBmYOtz1u4#ZNUtY#00e(W-Niif%Ow;?8O!##bY;9DG*Va?_}0Fu36ECT z=gNh=Er_|R1D^dw=80CgOVR^l!PH#XK#S+~X)#iICdF0rJ%zQU0~x83qTad%d>uhs z?u;0tq;qkJUS7OTI~*F?3;mQ#UtKlVbZ>3e|MGEf;H|BdN&XIdf4hwKT`ay80OuMF zp5j<0c8?ha!ATH9h4ya+$AYh(9Sw>)B<>M<>g0KQEPL^NUQ3ig$w=LqvEDNyJ7C&$ z?@`)WoV6wRmgDEvZ-`?n)-UGC5nV`IC+{l}JgQCKe-@*$$cCa=Rp=CWhTucMsl>Ph zIlP{Id+$~?I^hEC4+rDv9Xla3bcj0;W8>%@4@tyZtJ7pTKxE&ji^Fq!Z=m=MMG$@d0HR)A(@|~tuE!`7Rj*H@(-dN#@ z2;Ru29l-TPBlhjGy^bq+l@yqQb5V?{_Utl6oa;;?F1_j1;N_3dN&K(#;~Sz*nfXpV zOiK>43A!bCkbrdsD%2G&~c8ys_aqY>Bhs33Mt5nOsxTeC4g zJm>V1tUwQu%V*`RJ2}=A$DZ237KUXb{&wF}l8-;VhV|KST0ZP7IeJ2c*c24lC#~NE zJcD{2Z>Q(;!;K<0Wk&nSFQgYm9q>2M=TaJYo~beY;t#0Sh8)4HLEc4c*_Cs8*k)%^53czH}4;8NIuuw*zW~uk2swQ7slQF-i3|b zSmy}Ij8_x0NH>-vgT1i~BW}7mRhD#oNS-3W#_G#ObkOs;S}viePS()wwSd)w%I)V~ zqkH`BBdtU$QFqg!l<}qCPR4HA+CS;!?-vi8x#Eah-#v`)i3D136F(4p8NYEz=Idw% ztT~!TysTcgG4IlaZEJVpmf0hq_^0O8>g=^Q*jp21WB$lJR@G|tSEe;9i|)R~b7A#R zK$}AzVV<+$n48l!vW>5}2`+wR;*l#vuNyom=aLG$+Pcz7AsKYogVciXdm$#JiF=VQ z4oBBSdrH-kSZ^zP&7pPPs}_5m%4RgS2sQfu3@e=M?Lk7pUpmKU+YB&}mcQrud@4E;s?H>2z1^p~; z4vUuni!H2NqKDGzl7oMJjXu<&2ey#V*va=Cd3dEK)e5C_$6Lh?q0K+aQYXsM2>1L< zugO$hI+qkqTT00l1az`hy}-!sN=orD8H9UboChX6z|%s5v30=*ukX*FmOxX;aUHH2 zXxMrMGR|Sa!HiP%7h6yP-oCtwspMBT7tvgFqT9RVaF>2I%LhhW3`|{Gx@pyKfqft2 z=6!YvlNUKg-z4)=c$QXYQ3W`+dJZV25RmXStpX<{+&=FaWRAf?A7mHydyzjYLzcS_ zoF3jbsq&jwC?+w$0sWJc?0bFAri`p&ipCXTIsc5V)AS$=pKUZWT)vvIzSj|?c4slh z)^OOZDJFnO8`(G?YMd=r*UY@kiHb?fJ0-#vcT%hGlV&v{ZpJRD+dBeiF=Qm;__^*=(oCcRLj+G1mpQI|Q#6;~=EzE2+ z6Ex#YzU@O}RagN4A1K?T*vkfiW%1mbD*RINHs_hrbUhOh$P&J+gX~q@)b^gpt=tz| z1cU98s%3Ef8p^fCFPOOgrvlg7BYquTOJ6tPBVK3J zJzY<~nTU3Eb(42>c4M9t5NK|4c0--aBfC2-&dl%cPckR3q`b)f0|{8gB_92?x#(x}k%`L4 zSA5*xC`8Sty|uOtt^@DC5Gbs+->c~G@6u21j4KcE@|-<_H;?~A3BV?Ndt4jiA=Q>% zq3Gb?iswsVVWsH@$YWrlP1)2{1}afai!W`Rf2^5p=Qb+!h_L_$0`b)rMk)9A+zek| zJL{hbx5MAd{Fv}aiPxJ0toBh|U7?S2T>&&yj#LQ?S?;d}WPuXXG^Trn4>j+2p+2A+ zEK58-ROYq40zDAG-zR;gd#}89q^?^VCAUA}kgh{}8A1TNkbzgTp9wutVccB)SlIsi z>VX7EJn*REA3IPFr}_$8H*=BjJ_p>0SgBXn?Ug7)u6x(k<(B|%K57qSK%?Y$J`Y3& z{{I#&7K%!mJzmg>!56lA{nDv*5zqPpI1@aRV#d<(vO{~KeVBe9nlgFc#!t@6GYc+H zM7=kz&|kV_qJXN@cjo!;pZLeazUWv~DtHDiN9vyLAxZVg?Dis#BsG#uc()cI?A0$XtTn`B!jbenFuSQ;K04PIhJnt^_I;JK{Se{{>fnTQ zyO{!?JbMv&qZ&OL9V=AL=Yb0FlmboQ5`X@)M~H6WI=R{&vRutT*YX7tqvf4HQ4UBD z4V(&m!j@b-k>n~-H%5~Ts291+ewCvEBx%zZVqX&4n8VR0XB0akuP%Lw+u;F{lrfS-Q!UpHkV?=Q z%~vW9bO^NF5?&Hy)h*95t8)MHg0Ij7fY}wSmhRaG!D)xSFnsUTpG{1{G+kdWiO-eM z$mPj2hye=7BpE7idlAQDL}e@x071V^LB%i0Ui$q4sjsiKD&Ynsv*h5L!Us5_gWLlM8ZZb0CbYqG!D3`(qA=}r zjo64_vK9dD2T{$cvcmDhc2u8VYKRU^1W&q076d~Wms z0HJ+82vFAHpHr*F6wcRsCpFfByU%M?CqdlkfK?+k&z*zjzb(_i!9XSAa;SFNg-Dz6 zW%hivur8}L}dUYzKL72wj-n9XPXYP!qnj6b%N9B#|bJ9@7q zGqA7eV>%&~o%wW^yVi=S4+TL{MEPkqK4@=vk$VCRe-te*{6fDpBornKI3cA|S=QuF z+dudw@@EakDWuBP0w~-=DFW=ty;Oxf2_^^(>=}pEsex@6Ch}yx@3Uj~AOsWbKT!BW zl^{8>jP+(%?6i6;`;*R{|G1Bq?Bs+#-Z_3BHV3`%0DFv(U9=E-k2Sy9cFKz0de}?< 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 literal 0 HcmV?d00001 diff --git a/resources/images/stores/amazon_fr.png b/resources/images/stores/amazon_fr.png new file mode 100644 index 0000000000000000000000000000000000000000..5fe7d255c6e8db247a2c6fb55c3f2839bccf9f84 GIT binary patch literal 2378 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 literal 0 HcmV?d00001 diff --git a/resources/images/stores/amazon_it.png b/resources/images/stores/amazon_it.png new file mode 100644 index 0000000000000000000000000000000000000000..335efaf02fc56a19759deb3613354069686639c2 GIT binary patch literal 2335 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 literal 0 HcmV?d00001 diff --git a/resources/images/stores/amazon_sa.png b/resources/images/stores/amazon_sa.png new file mode 100644 index 0000000000000000000000000000000000000000..c1cc7add400b2e60979d1d62ffd28d3ec0b8c180 GIT binary patch literal 2460 zcmV;N31jw&P)(jHwRsy-HDmV)Omu;;t9 zCwM-=hux+acbmPgEf>h(2wDene&1S0)n3exX(zKRKVZgq6iojOO85~mR8(4VtV?*2 zGY%HV5s(1&d&wGG3(EB4qfxUpK7SIOlYUYsU!Vejsr1ca>y~Jus3BvxY7)*l+>p@1+A6LYn70 zAJ&Y&Re?a2nkPE@3k5!%t zkiX?_-AUUdbDy{8S)M2v0-R$O)7}(vgmcstD3#p;044?96)2YyJQW=UU`eS|?pNx&#C6a@`lmaK-s||O9DdnJC!407P zsj&9abmyi}WAW+Uw>mFscBr>rD<5&L`x)!kubE@t8SeMSn*jhPy$fP|Sm}Ng&V8>} zCfbczvS966vKq36e8td&Ax$W@2>=fb1X$JYwBJ)=hTm%37OH078}@E;A!u?`8J(1z2#w%BBO$RX5*b-nG1u(fqilf5# zx`Gm2_twrcKq4W{!IKM=NUv5OEem7K^;UG>{|q~mGD_@zJO$d1GvXMMjN@W07kuey|8{Q?J2~v znZlUQo$q<)Y@KHv0Or?Rz`IUfub~ZeD~)!7Y#MR4N5+f{R^sg%9vOJ zc`{tMX>W|v9@tj^izN_Z%&%g$&->=Cde&LXQ+bjmZf}IUpM!IqE7Z9gf&h@j>7916 zmPvql!(~gdrK!&ma|UXGlF?Cbfega#Jy5J>k`idS-B(S5r!A8J{H^WUvTnhm`vpNaVZWpDIDis>DL9zB*2zij+^bNPIf5W0;|V>gbJTG zQ+E#lKZ`N5@%gJ1z|?e*)Uo(Yn`jS|XR1jn&bkFF`el;+Nt++$k$R#J}rKp_FbTr2#(!=9H7fKv}V46wPIV{opWI0CbRdU@&>rvL_{{nH(o z3(^^fchkIm0Wi-XH>GLxujN;G&Rk*%X}E+xT5B;~Pg>fZ>}YM20qm%U_oR_g9Vud( z%Z5e)Ox9}4i4@h{7&6UdjMdM!-Eonk=i7MT?2}Uf^V^mOd@caC*_%*v!tOIWV*t4` zajq5I0~#F^CmK^GQR9TWA&Do^-^efSc{FJ-zW>bEpOUH*N#hm$YjX8s58KlKfi;i| zB+C`D*uDW*mDRGVKH|xv_`-d)AHdWPw_Bf-OIZ0qO#g}Q<+(!ACCSQ8<>f8OnDF^e zde1LqHrrRRDS%U#;)-zx^?Rwago9o|eqUf{fLKEDEcQGr77{^|#Uh-t)zo{{7-q+@ zlrWTvqWXUrK-s41-923_BrjC=QhiPuO1$DgrLJp5+%*gR;mU|omo5q$xFxMnrVJ-H1UaQzh z@LCN@1kmyvs!pLzXBvdE)v|;tb_sxPwqNoIzQE%xpyA;294KaYSZgW1cLNOu{bB(; zjeV3u)eCYb2xkSifDI7ft@zvKe|3hb>=h31GdV^pAtUx3zFanm0_KcbK^CNY_{ zlknD_yXWh2Nd5Z+rvn6eC+N#Y0<Kr5uiz+*SJew&&#cy9Kdb-`&wAtyO!H0C8=V4vX!tM aD9=9$X@yesE;Z}`0000<3WY0 zh(;jZmRGc*i7|*#@g@=@?t*|98myEt9?_V@R7^}f3a_F>QA$fuw>;K!SqU(m?|=Pz zxBKs7W?^Tuv8Jp3nwjp`uiyK>|M>pDM~J}D*itgHro0)l9P&P7BjkguVf{HRL8Phr=W6Fz3a;E)5I=6q(`WEiFK5JV4z#LEE`%O8+8R_5C)YP>OrWleZuF1U(O*k7Z1IR;E}Q6+3?7#9ea*IoqE%W0sCm=b;1Ah0=*BpNDjg$(J&s z#>bX9L;ea6D?{8ASw6OD1uW=7>pyK5uj*UJFv$8ihH z7OZ1&y}QEty0dxv$fyC@b1U4}F9rEWcKa51_EosHA>p2$Y{5P)0&u}`LgU~hx#spP zzIVmU@p(31D%K2Ly#cpM`D<)6|N2#dZuW&RChAi-yh&P$2&|EY3)pNuiv_ZuvL zvDT}rxi4o}r&I*kl#i!5PffrjkHqBb*e3r#9s|Y9#P8SnVlo9eItAcz6$(imLt!{% zfKhLM9TDebjGKb*_s7g}Av53^IJY`R)<*BOf7u6;;T){fR6mepoCW(-jJubu`O9px z{B|P1m7|ZF6z(_Ed*yWYpeHG0_ixgg5Tk_SQUG>U{x)QLL&0f4p1maj;7c3w&NyvE z+&eq_=P!+7*Vb`GXs`lftUD|2+i;pOwwKNQ+K6It$9~V353=ja*mIu&7G&%cb4Esy~awQQRSbH1K&k;qyDAE89=Fx@*XM64`AYu=N z6=gljyy3PW86)CGga+3-;&L?Bd{5f^k^GoDayyK9k*#}ukmPBeus$5QtnY@OYhT0x zq@0}uaIRpV)o)+~awKw15kqwlVHh{%`;PA&;xifBwP-OMpgtSP99 z%z$uVbI)+=9SFb_{=&+Nc+2TbnTx6cm>4*fR^*|Ic_!tv6oAQKBa9ejTCrH%hDDXL z`Me`rTXzV}m6!P_+49P)7=X)S>eQ_mz^^6gbx;pgn}Hoy%QP!`aEmpb<@uM=8K*VB z0>DTU*0ohL=>&lFTHoa4h_iOB{7d<~5rE*WG0#3J0pQ+fZ7o+;3osdAqiKDIq6Tpn zew~a@McWqVuuv621vwPDf+hQp@p%K~9328bttG%wA{GW%*XY4#5VKtdHb2c{y7up@ z0a$U2h{ws3S!677H;*VWW!CfrfY)v+z}k@fG`B&A^TmDFQTwZglt*5By)ioF zAf#Nv>3mGC==om(JbHpWfR`#<5%_&qC4hJB4k+9SyR`*3n&U@+oIm?9Lyri(Mh%JpaN{lhq0%(h*U=B zurE*q4%HCDtPC=FZp1q&*HyB7>ah+Lz!C36sERSb9+qNOG6Q&^Lf(Ey)umpWa|=JG zQJHKBN+hfPJ?NTd%S;SiAh2Tkh>wN3Rg>SzdH>HZQ z=q^?lul4J_S_3??sdW#{>eI@if8m~2cE;~p5lv1|hE{aG$@Fy@tqFVEP4~-Hdp207ld;h}ex94fiIo3l#&1ga z$($?~;o+d*L=m=Ww2fs(^g=2NW&j6E0QH{Quq_>$`1&@|SlWnwm;H%)<%kj|%n|Gr z6h0eU+V@ZCijAiZ7d2)4Jlr=2$}v56Y^31iE3oEJy&dhS$ydT9mNi2?54Eg5_k6$* zC~7^a-kp=dA;K~uT2^cHuD?tU8G5&^-r*LkpXktcHTu)XYuX+1_TWqVV9O4D)~TLY z$8eU51s~XV-LSq-mdBMF%pHx*mJ!K8FeEYz{JG#pRijf!3-4EXXNiAPRZ2-T7ko|EoQ8il_AYe_BU0UO_uTIv{1O zsL#~z4$(TS*11sZqdeI&$afLCx7F+K((9gFM>8Gh$&hzgZd(btj0lYD4n3oD7l~Z} z>JRYzhav0rn%3joEOmXp$mnw)4c8Piyg*(ec7vkkLe|9)nKh7w@a&V^+eEHr(S-7B zhnNHTcK}phUdH(Y`^I;Oi}m>{>*x(c(Vl1gHW0Ssqzj((IFx)7zMqB=+K%tzE9G$I zm5am(0t)v{!yH%O`!algF~P809a&CsXN#?wLA<2a@f)#c9kopXngH|ib9#vqm0n0U z2JEZfKAWCPbXz(9_wBel6e!$Y9x3v^SVya_-JM92^pK@O=9 z&Zm*o(HZm#U+-xqO8zIXfH^7LF@Y!+w7!F{AStC>)fG~N2X4`k_4%rGF xnm^Va*`nJKJ_CD&U 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 literal 0 HcmV?d00001 diff --git a/resources/images/stores/diy_com.svg b/resources/images/stores/diy_com.svg new file mode 100644 index 0000000..f353c0e --- /dev/null +++ b/resources/images/stores/diy_com.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/stores/ebay.png b/resources/images/stores/ebay.png new file mode 100644 index 0000000000000000000000000000000000000000..77dc6f1c962a035563ed1c6056296cc39fce4845 GIT binary patch literal 6903 zcma)hXEYp6)Hkb>=)Fhp5xrZ&>VmLVCtB1Hb=6p_ccMp0v}G5oi*7~lL=8dID2bMY zXs>_X_x4gj^g6r2x9TWSDyi9 zunWlTtJBiG6%-K#&crA3d2YhADVFQ`;^l}u*BJs2?lq7ueR(vqj zDnxr%J{&MoxFx?kc~`M^rLZ;&^SJd%zJo$11G=J~sGfV_y>k4qo_8UHj-^cnvLiGh z@81KJ^t_ir=0HFM5U$QnPN)XK1w;@g0NKf7(S-k>w4=BuDQF{r>=+IV$NvNU!^fik z!T!yW{|Dm`;t={Dvi?c?Ux_fF*w%MGAa+}(F{w;e$*i0p5UBo5WFj@lbiBJVVyvOG zh=~L{`i+s?b>4Vf-2M_ko?U7qeYSXzXL%!qij!_Hgv!h)!5}RiDY3vuqkKo>GdNF+ z1%LGkDPG_z>_RWX>R zceLmP(l@DeF*R9w_spbBZWlmQO(2go&o#u@(}gV-{C;M?^&^My(P1eU>U^d~NKpb` zX}4(VcNjy+Y9#$A82xzNM=V$Hm`+M!3Y_v2Y(*j8BC;mKr>o7c#9kn5s|WKSgODE< zECWjNGw6@@x%!;iS1mJLC$TtAK!jyl>hf4yO{F7F;d|MIio$K&sNRpDC|%3myQsBQ zc%Y}mJQlbZflf@9G7qiI%Y$fF$1J^35bMo?v7`bGrWcnPQ#ShB0P_cd_5~&w^PRDy z6;ij{ek*fslE)6Te(oi?O>ata-*e2#VkHNLu_u8twe|y>*^CzeN+ORmG$aW}S;YD# zl}Nz+xuwa@?RF1p1qbz#Y%N6OagsgABC2)CRSJewulJU=oMS4DyL#(GcRXzMG%a!- zIArFJCdw+!YOk34wh}9*Qs!4XmW?YhUh5(yRgDIv@L${#Glg?@>WYu!CmH2&`+PoOHcNdja}Vqsr5M(`9pCtbRDfdlHNTK_)iAhk zjm(m37owX9y(!!$7 zR)&PFGxR;!R^;@n>z9_f-l9R7g(O9nO`y1Yxmcw^oxUa}Ln3h(6?wkySFJ_W{22_d zJF=!ZjI&ZV4PdF;lB)^t!XN41`%v?K4vLgvj2p5ld<27&XUTuoz;kYUDPIiNIVQno zXbEH8v}%*NRd*v7GX#(6M?P%P#&Qv!Lbk!`YJp={zZW&9MGsF=`U5pTxJM;= zsn#SDz0ylzd*ufLcQ>RyJEx#v-nXX=JmCEqyQjq;HvGjd(8t6~Hf&ZgN7ngLsHlG% z5+fQBkdcZa6{zkc=k(7PfM>EYldehcXQMcFh{HxL9i53dkafc#;~4p^=SnygXEQtH z^|%NmNJig3LC@^h_gR3{FXr6a(s*MH!mci|=^s>r?#Wk=PD3@L#$|JuQnlpZJPO!% zH*B^=kw0D9OT7=VnfNl%ftDs0zxBpW##n?1EU>|pCsCPis!3yb5UQ3c+x}G8u>#$A zQhp`49~Q~L#8ONEog(B3eat)!{Z9n{OK(MKLI4Y4c);WCC7gWRhzj*%ADVj2gZvN8 zY|_%1ncL!PNqNYHYr{h&LCu&nAgKjZfHWng>c@Pw2TEghpS4p2r~eOEh<@Cx@CpzCDOo`P>nUindQ5z~>1ekFfVb5$Xv zu>0(ZBK3=GR;r+L7@;pKiOGD^0sjYDe2#sO;vOY(T9m7w@tK7I5uC zN$9_uHC+K1FNhj|4n8Hzii+^m3yA)vQUf~{$F~iKv|zMF9SdVmCeE85>-lGLt6J%u z`*(2d6Vx2cR?zS=JUP)%J{erO76Xt%gHq>CG`Bv=ND>Y&6LFxD(3iCtT@ZzhC6+*y zbwdh(Ze;V`T;_>9wzOB#VDNduzAMt_hsKliJ<@D{i1RQbf5 zrLj3#HEv%C_^f?Abgd|fm3(RVGE?&I@bU4WYQnRI{gGFs96Qa@yFNbWP?X8M^{$5J zm}vgZZVo`KjG2PPpMFq(OCjEvQ^-0qf^C5kyKwK2w1!5lZStL^6+?X*H29Y!-r=cQ zr-k2Tv`fy3DQ)91ro2W6l$p6Hy%smd@Qwlb)0IbLc+s${vb`jMmbSJ&<^~_dR?`t9 zuBT*&_Z9Y3LtB+jumt79#RZTrveNF4qhceV<22Fs=g5xVmBY|yaSn6`?}wa?4wu(o z)G<`n{00g_ma;ScrHe%s;%w3@7HHk7ji}_%!*0u+!)Zl*eCrk}&x_7effij656| z!~WJ>K3HCZNQF^qC;_pG$y`_yT5zaznEJ+mTEg}^a{jtO*V1&TH+Uq z>*d_vBpVh*B{G-X@&Fy7e$_SWfEOdM-*qf!kBDzr)J~7P8*g8$FiwTO+-;Y^RQ2wx zS9(eKZoX1_dXtfh=4fjFR+Ec25zJY8Lz?g3RA>TjUVBa8d0ozbB2yv#Nb0=6Jp7}S z<=1u=v0~p2QOs*Ro6pyt#vI=wk?COdcy=Majl`3|6Z2nonNhlw`o$yh2tnQRu!qb^ z_E&@L3Ug43@{!(J|0}lhV^{>`HSLqhT0Dw*rLq|WYfmuCw`+)ngb$8@eM9ss1&Zyr zOVDnQpi#1yGv>BNtGvWzDX%n=sfZ=iQXCyJ?noW+!s%MhFzhz3vLHnl}g-b39^q|5pDu#9z&`6-T3ANpJU12wy^3Mm6!pbFC|I8Iyd(;tNZ%MZ6H%_tf;LkqD z?qu1F5c7IXVKa1V$7<|6+Hkb3IMQC-lUg$nUv+=nzW$A<^*6~Tj)!13KU29&LQK5O zI?0s4=&NR(KUPq^miMu9uUP38B0u&qo5{QG_r4x5m;TztD!x#6!!tR^k^-3Zh5jZo zl~3w!UO4viZlT}Zwhwf%UtF&REr%cJ*hl={TtG)Wy5|W`)4m5eW8v$a>Clm1x|+n!hdM0&qSAc=zBlF#IJOW z&)1KP{Lc4OkG8#L*;w!GD-}w{O66s%iv-5R^If92WZFWxhfd1K)0sP5jVM@mC*`_@!0OrtG1 zXp=o8H**EKIP5=hcbj*(=kc~7YrfV^o>Va9+^>2a|_4$}Gxkyec&k0k`xu1#*TdIc?qZmhoG@v@`*DQ~T0iw519+6RhF` z*E+tJJZ{b{v!`Izx0s*G(G$|*?^Rs&xd69yYczzubK6V=8tzlQ&A$g5mKE&7m*CtV zt`i9}ql`@t#C`B2pGE(eD2e;eNT8RG*NlpY1tstqHk4r+h&~=G?zEhFRp|mt_7ZQP zLdz*R(UKy`^hdZMdDe&ej`Cot%K9FtGS(8N8Oxf|hkdJD6m= z3N&)ld7eEeSE&sl|ClVIyxF97qw8*{5=@l0yetCrI>xQg9 zh7q27HZ>L}I6VkND3+tdEGRCxY*_k6b$|n~6;?&-8->Ivkmc8EZ=I^uR+D{Z&-Ux6u!JMX~KE}v6Yx1U;ULGessyE|N9aJ+jlSRE6#Gn=aupNQ2Yx(CP4qi69LS zZ7}W9Dg+X}$+7^@85KZ&a&jZuL;}Y=N^mje$b9NM(Lrj@V+cDzqyCA&H zy!3WJ-%laY$P7GKYn~92()a}VX^iAl_-B?XqysNYP}D#?L9*>o0$W4HQyMm`%-R6Cg7%n*&;+Pw~ zF%&KRN*y71u?oL=R@o?9)Hk05NY62yZ*s$wWY`IX(-{!BHN!11oYi?{+1BsSQQ!R9 zEZ?W#j&I+E7lq-rxMX-G2Ry}lCZ>N;&7P`LYNVDiPpHGMyeUmEZRP(hsr}S{uRuoq zMMIN{9D*~9ZSY#y)EXM}ZD*r{X<$0`odzfFz^xV@hB(`RmBF{F%is^{FOEWRA)3?k zZ%T{@#>)bFzEha9XwQCtXTO*iOF29Zu)xr7w4d_c(r3DMn;K+J0Q+fNssv+*k((6Q z4*f^R`^2w_5-b!i8dvuDk?Y&XV@0^hcbO&cJ{wr8^*F7O6ckf^J3tOgf5j1~Q!9cRr| zj1~_nQgUjyA+dg9hSv3>4Q%lC6>LV(aHSPzWOwAXF^b0hJuL1Fy$5Z3AT*`p!Edk95PxkF>kQ5^XTH*YCJZ}g-ylguYGNVRWfZw*?Al9@#?<@TNAJr zo+S!+4B=9NWh8nnOBwC@4J>rHl~aw9d@I(SNCgF+7m!`gH1Gy-o^JAlSo83;y7t;D z_`g@Q1Ug#1>u#YFVmqYeK4fQY)vgx(O}8#y#YKJ)=IGArV;=mVHhy`pcoBR|D&3)0 zr&$r?kk~ySQz(ZnJDIzDfeFpZ*v)-RAm(BmfmYB>Sak5S#$jsJSEb7XvFE7+=!$vUWG4Nv^WJaKeJSnwpp~;uW5gLO2$zoLEXC$kh;|~e4xX< zE}@u1s{P%So9l(cv8t4Stx=c27A`KPUAR4x4d#)K#Etk=(lcS{PwPG-{J|nHqLVaj z_i1Z>pGUg(Nm1b#lk?U}d zxW?p0Mcadj99Y^1829nbzUI1f3GpPEacNmWXh<1lkWg4RHTk*DT>Ivb@u=?jW$D6RCddMIJ|F81q7>1!y9vN1kesBMHmEY%Mkm)Tyi_e08G%9a!wBl@%Yj0I+jy-2G z&0w@;U73M43a{&-OW#6&USo z5adjl<&CAk7CVY8r?5wm4Y=1Mv$*w83uxR_%s2=N^~q%&0+k{i*!12M8wDO_S`UrT zT!YobA=WTl&#Ut9si^tSv|>oUIYKqwrX!|{dLBX^240!l5hlKOz8)(0CpS38=1a9$4I7pr-ONg1#RrS&zU6! zR1l>HaBg59G^!c<;SGHGP+43F*Z35`_4WrDrC}J4(e#F1R^mF*WF~l5v+(-e%Tw4B zI8aZK8wJXiKgM_8k@UXL7N;PJ`U*~KrZmyDoEtc<6m$ODQW?ASJXi`!0wKB*SzNHv zKuLV`pW+f9-Aiu+|D9+}!)}O2Gvh(trgX|v%6V2+f!=f$g+(gHYHO0-!lh!bE-cC= zf!tE?$!k$BH5S9d{SH2U+Uch25Cgds*yUSl`V%e_qM{oS9kPc8MgbBqbFzjL3FgJm zNG>XqGF79?fnH!vb%qDoj_RE0*C+-JNY;XxPe14&lV!uH=9SE!Nu|tew&c{M*XW07 zAw2~eJRE%sq*BZV z;Y9|;Y@wzjeyx#-N~JrMMEx<;Wh3R!|? z8_wxX3ZJ~Ik(yj&OAaud->o}qTK-|(b~oZ|oq!7XMSS!a5W&s@U^M>xOhE3+(p>Al zYidRG5*vSTn9hZp2p8tNb)h@SF=wJTzsn{}(apR(K!2%Zr5;Onk`XA}2I@;;j;+4A?kS zm&`iOADGpqB*gvxhJ7+6b>B7E=_J>A=D(3j^3TG%F8{Q$vAP^TOSOAoQBK{gTmVi7 zvJaV<0_2pOp~5NuxG(odoJByC^N$rWWByHZ3)Yquy*VAU|F;k0La+ + + + + + + + + + + + diff --git a/resources/images/stores/walmart.png b/resources/images/stores/walmart.png new file mode 100644 index 0000000000000000000000000000000000000000..c46a269cfdc917172982ce8a1beac4bc9986c8e7 GIT binary patch literal 3908 zcmV-K54-S*P)QW^kMT&|8sPni$jcb2X z5>lk77=Rkrp5?Eo!5K15lTS26BPAGS3}j;VSfVm`%Yedj&Dz~0w-8C-TPYAn&z2T zwbp?P)at%xcvr21ovfPBh4xp5jXwr-(_*)zoS!Jt*o)5tTG$*W2N*NKJeDl1ob&4F;TgZ+wX&K3eyHpg)T7^{=_?%fylnrqgX!<+1$xjXM=k6;NHSe0lM?R&6Nga0Ohz;Pa{@9-!93J+%`3dsj42 zU*xZ5W<;#An&*?1R}54uP?q?r4Om{_g4{<#g92)s2I@fxAYj`C`@_gf2Ei3o{Qb27hKy8o&LprG5s_S~L z1m7?td~l5YeLc^IYyaV>Pcbbj0^3Cb0j{!9Ss#}Js=$IgDRDMjbA+SzKB9oCfGTJ4 zEx8=P_qHQoM70kfsNqK2y=6?B0&N+w&c;F$zy)-o7^J~lfcF-MAYF-mTM=9EZ<$+M z?JxCYJ>#BzjNiR4Fh*U^-y<8>u~qK+v&jA#_qGwE0g|9U zHva?fI4Qq5bITDQDwIi)+z)1|HAJ=FqszqeWKq}kRN(bgE4Pb83nie>ciVx#Ww6c_ zSX~DQX2^q#d)Rwk3}|43{b!tK@6QT!z>kI>Pd5$z3Y^vU0`KQ+0~ch08a4-Ut~=6d z8*wZAe~q9|>!45hF`^aKpHIEpHi1@BPpsp16i##^wgozg9+r^Pr*QpCfe8t|9}i{E zzIS8JH4F2&Ob+v{Sodq3JwE^W4mt+Mm+41 zCrSVm4PMU!K>N6R3aVCo%kON?0DpjSeKlEu*{+_4Ieb3gJa^qd_%h(Y38YX! z)ssJR+@G@Kjl@r_n{PV?R5)=dM>~1KqnuFg;~W7MwH}ccag2eQvA*``1|WGtXn~Cf zhq>ktX>Wog^S$KR&=<(k2nbZ+oLPO!2z_LT-|#JFF`uSkHJ0Te_q#e??)a6Zlf z)MkEUkR;!H1JcJT4jE0u%72tNx zGq;n;qqXrUqxzz1H=i0TjGM{oQfgdGj4%=7SL}WXv;*%zWwqUsYA|PS?Q#&KF-kY~ zq`#YPz6P4a6zKc6;NO>1Ll1yCZ2nv@F=P9?Bv5xj+k89$HOuf5)9wRsB}v`hXh}cs zlR&6{svueuOa`Oy8HI3Dl<}={IjUATc?lZ`jlEL57IOk0dgrXlk4Oy^3Y5RX^}20X zs!AV5oK;eh%V=$}UUf=U=M?;|U@{e1`Ez6S#n8ord0Z-g9p__6*-9dwAI4OxoA$z8M5V{C5YE%B+Guu-8XU27NGk;GGmg1wETRDTIr#Ceo ze5|rjQvLvSW8U^4+fOZshlYKsI7nbO*T+3PagrxFMH;^%c&BL&fVwkKX7)Lj0T1nq z_$%C!oWZ?LXe}Jy6yorSrLQS7gPdzNNcVSq(*@LeN*wwaATS4hRza)duY9nN^&6Rb|VfvPx8mNXB!O6(RgX37JV!eeBg(Dih+8-HakiI)n~#i7oIR%LcwtF!m|G5hR3m@R9YL9NVp$! z;Qo+3V9_{3th)d6`wIb7-FV}81S*zj0oTk3aHW_KZ(DhIk@LtL-^m1wscBEYa6@%+(9JCz>H_&eg zK-A6W`5}k0Km}jzaRk&IhY2v& z-UtQ&O>Jvt4EVAH9H?mb@E@Q9QWX}C zNT!~xwibsvx_%x_b6X?%tRtW%zXEFUl^(%LO!_Q0#5{(Or|?&24$#8MIP-V;{it;1 zn&7#R+AL5>ik&#tmOm*s9~Gd|#E)iIE;(7Ski_wl8&Y|w&JxF8?|8cB=etWBuOZ3q z(03Q=FA3V_q;yFpmeZhyKJwOBa6{w|(mx1%msbgZF{WJsRW~0Ktf)5&zVi_8R$ob= z(th&dp4aCkY`9Z#S2`?z@*%XFgnY3SOH`YTB|?csHyRpmTZ$!Ka;u9{EKSZW&-ig= zQ^$lQPlyV-LlUUhFg~nc075)5b2WuPZegVauynRPpq4uh-pAyHu=$~2BAizMpssNp zp=G~ET;>cI!MWDrF6Y@#09vW5;$~1S0?UdzEVveP_SV#5D^pQnkp$=^axTFt$%jL^WT#E={7Y?5^(AR+mLOktS~{DCR+JdWHzF|r=pRt$+B~z7 zWKIz`8@SsX#4<}&aA>i>LX9E^d|Bl}XUX3W{#0Meh4O{ESOTc!5_~300CfTLUh2l# zCAG54JM&*|b3mm&)Ga>CX^HPZ4JYq)7IYzhIGAJs?x@qlqu`!}jOvB+9r2(Axa*u0 z;I>xvJ=v#*RM-fNjK3oycJ_%oAje%YM{Szs`VYH zR8!j_-8Z~m8c@OM1v%0Js|4ort9Q&t=17Zpu#)a^yI+R?hG$PjLzA63{UjJ*ra+MU z_^Dla1NFD!kCNyowic2o8YhFVl2iv^PcCMg1S;+09EWn6L6RxhQC#bM2P&1t_oPl& zz~P@jVrGz+GX}-hHVaf*Q+O&rUOU}Bq8UFaxzj!C;k>Mv z^xu61YR2|pzp`Scl%wb%ICwa7sFPpEE*@ZjYM&>bchp#A zy;y&VRZJTFh_pa=6*-~6n8{5>CB*e2C{>`@EoO|tZO0m2R z?_OS68Z@k7iW?S`qZ94>sR`)?GO1wjL4F@ENu7K^zcFq(T8ue(J+BWk+aI<}I;19u zh2%sA8iU-J%88yq!0a+Hp15ip9tOqaQ|NU*Ar$6^E&DNqsAhZQMObo7844SkJ&ZcT82l8_=r#Q@ZJXt2PNe20>dB1OdjRER(loCG&QNl1~R zq5x{}Awe+xR3#xrii%>73RyM$Sm7B;LW&d>#fmCCF}zAiNRguASW({;#SJS7DN{ + return elem.id == store_id + }).url.replace("product_id",product_key); + else + return null + }, +} From d44b127f374aa47f4383b2235c76fb23ef90e466 Mon Sep 17 00:00:00 2001 From: YNWA Fawzy <38886749+Cybrarist@users.noreply.github.com> Date: Fri, 23 Aug 2024 10:30:33 +0400 Subject: [PATCH 2/4] V2.0 --- amazon.js | 189 -- resources/apexcharts.js | 4300 +++++++++++++++--------------- resources/images/stores/noon.svg | 40 +- 3 files changed, 2170 insertions(+), 2359 deletions(-) delete mode 100644 amazon.js diff --git a/amazon.js b/amazon.js deleted file mode 100644 index 1b69797..0000000 --- a/amazon.js +++ /dev/null @@ -1,189 +0,0 @@ -// Functions 3|czqWp37WoxCkdqMXKVbOLM98nbckc94SG1qM2XXI3a62f82f -function show_form_discount_bandit(){ - document.body.querySelector(".gray_layout").style.display="flex" -} - -//Variables -let product_name = document.getElementById("productTitle").textContent.trim() - - -//Populate Elements on the page - -//Chart -let header = document.createElement("div"); -header.setAttribute("id" , "chart") -document.body.querySelector("#ppd").after(header) - -//Button To Show Form To Add / Edit -let add_to_system= document.createElement("img") -add_to_system.setAttribute( "src" , "https://raw.githubusercontent.com/Cybrarist/Discount-Bandit/master/storage/app/public/bandit.png") -add_to_system.setAttribute( "id" , "discount_bandit_show") -add_to_system.style.maxWidth= "50px" -add_to_system.addEventListener("click", function (){ - show_form_discount_bandit() -}) -document.body.querySelector("#productTitle").after(add_to_system) - -//background layout -let gray_layout = document.createElement("div") -gray_layout.setAttribute("class" , "gray_layout") - -let messages=` -

-
- -
- Error Has Happened, please check the logs -
` - -document.body.insertAdjacentHTML("beforebegin" , messages) - - - -let form_string=` - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- -
-` -gray_layout.innerHTML = form_string -gray_layout.addEventListener("click" , function (e){ - if (e.target === this) - this.style.display="none" - -}) -document.body.prepend(gray_layout) - - - -var currentURL = window.location.href; - -//Get the current product pricing chart -browser.storage.sync.get().then(function (result) { - fetch(`${result.url}/api/products/get-product` ,{ - method: 'POST', - headers:{ - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Authorization': `Bearer ${result.token}`, - }, - body:JSON.stringify({ - url : currentURL - }) - }) - .then(response => { - return response.json(); - }) - .then(data => { - - var options = { - chart:data.chart, - series: data.series, - xaxis: data.xaxis, - stroke : data.stroke, - dataLabels: data.dataLabels, - legend: { - position: 'top' - } - } - - var chart = new ApexCharts(document.querySelector("#chart"), options); - chart.render(); - - document.getElementById("corePrice_feature_div").insertAdjacentHTML("afterbegin" , - ` -
Lowest Price ${data.prices.price_min / 100}
-
Lowest Price ${data.prices.price_max / 100}
- -` - ) - - }) - .catch(error => { - // Handle errors - console.log("Error:", error); - }); - - -}, function (error){ - console.log(`Error: ${error}`); -}) - - - - -//events -document.querySelector(".submit_amazon").addEventListener("click" , function (){ - browser.storage.sync.get().then(function (result) { - document.getElementsByClassName("success-message")[0].style.display="none" - document.getElementsByClassName("danger-message")[0].style.display="none" - document.getElementsByClassName("gray_layout")[0].style.display="none" - fetch(`${result.url}/api/products/create/amazon`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Authorization': `Bearer ${result.token}`, - }, - body: JSON.stringify({ - url: currentURL , - product_name: document.getElementById("product_name").value, - product_image: document.getElementById("landingImage").src, - notify_price: document.getElementById("notify_price").value, - official_seller: document.getElementById("official_seller").checked, - favourite: document.getElementById("favourite").checked, - stock_available: document.getElementById("stock_available").checked, - lowest_within: document.getElementById("lowest_within").value, - number_of_rates: document.getElementById("acrCustomerReviewText") - .textContent.split(" ")[0] - .replaceAll(",","") - .replaceAll("." , ""), - }) - }) - .then(response => { - return response.json(); - }) - .then(data => { - - if (data.errors) - document.getElementsByClassName("danger-message")[0].style.display="flex" - else{ - - document.getElementsByClassName("success-message")[0].innerHTML= - `

${ data.message}

You can check it from the following link

${data.link}

` - document.getElementsByClassName("success-message")[0].style.display="flex" - } - - }) - .catch(error => { - document.getElementsByClassName("danger-message")[0].style.display="flex" - }); - }) - -}) - - diff --git a/resources/apexcharts.js b/resources/apexcharts.js index 5e45844..1bccf37 100644 --- a/resources/apexcharts.js +++ b/resources/apexcharts.js @@ -29970,2165 +29970,2165 @@ return SVG; }); - /*! svg.filter.js - v2.0.2 - 2016-02-24 - * https://github.com/wout/svg.filter.js - * Copyright (c) 2016 Wout Fierens; Licensed MIT */ - (function() { - - // Main filter class - SVG.Filter = SVG.invent({ - create: 'filter', - inherit: SVG.Parent, - extend: { - // Static strings - source: 'SourceGraphic', - sourceAlpha: 'SourceAlpha', - background: 'BackgroundImage', - backgroundAlpha: 'BackgroundAlpha', - fill: 'FillPaint', - stroke: 'StrokePaint', - - autoSetIn: true, - // Custom put method for leaner code - put: function(element, i) { - this.add(element, i); - - if(!element.attr('in') && this.autoSetIn){ - element.attr('in',this.source); - } - if(!element.attr('result')){ - element.attr('result',element); - } - - return element - }, - // Blend effect - blend: function(in1, in2, mode) { - return this.put(new SVG.BlendEffect(in1, in2, mode)) - }, - // ColorMatrix effect - colorMatrix: function(type, values) { - return this.put(new SVG.ColorMatrixEffect(type, values)) - }, - // ConvolveMatrix effect - convolveMatrix: function(matrix) { - return this.put(new SVG.ConvolveMatrixEffect(matrix)) - }, - // ComponentTransfer effect - componentTransfer: function(components) { - return this.put(new SVG.ComponentTransferEffect(components)) - }, - // Composite effect - composite: function(in1, in2, operator) { - return this.put(new SVG.CompositeEffect(in1, in2, operator)) - }, - // Flood effect - flood: function(color, opacity) { - return this.put(new SVG.FloodEffect(color, opacity)) - }, - // Offset effect - offset: function(x, y) { - return this.put(new SVG.OffsetEffect(x,y)) - }, - // Image effect - image: function(src) { - return this.put(new SVG.ImageEffect(src)) - }, - // Merge effect - merge: function() { - //pass the array of arguments to the constructor because we dont know if the user gave us an array as the first arguemnt or wether they listed the effects in the arguments - var args = [undefined]; - for(var i in arguments) args.push(arguments[i]); - return this.put(new (SVG.MergeEffect.bind.apply(SVG.MergeEffect,args))) - }, - // Gaussian Blur effect - gaussianBlur: function(x,y) { - return this.put(new SVG.GaussianBlurEffect(x,y)) - }, - // Morphology effect - morphology: function(operator,radius){ - return this.put(new SVG.MorphologyEffect(operator,radius)) - }, - // DiffuseLighting effect - diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){ - return this.put(new SVG.DiffuseLightingEffect(surfaceScale,diffuseConstant,kernelUnitLength)) - }, - // DisplacementMap effect - displacementMap: function(in1,in2,scale,xChannelSelector,yChannelSelector){ - return this.put(new SVG.DisplacementMapEffect(in1,in2,scale,xChannelSelector,yChannelSelector)) - }, - // SpecularLighting effect - specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){ - return this.put(new SVG.SpecularLightingEffect(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength)) - }, - // Tile effect - tile: function(){ - return this.put(new SVG.TileEffect()); - }, - // Turbulence effect - turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){ - return this.put(new SVG.TurbulenceEffect(baseFrequency,numOctaves,seed,stitchTiles,type)) - }, - // Default string value - toString: function() { - return 'url(#' + this.attr('id') + ')' - } - } - }); - - //add .filter function - SVG.extend(SVG.Defs, { - // Define filter - filter: function(block) { - var filter = this.put(new SVG.Filter); - - /* invoke passed block */ - if (typeof block === 'function') - block.call(filter, filter); - - return filter - } - }); - SVG.extend(SVG.Container, { - // Define filter on defs - filter: function(block) { - return this.defs().filter(block) - } - }); - SVG.extend(SVG.Element, SVG.G, SVG.Nested, { - // Create filter element in defs and store reference - filter: function(block) { - this.filterer = block instanceof SVG.Element ? - block : this.doc().filter(block); - - if(this.doc() && this.filterer.doc() !== this.doc()){ - this.doc().defs().add(this.filterer); - } - - this.attr('filter', this.filterer); - - return this.filterer - }, - // Remove filter - unfilter: function(remove) { - /* also remove the filter node */ - if (this.filterer && remove === true) - this.filterer.remove(); - - /* delete reference to filterer */ - delete this.filterer; - - /* remove filter attribute */ - return this.attr('filter', null) - } - }); - - // Create SVG.Effect class - SVG.Effect = SVG.invent({ - create: function(){ - this.constructor.call(this); - }, - inherit: SVG.Element, - extend: { - // Set in attribute - in: function(effect) { - return effect == null? this.parent() && this.parent().select('[result="'+this.attr('in')+'"]').get(0) || this.attr('in') : this.attr('in', effect) - }, - // Named result - result: function(result) { - return result == null? this.attr('result') : this.attr('result',result) - }, - // Stringification - toString: function() { - return this.result() - } - } - }); - - // create class for parent effects like merge - // Inherit from SVG.Parent - SVG.ParentEffect = SVG.invent({ - create: function(){ - this.constructor.call(this); - }, - inherit: SVG.Parent, - extend: { - // Set in attribute - in: function(effect) { - return effect == null? this.parent() && this.parent().select('[result="'+this.attr('in')+'"]').get(0) || this.attr('in') : this.attr('in', effect) - }, - // Named result - result: function(result) { - return result == null? this.attr('result') : this.attr('result',result) - }, - // Stringification - toString: function() { - return this.result() - } - } - }); - - //chaining - var chainingEffects = { - // Blend effect - blend: function(in2, mode) { - return this.parent() && this.parent().blend(this, in2, mode) //pass this as the first input - }, - // ColorMatrix effect - colorMatrix: function(type, values) { - return this.parent() && this.parent().colorMatrix(type, values).in(this) - }, - // ConvolveMatrix effect - convolveMatrix: function(matrix) { - return this.parent() && this.parent().convolveMatrix(matrix).in(this) - }, - // ComponentTransfer effect - componentTransfer: function(components) { - return this.parent() && this.parent().componentTransfer(components).in(this) - }, - // Composite effect - composite: function(in2, operator) { - return this.parent() && this.parent().composite(this, in2, operator) //pass this as the first input - }, - // Flood effect - flood: function(color, opacity) { - return this.parent() && this.parent().flood(color, opacity) //this effect dont have inputs - }, - // Offset effect - offset: function(x, y) { - return this.parent() && this.parent().offset(x,y).in(this) - }, - // Image effect - image: function(src) { - return this.parent() && this.parent().image(src) //this effect dont have inputs - }, - // Merge effect - merge: function() { - return this.parent() && this.parent().merge.apply(this.parent(),[this].concat(arguments)) //pass this as the first argument - }, - // Gaussian Blur effect - gaussianBlur: function(x,y) { - return this.parent() && this.parent().gaussianBlur(x,y).in(this) - }, - // Morphology effect - morphology: function(operator,radius){ - return this.parent() && this.parent().morphology(operator,radius).in(this) - }, - // DiffuseLighting effect - diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){ - return this.parent() && this.parent().diffuseLighting(surfaceScale,diffuseConstant,kernelUnitLength).in(this) - }, - // DisplacementMap effect - displacementMap: function(in2,scale,xChannelSelector,yChannelSelector){ - return this.parent() && this.parent().displacementMap(this,in2,scale,xChannelSelector,yChannelSelector) //pass this as the first input - }, - // SpecularLighting effect - specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){ - return this.parent() && this.parent().specularLighting(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength).in(this) - }, - // Tile effect - tile: function(){ - return this.parent() && this.parent().tile().in(this) - }, - // Turbulence effect - turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){ - return this.parent() && this.parent().turbulence(baseFrequency,numOctaves,seed,stitchTiles,type).in(this) - } - }; - SVG.extend(SVG.Effect,chainingEffects); - SVG.extend(SVG.ParentEffect,chainingEffects); - - //crea class for child effects, like MergeNode, FuncR and lights - SVG.ChildEffect = SVG.invent({ - create: function(){ - this.constructor.call(this); - }, - inherit: SVG.Element, - extend: { - in: function(effect){ - this.attr('in',effect); - } - //dont include any "result" functions because these types of nodes dont have them - } - }); - - // Create all different effects - var effects = { - blend: function(in1,in2,mode){ - this.attr({ - in: in1, - in2: in2, - mode: mode || 'normal' - }); - }, - colorMatrix: function(type,values){ - if (type == 'matrix') - values = normaliseMatrix(values); - - this.attr({ - type: type - , values: typeof values == 'undefined' ? null : values - }); - }, - convolveMatrix: function(matrix){ - matrix = normaliseMatrix(matrix); - - this.attr({ - order: Math.sqrt(matrix.split(' ').length) - , kernelMatrix: matrix - }); - }, - composite: function(in1, in2, operator){ - this.attr({ - in: in1, - in2: in2, - operator: operator - }); - }, - flood: function(color,opacity){ - this.attr('flood-color',color); - if(opacity != null) this.attr('flood-opacity',opacity); - }, - offset: function(x,y){ - this.attr({ - dx: x, - dy: y - }); - }, - image: function(src){ - this.attr('href', src, SVG.xlink); - }, - displacementMap: function(in1,in2,scale,xChannelSelector,yChannelSelector){ - this.attr({ - in: in1, - in2: in2, - scale: scale, - xChannelSelector: xChannelSelector, - yChannelSelector: yChannelSelector - }); - }, - gaussianBlur: function(x,y){ - if(x != null || y != null) - this.attr('stdDeviation', listString(Array.prototype.slice.call(arguments))); - else - this.attr('stdDeviation', '0 0'); - }, - morphology: function(operator,radius){ - this.attr({ - operator: operator, - radius: radius - }); - }, - tile: function(){ - - }, - turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){ - this.attr({ - numOctaves: numOctaves, - seed: seed, - stitchTiles: stitchTiles, - baseFrequency: baseFrequency, - type: type - }); - } - }; - - // Create all parent effects - var parentEffects = { - merge: function(){ - var children; - - //test to see if we have a set - if(arguments[0] instanceof SVG.Set){ - var that = this; - arguments[0].each(function(i){ - if(this instanceof SVG.MergeNode) - that.put(this); - else if(this instanceof SVG.Effect || this instanceof SVG.ParentEffect) - that.put(new SVG.MergeNode(this)); - }); - } - else { - //if the first argument is an array use it - if(Array.isArray(arguments[0])) - children = arguments[0]; - else - children = arguments; - - for(var i = 0; i < children.length; i++){ - if(children[i] instanceof SVG.MergeNode){ - this.put(children[i]); - } - else this.put(new SVG.MergeNode(children[i])); - } - } - }, - componentTransfer: function(compontents){ - /* create rgb set */ - this.rgb = new SVG.Set - - /* create components */ - ;(['r', 'g', 'b', 'a']).forEach(function(c) { - /* create component */ - this[c] = new SVG['Func' + c.toUpperCase()]('identity'); - - /* store component in set */ - this.rgb.add(this[c]); - - /* add component node */ - this.node.appendChild(this[c].node); - }.bind(this)); //lost context in foreach - - /* set components */ - if (compontents) { - if (compontents.rgb) { - (['r', 'g', 'b']).forEach(function(c) { - this[c].attr(compontents.rgb); - }.bind(this)); - - delete compontents.rgb; - } - - /* set individual components */ - for (var c in compontents) - this[c].attr(compontents[c]); - } - }, - diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){ - this.attr({ - surfaceScale: surfaceScale, - diffuseConstant: diffuseConstant, - kernelUnitLength: kernelUnitLength - }); - }, - specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){ - this.attr({ - surfaceScale: surfaceScale, - diffuseConstant: diffuseConstant, - specularExponent: specularExponent, - kernelUnitLength: kernelUnitLength - }); - }, - }; - - // Create child effects like PointLight and MergeNode - var childEffects = { - distantLight: function(azimuth, elevation){ - this.attr({ - azimuth: azimuth, - elevation: elevation - }); - }, - pointLight: function(x,y,z){ - this.attr({ - x: x, - y: y, - z: z - }); - }, - spotLight: function(x,y,z,pointsAtX,pointsAtY,pointsAtZ){ - this.attr({ - x: x, - y: y, - z: z, - pointsAtX: pointsAtX, - pointsAtY: pointsAtY, - pointsAtZ: pointsAtZ - }); - }, - mergeNode: function(in1){ - this.attr('in',in1); - } - } - - // Create compontent functions - ;(['r', 'g', 'b', 'a']).forEach(function(c) { - /* create class */ - childEffects['Func' + c.toUpperCase()] = function(type) { - this.attr('type',type); - - // take diffent arguments based on the type - switch(type){ - case 'table': - this.attr('tableValues',arguments[1]); - break - case 'linear': - this.attr('slope',arguments[1]); - this.attr('intercept',arguments[2]); - break - case 'gamma': - this.attr('amplitude',arguments[1]); - this.attr('exponent',arguments[2]); - this.attr('offset',arguments[2]); - break - } - }; - }); - - //create effects - foreach(effects,function(effect,i){ - - /* capitalize name */ - var name = i.charAt(0).toUpperCase() + i.slice(1); - var proto = {}; - - /* create class */ - SVG[name + 'Effect'] = SVG.invent({ - create: function() { - //call super - this.constructor.call(this, SVG.create('fe' + name)); - - //call constructor for this effect - effect.apply(this,arguments); - - //set the result - this.result(this.attr('id') + 'Out'); - }, - inherit: SVG.Effect, - extend: proto - }); - }); - - //create parent effects - foreach(parentEffects,function(effect,i){ - - /* capitalize name */ - var name = i.charAt(0).toUpperCase() + i.slice(1); - var proto = {}; - - /* create class */ - SVG[name + 'Effect'] = SVG.invent({ - create: function() { - //call super - this.constructor.call(this, SVG.create('fe' + name)); - - //call constructor for this effect - effect.apply(this,arguments); - - //set the result - this.result(this.attr('id') + 'Out'); - }, - inherit: SVG.ParentEffect, - extend: proto - }); - }); - - //create child effects - foreach(childEffects,function(effect,i){ - - /* capitalize name */ - var name = i.charAt(0).toUpperCase() + i.slice(1); - var proto = {}; - - /* create class */ - SVG[name] = SVG.invent({ - create: function() { - //call super - this.constructor.call(this, SVG.create('fe' + name)); - - //call constructor for this effect - effect.apply(this,arguments); - }, - inherit: SVG.ChildEffect, - extend: proto - }); - }); - - // Effect-specific extensions - SVG.extend(SVG.MergeEffect,{ - in: function(effect){ - if(effect instanceof SVG.MergeNode) - this.add(effect,0); - else - this.add(new SVG.MergeNode(effect),0); - - return this - } - }); - SVG.extend(SVG.CompositeEffect,SVG.BlendEffect,SVG.DisplacementMapEffect,{ - in2: function(effect){ - return effect == null? this.parent() && this.parent().select('[result="'+this.attr('in2')+'"]').get(0) || this.attr('in2') : this.attr('in2', effect) - } - }); - - // Presets - SVG.filter = { - sepiatone: [ .343, .669, .119, 0, 0 - , .249, .626, .130, 0, 0 - , .172, .334, .111, 0, 0 - , .000, .000, .000, 1, 0 ] - }; - - // Helpers - function normaliseMatrix(matrix) { - /* convert possible array value to string */ - if (Array.isArray(matrix)) - matrix = new SVG.Array(matrix); - - /* ensure there are no leading, tailing or double spaces */ - return matrix.toString().replace(/^\s+/, '').replace(/\s+$/, '').replace(/\s+/g, ' ') - } - - function listString(list) { - if (!Array.isArray(list)) - return list - - for (var i = 0, l = list.length, s = []; i < l; i++) - s.push(list[i]); - - return s.join(' ') - } - - function foreach(){ //loops through mutiple objects - var fn = function(){}; - if(typeof arguments[arguments.length-1] == 'function'){ - fn = arguments[arguments.length-1]; - Array.prototype.splice.call(arguments,arguments.length-1,1); - } - for(var k in arguments){ - for(var i in arguments[k]){ - fn(arguments[k][i],i,arguments[k]); - } - } - } - + /*! svg.filter.js - v2.0.2 - 2016-02-24 + * https://github.com/wout/svg.filter.js + * Copyright (c) 2016 Wout Fierens; Licensed MIT */ + (function() { + + // Main filter class + SVG.Filter = SVG.invent({ + create: 'filter', + inherit: SVG.Parent, + extend: { + // Static strings + source: 'SourceGraphic', + sourceAlpha: 'SourceAlpha', + background: 'BackgroundImage', + backgroundAlpha: 'BackgroundAlpha', + fill: 'FillPaint', + stroke: 'StrokePaint', + + autoSetIn: true, + // Custom put method for leaner code + put: function(element, i) { + this.add(element, i); + + if(!element.attr('in') && this.autoSetIn){ + element.attr('in',this.source); + } + if(!element.attr('result')){ + element.attr('result',element); + } + + return element + }, + // Blend effect + blend: function(in1, in2, mode) { + return this.put(new SVG.BlendEffect(in1, in2, mode)) + }, + // ColorMatrix effect + colorMatrix: function(type, values) { + return this.put(new SVG.ColorMatrixEffect(type, values)) + }, + // ConvolveMatrix effect + convolveMatrix: function(matrix) { + return this.put(new SVG.ConvolveMatrixEffect(matrix)) + }, + // ComponentTransfer effect + componentTransfer: function(components) { + return this.put(new SVG.ComponentTransferEffect(components)) + }, + // Composite effect + composite: function(in1, in2, operator) { + return this.put(new SVG.CompositeEffect(in1, in2, operator)) + }, + // Flood effect + flood: function(color, opacity) { + return this.put(new SVG.FloodEffect(color, opacity)) + }, + // Offset effect + offset: function(x, y) { + return this.put(new SVG.OffsetEffect(x,y)) + }, + // Image effect + image: function(src) { + return this.put(new SVG.ImageEffect(src)) + }, + // Merge effect + merge: function() { + //pass the array of arguments to the constructor because we dont know if the user gave us an array as the first arguemnt or wether they listed the effects in the arguments + var args = [undefined]; + for(var i in arguments) args.push(arguments[i]); + return this.put(new (SVG.MergeEffect.bind.apply(SVG.MergeEffect,args))) + }, + // Gaussian Blur effect + gaussianBlur: function(x,y) { + return this.put(new SVG.GaussianBlurEffect(x,y)) + }, + // Morphology effect + morphology: function(operator,radius){ + return this.put(new SVG.MorphologyEffect(operator,radius)) + }, + // DiffuseLighting effect + diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){ + return this.put(new SVG.DiffuseLightingEffect(surfaceScale,diffuseConstant,kernelUnitLength)) + }, + // DisplacementMap effect + displacementMap: function(in1,in2,scale,xChannelSelector,yChannelSelector){ + return this.put(new SVG.DisplacementMapEffect(in1,in2,scale,xChannelSelector,yChannelSelector)) + }, + // SpecularLighting effect + specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){ + return this.put(new SVG.SpecularLightingEffect(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength)) + }, + // Tile effect + tile: function(){ + return this.put(new SVG.TileEffect()); + }, + // Turbulence effect + turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){ + return this.put(new SVG.TurbulenceEffect(baseFrequency,numOctaves,seed,stitchTiles,type)) + }, + // Default string value + toString: function() { + return 'url(#' + this.attr('id') + ')' + } + } + }); + + //add .filter function + SVG.extend(SVG.Defs, { + // Define filter + filter: function(block) { + var filter = this.put(new SVG.Filter); + + /* invoke passed block */ + if (typeof block === 'function') + block.call(filter, filter); + + return filter + } + }); + SVG.extend(SVG.Container, { + // Define filter on defs + filter: function(block) { + return this.defs().filter(block) + } + }); + SVG.extend(SVG.Element, SVG.G, SVG.Nested, { + // Create filter element in defs and store reference + filter: function(block) { + this.filterer = block instanceof SVG.Element ? + block : this.doc().filter(block); + + if(this.doc() && this.filterer.doc() !== this.doc()){ + this.doc().defs().add(this.filterer); + } + + this.attr('filter', this.filterer); + + return this.filterer + }, + // Remove filter + unfilter: function(remove) { + /* also remove the filter node */ + if (this.filterer && remove === true) + this.filterer.remove(); + + /* delete reference to filterer */ + delete this.filterer; + + /* remove filter attribute */ + return this.attr('filter', null) + } + }); + + // Create SVG.Effect class + SVG.Effect = SVG.invent({ + create: function(){ + this.constructor.call(this); + }, + inherit: SVG.Element, + extend: { + // Set in attribute + in: function(effect) { + return effect == null? this.parent() && this.parent().select('[result="'+this.attr('in')+'"]').get(0) || this.attr('in') : this.attr('in', effect) + }, + // Named result + result: function(result) { + return result == null? this.attr('result') : this.attr('result',result) + }, + // Stringification + toString: function() { + return this.result() + } + } + }); + + // create class for parent effects like merge + // Inherit from SVG.Parent + SVG.ParentEffect = SVG.invent({ + create: function(){ + this.constructor.call(this); + }, + inherit: SVG.Parent, + extend: { + // Set in attribute + in: function(effect) { + return effect == null? this.parent() && this.parent().select('[result="'+this.attr('in')+'"]').get(0) || this.attr('in') : this.attr('in', effect) + }, + // Named result + result: function(result) { + return result == null? this.attr('result') : this.attr('result',result) + }, + // Stringification + toString: function() { + return this.result() + } + } + }); + + //chaining + var chainingEffects = { + // Blend effect + blend: function(in2, mode) { + return this.parent() && this.parent().blend(this, in2, mode) //pass this as the first input + }, + // ColorMatrix effect + colorMatrix: function(type, values) { + return this.parent() && this.parent().colorMatrix(type, values).in(this) + }, + // ConvolveMatrix effect + convolveMatrix: function(matrix) { + return this.parent() && this.parent().convolveMatrix(matrix).in(this) + }, + // ComponentTransfer effect + componentTransfer: function(components) { + return this.parent() && this.parent().componentTransfer(components).in(this) + }, + // Composite effect + composite: function(in2, operator) { + return this.parent() && this.parent().composite(this, in2, operator) //pass this as the first input + }, + // Flood effect + flood: function(color, opacity) { + return this.parent() && this.parent().flood(color, opacity) //this effect dont have inputs + }, + // Offset effect + offset: function(x, y) { + return this.parent() && this.parent().offset(x,y).in(this) + }, + // Image effect + image: function(src) { + return this.parent() && this.parent().image(src) //this effect dont have inputs + }, + // Merge effect + merge: function() { + return this.parent() && this.parent().merge.apply(this.parent(),[this].concat(arguments)) //pass this as the first argument + }, + // Gaussian Blur effect + gaussianBlur: function(x,y) { + return this.parent() && this.parent().gaussianBlur(x,y).in(this) + }, + // Morphology effect + morphology: function(operator,radius){ + return this.parent() && this.parent().morphology(operator,radius).in(this) + }, + // DiffuseLighting effect + diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){ + return this.parent() && this.parent().diffuseLighting(surfaceScale,diffuseConstant,kernelUnitLength).in(this) + }, + // DisplacementMap effect + displacementMap: function(in2,scale,xChannelSelector,yChannelSelector){ + return this.parent() && this.parent().displacementMap(this,in2,scale,xChannelSelector,yChannelSelector) //pass this as the first input + }, + // SpecularLighting effect + specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){ + return this.parent() && this.parent().specularLighting(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength).in(this) + }, + // Tile effect + tile: function(){ + return this.parent() && this.parent().tile().in(this) + }, + // Turbulence effect + turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){ + return this.parent() && this.parent().turbulence(baseFrequency,numOctaves,seed,stitchTiles,type).in(this) + } + }; + SVG.extend(SVG.Effect,chainingEffects); + SVG.extend(SVG.ParentEffect,chainingEffects); + + //crea class for child effects, like MergeNode, FuncR and lights + SVG.ChildEffect = SVG.invent({ + create: function(){ + this.constructor.call(this); + }, + inherit: SVG.Element, + extend: { + in: function(effect){ + this.attr('in',effect); + } + //dont include any "result" functions because these types of nodes dont have them + } + }); + + // Create all different effects + var effects = { + blend: function(in1,in2,mode){ + this.attr({ + in: in1, + in2: in2, + mode: mode || 'normal' + }); + }, + colorMatrix: function(type,values){ + if (type == 'matrix') + values = normaliseMatrix(values); + + this.attr({ + type: type + , values: typeof values == 'undefined' ? null : values + }); + }, + convolveMatrix: function(matrix){ + matrix = normaliseMatrix(matrix); + + this.attr({ + order: Math.sqrt(matrix.split(' ').length) + , kernelMatrix: matrix + }); + }, + composite: function(in1, in2, operator){ + this.attr({ + in: in1, + in2: in2, + operator: operator + }); + }, + flood: function(color,opacity){ + this.attr('flood-color',color); + if(opacity != null) this.attr('flood-opacity',opacity); + }, + offset: function(x,y){ + this.attr({ + dx: x, + dy: y + }); + }, + image: function(src){ + this.attr('href', src, SVG.xlink); + }, + displacementMap: function(in1,in2,scale,xChannelSelector,yChannelSelector){ + this.attr({ + in: in1, + in2: in2, + scale: scale, + xChannelSelector: xChannelSelector, + yChannelSelector: yChannelSelector + }); + }, + gaussianBlur: function(x,y){ + if(x != null || y != null) + this.attr('stdDeviation', listString(Array.prototype.slice.call(arguments))); + else + this.attr('stdDeviation', '0 0'); + }, + morphology: function(operator,radius){ + this.attr({ + operator: operator, + radius: radius + }); + }, + tile: function(){ + + }, + turbulence: function(baseFrequency,numOctaves,seed,stitchTiles,type){ + this.attr({ + numOctaves: numOctaves, + seed: seed, + stitchTiles: stitchTiles, + baseFrequency: baseFrequency, + type: type + }); + } + }; + + // Create all parent effects + var parentEffects = { + merge: function(){ + var children; + + //test to see if we have a set + if(arguments[0] instanceof SVG.Set){ + var that = this; + arguments[0].each(function(i){ + if(this instanceof SVG.MergeNode) + that.put(this); + else if(this instanceof SVG.Effect || this instanceof SVG.ParentEffect) + that.put(new SVG.MergeNode(this)); + }); + } + else { + //if the first argument is an array use it + if(Array.isArray(arguments[0])) + children = arguments[0]; + else + children = arguments; + + for(var i = 0; i < children.length; i++){ + if(children[i] instanceof SVG.MergeNode){ + this.put(children[i]); + } + else this.put(new SVG.MergeNode(children[i])); + } + } + }, + componentTransfer: function(compontents){ + /* create rgb set */ + this.rgb = new SVG.Set + + /* create components */ + ;(['r', 'g', 'b', 'a']).forEach(function(c) { + /* create component */ + this[c] = new SVG['Func' + c.toUpperCase()]('identity'); + + /* store component in set */ + this.rgb.add(this[c]); + + /* add component node */ + this.node.appendChild(this[c].node); + }.bind(this)); //lost context in foreach + + /* set components */ + if (compontents) { + if (compontents.rgb) { + (['r', 'g', 'b']).forEach(function(c) { + this[c].attr(compontents.rgb); + }.bind(this)); + + delete compontents.rgb; + } + + /* set individual components */ + for (var c in compontents) + this[c].attr(compontents[c]); + } + }, + diffuseLighting: function(surfaceScale,diffuseConstant,kernelUnitLength){ + this.attr({ + surfaceScale: surfaceScale, + diffuseConstant: diffuseConstant, + kernelUnitLength: kernelUnitLength + }); + }, + specularLighting: function(surfaceScale,diffuseConstant,specularExponent,kernelUnitLength){ + this.attr({ + surfaceScale: surfaceScale, + diffuseConstant: diffuseConstant, + specularExponent: specularExponent, + kernelUnitLength: kernelUnitLength + }); + }, + }; + + // Create child effects like PointLight and MergeNode + var childEffects = { + distantLight: function(azimuth, elevation){ + this.attr({ + azimuth: azimuth, + elevation: elevation + }); + }, + pointLight: function(x,y,z){ + this.attr({ + x: x, + y: y, + z: z + }); + }, + spotLight: function(x,y,z,pointsAtX,pointsAtY,pointsAtZ){ + this.attr({ + x: x, + y: y, + z: z, + pointsAtX: pointsAtX, + pointsAtY: pointsAtY, + pointsAtZ: pointsAtZ + }); + }, + mergeNode: function(in1){ + this.attr('in',in1); + } + } + + // Create compontent functions + ;(['r', 'g', 'b', 'a']).forEach(function(c) { + /* create class */ + childEffects['Func' + c.toUpperCase()] = function(type) { + this.attr('type',type); + + // take diffent arguments based on the type + switch(type){ + case 'table': + this.attr('tableValues',arguments[1]); + break + case 'linear': + this.attr('slope',arguments[1]); + this.attr('intercept',arguments[2]); + break + case 'gamma': + this.attr('amplitude',arguments[1]); + this.attr('exponent',arguments[2]); + this.attr('offset',arguments[2]); + break + } + }; + }); + + //create effects + foreach(effects,function(effect,i){ + + /* capitalize name */ + var name = i.charAt(0).toUpperCase() + i.slice(1); + var proto = {}; + + /* create class */ + SVG[name + 'Effect'] = SVG.invent({ + create: function() { + //call super + this.constructor.call(this, SVG.create('fe' + name)); + + //call constructor for this effect + effect.apply(this,arguments); + + //set the result + this.result(this.attr('id') + 'Out'); + }, + inherit: SVG.Effect, + extend: proto + }); + }); + + //create parent effects + foreach(parentEffects,function(effect,i){ + + /* capitalize name */ + var name = i.charAt(0).toUpperCase() + i.slice(1); + var proto = {}; + + /* create class */ + SVG[name + 'Effect'] = SVG.invent({ + create: function() { + //call super + this.constructor.call(this, SVG.create('fe' + name)); + + //call constructor for this effect + effect.apply(this,arguments); + + //set the result + this.result(this.attr('id') + 'Out'); + }, + inherit: SVG.ParentEffect, + extend: proto + }); + }); + + //create child effects + foreach(childEffects,function(effect,i){ + + /* capitalize name */ + var name = i.charAt(0).toUpperCase() + i.slice(1); + var proto = {}; + + /* create class */ + SVG[name] = SVG.invent({ + create: function() { + //call super + this.constructor.call(this, SVG.create('fe' + name)); + + //call constructor for this effect + effect.apply(this,arguments); + }, + inherit: SVG.ChildEffect, + extend: proto + }); + }); + + // Effect-specific extensions + SVG.extend(SVG.MergeEffect,{ + in: function(effect){ + if(effect instanceof SVG.MergeNode) + this.add(effect,0); + else + this.add(new SVG.MergeNode(effect),0); + + return this + } + }); + SVG.extend(SVG.CompositeEffect,SVG.BlendEffect,SVG.DisplacementMapEffect,{ + in2: function(effect){ + return effect == null? this.parent() && this.parent().select('[result="'+this.attr('in2')+'"]').get(0) || this.attr('in2') : this.attr('in2', effect) + } + }); + + // Presets + SVG.filter = { + sepiatone: [ .343, .669, .119, 0, 0 + , .249, .626, .130, 0, 0 + , .172, .334, .111, 0, 0 + , .000, .000, .000, 1, 0 ] + }; + + // Helpers + function normaliseMatrix(matrix) { + /* convert possible array value to string */ + if (Array.isArray(matrix)) + matrix = new SVG.Array(matrix); + + /* ensure there are no leading, tailing or double spaces */ + return matrix.toString().replace(/^\s+/, '').replace(/\s+$/, '').replace(/\s+/g, ' ') + } + + function listString(list) { + if (!Array.isArray(list)) + return list + + for (var i = 0, l = list.length, s = []; i < l; i++) + s.push(list[i]); + + return s.join(' ') + } + + function foreach(){ //loops through mutiple objects + var fn = function(){}; + if(typeof arguments[arguments.length-1] == 'function'){ + fn = arguments[arguments.length-1]; + Array.prototype.splice.call(arguments,arguments.length-1,1); + } + for(var k in arguments){ + for(var i in arguments[k]){ + fn(arguments[k][i],i,arguments[k]); + } + } + } + }).call(undefined); - (function() { - - SVG.extend(SVG.PathArray, { - morph: function(array) { - - var startArr = this.value - , destArr = this.parse(array); - - var startOffsetM = 0 - , destOffsetM = 0; - - var startOffsetNextM = false - , destOffsetNextM = false; - - while(true){ - // stop if there is no M anymore - if(startOffsetM === false && destOffsetM === false) break - - // find the next M in path array - startOffsetNextM = findNextM(startArr, startOffsetM === false ? false : startOffsetM+1); - destOffsetNextM = findNextM( destArr, destOffsetM === false ? false : destOffsetM+1); - - // We have to add one M to the startArray - if(startOffsetM === false){ - var bbox = new SVG.PathArray(result.start).bbox(); - - // when the last block had no bounding box we simply take the first M we got - if(bbox.height == 0 || bbox.width == 0){ - startOffsetM = startArr.push(startArr[0]) - 1; - }else { - // we take the middle of the bbox instead when we got one - startOffsetM = startArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1; - } - } - - // We have to add one M to the destArray - if( destOffsetM === false){ - var bbox = new SVG.PathArray(result.dest).bbox(); - - if(bbox.height == 0 || bbox.width == 0){ - destOffsetM = destArr.push(destArr[0]) - 1; - }else { - destOffsetM = destArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1; - } - } - - // handle block from M to next M - var result = handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM); - - // update the arrays to their new values - startArr = startArr.slice(0, startOffsetM).concat(result.start, startOffsetNextM === false ? [] : startArr.slice(startOffsetNextM)); - destArr = destArr.slice(0, destOffsetM).concat(result.dest , destOffsetNextM === false ? [] : destArr.slice( destOffsetNextM)); - - // update offsets - startOffsetM = startOffsetNextM === false ? false : startOffsetM + result.start.length; - destOffsetM = destOffsetNextM === false ? false : destOffsetM + result.dest.length; - - } - - // copy back arrays - this.value = startArr; - this.destination = new SVG.PathArray(); - this.destination.value = destArr; - - return this - } - }); - - - - // sorry for the long declaration - // slices out one block (from M to M) and syncronize it so the types and length match - function handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM, undefined$1){ - - // slice out the block we need - var startArrTemp = startArr.slice(startOffsetM, startOffsetNextM || undefined$1) - , destArrTemp = destArr.slice( destOffsetM, destOffsetNextM || undefined$1); - - var i = 0 - , posStart = {pos:[0,0], start:[0,0]} - , posDest = {pos:[0,0], start:[0,0]}; - - do{ - - // convert shorthand types to long form - startArrTemp[i] = simplyfy.call(posStart, startArrTemp[i]); - destArrTemp[i] = simplyfy.call(posDest , destArrTemp[i]); - - // check if both shape types match - // 2 elliptical arc curve commands ('A'), are considered different if the - // flags (large-arc-flag, sweep-flag) don't match - if(startArrTemp[i][0] != destArrTemp[i][0] || startArrTemp[i][0] == 'M' || - (startArrTemp[i][0] == 'A' && - (startArrTemp[i][4] != destArrTemp[i][4] || startArrTemp[i][5] != destArrTemp[i][5]) - ) - ) { - - // if not, convert shapes to beziere - Array.prototype.splice.apply(startArrTemp, [i, 1].concat(toBeziere.call(posStart, startArrTemp[i]))); - Array.prototype.splice.apply(destArrTemp, [i, 1].concat(toBeziere.call(posDest, destArrTemp[i]))); - - } else { - - // only update positions otherwise - startArrTemp[i] = setPosAndReflection.call(posStart, startArrTemp[i]); - destArrTemp[i] = setPosAndReflection.call(posDest , destArrTemp[i]); - - } - - // we are at the end at both arrays. stop here - if(++i == startArrTemp.length && i == destArrTemp.length) break - - // destArray is longer. Add one element - if(i == startArrTemp.length){ - startArrTemp.push([ - 'C', - posStart.pos[0], - posStart.pos[1], - posStart.pos[0], - posStart.pos[1], - posStart.pos[0], - posStart.pos[1], - ]); - } - - // startArr is longer. Add one element - if(i == destArrTemp.length){ - destArrTemp.push([ - 'C', - posDest.pos[0], - posDest.pos[1], - posDest.pos[0], - posDest.pos[1], - posDest.pos[0], - posDest.pos[1] - ]); - } - - - }while(true) - - // return the updated block - return {start:startArrTemp, dest:destArrTemp} - } - - // converts shorthand types to long form - function simplyfy(val){ - - switch(val[0]){ - case 'z': // shorthand line to start - case 'Z': - val[0] = 'L'; - val[1] = this.start[0]; - val[2] = this.start[1]; - break - case 'H': // shorthand horizontal line - val[0] = 'L'; - val[2] = this.pos[1]; - break - case 'V': // shorthand vertical line - val[0] = 'L'; - val[2] = val[1]; - val[1] = this.pos[0]; - break - case 'T': // shorthand quadratic beziere - val[0] = 'Q'; - val[3] = val[1]; - val[4] = val[2]; - val[1] = this.reflection[1]; - val[2] = this.reflection[0]; - break - case 'S': // shorthand cubic beziere - val[0] = 'C'; - val[6] = val[4]; - val[5] = val[3]; - val[4] = val[2]; - val[3] = val[1]; - val[2] = this.reflection[1]; - val[1] = this.reflection[0]; - break - } - - return val - - } - - // updates reflection point and current position - function setPosAndReflection(val){ - - var len = val.length; - - this.pos = [ val[len-2], val[len-1] ]; - - if('SCQT'.indexOf(val[0]) != -1) - this.reflection = [ 2 * this.pos[0] - val[len-4], 2 * this.pos[1] - val[len-3] ]; - - return val - } - - // converts all types to cubic beziere - function toBeziere(val){ - var retVal = [val]; - - switch(val[0]){ - case 'M': // special handling for M - this.pos = this.start = [val[1], val[2]]; - return retVal - case 'L': - val[5] = val[3] = val[1]; - val[6] = val[4] = val[2]; - val[1] = this.pos[0]; - val[2] = this.pos[1]; - break - case 'Q': - val[6] = val[4]; - val[5] = val[3]; - val[4] = val[4] * 1/3 + val[2] * 2/3; - val[3] = val[3] * 1/3 + val[1] * 2/3; - val[2] = this.pos[1] * 1/3 + val[2] * 2/3; - val[1] = this.pos[0] * 1/3 + val[1] * 2/3; - break - case 'A': - retVal = arcToBeziere(this.pos, val); - val = retVal[0]; - break - } - - val[0] = 'C'; - this.pos = [val[5], val[6]]; - this.reflection = [2 * val[5] - val[3], 2 * val[6] - val[4]]; - - return retVal - - } - - // finds the next position of type M - function findNextM(arr, offset){ - - if(offset === false) return false - - for(var i = offset, len = arr.length;i < len;++i){ - - if(arr[i][0] == 'M') return i - - } - - return false - } - - - - // Convert an arc segment into equivalent cubic Bezier curves - // Depending on the arc, up to 4 curves might be used to represent it since a - // curve gives a good approximation for only a quarter of an ellipse - // The curves are returned as an array of SVG curve commands: - // [ ['C', x1, y1, x2, y2, x, y] ... ] - function arcToBeziere(pos, val) { - // Parameters extraction, handle out-of-range parameters as specified in the SVG spec - // See: https://www.w3.org/TR/SVG11/implnote.html#ArcOutOfRangeParameters - var rx = Math.abs(val[1]), ry = Math.abs(val[2]), xAxisRotation = val[3] % 360 - , largeArcFlag = val[4], sweepFlag = val[5], x = val[6], y = val[7] - , A = new SVG.Point(pos), B = new SVG.Point(x, y) - , primedCoord, lambda, mat, k, c, cSquare, t, O, OA, OB, tetaStart, tetaEnd - , deltaTeta, nbSectors, f, arcSegPoints, angle, sinAngle, cosAngle, pt, i, il - , retVal = [], x1, y1, x2, y2; - - // Ensure radii are non-zero - if(rx === 0 || ry === 0 || (A.x === B.x && A.y === B.y)) { - // treat this arc as a straight line segment - return [['C', A.x, A.y, B.x, B.y, B.x, B.y]] - } - - // Ensure radii are large enough using the algorithm provided in the SVG spec - // See: https://www.w3.org/TR/SVG11/implnote.html#ArcCorrectionOutOfRangeRadii - primedCoord = new SVG.Point((A.x-B.x)/2, (A.y-B.y)/2).transform(new SVG.Matrix().rotate(xAxisRotation)); - lambda = (primedCoord.x * primedCoord.x) / (rx * rx) + (primedCoord.y * primedCoord.y) / (ry * ry); - if(lambda > 1) { - lambda = Math.sqrt(lambda); - rx = lambda*rx; - ry = lambda*ry; - } - - // To simplify calculations, we make the arc part of a unit circle (rayon is 1) instead of an ellipse - mat = new SVG.Matrix().rotate(xAxisRotation).scale(1/rx, 1/ry).rotate(-xAxisRotation); - A = A.transform(mat); - B = B.transform(mat); - - // Calculate the horizontal and vertical distance between the initial and final point of the arc - k = [B.x-A.x, B.y-A.y]; - - // Find the length of the chord formed by A and B - cSquare = k[0]*k[0] + k[1]*k[1]; - c = Math.sqrt(cSquare); - - // Calculate the ratios of the horizontal and vertical distance on the length of the chord - k[0] /= c; - k[1] /= c; - - // Calculate the distance between the circle center and the chord midpoint - // using this formula: t = sqrt(r^2 - c^2 / 4) - // where t is the distance between the cirle center and the chord midpoint, - // r is the rayon of the circle and c is the chord length - // From: http://www.ajdesigner.com/phpcircle/circle_segment_chord_t.php - // Because of the imprecision of floating point numbers, cSquare might end - // up being slightly above 4 which would result in a negative radicand - // To prevent that, a test is made before computing the square root - t = (cSquare < 4) ? Math.sqrt(1 - cSquare/4) : 0; - - // For most situations, there are actually two different ellipses that - // satisfy the constraints imposed by the points A and B, the radii rx and ry, - // and the xAxisRotation - // When the flags largeArcFlag and sweepFlag are equal, it means that the - // second ellipse is used as a solution - // See: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands - if(largeArcFlag === sweepFlag) { - t *= -1; - } - - // Calculate the coordinates of the center of the circle from the midpoint of the chord - // This is done by multiplying the ratios calculated previously by the distance between - // the circle center and the chord midpoint and using these values to go from the midpoint - // to the center of the circle - // The negative of the vertical distance ratio is used to modify the x coordinate while - // the horizontal distance ratio is used to modify the y coordinate - // That is because the center of the circle is perpendicular to the chord and perpendicular - // lines are negative reciprocals - O = new SVG.Point((B.x+A.x)/2 + t*-k[1], (B.y+A.y)/2 + t*k[0]); - // Move the center of the circle at the origin - OA = new SVG.Point(A.x-O.x, A.y-O.y); - OB = new SVG.Point(B.x-O.x, B.y-O.y); - - // Calculate the start and end angle - tetaStart = Math.acos(OA.x/Math.sqrt(OA.x*OA.x + OA.y*OA.y)); - if (OA.y < 0) { - tetaStart *= -1; - } - tetaEnd = Math.acos(OB.x/Math.sqrt(OB.x*OB.x + OB.y*OB.y)); - if (OB.y < 0) { - tetaEnd *= -1; - } - - // If sweep-flag is '1', then the arc will be drawn in a "positive-angle" direction, - // make sure that the end angle is above the start angle - if (sweepFlag && tetaStart > tetaEnd) { - tetaEnd += 2*Math.PI; - } - // If sweep-flag is '0', then the arc will be drawn in a "negative-angle" direction, - // make sure that the end angle is below the start angle - if (!sweepFlag && tetaStart < tetaEnd) { - tetaEnd -= 2*Math.PI; - } - - // Find the number of Bezier curves that are required to represent the arc - // A cubic Bezier curve gives a good enough approximation when representing at most a quarter of a circle - nbSectors = Math.ceil(Math.abs(tetaStart-tetaEnd) * 2/Math.PI); - - // Calculate the coordinates of the points of all the Bezier curves required to represent the arc - // For an in-depth explanation of this part see: http://pomax.github.io/bezierinfo/#circles_cubic - arcSegPoints = []; - angle = tetaStart; - deltaTeta = (tetaEnd-tetaStart)/nbSectors; - f = 4*Math.tan(deltaTeta/4)/3; - for (i = 0; i <= nbSectors; i++) { // The <= is because a Bezier curve have a start and a endpoint - cosAngle = Math.cos(angle); - sinAngle = Math.sin(angle); - - pt = new SVG.Point(O.x+cosAngle, O.y+sinAngle); - arcSegPoints[i] = [new SVG.Point(pt.x+f*sinAngle, pt.y-f*cosAngle), pt, new SVG.Point(pt.x-f*sinAngle, pt.y+f*cosAngle)]; - - angle += deltaTeta; - } - - // Remove the first control point of the first segment point and remove the second control point of the last segment point - // These two control points are not used in the approximation of the arc, that is why they are removed - arcSegPoints[0][0] = arcSegPoints[0][1].clone(); - arcSegPoints[arcSegPoints.length-1][2] = arcSegPoints[arcSegPoints.length-1][1].clone(); - - // Revert the transformation that was applied to make the arc part of a unit circle instead of an ellipse - mat = new SVG.Matrix().rotate(xAxisRotation).scale(rx, ry).rotate(-xAxisRotation); - for (i = 0, il = arcSegPoints.length; i < il; i++) { - arcSegPoints[i][0] = arcSegPoints[i][0].transform(mat); - arcSegPoints[i][1] = arcSegPoints[i][1].transform(mat); - arcSegPoints[i][2] = arcSegPoints[i][2].transform(mat); - } - - - // Convert the segments points to SVG curve commands - for (i = 1, il = arcSegPoints.length; i < il; i++) { - pt = arcSegPoints[i-1][2]; - x1 = pt.x; - y1 = pt.y; - - pt = arcSegPoints[i][0]; - x2 = pt.x; - y2 = pt.y; - - pt = arcSegPoints[i][1]; - x = pt.x; - y = pt.y; - - retVal.push(['C', x1, y1, x2, y2, x, y]); - } - - return retVal - } - }()); + (function() { + + SVG.extend(SVG.PathArray, { + morph: function(array) { + + var startArr = this.value + , destArr = this.parse(array); + + var startOffsetM = 0 + , destOffsetM = 0; + + var startOffsetNextM = false + , destOffsetNextM = false; + + while(true){ + // stop if there is no M anymore + if(startOffsetM === false && destOffsetM === false) break + + // find the next M in path array + startOffsetNextM = findNextM(startArr, startOffsetM === false ? false : startOffsetM+1); + destOffsetNextM = findNextM( destArr, destOffsetM === false ? false : destOffsetM+1); + + // We have to add one M to the startArray + if(startOffsetM === false){ + var bbox = new SVG.PathArray(result.start).bbox(); + + // when the last block had no bounding box we simply take the first M we got + if(bbox.height == 0 || bbox.width == 0){ + startOffsetM = startArr.push(startArr[0]) - 1; + }else { + // we take the middle of the bbox instead when we got one + startOffsetM = startArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1; + } + } + + // We have to add one M to the destArray + if( destOffsetM === false){ + var bbox = new SVG.PathArray(result.dest).bbox(); + + if(bbox.height == 0 || bbox.width == 0){ + destOffsetM = destArr.push(destArr[0]) - 1; + }else { + destOffsetM = destArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1; + } + } + + // handle block from M to next M + var result = handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM); + + // update the arrays to their new values + startArr = startArr.slice(0, startOffsetM).concat(result.start, startOffsetNextM === false ? [] : startArr.slice(startOffsetNextM)); + destArr = destArr.slice(0, destOffsetM).concat(result.dest , destOffsetNextM === false ? [] : destArr.slice( destOffsetNextM)); + + // update offsets + startOffsetM = startOffsetNextM === false ? false : startOffsetM + result.start.length; + destOffsetM = destOffsetNextM === false ? false : destOffsetM + result.dest.length; + + } + + // copy back arrays + this.value = startArr; + this.destination = new SVG.PathArray(); + this.destination.value = destArr; + + return this + } + }); + + + + // sorry for the long declaration + // slices out one block (from M to M) and syncronize it so the types and length match + function handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM, undefined$1){ + + // slice out the block we need + var startArrTemp = startArr.slice(startOffsetM, startOffsetNextM || undefined$1) + , destArrTemp = destArr.slice( destOffsetM, destOffsetNextM || undefined$1); + + var i = 0 + , posStart = {pos:[0,0], start:[0,0]} + , posDest = {pos:[0,0], start:[0,0]}; + + do{ + + // convert shorthand types to long form + startArrTemp[i] = simplyfy.call(posStart, startArrTemp[i]); + destArrTemp[i] = simplyfy.call(posDest , destArrTemp[i]); + + // check if both shape types match + // 2 elliptical arc curve commands ('A'), are considered different if the + // flags (large-arc-flag, sweep-flag) don't match + if(startArrTemp[i][0] != destArrTemp[i][0] || startArrTemp[i][0] == 'M' || + (startArrTemp[i][0] == 'A' && + (startArrTemp[i][4] != destArrTemp[i][4] || startArrTemp[i][5] != destArrTemp[i][5]) + ) + ) { + + // if not, convert shapes to beziere + Array.prototype.splice.apply(startArrTemp, [i, 1].concat(toBeziere.call(posStart, startArrTemp[i]))); + Array.prototype.splice.apply(destArrTemp, [i, 1].concat(toBeziere.call(posDest, destArrTemp[i]))); + + } else { + + // only update positions otherwise + startArrTemp[i] = setPosAndReflection.call(posStart, startArrTemp[i]); + destArrTemp[i] = setPosAndReflection.call(posDest , destArrTemp[i]); + + } + + // we are at the end at both arrays. stop here + if(++i == startArrTemp.length && i == destArrTemp.length) break + + // destArray is longer. Add one element + if(i == startArrTemp.length){ + startArrTemp.push([ + 'C', + posStart.pos[0], + posStart.pos[1], + posStart.pos[0], + posStart.pos[1], + posStart.pos[0], + posStart.pos[1], + ]); + } + + // startArr is longer. Add one element + if(i == destArrTemp.length){ + destArrTemp.push([ + 'C', + posDest.pos[0], + posDest.pos[1], + posDest.pos[0], + posDest.pos[1], + posDest.pos[0], + posDest.pos[1] + ]); + } + + + }while(true) + + // return the updated block + return {start:startArrTemp, dest:destArrTemp} + } + + // converts shorthand types to long form + function simplyfy(val){ + + switch(val[0]){ + case 'z': // shorthand line to start + case 'Z': + val[0] = 'L'; + val[1] = this.start[0]; + val[2] = this.start[1]; + break + case 'H': // shorthand horizontal line + val[0] = 'L'; + val[2] = this.pos[1]; + break + case 'V': // shorthand vertical line + val[0] = 'L'; + val[2] = val[1]; + val[1] = this.pos[0]; + break + case 'T': // shorthand quadratic beziere + val[0] = 'Q'; + val[3] = val[1]; + val[4] = val[2]; + val[1] = this.reflection[1]; + val[2] = this.reflection[0]; + break + case 'S': // shorthand cubic beziere + val[0] = 'C'; + val[6] = val[4]; + val[5] = val[3]; + val[4] = val[2]; + val[3] = val[1]; + val[2] = this.reflection[1]; + val[1] = this.reflection[0]; + break + } + + return val + + } + + // updates reflection point and current position + function setPosAndReflection(val){ + + var len = val.length; + + this.pos = [ val[len-2], val[len-1] ]; + + if('SCQT'.indexOf(val[0]) != -1) + this.reflection = [ 2 * this.pos[0] - val[len-4], 2 * this.pos[1] - val[len-3] ]; + + return val + } + + // converts all types to cubic beziere + function toBeziere(val){ + var retVal = [val]; + + switch(val[0]){ + case 'M': // special handling for M + this.pos = this.start = [val[1], val[2]]; + return retVal + case 'L': + val[5] = val[3] = val[1]; + val[6] = val[4] = val[2]; + val[1] = this.pos[0]; + val[2] = this.pos[1]; + break + case 'Q': + val[6] = val[4]; + val[5] = val[3]; + val[4] = val[4] * 1/3 + val[2] * 2/3; + val[3] = val[3] * 1/3 + val[1] * 2/3; + val[2] = this.pos[1] * 1/3 + val[2] * 2/3; + val[1] = this.pos[0] * 1/3 + val[1] * 2/3; + break + case 'A': + retVal = arcToBeziere(this.pos, val); + val = retVal[0]; + break + } + + val[0] = 'C'; + this.pos = [val[5], val[6]]; + this.reflection = [2 * val[5] - val[3], 2 * val[6] - val[4]]; + + return retVal + + } + + // finds the next position of type M + function findNextM(arr, offset){ + + if(offset === false) return false + + for(var i = offset, len = arr.length;i < len;++i){ + + if(arr[i][0] == 'M') return i + + } + + return false + } + + + + // Convert an arc segment into equivalent cubic Bezier curves + // Depending on the arc, up to 4 curves might be used to represent it since a + // curve gives a good approximation for only a quarter of an ellipse + // The curves are returned as an array of SVG curve commands: + // [ ['C', x1, y1, x2, y2, x, y] ... ] + function arcToBeziere(pos, val) { + // Parameters extraction, handle out-of-range parameters as specified in the SVG spec + // See: https://www.w3.org/TR/SVG11/implnote.html#ArcOutOfRangeParameters + var rx = Math.abs(val[1]), ry = Math.abs(val[2]), xAxisRotation = val[3] % 360 + , largeArcFlag = val[4], sweepFlag = val[5], x = val[6], y = val[7] + , A = new SVG.Point(pos), B = new SVG.Point(x, y) + , primedCoord, lambda, mat, k, c, cSquare, t, O, OA, OB, tetaStart, tetaEnd + , deltaTeta, nbSectors, f, arcSegPoints, angle, sinAngle, cosAngle, pt, i, il + , retVal = [], x1, y1, x2, y2; + + // Ensure radii are non-zero + if(rx === 0 || ry === 0 || (A.x === B.x && A.y === B.y)) { + // treat this arc as a straight line segment + return [['C', A.x, A.y, B.x, B.y, B.x, B.y]] + } + + // Ensure radii are large enough using the algorithm provided in the SVG spec + // See: https://www.w3.org/TR/SVG11/implnote.html#ArcCorrectionOutOfRangeRadii + primedCoord = new SVG.Point((A.x-B.x)/2, (A.y-B.y)/2).transform(new SVG.Matrix().rotate(xAxisRotation)); + lambda = (primedCoord.x * primedCoord.x) / (rx * rx) + (primedCoord.y * primedCoord.y) / (ry * ry); + if(lambda > 1) { + lambda = Math.sqrt(lambda); + rx = lambda*rx; + ry = lambda*ry; + } + + // To simplify calculations, we make the arc part of a unit circle (rayon is 1) instead of an ellipse + mat = new SVG.Matrix().rotate(xAxisRotation).scale(1/rx, 1/ry).rotate(-xAxisRotation); + A = A.transform(mat); + B = B.transform(mat); + + // Calculate the horizontal and vertical distance between the initial and final point of the arc + k = [B.x-A.x, B.y-A.y]; + + // Find the length of the chord formed by A and B + cSquare = k[0]*k[0] + k[1]*k[1]; + c = Math.sqrt(cSquare); + + // Calculate the ratios of the horizontal and vertical distance on the length of the chord + k[0] /= c; + k[1] /= c; + + // Calculate the distance between the circle center and the chord midpoint + // using this formula: t = sqrt(r^2 - c^2 / 4) + // where t is the distance between the cirle center and the chord midpoint, + // r is the rayon of the circle and c is the chord length + // From: http://www.ajdesigner.com/phpcircle/circle_segment_chord_t.php + // Because of the imprecision of floating point numbers, cSquare might end + // up being slightly above 4 which would result in a negative radicand + // To prevent that, a test is made before computing the square root + t = (cSquare < 4) ? Math.sqrt(1 - cSquare/4) : 0; + + // For most situations, there are actually two different ellipses that + // satisfy the constraints imposed by the points A and B, the radii rx and ry, + // and the xAxisRotation + // When the flags largeArcFlag and sweepFlag are equal, it means that the + // second ellipse is used as a solution + // See: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands + if(largeArcFlag === sweepFlag) { + t *= -1; + } + + // Calculate the coordinates of the center of the circle from the midpoint of the chord + // This is done by multiplying the ratios calculated previously by the distance between + // the circle center and the chord midpoint and using these values to go from the midpoint + // to the center of the circle + // The negative of the vertical distance ratio is used to modify the x coordinate while + // the horizontal distance ratio is used to modify the y coordinate + // That is because the center of the circle is perpendicular to the chord and perpendicular + // lines are negative reciprocals + O = new SVG.Point((B.x+A.x)/2 + t*-k[1], (B.y+A.y)/2 + t*k[0]); + // Move the center of the circle at the origin + OA = new SVG.Point(A.x-O.x, A.y-O.y); + OB = new SVG.Point(B.x-O.x, B.y-O.y); + + // Calculate the start and end angle + tetaStart = Math.acos(OA.x/Math.sqrt(OA.x*OA.x + OA.y*OA.y)); + if (OA.y < 0) { + tetaStart *= -1; + } + tetaEnd = Math.acos(OB.x/Math.sqrt(OB.x*OB.x + OB.y*OB.y)); + if (OB.y < 0) { + tetaEnd *= -1; + } + + // If sweep-flag is '1', then the arc will be drawn in a "positive-angle" direction, + // make sure that the end angle is above the start angle + if (sweepFlag && tetaStart > tetaEnd) { + tetaEnd += 2*Math.PI; + } + // If sweep-flag is '0', then the arc will be drawn in a "negative-angle" direction, + // make sure that the end angle is below the start angle + if (!sweepFlag && tetaStart < tetaEnd) { + tetaEnd -= 2*Math.PI; + } + + // Find the number of Bezier curves that are required to represent the arc + // A cubic Bezier curve gives a good enough approximation when representing at most a quarter of a circle + nbSectors = Math.ceil(Math.abs(tetaStart-tetaEnd) * 2/Math.PI); + + // Calculate the coordinates of the points of all the Bezier curves required to represent the arc + // For an in-depth explanation of this part see: http://pomax.github.io/bezierinfo/#circles_cubic + arcSegPoints = []; + angle = tetaStart; + deltaTeta = (tetaEnd-tetaStart)/nbSectors; + f = 4*Math.tan(deltaTeta/4)/3; + for (i = 0; i <= nbSectors; i++) { // The <= is because a Bezier curve have a start and a endpoint + cosAngle = Math.cos(angle); + sinAngle = Math.sin(angle); + + pt = new SVG.Point(O.x+cosAngle, O.y+sinAngle); + arcSegPoints[i] = [new SVG.Point(pt.x+f*sinAngle, pt.y-f*cosAngle), pt, new SVG.Point(pt.x-f*sinAngle, pt.y+f*cosAngle)]; + + angle += deltaTeta; + } + + // Remove the first control point of the first segment point and remove the second control point of the last segment point + // These two control points are not used in the approximation of the arc, that is why they are removed + arcSegPoints[0][0] = arcSegPoints[0][1].clone(); + arcSegPoints[arcSegPoints.length-1][2] = arcSegPoints[arcSegPoints.length-1][1].clone(); + + // Revert the transformation that was applied to make the arc part of a unit circle instead of an ellipse + mat = new SVG.Matrix().rotate(xAxisRotation).scale(rx, ry).rotate(-xAxisRotation); + for (i = 0, il = arcSegPoints.length; i < il; i++) { + arcSegPoints[i][0] = arcSegPoints[i][0].transform(mat); + arcSegPoints[i][1] = arcSegPoints[i][1].transform(mat); + arcSegPoints[i][2] = arcSegPoints[i][2].transform(mat); + } + + + // Convert the segments points to SVG curve commands + for (i = 1, il = arcSegPoints.length; i < il; i++) { + pt = arcSegPoints[i-1][2]; + x1 = pt.x; + y1 = pt.y; + + pt = arcSegPoints[i][0]; + x2 = pt.x; + y2 = pt.y; + + pt = arcSegPoints[i][1]; + x = pt.x; + y = pt.y; + + retVal.push(['C', x1, y1, x2, y2, x, y]); + } + + return retVal + } + }()); + + /*! svg.draggable.js - v2.2.2 - 2019-01-08 + * https://github.com/svgdotjs/svg.draggable.js + * Copyright (c) 2019 Wout Fierens; Licensed MIT */ + (function() { + + // creates handler, saves it + function DragHandler(el){ + el.remember('_draggable', this); + this.el = el; + } + + + // Sets new parameter, starts dragging + DragHandler.prototype.init = function(constraint, val){ + var _this = this; + this.constraint = constraint; + this.value = val; + this.el.on('mousedown.drag', function(e){ _this.start(e); }); + this.el.on('touchstart.drag', function(e){ _this.start(e); }); + }; + + // transforms one point from screen to user coords + DragHandler.prototype.transformPoint = function(event, offset){ + event = event || window.event; + var touches = event.changedTouches && event.changedTouches[0] || event; + this.p.x = touches.clientX - (offset || 0); + this.p.y = touches.clientY; + return this.p.matrixTransform(this.m) + }; + + // gets elements bounding box with special handling of groups, nested and use + DragHandler.prototype.getBBox = function(){ + + var box = this.el.bbox(); + + if(this.el instanceof SVG.Nested) box = this.el.rbox(); + + if (this.el instanceof SVG.G || this.el instanceof SVG.Use || this.el instanceof SVG.Nested) { + box.x = this.el.x(); + box.y = this.el.y(); + } + + return box + }; + + // start dragging + DragHandler.prototype.start = function(e){ + + // check for left button + if(e.type == 'click'|| e.type == 'mousedown' || e.type == 'mousemove'){ + if((e.which || e.buttons) != 1){ + return + } + } + + var _this = this; + + // fire beforedrag event + this.el.fire('beforedrag', { event: e, handler: this }); + if(this.el.event().defaultPrevented) return; + + // prevent browser drag behavior as soon as possible + e.preventDefault(); + + // prevent propagation to a parent that might also have dragging enabled + e.stopPropagation(); + + // search for parent on the fly to make sure we can call + // draggable() even when element is not in the dom currently + this.parent = this.parent || this.el.parent(SVG.Nested) || this.el.parent(SVG.Doc); + this.p = this.parent.node.createSVGPoint(); + + // save current transformation matrix + this.m = this.el.node.getScreenCTM().inverse(); + + var box = this.getBBox(); + + var anchorOffset; + + // fix text-anchor in text-element (#37) + if(this.el instanceof SVG.Text){ + anchorOffset = this.el.node.getComputedTextLength(); + + switch(this.el.attr('text-anchor')){ + case 'middle': + anchorOffset /= 2; + break + case 'start': + anchorOffset = 0; + break; + } + } + + this.startPoints = { + // We take absolute coordinates since we are just using a delta here + point: this.transformPoint(e, anchorOffset), + box: box, + transform: this.el.transform() + }; + + // add drag and end events to window + SVG.on(window, 'mousemove.drag', function(e){ _this.drag(e); }); + SVG.on(window, 'touchmove.drag', function(e){ _this.drag(e); }); + SVG.on(window, 'mouseup.drag', function(e){ _this.end(e); }); + SVG.on(window, 'touchend.drag', function(e){ _this.end(e); }); + + // fire dragstart event + this.el.fire('dragstart', {event: e, p: this.startPoints.point, m: this.m, handler: this}); + }; + + // while dragging + DragHandler.prototype.drag = function(e){ + + var box = this.getBBox() + , p = this.transformPoint(e) + , x = this.startPoints.box.x + p.x - this.startPoints.point.x + , y = this.startPoints.box.y + p.y - this.startPoints.point.y + , c = this.constraint + , gx = p.x - this.startPoints.point.x + , gy = p.y - this.startPoints.point.y; + + this.el.fire('dragmove', { + event: e + , p: p + , m: this.m + , handler: this + }); + + if(this.el.event().defaultPrevented) return p + + // move the element to its new position, if possible by constraint + if (typeof c == 'function') { + + var coord = c.call(this.el, x, y, this.m); + + // bool, just show us if movement is allowed or not + if (typeof coord == 'boolean') { + coord = { + x: coord, + y: coord + }; + } + + // if true, we just move. If !false its a number and we move it there + if (coord.x === true) { + this.el.x(x); + } else if (coord.x !== false) { + this.el.x(coord.x); + } + + if (coord.y === true) { + this.el.y(y); + } else if (coord.y !== false) { + this.el.y(coord.y); + } + + } else if (typeof c == 'object') { + + // keep element within constrained box + if (c.minX != null && x < c.minX) { + x = c.minX; + gx = x - this.startPoints.box.x; + } else if (c.maxX != null && x > c.maxX - box.width) { + x = c.maxX - box.width; + gx = x - this.startPoints.box.x; + } if (c.minY != null && y < c.minY) { + y = c.minY; + gy = y - this.startPoints.box.y; + } else if (c.maxY != null && y > c.maxY - box.height) { + y = c.maxY - box.height; + gy = y - this.startPoints.box.y; + } + + if (c.snapToGrid != null) { + x = x - (x % c.snapToGrid); + y = y - (y % c.snapToGrid); + gx = gx - (gx % c.snapToGrid); + gy = gy - (gy % c.snapToGrid); + } + + if(this.el instanceof SVG.G) + this.el.matrix(this.startPoints.transform).transform({x:gx, y: gy}, true); + else + this.el.move(x, y); + } + + // so we can use it in the end-method, too + return p + }; + + DragHandler.prototype.end = function(e){ + + // final drag + var p = this.drag(e); + + // fire dragend event + this.el.fire('dragend', { event: e, p: p, m: this.m, handler: this }); + + // unbind events + SVG.off(window, 'mousemove.drag'); + SVG.off(window, 'touchmove.drag'); + SVG.off(window, 'mouseup.drag'); + SVG.off(window, 'touchend.drag'); + + }; + + SVG.extend(SVG.Element, { + // Make element draggable + // Constraint might be an object (as described in readme.md) or a function in the form "function (x, y)" that gets called before every move. + // The function can return a boolean or an object of the form {x, y}, to which the element will be moved. "False" skips moving, true moves to raw x, y. + draggable: function(value, constraint) { + + // Check the parameters and reassign if needed + if (typeof value == 'function' || typeof value == 'object') { + constraint = value; + value = true; + } + + var dragHandler = this.remember('_draggable') || new DragHandler(this); + + // When no parameter is given, value is true + value = typeof value === 'undefined' ? true : value; + + if(value) dragHandler.init(constraint || {}, value); + else { + this.off('mousedown.drag'); + this.off('touchstart.drag'); + } + + return this + } + + }); + + }).call(undefined); + + (function() { + + function SelectHandler(el) { + + this.el = el; + el.remember('_selectHandler', this); + this.pointSelection = {isSelected: false}; + this.rectSelection = {isSelected: false}; + + // helper list with position settings of each type of point + this.pointsList = { + lt: [ 0, 0 ], + rt: [ 'width', 0 ], + rb: [ 'width', 'height' ], + lb: [ 0, 'height' ], + t: [ 'width', 0 ], + r: [ 'width', 'height' ], + b: [ 'width', 'height' ], + l: [ 0, 'height' ] + }; + + // helper function to get point coordinates based on settings above and an object (bbox in our case) + this.pointCoord = function (setting, object, isPointCentered) { + var coord = typeof setting !== 'string' ? setting : object[setting]; + // Top, bottom, right and left points are placed in the center of element width/height + return isPointCentered ? coord / 2 : coord + }; + + this.pointCoords = function (point, object) { + var settings = this.pointsList[point]; + + return { + x: this.pointCoord(settings[0], object, (point === 't' || point === 'b')), + y: this.pointCoord(settings[1], object, (point === 'r' || point === 'l')) + } + }; + } + + SelectHandler.prototype.init = function (value, options) { + + var bbox = this.el.bbox(); + this.options = {}; + + // store defaults list of points in order to verify users config + var points = this.el.selectize.defaults.points; + + // Merging the defaults and the options-object together + for (var i in this.el.selectize.defaults) { + this.options[i] = this.el.selectize.defaults[i]; + if (options[i] !== undefined) { + this.options[i] = options[i]; + } + } + + // prepare & validate list of points to be added (or excluded) + var pointsLists = ['points', 'pointsExclude']; + + for (var i in pointsLists) { + var option = this.options[pointsLists[i]]; + + if (typeof option === 'string') { + if (option.length > 0) { + // if set as comma separated string list => convert it into an array + option = option.split(/\s*,\s*/i); + } else { + option = []; + } + } else if (typeof option === 'boolean' && pointsLists[i] === 'points') { + // this is not needed, but let's have it for legacy support + option = option ? points : []; + } + + this.options[pointsLists[i]] = option; + } + + // intersect correct all points options with users config (exclude unwanted points) + // ES5 -> NO arrow functions nor Array.includes() + this.options.points = [ points, this.options.points ].reduce( + function (a, b) { + return a.filter( + function (c) { + return b.indexOf(c) > -1; + } + ) + } + ); + + // exclude pointsExclude, if wanted + this.options.points = [ this.options.points, this.options.pointsExclude ].reduce( + function (a, b) { + return a.filter( + function (c) { + return b.indexOf(c) < 0; + } + ) + } + ); + + this.parent = this.el.parent(); + this.nested = (this.nested || this.parent.group()); + this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y)); + + // When deepSelect is enabled and the element is a line/polyline/polygon, draw only points for moving + if (this.options.deepSelect && ['line', 'polyline', 'polygon'].indexOf(this.el.type) !== -1) { + this.selectPoints(value); + } else { + this.selectRect(value); + } + + this.observe(); + this.cleanup(); + + }; + + SelectHandler.prototype.selectPoints = function (value) { + + this.pointSelection.isSelected = value; + + // When set is already there we dont have to create one + if (this.pointSelection.set) { + return this; + } + + // Create our set of elements + this.pointSelection.set = this.parent.set(); + // draw the points and mark the element as selected + this.drawPoints(); + + return this; + + }; + + // create the point-array which contains the 2 points of a line or simply the points-array of polyline/polygon + SelectHandler.prototype.getPointArray = function () { + var bbox = this.el.bbox(); + + return this.el.array().valueOf().map(function (el) { + return [el[0] - bbox.x, el[1] - bbox.y]; + }); + }; + + // Draws a points + SelectHandler.prototype.drawPoints = function () { - /*! svg.draggable.js - v2.2.2 - 2019-01-08 - * https://github.com/svgdotjs/svg.draggable.js - * Copyright (c) 2019 Wout Fierens; Licensed MIT */ - (function() { - - // creates handler, saves it - function DragHandler(el){ - el.remember('_draggable', this); - this.el = el; - } - - - // Sets new parameter, starts dragging - DragHandler.prototype.init = function(constraint, val){ - var _this = this; - this.constraint = constraint; - this.value = val; - this.el.on('mousedown.drag', function(e){ _this.start(e); }); - this.el.on('touchstart.drag', function(e){ _this.start(e); }); - }; - - // transforms one point from screen to user coords - DragHandler.prototype.transformPoint = function(event, offset){ - event = event || window.event; - var touches = event.changedTouches && event.changedTouches[0] || event; - this.p.x = touches.clientX - (offset || 0); - this.p.y = touches.clientY; - return this.p.matrixTransform(this.m) - }; - - // gets elements bounding box with special handling of groups, nested and use - DragHandler.prototype.getBBox = function(){ - - var box = this.el.bbox(); - - if(this.el instanceof SVG.Nested) box = this.el.rbox(); - - if (this.el instanceof SVG.G || this.el instanceof SVG.Use || this.el instanceof SVG.Nested) { - box.x = this.el.x(); - box.y = this.el.y(); - } - - return box - }; - - // start dragging - DragHandler.prototype.start = function(e){ - - // check for left button - if(e.type == 'click'|| e.type == 'mousedown' || e.type == 'mousemove'){ - if((e.which || e.buttons) != 1){ - return - } - } - - var _this = this; - - // fire beforedrag event - this.el.fire('beforedrag', { event: e, handler: this }); - if(this.el.event().defaultPrevented) return; - - // prevent browser drag behavior as soon as possible - e.preventDefault(); - - // prevent propagation to a parent that might also have dragging enabled - e.stopPropagation(); - - // search for parent on the fly to make sure we can call - // draggable() even when element is not in the dom currently - this.parent = this.parent || this.el.parent(SVG.Nested) || this.el.parent(SVG.Doc); - this.p = this.parent.node.createSVGPoint(); - - // save current transformation matrix - this.m = this.el.node.getScreenCTM().inverse(); - - var box = this.getBBox(); - - var anchorOffset; - - // fix text-anchor in text-element (#37) - if(this.el instanceof SVG.Text){ - anchorOffset = this.el.node.getComputedTextLength(); - - switch(this.el.attr('text-anchor')){ - case 'middle': - anchorOffset /= 2; - break - case 'start': - anchorOffset = 0; - break; - } - } - - this.startPoints = { - // We take absolute coordinates since we are just using a delta here - point: this.transformPoint(e, anchorOffset), - box: box, - transform: this.el.transform() - }; - - // add drag and end events to window - SVG.on(window, 'mousemove.drag', function(e){ _this.drag(e); }); - SVG.on(window, 'touchmove.drag', function(e){ _this.drag(e); }); - SVG.on(window, 'mouseup.drag', function(e){ _this.end(e); }); - SVG.on(window, 'touchend.drag', function(e){ _this.end(e); }); - - // fire dragstart event - this.el.fire('dragstart', {event: e, p: this.startPoints.point, m: this.m, handler: this}); - }; - - // while dragging - DragHandler.prototype.drag = function(e){ - - var box = this.getBBox() - , p = this.transformPoint(e) - , x = this.startPoints.box.x + p.x - this.startPoints.point.x - , y = this.startPoints.box.y + p.y - this.startPoints.point.y - , c = this.constraint - , gx = p.x - this.startPoints.point.x - , gy = p.y - this.startPoints.point.y; - - this.el.fire('dragmove', { - event: e - , p: p - , m: this.m - , handler: this - }); - - if(this.el.event().defaultPrevented) return p - - // move the element to its new position, if possible by constraint - if (typeof c == 'function') { - - var coord = c.call(this.el, x, y, this.m); - - // bool, just show us if movement is allowed or not - if (typeof coord == 'boolean') { - coord = { - x: coord, - y: coord - }; - } - - // if true, we just move. If !false its a number and we move it there - if (coord.x === true) { - this.el.x(x); - } else if (coord.x !== false) { - this.el.x(coord.x); - } - - if (coord.y === true) { - this.el.y(y); - } else if (coord.y !== false) { - this.el.y(coord.y); - } - - } else if (typeof c == 'object') { - - // keep element within constrained box - if (c.minX != null && x < c.minX) { - x = c.minX; - gx = x - this.startPoints.box.x; - } else if (c.maxX != null && x > c.maxX - box.width) { - x = c.maxX - box.width; - gx = x - this.startPoints.box.x; - } if (c.minY != null && y < c.minY) { - y = c.minY; - gy = y - this.startPoints.box.y; - } else if (c.maxY != null && y > c.maxY - box.height) { - y = c.maxY - box.height; - gy = y - this.startPoints.box.y; - } - - if (c.snapToGrid != null) { - x = x - (x % c.snapToGrid); - y = y - (y % c.snapToGrid); - gx = gx - (gx % c.snapToGrid); - gy = gy - (gy % c.snapToGrid); - } - - if(this.el instanceof SVG.G) - this.el.matrix(this.startPoints.transform).transform({x:gx, y: gy}, true); - else - this.el.move(x, y); - } - - // so we can use it in the end-method, too - return p - }; - - DragHandler.prototype.end = function(e){ - - // final drag - var p = this.drag(e); - - // fire dragend event - this.el.fire('dragend', { event: e, p: p, m: this.m, handler: this }); - - // unbind events - SVG.off(window, 'mousemove.drag'); - SVG.off(window, 'touchmove.drag'); - SVG.off(window, 'mouseup.drag'); - SVG.off(window, 'touchend.drag'); - - }; - - SVG.extend(SVG.Element, { - // Make element draggable - // Constraint might be an object (as described in readme.md) or a function in the form "function (x, y)" that gets called before every move. - // The function can return a boolean or an object of the form {x, y}, to which the element will be moved. "False" skips moving, true moves to raw x, y. - draggable: function(value, constraint) { - - // Check the parameters and reassign if needed - if (typeof value == 'function' || typeof value == 'object') { - constraint = value; - value = true; - } - - var dragHandler = this.remember('_draggable') || new DragHandler(this); - - // When no parameter is given, value is true - value = typeof value === 'undefined' ? true : value; - - if(value) dragHandler.init(constraint || {}, value); - else { - this.off('mousedown.drag'); - this.off('touchstart.drag'); - } - - return this - } - - }); - - }).call(undefined); + var _this = this, array = this.getPointArray(); - (function() { + // go through the array of points + for (var i = 0, len = array.length; i < len; ++i) { + + var curriedEvent = (function (k) { + return function (ev) { + ev = ev || window.event; + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + ev.stopPropagation(); + + var x = ev.pageX || ev.touches[0].pageX; + var y = ev.pageY || ev.touches[0].pageY; + _this.el.fire('point', {x: x, y: y, i: k, event: ev}); + }; + })(i); + + // add every point to the set + // add css-classes and a touchstart-event which fires our event for moving points + var point = this.drawPoint(array[i][0], array[i][1]) + .addClass(this.options.classPoints) + .addClass(this.options.classPoints + '_point') + .on('touchstart', curriedEvent) + .on('mousedown', curriedEvent); + this.pointSelection.set.add(point); + } + }; + + // The function to draw single point + SelectHandler.prototype.drawPoint = function (cx, cy) { + var pointType = this.options.pointType; + + switch (pointType) { + case 'circle': + return this.drawCircle(cx, cy); + case 'rect': + return this.drawRect(cx, cy); + default: + if (typeof pointType === 'function') { + return pointType.call(this, cx, cy); + } + + throw new Error('Unknown ' + pointType + ' point type!'); + } + }; + + // The function to draw the circle point + SelectHandler.prototype.drawCircle = function (cx, cy) { + return this.nested.circle(this.options.pointSize) + .center(cx, cy); + }; + + // The function to draw the rect point + SelectHandler.prototype.drawRect = function (cx, cy) { + return this.nested.rect(this.options.pointSize, this.options.pointSize) + .center(cx, cy); + }; + + // every time a point is moved, we have to update the positions of our point + SelectHandler.prototype.updatePointSelection = function () { + var array = this.getPointArray(); + + this.pointSelection.set.each(function (i) { + if (this.cx() === array[i][0] && this.cy() === array[i][1]) { + return; + } + this.center(array[i][0], array[i][1]); + }); + }; + + SelectHandler.prototype.updateRectSelection = function () { + var _this = this, bbox = this.el.bbox(); + + this.rectSelection.set.get(0).attr({ + width: bbox.width, + height: bbox.height + }); + + // set.get(1) is always in the upper left corner. no need to move it + if (this.options.points.length) { + this.options.points.map(function (point, index) { + var coords = _this.pointCoords(point, bbox); + + _this.rectSelection.set.get(index + 1).center(coords.x, coords.y); + }); + } + + if (this.options.rotationPoint) { + var length = this.rectSelection.set.length(); + + this.rectSelection.set.get(length - 1).center(bbox.width / 2, 20); + } + }; + + SelectHandler.prototype.selectRect = function (value) { + + var _this = this, bbox = this.el.bbox(); + + this.rectSelection.isSelected = value; + + // when set is already p + this.rectSelection.set = this.rectSelection.set || this.parent.set(); + + // helperFunction to create a mouse-down function which triggers the event specified in `eventName` + function getMoseDownFunc(eventName) { + return function (ev) { + ev = ev || window.event; + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + ev.stopPropagation(); + + var x = ev.pageX || ev.touches[0].pageX; + var y = ev.pageY || ev.touches[0].pageY; + _this.el.fire(eventName, {x: x, y: y, event: ev}); + }; + } + + // create the selection-rectangle and add the css-class + if (!this.rectSelection.set.get(0)) { + this.rectSelection.set.add(this.nested.rect(bbox.width, bbox.height).addClass(this.options.classRect)); + } + + // Draw Points at the edges, if enabled + if (this.options.points.length && this.rectSelection.set.length() < 2) { + var ename ="touchstart", mname = "mousedown"; + + this.options.points.map(function (point, index) { + var coords = _this.pointCoords(point, bbox); + + var pointElement = _this.drawPoint(coords.x, coords.y) + .attr('class', _this.options.classPoints + '_' + point) + .on(mname, getMoseDownFunc(point)) + .on(ename, getMoseDownFunc(point)); + _this.rectSelection.set.add(pointElement); + }); + + this.rectSelection.set.each(function () { + this.addClass(_this.options.classPoints); + }); + } + + // draw rotationPint, if enabled + if (this.options.rotationPoint && ((this.options.points && !this.rectSelection.set.get(9)) || (!this.options.points && !this.rectSelection.set.get(1)))) { + + var curriedEvent = function (ev) { + ev = ev || window.event; + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + ev.stopPropagation(); + + var x = ev.pageX || ev.touches[0].pageX; + var y = ev.pageY || ev.touches[0].pageY; + _this.el.fire('rot', {x: x, y: y, event: ev}); + }; + + var pointElement = this.drawPoint(bbox.width / 2, 20) + .attr('class', this.options.classPoints + '_rot') + .on("touchstart", curriedEvent) + .on("mousedown", curriedEvent); + this.rectSelection.set.add(pointElement); + } + + }; + + SelectHandler.prototype.handler = function () { - function SelectHandler(el) { - - this.el = el; - el.remember('_selectHandler', this); - this.pointSelection = {isSelected: false}; - this.rectSelection = {isSelected: false}; - - // helper list with position settings of each type of point - this.pointsList = { - lt: [ 0, 0 ], - rt: [ 'width', 0 ], - rb: [ 'width', 'height' ], - lb: [ 0, 'height' ], - t: [ 'width', 0 ], - r: [ 'width', 'height' ], - b: [ 'width', 'height' ], - l: [ 0, 'height' ] - }; - - // helper function to get point coordinates based on settings above and an object (bbox in our case) - this.pointCoord = function (setting, object, isPointCentered) { - var coord = typeof setting !== 'string' ? setting : object[setting]; - // Top, bottom, right and left points are placed in the center of element width/height - return isPointCentered ? coord / 2 : coord - }; - - this.pointCoords = function (point, object) { - var settings = this.pointsList[point]; - - return { - x: this.pointCoord(settings[0], object, (point === 't' || point === 'b')), - y: this.pointCoord(settings[1], object, (point === 'r' || point === 'l')) - } - }; - } - - SelectHandler.prototype.init = function (value, options) { - - var bbox = this.el.bbox(); - this.options = {}; - - // store defaults list of points in order to verify users config - var points = this.el.selectize.defaults.points; - - // Merging the defaults and the options-object together - for (var i in this.el.selectize.defaults) { - this.options[i] = this.el.selectize.defaults[i]; - if (options[i] !== undefined) { - this.options[i] = options[i]; - } - } - - // prepare & validate list of points to be added (or excluded) - var pointsLists = ['points', 'pointsExclude']; - - for (var i in pointsLists) { - var option = this.options[pointsLists[i]]; - - if (typeof option === 'string') { - if (option.length > 0) { - // if set as comma separated string list => convert it into an array - option = option.split(/\s*,\s*/i); - } else { - option = []; - } - } else if (typeof option === 'boolean' && pointsLists[i] === 'points') { - // this is not needed, but let's have it for legacy support - option = option ? points : []; - } - - this.options[pointsLists[i]] = option; - } - - // intersect correct all points options with users config (exclude unwanted points) - // ES5 -> NO arrow functions nor Array.includes() - this.options.points = [ points, this.options.points ].reduce( - function (a, b) { - return a.filter( - function (c) { - return b.indexOf(c) > -1; - } - ) - } - ); - - // exclude pointsExclude, if wanted - this.options.points = [ this.options.points, this.options.pointsExclude ].reduce( - function (a, b) { - return a.filter( - function (c) { - return b.indexOf(c) < 0; - } - ) - } - ); - - this.parent = this.el.parent(); - this.nested = (this.nested || this.parent.group()); - this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y)); - - // When deepSelect is enabled and the element is a line/polyline/polygon, draw only points for moving - if (this.options.deepSelect && ['line', 'polyline', 'polygon'].indexOf(this.el.type) !== -1) { - this.selectPoints(value); - } else { - this.selectRect(value); - } - - this.observe(); - this.cleanup(); - - }; - - SelectHandler.prototype.selectPoints = function (value) { - - this.pointSelection.isSelected = value; - - // When set is already there we dont have to create one - if (this.pointSelection.set) { - return this; - } - - // Create our set of elements - this.pointSelection.set = this.parent.set(); - // draw the points and mark the element as selected - this.drawPoints(); - - return this; - - }; - - // create the point-array which contains the 2 points of a line or simply the points-array of polyline/polygon - SelectHandler.prototype.getPointArray = function () { - var bbox = this.el.bbox(); - - return this.el.array().valueOf().map(function (el) { - return [el[0] - bbox.x, el[1] - bbox.y]; - }); - }; - - // Draws a points - SelectHandler.prototype.drawPoints = function () { - - var _this = this, array = this.getPointArray(); - - // go through the array of points - for (var i = 0, len = array.length; i < len; ++i) { - - var curriedEvent = (function (k) { - return function (ev) { - ev = ev || window.event; - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - ev.stopPropagation(); - - var x = ev.pageX || ev.touches[0].pageX; - var y = ev.pageY || ev.touches[0].pageY; - _this.el.fire('point', {x: x, y: y, i: k, event: ev}); - }; - })(i); - - // add every point to the set - // add css-classes and a touchstart-event which fires our event for moving points - var point = this.drawPoint(array[i][0], array[i][1]) - .addClass(this.options.classPoints) - .addClass(this.options.classPoints + '_point') - .on('touchstart', curriedEvent) - .on('mousedown', curriedEvent); - this.pointSelection.set.add(point); - } - }; - - // The function to draw single point - SelectHandler.prototype.drawPoint = function (cx, cy) { - var pointType = this.options.pointType; - - switch (pointType) { - case 'circle': - return this.drawCircle(cx, cy); - case 'rect': - return this.drawRect(cx, cy); - default: - if (typeof pointType === 'function') { - return pointType.call(this, cx, cy); - } - - throw new Error('Unknown ' + pointType + ' point type!'); - } - }; - - // The function to draw the circle point - SelectHandler.prototype.drawCircle = function (cx, cy) { - return this.nested.circle(this.options.pointSize) - .center(cx, cy); - }; - - // The function to draw the rect point - SelectHandler.prototype.drawRect = function (cx, cy) { - return this.nested.rect(this.options.pointSize, this.options.pointSize) - .center(cx, cy); - }; - - // every time a point is moved, we have to update the positions of our point - SelectHandler.prototype.updatePointSelection = function () { - var array = this.getPointArray(); - - this.pointSelection.set.each(function (i) { - if (this.cx() === array[i][0] && this.cy() === array[i][1]) { - return; - } - this.center(array[i][0], array[i][1]); - }); - }; - - SelectHandler.prototype.updateRectSelection = function () { - var _this = this, bbox = this.el.bbox(); - - this.rectSelection.set.get(0).attr({ - width: bbox.width, - height: bbox.height - }); - - // set.get(1) is always in the upper left corner. no need to move it - if (this.options.points.length) { - this.options.points.map(function (point, index) { - var coords = _this.pointCoords(point, bbox); - - _this.rectSelection.set.get(index + 1).center(coords.x, coords.y); - }); - } - - if (this.options.rotationPoint) { - var length = this.rectSelection.set.length(); - - this.rectSelection.set.get(length - 1).center(bbox.width / 2, 20); - } - }; - - SelectHandler.prototype.selectRect = function (value) { - - var _this = this, bbox = this.el.bbox(); - - this.rectSelection.isSelected = value; - - // when set is already p - this.rectSelection.set = this.rectSelection.set || this.parent.set(); - - // helperFunction to create a mouse-down function which triggers the event specified in `eventName` - function getMoseDownFunc(eventName) { - return function (ev) { - ev = ev || window.event; - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - ev.stopPropagation(); - - var x = ev.pageX || ev.touches[0].pageX; - var y = ev.pageY || ev.touches[0].pageY; - _this.el.fire(eventName, {x: x, y: y, event: ev}); - }; - } - - // create the selection-rectangle and add the css-class - if (!this.rectSelection.set.get(0)) { - this.rectSelection.set.add(this.nested.rect(bbox.width, bbox.height).addClass(this.options.classRect)); - } - - // Draw Points at the edges, if enabled - if (this.options.points.length && this.rectSelection.set.length() < 2) { - var ename ="touchstart", mname = "mousedown"; - - this.options.points.map(function (point, index) { - var coords = _this.pointCoords(point, bbox); - - var pointElement = _this.drawPoint(coords.x, coords.y) - .attr('class', _this.options.classPoints + '_' + point) - .on(mname, getMoseDownFunc(point)) - .on(ename, getMoseDownFunc(point)); - _this.rectSelection.set.add(pointElement); - }); - - this.rectSelection.set.each(function () { - this.addClass(_this.options.classPoints); - }); - } - - // draw rotationPint, if enabled - if (this.options.rotationPoint && ((this.options.points && !this.rectSelection.set.get(9)) || (!this.options.points && !this.rectSelection.set.get(1)))) { - - var curriedEvent = function (ev) { - ev = ev || window.event; - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - ev.stopPropagation(); - - var x = ev.pageX || ev.touches[0].pageX; - var y = ev.pageY || ev.touches[0].pageY; - _this.el.fire('rot', {x: x, y: y, event: ev}); - }; - - var pointElement = this.drawPoint(bbox.width / 2, 20) - .attr('class', this.options.classPoints + '_rot') - .on("touchstart", curriedEvent) - .on("mousedown", curriedEvent); - this.rectSelection.set.add(pointElement); - } - - }; - - SelectHandler.prototype.handler = function () { - - var bbox = this.el.bbox(); - this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y)); - - if (this.rectSelection.isSelected) { - this.updateRectSelection(); - } - - if (this.pointSelection.isSelected) { - this.updatePointSelection(); - } - - }; - - SelectHandler.prototype.observe = function () { - var _this = this; - - if (MutationObserver) { - if (this.rectSelection.isSelected || this.pointSelection.isSelected) { - this.observerInst = this.observerInst || new MutationObserver(function () { - _this.handler(); - }); - this.observerInst.observe(this.el.node, {attributes: true}); - } else { - try { - this.observerInst.disconnect(); - delete this.observerInst; - } catch (e) { - } - } - } else { - this.el.off('DOMAttrModified.select'); - - if (this.rectSelection.isSelected || this.pointSelection.isSelected) { - this.el.on('DOMAttrModified.select', function () { - _this.handler(); - }); - } - } - }; - - SelectHandler.prototype.cleanup = function () { - - //var _this = this; - - if (!this.rectSelection.isSelected && this.rectSelection.set) { - // stop watching the element, remove the selection - this.rectSelection.set.each(function () { - this.remove(); - }); - - this.rectSelection.set.clear(); - delete this.rectSelection.set; - } - - if (!this.pointSelection.isSelected && this.pointSelection.set) { - // Remove all points, clear the set, stop watching the element - this.pointSelection.set.each(function () { - this.remove(); - }); - - this.pointSelection.set.clear(); - delete this.pointSelection.set; - } - - if (!this.pointSelection.isSelected && !this.rectSelection.isSelected) { - this.nested.remove(); - delete this.nested; - - } - }; - - - SVG.extend(SVG.Element, { - // Select element with mouse - selectize: function (value, options) { - - // Check the parameters and reassign if needed - if (typeof value === 'object') { - options = value; - value = true; - } - - var selectHandler = this.remember('_selectHandler') || new SelectHandler(this); - - selectHandler.init(value === undefined ? true : value, options || {}); - - return this; - - } - }); - - SVG.Element.prototype.selectize.defaults = { - points: ['lt', 'rt', 'rb', 'lb', 't', 'r', 'b', 'l'], // which points to draw, default all - pointsExclude: [], // easier option if to exclude few than rewrite all - classRect: 'svg_select_boundingRect', // Css-class added to the rect - classPoints: 'svg_select_points', // Css-class added to the points - pointSize: 7, // size of point - rotationPoint: true, // If true, rotation point is drawn. Needed for rotation! - deepSelect: false, // If true, moving of single points is possible (only line, polyline, polyon) - pointType: 'circle' // Point type: circle or rect, default circle + var bbox = this.el.bbox(); + this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y)); + + if (this.rectSelection.isSelected) { + this.updateRectSelection(); + } + + if (this.pointSelection.isSelected) { + this.updatePointSelection(); + } + + }; + + SelectHandler.prototype.observe = function () { + var _this = this; + + if (MutationObserver) { + if (this.rectSelection.isSelected || this.pointSelection.isSelected) { + this.observerInst = this.observerInst || new MutationObserver(function () { + _this.handler(); + }); + this.observerInst.observe(this.el.node, {attributes: true}); + } else { + try { + this.observerInst.disconnect(); + delete this.observerInst; + } catch (e) { + } + } + } else { + this.el.off('DOMAttrModified.select'); + + if (this.rectSelection.isSelected || this.pointSelection.isSelected) { + this.el.on('DOMAttrModified.select', function () { + _this.handler(); + }); + } + } + }; + + SelectHandler.prototype.cleanup = function () { + + //var _this = this; + + if (!this.rectSelection.isSelected && this.rectSelection.set) { + // stop watching the element, remove the selection + this.rectSelection.set.each(function () { + this.remove(); + }); + + this.rectSelection.set.clear(); + delete this.rectSelection.set; + } + + if (!this.pointSelection.isSelected && this.pointSelection.set) { + // Remove all points, clear the set, stop watching the element + this.pointSelection.set.each(function () { + this.remove(); + }); + + this.pointSelection.set.clear(); + delete this.pointSelection.set; + } + + if (!this.pointSelection.isSelected && !this.rectSelection.isSelected) { + this.nested.remove(); + delete this.nested; + + } + }; + + + SVG.extend(SVG.Element, { + // Select element with mouse + selectize: function (value, options) { + + // Check the parameters and reassign if needed + if (typeof value === 'object') { + options = value; + value = true; + } + + var selectHandler = this.remember('_selectHandler') || new SelectHandler(this); + + selectHandler.init(value === undefined ? true : value, options || {}); + + return this; + + } + }); + + SVG.Element.prototype.selectize.defaults = { + points: ['lt', 'rt', 'rb', 'lb', 't', 'r', 'b', 'l'], // which points to draw, default all + pointsExclude: [], // easier option if to exclude few than rewrite all + classRect: 'svg_select_boundingRect', // Css-class added to the rect + classPoints: 'svg_select_points', // Css-class added to the points + pointSize: 7, // size of point + rotationPoint: true, // If true, rotation point is drawn. Needed for rotation! + deepSelect: false, // If true, moving of single points is possible (only line, polyline, polyon) + pointType: 'circle' // Point type: circle or rect, default circle }; }()); (function() { - (function () { - - function ResizeHandler(el) { - - el.remember('_resizeHandler', this); - - this.el = el; - this.parameters = {}; - this.lastUpdateCall = null; - this.p = el.doc().node.createSVGPoint(); - } - - ResizeHandler.prototype.transformPoint = function(x, y, m){ - - this.p.x = x - (this.offset.x - window.pageXOffset); - this.p.y = y - (this.offset.y - window.pageYOffset); - - return this.p.matrixTransform(m || this.m); - - }; - - ResizeHandler.prototype._extractPosition = function(event) { - // Extract a position from a mouse/touch event. - // Returns { x: .., y: .. } - return { - x: event.clientX != null ? event.clientX : event.touches[0].clientX, - y: event.clientY != null ? event.clientY : event.touches[0].clientY - } - }; - - ResizeHandler.prototype.init = function (options) { - - var _this = this; - - this.stop(); - - if (options === 'stop') { - return; - } - - this.options = {}; - - // Merge options and defaults - for (var i in this.el.resize.defaults) { - this.options[i] = this.el.resize.defaults[i]; - if (typeof options[i] !== 'undefined') { - this.options[i] = options[i]; - } - } - - // We listen to all these events which are specifying different edges - this.el.on('lt.resize', function(e){ _this.resize(e || window.event); }); // Left-Top - this.el.on('rt.resize', function(e){ _this.resize(e || window.event); }); // Right-Top - this.el.on('rb.resize', function(e){ _this.resize(e || window.event); }); // Right-Bottom - this.el.on('lb.resize', function(e){ _this.resize(e || window.event); }); // Left-Bottom - - this.el.on('t.resize', function(e){ _this.resize(e || window.event); }); // Top - this.el.on('r.resize', function(e){ _this.resize(e || window.event); }); // Right - this.el.on('b.resize', function(e){ _this.resize(e || window.event); }); // Bottom - this.el.on('l.resize', function(e){ _this.resize(e || window.event); }); // Left - - this.el.on('rot.resize', function(e){ _this.resize(e || window.event); }); // Rotation - - this.el.on('point.resize', function(e){ _this.resize(e || window.event); }); // Point-Moving - - // This call ensures, that the plugin reacts to a change of snapToGrid immediately - this.update(); - - }; - - ResizeHandler.prototype.stop = function(){ - this.el.off('lt.resize'); - this.el.off('rt.resize'); - this.el.off('rb.resize'); - this.el.off('lb.resize'); - - this.el.off('t.resize'); - this.el.off('r.resize'); - this.el.off('b.resize'); - this.el.off('l.resize'); - - this.el.off('rot.resize'); - - this.el.off('point.resize'); - - return this; - }; - - ResizeHandler.prototype.resize = function (event) { - - var _this = this; - - this.m = this.el.node.getScreenCTM().inverse(); - this.offset = { x: window.pageXOffset, y: window.pageYOffset }; - - var txPt = this._extractPosition(event.detail.event); - this.parameters = { - type: this.el.type, // the type of element - p: this.transformPoint(txPt.x, txPt.y), - x: event.detail.x, // x-position of the mouse when resizing started - y: event.detail.y, // y-position of the mouse when resizing started - box: this.el.bbox(), // The bounding-box of the element - rotation: this.el.transform().rotation // The current rotation of the element - }; - - // Add font-size parameter if the element type is text - if (this.el.type === "text") { - this.parameters.fontSize = this.el.attr()["font-size"]; - } - - // the i-param in the event holds the index of the point which is moved, when using `deepSelect` - if (event.detail.i !== undefined) { - - // get the point array - var array = this.el.array().valueOf(); - - // Save the index and the point which is moved - this.parameters.i = event.detail.i; - this.parameters.pointCoords = [array[event.detail.i][0], array[event.detail.i][1]]; - } - - // Lets check which edge of the bounding-box was clicked and resize the this.el according to this - switch (event.type) { - - // Left-Top-Edge - case 'lt': - // We build a calculating function for every case which gives us the new position of the this.el - this.calc = function (diffX, diffY) { - // The procedure is always the same - // First we snap the edge to the given grid (snapping to 1px grid is normal resizing) - var snap = this.snapToGrid(diffX, diffY); - - // Now we check if the new height and width still valid (> 0) - if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height - snap[1] > 0) { - // ...if valid, we resize the this.el (which can include moving because the coord-system starts at the left-top and this edge is moving sometimes when resized) - - /* - * but first check if the element is text box, so we can change the font size instead of - * the width and height - */ - - if (this.parameters.type === "text") { - this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y); - this.el.attr("font-size", this.parameters.fontSize - snap[0]); - return; - } - - snap = this.checkAspectRatio(snap); - - this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y + snap[1]).size(this.parameters.box.width - snap[0], this.parameters.box.height - snap[1]); - } - }; - break; - - // Right-Top - case 'rt': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1 << 1); - if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height - snap[1] > 0) { - if (this.parameters.type === "text") { - this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y); - this.el.attr("font-size", this.parameters.fontSize + snap[0]); - return; - } - - snap = this.checkAspectRatio(snap, true); - - this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).size(this.parameters.box.width + snap[0], this.parameters.box.height - snap[1]); - } - }; - break; - - // Right-Bottom - case 'rb': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 0); - if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height + snap[1] > 0) { - if (this.parameters.type === "text") { - this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y); - this.el.attr("font-size", this.parameters.fontSize + snap[0]); - return; - } - - snap = this.checkAspectRatio(snap); - - this.el.move(this.parameters.box.x, this.parameters.box.y).size(this.parameters.box.width + snap[0], this.parameters.box.height + snap[1]); - } - }; - break; - - // Left-Bottom - case 'lb': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1); - if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height + snap[1] > 0) { - if (this.parameters.type === "text") { - this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y); - this.el.attr("font-size", this.parameters.fontSize - snap[0]); - return; - } - - snap = this.checkAspectRatio(snap, true); - - this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).size(this.parameters.box.width - snap[0], this.parameters.box.height + snap[1]); - } - }; - break; - - // Top - case 't': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1 << 1); - if (this.parameters.box.height - snap[1] > 0) { - // Disable the font-resizing if it is not from the corner of bounding-box - if (this.parameters.type === "text") { - return; - } - - this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).height(this.parameters.box.height - snap[1]); - } - }; - break; - - // Right - case 'r': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 0); - if (this.parameters.box.width + snap[0] > 0) { - if (this.parameters.type === "text") { - return; - } - - this.el.move(this.parameters.box.x, this.parameters.box.y).width(this.parameters.box.width + snap[0]); - } - }; - break; - - // Bottom - case 'b': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 0); - if (this.parameters.box.height + snap[1] > 0) { - if (this.parameters.type === "text") { - return; - } - - this.el.move(this.parameters.box.x, this.parameters.box.y).height(this.parameters.box.height + snap[1]); - } - }; - break; - - // Left - case 'l': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1); - if (this.parameters.box.width - snap[0] > 0) { - if (this.parameters.type === "text") { - return; - } - - this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).width(this.parameters.box.width - snap[0]); - } - }; - break; - - // Rotation - case 'rot': - // s.a. - this.calc = function (diffX, diffY) { - - // yes this is kinda stupid but we need the mouse coords back... - var current = {x: diffX + this.parameters.p.x, y: diffY + this.parameters.p.y}; - - // start minus middle - var sAngle = Math.atan2((this.parameters.p.y - this.parameters.box.y - this.parameters.box.height / 2), (this.parameters.p.x - this.parameters.box.x - this.parameters.box.width / 2)); - - // end minus middle - var pAngle = Math.atan2((current.y - this.parameters.box.y - this.parameters.box.height / 2), (current.x - this.parameters.box.x - this.parameters.box.width / 2)); - - var angle = this.parameters.rotation + (pAngle - sAngle) * 180 / Math.PI + this.options.snapToAngle / 2; - - // We have to move the element to the center of the box first and change the rotation afterwards - // because rotation always works around a rotation-center, which is changed when moving the element - // We also set the new rotation center to the center of the box. - this.el.center(this.parameters.box.cx, this.parameters.box.cy).rotate(angle - (angle % this.options.snapToAngle), this.parameters.box.cx, this.parameters.box.cy); - }; - break; - - // Moving one single Point (needed when an element is deepSelected which means you can move every single point of the object) - case 'point': - this.calc = function (diffX, diffY) { - - // Snapping the point to the grid - var snap = this.snapToGrid(diffX, diffY, this.parameters.pointCoords[0], this.parameters.pointCoords[1]); - - // Get the point array - var array = this.el.array().valueOf(); - - // Changing the moved point in the array - array[this.parameters.i][0] = this.parameters.pointCoords[0] + snap[0]; - array[this.parameters.i][1] = this.parameters.pointCoords[1] + snap[1]; - - // And plot the new this.el - this.el.plot(array); - }; - } - - this.el.fire('resizestart', {dx: this.parameters.x, dy: this.parameters.y, event: event}); - // When resizing started, we have to register events for... - // Touches. - SVG.on(window, 'touchmove.resize', function(e) { - _this.update(e || window.event); - }); - SVG.on(window, 'touchend.resize', function() { - _this.done(); - }); - // Mouse. - SVG.on(window, 'mousemove.resize', function (e) { - _this.update(e || window.event); - }); - SVG.on(window, 'mouseup.resize', function () { - _this.done(); - }); - - }; - - // The update-function redraws the element every time the mouse is moving - ResizeHandler.prototype.update = function (event) { - - if (!event) { - if (this.lastUpdateCall) { - this.calc(this.lastUpdateCall[0], this.lastUpdateCall[1]); - } - return; - } - - // Calculate the difference between the mouseposition at start and now - var txPt = this._extractPosition(event); - var p = this.transformPoint(txPt.x, txPt.y); - - var diffX = p.x - this.parameters.p.x, - diffY = p.y - this.parameters.p.y; - - this.lastUpdateCall = [diffX, diffY]; - - // Calculate the new position and height / width of the element - this.calc(diffX, diffY); - - // Emit an event to say we have changed. - this.el.fire('resizing', {dx: diffX, dy: diffY, event: event}); - }; - - // Is called on mouseup. - // Removes the update-function from the mousemove event - ResizeHandler.prototype.done = function () { - this.lastUpdateCall = null; - SVG.off(window, 'mousemove.resize'); - SVG.off(window, 'mouseup.resize'); - SVG.off(window, 'touchmove.resize'); - SVG.off(window, 'touchend.resize'); - this.el.fire('resizedone'); - }; - - // The flag is used to determine whether the resizing is used with a left-Point (first bit) and top-point (second bit) - // In this cases the temp-values are calculated differently - ResizeHandler.prototype.snapToGrid = function (diffX, diffY, flag, pointCoordsY) { - - var temp; - - // If `pointCoordsY` is given, a single Point has to be snapped (deepSelect). That's why we need a different temp-value - if (typeof pointCoordsY !== 'undefined') { - // Note that flag = pointCoordsX in this case - temp = [(flag + diffX) % this.options.snapToGrid, (pointCoordsY + diffY) % this.options.snapToGrid]; - } else { - // We check if the flag is set and if not we set a default-value (both bits set - which means upper-left-edge) - flag = flag == null ? 1 | 1 << 1 : flag; - temp = [(this.parameters.box.x + diffX + (flag & 1 ? 0 : this.parameters.box.width)) % this.options.snapToGrid, (this.parameters.box.y + diffY + (flag & (1 << 1) ? 0 : this.parameters.box.height)) % this.options.snapToGrid]; - } - - if(diffX < 0) { - temp[0] -= this.options.snapToGrid; - } - if(diffY < 0) { - temp[1] -= this.options.snapToGrid; - } - - diffX -= (Math.abs(temp[0]) < this.options.snapToGrid / 2 ? - temp[0] : - temp[0] - (diffX < 0 ? -this.options.snapToGrid : this.options.snapToGrid)); - diffY -= (Math.abs(temp[1]) < this.options.snapToGrid / 2 ? - temp[1] : - temp[1] - (diffY < 0 ? -this.options.snapToGrid : this.options.snapToGrid)); - - return this.constraintToBox(diffX, diffY, flag, pointCoordsY); - - }; - - // keep element within constrained box - ResizeHandler.prototype.constraintToBox = function (diffX, diffY, flag, pointCoordsY) { - //return [diffX, diffY] - var c = this.options.constraint || {}; - var orgX, orgY; - - if (typeof pointCoordsY !== 'undefined') { - orgX = flag; - orgY = pointCoordsY; - } else { - orgX = this.parameters.box.x + (flag & 1 ? 0 : this.parameters.box.width); - orgY = this.parameters.box.y + (flag & (1<<1) ? 0 : this.parameters.box.height); - } - - if (typeof c.minX !== 'undefined' && orgX + diffX < c.minX) { - diffX = c.minX - orgX; - } - - if (typeof c.maxX !== 'undefined' && orgX + diffX > c.maxX) { - diffX = c.maxX - orgX; - } - - if (typeof c.minY !== 'undefined' && orgY + diffY < c.minY) { - diffY = c.minY - orgY; - } - - if (typeof c.maxY !== 'undefined' && orgY + diffY > c.maxY) { - diffY = c.maxY - orgY; - } - - return [diffX, diffY]; - }; - - ResizeHandler.prototype.checkAspectRatio = function (snap, isReverse) { - if (!this.options.saveAspectRatio) { - return snap; - } - - var updatedSnap = snap.slice(); - var aspectRatio = this.parameters.box.width / this.parameters.box.height; - var newW = this.parameters.box.width + snap[0]; - var newH = this.parameters.box.height - snap[1]; - var newAspectRatio = newW / newH; - - if (newAspectRatio < aspectRatio) { - // Height is too big. Adapt it - updatedSnap[1] = newW / aspectRatio - this.parameters.box.height; - isReverse && (updatedSnap[1] = -updatedSnap[1]); - } else if (newAspectRatio > aspectRatio) { - // Width is too big. Adapt it - updatedSnap[0] = this.parameters.box.width - newH * aspectRatio; - isReverse && (updatedSnap[0] = -updatedSnap[0]); - } - - return updatedSnap; - }; - - SVG.extend(SVG.Element, { - // Resize element with mouse - resize: function (options) { - - (this.remember('_resizeHandler') || new ResizeHandler(this)).init(options || {}); - - return this; - - } - - }); - - SVG.Element.prototype.resize.defaults = { - snapToAngle: 0.1, // Specifies the speed the rotation is happening when moving the mouse - snapToGrid: 1, // Snaps to a grid of `snapToGrid` Pixels - constraint: {}, // keep element within constrained box - saveAspectRatio: false // Save aspect ratio when resizing using lt, rt, rb or lb points - }; - + (function () { + + function ResizeHandler(el) { + + el.remember('_resizeHandler', this); + + this.el = el; + this.parameters = {}; + this.lastUpdateCall = null; + this.p = el.doc().node.createSVGPoint(); + } + + ResizeHandler.prototype.transformPoint = function(x, y, m){ + + this.p.x = x - (this.offset.x - window.pageXOffset); + this.p.y = y - (this.offset.y - window.pageYOffset); + + return this.p.matrixTransform(m || this.m); + + }; + + ResizeHandler.prototype._extractPosition = function(event) { + // Extract a position from a mouse/touch event. + // Returns { x: .., y: .. } + return { + x: event.clientX != null ? event.clientX : event.touches[0].clientX, + y: event.clientY != null ? event.clientY : event.touches[0].clientY + } + }; + + ResizeHandler.prototype.init = function (options) { + + var _this = this; + + this.stop(); + + if (options === 'stop') { + return; + } + + this.options = {}; + + // Merge options and defaults + for (var i in this.el.resize.defaults) { + this.options[i] = this.el.resize.defaults[i]; + if (typeof options[i] !== 'undefined') { + this.options[i] = options[i]; + } + } + + // We listen to all these events which are specifying different edges + this.el.on('lt.resize', function(e){ _this.resize(e || window.event); }); // Left-Top + this.el.on('rt.resize', function(e){ _this.resize(e || window.event); }); // Right-Top + this.el.on('rb.resize', function(e){ _this.resize(e || window.event); }); // Right-Bottom + this.el.on('lb.resize', function(e){ _this.resize(e || window.event); }); // Left-Bottom + + this.el.on('t.resize', function(e){ _this.resize(e || window.event); }); // Top + this.el.on('r.resize', function(e){ _this.resize(e || window.event); }); // Right + this.el.on('b.resize', function(e){ _this.resize(e || window.event); }); // Bottom + this.el.on('l.resize', function(e){ _this.resize(e || window.event); }); // Left + + this.el.on('rot.resize', function(e){ _this.resize(e || window.event); }); // Rotation + + this.el.on('point.resize', function(e){ _this.resize(e || window.event); }); // Point-Moving + + // This call ensures, that the plugin reacts to a change of snapToGrid immediately + this.update(); + + }; + + ResizeHandler.prototype.stop = function(){ + this.el.off('lt.resize'); + this.el.off('rt.resize'); + this.el.off('rb.resize'); + this.el.off('lb.resize'); + + this.el.off('t.resize'); + this.el.off('r.resize'); + this.el.off('b.resize'); + this.el.off('l.resize'); + + this.el.off('rot.resize'); + + this.el.off('point.resize'); + + return this; + }; + + ResizeHandler.prototype.resize = function (event) { + + var _this = this; + + this.m = this.el.node.getScreenCTM().inverse(); + this.offset = { x: window.pageXOffset, y: window.pageYOffset }; + + var txPt = this._extractPosition(event.detail.event); + this.parameters = { + type: this.el.type, // the type of element + p: this.transformPoint(txPt.x, txPt.y), + x: event.detail.x, // x-position of the mouse when resizing started + y: event.detail.y, // y-position of the mouse when resizing started + box: this.el.bbox(), // The bounding-box of the element + rotation: this.el.transform().rotation // The current rotation of the element + }; + + // Add font-size parameter if the element type is text + if (this.el.type === "text") { + this.parameters.fontSize = this.el.attr()["font-size"]; + } + + // the i-param in the event holds the index of the point which is moved, when using `deepSelect` + if (event.detail.i !== undefined) { + + // get the point array + var array = this.el.array().valueOf(); + + // Save the index and the point which is moved + this.parameters.i = event.detail.i; + this.parameters.pointCoords = [array[event.detail.i][0], array[event.detail.i][1]]; + } + + // Lets check which edge of the bounding-box was clicked and resize the this.el according to this + switch (event.type) { + + // Left-Top-Edge + case 'lt': + // We build a calculating function for every case which gives us the new position of the this.el + this.calc = function (diffX, diffY) { + // The procedure is always the same + // First we snap the edge to the given grid (snapping to 1px grid is normal resizing) + var snap = this.snapToGrid(diffX, diffY); + + // Now we check if the new height and width still valid (> 0) + if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height - snap[1] > 0) { + // ...if valid, we resize the this.el (which can include moving because the coord-system starts at the left-top and this edge is moving sometimes when resized) + + /* + * but first check if the element is text box, so we can change the font size instead of + * the width and height + */ + + if (this.parameters.type === "text") { + this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y); + this.el.attr("font-size", this.parameters.fontSize - snap[0]); + return; + } + + snap = this.checkAspectRatio(snap); + + this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y + snap[1]).size(this.parameters.box.width - snap[0], this.parameters.box.height - snap[1]); + } + }; + break; + + // Right-Top + case 'rt': + // s.a. + this.calc = function (diffX, diffY) { + var snap = this.snapToGrid(diffX, diffY, 1 << 1); + if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height - snap[1] > 0) { + if (this.parameters.type === "text") { + this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y); + this.el.attr("font-size", this.parameters.fontSize + snap[0]); + return; + } + + snap = this.checkAspectRatio(snap, true); + + this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).size(this.parameters.box.width + snap[0], this.parameters.box.height - snap[1]); + } + }; + break; + + // Right-Bottom + case 'rb': + // s.a. + this.calc = function (diffX, diffY) { + var snap = this.snapToGrid(diffX, diffY, 0); + if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height + snap[1] > 0) { + if (this.parameters.type === "text") { + this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y); + this.el.attr("font-size", this.parameters.fontSize + snap[0]); + return; + } + + snap = this.checkAspectRatio(snap); + + this.el.move(this.parameters.box.x, this.parameters.box.y).size(this.parameters.box.width + snap[0], this.parameters.box.height + snap[1]); + } + }; + break; + + // Left-Bottom + case 'lb': + // s.a. + this.calc = function (diffX, diffY) { + var snap = this.snapToGrid(diffX, diffY, 1); + if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height + snap[1] > 0) { + if (this.parameters.type === "text") { + this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y); + this.el.attr("font-size", this.parameters.fontSize - snap[0]); + return; + } + + snap = this.checkAspectRatio(snap, true); + + this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).size(this.parameters.box.width - snap[0], this.parameters.box.height + snap[1]); + } + }; + break; + + // Top + case 't': + // s.a. + this.calc = function (diffX, diffY) { + var snap = this.snapToGrid(diffX, diffY, 1 << 1); + if (this.parameters.box.height - snap[1] > 0) { + // Disable the font-resizing if it is not from the corner of bounding-box + if (this.parameters.type === "text") { + return; + } + + this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).height(this.parameters.box.height - snap[1]); + } + }; + break; + + // Right + case 'r': + // s.a. + this.calc = function (diffX, diffY) { + var snap = this.snapToGrid(diffX, diffY, 0); + if (this.parameters.box.width + snap[0] > 0) { + if (this.parameters.type === "text") { + return; + } + + this.el.move(this.parameters.box.x, this.parameters.box.y).width(this.parameters.box.width + snap[0]); + } + }; + break; + + // Bottom + case 'b': + // s.a. + this.calc = function (diffX, diffY) { + var snap = this.snapToGrid(diffX, diffY, 0); + if (this.parameters.box.height + snap[1] > 0) { + if (this.parameters.type === "text") { + return; + } + + this.el.move(this.parameters.box.x, this.parameters.box.y).height(this.parameters.box.height + snap[1]); + } + }; + break; + + // Left + case 'l': + // s.a. + this.calc = function (diffX, diffY) { + var snap = this.snapToGrid(diffX, diffY, 1); + if (this.parameters.box.width - snap[0] > 0) { + if (this.parameters.type === "text") { + return; + } + + this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).width(this.parameters.box.width - snap[0]); + } + }; + break; + + // Rotation + case 'rot': + // s.a. + this.calc = function (diffX, diffY) { + + // yes this is kinda stupid but we need the mouse coords back... + var current = {x: diffX + this.parameters.p.x, y: diffY + this.parameters.p.y}; + + // start minus middle + var sAngle = Math.atan2((this.parameters.p.y - this.parameters.box.y - this.parameters.box.height / 2), (this.parameters.p.x - this.parameters.box.x - this.parameters.box.width / 2)); + + // end minus middle + var pAngle = Math.atan2((current.y - this.parameters.box.y - this.parameters.box.height / 2), (current.x - this.parameters.box.x - this.parameters.box.width / 2)); + + var angle = this.parameters.rotation + (pAngle - sAngle) * 180 / Math.PI + this.options.snapToAngle / 2; + + // We have to move the element to the center of the box first and change the rotation afterwards + // because rotation always works around a rotation-center, which is changed when moving the element + // We also set the new rotation center to the center of the box. + this.el.center(this.parameters.box.cx, this.parameters.box.cy).rotate(angle - (angle % this.options.snapToAngle), this.parameters.box.cx, this.parameters.box.cy); + }; + break; + + // Moving one single Point (needed when an element is deepSelected which means you can move every single point of the object) + case 'point': + this.calc = function (diffX, diffY) { + + // Snapping the point to the grid + var snap = this.snapToGrid(diffX, diffY, this.parameters.pointCoords[0], this.parameters.pointCoords[1]); + + // Get the point array + var array = this.el.array().valueOf(); + + // Changing the moved point in the array + array[this.parameters.i][0] = this.parameters.pointCoords[0] + snap[0]; + array[this.parameters.i][1] = this.parameters.pointCoords[1] + snap[1]; + + // And plot the new this.el + this.el.plot(array); + }; + } + + this.el.fire('resizestart', {dx: this.parameters.x, dy: this.parameters.y, event: event}); + // When resizing started, we have to register events for... + // Touches. + SVG.on(window, 'touchmove.resize', function(e) { + _this.update(e || window.event); + }); + SVG.on(window, 'touchend.resize', function() { + _this.done(); + }); + // Mouse. + SVG.on(window, 'mousemove.resize', function (e) { + _this.update(e || window.event); + }); + SVG.on(window, 'mouseup.resize', function () { + _this.done(); + }); + + }; + + // The update-function redraws the element every time the mouse is moving + ResizeHandler.prototype.update = function (event) { + + if (!event) { + if (this.lastUpdateCall) { + this.calc(this.lastUpdateCall[0], this.lastUpdateCall[1]); + } + return; + } + + // Calculate the difference between the mouseposition at start and now + var txPt = this._extractPosition(event); + var p = this.transformPoint(txPt.x, txPt.y); + + var diffX = p.x - this.parameters.p.x, + diffY = p.y - this.parameters.p.y; + + this.lastUpdateCall = [diffX, diffY]; + + // Calculate the new position and height / width of the element + this.calc(diffX, diffY); + + // Emit an event to say we have changed. + this.el.fire('resizing', {dx: diffX, dy: diffY, event: event}); + }; + + // Is called on mouseup. + // Removes the update-function from the mousemove event + ResizeHandler.prototype.done = function () { + this.lastUpdateCall = null; + SVG.off(window, 'mousemove.resize'); + SVG.off(window, 'mouseup.resize'); + SVG.off(window, 'touchmove.resize'); + SVG.off(window, 'touchend.resize'); + this.el.fire('resizedone'); + }; + + // The flag is used to determine whether the resizing is used with a left-Point (first bit) and top-point (second bit) + // In this cases the temp-values are calculated differently + ResizeHandler.prototype.snapToGrid = function (diffX, diffY, flag, pointCoordsY) { + + var temp; + + // If `pointCoordsY` is given, a single Point has to be snapped (deepSelect). That's why we need a different temp-value + if (typeof pointCoordsY !== 'undefined') { + // Note that flag = pointCoordsX in this case + temp = [(flag + diffX) % this.options.snapToGrid, (pointCoordsY + diffY) % this.options.snapToGrid]; + } else { + // We check if the flag is set and if not we set a default-value (both bits set - which means upper-left-edge) + flag = flag == null ? 1 | 1 << 1 : flag; + temp = [(this.parameters.box.x + diffX + (flag & 1 ? 0 : this.parameters.box.width)) % this.options.snapToGrid, (this.parameters.box.y + diffY + (flag & (1 << 1) ? 0 : this.parameters.box.height)) % this.options.snapToGrid]; + } + + if(diffX < 0) { + temp[0] -= this.options.snapToGrid; + } + if(diffY < 0) { + temp[1] -= this.options.snapToGrid; + } + + diffX -= (Math.abs(temp[0]) < this.options.snapToGrid / 2 ? + temp[0] : + temp[0] - (diffX < 0 ? -this.options.snapToGrid : this.options.snapToGrid)); + diffY -= (Math.abs(temp[1]) < this.options.snapToGrid / 2 ? + temp[1] : + temp[1] - (diffY < 0 ? -this.options.snapToGrid : this.options.snapToGrid)); + + return this.constraintToBox(diffX, diffY, flag, pointCoordsY); + + }; + + // keep element within constrained box + ResizeHandler.prototype.constraintToBox = function (diffX, diffY, flag, pointCoordsY) { + //return [diffX, diffY] + var c = this.options.constraint || {}; + var orgX, orgY; + + if (typeof pointCoordsY !== 'undefined') { + orgX = flag; + orgY = pointCoordsY; + } else { + orgX = this.parameters.box.x + (flag & 1 ? 0 : this.parameters.box.width); + orgY = this.parameters.box.y + (flag & (1<<1) ? 0 : this.parameters.box.height); + } + + if (typeof c.minX !== 'undefined' && orgX + diffX < c.minX) { + diffX = c.minX - orgX; + } + + if (typeof c.maxX !== 'undefined' && orgX + diffX > c.maxX) { + diffX = c.maxX - orgX; + } + + if (typeof c.minY !== 'undefined' && orgY + diffY < c.minY) { + diffY = c.minY - orgY; + } + + if (typeof c.maxY !== 'undefined' && orgY + diffY > c.maxY) { + diffY = c.maxY - orgY; + } + + return [diffX, diffY]; + }; + + ResizeHandler.prototype.checkAspectRatio = function (snap, isReverse) { + if (!this.options.saveAspectRatio) { + return snap; + } + + var updatedSnap = snap.slice(); + var aspectRatio = this.parameters.box.width / this.parameters.box.height; + var newW = this.parameters.box.width + snap[0]; + var newH = this.parameters.box.height - snap[1]; + var newAspectRatio = newW / newH; + + if (newAspectRatio < aspectRatio) { + // Height is too big. Adapt it + updatedSnap[1] = newW / aspectRatio - this.parameters.box.height; + isReverse && (updatedSnap[1] = -updatedSnap[1]); + } else if (newAspectRatio > aspectRatio) { + // Width is too big. Adapt it + updatedSnap[0] = this.parameters.box.width - newH * aspectRatio; + isReverse && (updatedSnap[0] = -updatedSnap[0]); + } + + return updatedSnap; + }; + + SVG.extend(SVG.Element, { + // Resize element with mouse + resize: function (options) { + + (this.remember('_resizeHandler') || new ResizeHandler(this)).init(options || {}); + + return this; + + } + + }); + + SVG.Element.prototype.resize.defaults = { + snapToAngle: 0.1, // Specifies the speed the rotation is happening when moving the mouse + snapToGrid: 1, // Snaps to a grid of `snapToGrid` Pixels + constraint: {}, // keep element within constrained box + saveAspectRatio: false // Save aspect ratio when resizing using lt, rt, rb or lb points + }; + }).call(this); }()); diff --git a/resources/images/stores/noon.svg b/resources/images/stores/noon.svg index 5927fc7..fd32d07 100644 --- a/resources/images/stores/noon.svg +++ b/resources/images/stores/noon.svg @@ -1,20 +1,20 @@ - - - - - - - - - - - - + + + + + + + + + + + + From 23c129bcf8a45a52b9af9c33009f50797865dcf6 Mon Sep 17 00:00:00 2001 From: YNWA Fawzy <38886749+Cybrarist@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:01:09 +0400 Subject: [PATCH 3/4] Features: Enhancements: - better handling of which store to initialize - Fixes: - fix update live wasn't reflecting properly on options page. - fix duplicate updates , prices, showing on amazon. - fix coming soon page doesn't show the icon to add - fix out of stock items doesn't show the icon to add Took 1 hour 28 minutes --- Product.js | 54 +++++++++----- Stores/Amazon.js | 110 +++++++++++++++------------- Stores/Argos.js | 2 +- Stores/Ebay.js | 2 +- Stores/Emax.js | 78 ++++++++++++++++++++ Stores/Fnac.js | 74 +++++++++++++++++++ Stores/Noon.js | 114 ++++++++++++++--------------- functions.js | 156 +++++++++++++++++++++++++++++++++++++++- manifest.json | 1 + resources/css/style.css | 8 ++- resources/js/options.js | 16 +++-- 11 files changed, 480 insertions(+), 135 deletions(-) create mode 100644 Stores/Emax.js create mode 100644 Stores/Fnac.js diff --git a/Product.js b/Product.js index 196b165..a51e1b7 100644 --- a/Product.js +++ b/Product.js @@ -4,8 +4,10 @@ class Product { price; rate; number_of_rates; + seller; url; update_product; + in_stock=true; #token; storage_promise; @@ -14,6 +16,11 @@ class Product { this.storage_promise= this.get_storage_data() } + /** + * urls on the server api. + * @returns {string} + */ + update_url () { return `${this.url}/api/products/update` }; @@ -24,18 +31,24 @@ class Product { return `${this.url}/api/products/create` }; + /** + * get the data saved in the browser + * @returns {Promise} + */ + async get_storage_data(){ var result= await browser.storage.sync.get() this.#token= result.token; this.url=result.url; this.update_product=result.update_product; - - console.log(this.#token) } + + async update_server_product(){ await this.storage_promise - if (!self.update_product) + + if (!this.update_product) return fetch(this.update_url() ,{ @@ -71,12 +84,11 @@ class Product { }) }) - .then(response => { + .then((response) => { return response.json(); }) .catch(error => { - // Handle errors - console.log("Error:", error); + // product doesn't exist in the system }); } @@ -107,7 +119,6 @@ class Product { return response.json(); }) .then(data => { - console.log(data); if (data.errors) add_notification_to_page('danger' , 'Something wrong Happened') else @@ -117,11 +128,11 @@ class Product {

${data.link}

` - ) + ) }) .catch(error => { - add_notification_to_page('danger' , 'Something wrong Happened') + add_notification_to_page('danger' , 'Something wrong Happened here') }); } @@ -143,10 +154,12 @@ class Product { product.populate_dom_with_charts() - document.getElementById("submit_discount_form") - .addEventListener("click" , function (){ - product.submit_form() - }) + setTimeout(() => { + document.getElementById("submit_discount_form") + .addEventListener("click" , function (){ + product.submit_form() + }) + }, 2000); product.get_product_data().then(response => { @@ -156,15 +169,24 @@ class Product { } -let previousUrl = ''; + + +let previousUrl = null; const observer = new MutationObserver(function(mutations) { - if (location.href !== previousUrl) { - previousUrl = location.href; + + current_url=new URL(location.href) + + if (current_url.pathname !== previousUrl?.pathname) { + previousUrl = new URL(location.href); product.refresh_dom_all() } }); + const config = {subtree: true, childList: true}; observer.observe(document, config); + +//share the url across all classes +var current_url = new URL(window.location.href); diff --git a/Stores/Amazon.js b/Stores/Amazon.js index a0d28cf..64d4c61 100644 --- a/Stores/Amazon.js +++ b/Stores/Amazon.js @@ -1,69 +1,81 @@ -if (window.location.href.includes('amazon.') ){ +class Amazon extends Product { + constructor() { - class Amazon extends Product { - constructor() { - super(); - } - get_dom_product_details(){ - this.name= document.getElementById("productTitle").textContent.trim() - this.price= document.getElementById("twister-plus-price-data-price").value - this.image= document.querySelector(".imgTagWrapper img").src - this.number_of_rates=document.getElementById("acrCustomerReviewText") - .textContent.split(" ")[0] - .replaceAll(",","") - .replaceAll("." , "") - } + super(); + } + + get_dom_product_details(){ + this.name= document.getElementById("productTitle").textContent.trim() + this.price= document.getElementById("twister-plus-price-data-price")?.value ?? 0 + this.image= document.querySelector(".imgTagWrapper img").src + this.number_of_rates=document.getElementById("acrCustomerReviewText") + ?.textContent.split(" ")[0] + .replaceAll(",","") + .replaceAll("." , "") + .replaceAll("(" , "") + .replaceAll(")" , "") + + console.log(this.number_of_rates) - populate_dom_with_charts() { + } - document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) + populate_dom_with_charts() { - document.body.querySelector("#title_feature_div") - .insertAdjacentHTML('afterend', ``) + document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) - //add the chart and stores - var main_body=document.body.querySelector("#ppd") - main_body.insertAdjacentHTML("afterend" , "
") - main_body.insertAdjacentHTML("afterend" , "
") - } + document.body.querySelector("#title_feature_div") + .insertAdjacentHTML('afterend', ``) - async get_product_data() { + //add the chart and stores + var main_body=document.body.querySelector("#ppd") + main_body.insertAdjacentHTML("afterend" , "
") + main_body.insertAdjacentHTML("afterend" , "
") + } - super.get_product_data().then(data => { + async get_product_data() { + super.get_product_data().then(data => { + if (!data) + return; + + insert_chart_into_dom(data.series) - insert_chart_into_dom(data.series) - //add highest and lowest prices - document.getElementById("buybox").insertAdjacentHTML("afterbegin" , - `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
-
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` + //add highest and lowest prices + document.getElementById("buybox") + .insertAdjacentHTML("afterbegin" , + `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
+
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` ) - //add open in Discount Bandit - document.getElementById("submit.buy-now").insertAdjacentHTML("afterend" , - ` - - Discount Bandit` - ) - //add other stores available. - var stores_elements=document.getElementById("all_stores_cards"); - for (const [store, value] of Object.entries(data.prices)) - if (store != data.current_store_id) - stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) - }) - } + + Discount Bandit + `) + //add other stores available. + var stores_elements=document.getElementById("all_stores_cards"); + + for (const [store, value] of Object.entries(data.prices)) + if (store != data.current_store_id) + stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) + }) } - var product= new Amazon() +} +//todo make product call class name dynamically +if ( current_url.host.includes('amazon.') && (current_url.pathname.includes("/dp/") || current_url.pathname.includes("/gp/product") ) ){ + var product= new Amazon() product.get_storage_data().then(r => {}) } + + diff --git a/Stores/Argos.js b/Stores/Argos.js index b6c8ad0..af9039a 100644 --- a/Stores/Argos.js +++ b/Stores/Argos.js @@ -44,7 +44,7 @@ if (window.location.href.includes('argos.co.uk') ){ document.querySelectorAll("[data-test='product-price-primary']")[0] .insertAdjacentHTML("afterbegin" , `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
-
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` +
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` ) diff --git a/Stores/Ebay.js b/Stores/Ebay.js index 01d70df..d67ba50 100644 --- a/Stores/Ebay.js +++ b/Stores/Ebay.js @@ -40,7 +40,7 @@ if (window.location.href.includes('ebay.') ){ document.querySelector('[data-testid="x-price-primary"]') .insertAdjacentHTML("afterbegin" , `
Lowest Price ${data.prices[data.current_store_id]?.lowest_price.toLocaleString()}
-
Highest Price ${data.prices[data.current_store_id]?.highest_price.toLocaleString()}
` +
Highest Price ${data.prices[data.current_store_id]?.highest_price.toLocaleString()}
` ) //add open in Discount Bandit diff --git a/Stores/Emax.js b/Stores/Emax.js new file mode 100644 index 0000000..0bf7894 --- /dev/null +++ b/Stores/Emax.js @@ -0,0 +1,78 @@ +// class Emax extends Product { +// +// constructor() { +// super(); +// } +// +// get_dom_product_details(){ +// setTimeout(() => { +// this.name=document.getElementsByTagName('title')[0].innerText; +// let meta_elements= document.getElementsByTagName('meta'); +// +// Array.from(meta_elements).forEach((element) => { +// switch (element.getAttribute('property')) { +// case 'og:title': this.name= element.getAttribute('content'); break; +// case 'og:image': this.image= element.getAttribute('content'); break; +// case 'product:price:amount': this.price= element.getAttribute('content'); break; +// case 'product:availability': this.in_stock = element.getAttribute('content') === 'in stock'; break; +// } +// }) +// +// console.log(this.name) +// }, 1000); +// } +// +// populate_dom_with_charts() { +// +// setTimeout(() => { +// document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) +// +// document.body.querySelector("h1").parentElement +// .insertAdjacentHTML('afterend', +// `
+//
`) +// +// var main_body=document.body.querySelector("#prod-shppng_QA") +// main_body.insertAdjacentHTML("afterend" , "
") +// main_body.insertAdjacentHTML("afterend" , "
") +// }, 1000); +// +// } +// +// async get_product_data() { +// +// super.get_product_data().then(data => { +// // +// // insert_chart_into_dom(data.series) +// //add highest and lowest prices +// // document.querySelector(".priceNow[data-qa='div-price-now']").insertAdjacentHTML("beforebegin" , +// // `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
+// //
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` +// // ) +// // +// // //add open in Discount Bandit +// // document.querySelector("[data-qa^='pdp-quantity-']").insertAdjacentHTML("afterend" , +// // ` +// // +// // Discount Bandit` +// // ) +// // //add other stores available. +// // var stores_elements=document.getElementById("all_stores_cards"); +// // for (const [store, value] of Object.entries(data.prices)) +// // if (store != data.current_store_id) +// // stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) +// }) +// } +// +// } +// +// if ( current_url.host.includes('emaxme.') && current_url.pathname.endsWith(".html") ){ +// var product= new Emax() +// product.get_storage_data().then(r => {}) +// } diff --git a/Stores/Fnac.js b/Stores/Fnac.js new file mode 100644 index 0000000..69a836a --- /dev/null +++ b/Stores/Fnac.js @@ -0,0 +1,74 @@ +// class Fnac extends Product { +// constructor() { +// super(); +// } +// +// get_dom_product_details(){ +// this.name= document.querySelector(".f-productHeader__heading").textContent.trim() +// this.price= get_numbers_only_with_comma(document.querySelector(".userPrice")?.textContent.trim() ?? 0).replace(',' ,'.') +// this.image= document.querySelector(".f-productMedias__viewItem--main").src +// +// this.number_of_rates=document.querySelector(".f-star-score") +// ?.textContent +// } +// +// populate_dom_with_charts() { +// +// document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) +// +// document.body.querySelector(".f-productHeader-reviewContainer") +// .insertAdjacentHTML('afterend', ``) +// +// //add the chart and stores +// var main_body=document.body.querySelector(".f-productLabels") +// main_body.insertAdjacentHTML("afterend" , "
") +// main_body.insertAdjacentHTML("afterend" , "
") +// } +// +// async get_product_data() { +// +// super.get_product_data().then(data => { +// if (!data) +// return; +// +// insert_chart_into_dom(data.series) +// +// //add highest and lowest prices +// document.getElementById("buybox") +// .insertAdjacentHTML("afterbegin" , +// `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
+//
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` +// ) +// +// //add open in Discount Bandit +// document.getElementById("submit.buy-now").insertAdjacentHTML("afterend" , +// ` +// +// Discount Bandit +// `) +// +// //add other stores available. +// var stores_elements=document.getElementById("all_stores_cards"); +// +// for (const [store, value] of Object.entries(data.prices)) +// if (store != data.current_store_id) +// stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) +// }) +// } +// +// } +// +// //todo make product call class name dynamically +// if ( current_url.host.includes('fnac.')){ +// +// var product= new Fnac() +// product.get_storage_data().then(r => {}) +// } +// +// diff --git a/Stores/Noon.js b/Stores/Noon.js index 056e8fd..0d82202 100644 --- a/Stores/Noon.js +++ b/Stores/Noon.js @@ -1,72 +1,72 @@ -if (window.location.href.includes('noon.') ){ - class Noon extends Product { +class Noon extends Product { - constructor() { - super(); - } - - get_dom_product_details(){ - - document.querySelectorAll("[type='application/ld+json']").forEach((elem)=>{ - if(elem.textContent.trim().includes('"@type":"Product"')){ - var json_string= JSON.parse(elem.textContent); - this.price=json_string.offers[0].price; - this.image= json_string.image[0]; - this.number_of_rates=json_string.aggregateRating.reviewCount; - } + constructor() { + super(); + } - }) - this.name= document.querySelector("title").textContent.trim().split('|' )[0] - } + get_dom_product_details(){ + document.querySelectorAll("[type='application/ld+json']").forEach((elem)=>{ + if(elem.textContent.trim().includes('"@type":"Product"')){ + var json_string= JSON.parse(elem.textContent); + this.price=json_string.offers[0].price; + this.image= json_string.image[0]; + this.number_of_rates=json_string.aggregateRating.reviewCount; + } - populate_dom_with_charts() { + }) + this.name= document.querySelector("title").textContent.trim().split('|' )[0] + } - document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) + populate_dom_with_charts() { - document.body.querySelector("h1") - .insertAdjacentHTML('afterend', ``) + document.body.insertAdjacentHTML("afterbegin" , `
${get_global_form()}
`) - //add the chart and stores - var main_body=document.body.querySelector(".noGap") - main_body.insertAdjacentHTML("afterend" , "
") - main_body.insertAdjacentHTML("afterend" , "
") - } + document.body.querySelector("h1") + .insertAdjacentHTML('afterend', ``) - async get_product_data() { + //add the chart and stores + var main_body=document.body.querySelector(".noGap") + main_body.insertAdjacentHTML("afterend" , "
") + main_body.insertAdjacentHTML("afterend" , "
") + } - super.get_product_data().then(data => { - // - insert_chart_into_dom(data.series) - //add highest and lowest prices - document.querySelector(".priceNow[data-qa='div-price-now']").insertAdjacentHTML("beforebegin" , - `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
-
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` - ) + async get_product_data() { - //add open in Discount Bandit - document.querySelector("[data-qa^='pdp-quantity-']").insertAdjacentHTML("afterend" , - ` - - Discount Bandit` - ) - //add other stores available. - var stores_elements=document.getElementById("all_stores_cards"); - for (const [store, value] of Object.entries(data.prices)) - if (store != data.current_store_id) - stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) - }) - } + super.get_product_data().then(data => { + // + insert_chart_into_dom(data.series) + //add highest and lowest prices + document.querySelector(".priceNow[data-qa='div-price-now']").insertAdjacentHTML("beforebegin" , + `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
+
Highest Price ${data.prices[data.current_store_id].highest_price.toLocaleString()}
` + ) + //add open in Discount Bandit + document.querySelector("[data-qa^='pdp-quantity-']").insertAdjacentHTML("afterend" , + ` + + Discount Bandit` + ) + //add other stores available. + var stores_elements=document.getElementById("all_stores_cards"); + for (const [store, value] of Object.entries(data.prices)) + if (store != data.current_store_id) + stores_elements.insertAdjacentHTML("afterbegin", product_per_store_price_template(store,value)) + }) } - var product= new Noon() +} +if (current_url.host.includes('noon.') && current_url.pathname.endsWith("/p/") ){ + var product= new Noon() product.get_storage_data().then(r => {}) } + + diff --git a/functions.js b/functions.js index 340561a..f7fc983 100644 --- a/functions.js +++ b/functions.js @@ -108,6 +108,8 @@ function insert_chart_into_dom(series){ function get_global_form(){ + console.log(product) + return`
@@ -145,4 +147,156 @@ function get_global_form(){ function get_numbers_with_dots(string) { return string.replace(/[^0-9.]/g, ''); -} \ No newline at end of file +} +function product_per_store_price_template(store_id, data){ + + var current_store=stores.data[store_id-1]; + + return ` + +
+
+
+
+
+ +
+
${current_store.name}
+
+ +

${current_store.currency} ${data.current_price.toLocaleString() }

+ +
+

+ + ${current_store.currency} ${data.highest_price.toLocaleString()} +

+

+ + ${current_store.currency} ${data.lowest_price.toLocaleString()} +

+
+
Seller${data.seller} +
+
+
+
+
+` +} + +function add_notification_to_page(type , message){ + document.body.insertAdjacentHTML("beforebegin" , ` +
+ ${message} +
+ `) +} + +function get_numbers_only_with_comma(str) { + return str.replace(/[^0-9,]/g, ''); +} + + + + + + + +// +// function insert_chart_into_dom(series){ +// //Generate the apex chart +// var options = { +// chart: { +// type:'area', +// height:300 +// }, +// theme:{ +// palette: "pallet1" +// }, +// series: series, +// xaxis: { +// type:'datetime', +// categories:[ +// 'Jan', +// 'Feb', +// 'Mar', +// 'Apr', +// 'May', +// 'Jun', +// 'Jul', +// 'Aug', +// 'Sep', +// 'Oct', +// 'Nov', +// 'Dec' +// ], +// labels:{ +// style:{ +// fontFamily:'inherit' +// } +// } +// }, +// yaxis: { +// labels: { +// formatter: function (val, index) { +// return val.toLocaleString('en-US'); +// } +// } +// }, +// stroke : { +// curve:'smooth' +// }, +// +// dataLabels: { +// enabled: false, +// }, +// legend: { +// position: 'top' +// } +// } +// +// var chart = new ApexCharts(document.querySelector("#chart"), options); +// chart.render(); +// +// } +// +// function get_global_form(){ +// +// return`
+//
+// +// +//
+//
+// +// +//
+//
+// +// +//
+//
+// +// +//
+//
+// +// +//
+//
+// +// +//
+// +//
+// ` +// +// +// +// } diff --git a/manifest.json b/manifest.json index d216dac..67a3a0b 100644 --- a/manifest.json +++ b/manifest.json @@ -60,6 +60,7 @@ "Stores/Noon.js", + "functions.js" , "stores.js"], "css": ["resources/css/style.css"] } diff --git a/resources/css/style.css b/resources/css/style.css index ac36f2a..d3340eb 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -1,12 +1,13 @@ #gray_layout{ position: fixed; background: #99999969; - z-index: 200; height: 100vh; width: 100vw; display: none; justify-content: center; align-items: center; + top: 0; + z-index: 10000; } .show_flex{ @@ -76,6 +77,7 @@ #discount_bandit_show { cursor: pointer !important; max-width: 50px; + max-height: 50px; } #submit_discount_form{ @@ -143,11 +145,11 @@ .lowest_price{ color: #60a220; } -.max_price{ +.highest_price{ color: #dc3545; } -.lowest_price , .max_price{ +.lowest_price , .highest_price{ width: 100%; display: flex; } diff --git a/resources/js/options.js b/resources/js/options.js index 7ed424c..560d23c 100644 --- a/resources/js/options.js +++ b/resources/js/options.js @@ -1,18 +1,19 @@ function saveOptions(e) { - e.preventDefault(); browser.storage.sync.set({ - url: document.querySelector("#url").value, - token: document.querySelector("#token").value, - update_product: document.querySelector("#update_product").value, + url: document.getElementById("url").value, + token: document.getElementById("token").value, + update_product: document.getElementById("update_product").checked, }); + + alert('data saved successfully') } function restoreOptions() { function setCurrentChoice(result) { - document.querySelector("#url").value = result.url || ""; - document.querySelector("#token").value = result.token || ""; - document.querySelector("#update_live_product").value = result.update_product || ""; + document.getElementById("url").value = result.url || ""; + document.getElementById("token").value = result.token || ""; + document.getElementById("update_product").checked = result.update_product || ""; } function onError(error) { console.log(`Error: ${error}`); @@ -23,4 +24,5 @@ function restoreOptions() { } document.addEventListener("DOMContentLoaded", restoreOptions); + document.querySelector("form").addEventListener("submit", saveOptions); From c9f053e56c986521cce73a673875cbd6a29b59fb Mon Sep 17 00:00:00 2001 From: YNWA Fawzy <38886749+Cybrarist@users.noreply.github.com> Date: Wed, 15 Jan 2025 02:08:30 -0800 Subject: [PATCH 4/4] - fix error when no rate is available for product on amazon - fix amazon products don't submit due to change in ratings format --- Product.js | 2 +- Stores/Amazon.js | 1 - Stores/Argos.js | 3 ++- Stores/Ebay.js | 5 ++--- Stores/Noon.js | 5 ++++- functions.js | 8 +------- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Product.js b/Product.js index a51e1b7..2a0a63e 100644 --- a/Product.js +++ b/Product.js @@ -111,7 +111,7 @@ class Product { favourite: document.getElementById("favourite").checked, stock_available: document.getElementById("stock_available").checked, lowest_within: document.getElementById("lowest_within").value, - number_of_rates:this.number_of_rates, + number_of_rates:this.number_of_rates ?? 0, price:this?.price ?? 0, }) }) diff --git a/Stores/Amazon.js b/Stores/Amazon.js index 64d4c61..0e0b517 100644 --- a/Stores/Amazon.js +++ b/Stores/Amazon.js @@ -16,7 +16,6 @@ class Amazon extends Product { .replaceAll("(" , "") .replaceAll(")" , "") - console.log(this.number_of_rates) } diff --git a/Stores/Argos.js b/Stores/Argos.js index af9039a..f9308d1 100644 --- a/Stores/Argos.js +++ b/Stores/Argos.js @@ -61,7 +61,8 @@ if (window.location.href.includes('argos.co.uk') ){ ) setTimeout(() => { - + if (!data) + return; insert_chart_into_dom(data.series) var stores_elements=document.getElementById("all_stores_cards"); diff --git a/Stores/Ebay.js b/Stores/Ebay.js index d67ba50..b9aab88 100644 --- a/Stores/Ebay.js +++ b/Stores/Ebay.js @@ -31,9 +31,8 @@ if (window.location.href.includes('ebay.') ){ async get_product_data() { super.get_product_data().then(data => { - - - console.log(data) + if (!data) + return; insert_chart_into_dom(data.series) //add highest and lowest prices diff --git a/Stores/Noon.js b/Stores/Noon.js index 0d82202..58ab64c 100644 --- a/Stores/Noon.js +++ b/Stores/Noon.js @@ -37,7 +37,10 @@ class Noon extends Product { super.get_product_data().then(data => { // - insert_chart_into_dom(data.series) + if (!data) + return; + + insert_chart_into_dom(data.series) //add highest and lowest prices document.querySelector(".priceNow[data-qa='div-price-now']").insertAdjacentHTML("beforebegin" , `
Lowest Price ${data.prices[data.current_store_id].lowest_price.toLocaleString()}
diff --git a/functions.js b/functions.js index f7fc983..53aaba7 100644 --- a/functions.js +++ b/functions.js @@ -108,8 +108,6 @@ function insert_chart_into_dom(series){ function get_global_form(){ - console.log(product) - return`
@@ -137,11 +135,7 @@ function get_global_form(){
-` - - - -} +`}