Smart IP is every installer’s dream: a range of networked active loudspeaker systems that deliver exceptional audio, power and sophisticated loudspeaker management via a single CAT cable.
Learn more ›
Smart IP
Watch our Global Launch Event
Smart IP is every installer’s dream: a range of networked active loudspeaker systems that deliver exceptional audio, power and sophisticated loudspeaker management via a single CAT cable.
Learn more ›
Power to the speaker.
Power to the installer.
Originally launched in 2019, our Smart IP family of installation loudspeakers intended for AV has now expanded to include three on-wall models, an in-ceiling model and a hanging pendant model - all of which benefit from our comprehensive system management software and simple, intuitive controller app for end users. The visually elegant Smart IP range combines the finest studio-quality loudspeaker performance with all the flexibility and convenience that networked audio brings.
—Aki Mäkivirta • Genelec Director of R&D
![](https://images.ctfassets.net/4zjnzn055a4v/3QbqVTPgkw781XRMyzFT8L/aca06c1ed006c6617bde7c1be170db63/smart_ip_awards_img1.jpg)
![](https://images.ctfassets.net/4zjnzn055a4v/7lUX2Ir1yfinpk9CqhqAWx/c3dba9e236e28eea27f6b7a340b21f1c/smart_ip_awards_img2.jpg)
![](http://images.ctfassets.net/4zjnzn055a4v/3aZ9ecj5EZ1tCEzWZ4QRhZ/d6f2b76ca107829416a02dac0e1894f2/Ilmatar_smart_ip_square.jpg?w=990)
Created for professionals
Smart IP was created for discerning professionals who demand premium loudspeaker performance that can integrate quickly and seamlessly into any AV application, no matter how complex the project.
Powered by PoE and compatible with both Dante and AES67 IP audio streams, Smart IP also offers flexible loudspeaker management to provide system designers, integrators and installers with a scalable, powerful and cost-effective solution.
And with a flexible choice of on-wall, in-ceiling and hanging pendant designs complemented by a wide range of mounting accessories and colour finishes, Smart IP blends perfectly into any environment or colour scheme.
![](http://images.ctfassets.net/4zjnzn055a4v/48XUmzvpy9unphzdkFAcz4/d52ec260105aaf99feaedb1fbcccfcd0/smart_ip_4435_4436_square.jpg?w=990)
Exceptional audio
To deliver pristine audio quality, Smart IP loudspeakers utilise the same two-way active technology that has made Genelec the first choice for demanding studio and broadcast applications for over forty years.
Featuring our trademark Minimum Diffraction Enclosure and Directivity Control Waveguide, Smart IP loudspeakers provide clarity, intelligibility and beautifully uniform coverage, combined with bulletproof 24/7 reliability.
There’s much more, though. Smart IP supports uncompressed low-latency audio streaming directly into each loudspeaker, with synchronisation to sub-microsecond level for solid acoustic imaging. Each loudspeaker supports up to eight audio channels in a stream with sample rates of 32 to 96 kHz and 16 to 24 bit resolution, all manageable via Dante Controller software.
![](http://images.ctfassets.net/4zjnzn055a4v/5CPDFOKMQiWus1BYuQF8C7/93c30c6695883ffb8e9620d6c07508f9/smart_ip_manager_peder_carlsson.jpg?w=990)
Total control
Designed to work closely with any Smart IP loudspeaker system, Smart IP Manager software allows installers to configure an almost unlimited number of rooms, zones, loudspeakers and audio channels, and includes device discovery, versatile room-equalisation tools, system organisation and status monitoring. This enables installers to deploy Smart IP loudspeakers even on highly complex, acoustically challenging projects, with a public API command set to allow external control via third-party hardware, software or house automation systems. For seamless integration with Control4, QSC Q-SYS and AMX systems, we provide a simple, ready-to-go driver, which can be downloaded and used free of charge.
For smaller, less complex installations, the Smart IP Controller app gives the end user day-to-day control of mute, volume control, power on/off and zone control, all via a smartphone or tablet. The Controller app will automatically discover the loudspeakers on the network, and offers a clean and intuitive user interface that both experts and non-technical staff alike will find straightforward and satisfying.
![](http://images.ctfassets.net/4zjnzn055a4v/oaJbt2eaFwasIm7s8456i/4ad71182f953000a1b608b71980f7638/factory_summer_2020_square.jpg?w=990)
Sustainability
Created in conjunction with leading Finnish industrial designer Harri Koskinen, the elegantly minimalist Smart IP loudspeaker enclosures are built to the highest environmental standards at our headquarters in Iisalmi, Finland – with the 4410, 4420 and 4430 models all fashioned from recycled aluminium.
Designed for low power consumption and offering many years of reliable performance – all backed up by Genelec’s renowned service and support – Smart IP systems are a secure, long-term and environmentally sound investment.
Genelec Smart IP | Premium networked audio for AV made simple
Genelec Smart IP | Quick Setup Guide - How to create a simple networked audio system
Genelec Smart IP | Premium networked audio for AV made simple
Introducing Smart IP. Genelec's family of elegant loudspeakers for premium audio over a single CAT cable.
Genelec Smart IP | Quick Setup Guide - How to create a simple networked audio system
In this video, Genelec expert, Marcel Schechter, walks you through the basic steps of creating a Smart IP installation: Connecting the loudspeakers to the network switch with a CAT cable, connecting your computer to the network switch, downloading our free software and using Dante Controller/Virtual Soundcard.
How it works
We’ve long been designing compact two-way loudspeakers, and introduced our first two-way IP studio model back in 2016, but it was Power-over-Ethernet that was key to the single cable design of Smart IP.
Smart IP utilises proprietary internal power management that delivers a significantly higher SPL than was previously possible via any conventional PoE. In fact, Smart IP can produce maximum SPL whenever needed, sufficient to power small and medium sized audio systems – a world first.
The internal power supply stores power, to enable Smart IP loudspeakers to work with real audio signals, music and speech. Since audio signals are dynamic, periods of high peak power typically have finite duration while the effective average power level, or RMS power, remains much lower.
![](https://images.ctfassets.net/4zjnzn055a4v/6tETDFVlaj3rYweXBK84L6/8a933d156bbc5ed99ee4033840d15ce4/poe_diagram.png)
Smart IP Speakers
![](http://images.ctfassets.net/4zjnzn055a4v/3T5a7o1crY1LRmbgXELjpP/bde04a3f43c6d62bae1fe0d12b6135c2/4010_listingimage.png?w=417)
4410A
Smart IP Installation SpeakerWhen space is tight, the 4410A delivers the single cable networked convenience of Smart IP in a powerful, super-compact format.
SPL
96 dB
Amplifier Power
18 W Bass (Class D) + 18 W Treble (Class D)
Frequency Response
67 Hz - 40 kHz (-6 dB)
Accuracy of Frequency Response
± 2.5 dB (74 Hz - 20 kHz)
Driver Dimensions
⌀ 76mm Bass + ⌀ 19mm Treble (view in inches)
Dimensions
H 181 x W 121 x D 115 mm, (view in inches)
Weight
1.5 kg / 3.3 lb
Connections
1 x RJ45 AES67 / Dante Input
![](http://images.ctfassets.net/4zjnzn055a4v/2laA6O02KXJ8n5K4RiwW7r/24c38d535790d517dfad04769042cab6/4020_listingimage.png?w=417)
4420A
Smart IP Installation SpeakerThe 4420A packs the power and flexibility of Smart IP technology into an ultra-compact enclosure, bringing clarity, intelligibility and uniform room coverage to the tightest of spaces.
SPL
100 dB
Amplifier Power
50 W Bass (Class D) + 50 W Treble (Class D)
Frequency Response
55 Hz - 39 kHz (-6 dB)
Accuracy of Frequency Response
± 1.5 dB (62 Hz - 20 kHz)
Driver Dimensions
⌀ 105mm Woofer + ⌀ 19mm Tweeter (view in inches)
Dimensions
H 226 x W 151 x D 142 mm, (view in inches)
Weight
3.1 kg / 6.8 lb
Connections
1 x Euroblock Analog Input
1 x RJ45 AES67 / Dante Input
![](http://images.ctfassets.net/4zjnzn055a4v/62MA2u9Bf6wqRUALvLbHCh/d0ed9c3f0d4f3b4d05132f3ce7e00a0e/4030_listingimage.png?w=417)
4430A
Smart IP Installation SpeakerLooking for exceptional sound over IP? The 4430A offers power, audio and loudspeaker management via a single standard CAT cable, delivering unrivalled flexibility, cost-effectiveness and simplicity of installation.
SPL
104 dB
Amplifier Power
50 W Bass (Class D) + 50 W Treble (Class D)
Frequency Response
45 Hz - 39 kHz (-6 dB)
Accuracy of Frequency Response
± 1.5 dB (58 Hz - 20 kHz)
Driver Dimensions
⌀ 130mm Woofer + ⌀ 19mm Tweeter (view in inches)
Dimensions
H 285 x W 189 x D 178 mm, (view in inches)
Weight
5.1 kg / 11.2 lb
Connections
1 x RJ45 AES67 / Dante Input
1 x Euroblock Analog Input
An error has occurred when reading existing sub-variable "amplifierTechnology"; see cause exception! The type of the containing value was: extended_hash+string (org.json.JSONObject wrapped into f.e.b.StringModel) ---- FTL stack trace ("~" means nesting-related): - Failed at: ${localizeField(amplifierSpecificatio... [in template "20116#20152#1270569" at line 154, column 30] ----
1<#--
2 Product comparison (column view)
3
4 Product comparison - buy (column view) template also use this template
5 buy setting variable buyMode = true
6
7 When true, show green buy button and fill in prices
8
9-->
10
11<#include "${templatesPath}/300307" />
12<#assign ns = themeDisplay.portletDisplay.getId() >
13<#assign instanceId = themeDisplay.portletDisplay.getId() >
14<#assign portletId = "p_p_id_" + instanceId + "_">
15<#assign siteDefaultLocale = portalUtil.getSiteDefaultLocale(groupId)?string?replace("_","-") >
16
17<#if entities?has_content>
18 <#if bgColor?has_content>
19 <#if bgColor?matches("default", "i")>
20 <#assign cssClasses="bg-gray-light">
21 <#else>
22 <#assign cssClasses="${bgColor}">
23 </#if>
24 <#else>
25 <#assign cssClasses="">
26 </#if>
27<@sectionHeader title="${title}" menuTitle="${menuTitle}" cssClasses="${cssClasses} product-compare-column-view" id="${instanceId}-compare-column-view" />
28
29<#if buyMode?? && buyMode>
30 <#assign columnsCss = "owl-carousel owl-column-view owl-theme text-center">
31<#else>
32 <#assign columnsCss = "flex-columns-${entities?size} text-center">
33</#if>
34
35 <div class="flex-container ${columnsCss}">
36
37 <#list entities as cmsEntity>
38 <#if cmsEntity?has_content>
39 <#if cmsEntity.getField(profile, "/entity/fields/summary")?has_content>
40 <#assign summary = cmsContentFormatterService.getMarkdown(cmsEntity.getLocalizedField(profile, "/entity/fields/summary"))>
41 </#if>
42
43 <#if cmsEntity.getField("/entity/fields/title")?has_content>
44 <#assign entityTitle = cmsEntity.getLocalizedField(profile, "/entity/fields/title")>
45 <#else>
46 <#assign entityTitle = cmsEntity.getField(profile, "/entity/fields/name")>
47 </#if>
48
49 <#assign subtitle= "">
50 <#if cmsEntity.getLocalizedField(profile, "/entity/fields/subtitle")?has_content>
51 <#assign subtitle = cmsEntity.getLocalizedField(profile, "/entity/fields/subtitle")>
52 </#if>
53
54 <#assign entity = cmsEntity.getField(profile, "/entity") >
55
56 <#assign autoWidth = 1250/entities?size>
57
58
59 <div class="card rounded card-img flex-item">
60
61 <#if cmsContentService.getUrlForEntity(themeDisplay, entity.id)?has_content>
62 <#assign pageUrl = cmsContentService.getUrlForEntity(themeDisplay, entity.id)>
63 <#elseif cmsEntity.getLocalizedField(profile, "/entity/fields/url")?has_content>
64 <#assign pageUrl = cmsEntity.getLocalizedField(profile, "/entity/fields/url")>
65 <#elseif cmsEntity.getField(profile, "/entity/fields/url")?has_content>
66 <#assign pageUrl = cmsEntity.getField(profile, "/entity/fields/url")>
67 <#elseif cmsEntity.getField(profile, "/entity/assets/file/url")?has_content>
68 <#-- Documents -->
69
70 <#assign pageUrl = cmsEntity.getField(profile, "/entity/assets/file/url")>
71 <#else>
72 <#assign pageUrl = "">
73 </#if>
74
75 <#if pageUrl?has_content>
76 <a class="disableInMobile text-dark" href="${pageUrl}" >
77 </#if>
78 <#if cmsEntity.getLocalizedField(profile, "/entity/assets/listingImage")?has_content>
79 <#assign listingImage = cmsEntity.getLocalizedField(profile, "/entity/assets/listingImage") >
80 <#assign listingImageRel = cmsEntity.getLocalizedField(profile, "/entity/relationships/listingImage/locales")!"" >
81 <#assign alt="" >
82 <#if listingImageRel?has_content && !listingImageRel.properties.isNull("altText")>
83 <#assign alt = localizeField(listingImageRel.properties.altText, profile.getCMSLangCode()) >
84 </#if>
85
86 <div class="aspect-ratio aspect-ratio-1-to-1">
87 <@imageThumbnail src="${listingImage.url}" thumbnail="columnViewImg" id="" cssClasses="aspect-ratio-item-fluid aspect-ratio-item-center-middle" autoWidth=autoWidth?ceiling alt=alt/>
88 </div>
89 </#if>
90 <div class="card-padding text-dark adjust-height-summary">
91
92 <h3 class="mb-0">${entityTitle}</h3>
93 <#if subtitle?has_content>
94 <span class="subtitle text-bold">${subtitle}</span>
95 </#if>
96 <#if cmsEntity.getField(profile, "/entity/fields/summary")?has_content>
97 <div class="mt-2">
98 ${summary}
99 </div>
100 </#if>
101
102
103 </div>
104 <#if pageUrl?has_content>
105 </a>
106 </#if>
107
108
109 <#--- Technical specifications
110
111 Show headers even if values are missing.
112 Make columsn same hight
113
114 --->
115
116 <#if cmsEntity.getField(profile, "/entity/relationships/productSpecifications")?has_content>
117 <#assign productSpecifications = cmsEntity.getField(profile, "/entity/relationships/productSpecifications") >
118 </#if>
119 <#if cmsEntity.getField(profile, "/entity/relationships/amplifierSpecifications")?has_content>
120 <#assign amplifierSpecifications = cmsEntity.getField(profile, "/entity/relationships/amplifierSpecifications") >
121 </#if>
122 <#if cmsEntity.getField(profile, "/entity/relationships/connectorSpecifications")?has_content>
123 <#assign connectorSpecifications = cmsEntity.getField(profile, "/entity/relationships/connectorSpecifications") >
124 </#if>
125 <#if cmsEntity.getField(profile, "/entity/relationships/driverSpecifications")?has_content>
126 <#assign driverSpecifications = cmsEntity.getField(profile, "/entity/relationships/driverSpecifications") >
127 </#if>
128
129
130 <#if productSpecifications?has_content || connectorSpecifications?has_content || driverSpecifications?has_content>
131 <hr class="comparison-divider" style="margin:-20px 40px 20px 40px;" />
132
133 <div class="section-specs card-padding">
134
135 <#-- SPL -->
136
137 <#assign spl = "">
138 <#if productSpecifications?has_content && !productSpecifications.properties.isNull("shortTermMaxSpl")>
139 <#assign spl = "${productSpecifications.properties.shortTermMaxSpl} dB" >
140 </#if>
141 <@printSpecRow "spl" spl />
142
143 <#-- Amplifier Power -->
144
145 <#assign amplifierPower = "">
146 <#if amplifierSpecifications?has_content>
147 <#assign amplifierPower>
148 <#list amplifierSpecifications.iterator() as amplifierSpecification>
149 <span class="text-nowrap">
150 <#if amplifierSpecification.properties.amplifierCount gt 1>
151 ${amplifierSpecification.properties.amplifierCount} X
152 </#if>
153 ${amplifierPower} ${amplifierSpecification.properties.amplifierPower} W ${localizeField(amplifierSpecification.properties.frequencyRange, profile.getCMSLangCode())}
154 (${localizeField(amplifierSpecification.properties.amplifierTechnology, profile.getCMSLangCode())}) <#sep>+
155 </span>
156 </#list>
157 </#assign>
158 </#if>
159 <@printSpecRow "amplifier-power" amplifierPower />
160
161 <#-- Frequency Response -->
162
163 <#assign frequencyResponse = "">
164 <#if productSpecifications?has_content>
165 <#assign frequencyResponse>
166 <#if !productSpecifications.properties.isNull("frequencyResponseLowCutoff6dB")>
167 ${productSpecifications.properties.frequencyResponseLowCutoff6dB}
168 </#if>
169 <#if !productSpecifications.properties.isNull("frequencyResponseHighCutoff6dB")>
170 Hz -
171 <#if productSpecifications.properties.frequencyResponseHighCutoff6dB < 1000>
172 ${productSpecifications.properties.frequencyResponseHighCutoff6dB} Hz
173 <#else>
174 ${productSpecifications.properties.frequencyResponseHighCutoff6dB/1000} kHz
175 </#if>
176 </#if>
177 (-6 dB)
178 </#assign>
179 </#if>
180 <@printSpecRow "frequency-response" frequencyResponse />
181
182 <#-- Accuracy of Frequency Response -->
183
184 <#assign frequencyResponseAccuracy = "">
185 <#if productSpecifications?has_content>
186 <#if !productSpecifications.properties.isNull("frequencyResponseAccuracy")>
187 <#assign frequencyResponseAccuracy>
188 ±
189 <#if !productSpecifications.properties.isNull("frequencyResponseAccuracy")>
190 ${productSpecifications.properties.frequencyResponseAccuracy} dB
191 </#if>
192 <#if !productSpecifications.properties.isNull("frequencyResponseLowCutoff")>
193 (${productSpecifications.properties.frequencyResponseLowCutoff} Hz
194 </#if>
195 <#if !productSpecifications.properties.isNull("frequencyResponseHighCutoff")>
196 -
197 <#if productSpecifications.properties.frequencyResponseHighCutoff < 1000>
198 ${productSpecifications.properties.frequencyResponseHighCutoff} Hz)
199 <#else>
200 ${productSpecifications.properties.frequencyResponseHighCutoff/1000} kHz)
201 </#if>
202 </#if>
203 </#assign>
204 </#if>
205 </#if>
206 <@printSpecRow "accuracy-of-frequency-response" frequencyResponseAccuracy />
207
208 <#-- Driver Dimensions -->
209
210 <#assign driverDimension = "" >
211 <#if driverSpecifications?has_content>
212 <#list driverSpecifications.iterator() as driverSpecification>
213 <#assign driverId = driverSpecification.id>
214 <#assign driverDimension>
215 ${driverDimension}
216 <#if !driverSpecification.properties.isNull("driverHeight") || !driverSpecification.properties.isNull("driverWidth")>
217 <#if driverSpecification.properties.driverCount gt 1>
218 <span>${driverSpecification.properties.driverCount} x</span>
219 </#if>
220 <#if !driverSpecification.properties.isNull("driverHeight")>
221 <span>H </span><span data-mm-value="${driverSpecification.properties.driverHeight}"> ${driverSpecification.properties.driverHeight} </span><span> x</span>
222 </#if>
223 <#if !driverSpecification.properties.isNull("driverWidth")>
224 <span> W </span><span data-mm-value="${driverSpecification.properties.driverWidth}"> ${driverSpecification.properties.driverWidth}</span>
225 </#if>
226 <span class="unit">mm</span>
227 <#if !driverSpecification.properties.isNull("frequencyRange")>
228 <span>${localizeField(driverSpecification.properties.frequencyRange, profile.getCMSLangCode())}</span> <#sep>+
229 </#if>
230 <#elseif !driverSpecification.properties.isNull("driverDiameter")>
231 <#if driverSpecification.properties.driverCount gt 1>
232 <span>${driverSpecification.properties.driverCount} x</span>
233 </#if>
234 <#if !driverSpecification.properties.isNull("driverDiameter")>
235 <span class="text-intro">⌀ </span>
236 <span data-mm-value="${driverSpecification.properties.driverDiameter}">${driverSpecification.properties.driverDiameter}</span><span class="unit">mm</span>
237 </#if>
238 <#if !driverSpecification.properties.isNull("frequencyRange")>
239 <span>${localizeField(driverSpecification.properties.frequencyRange, profile.getCMSLangCode())}</span> <#sep>+
240 </#if>
241 </#if>
242 </#assign>
243 </#list>
244 <#assign driverDimension>${driverDimension}
245 <a class="small mmtoinch" href="#" onclick="return false;">(<@liferay.language key="view-in-inches"/>)</a>
246 </#assign>
247 </#if>
248 <@printSpecRow "driver-dimensions" driverDimension />
249
250 <#-- Dimensions -->
251 <#assign dimensions = "">
252 <#if productSpecifications?has_content>
253 <#assign dimensions>
254 <#if !productSpecifications.properties.isNull("heightWithIsoPod")>
255 H <span data-mm-value="${productSpecifications.properties.heightWithIsoPod}">${productSpecifications.properties.heightWithIsoPod}</span>
256 x W <span data-mm-value="${productSpecifications.properties.width}">${productSpecifications.properties.width}</span> x D <span data-mm-value="${productSpecifications.properties.depth}">${productSpecifications.properties.depth}</span>
257 <span class="unit">mm</span>,
258 <@liferay.language key="with-iso-pod"/>™
259 <a class="small mmtoinch" href="#" onclick="return false;">(<@liferay.language key="view-in-inches"/>)</a>
260 <#else>
261 H <span data-mm-value="${productSpecifications.properties.height}">${productSpecifications.properties.height}</span>
262 x W <span data-mm-value="${productSpecifications.properties.width}">${productSpecifications.properties.width}</span> x D <span data-mm-value="${productSpecifications.properties.depth}">${productSpecifications.properties.depth}</span>
263 <span class="unit">mm</span>,
264 <a class="small mmtoinch" href="#" onclick="return false;">(<@liferay.language key="view-in-inches"/>)</a>
265 </#if>
266 </#assign>
267 </#if>
268 <@printSpecRow "dimensions" dimensions />
269
270 <#-- Weight -->
271 <#assign weight = "">
272 <#if productSpecifications?has_content>
273 <#assign lbWeigt = "${productSpecifications.properties.weight*2.20462262}" >
274 <#assign weight>${productSpecifications.properties.weight} kg / ${lbWeigt?number?string[".#"]} lb</#assign>
275 </#if>
276 <@printSpecRow "weight" weight />
277
278 <#-- Connections -->
279 <#assign connections = "">
280 <#if connectorSpecifications?has_content>
281 <#list connectorSpecifications.iterator() as connectorSpecification>
282 <#assign connections>${connections}
283 <p class="small">
284 <#if !connectorSpecification.properties.isNull("connectorCount")>
285 ${connectorSpecification.properties.connectorCount}
286 </#if>
287 <#if !connectorSpecification.properties.isNull("connectorType")>
288 x ${localizeField(connectorSpecification.properties.connectorType, profile.getCMSLangCode())}
289 </#if>
290 <#if !connectorSpecification.properties.isNull("connectorSignal")>
291 ${localizeField(connectorSpecification.properties.connectorSignal, profile.getCMSLangCode())}
292 </#if>
293 <#if !connectorSpecification.properties.isNull("connectorFunction")>
294 ${localizeField(connectorSpecification.properties.connectorFunction, profile.getCMSLangCode())}
295 </#if>
296 </p>
297 </#assign>
298 </#list>
299 </#if>
300 <@printSpecRow "connections" connections />
301
302 </div>
303 </#if>
304
305
306 <#if buyMode?? && buyMode>
307
308 <#assign variantsShopifyIds = "" >
309
310 <#assign minimumWebshopAmount = 1 >
311 <#if cmsEntity.getField(profile, "/entity/fields/minimumWebshopAmount")?has_content>
312 <#assign minimumWebshopAmount = cmsEntity.getField(profile, "/entity/fields/minimumWebshopAmount") >
313 </#if>
314
315 <#assign b2cVisibility = "none">
316
317 <#if cmsEntity.getField(profile, "/entity/fields/b2cVisibilityKey")?has_content>
318 <#assign b2cVisibility = cmsEntity.getField(profile, "/entity/fields").get("b2cVisibilityKey")[siteDefaultLocale]!"" >
319 </#if>
320
321
322 <#if b2cVisibility == "showWithPrice" || b2cVisibility == "showForPurchase">
323 <#if cmsEntity.getField(profile, "/entity/relationships/variants")?has_content>
324 <#assign variants = cmsEntity.getField(profile, "/entity/relationships/variants") >
325
326
327 <#list variants.iterator() as variant>
328 <#if variant.typeId == "accessoryVariant">
329 <#assign shopifyIdField = "shopifyAccessoryVariant">
330 <#else>
331 <#assign shopifyIdField = "shopifyProductVariant">
332 </#if>
333
334 <#if !variant.properties.isNull(shopifyIdField) && localizeField(variant.properties[shopifyIdField], siteDefaultLocale)?has_content>
335 <#assign variantsShopifyIds>${variantsShopifyIds}${localizeField(variant.properties[shopifyIdField], siteDefaultLocale)},</#assign>
336 </#if>
337 </#list>
338 </#if>
339 </#if>
340 <div class="same-level-btn card-padding mb-4">
341 <div class="btn-container">
342 <#if variantsShopifyIds?has_content>
343 <div class="mb-3"><span class="price text-bold" data-variant-prices="${variantsShopifyIds}" data-qty="${minimumWebshopAmount}" > </span></div>
344 </#if>
345 <#if pageUrl?has_content>
346 <a class="btn btn-theme btn-medium btn-margin-top text-bolder" href="${pageUrl}">
347 <@liferay.language key="buy-now"/>
348 <@linkIcon cssClasses="icon tiny-icon" alt="" icon="arrow-white"/>
349 </a>
350 </#if>
351 </div>
352 </div>
353 <#else>
354 <#if pageUrl?has_content>
355 <div class="card-padding">
356 <div class="btn-container">
357
358 <a class="btn btn-dark btn-medium btn-margin-top text-bolder" href="${pageUrl}">
359 <@liferay.language key="read-more"/>
360 <@linkIcon cssClasses="icon tiny-icon" alt="" icon="arrow-white"/>
361 </a>
362 </div>
363 </div>
364 </#if>
365
366 </#if>
367
368 </div>
369 </#if>
370 </#list>
371
372 </div>
373<@sectionFooter/>
374</#if>
375
376<#macro printSpecRow headerTextLanguageKey valueText>
377 <div class="row">
378 <div class="col-lg-12">
379 <div class="media product-page-media adjust-height-${headerTextLanguageKey}">
380 <div class="media-body">
381 <h4 class="mt-0 text-bolder"><@liferay.language key="${headerTextLanguageKey}"/></h4>
382 <p class="small">${valueText}</p>
383 </div>
384 </div>
385 </div>
386 </div>
387</#macro>
388
389<style>
390 .product-compare-column-view {color: #333; }
391</style>
392
393<script>
394if ($(window).outerWidth() < 992) {
395 $('.disableInMobile').contents().unwrap();
396 }
397
398
399
400 $("#${portletId} .mmtoinch").click(function(){
401 // mminch('value').toInch()
402 var toggleElement = $(this);
403 var unit = "";
404
405 $("#${portletId} span[data-mm-value]").each(function(){
406 unit = $(this).attr("data-unit");
407
408 // flip unit (empty = mm)
409 unit = !unit || unit == "mm" ? "inch" : "mm";
410
411 $(this).attr("data-unit" , unit);
412
413 if (unit == "inch") {
414 $(this).html(mminch($(this).attr("data-mm-value")).toInch());
415 } else {
416 $(this).html($(this).attr("data-mm-value"));
417 }
418 $("#${portletId} span.unit").text(unit);
419 });
420
421 $("#${portletId} .mmtoinch").text(
422 unit == "mm" ? '(<@liferay.language key="view-in-inches"/>)' : '(<@liferay.language key="view-in-mm"/>)'
423 );
424 });
425
426
427
428
429 // close popup when clicking outside of the element
430 function closeTechSpecsPopup(e) {
431 if(!$('#popupContent'+"3").is(e.target) &&
432 $('#popupContent'+"3").has(e.target).length == 0 &&
433 $(e.target).attr('class') != 'close' &&
434 $('#theme-popup'+"3").css("display") == "block"){
435 closePopup("3");
436 history.pushState(null, null, ' ');
437 }
438 };
439
440
441
442 $(document).ready(function(){
443 if(window.location.hash.indexOf("section-technical-specifications") > -1) {
444 openPopup(3);
445 };
446 });
447
448
449$(document).on({
450 "mouseup": function (e) { closeTechSpecsPopup(e); },
451 "touchstart": function (e) { closeTechSpecsPopup(e); }
452});
453
454 $(".close").click(function(){
455 history.pushState(null, null, ' ');
456 });
457
458
459
460 <#--
461 Go through elements that have adjust-height-* classes and adjust element heights
462 -->
463 $(document).ready(function(){
464 if (window.innerWidth < 576 ) {
465 return;
466 }
467
468 var adjustHeightUniqueClasses = {};
469
470 $("#${portletId} div[class*='adjust-height-']").each(function(){
471 for (const clazz of this.classList) {
472 if (clazz.includes("adjust-height")) {
473 adjustHeightUniqueClasses[clazz] = clazz;
474 }
475 }
476 });
477
478 for (const adjustClass in adjustHeightUniqueClasses) {
479 var maxHeight = 0;
480
481 $("#${portletId} ."+adjustClass).each(function(){
482 maxHeight = Math.max(maxHeight, $(this).height());
483 });
484
485 $("#${portletId} ."+adjustClass).height(maxHeight);
486 }
487 });
488
489 <#if buyMode?? && buyMode>
490
491let variantIds_${instanceId} = [];
492
493$("#${instanceId}-compare-column-view").find("[data-variant-prices!=''][data-variant-prices]").each(function() {
494 variantIds_${instanceId} = variantIds_${instanceId}.concat($(this).attr("data-variant-prices").split(',').filter(n => n));
495});
496
497
498
499genelec.shopify.getVariants(variantIds_${instanceId}).then(variants => {
500 let priceLookup = {};
501 for (i in variants) {
502 let variant = variants[i];
503 priceLookup[btoa(variant.id)] = variant.price;
504 }
505
506 $("#${instanceId}-compare-column-view").find("[data-variant-prices!=''][data-variant-prices]").each(function() {
507 let elemVariantIds = $(this).attr("data-variant-prices").split(',').filter(n => n);
508
509 let cheapestPrice = { amount: 9999999};
510 let cheapestId = "";
511
512 let qty = $(this).data("qty");
513 if (!qty) {
514 qty = 1;
515 }
516
517 for(i in elemVariantIds) {
518 let variantId = elemVariantIds[i];
519
520 if (priceLookup[variantId].amount < cheapestPrice.amount) {
521 cheapestId = priceLookup[variantId];
522 cheapestPrice = priceLookup[variantId];
523 }
524 }
525
526 if (cheapestPrice.amount && cheapestPrice.currencyCode) {
527 $(this).html((parseInt(cheapestPrice.amount)*qty).toLocaleFixed(2) + " " + cheapestPrice.currencyCode.toCurrency());
528 }
529 });
530
531});
532
533<#-- We use owl carousel in buy mode -->
534$("#${instanceId}-compare-column-view .owl-carousel").owlCarousel({
535 loop:false,
536 nav:true,
537 dots:false,
538 margin:8,
539 lazyLoad:true,
540 navText : ['<i class="icon-angle-left">','<i class="icon-angle-right">'],
541 responsive:{
542 0:{items:1, slideBy:1},
543 600:{items:2, slideBy:1},
544 800:{items:3, slideBy:3},
545 1200:{items:5, slideBy:3}
546 },
547})
548
549</#if> <#-- // End Of buyMode -->
550
551</script>