Compare commits
6 Commits
06c548c96a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b4582b098 | |||
| 69452672d4 | |||
| c178a0e695 | |||
| 9a5944bc20 | |||
| 95be6c28ba | |||
| 89c0a3241f |
16
XMLtoGraphQL/readme.md
Normal file
16
XMLtoGraphQL/readme.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# XMLtoGraphQL #
|
||||||
|
Function and xslt skeleton to turn XML products data to same as you would get from graphQL api
|
||||||
|
|
||||||
|
## Warning ##
|
||||||
|
Not every single piece of data is being handled by that function.
|
||||||
|
|
||||||
|
## Usage ##
|
||||||
|
With JS code you only need to put functions in script.js inside your code and then use idmGetGraphQLData to get graphQL'like data
|
||||||
|
```
|
||||||
|
const data = await idmGetGraphQLData("/pl/menu/perfumy/perfumy-meskie-944.html");
|
||||||
|
console.log(data);
|
||||||
|
```
|
||||||
|
|
||||||
|
With XSLT you will get script tag with JSON data inside. You just need to read that in your function
|
||||||
|
|
||||||
|
Created by • **[IdoMods](https://idomods.pl/)** • 2025
|
||||||
328
XMLtoGraphQL/script.js
Normal file
328
XMLtoGraphQL/script.js
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
function idmGetGrossNetPrices({priceNode, name}){
|
||||||
|
return {
|
||||||
|
gross: {
|
||||||
|
value: priceNode?.getAttribute(`${name}`) !== null ? +priceNode?.getAttribute(`${name}`) : undefined,
|
||||||
|
formatted: priceNode?.getAttribute(`${name}_formatted`) ?? undefined,
|
||||||
|
},
|
||||||
|
net: {
|
||||||
|
value: priceNode?.getAttribute(`${name}_net`) !== null ? +priceNode?.getAttribute(`${name}_net`) : undefined,
|
||||||
|
formatted: priceNode?.getAttribute(`${name}_net_formatted`) ?? undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function idmGetPriceFromNode(priceNode){
|
||||||
|
const priceObj = {
|
||||||
|
price: {
|
||||||
|
gross: {
|
||||||
|
value: priceNode?.getAttribute("value") !== null ? +priceNode.getAttribute("value") : undefined,
|
||||||
|
formatted: priceNode?.getAttribute("price_formatted") ?? undefined,
|
||||||
|
},
|
||||||
|
net: {
|
||||||
|
value: priceNode?.getAttribute("price_net") !== null ? +priceNode.getAttribute("price_net") : undefined,
|
||||||
|
formatted: priceNode?.getAttribute("price_net_formatted") ?? undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rebateCodeActive: priceNode?.getAttribute("rebate_code_active") === "y" ? true : false,
|
||||||
|
omnibusPrice: {
|
||||||
|
...idmGetGrossNetPrices({priceNode, name: "omnibus_price"})
|
||||||
|
},
|
||||||
|
// depositPrice: {},
|
||||||
|
// depositPriceUnit: {},
|
||||||
|
// totalDepositPrice: {},
|
||||||
|
// totalDepositPriceUnit: {},
|
||||||
|
omnibusPriceDetails: {
|
||||||
|
// unit: {},
|
||||||
|
youSave: {
|
||||||
|
...idmGetGrossNetPrices({priceNode, name: "omnibus_yousave"})
|
||||||
|
},
|
||||||
|
youSavePercent: priceNode?.getAttribute("price_net") !== null ? +priceNode.getAttribute("omnibus_yousave_percent") : undefined,
|
||||||
|
omnibusPriceIsHigherThanSellingPrice: priceNode?.getAttribute("omnibus_price_is_higher_than_selling_price") === "true" ? true : false,
|
||||||
|
// newPriceEffectiveUntil: {},
|
||||||
|
},
|
||||||
|
tax: {
|
||||||
|
// worth: {},
|
||||||
|
vatPercent: priceNode?.getAttribute("tax") !== null ? +priceNode.getAttribute("tax") : undefined,
|
||||||
|
// vatString: ""
|
||||||
|
},
|
||||||
|
beforeRebate: {
|
||||||
|
...idmGetGrossNetPrices({priceNode, name: "beforerebate"})
|
||||||
|
},
|
||||||
|
// beforeRebateDetails: {
|
||||||
|
// youSave: {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// youSavePercent: "",
|
||||||
|
// unit: {}
|
||||||
|
// },
|
||||||
|
// crossedPrice: {},
|
||||||
|
youSave: {
|
||||||
|
...idmGetGrossNetPrices({priceNode, name: "yousave"})
|
||||||
|
},
|
||||||
|
youSavePercent: priceNode?.getAttribute("yousave_percent") !== null ? +priceNode.getAttribute("yousave_percent") : undefined,
|
||||||
|
// unit: {},
|
||||||
|
max: {
|
||||||
|
...idmGetGrossNetPrices({priceNode, name: "maxprice"})
|
||||||
|
},
|
||||||
|
// maxPriceUnit: {},
|
||||||
|
// suggested: {},
|
||||||
|
unitConvertedPrice: {
|
||||||
|
...idmGetGrossNetPrices({priceNode, name: "unit_converted_price"})
|
||||||
|
},
|
||||||
|
// rebateNumber: {},
|
||||||
|
lastPriceChangeDate: priceNode?.getAttribute("last_price_change_date") ?? undefined,
|
||||||
|
// advancePrice: {},
|
||||||
|
promotionDuration: {
|
||||||
|
promotionTill: {
|
||||||
|
date: {
|
||||||
|
date: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[2] ?? undefined,
|
||||||
|
month: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[1] ?? undefined,
|
||||||
|
year: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[0] ?? undefined,
|
||||||
|
// weekDay: "",
|
||||||
|
// formatted: ""
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
hour: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[0] ?? undefined,
|
||||||
|
minutes: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[1] ?? undefined,
|
||||||
|
seconds: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[2] ?? undefined,
|
||||||
|
},
|
||||||
|
// timestamp: ""
|
||||||
|
},
|
||||||
|
// discountTill: {},
|
||||||
|
// distinguishedTill: {},
|
||||||
|
// specialTill: {},
|
||||||
|
},
|
||||||
|
// subscriptionPrice: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
return priceObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function idmGetTraitsFromNode(node){
|
||||||
|
const awardedParameters = [];
|
||||||
|
|
||||||
|
node.querySelectorAll(":scope > trait").forEach(trait=>{
|
||||||
|
const currParameter = awardedParameters.find(param=>param.id === trait.getAttribute("groupid")) || {
|
||||||
|
id: trait.getAttribute("groupid") ?? undefined,
|
||||||
|
name: trait.getAttribute("groupdescription") ?? undefined,
|
||||||
|
// description: "",
|
||||||
|
values: [],
|
||||||
|
// contextValue: "",
|
||||||
|
// search: {
|
||||||
|
// icon: "",
|
||||||
|
// },
|
||||||
|
}
|
||||||
|
|
||||||
|
currParameter.values.push({
|
||||||
|
id: trait.getAttribute("traitid") ?? undefined,
|
||||||
|
name: trait.getAttribute("traitid") ?? undefined,
|
||||||
|
link: trait.getAttribute("link") ?? undefined,
|
||||||
|
// description: "",
|
||||||
|
// search: {
|
||||||
|
// icon: ""
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if(currParameter.values.length === 1) awardedParameters.push(currParameter);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return awardedParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function idmGetGraphQLData(link){
|
||||||
|
try{
|
||||||
|
const res = await fetch(`${link}${link.includes("?") ? "&getProductXML=t" : "?getProductXML=t"}`);
|
||||||
|
const str = await res.text();
|
||||||
|
const xml = new window.DOMParser().parseFromString(str, "text/xml");
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const allProducts = xml.querySelectorAll("product");
|
||||||
|
if(allProducts.length === 0) throw new Error("Nie znaleziono produktów");
|
||||||
|
|
||||||
|
const data = [];
|
||||||
|
|
||||||
|
|
||||||
|
allProducts.forEach(prod=>{
|
||||||
|
|
||||||
|
// NODES
|
||||||
|
const priceNode = prod.querySelector(":scope >price");
|
||||||
|
const firmNode = prod.querySelector(":scope > firm");
|
||||||
|
const categoryNode = prod.querySelector(":scope > category");
|
||||||
|
const seriesNode = prod.querySelector(":scope > series");
|
||||||
|
const commentsNode = prod.querySelector(":scope > comments");
|
||||||
|
const sizesNode = prod.querySelector(":scope > sizes");
|
||||||
|
const versionsNode = prod.querySelector(":scope > versions");
|
||||||
|
|
||||||
|
// STREFY
|
||||||
|
const zones = [];
|
||||||
|
|
||||||
|
if(prod.getAttribute("promo") === "yes") zones.push("promotion");
|
||||||
|
if(prod.getAttribute("discount") === "yes") zones.push("discount");
|
||||||
|
if(prod.getAttribute("distinguished") === "yes") zones.push("distinguished");
|
||||||
|
if(prod.getAttribute("special") === "yes") zones.push("special");
|
||||||
|
if(prod.getAttribute("new") === "yes") zones.push("new");
|
||||||
|
if(prod.getAttribute("bestseller") === "yes") zones.push("bestseller");
|
||||||
|
if(prod.getAttribute("subscription") === "yes") zones.push("subscription");
|
||||||
|
|
||||||
|
// ZDJĘCIA
|
||||||
|
const enclosuresImages = [];
|
||||||
|
prod.querySelectorAll(":scope > enclosures > images > enclosure").forEach(img=>{
|
||||||
|
enclosuresImages.push({
|
||||||
|
position: img.getAttribute("position") ?? undefined,
|
||||||
|
type: img.getAttribute("type") ?? undefined,
|
||||||
|
typeSecond: img.getAttribute("type_second") ?? undefined,
|
||||||
|
url: img.getAttribute("url") ?? undefined,
|
||||||
|
urlSecond: img.getAttribute("url_second") ?? undefined,
|
||||||
|
width: img.getAttribute("width") ?? undefined,
|
||||||
|
height: img.getAttribute("height") ?? undefined,
|
||||||
|
iconUrl: img.getAttribute("icon") ?? undefined,
|
||||||
|
iconUrlSecond: img.getAttribute("icon_second") ?? undefined,
|
||||||
|
iconWidth: img.getAttribute("icon_width") ?? undefined,
|
||||||
|
iconHeight: img.getAttribute("icon_height") ?? undefined,
|
||||||
|
mediumUrl: img.getAttribute("medium") ?? undefined,
|
||||||
|
mediumUrlSecond: img.getAttribute("medium_second") ?? undefined,
|
||||||
|
mediumWidth: img.getAttribute("medium_width") ?? undefined,
|
||||||
|
mediumHeight: img.getAttribute("medium_height") ?? undefined,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// SIZES
|
||||||
|
const sizes = [];
|
||||||
|
sizesNode.querySelectorAll(":scope > size").forEach(size=>{
|
||||||
|
const availabilityNode = size.querySelector(":scope > availability");
|
||||||
|
const shippingTimeNode = availabilityNode.querySelector(":scope > shipping_time");
|
||||||
|
const sizePriceNode = size.querySelector(":scope > price");
|
||||||
|
const weightNode = size.querySelector(":scope > weight");
|
||||||
|
|
||||||
|
sizes.push({
|
||||||
|
id: size?.getAttribute("type") ?? undefined,
|
||||||
|
name: size?.getAttribute("name") ?? undefined,
|
||||||
|
code: size?.getAttribute("code") ?? undefined,
|
||||||
|
codeProducer: size?.getAttribute("code_producer") ?? undefined,
|
||||||
|
codeExtern: size?.getAttribute("code_extern") ?? undefined,
|
||||||
|
amount: size?.getAttribute("amount") !== null ? +size.getAttribute("amount") : undefined,
|
||||||
|
amount_mo: size?.getAttribute("amount_mo") !== null ? +size.getAttribute("amount_mo") : undefined,
|
||||||
|
amount_mw: size?.getAttribute("amount_mw") !== null ? +size.getAttribute("amount_mw") : undefined,
|
||||||
|
amount_mp: size?.getAttribute("amount_mp") !== null ? +size.getAttribute("amount_mp") : undefined,
|
||||||
|
weight: weightNode?.getAttribute("g") !== null ? +weightNode.getAttribute("g") : undefined,
|
||||||
|
availability: {
|
||||||
|
visible: availabilityNode?.getAttribute("visible") === "y" ? true : false,
|
||||||
|
description: availabilityNode?.getAttribute("status_description") ?? undefined,
|
||||||
|
status: availabilityNode?.getAttribute("status") ?? undefined,
|
||||||
|
icon: availabilityNode?.getAttribute("status_gfx") ?? undefined,
|
||||||
|
deliveryDate: shippingTimeNode?.getAttribute("today") === "true" ? `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate().toString().padStart(2, 0)}` : availabilityNode?.getAttribute("delivery_date"),
|
||||||
|
minimumStockOfProduct: availabilityNode?.getAttribute("minimum_stock_of_product") ?? undefined,
|
||||||
|
// descriptionTel: "",
|
||||||
|
// iconTel: "",
|
||||||
|
},
|
||||||
|
shipping: {
|
||||||
|
today: shippingTimeNode?.getAttribute("today") === "true" ? true : false,
|
||||||
|
},
|
||||||
|
price: idmGetPriceFromNode(sizePriceNode),
|
||||||
|
// amountWholesale: "",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// VERSIONS
|
||||||
|
const versions = [];
|
||||||
|
versionsNode.querySelectorAll(":scope > version").forEach(ver=>{
|
||||||
|
versions.push({
|
||||||
|
id: ver?.getAttribute("id") !== null ? +ver?.getAttribute("id") : undefined,
|
||||||
|
name: ver?.getAttribute("name") ?? undefined,
|
||||||
|
icon: ver?.getAttribute("gfx") ?? undefined,
|
||||||
|
iconSecond: ver?.getAttribute("gfx_second") ?? undefined,
|
||||||
|
iconSmall: ver?.getAttribute("gfx_small") ?? undefined,
|
||||||
|
iconSmallSecond: ver?.getAttribute("gfx_small_second") ?? undefined,
|
||||||
|
productIcon: ver?.getAttribute("icon") ?? undefined,
|
||||||
|
productIconSecond: ver?.getAttribute("icon_second") ?? undefined,
|
||||||
|
productIconSmall: ver?.getAttribute("icon_small") ?? undefined,
|
||||||
|
productIconSmallSecond: ver?.getAttribute("icon_small_second") ?? undefined,
|
||||||
|
link: ver?.getAttribute("link") ?? undefined,
|
||||||
|
// parameterValues: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
const typeParts = prod.getAttribute("product_type");
|
||||||
|
data.push({
|
||||||
|
id: prod?.getAttribute("id") !== null ? +prod?.getAttribute("id") : undefined,
|
||||||
|
type: typeParts[1] === "item" ? typeParts[0] : typeParts[1],
|
||||||
|
code: prod?.getAttribute("code") ?? undefined,
|
||||||
|
name: prod.querySelector(":scope > name")?.textContent ?? undefined,
|
||||||
|
versionName: prod.querySelector(":scope > versions")?.getAttribute("name") ?? undefined,
|
||||||
|
description: prod.querySelector(":scope > description")?.textContent ?? undefined,
|
||||||
|
// longDescription: prod.querySelector("vlongdescription")?.textContent ?? undefined,
|
||||||
|
// longDescriptionSections: "",
|
||||||
|
link: prod?.getAttribute("link"),
|
||||||
|
zones,
|
||||||
|
icon: prod.querySelector(":scope > icon")?.textContent ?? undefined,
|
||||||
|
iconSecond: prod.querySelector(":scope > icon_second")?.textContent ?? undefined,
|
||||||
|
iconSmall: prod.querySelector(":scope > icon_small")?.textContent ?? undefined,
|
||||||
|
iconSmallSecond: prod.querySelector(":scope > icon_small_second")?.textContent ?? undefined,
|
||||||
|
price: idmGetPriceFromNode(priceNode),
|
||||||
|
unit: {
|
||||||
|
id: sizesNode?.getAttribute("unit_id") ?? undefined,
|
||||||
|
// name: "",
|
||||||
|
singular: sizesNode?.getAttribute("unit_single") !== null ? +sizesNode?.getAttribute("unit_single") : undefined,
|
||||||
|
plural: sizesNode?.getAttribute("unit_plural") ?? undefined,
|
||||||
|
fraction: sizesNode?.getAttribute("unit_fraction") ?? undefined,
|
||||||
|
sellBy: sizesNode?.getAttribute("unit_sellby") !== null ? +sizesNode?.getAttribute("unit_sellby") : undefined,
|
||||||
|
precision: sizesNode?.getAttribute("unit_precision") !== null ? +sizesNode?.getAttribute("unit_precision") : undefined,
|
||||||
|
unitConvertedFormat: sizesNode?.getAttribute("unit_converted_format") ?? undefined,
|
||||||
|
},
|
||||||
|
producer: {
|
||||||
|
id: firmNode?.getAttribute("id") !== null ? +firmNode?.getAttribute("id") : undefined,
|
||||||
|
name: firmNode?.getAttribute("name") ?? undefined,
|
||||||
|
link: firmNode?.getAttribute("productslink") ?? undefined,
|
||||||
|
searchIcons: {
|
||||||
|
icon: firmNode?.getAttribute("icon") ?? undefined,
|
||||||
|
},
|
||||||
|
// projectorIcons: {},
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
id: categoryNode?.getAttribute("id") !== null ? +categoryNode?.getAttribute("id") : undefined,
|
||||||
|
name: categoryNode?.getAttribute("name") ?? undefined,
|
||||||
|
link: categoryNode?.getAttribute("productslink") ?? undefined
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
id: versionsNode?.getAttribute("id") !== null ? +versionsNode?.getAttribute("id") : undefined,
|
||||||
|
name: versionsNode?.getAttribute("name") ?? undefined,
|
||||||
|
displayAll: versionsNode?.getAttribute("display_all") === "true" ? true : false,
|
||||||
|
link: versionsNode?.getAttribute("link") ?? undefined,
|
||||||
|
versions,
|
||||||
|
groupParameters: idmGetTraitsFromNode(versionsNode.querySelector(":scope > groupParameters"))
|
||||||
|
},
|
||||||
|
opinion: {
|
||||||
|
rating: commentsNode?.getAttribute("avg") !== null ? +commentsNode?.getAttribute("avg") : undefined,
|
||||||
|
count: commentsNode?.getAttribute("count") !== null ? +commentsNode?.getAttribute("count") : undefined,
|
||||||
|
link: commentsNode?.getAttribute("link") ?? undefined
|
||||||
|
},
|
||||||
|
enclosuresImages,
|
||||||
|
// enclosuresAttachments: [],
|
||||||
|
series: {
|
||||||
|
id: seriesNode?.getAttribute("id") !== null ? +seriesNode?.getAttribute("id") : undefined,
|
||||||
|
name: seriesNode?.getAttribute("name") ?? undefined,
|
||||||
|
link: seriesNode?.getAttribute("link") ?? undefined
|
||||||
|
},
|
||||||
|
awardedParameters: idmGetTraitsFromNode(prod.querySelector(":scope > traits")),
|
||||||
|
// parameteresWithContext: [],
|
||||||
|
sizes,
|
||||||
|
points: priceNode?.getAttribute("points") !== null ? +priceNode?.getAttribute("points") : undefined,
|
||||||
|
pointsReceive: priceNode?.getAttribute("points_recive") !== null ? +priceNode?.getAttribute("points_recive") : undefined,
|
||||||
|
// subscription: {},
|
||||||
|
// bundled: [],
|
||||||
|
// responsibleEntity: {},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}catch(err){
|
||||||
|
console.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const testData = await idmGetGraphQLData("/pl/menu/perfumy/perfumy-meskie-944.html");
|
||||||
|
// console.log(testData);
|
||||||
126
XMLtoGraphQL/stary.xslt
Normal file
126
XMLtoGraphQL/stary.xslt
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<script type="application/json" id="blogRecommendedProducts">
|
||||||
|
[
|
||||||
|
<iaixsl:for-each select="page/cms_products/*">
|
||||||
|
{
|
||||||
|
"id": <iaixsl:value-of select="@id"/>,
|
||||||
|
"type": "<iaixsl:choose><iaixsl:when test="@product_type = 'product_item'">product</iaixsl:when><iaixsl:otherwise><iaixsl:value-of select="substring-after(@product_type, '_')"/>",</iaixsl:otherwise></iaixsl:choose>",
|
||||||
|
"code": "<iaixsl:value-of select="@code"/>",
|
||||||
|
"name": "<iaixsl:value-of select="name"/>",
|
||||||
|
"versionName": "<iaixsl:value-of select="@version_name"/>",
|
||||||
|
"description": "<iaixsl:value-of select="cleardescription"/>",
|
||||||
|
"link": "<iaixsl:value-of select="@link"/>",
|
||||||
|
"zones": [
|
||||||
|
<iaixsl:if test="@promo = 'true'">"promotion"<iaixsl:if test="@bestseller = 'true' or @new = 'true'">,</iaixsl:if></iaixsl:if>
|
||||||
|
<iaixsl:if test="@bestseller = 'true'">"bestseller"<iaixsl:if test="@new = 'true'">,</iaixsl:if></iaixsl:if>
|
||||||
|
<iaixsl:if test="@new = 'true'">"new"</iaixsl:if>
|
||||||
|
],
|
||||||
|
"icon": "<iaixsl:value-of select="icon"/>",
|
||||||
|
"iconSmall": "<iaixsl:value-of select="icon_small"/>",
|
||||||
|
"points": <iaixsl:choose><iaixsl:when test="price/@points > 0"><iaixsl:value-of select="price/@points"/></iaixsl:when><iaixsl:otherwise>0</iaixsl:otherwise></iaixsl:choose>,
|
||||||
|
"price": {
|
||||||
|
<iaixsl:if test="/shop/rebatecode">"rebateCodeActive": true,</iaixsl:if>
|
||||||
|
"price": {
|
||||||
|
"gross": {
|
||||||
|
"value": <iaixsl:value-of select="price/@value"/>,
|
||||||
|
"formatted": "<iaixsl:value-of select="price/@price_formatted"/>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"omnibusPrice": {
|
||||||
|
"gross": <iaixsl:choose><iaixsl:when test="price/@omnibus_price">{
|
||||||
|
"value": <iaixsl:value-of select="price/@omnibus_price"/>,
|
||||||
|
"formatted": "<iaixsl:value-of select="price/@omnibus_price_formatted"/>"
|
||||||
|
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
|
||||||
|
},
|
||||||
|
"omnibusPriceDetails": {
|
||||||
|
<iaixsl:if test="price/@omnibus_yousave_percent">
|
||||||
|
"youSavePercent": <iaixsl:value-of select="price/@omnibus_yousave_percent"/>,
|
||||||
|
"omnibusPriceIsHigherThanSellingPrice": <iaixsl:value-of select="price/@omnibus_price_is_higher_than_selling_price"/>
|
||||||
|
</iaixsl:if>
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"gross": <iaixsl:choose><iaixsl:when test="price/@maxprice">{
|
||||||
|
"value": <iaixsl:value-of select="price/@maxprice"/>,
|
||||||
|
"formatted": "<iaixsl:value-of select="price/@maxprice_formatted"/>"
|
||||||
|
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"gross": <iaixsl:choose><iaixsl:when test="price/@price_unit">{
|
||||||
|
"value": <iaixsl:value-of select="price/@price_unit"/>,
|
||||||
|
"formatted": "<iaixsl:value-of select="price/@price_unit_formatted"/>"
|
||||||
|
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
|
||||||
|
},
|
||||||
|
"unitConvertedPrice": {
|
||||||
|
"gross": <iaixsl:choose><iaixsl:when test="price/@unit_converted_price">{
|
||||||
|
"value": <iaixsl:value-of select="price/@unit_converted_price"/>,
|
||||||
|
"formatted": "<iaixsl:value-of select="price/@unit_converted_price_formatted"/>"
|
||||||
|
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
|
||||||
|
},
|
||||||
|
<iaixsl:if test="price/@yousave_percent">"youSavePercent": <iaixsl:value-of select="price/@yousave_percent"/>,</iaixsl:if>
|
||||||
|
"beforeRebate": {
|
||||||
|
"gross": <iaixsl:choose><iaixsl:when test="price/@beforerebate">{
|
||||||
|
"value": <iaixsl:value-of select="price/@beforerebate"/>,
|
||||||
|
"formatted": "<iaixsl:value-of select="price/@beforerebate_formatted"/>"
|
||||||
|
}</iaixsl:when><iaixsl:otherwise>null</iaixsl:otherwise></iaixsl:choose>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"opinion": {
|
||||||
|
"rating": 0,
|
||||||
|
"count": 0
|
||||||
|
},
|
||||||
|
"category": {
|
||||||
|
"name": "<iaixsl:value-of select="category/@name"/>"
|
||||||
|
},
|
||||||
|
"awardedParameters": [
|
||||||
|
<iaixsl:for-each select="traits/trait[not(@groupid = preceding-sibling::trait/@groupid)]">
|
||||||
|
{
|
||||||
|
"id": "<iaixsl:value-of select="@groupid"/>",
|
||||||
|
"name": "<iaixsl:value-of select="@groupdescription"/>",
|
||||||
|
"description": "<iaixsl:value-of select="description"/>",
|
||||||
|
"values": [
|
||||||
|
<iaixsl:for-each select="../trait[@groupid = current()/@groupid]">
|
||||||
|
{
|
||||||
|
"id": "<iaixsl:value-of select="@traitid"/>",
|
||||||
|
"name": "<iaixsl:value-of select="@traitdescription"/>"
|
||||||
|
}<iaixsl:if test="last() != position()">,</iaixsl:if>
|
||||||
|
</iaixsl:for-each>
|
||||||
|
]
|
||||||
|
}<iaixsl:if test="last() != position()">,</iaixsl:if>
|
||||||
|
</iaixsl:for-each>
|
||||||
|
],
|
||||||
|
"sizes": [
|
||||||
|
<iaixsl:for-each select="sizes/size">
|
||||||
|
{
|
||||||
|
"id": "<iaixsl:value-of select="@type"/>",
|
||||||
|
"amount": <iaixsl:value-of select="@amount"/>
|
||||||
|
}<iaixsl:if test="last() != position()">,</iaixsl:if>
|
||||||
|
</iaixsl:for-each>
|
||||||
|
],
|
||||||
|
"enclosuresImages": [
|
||||||
|
<iaixsl:for-each select="enclosures/images/enclosure">
|
||||||
|
{
|
||||||
|
"position": <iaixsl:value-of select="@position"/>,
|
||||||
|
"type": "<iaixsl:value-of select="@type"/>",
|
||||||
|
"url": "<iaixsl:value-of select="@url"/>",
|
||||||
|
"width": <iaixsl:value-of select="@width"/>,
|
||||||
|
"height": <iaixsl:value-of select="@height"/>,
|
||||||
|
"iconUrl": "<iaixsl:value-of select="@icon"/>",
|
||||||
|
"iconWidth": <iaixsl:value-of select="@icon_width"/>,
|
||||||
|
"iconHeight": <iaixsl:value-of select="@icon_height"/>,
|
||||||
|
"mediumUrl": "<iaixsl:value-of select="@medium"/>",
|
||||||
|
"mediumWidth": <iaixsl:value-of select="@medium_width"/>,
|
||||||
|
"mediumHeight": <iaixsl:value-of select="@medium_height"/>
|
||||||
|
}<iaixsl:if test="last() != position()">,</iaixsl:if>
|
||||||
|
</iaixsl:for-each>
|
||||||
|
],
|
||||||
|
"producer": {
|
||||||
|
"id": "<iaixsl:value-of select="substring-before(substring-after(firm/@icon, '/gfx/projector/'), '_1.jpg')"/>",
|
||||||
|
"name": "<iaixsl:value-of select="firm/@name"/>",
|
||||||
|
"link": "<iaixsl:value-of select="firm/@productslink"/>",
|
||||||
|
"searchIcons": {
|
||||||
|
"icon": "<iaixsl:value-of select="firm/@icon"/>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}<iaixsl:if test="last() != position()">,</iaixsl:if>
|
||||||
|
</iaixsl:for-each>
|
||||||
|
]
|
||||||
|
</script>
|
||||||
5
component-opening/index.xslt
Normal file
5
component-opening/index.xslt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!-- Open in some component -->
|
||||||
|
<iaixsl:text disable-output-escaping="yes"><div class="product-info"></iaixsl:text>
|
||||||
|
|
||||||
|
<!-- And close in another one -->
|
||||||
|
<iaixsl:text disable-output-escaping="yes"></div></iaixsl:text>
|
||||||
7
devTrue/README.md
Normal file
7
devTrue/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# DEV=TRUE #
|
||||||
|
It's a short code to make your code safe to check on production, without people visiting the site seeing it
|
||||||
|
|
||||||
|
## USE ##
|
||||||
|
Put code, or function call inside dev=true code, and then you can check it by adding ?dev=true to your url.(&dev=true if there already is ? in your url)
|
||||||
|
|
||||||
|
Created by • **[IdoMods](https://idomods.pl/)** • 2025
|
||||||
9
devTrue/dev.js
Normal file
9
devTrue/dev.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
const queryString = window.location.search;
|
||||||
|
const urlParams = new URLSearchParams(queryString);
|
||||||
|
const dev = urlParams.get('dev')
|
||||||
|
if(dev == 'true'){
|
||||||
|
// INSERT YOUR CODE HERE
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
24
swiper-scrollbar/README.md
Normal file
24
swiper-scrollbar/README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Swiper Scrollbar #
|
||||||
|
This is scrollbar function made for idosell swiper.
|
||||||
|
|
||||||
|
### How to use ###
|
||||||
|
To use it You need to inject the code right after you create Swiper.
|
||||||
|
|
||||||
|
new IdmSwiperProgress(swiperFn, swiperSelector);
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
|
||||||
|
// Instalacja slidera dla instagrama
|
||||||
|
const setupSliderResult = await slider.setupSlider({
|
||||||
|
element: sliderWrapper,
|
||||||
|
});
|
||||||
|
|
||||||
|
new IdmSwiperProgress(setupSliderResult, "#instagram .swiper");
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Created by **[IdoMods](https://idomods.pl/)** 2025
|
||||||
108
swiper-scrollbar/scrollbar.js
Normal file
108
swiper-scrollbar/scrollbar.js
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
==============================================================
|
||||||
|
*/
|
||||||
|
// SWIPER PASEK
|
||||||
|
class IdmSwiperProgress {
|
||||||
|
constructor(swiper, selector) {
|
||||||
|
this.swiper = swiper?.slider?.slider ?? swiper?.slider ?? swiper;
|
||||||
|
this.selector = selector;
|
||||||
|
this.scrollbarEl = null;
|
||||||
|
this.progressEl = null;
|
||||||
|
this.isDragging = false;
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
const el = document.querySelector(this.selector);
|
||||||
|
if (!el || el.querySelector(".idm-scrollbar")) return;
|
||||||
|
|
||||||
|
el.insertAdjacentHTML(
|
||||||
|
"beforeend",
|
||||||
|
`<div class="idm-scrollbar swiper-scrollbar"><div class="idm-progress"></div></div>`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.scrollbarEl = el.querySelector(".idm-scrollbar");
|
||||||
|
this.progressEl = this.scrollbarEl.querySelector(".idm-progress");
|
||||||
|
|
||||||
|
this.updateBarWidth();
|
||||||
|
this.addDragTracking();
|
||||||
|
|
||||||
|
this.swiper.on("progress", () => this.updateProgress());
|
||||||
|
this.swiper.on("breakpoint", () => {this.updateBarWidth()});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBarWidth() {
|
||||||
|
const { slidesPerGroup, slidesPerView } = this.swiper.params;
|
||||||
|
const totalSlides = this.swiper.slides.length;
|
||||||
|
|
||||||
|
const progressWidth = 100 / (((totalSlides - slidesPerView) / slidesPerGroup) + 1);
|
||||||
|
this.progressEl.style.width = `${progressWidth}%`;
|
||||||
|
|
||||||
|
if (progressWidth >= 100 || progressWidth <= 0) this.scrollbarEl.style.display = "none";
|
||||||
|
else this.scrollbarEl.style.display = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress() {
|
||||||
|
const progress = this.swiper.progress;
|
||||||
|
const { slidesPerGroup, slidesPerView } = this.swiper.params;
|
||||||
|
const totalSlides = this.swiper.slides.length;
|
||||||
|
|
||||||
|
const progressWidth = 100 / (((totalSlides - slidesPerView) / slidesPerGroup) + 1);
|
||||||
|
const newLeft = (100 - progressWidth) * progress;
|
||||||
|
this.progressEl.style.left = `${Math.min(
|
||||||
|
100 - progressWidth,
|
||||||
|
Math.max(0, newLeft)
|
||||||
|
)}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDragTracking() {
|
||||||
|
const handle = this.progressEl;
|
||||||
|
let grabOffset = 0;
|
||||||
|
let scrollbarWidth = 0;
|
||||||
|
let handleWidth = 0;
|
||||||
|
|
||||||
|
const startDrag = (e) => {
|
||||||
|
this.isDragging = true;
|
||||||
|
this.scrollbarEl.classList.add("--drag-start");
|
||||||
|
|
||||||
|
const rect = this.scrollbarEl.getBoundingClientRect();
|
||||||
|
const handleRect = handle.getBoundingClientRect();
|
||||||
|
scrollbarWidth = rect.width;
|
||||||
|
handleWidth = handleRect.width;
|
||||||
|
|
||||||
|
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||||
|
grabOffset = clientX - handleRect.left;
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", handleDrag);
|
||||||
|
document.addEventListener("mouseup", stopDrag);
|
||||||
|
document.addEventListener("touchmove", handleDrag);
|
||||||
|
document.addEventListener("touchend", stopDrag);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrag = (e) => {
|
||||||
|
if (!this.isDragging) return;
|
||||||
|
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||||
|
const rect = this.scrollbarEl.getBoundingClientRect();
|
||||||
|
let newLeftPx = clientX - rect.left - grabOffset;
|
||||||
|
const maxLeft = scrollbarWidth - handleWidth;
|
||||||
|
newLeftPx = Math.max(0, Math.min(maxLeft, newLeftPx));
|
||||||
|
const progress = newLeftPx / maxLeft;
|
||||||
|
this.swiper.setProgress(progress, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopDrag = () => {
|
||||||
|
if (!this.isDragging) return;
|
||||||
|
this.isDragging = false;
|
||||||
|
document.removeEventListener("mousemove", handleDrag);
|
||||||
|
document.removeEventListener("mouseup", stopDrag);
|
||||||
|
document.removeEventListener("touchmove", handleDrag);
|
||||||
|
document.removeEventListener("touchend", stopDrag);
|
||||||
|
this.scrollbarEl.classList.remove("--drag-start");
|
||||||
|
this.swiper.slideReset(400);
|
||||||
|
};
|
||||||
|
|
||||||
|
handle.addEventListener("mousedown", startDrag);
|
||||||
|
handle.addEventListener("touchstart", startDrag);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
swiper-scrollbar/style.css
Normal file
21
swiper-scrollbar/style.css
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/* SWIPER PROGRESS */
|
||||||
|
.idm-scrollbar{
|
||||||
|
flex: 1;
|
||||||
|
height: 2px!important;
|
||||||
|
position: relative!important;
|
||||||
|
margin-top: 2rem;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
.idm-scrollbar.--drag-start .idm-progress{
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
.idm-progress{
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
height: calc(100% + 2px);
|
||||||
|
background-color: #0d0d0d;
|
||||||
|
left: 0;
|
||||||
|
top: -1px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
4
tooltip/index.html
Normal file
4
tooltip/index.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<span class="idm_tooltip">
|
||||||
|
<span class="idm_tooltip__info_icon"></span>
|
||||||
|
<p class="idm_tooltip__content --one-line">Najniższa cena z 30 dni przed obniżką</p>
|
||||||
|
</span>
|
||||||
8
tooltip/readme.md
Normal file
8
tooltip/readme.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Customowy tooltip #
|
||||||
|
Functions and styles for custom tooltip
|
||||||
|
|
||||||
|
## Usage ##
|
||||||
|
Put JS code and CSS inside your tooltip component. Then you only need to put div with right structure like with index.html
|
||||||
|
|
||||||
|
|
||||||
|
Created by • **[IdoMods](https://idomods.pl/)** • 2025
|
||||||
43
tooltip/style.css
Normal file
43
tooltip/style.css
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
.idm_tooltip{
|
||||||
|
--tooltip-background: #111;
|
||||||
|
--tooltip-border: #999;
|
||||||
|
--tooltip-color: #fff;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
&__info_icon{
|
||||||
|
color: var(--tooltip-border);
|
||||||
|
display: inline-block;
|
||||||
|
&:before{
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
width: 1.2rem;
|
||||||
|
height: 1.2rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: bottom;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' class='idm_tooltip__info_icon' viewBox='0 0 16 16'%3E%3Cpath d='M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,14.667A6.667,6.667,0,1,1,14.667,8,6.667,6.667,0,0,1,8,14.667Z' fill='currentColor'%3E%3C/path%3E%3Cpath d='M11.333,10h-.667a.667.667,0,1,0,0,1.333h.667v4a.667.667,0,0,0,1.333,0v-4A1.333,1.333,0,0,0,11.333,10Z' transform='translate(-3.333 -3.333)' fill='currentColor'%3E%3C/path%3E%3Ccircle cx='1' cy='1' r='1' transform='translate(7 3.333)' fill='currentColor'%3E%3C/circle%3E%3C/svg%3E");
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__content{
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: var(--tooltip-background);
|
||||||
|
color: var(--tooltip-color);
|
||||||
|
border: 1px solid var(--tooltip-border);
|
||||||
|
bottom: 150%;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
@media @tablet{
|
||||||
|
&.--one-line{
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.--visible{
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
tooltip/tooltip.js
Normal file
59
tooltip/tooltip.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
function idmShowTooltip(tooltipEl){
|
||||||
|
const tooltipContentEl = tooltipEl.querySelector(".idm_tooltip__content");
|
||||||
|
if(!tooltipContentEl) return;
|
||||||
|
|
||||||
|
tooltipContentEl.classList.add("--visible");
|
||||||
|
|
||||||
|
// Logika pokazywania się i chowania tooltipa
|
||||||
|
let timeoutVar;
|
||||||
|
|
||||||
|
function onMouseLeave() {
|
||||||
|
timeoutVar = idmHideTooltipTimer(tooltipEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseEnter() {
|
||||||
|
clearTimeout(timeoutVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScroll() {
|
||||||
|
idmHideTooltip(tooltipEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store references for later removal
|
||||||
|
tooltipEl._onMouseLeave = onMouseLeave;
|
||||||
|
tooltipEl._onMouseEnter = onMouseEnter;
|
||||||
|
tooltipEl._onScroll = onScroll;
|
||||||
|
|
||||||
|
tooltipEl.addEventListener("mouseleave", onMouseLeave);
|
||||||
|
tooltipEl.addEventListener("mouseenter", onMouseEnter);
|
||||||
|
document.addEventListener("scroll", onScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
function idmHideTooltipTimer(tooltipEl){
|
||||||
|
return setTimeout(() => idmHideTooltip(tooltipEl), 1500);
|
||||||
|
}
|
||||||
|
function idmHideTooltip(tooltipEl){
|
||||||
|
const tooltipContentEl = tooltipEl.querySelector(".idm_tooltip__content");
|
||||||
|
if (!tooltipContentEl) return;
|
||||||
|
|
||||||
|
tooltipContentEl.classList.remove("--visible");
|
||||||
|
|
||||||
|
tooltipEl.removeEventListener("mouseleave", tooltipEl._onMouseLeave);
|
||||||
|
tooltipEl.removeEventListener("mouseenter", tooltipEl._onMouseEnter);
|
||||||
|
document.removeEventListener("scroll", tooltipEl._onScroll);
|
||||||
|
|
||||||
|
delete tooltipEl._onMouseLeave;
|
||||||
|
delete tooltipEl._onMouseEnter;
|
||||||
|
delete tooltipEl._onScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", ()=>{
|
||||||
|
document.body.addEventListener("click", e=>{
|
||||||
|
const tooltipEl = e.target.closest(".idm_tooltip");
|
||||||
|
if(!e.target.closest(".idm_tooltip__info_icon") || !tooltipEl) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
idmShowTooltip(tooltipEl);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user