Skip to content

Instantly share code, notes, and snippets.

@Sarapulov
Last active June 21, 2023 20:17
Show Gist options
  • Save Sarapulov/98793001c2d4a49ea481870bcb409130 to your computer and use it in GitHub Desktop.
Save Sarapulov/98793001c2d4a49ea481870bcb409130 to your computer and use it in GitHub Desktop.
Zendesk Widget customisation allowing to deeplink particular Widget channels [PUBLIC]. Version of Zendesk Guide.
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<script >
// This is upgraded Zendesk Widget handler script. Ver: 1.11 Updated: 2020-01-14
// It is intended to simplify the control of Widget behaviour through custom JavaScript configuration
// Script is using Widget JS API https://developer.zendesk.com/embeddables/docs/widget/introduction
// Script expects integrated Chat experience to be available
// To run the script follow the steps below:
// 1. update WIDGET_KEY
// 2. Decide which part of the Widget you want to run using runWidgetLogic().init({settings object});
// 3. Configure {settings object}
// 4. Copy final code to desired pages and run.
(function() {
var WIDGET_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; // widget key
function fireWidgetOnLoad() {
var zendeskScript = document.createElement('script');
zendeskScript.src = 'https://static.zdassets.com/ekr/snippet.js?key=' + WIDGET_KEY;
zendeskScript.id = 'ze-snippet';
zendeskScript.addEventListener('load', function() {
var name, email;
try {
name = HelpCenter && HelpCenter.user.name || '';
email = HelpCenter && HelpCenter.user.email || '';
} catch(err) {
name = '';
email = '';
}
/**
* Change the Widget settings.
* @params {object} Config - required object defining Widget channels and settings
*/
runWidgetLogic().init({
// REQUIRED: enable HC Search?
HCSearch: true,
// REQUIRED: enable chat?
chat: true,
// REQUIRED: enable contact form?
contactForm: true,
// REQUIRED: enable Talk (call back)?
talk: false,
// REQUIRED: enable answerBot?
answerBot: false,
// OPTIONAL: Chat department configuration
// enabled: [''] will hide Department selector
// select: 'Other HR questions (Latvia)' will preset Chat department
departments: {
// enabled: [''],
select: "Employment Related Questions (Latvia)"
},
// OPTIONAL: Set widget language https://developer.zendesk.com/embeddables/docs/widget/core#setlocale
locale: 'en-us',
// load the Web Widget in the "show" or "hide" state: https://developer.zendesk.com/embeddables/docs/widget/core#show
widget_visibility: 'show',
articleLabels: ['test'],
prefill_name: name,
prefill_email: email,
identify_name: name,
identify_email: email,
// OPTIONAL: config for window.zESettings.webWidget
webWidgetConfig: {
// Specifies whether to enable or disable Google Analytics tracking. https://support.zendesk.com/hc/en-us/community/posts/360003215947
analytics: true,
zIndex: 9999999998,
contactOptions: {
enabled: true,
contactButton: {
'*': 'Get in Touch'
},
// The Chat Label on the Contact Options window
chatLabelOnline: {
'*': 'Live Chat'
},
chatLabelOffline: {
'*': 'Chat is Offline'
},
// The Contact Form Label
contactFormLabel: {
'*': 'Send us a message'
}
},
// The Widget Color
color: {
theme: '#9C8EB5'
},
launcher: {
// The Web Widget button title (HC/Contact Form are On)
label: {
'*': 'Need Help?'
},
// The Web Widget button title (HC is Off)
chatLabel: {
'*': 'Chat now'
},
// Set to true if you want to display the label in mobile browsers
mobile: {
labelVisible: false
}
},
helpCenter: {
// Sets the title of the Help Center Window
title: {
'*': 'nBrown Support'
},
// Sets the visibility of the button that would redirect the user to the full HC portal
originalArticleButton: true
},
contactForm: {
title: {
'*': 'nBrown Support'
},
// Set a specific ticket form. The form ID can be found in the URL while on the form page. Uncomment the following line to enable this.
//ticketForms: [{ id: 360000102598 }, { id: 360000102718 }],
// Enable to disable the attachments option in the Contact Forms
attachments: true
},
// Sets the visibility of the popout button
navigation: {
popoutButton: {
enabled: false
}
},
talk: {
nickname: 'CallBack_SPOT'
},
chat: {
prechatForm: {
// The Prechat greeting text
greeting: {
'*': 'Please fill out the form below to start the chat with us.'
}
},
title: {
'*': 'Chat with us'
},
concierge: {
// the Concierge parameters
avatarPath: 'https://theme.zdassets.com/theme_assets/1023626/8c5e1ce2dd7d5a649a8f297295e52666fa125d81.png',
name: 'Welcome to Live Chat',
title: {
'*': 'How can we help you today ?'
}
},
// Sets the visibility of the "Email Transcript" option on the chat window:https://dl.dropbox.com/s/e822el7vir7ur8p/Web_Widget_chat_Window_menu_options_email_transcript.png?dl=0
menuOptions: {
emailTranscript: true
},
// Add the relevant tags to the Chat session
tags: ['brand_ot']
},
answerBot: {
title: {
'*': 'nBrown Support'
},
avatar: {
//url: '',
name: {
'*': 'Olivia'
}
},
// Set to TRUE if you want the question to be asked before other contact options are displayed
contactOnlyAfterQuery: false
}
}
});
});
document.getElementsByTagName('body')[0].appendChild(zendeskScript);
}
var runWidgetLogic = function() {
'use strict';
var ze_module = {};
ze_module.init = function(config) { // init Widget logic
if (zE && config) {
window.onload = _applyWidgetConfig(config);
} else {
console.log('ERROR: Widget or Widget configuration is missing. Widget settings will not be appllied.');
return;
}
};
function _applyWidgetConfig(config) { // execute Widget logic
_updateChatSettings(config);
_setLocale(config.locale);
_ww_visibility(config.widget_visibility);
_setKBSuggestions(config.articleLabels);
_updatePath(config.path);
_setUserDetails(config.prefill_name, config.prefill_email);
_IdentifyUsers(config.identify_name, config.identify_email);
_updateGlobalSettings(config);
}
function _setLocale(locale) { // set the Widget language
locale && zE('webWidget', 'setLocale', locale);
}
function _ww_visibility(widget_visibility) { // set the Widget visibility (hide/open/show)
widget_visibility && zE('webWidget', widget_visibility);
}
function _setKBSuggestions(articleLabels) { // set the Widget language
articleLabels && zE('webWidget', 'helpCenter:setSuggestions', {
labels: articleLabels
});
}
function _setUserDetails(prefill_name, prefill_email) { // Prefills the user details
prefill_email && zE('webWidget', 'prefill', {
name: {
value: prefill_name,
readOnly: true
},
email: {
value: prefill_email,
readOnly: true
}
});
}
function _IdentifyUsers(identify_name, identify_email) { // Identifies an end user to Zendesk
identify_email && zE('webWidget', 'identify', {
name: identify_name,
email: identify_email
});
}
function _updatePath(pathObject) { // update the chat visitor’s webpath.
pathObject && zE('webWidget', 'updatePath', pathObject);
}
function _setSurpressSettingProp(prop, val) { // extend config with suppress logic
if (window.zESettings && window.zESettings.webWidget) {
var ww = window.zESettings.webWidget;
if (ww[prop]) ww[prop].suppress = val;
else ww[prop] = {
'suppress': val
}
} else console.log('ERROR: Widget is missing window.zESettings object.')
}
function _updateGlobalSettings(config) { // update global Widget settings
window.zESettings = config.webWidgetConfig ? {
webWidget: config.webWidgetConfig
} : {
webWidget: {
chat: {
departments: {}
}
}
};
if (!window.zESettings.webWidget.chat) window.zESettings.webWidget.chat = {
departments: {}
};
_setSurpressSettingProp('helpCenter', !config.HCSearch);
_setSurpressSettingProp('contactForm', !config.contactForm);
_setSurpressSettingProp('talk', !config.talk);
_setSurpressSettingProp('answerBot', !config.answerBot);
// _setSurpressSettingProp('chat', !config.chat); // suppressing chat channel is handled after chat is connected
_adjustChatLabel(config.HCSearch, config.chat, config.contactForm)
}
function _adjustChatLabel(HCSearch, chat, contactForm) { // change launcher label if contact options and chat are enabled
// when contact options and chat are enabled Widget shows "Chat now" from webWidget.launcher.chatLabel,
// however, it opens contact options instead of chat.
// this function displays "Need Help?" from webWidget.launcher.label so label will make more sense
var wwConf = window.zESettings.webWidget;
if (wwConf.contactOptions && wwConf.contactOptions.enabled && wwConf.launcher) {
if (!(chat && !HCSearch && !contactForm)) wwConf.launcher.chatLabel = wwConf.launcher.label;
}
}
function _updateChatSettings(config) { // update Chat settings when chat is connected
zE('webWidget:on', 'chat:connected', function() {
_handleChatSettings(config);
// /show/hide the concierge options in the profile card area
zE('webWidget', 'updateSettings', {
webWidget: {
chat: {
profileCard: {
avatar: true,
rating: true,
title: true
}
}
}
});
zE('webWidget:on', 'chat:status', function(status) {
_handleChatSettings(config);
});
zE('webWidget:on', 'chat:departmentStatus', function(status) {
_handleChatSettings(config);
});
// The below 2 functions can be used to identify particular events (chat started/ended), and as a result, perform actions like update 3rd parties (Google Analytics, etc)
// execute command on Chat Started
zE('webWidget:on', 'chat:start', function() {
// your code here
});
// execute command on Chat Ended
zE('webWidget:on', 'chat:end', function() {
// your code here
});
// For demo purposes only on Zendesk Guide
// <a style="color:blue;" id="open_chat" class="submit-a-request" href="#">Open Chat</a>
// When clicked will display Chat in the Widget
$('#open_chat').on('click', function(e) {
console.log('Chat requested');
e.stopPropagation();
_openChannel('chat', config);
});
// For demo purposes only on Zendesk Guide
// <a style="color:green;" id="open_contact_form" class="submit-a-request" href="#">Open Contact Form</a>
// When clicked will display Contact Form in the Widget
$('#open_contact_form').on('click', function(e) {
console.log('Contact form requested');
e.stopPropagation();
_openChannel('contactForm', config);
});
// When visitor is chating on one page and navigates to the page where chat is supressed
// it should re-open the Widget on the chat screen. Since there is no method to show chat screen
// this workaround will supress other channels > show Widget on the chat screen > re-enable
// other channels as per the config. Visitor may need to minimize and re-open the wiget to
// see the effect of re-enabling the other channels.
// Widget will not be auto-shown on mobile devices.
var isDesktop = !(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
if ((zE && zE('webWidget:get', 'chat:isChatting')) && (config.HCSearch || config.contactForm || config.answerBot) && isDesktop) {
if (zE('webWidget:get', 'display') !== 'chat') {
zE('webWidget', 'updateSettings', {
helpCenter: {
'suppress': true
},
contactForm: {
'suppress': true
},
talk: {
'suppress': true
},
answerBot: {
'suppress': true
}
});
zE('webWidget', 'show');
zE('webWidget', 'open');
zE('webWidget', 'updateSettings', {
helpCenter: {
'suppress': !config.HCSearch
},
contactForm: {
'suppress': !config.contactForm
},
talk: {
'suppress': !config.talk
},
answerBot: {
'suppress': !config.answerBot
}
});
}
}
});
}
// Possible 'channel' values = helpCenter, contactForm, talk, answerBot
// config = global variable for config
// Suppress all Widget channels expect the given one and display the Widget
// Intended to be used when Widget is controlled outside. For example,
// Link on web site header to open contact form
function _openChannel(channel, config) {
console.log("Openning " + channel + " channel on the Web Widget");
var allowedChannels = ["contactForm","chat"]; // limiting channels to desired ones
if (channel && allowedChannels.indexOf(channel) > -1) {
zE('webWidget', 'updateSettings', _getSingleChanelSettings(channel));
zE('webWidget', 'show');
zE('webWidget', 'open');
}
onCloseRevertBackWidgetConfig(config);
}
// Add event listener when Widget is minimised
// Function will be executed only once
var onCloseRevertBackWidgetConfig = (function(config) {
var executed = false;
return function(config) {
if (!executed) {
executed = true;
zE('webWidget:on', 'close', function() {
console.log('The widget has been closed! Reverting channels back');
zE('webWidget', 'updateSettings', _getDefaultWidgetSettings(config));
});
}
};
})();
// config = global variable for config
// return the original state of widget channels
function _getDefaultWidgetSettings(config) {
return {
helpCenter: { 'suppress': !config.HCSearch },
contactForm: { 'suppress': !config.contactForm },
talk: { 'suppress': !config.talk },
answerBot: { 'suppress': !config.answerBot },
chat: { 'suppress': !config.chat }
};
}
// Possible 'channel' values = helpCenter, contactForm, talk, answerBot
// Return all Widget channels suppressed except the desired one
function _getSingleChanelSettings(channel) {
var defaultWidgetSettings = {
helpCenter: { 'suppress': true },
contactForm: { 'suppress': true },
talk: { 'suppress': true },
answerBot: { 'suppress': true },
chat: { 'suppress': true }
};
defaultWidgetSettings[channel].suppress = false;
console.log("Widget Settings = ", defaultWidgetSettings);
return defaultWidgetSettings;
}
function _handleChatSettings(config) {
setTimeout(function() { // Delay is needed to allow chat server to update. Otherwise isChatting is always true
if (config.departments) {
var department_status,
isDepartmentOnline,
isChatting = zE && zE('webWidget:get', 'chat:isChatting');
if (config.departments.select) {
department_status = zE('webWidget:get', 'chat:department', config.departments.select);
isDepartmentOnline = department_status.status === 'online';
}
if (isChatting || isDepartmentOnline) {
// ONLINE LOGIC
// Chat will be suppressed when noone is chatting or when department is offline
zE('webWidget', 'updateSettings', {
webWidget: {
chat: {
departments: config.departments,
suppress: false
}
}
});
} else {
// OFFLINE & UNDEFINED LOGIC (undefined = a department with this name doesn't exist OR the department has been disabled)
// suppress the Chat channel as the targeted department is offline
zE('webWidget', 'updateSettings', {
webWidget: {
chat: {
suppress: true
}
}
});
}
} else {
zE('webWidget', 'updateSettings', window.zESettings.webWidget);
}
}, 250);
}
return ze_module;
};
window.addEventListener ? window.addEventListener('load', fireWidgetOnLoad, !1) : window.attachEvent ? window.attachEvent('onload', fireWidgetOnLoad) : window.onload = fireWidgetOnLoad
})();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment