Apps and Browser Extensions
We detect your platform to show the right downloads—no guesswork, just the apps you need.
const axios = require("axios");
const queryString = require("query-string");
const ui = require("./ui");
const $ = require("jquery");
const cookieJS = require("js-cookie");
const utils = require("../utils/common");
const parsedUA = require("ua-parser-js")();
const scrollToElement = require("scroll-to-element");
const html_escaper = require('html-escaper');
import DeviceDetector from "device-detector-js";
//@Rishi general comments:
// This file is > 1000 lines. this is a red flag for splitting it up into multiple files
// I highly recommend you make a file called "form.js" in the support folder and move your form logic code into there.
// Also, you have a lot of UI specific code that should end up in ui.js
const ERROR = -1;
const INFORM = 0;
const file_size_limit = 15000000;
let banner_number = 1;
let time_out = null;
/* init with UA string from browser */
console.log(parsedUA);
// Object that maps type (c/f/a) to id of current nav.
let state = {};
// Map of ids to names of (c/f/a)
let nameMap = {};
// List of objects containing data for breadcrumb buttons
// breadcrumb: {type: string (c/f/a), id: int, interactive: bool}
let breadcrumbs = [];
let idMap = {};
let order_sel = false;
let $search = $("#search");
let $noResults = $("#no-results");
let $breadcrumbHome = $("#breadcrumb-home");
// let $supportSubmit = $("#support_submit");
let revealer = document.getElementById("support-info-revealer");
let support_btn = document.getElementById("submit_form");
let dropArea = document.getElementById("drop-area");
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
dropArea.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
["dragenter", "dragover"].forEach((eventName) => {
dropArea.addEventListener(eventName, function() {
dropArea.classList.add("highlight");
}, false);
});
["dragleave", "drop"].forEach((eventName) => {
dropArea.addEventListener(eventName, function() {
dropArea.classList.remove("highlight");
}, false);
});
let files_store = [];
dropArea.addEventListener("click", function() {
console.log("drop area clicked");
$("#attached_file").trigger("click");
});
//this is triggered when user drops on drop-area to add files
dropArea.ondrop = function(ev) {
console.log("files dropped");
let filenames = [];
Array.from(ev.dataTransfer.files).forEach((file) => {
filenames.push(file.name);
files_store.push(file);
});
filenames.forEach(name => {
let node = document.createElement('LI');
node.id = `success_msg_${banner_number}`;
node.classList.add('banner-msg');
let textNode = document.createTextNode(`${name} `);
let delButton = document.createElement('div');
delButton.id = `${banner_number}`;
let buttonText = document.createTextNode(`×`);
delButton.appendChild(buttonText);
delButton.classList.add('del_button');
node.appendChild(textNode);
node.appendChild(delButton);
document.getElementById('banners').appendChild(node);
banner_number += 1;
});
setTimeout(() => {
document.getElementById('banners').onclick = function(event) {
let target = getEventTarget(event);
let del_id = target.id;
console.log(del_id);
let bannerToDel = document.getElementById(`success_msg_${del_id}`);
let buttonToDel = document.getElementById(`${del_id}`);
bannerToDel.parentNode.removeChild(bannerToDel);
buttonToDel.parentNode.removeChild(buttonToDel);
console.log(files_store);
files_store[del_id - 1] = null;
};
}, 200);
// banner_number += 1;
ev.target.value = null;
preventDefaults(ev);
};
function getEventTarget(event) {
event = event || window.event;
return event.target || event.srcElement;
}
$('input[type="file"]').change(function(ev) {
if (ev.target.files.length != 0) {
// files_store.push(ev.target.files);
let filenames = [];
Array.from(ev.target.files).forEach((file) => {
filenames.push(file.name);
files_store.push(file);
});
console.log(files_store);
// let stringify_names = filenames.toString();
filenames.forEach(name => {
let node = document.createElement('LI');
node.id = `success_msg_${banner_number}`;
node.classList.add('banner-msg');
let textNode = document.createTextNode(`${name} `);
let delButton = document.createElement('div');
delButton.id = `${banner_number}`;
let buttonText = document.createTextNode(`×`);
delButton.appendChild(buttonText);
delButton.classList.add('del_button');
node.appendChild(textNode);
node.appendChild(delButton);
document.getElementById('banners').appendChild(node);
banner_number += 1;
});
setTimeout(() => {
document.getElementById('banners').onclick = function(event) {
let target = getEventTarget(event);
let del_id = target.id;
console.log(del_id);
let bannerToDel = document.getElementById(`success_msg_${del_id}`);
let buttonToDel = document.getElementById(`${del_id}`);
bannerToDel.parentNode.removeChild(bannerToDel);
buttonToDel.parentNode.removeChild(buttonToDel);
console.log(files_store);
files_store[del_id - 1] = null;
};
}, 200);
//@Rishi pls link the stackoverflow/bug tracker/forum where you found the bug and the workaround for reference to future devs
//apparently a nice little chrome bug
//@Seva I don't remember the website I found it in :/ I'll search for it later...
ev.target.value = null;
preventDefaults(ev);
}
});
function validate_form(
name,
email,
subject,
description,
file_size,
cat_sel,
sub_cat_sel
) {
if (
name.validity.valid &&
email.validity.valid &&
utils.isEmail(email.value.trim()) &&
subject.validity.valid &&
description.validity.valid &&
//@Rishi extract magic numbers
file_size <= file_size_limit &&
cat_sel &&
sub_cat_sel
) {
return true;
}
return false;
}
function displayFeedbackBanner(type, message) {
let banner = document.getElementById("feedback-banner");
if (type === ERROR) {
banner.style.backgroundColor = " #ff0000";
} else if (type === INFORM) {
banner.style.backgroundColor = "#0085f5";
} else { //is success
banner.style.backgroundColor = "#0085f5";
}
banner.style.display = "block";
banner.innerText = message;
}
function setBannerTimeout(type, message) {
if (time_out !== null) {
clearTimeout(time_out);
}
displayFeedbackBanner(type, message);
time_out = setTimeout(() => {
document.getElementById('feedback-banner').style.display = 'none';
}, 7000);
}
function alert_missing_fields(
name,
email,
subject,
description,
file_size,
cat_sel,
sub_cat_sel
) {
if (!cat_sel) {
setBannerTimeout(ERROR, "Select a category");
} else if (!sub_cat_sel) {
setBannerTimeout(ERROR, "Select a subcategory");
} else if (!name.validity.valid) {
setBannerTimeout(ERROR, "Enter a name");
} else if (!email.validity.valid || !utils.isEmail(email.value.trim())) {
setBannerTimeout(ERROR, "Enter a valid email");
} else if (!subject.validity.valid) {
setBannerTimeout(ERROR, "Enter a subject");
} else if (!description.validity.valid) {
setBannerTimeout(ERROR, "Enter a message");
} else if (file_size >= file_size_limit) {
setBannerTimeout(ERROR, "Attachment file size cannot exceed 15 MB");
}
}
function formatNotes(userInfo, ord_num, parsedUA_data, name, email) {
let formatted_info;
console.log(userInfo.device);
name = name || "Anonymous";
if (email) {
if (ord_num == null) {
formatted_info = `
Contact - ${name} <${email}>
Browser - ${parsedUA_data.browser.name} ${parsedUA_data.browser.version}
Operating System - ${userInfo.os.name} ${userInfo.os.version}
Device - ${userInfo.device.brand} ${userInfo.device.model} ${userInfo.device.type}
`;
} else {
formatted_info = `
Order Number - ${ord_num}
Contact - ${name} <${email}>
Browser - ${parsedUA_data.browser.name} ${parsedUA_data.browser.version}
Operating System - ${userInfo.os.name} ${userInfo.os.version}
Device - ${userInfo.device.brand} ${userInfo.device.model} ${userInfo.device.type}
`;
}
} else {
if (ord_num == null) {
formatted_info = `
Contact - ${name}
Browser - ${parsedUA_data.browser.name} ${parsedUA_data.browser.version}
Operating System - ${userInfo.os.name} ${userInfo.os.version}
Device - ${userInfo.device.brand} ${userInfo.device.model} ${userInfo.device.type}
`;
} else {
formatted_info = `
Order Number - ${ord_num}
Contact - ${name}
Browser - ${parsedUA_data.browser.name} ${parsedUA_data.browser.version}
Operating System - ${userInfo.os.name} ${userInfo.os.version}
Device - ${userInfo.device.brand} ${userInfo.device.model} ${userInfo.device.type}
`;
}
}
return formatted_info;
}
function validate_order_num() {
let order_num = document.getElementById("order_number");
if (order_num.validity.valid) {
order_num = order_num.value.trim();
return order_num;
}
return null;
}
function clearOrderNumber() {
document.getElementById("ord-number").style.maxHeight = '0px';
document.getElementById("ord-number").style.opacity = 0;
document.getElementById("ord-number").style.transition = "max-height 350ms ease-out, opacity 200ms ease-in";
setTimeout(() => {
document.getElementById("ord-number").innerHTML = "";
}, 350);
}
function request_order_num() {
let ord_num = document.getElementById("ord-number");
ord_num.innerHTML = `
`;
setTimeout(() => {
ord_num.style.maxHeight = '108px';
ord_num.style.opacity = 1;
ord_num.style.transition = "max-height 350ms ease-out, opacity .5s ease-in";
}, 350);
}
function sliceTags(tag) {
//brute force through these 3
if (tag == "Everykey Two-Factor Authentication") {
return "Everykey Two Factor";
}
if (tag == "Cancellations, Returns, and Exchanges") {
return "Refunds Returns and Exchanges";
}
if (tag == "Product Updates and Special Offers") {
return "Updates and Special Offers";
}
//handle rest
let sliced_tag = tag.replace(/,/g, "");
if (sliced_tag.length >= 32) {
return sliced_tag.slice(0, 31);
}
return sliced_tag;
}
function getFileSize(files) {
let f_size = 0;
for (let i = 0, numFiles = files.length; i < numFiles; i++) {
const file = files[i];
f_size += file.size;
}
return f_size;
}
function displaySuccessPage() {
$("#toScroll").addClass('hidden-content');
$("#form_submitted").removeClass('hidden-content');
$("#form_submitted").addClass('submit_page');
window.scrollTo(0, 0);
}
// **************************************
// After submit button is clicked
// **************************************
support_btn.addEventListener("click", function() {
let cat_selected = false;
let sub_cat_sel = false;
let type = $("#category").find("option:selected").val();
let _tags;
if (document.getElementById("category").selectedIndex != 0) {
cat_selected = true;
_tags = $(`#inner_${type}`).find("option:selected").val();
//if inner_type is null, reset all the fields
if (document.getElementById(`inner_${type}`).selectedIndex != 0) {
sub_cat_sel = true;
}
}
console.log(document.getElementsByClassName('files'));
let name = document.getElementById("name");
let email = document.getElementById("email");
let subject = document.getElementById("subject");
let description = document.getElementById("description");
let attchd_files = files_store.filter(file => file !== null);
let file_size = getFileSize(attchd_files);
console.log(attchd_files);
let ord_num = null;
const deviceDetector = new DeviceDetector();
if (
validate_form(
name,
email,
subject,
description,
file_size,
cat_selected,
sub_cat_sel
)
) {
support_btn.disabled = true;
support_btn.style.display = 'none';
document.getElementById('shop-loading').style.display = 'block';
document.getElementById('feedback-banner').style.display = 'none';
name = html_escaper.escape(name.value.trim());
email = html_escaper.escape(email.value.trim());
subject = html_escaper.escape(subject.value.trim());
description = html_escaper.escape(description.value.trim());
console.log(name, email, subject, description);
let form = new FormData();
form.append("name", name);
form.append("email", email);
form.append("description", description);
form.append("subject", subject);
let file_names = [];
for (let i = 0, numFiles = attchd_files.length; i < numFiles; i++) {
file_names.push(attchd_files[i].name);
const file = attchd_files[i];
console.log(file);
form.append(`file${i}`, file);
}
if (order_sel) {
ord_num = validate_order_num();
}
let deviceDetInfo = deviceDetector.parse(navigator.userAgent);
let formattedInfo = formatNotes(deviceDetInfo, ord_num, parsedUA, name, email);
console.log(formattedInfo);
form.append("userInfo", formattedInfo);
form.append("type", type);
_tags = sliceTags(_tags);
console.log("sliced tag", _tags);
form.append("tags", _tags);
return axios({
method: 'post',
url: '/api/support/submit-ticket',
headers: {
'Content-Type': 'multipart/form-data'
},
data: form,
}).then(function(res) {
console.log(res);
if (res.data.status === 0) {
displaySuccessPage();
} else {
setBannerTimeout(ERROR, res.data.text);
document.getElementById('shop-loading').style.display = 'none';
support_btn.disabled = false;
support_btn.style.display = 'inline-block';
}
// if (res.status == 201) {
// displaySuccessPage();
// } else {
// setBannerTimeout(ERROR, res.body);
// document.getElementById('shop-loading').style.display = 'none';
// support_btn.style.display = 'inline-block';
// console.log(res);
// }
}).catch((err) => {
setBannerTimeout(ERROR, err);
document.getElementById('shop-loading').style.display = 'none';
support_btn.disabled = false;
support_btn.style.display = 'inline-block';
console.log(err);
});
} else {
alert_missing_fields(
name,
email,
subject,
description,
file_size,
cat_selected,
sub_cat_sel
);
}
});
//@Rishi does not need to be in an onready event trigger
$(function() {
console.log($breadcrumbHome);
$breadcrumbHome.click(function() {
state = {};
breadcrumbs = [];
$search.val("");
generateBreadcrumbsAndTitle();
window.history.pushState(state, "", location.pathname);
displayCategories();
});
// console.log(location);
let processInputTimeout;
$search.on("keyup", function() {
clearTimeout(processInputTimeout);
processInputTimeout = setTimeout(function() {
if (state.q !== $search.val()) {
delete state.c;
delete state.f;
delete state.a;
queryHandler($search.val(), {
replaceState: true,
fade: !state.q,
});
}
}, 250);
});
let info = document.getElementById("support-info");
revealer.onclick = function() {
//fade this out fade that in
revealer.style.display = "none";
info.style.display = "block";
info.style.visibility = "visible";
document.getElementById('contact-us-header').style.display = "block";
document.getElementById('contact-us-mini-header').style.display = "block";
scrollToElement("#contact-us-header", {
offset: -60,
duration: 500
});
if (document.getElementById("category").selectedIndex != 0) {
let catgry = $("#category").find("option:selected").val();
if (document.getElementById(`inner_${catgry}`) == null) {
// $("#category").val(catgry).trigger("change");
$("#category").val("Category");
}
}
if (navigator.cookieEnabled) {
cookieJS.set("clickedSupport", 1);
}
// fetchCategories();
};
// **************************************
// Populate idMap which stores category names and their ids
// **************************************
let sub_cat_sel = document.getElementById("sub-category");
function set_cat_animation() {
sub_cat_sel.style.maxHeight = '108px';
sub_cat_sel.style.opacity = 1;
sub_cat_sel.style.transition = "max-height 350ms ease-out, opacity .5s ease-in";
}
function fill_folders(fldr_array, category) {
getFolders(idMap[category]).then(function(res) {
res.data.forEach((folder) => {
fldr_array.push(folder.name);
});
return fldr_array;
}).then((f_arr) => {
sub_cat_sel.innerHTML = `
`
);
} else {
document.getElementById(`inner_${category}`).insertAdjacentHTML(
"beforeend",
`
`
);
}
}).then(() => {
// addtnl_reqrmnts();
set_cat_animation();
if (category == "Orders") {
document.getElementById(`inner_${category}`).addEventListener('change', function() {
let sub_cat = $(`#inner_${category}`).val();
$(`#inner_${category}`).trigger("change");
if (sub_cat == "Placing a New Order") {
order_sel = false;
clearOrderNumber();
} else {
order_sel = true;
request_order_num();
}
});
} else {
order_sel = false;
clearOrderNumber();
}
}).catch((err) => console.log(err));
}
// **************************************
//This chunk of code is to dynamically display fill drop downs based on the categories selected
// **************************************
// **************************************
// Fill in the folders when a category is selected
// **************************************
function fetchData() {
// @Rishi Why are you using asyncCallback with only 1 function?
//@seva I just copied the code from categoryhandler() below. Yeah it makes no sense lol. Will change
axios.get("/api/support/categories").then(response => {
console.log(response.data);
response.data.forEach(function(category) {
idMap[category.name] = category.id;
console.log("contents of idMap");
console.log(idMap);
});
}).catch(console.error);
}
$("#category").change(function() {
//fetch for data and populate idMap field
fetchData();
console.log("entering change function");
fill_folders([], $(this).find("option:selected").attr("id"));
});
if (location.search) {
state = queryString.parse(location.search);
console.log(state);
}
console.log("state: " + JSON.stringify(state));
window.history.replaceState(state, "", location.pathname + location.search);
navFromState();
});
function pushState(breadcrumb, options) {
$noResults.hide();
console.log("New Breadcrumb: " + JSON.stringify(breadcrumb));
if (breadcrumb.text || nameMap[breadcrumb.id]) {
breadcrumbs.push(breadcrumb);
}
console.log("Breadcrumbs: " + JSON.stringify(breadcrumbs));
generateBreadcrumbsAndTitle();
if (breadcrumb.id) {
state[breadcrumb.type] = breadcrumb.id;
} else {
delete state[breadcrumb.type];
}
console.log("state: " + JSON.stringify(state));
if (options.pushHistory) {
window.history.pushState(
state,
"",
location.pathname + "?" + queryString.stringify(state)
);
} else if (options.replaceState) {
window.history.replaceState(
state,
"",
location.pathname + "?" + queryString.stringify(state)
);
}
}
window.onpopstate = function(event) {
console.log("Previous State: " + JSON.stringify(state));
console.log("New State from onpopstate: " + JSON.stringify(event.state));
console.log(nameMap);
state = event.state || {};
breadcrumbs = [];
generateBreadcrumbsAndTitle();
navFromState();
};
function generateBreadcrumbsAndTitle() {
ui.clearBreadcrumbs();
// We start at breadcrumbs.length - 2 because we know that the last
// breadcrumb in the list will never be interactive.
// Be default, breadcrumbClick is a noop
let breadcrumbClick = function() {};
for (let i = breadcrumbs.length - 2; i >= 0; i--) {
if (!breadcrumbs[i].onclick) {
if (breadcrumbs[i].interactive) {
breadcrumbClick = function() {
console.log(
"Clicked Crumb #" +
i +
" " +
JSON.stringify(breadcrumbs[i])
);
// Pop off all crumbs that are after the clicked crumb
for (let j = breadcrumbs.length - 1; j > i; j--) {
delete state[breadcrumbs.pop().type];
}
// Pop off the clicked crumb as well, it will get
// added back in the navFromBreadcrumb function.
navFromBreadcrumb(breadcrumbs.pop());
};
}
breadcrumbs[i].onclick = breadcrumbClick;
}
}
for (let i = 0; i < breadcrumbs.length - 1; i++) {
let breadcrumb = breadcrumbs[i];
if (breadcrumb.interactive) {
createBreadcrumb(breadcrumb);
}
}
// Unique logic for last breadcrumb in list
//if the last breadcrumb is an article don't set it's title
if (breadcrumbs.length) {
$breadcrumbHome.show();
let lastCrumb = breadcrumbs[breadcrumbs.length - 1];
console.log(lastCrumb.type);
//this should be missing as well
if (lastCrumb.type == "c" || lastCrumb.type == "f") {
if (breadcrumbs.length > 1 || !state.a) {
createBreadcrumb(lastCrumb);
}
}
ui.setTitle(lastCrumb.text || nameMap[lastCrumb.id]);
}
}
function createBreadcrumb(breadcrumb) {
ui.createBreadcrumb(
breadcrumb.text || nameMap[breadcrumb.id],
breadcrumb.onclick
);
}
function navFromBreadcrumb(breadcrumb) {
return {
c: categoryHandler,
f: folderHandler,
a: articleHandler,
q: queryHandler,
} [breadcrumb.type](breadcrumb.id, {
pushHistory: true,
fade: true,
});
}
function navFromState() {
let shouldQueryNames = false;
["c", "f", "a"].forEach(function(type) {
let id = state[type];
if (id && !nameMap[id]) {
shouldQueryNames = true;
}
});
if (shouldQueryNames) {
queryNames().then(function(response) {
if (response && response.data) {
console.log(
"Got new names for state: " + JSON.stringify(response.data)
);
//support form prepop
Object.keys(response.data).forEach(function(type) {
idMap[response.data[type]] = state[type];
nameMap[state[type]] = response.data[type];
});
//missed in merge
let cat = null;
let sub_cat = null;
if (Object.keys(response.data).length >= 1) {
cat = response.data.c;
if (response.data.f) {
sub_cat = response.data.f;
}
}
function category_prepop() {
if (cat != null) {
$("#category").val(cat).trigger("change");
}
setTimeout(subcat_prepop, 1000);
}
function subcat_prepop() {
if (sub_cat != null) {
console.log(`inner_${cat}`);
console.log("inside subcat_prepop");
// document.getElementById(`inner_${cat}`).value = sub_cat;
$(`#inner_${cat}`).val(sub_cat).trigger("change");
if (cat == "Orders") {
if (sub_cat == "Placing a New Order") {
order_sel = false;
clearOrderNumber();
} else {
order_sel = true;
request_order_num();
}
}
}
}
category_prepop();
}
processNavFromState();
//till here ^
});
} else {
processNavFromState();
}
}
function processNavFromState() {
if (state.a) {
if (state.q) {
$search.val(state.q);
pushStateFromQuery(state.q, {
pushHistory: false,
});
} else {
pushStateFromState("c");
pushStateFromState("f");
}
articleHandler(state.a, {
pushHistory: false,
});
} else if (state.q) {
$search.val(state.q);
queryHandler(state.q, {
fade: true,
});
} else {
$search.val("");
if (state.f) {
pushStateFromState("c");
folderHandler(state.f, {
pushHistory: false,
});
} else if (state.c) {
categoryHandler(state.c, {
pushHistory: false,
});
} else {
displayCategories();
}
}
}
function pushStateFromState(type) {
let id = state[type];
if (id && nameMap[id]) {
pushState({
type: type,
id: id,
interactive: true,
}, {
pushHistory: false,
});
}
}
function pushStateFromQuery(query, options) {
pushState({
type: "q",
id: query,
text: 'Search Results for "' + query + '"',
interactive: true,
}, options);
}
function queryHandler(query, options) {
if (query) {
options = options || {};
let asyncFunctions = [function(callback) {
queryArticles(query).then(callback);
}];
if (options.fade) {
asyncFunctions.push(function(callback) {
ui.fade(0, callback);
});
}
utils.asyncCallback(asyncFunctions, function(response) {
breadcrumbs = [];
pushStateFromQuery(query, {
pushHistory: options.pushHistory || !state.q,
replaceState: options.replaceState,
});
ui.clear();
if (response && response.data && response.data.length) {
response.data.forEach(function(article) {
processArticle(JSON.parse(article.json), false, {
pushHistory: true,
});
});
} else {
$noResults.show();
ui.fade(1);
}
});
} // else do nothing
}
function displayCategories() {
utils.asyncCallback([
function(callback) {
ui.fade(0, callback);
},
function(callback) {
axios.get("/api/support/categories").then(callback).catch(console.error);
},
], function(response) {
console.log(response.data);
$breadcrumbHome.hide();
ui.hideBreadCrumbs();
$noResults.hide();
ui.clear();
ui.setTitle("Everykey Support");
response.data.forEach(function(category) {
nameMap[category.id] = category.name;
idMap[category.name] = category.id;
console.log("contents of idMap");
console.log(idMap);
ui.renderBlock(category.name, function() {
categoryClick(category.id);
});
window.scrollTo(window.scrollX, 0);
});
ui.fade(1);
});
}
function categoryClick(id) {
let catName = nameMap[id];
$("#category").val(catName).trigger("change");
//this doesnt merge as well
categoryHandler(id, {
pushHistory: true,
});
}
function categoryHandler(id, options) {
options = options || {};
utils.asyncCallback([
function(callback) {
ui.fade(0, callback);
},
function(callback) {
getFolders(id).then(callback);
},
], function(response) {
pushState({
type: "c",
id: id,
interactive: true,
}, options);
ui.clear();
response.data.forEach(function(folder) {
nameMap[folder.id] = folder.name;
idMap[folder.name] = folder.id;
ui.renderBlock(folder.name, function() {
folderClick(folder.id);
});
window.scrollTo(window.scrollX, 0);
});
ui.fade(1);
});
}
function folderClick(id) {
//this should show up as well
let foldName = nameMap[id];
console.log(foldName);
console.log(state.c);
console.log(state);
let catN = nameMap[state.c];
console.log(catN);
//condense this code later
$(`#inner_${catN}`).val(foldName).trigger("change");
if (catN == "Orders") {
if (foldName == "Placing a New Order") {
order_sel = false;
clearOrderNumber();
} else {
order_sel = true;
request_order_num();
}
//^
}
folderHandler(id, {
pushHistory: true,
});
}
function folderHandler(id, options) {
options = options || {};
utils.asyncCallback([
function(callback) {
ui.fade(0, callback);
},
function(callback) {
getArticles(id).then(callback);
},
], function(response) {
ui.clear();
if (response.data.length === 1) {
pushState({
type: "f",
id: id,
//these must be true
interactive: true,
}, {
pushHistory: true,
});
processArticle(response.data[0], true, options);
} else {
pushState({
type: "f",
id: id,
interactive: true,
}, options);
response.data.forEach(function(article) {
processArticle(article, false, options);
});
}
});
}
function articleHandler(id, options) {
utils.asyncCallback([function(callback) {
ui.fade(0, callback);
},
function(callback) {
getArticle(id).then(callback);
},
], function(response) {
ui.clear();
processArticle(response.data, true, options);
});
}
function processArticle(article, single, options) {
options = options || {};
console.log(article);
nameMap[article.id] = article.title;
// Sometimes JSON keys are flattened and contained in top level of article object
let redirectData = (function(descriptionText) {
try {
return JSON.parse(descriptionText);
} catch (exception) {
return;
}
})(article.description_text || article.json.description_text);
if (redirectData) {
if (redirectData.a) {
// redirect from one article to another, simplest case.
// query for the other article and put its data into the
// article view instead of the article we currently have.
processSingle(article.title, single, function() {
articleHandler(redirectData.a, options);
});
} else if (redirectData.f) {
// redirect from article to folder (list of articles)
// query the articles and we make a single list entry which
// once clicked will act as if a folder was clicked rather
// than opening a new article.
processSingle(article.title, single, function() {
folderHandler(redirectData.f, options);
});
} else if (redirectData.c) {
// redirect from article to category (list of folders)
// since folders can not redirect, we simply create a list
// entry that once clicked will open the category view.
// if the article is alone in the folder, immediately open
// the category view
processSingle(article.title, single, function() {
categoryHandler(redirectData.c, options);
});
} else {
console.error("Cannot Redirect to " + JSON.stringify(redirectData));
}
} else {
// Not a redirect, proceed to default rendering
let description = article.description || article.json.description;
let tags = article.tags || article.json.tags;
if (single) {
pushState({
type: "a",
id: article.id,
interactive: false,
}, options);
ui.renderArticle(description);
window.scrollTo(window.scrollX, 0);
ui.fade(1);
} else if (tags.indexOf("hidden_article") === -1) {
// Hidden articles can still be rendered when navigated to directly
ui.renderList(article.title, function() {
ui.fade(0, function() {
pushState({
type: "a",
id: article.id,
interactive: false,
}, options);
ui.clear();
ui.renderArticle(description);
window.scrollTo(window.scrollX, 0);
ui.fade(1);
});
});
window.scrollTo(window.scrollX, 0);
ui.fade(1);
} else {
console.log("HIDE ARTICLE FROM LIST!");
console.log(article);
}
}
}
// Render a list view with given data unless single is true in
// which case do not render, just trigger callback immediately
function processSingle(title, single, callback) {
if (single) {
callback();
} else {
ui.renderList(title, function() {
callback();
});
window.scrollTo(window.scrollX, 0);
}
}
function getArticles(id) {
return axios.get("/api/support/articles?id=" + id).catch(console.error);
}
function getArticle(id) {
return axios.get("/api/support/article?id=" + id).catch(console.error);
}
function getFolders(id) {
return axios.get("/api/support/folders?id=" + id).catch(console.error);
}
function queryArticles(query) {
return axios.get("/api/support/search?q=" + query).catch(console.error);
}
function queryNames() {
return axios.post("/api/support/names", {
state: state,
}).catch(console.error);
}
const $ = require('jquery');
const utils = require('../utils/common');
const $content = $('#content');
const $title = $("#title");
const breadcrumbs = document.getElementById("breadcrumbs");
const revealer = document.getElementById("support-info-revealer");
let currOpacity = $content.css("opacity");
module.exports = {
clear: function() {
$content.html("");
},
fade: function(opacity, callback) {
console.log(currOpacity + " -> " + opacity);
if (currOpacity != opacity) {
currOpacity = opacity;
$content.add($title).fadeTo(250, opacity, utils.callbackOnce(callback));
} else {
console.log("DONT FADE, ALREADY AT STATE");
callback && callback();
}
},
renderBlock: function(title, onclick) {
$title.removeClass("small");
//this isn't merged too
revealer.innerText = "Contact Us";
let button = document.createElement("button");
button.setAttribute("class", "button category");
button.innerText = title;
button.addEventListener("click", onclick);
$content.append(button);
},
renderList: function(title, onclick) {
$title.removeClass("small");
revealer.innerText = "Contact Us";
let button = document.createElement("button");
button.setAttribute("class", "button article");
button.innerText = title;
button.addEventListener("click", onclick);
$content.append(button);
},
renderArticle: function(articleHTML) {
$title.addClass("small");
revealer.innerText = "Contact Us";
$content.html(articleHTML);
},
hideBreadCrumbs: function() {
breadcrumbs.style.display = "none";
},
clearBreadcrumbs: function() {
breadcrumbs.innerHTML = "";
},
createBreadcrumb: function(text, onclick) {
breadcrumbs.style.display = "inline";
let a = document.createElement("a");
a.innerText = text;
a.onclick = onclick;
let li = document.createElement("li");
li.append(a);
breadcrumbs.append(li);
},
setTitle: function(title) {
$title.text(title);
}
};