var _cCurrentNavigation async function boot() { let init = async () => { let [navigation, session] = await initializeService() _cCurrentNavigation = navigation getAsyncInfos(navigation, session) await initProcessSession(navigation, session) initProcessProduct(navigation, session) initProcessUser(navigation, session) initProcessRemoveCart(navigation, session) initProcessOrder(navigation, session) initCustomClicks(navigation, session, _cvConfig.custom_clicks) initPopPushs(navigation, session, _cvConfig.pushs) initProcessCheckout(navigation) initProcessCheckProducts(navigation) _cpAddProductToCart(navigation) } if (_cvConfig.windowLoad == true) { window.addEventListener("load", async () => { init() }) } else if (_cvConfig.singlePage == true) { init() // init first time let currentUrl = window.location.href setInterval(() => { let urlNow = window.location.href if (currentUrl != urlNow) { currentUrl = urlNow init() } }, 500) } else { init() } } async function initializeService() { return new Promise(async resolve => { let navigation = new _Navigation() let session = new _Session() let cart = new _Cart() let newSession let initNewSession = async () => { newSession = true let hashCookie = await getCookieCV('_cvhash') if (hashCookie != 0) { hashCookie = JSON.parse(hashCookie) session.hash = hashCookie.hash session.identified = hashCookie.identified } else { session.hash = await generateCVID() setCookieCV('_cvhash', JSON.stringify({ hash: session.hash, identified: session.identified }), 365) } session.session = await generateCVID() session.started_at = new Date() session.processTimeOut(_cvConfig.session_timeout) processTimeSpend() session.save() } if (localStorage._cvsession) { session.init() if (await session.processSessionTime() == true) { session = new _Session() await initNewSession() } else { newSession = false session.processTimeOut(_cvConfig.session_timeout) processTimeSpend() session.page_views++ } resolve([navigation, session]) } else { await initNewSession() resolve([navigation, session]) } if (localStorage._cvcart) { cart.init() if (newSession == true) { await cart.clearRemoved() if (cart.hasActiveProducts == true) { cart.cart_id = await generateCVID() cart.save() } else cart.clear() } if (cart.hasActiveProducts == true) { if (cart.record_duration != _cvConfig.cart_duration) cart.record_duration = _cvConfig.cart_duration await cart.processCartDuration() } } }) } async function initProcessSession(navigation, session) { await processForSession(navigation, _cvConfig.functions.forNavCategories, session, 'categories_view', [navigation, 'getPathCategories', _cvConfig.notValidCategories]) await processForSession(navigation, _cvConfig.functions.forNavProduct, session, 'products_view') await processForSession(navigation, _cvConfig.functions.forNavSearches, session, 'searches') let utms = await getUrlParameters(navigation.params, 'utm') if (Object.entries(utms).length > 0) { if (session.utms.length > 0) { await processForUtms(utms, session) } else session.utms.push(utms) } session.has_order = await initProcessOrder(navigation, session) new _PayloadNavigation(navigation, session).send() if (session.bis == false) session.bis = true if (navigation.geolocation != undefined || session.gls == false) session.gls = true await updateInterationLists(session) session.save() } async function updateInterationLists(session) { if (session.pushs) { // for pushs for (let i = 0; i < session.pushs.length; i++) { session.pushs[i].intervalAttempts++ } } } async function processForSession(navigation, functionName, objTo, paramTo, callbackElse) { if (functionName != 'default') { if (window[functionName]) { let values = await window[functionName](navigation).catch(err => { new _CVPROCESSBEAT('ERROR', functionName, `Erro na função de captação para sessão --> ${err}`) }) if (values) { let valuesList = await processListObjs(values, objTo[paramTo]) objTo[paramTo] = valuesList[0] navigation[paramTo] = valuesList[1] } } else new _CVPROCESSBEAT('ERROR', functionName, 'Função de captação para sessão não existe na tag') } else { objTo[paramTo] = await processListObjs(await callbackElse[0][callbackElse[1]](callbackElse[2]), objTo[paramTo]) } } async function processForUtms(utms, session) { let exist = false for (let i = 0; i < session.utms.length; i++) { if (session.utms[i].utm_campaign && utms.utm_campaign) if (await session.utms[i].utm_campaign == utms.utm_campaign) exist = true } if (!exist) session.utms.push(utms) } async function initProcessProduct(navigation) { if (window[_cvConfig.functions.isProductPage] && window[_cvConfig.functions.forProductInfos]) { if (await window[_cvConfig.functions.isProductPage](navigation) != undefined) { initProcessAddCart(navigation) let product = await window[_cvConfig.functions.forProductInfos](navigation).catch(err => { new _CVPROCESSBEAT('ERROR', _cvConfig.functions.isProductPage, `Erro na função de captação para produto --> ${err}`) }) if (product) { let productClass = new _Product(product) productClass.save() productClass.send() productClass.validate() } else new _CVPROCESSBEAT('ERROR', _cvConfig.functions.forProductInfos, 'Função de captação para produto não retornou o objeto esperado', product) } } else new _CVPROCESSBEAT('ERROR', _cvConfig.functions.isProductPage, 'Função de captação para produto não existe na tag') } async function initProcessAddCart(navigation) { if (window[_cvConfig.functions.addProductToCart]) { window[_cvConfig.functions.addProductToCart](navigation) } else new _CVPROCESSBEAT('ERROR', _cvConfig.functions.addProductToCart, 'Função de adicionar produto no carrinho não existe na tag') } async function initProcessRemoveCart(navigation) { if (window[_cvConfig.functions.removeProductToCart]) { window[_cvConfig.functions.removeProductToCart](navigation) } else new _CVPROCESSBEAT('ERROR', _cvConfig.functions.removeProductToCart, 'Função de remover produto do carrinho não existe na tag') } async function initProcessCheckProducts(navigation) { if (window[_cvConfig.functions.checkCartProducts]) { let objProducts = await window[_cvConfig.functions.checkCartProducts](navigation) if (objProducts != undefined) { let cart = new _Cart() if (localStorage._cvcart) cart.init() cart.checkProducts(objProducts[0], objProducts[1], objProducts[3]) } } else new _CVPROCESSBEAT('ERROR', 'initProcessCheckProducts', 'Função de validação de produtos no carrinho não existe na tag') } var _cAlreadyE = [] async function initProcessUser(navigation, session) { if (_cvConfig.optin_forms.length > 0) { let mapped = (form, currentNav) => { if (form.default_visible == true) { let trigger = document.querySelector(form.trigger) if (trigger) addEvent(trigger, form, currentNav) else new _CVPROCESSBEAT('ERROR', 'initProcessUser', `Trigger ${form.trigger} cadastrada para form ${form.name} não existe`) } else { let interval1 = form => { let idI1 = setInterval(() => { let trigger = document.querySelector(form.trigger) if (trigger) { clearInterval(idI1) addEvent(trigger, form, currentNav) interval2(form) } }, 600) } let interval2 = form => { let idI2 = setInterval(() => { let trigger = document.querySelector(form.trigger) if (trigger == undefined) { clearInterval(idI2) interval1(form) } }, 800) } interval1(form) } } let addEvent = (element, form, currentNav) => { element.addEventListener('click', () => { catchInfo(form, currentNav) }) element.addEventListener('touchstart', () => { catchInfo(form, currentNav) }) document.addEventListener('keydown', event => { if (event.key == 'Enter' || event.keyCode == 13) catchInfo(form, currentNav) }) } let catchInfo = (form, currentNav) => { let acceptTerm = async form => { let getVariants = async form => { let variantsToReturn = {} if (form.variants.length > 0) { for (let i = 0; i < form.variants.length; i++) { const variant = form.variants[i] let field = document.querySelector(variant.field) if (field) { let value = field.value if (await value.length > 0) { variantsToReturn[variant.name] = value } else { value = field.innerText if (await value.length > 0) { variantsToReturn[variant.name] = value } } } else if (variant.required == true) { new _CVPROCESSBEAT('ERROR', 'initProcessUser', `Variante ${variant.field} para form ${form.name} não existe`) return undefined } } } return variantsToReturn } let email = document.querySelector(form.email) if (email) { email = await email.innerText == '' ? email.value : email.innerText if (await validateEmail(email) == true && _cAlreadyE.find(e => e === email) === undefined) { let variants = await getVariants(form) if (variants) { let user = new _User() user.email_address = email user.variants = variants user.origin = form.name if (form.optin_field == true) user.optin_field = true else { let optin_field = document.querySelector(form.optin_field) if (await optin_field) user.optin_field = optin_field.checked else { user.optin_field = false new _CVPROCESSBEAT('ERROR', 'initProcessUser', `Campo de opt-in ${form.optin_field} para form ${form.name} não existe`) } } _cAlreadyE.push(user.email_address) registerCvUser(user, session) } } } else new _CVPROCESSBEAT('ERROR', 'initProcessUser', `Campo de email ${form.email} para form ${form.name} não existe`) } if (_cCurrentNavigation.cleanUrl === currentNav.cleanUrl) { if (form.term_field == true) { acceptTerm(form) } else { let termField = document.querySelector(form.term_field) if (termField) { if (termField.checked == true) acceptTerm(form) else if (termField.value == 'true') acceptTerm(form) } else new _CVPROCESSBEAT('ERROR', 'initProcessUser', `Termo de consentimento ${form.term_field} para form ${form.name} não existe`) } } } for (let i = 0; i < _cvConfig.optin_forms.length; i++) { const form = _cvConfig.optin_forms[i] if (form.url == 'all') mapped(form, navigation) else if (navigation.cleanUrl.indexOf(form.url) >= 0) mapped(form, navigation) } } else new _CVPROCESSBEAT('ERROR', 'initProcessUser', 'Não existem formulários de opt-in para captação de usuários') } async function initProcessOrder(navigation, session) { if (window[_cvConfig.functions.isOrderPage]) { if (session.has_order == false) { if (await window[_cvConfig.functions.isOrderPage](navigation) == true) { if (localStorage._cvcart) { let cart = new _Cart() cart.init() cart.type = 2 new _PayloadCart(cart).send() cart.clear() } return true } else return false } else return true } else { new _CVPROCESSBEAT('ERROR', _cvConfig.functions.isOrderPage, 'Função de identificação de página de compra não existe na tag') return false } } async function initProcessCheckout(navigation) { if (window[_cvConfig.functions.forOriginCart]) { if (localStorage._cvcart) { let origin = await window[_cvConfig.functions.forOriginCart](navigation) let cart = new _Cart() cart.init() if (cart.id_origin != origin) { cart.id_origin = origin cart.save() cart.type = 1 new _PayloadCart(cart).send() } } } else new _CVPROCESSBEAT('ERROR', _cvConfig.functions.forOriginCart, 'Função de origem de carrinho não existe na tag') } async function registerCvUser(user, session) { let hash = await triggerRequest('post', user, _cvConfig.endpoints.user, true) if (hash) { let saveSession = async (session, hashCookie) => { session.identified = true session.hash = hashCookie setCookieCV('_cvhash', JSON.stringify({ hash: hashCookie, identified: true }), 365) await session.save() session = new _Session().init() let navigation = new _Navigation() new _PayloadNavigation(navigation, session, true).send() } if (session.identified == false) { saveSession(session, hash) } else if (session.hash != hash) { saveSession(session, hash) } } else new _CVPROCESSBEAT('ERROR', 'registerCvUser', 'Requisição para registro de usário não retornou a hash esperada') } async function initPopPushs(navigation, session, pushs) { let pushToProcess = [] for (let i = 0; i < pushs.length; i++) { let rules = pushs[i].rules let passHomolog = true if (rules.homologHashs != false) { passHomolog = false if (session.identified == true) { let index = await rules.homologHashs.findIndex(e => e == session.hash) if (index >= 0) passHomolog = true else passHomolog = false } } else { if (rules.identified == 'all') passHomolog = true else if (await rules.identified == true && session.identified == false) passHomolog = false else if (await rules.identified == false && session.identified == true) passHomolog = false } if (passHomolog == true) { let passPage = true for (let j = 0; j < rules.pagesToIgnore.length; j++) { if (navigation.cleanUrl.indexOf(rules.pagesToIgnore[j]) >= 0) passPage = false } if (passPage == true) { if (rules.pagesToShow == 'all') { pushToProcess.push(pushs[i]) } else { let valid = false for (let j = 0; j < rules.pagesToShow.length; j++) { if (rules.pagesToShow[j] == '$product') { valid = await window[_cvConfig.functions.isProductPage](navigation) != undefined ? true : false if (valid == true) break } else if (rules.pagesToShow[j] == '$categorie') { valid = await window[_cvConfig.functions.isCategoriePage](navigation) if (valid == true) break } else if (navigation.cleanUrl == rules.pagesToShow[j]) { valid = true; break } } if (valid == true) { pushToProcess.push(pushs[i]) } } } } } if (pushToProcess.length > 0) processPopPushs(navigation, pushToProcess, 0, session) } async function processPopPushs(navigation, pushs, index, session) { let initPush = push => { let show = true if (push.rules.hasCart === true) { if (localStorage._cvcart) { let cart = new _Cart().init() if (cart.hasActiveProducts === true) show = true else show = false } else show = false } if (show == true) { if (push.rules.show.mouseLeave == true) { let lock = false document.addEventListener('mouseleave', () => { if (!lock) { new _Push(push).showPush(navigation) lock = true } }) } else if (push.rules.show.pageLoad == true) new _Push(push).showPush(navigation) else if (push.rules.show.clickSelectors != false) { // dev selectors } } else { if (index + 1 < pushs.length) { processPopPushs(navigation, pushs, index + 1, session) } } } let currentPush = pushs[index] if (session.pushs) { let sessionIndex = await session.pushs.findIndex(e => e.name == currentPush.name) if (sessionIndex >= 0) { if (currentPush.rules.oneTimeClick === false || (currentPush.rules.oneTimeClick === true && session.pushs[sessionIndex].clicks == 0)) { if (session.pushs[sessionIndex].attempts < currentPush.rules.maxTry) { if (currentPush.rules.intervalPagesToTry < session.pushs[sessionIndex].intervalAttempts) { initPush(currentPush) } else if (index + 1 < pushs.length) processPopPushs(navigation, pushs, index + 1, session) } else if (index + 1 < pushs.length) processPopPushs(navigation, pushs, index + 1, session) } else if (index + 1 < pushs.length) processPopPushs(navigation, pushs, index + 1, session) } else { initPush(currentPush) } } else { initPush(currentPush) } } async function processCartAddProduct(product, paramToCompare, send, isEquals, check) { let cart = new _Cart() if (localStorage._cvcart) { cart.init() await cart.insertProduct(product, paramToCompare, isEquals, check) if (send) { cart.type = 1 new _PayloadCart(cart).send() } } else { cart.cart_id = await generateCVID() cart.created_at = new Date() cart.record_duration = _cvConfig.cart_duration await cart.insertProduct(product, paramToCompare, isEquals, check) if (send) { cart.type = 1 new _PayloadCart(cart).send() } } } async function processCartRemoveProduct(paramToCompare, valueToCompare, send, isEquals) { let cart = new _Cart() if (localStorage._cvcart) { cart.init() await cart.deleteProduct(paramToCompare, valueToCompare, isEquals) if (send) new _PayloadCart(cart).send() } else new _CVPROCESSBEAT('INFO', 'processCartRemoveProduct', 'Objeto de persistência de carrinho não existe') } async function initCustomClicks(navigation, session, custom) { for (let i = 0; i < custom.length; i++) { if (custom[i].page == 'all') { processCustomClicks(custom[i], session) } else if (custom[i].page == navigation.cleanUrl) { processCustomClicks(custom[i], session) } } } async function processCustomClicks(custom, session) { let indexClick = session.custom_clicks.findIndex(e => e.name == custom.name) let mapedElements = async () => { let element if (await custom.unique === true) { element = document.querySelector(custom.selector) } else { element = document.querySelectorAll(custom.selector) if (await custom.index != 'all') element = element[custom.index] } if (element) { if (await custom.unique === true || custom.index != 'all') { element.addEventListener('click', countClick) } else { for (let i = 0; i < element.length; i++) { element[i].addEventListener('click', countClick) } } } else { new _CVPROCESSBEAT('ERROR', 'processCustomClicks', `Não foi possível mapear o elemento configurado no clique personalizado`, custom) } } let countClick = async () => { custom.clicked_at = new Date() if (indexClick >= 0) { session.custom_clicks[indexClick].countClick++ } else { if (await custom.count === true) custom.countClick = 1 session.custom_clicks.push(custom) } session.save() } if (custom.count === false) { if (indexClick >= 0) mapedElements() } else mapedElements() } async function getAsyncInfos(navigation, session) { if (_cvConfig.geolocation === true) { navigation.geolocation = await navigation.getGeolocation().catch(err => { }) } if (window[_cvConfig.functions.getLoged]) window[_cvConfig.functions.getLoged](navigation, session) } function triggerRequest(method, payload, endpoint, hasReturn) { return new Promise((resolve, reject) => { let request = new XMLHttpRequest() request.open(method, endpoint, true) request.setRequestHeader('Authorization', _cvConfig.customerKey) request.setRequestHeader('Access-Control-Allow-Origin', 'http://localhost:3500') if (payload) { request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8') request.send(JSON.stringify(payload)) } else { request.send() } request.onload = () => { if (request.status === 200) { if (hasReturn) resolve(request.responseText) else resolve() } else { new _CVPROCESSBEAT('INFO', 'triggerRequest', `Status ${request.status} para endpoint ${endpoint}`, payload) reject() } } request.onerror = () => { new _CVPROCESSBEAT('ERROR', 'triggerRequest', `Status ${request.status} para endpoint ${endpoint}`, payload) reject() } }) } async function setCookieCV(name, value, days) { let d = new Date() d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)) let expires = "expires=" + d.toUTCString() document.cookie = name + "=" + value + ";" + expires + ";path=/" } async function getCookieCV(cname) { return new Promise(async resolve => { let name = cname + "=" let decodedCookie = decodeURIComponent(document.cookie) let ca = decodedCookie.split(';') for (let i = 0; i < ca.length; i++) { let c = ca[i] while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { resolve(c.substring(name.length, c.length)) } } resolve(0) }) } async function generateCVID() { let s4 = () => { return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) } return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() } async function replaceChain(s) { return await s.replaceAll('-', ' ').replaceAll('_', ' ').replaceAll(' ', ' ') .replaceAll('á', 'a').replaceAll('à', 'a').replaceAll('ã', 'a').replaceAll('â', 'a') .replaceAll('é', 'e').replaceAll('è', 'e').replaceAll('ê', 'e') .replaceAll('í', 'i').replaceAll('ì', 'i') .replaceAll('ó', 'o').replaceAll('ò', 'o').replaceAll('õ', 'o').replaceAll('ô', 'o') .replaceAll('ú', 'u').replaceAll('ù', 'u').replaceAll('û', 'u') .replaceAll('ç', 'c').trim().toLowerCase() } async function validateEmail(email) { if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$/.test(email)) return true else return false } function processTimeSpend() { let startInterval = () => { setInterval(() => { let time = Number(localStorage._cvtime) time++ localStorage.setItem('_cvtime', time) }, 1000) } if (_cvConfig.singlePage == true) { if (!localStorage._cvtime) { _cvConfig.intervalStarts = true localStorage.setItem('_cvtime', '0') startInterval() } else if (!_cvConfig.intervalStarts) { _cvConfig.intervalStarts = true startInterval() } } else { if (!localStorage._cvtime) localStorage.setItem('_cvtime', '0') startInterval() } } async function getUrlParameters(params, compare) { if (!params) params = location.search let results = {} if (params) { let query = params.substring(1); query.split('&').forEach(p => { let item = p.split('=') if (compare) { if (item[0].indexOf(compare) >= 0) { results[item[0]] = decodeURIComponent(item[1]) } } else { results[item[0]] = decodeURIComponent(item[1]) } }) return results } else { return results } } async function processListObjs(values, list) { if (values.length > 0) { if (list.length > 0) { let listToNav = [] for (let i = 0; i < values.length; i++) { listToNav.push({ value: values[i], count: 1, level: i }) let index = await checkValueExists(values[i], list) if (index >= 0) { list[index].count++ } else { await list.push({ value: values[i], count: 1, level: i }) } } await orderCvList(list, 'count') return [list, listToNav] } else { let listReturn = [] for (let i = 0; i < values.length; i++) { listReturn.push({ value: values[i], count: 1, level: i }) } await orderCvList(listReturn, 'count') return [listReturn, listReturn] } } else { await orderCvList(list, 'count') return [list, undefined] } } async function checkValueExists(value, list) { for (let i = 0; i < list.length; i++) { if (await value == list[i].value) { return i } } return -1 } async function orderCvList(list, param) { if (list.length > 0) { let n = list.length for (let i = 1; i < n; ++i) { let key = list[i] let j = i - 1 while (j >= 0 && list[j][param] < key[param]) { list[j + 1] = list[j] j = j - 1 } list[j + 1] = key } } } class _Navigation { constructor() { this.urlBase = decodeURIComponent(window.location.origin + '/') this.url = decodeURIComponent(window.location.href) this.params = decodeURIComponent(window.location.search) this.cleanUrl = this.url.replace(this.params, '') this.pathsEncoded = window.location.pathname == '/' ? '' : window.location.pathname this.pathsDecoded = decodeURIComponent(this.pathsEncoded) this.geolocation = undefined this.browserInfos = this.getBrowserInfos(), this.categories_view = undefined this.products_view = undefined this.searches = undefined } getBrowserInfos() { let userAgentString = navigator.userAgent let chromeAgent = userAgentString.indexOf("Chrome") > -1 ? 'chrome' : undefined let IExplorerAgent = userAgentString.indexOf("MSIE") > -1 || userAgentString.indexOf("rv:") > -1 ? 'internet explorer' : undefined let firefoxAgent = userAgentString.indexOf("Firefox") > -1 ? 'firefox' : undefined let safariAgent = userAgentString.indexOf("Safari") > -1 ? 'safari' : undefined if ((chromeAgent) && (safariAgent)) safariAgent = undefined let operaAgent = userAgentString.indexOf("OP") > -1 ? 'opera' : undefined if ((chromeAgent) && (operaAgent)) chromeAgent = undefined return { userAgent: userAgentString, language: navigator.language, browser: safariAgent || chromeAgent || IExplorerAgent || operaAgent || firefoxAgent } } async getGeolocation() { return new Promise(resolve => { try { navigator.geolocation.getCurrentPosition(position => { resolve(position) }) } catch (e) { } }) } async getPathCategories(notValidCategories) { let validCategories = [] if (this.pathsEncoded != '') { let pathToCompare = this.pathsEncoded if (pathToCompare.slice(0, 1) == '/') pathToCompare = pathToCompare.slice(1) let pathCategories = pathToCompare.split('/') for (let i = 0; i < pathCategories.length; i++) { pathCategories[i] = decodeURIComponent(pathCategories[i]) let valid = true for (let j = 0; j < notValidCategories.length; j++) { if (await notValidCategories[j] == '$pagination') { if (isNaN(pathCategories[i]) == false) valid = false } else if (await notValidCategories[j] == pathCategories[i] || pathCategories[i] == '') valid = false } if (valid) validCategories.push(pathCategories[i].replaceAll('-', ' ')) } } return validCategories } } class _PayloadNavigation { constructor(navigation, session, hasChange) { this.url = navigation.url this.cleanUrl = navigation.cleanUrl this.params = navigation.params this.paths = navigation.pathsDecoded this.session = session.session this.hash = session.hash this.identified = session.identified this.page_views = session.page_views this.utms = session.utms.length > 0 ? session.utms : undefined this.permanence_time = Number(localStorage._cvtime) this.browserInfos = session.bis == false ? navigation.browserInfos : undefined this.categories_view = session.categories_view.length > 0 ? session.categories_view : undefined this.products_view = session.products_view.length > 0 ? session.products_view : undefined this.searches = session.searches.length > 0 ? session.searches : undefined this.navigation = { categories_view: navigation.categories_view, products_view: navigation.products_view, searches: navigation.searches } this.has_order = session.has_order this.custom_clicks = session.custom_clicks.length > 0 ? session.custom_clicks : undefined this.pushs = session.pushs this.shc = hasChange this.nps_answered = session.nps_answered this.geolocation = session.gls == false ? navigation.geolocation : undefined this.started_at = session.started_at } send() { triggerRequest('post', this, _cvConfig.endpoints.navigation, false) } } class _Session { constructor() { this.session = undefined this.hash = undefined this.identified = false this.page_views = 1 this.utms = [] this.categories_view = [] this.products_view = [] this.searches = [] this.custom_clicks = [] this.pushs = undefined this.nps_answered = undefined this.has_order = false this.started_at = undefined this.updated_at = undefined this.bis = false this.gls = false } init() { let session = JSON.parse(localStorage._cvsession) this.session = session.session this.hash = session.hash this.identified = session.identified this.page_views = session.page_views this.utms = session.utms this.categories_view = session.categories_view this.products_view = session.products_view this.searches = session.searches this.custom_clicks = session.custom_clicks this.pushs = session.pushs this.nps_answered = session.nps_answered this.has_order = session.has_order this.started_at = new Date(session.started_at) this.updated_at = new Date(session.updated_at) this.bis = session.bis this.gls = session.gls return this } save() { this.updated_at = new Date() localStorage.setItem('_cvsession', JSON.stringify(this)) } remove() { localStorage.removeItem('_cvsession') localStorage.removeItem('_cvtime') } async processSessionTime() { let now = new Date() let cloneUpdate = new Date(this.updated_at) cloneUpdate.setMinutes(cloneUpdate.getMinutes() + _cvConfig.session_timeout) if (now.getTime() >= cloneUpdate) { this.remove() return true } else return false } processTimeOut(sessionTimeOut) { let intervalId = setTimeout(() => { triggerRequest('post', { session: this.session }, _cvConfig.endpoints.endsession, false) this.remove() clearInterval(intervalId) }, sessionTimeOut * 60000) } } class _Cart { constructor() { this.cart_id = undefined this.products = [] this.hasActiveProducts = false this.type = 1 this.id_origin = undefined this.created_at = undefined this.updated_at = undefined this.record_duration = undefined } init() { let cart = JSON.parse(localStorage._cvcart) this.cart_id = cart.cart_id this.products = cart.products this.hasActiveProducts = cart.hasActiveProducts this.type = cart.type this.id_origin = cart.id_origin this.created_at = new Date(cart.created_at) this.updated_at = new Date(cart.updated_at) this.record_duration = cart.record_duration return this } save() { this.updated_at = new Date() localStorage.setItem('_cvcart', JSON.stringify(this)) } async processCartDuration() { let now = new Date() let cloneUpdate = new Date(this.updated_at) cloneUpdate.setMinutes(cloneUpdate.getMinutes() + this.record_duration) if (now.getTime() >= cloneUpdate) { await this.clear() } } async clear() { localStorage.removeItem('_cvcart') } async clearRemoved() { this.hasActiveProducts = false for (let i = 0; i < this.products.length; i++) { if (await this.products[i].removed == true) { this.products.splice(i, 1) i-- } else this.hasActiveProducts = true } } async insertProduct(productToInsert, param, isEquals, check) { if (productToInsert[param]) { let index = false for (let i = 0; i < this.products.length; i++) { let product = this.products[i] try { if (isEquals == true) { if (await product[param] == productToInsert[param]) { index = i break } } else { if (await productToInsert[param].indexOf(product[param]) >= 0) { index = i break } } } catch (err) { new _CVPROCESSBEAT('ERROR', 'insertProduct', err) } } if (index === false) { this.products.push(productToInsert) this.hasActiveProducts = true this.save() } else { if (!check) { if (this.products[index].selectable) this.products[index].selectable.qtd += productToInsert.selectable ? productToInsert.selectable.qtd : 1 else this.products[index].selectable = { qtd: productToInsert.selectable ? productToInsert.selectable.qtd : 1 } } if (this.products[index].removed == true) this.products[index].removed = false this.save() } } else { new _CVPROCESSBEAT('ERROR', 'insertProduct', 'Produto a ser inserido não possui parâmetro de comparação informado', productToInsert) } } async deleteProduct(param, valueToCompare, isEquals) { let exist = false for (let i = 0; i < this.products.length; i++) { let product = this.products[i] if (isEquals) { if (await product[param] == valueToCompare) { exist = true product.removed = true } } else { if (await valueToCompare.indexOf(product[param]) > -1) { exist = true product.removed = true } } } if (exist) { let active = this.products.findIndex(e => e.removed !== true) this.hasActiveProducts = active >= 0 ? true : false this.save() } else new _CVPROCESSBEAT('INFO', 'deleteProduct', 'Produto a ser deletado não foi encontrado no objeto de carrinho') } async checkProducts(currentProducts, param, isEquals) { let update = false for (let i = 0; i < currentProducts.length; i++) { // insert products in cart let exist = false for (let j = 0; j < this.products.length; j++) { if (isEquals == true) { if (await this.products[j][param] == currentProducts[i][param]) { exist = true break } } else { if (await currentProducts[i][param].indexOf(this.products[j][param]) >= 0) { exist = true break } } } if (!exist) { await processCartAddProduct(currentProducts[i], param, false, isEquals) update = true } else { await processCartAddProduct(currentProducts[i], param, false, isEquals, true) } } for (let i = 0; i < this.products.length; i++) { // delete products that are no longer in cart let exist = false for (let j = 0; j < currentProducts.length; j++) { if (isEquals == true) { if (await this.products[i][param] == currentProducts[j][param]) { exist = true break } } else { if (await currentProducts[j][param].indexOf(this.products[i][param]) >= 0) { exist = true break } } } if (!exist && this.products[i].removed != true) { update = true await processCartRemoveProduct(param, this.products[i][param], false, true) } } if (update) new _PayloadCart(this.init()).send() } } class _User { constructor() { this.email_address = undefined this.variants = {} this.origin = undefined this.optin_field = false } } class _Recommendation { constructor() { this.products = undefined } init() { let recommendation = JSON.parse(sessionStorage._cvrecommendation) this.products = recommendation.products return this } save() { sessionStorage.setItem('_cvrecommendation', JSON.stringify(this)) } } class _Product { constructor(product) { this.name = product.name this.sku = product.sku this.categories = product.categories this.url = product.url this.price = product.price this.selectable = product.selectable this.images = product.images this.defaultImage = product.defaultImage this.variants = product.variants this.removed = product.removed this.unavailable = product.unavailable } init() { if (sessionStorage._cvproduct) { let obj = JSON.parse(sessionStorage._cvproduct) this.name = obj.name this.sku = obj.sku this.categories = obj.categories this.url = obj.url this.price = obj.price this.selectable = obj.selectable this.images = obj.images this.defaultImage = obj.defaultImage this.variants = obj.variants this.removed = obj.removed this.unavailable = obj.unavailable return this } else return undefined } validate() { if (!this.name && !this.sku && this.categories.length > 0 && !this.price && !this.defaultImage) new _CVPROCESSBEAT('ERROR', 'validateProduct', 'Produto captado não atendeu os requisitos de validação', this) } save() { sessionStorage.setItem('_cvproduct', JSON.stringify(this)) } send() { triggerRequest('post', this, _cvConfig.endpoints.product, false) } } class _Push { constructor({ name, pushHTML, rules }) { this.name = name this.pushHTML = pushHTML this.rules = rules this.pushElement = undefined } showPush(navigation) { let alreadyPush = document.querySelector('.cv-main-container') if (!alreadyPush) { setTimeout(() => { let pushElement = document.createElement('section') pushElement.className = 'cv-main-container' pushElement.innerHTML = this.pushHTML this.pushElement = pushElement if (this.rules.recommendation === true) { this.processPushRecommendation(navigation) } else { document.body.appendChild(pushElement) if (this.rules.customCss) document.head.insertAdjacentHTML('beforeend', this.rules.customCss) this.addEvents() this.processShow(navigation) } }, 3000) } } async processPushRecommendation(navigation) { if (sessionStorage._cvrecommendation) { let session = new _Session().init() let recommendation = new _Recommendation().init() this.showPushRecommendation(session, recommendation, navigation) } else { let session = new _Session().init() let payload = { hash: session.hash, categories: session.categories_view, rules: { limit_of_products: 5 } } let recCall = await triggerRequest('post', payload, _cvConfig.endpoints.recommendation, true).catch(err => { }) recCall = JSON.parse(recCall) if (recCall.recommended) { let recommendation = new _Recommendation() recommendation.products = recCall.recommended recommendation.save() this.showPushRecommendation(session, recommendation, navigation) }//else no products to recommended } } async showPushRecommendation(session, recommendation, navigation) { let indexRecom = 0 let productRecom = recommendation.products[indexRecom] if (session.products_view.length > 0) { for (let i = 0; i < session.products_view.length; i++) { if (session.products_view[i].value == productRecom.sku || productRecom.show == true) { indexRecom += 1 if (await recommendation.products[indexRecom] != undefined) { productRecom = recommendation.products[indexRecom] } else { productRecom = undefined; break } } else break } } if (productRecom) { this.pushElement.style = 'display: none' document.body.appendChild(this.pushElement) if (this.rules.customCss) document.head.insertAdjacentHTML('beforeend', this.rules.customCss) let titleElement = document.querySelector('.cv-push-box p') titleElement.innerText = productRecom.name let imgElement = document.querySelector('.cv-push-img img') imgElement.src = productRecom.defaultImage this.pushElement.style = '' recommendation.products[indexRecom].show = true recommendation.save() this.addEvents(productRecom.url + this.rules.params) this.processShow(navigation, productRecom) }//else no products to recommended } addEvents(redirect) { let closedElement = document.querySelector('.cv-client-header .svg-close') closedElement.addEventListener('click', () => { this.pushElement.remove() }) // let boxElement = document.querySelector('.cv-push-box') // boxElement.addEventListener('click', () => { this.click(redirect) }) // let imgElement = document.querySelector('.cv-push-img') // imgElement.addEventListener('click', () => { this.click(redirect) }) } async processShow(navigation, product) { let session = new _Session().init() let createPush = exist => { let pushPayload = { name: this.name, attempts: 1, clicks: 0, intervalAttempts: 0, history: [{ url: navigation.cleanUrl, created_at: new Date(), click: 0, product: product ? { sku: product.sku } : undefined }] } if (exist) { session.pushs.push(pushPayload) } else { session.pushs = [pushPayload] } session.save() } if (session.pushs) { let index = await session.pushs.findIndex(e => e.name == this.name) if (index >= 0) { session.pushs[index].attempts++ session.pushs[index].intervalAttempts = 0 session.pushs[index].history.push({ url: navigation.cleanUrl, created_at: new Date(), click: 0, product: product ? { sku: product.sku } : undefined }) session.save() } else createPush(true) } else createPush(false) } async click(redirect) { let session = new _Session().init() let index = await session.pushs.findIndex(e => e.name == this.name) if (index >= 0) { session.pushs[index].clicks++ session.pushs[index].history[session.pushs[index].history.length - 1].click++ session.save() if (redirect) window.location.replace(redirect) else window.location.replace(this.rules.redirect) } else new _CVPROCESSBEAT('ERROR', 'clickPush', `Objeto push ${this.name} não existe na lista de pushs da sessão`) } } class _PayloadCart { constructor(cart) { let session = new _Session().init() this.session = session.session this.hash = session.hash this.identified = session.identified this.utms = session.utms.length > 0 ? session.utms : undefined this.cart_id = cart.cart_id this.products = cart.products this.type = cart.type this.id_origin = cart.id_origin this.created_at = cart.created_at this.updated_at = cart.updated_at this.pushs = session.pushs } send() { triggerRequest('post', this, _cvConfig.endpoints.cart, false) } } class _CVPROCESSBEAT { constructor(type, method, message, payload) { this.type = type this.method = method this.message = message this.payload = payload this.process() } process() { switch (this.type) { case 'INFO': console.log(`[CV.INFO.${this.method}] ${this.message}`) break case 'ERROR': triggerRequest('POST', this, _cvConfig.endpoints.beat, false) break default: break } } } const _cvConfig = { "customerKey": "9847069TII", "session_timeout": 5, "windowLoad": false, "cart_duration": 10080, "geolocation": false, "singlePage": true, "notValidCategories": [""], "endpoints": { "navigation": "https://oci.claravista-api.com.br/register/navigation", "cart": "https://oci.claravista-api.com.br/register/cart", "product": "https://oci.claravista-api.com.br/register/product", "user": "https://oci.claravista-api.com.br/register/user", "beat": "https://oci.claravista-api.com.br/register/beat", "recommendation": "https://dashp5.claravista.com.br/core/api/v.2.2/recommendation/push" }, "functions": { "forNavCategories": "_cGetNavCategorie", "isProductPage": "_cIsProductPage", "isCategoriePage": "_cIsCategoriePage", "forNavProduct": "_cGetNavProduct", "forNavSearches": "_cGetSeachers", "forProductInfos": "_cGetProductInfo", "addProductToCart": "_cAddProductToCart", "removeProductToCart": "_cRemoveProductToCart", "forOriginCart": "_cOriginCartPage", "isOrderPage": "_cIsOrderPage", "getLoged": "_cGetLoged", "checkCartProducts": "_cCheckCartProducts" }, "custom_clicks": [], "pushs": [ { "name": "optin_modal", "active": true, "pushHTML": "

CADASTRE-SE

e seja um cliente Triider


Pedido Sem Compromisso

Receba orçamentos grátis
em 24 horas.

Economize Tempo

Compare preços e negocie
dentro da plataforma.

Poupe Dinheiro

Escolha o orçamento que
melhor cabe no seu bolso.

Profissionais Verificados

Conectamos você aos melhores
prestadores de serviço.

", "rules": { "show": { "mouseLeave": false, "pageLoad": true, "clickSelectors": false }, "homologHashs": [], "identified": false, "oneTimeClick": true, "maxTry": 1, "intervalPagesToTry": 0, "hasCart": false, "recommendation": false, "priority": 0, "pagesToShow": "all", "pagesToIgnore": ["cadastro", "conta", "entrar", "/u/", "ofertasblackissimo"], "redirect": "", "customCss": "" } } ], "optin_forms": [ { "name": "login_page", "url": "https://www.triider.com.br/u/entrar", "default_visible": false, "email": "input[name=\"email\"]", "trigger": ".sc-VigVT.bSzmGO", "term_field": true, "optin_field": true, "variants": [] }, { "name": "register_page", "url": "https://www.triider.com.br/u/cadastro/conta", "default_visible": false, "email": "input[name=\"email\"]", "trigger": ".sc-VigVT.zcXIz", "term_field": true, "optin_field": true, "variants": [ { "name": "name", "field": "input[name=\"name\"]", "required": true } ] }, { "name": "newsletter", "url": "all", "default_visible": false, "email": ".pipz-input.email[name=\"contact.email\"]", "trigger": ".pipz-button", "term_field": true, "optin_field": true, "variants": [ { "name": "name", "field": ".pipz-input.name[name=\"contact.name\"]", "required": true } ] } ] } //ok async function _cIsProductPage() { if (document.querySelector('.styles_content-cta__1Z_1q .styles_content-cta__title__FlU-4') && document.querySelector('.styles_content-cta__1Z_1q .content-cta__pedir-orcamentos')) return true else false } //ok async function _cIsCategoriePage() { return false } //ok async function _cIsSearchPage() { return false } //ok async function _cGetNavCategorie(navigation) { if (await _cIsProductPage()) { let toReturn = [] let breadcrumb = document.querySelectorAll('.styles_breadcrumb__1zd8N .styles_breadcrumb__link__2wtlD') for (let i = 0; i < breadcrumb.length; i++) { let categorie = breadcrumb[i].innerText categorie = await replaceChain(categorie) if (categorie != 'triider') toReturn.push(categorie) } return toReturn } else { let categories = await navigation.getPathCategories(_cvConfig.notValidCategories) for (let i = 0; i < categories.length; i++) { categories[i] = await replaceChain(categories[i]) } return categories } } //ok async function _cGetNavProduct() { if (await _cIsProductPage()) { let name = document.querySelector('.styles_content-cta__1Z_1q .styles_content-cta__title__FlU-4').innerText name = name.toLowerCase().trim() return [name] } else return [] } //ok async function _cGetSeachers() { return [] } //ok async function _cGetProductInfo(navigation) { let product = { variants: {}, categories: [], images: [] } try { let name = document.querySelector('.styles_content-cta__1Z_1q .styles_content-cta__title__FlU-4').innerText name = name.toLowerCase().trim() product.name = name product.sku = name let price = document.querySelector('.styles_content-cta__1Z_1q .content-cta__pedir-orcamentos .styles_content-cta__price__NO2Dh').innerText price = price.toLowerCase().trim() price = price.replaceAll('r$', '').replaceAll(',', '.') if (price.indexOf('-') >= 0) { price = price.split('-') let minPrice = Number(Number(price[0]).toFixed(2)) product.price = minPrice product.variants.minPrice = minPrice product.variants.maxPrice = Number(Number(price[1]).toFixed(2)) } else { product.price = Number(Number(price).toFixed(2)) } product.url = navigation.cleanUrl product.unavailable = false let breadcrumb = document.querySelectorAll('.styles_breadcrumb__1zd8N .styles_breadcrumb__link__2wtlD') for (let i = 0; i < breadcrumb.length; i++) { let categorie = breadcrumb[i].innerText categorie = await replaceChain(categorie) if (categorie != 'triider') product.categories.push(categorie) } } catch (err) { new _CVPROCESSBEAT('INFO', '_cGetProductInfo', `Informações primárias estão nulas. Erro: ${err}`) throw `Informações primárias estão nulas. Erro: ${err}` } try { let tag = document.querySelector('.styles_content-cta__1Z_1q .styles_content-cta__cta-category__2vWEQ').innerText product.variants.tag = tag.toLowerCase().trim() product.variants.rating = document.querySelector('.styles_content-cta__1Z_1q .styles_content-cta__rating-total__3QnUB').innerText } catch (err) { new _CVPROCESSBEAT('INFO', '_cGetProductInfo', `Informações variantes estão nulas. Erro: ${err}`) throw `Informações variantes estão nulas. Erro: ${err}` } try { let defaultImage = document.querySelector('.slick-track .slick-slide.slick-active img') product.defaultImage = defaultImage.src product.images.push(defaultImage.src) } catch (err) { new _CVPROCESSBEAT('INFO', '_cGetProductInfo', `Seletor de imagem para registro de produto é nulo. Erro: ${err}`) throw `Seletor de imagem para registro de produto é nulo. Erro: ${err}` } return product } //ok async function _cAddProductToCart() { let buttons = ['.content-cta__pedir-orcamentos .styles_button__3MRM9'] let mapped = element => { element.addEventListener('click', addClick) element.addEventListener('touchstart', addClick) } let addClick = () => { let product = new _Product({}).init() if (product != undefined) processCartAddProduct(product, 'sku', true, true) else new _CVPROCESSBEAT('ERROR', '_cAddProductToCart', 'Produto a ser adicionado no carrinho não existe') } for (let i = 0; i < buttons.length; i++) { let element = document.querySelector(buttons[i]) if (element) mapped(element) } } //ok - método principal alterado para captação das diversas origens async function _cOriginCartPage(navigation) { if (navigation.pathsDecoded.indexOf('pedir-orcamento') >= 0) { return 1 } else if (navigation.pathsDecoded.indexOf('Form') >= 0) { return 2 } else if (navigation.pathsDecoded.indexOf('orcamentos') >= 0) { return 3 } else if (navigation.pathsDecoded.indexOf('fechar') >= 0) { return 4 } else if (navigation.pathsDecoded.indexOf('contratacao-realizada') >= 0) { return 5 } else if (navigation.pathsDecoded.indexOf('pagar') >= 0) { return 6 } else if (navigation.pathsDecoded.indexOf('pagamento-autorizado') >= 0) { return 7 } } //ok async function _cRemoveProductToCart() { // not remove } //ok async function _cIsOrderPage(navigation) { if (navigation.pathsDecoded.indexOf('pedido-realizado') >= 0) { return true } else { return false } } //ok async function _cGetLoged(navigation, session) { if (localStorage.pipz_user_traits && session.identified == false) { let trailStorage = localStorage.pipz_user_traits trailStorage = JSON.parse(trailStorage) if (await validateEmail(trailStorage.email)) { let user = new _User() user.email_address = trailStorage.email user.origin = 'loged' user.optin_field = true user.variants.city = trailStorage.city_name user.variants.state = trailStorage.state user.variants.name = trailStorage.name user.variants.phone = trailStorage.phone user.variants.birthdate = trailStorage.birthdate registerCvUser(user, session) } } } //ok async function _cCheckCartProducts(navigation) { return undefined } async function _cGetOrders() { let orders = document.querySelectorAll('.sc-jUpvKA.jAJaUL') if (orders.length > 0) { let listOrders = [] for (let i = 0; i < orders.length; i++) { try { let serviceName = orders[i].querySelector('.sc-jRuhRL.sc-eopZyb.AIgcl').innerText.toLowerCase().trim() let serviceRequest = orders[i].querySelector('.sc-eEieub.dvCBVl').innerText let serviceEstimate = orders[i].querySelector('.sc-cqPOvA.eaZYFo') let serviceStatus = orders[i].querySelector('.sc-RbTVP.jTYUWw') if (!serviceStatus) { serviceStatus = orders[i].querySelector('.sc-drlKqa.bRbnCP') } listOrders.push({ name: serviceName, created_at: serviceRequest, status: serviceStatus ? serviceStatus.innerText.toLowerCase().trim() : undefined, estimate: serviceEstimate ? serviceEstimate.innerText : undefined }) } catch (err) { new _CVPROCESSBEAT('INFO', '_cGetOrders', `Elementos de acesso as informações são nulas. Erro: ${err}`) throw `Elementos de acesso as informações são nulas. Erro: ${err}` } } return listOrders } else return undefined } async function _cpAddProductToCart(navigation) { let mapped = (element, callback) => { element.addEventListener('click', callback) element.addEventListener('touchstart', callback) } //for home button let homeButton = document.querySelector('.sc-bdVaJa.hnwIKb .btn-chamar') if (homeButton) { let homeCallback = () => { let state = document.querySelector('.sc-bdVaJa.hnwIKb .city-drop') let service = document.querySelector('.sc-bdVaJa.hnwIKb .service-drop') if (state && service) { state = state.innerText.toLowerCase().trim() service = service.innerText.toLowerCase().trim() if (state && service) { let product = new _Product({ name: service, sku: service, selectable: { qtd: 1 }, variants: { origin: 'home', state: state } }) processCartAddProduct(product, 'sku', true, true) } } } mapped(homeButton, homeCallback) } //for home list click let homeServiceList = document.querySelectorAll('.sc-bdVaJa.hnwIKb #dropdown-list li') if (homeServiceList.length > 0) { let blogCallback = event => { let state = document.querySelector('.sc-bdVaJa.hnwIKb .city-drop') let service = event.target if (state && service) { state = state.innerText.toLowerCase().trim() service = service.innerText.toLowerCase().trim() if (state && service) { let product = new _Product({ name: service, sku: service, selectable: { qtd: 1 }, variants: { origin: 'home', state: state } }) processCartAddProduct(product, 'sku', true, true) } } } for (let i = 0; i < homeServiceList.length; i++) { mapped(homeServiceList[i], blogCallback) } } let blogServiceList = document.querySelectorAll('.sc-qrIAp.idHNEg #dropdown-list li') if (blogServiceList.length > 0) { let blogCallback = event => { let state = document.querySelector('.sc-iqzUVk.edkwoX') let service = event.target if (state && service) { state = state.innerText.toLowerCase().trim() service = service.innerText.toLowerCase().trim() if (state && service) { let product = new _Product({ name: service, sku: service, selectable: { qtd: 1 }, variants: { origin: 'blog', url: navigation.cleanUrl, state: state } }) processCartAddProduct(product, 'sku', true, true) } } } for (let i = 0; i < blogServiceList.length; i++) { mapped(blogServiceList[i], blogCallback) } } } boot()