1// Lytics Pathfora form 2// https://lytics.github.io/pathforadocs/ 3 4function setupFormCallbackAndCreateFormIfSub(siteName, spot) { // eslint-disable-line no-unused-vars 5 var thisID = '#demographics-box-' + spot; 6 var lyticsID = null; 7 let userEmail = ''; 8 // vars for demographics box 9 var diveInsiderMainPageUrl = 'https://www.industrydive.com/insiders/'; 10 var headlineValue = window.gettext('We\'re covering the news relevant to you.'); 11 var messageValue = window.pgettext('"get it right" here means "achieve our goal"', 'Help us get it right by telling us more about you.'); 12 var successusMessageValue = window.interpolate( 13 window.gettext('Love our content? Become a %(placeholder)s by referring others and get rewarded.'), 14 { placeholder: `<a href="${diveInsiderMainPageUrl}">Dive Insider</a>` }, 15 true 16 ); 17 var okMessageValue = window.pgettext('form submission button label', 'Submit'); 18 var fieldsToDisplay = []; 19 var initialValues = { 20 'dive_company_type': '', 21 'dive_job_function': '', 22 'dive_job_level': '', 23 }; 24 var sailthruSource = 'sidebar'; // default sailthru source value for user signup 25 26 var demographicFormVariant = ''; 27 var demographicFormDomain = window.location.hostname; 28 var userAudiences = []; 29 30 // The ternary is mainly so the form doesn't break on localhost. All prod/qa sites should have .com in the url, 31 // but localhost host doesn't so need to account for that. 32 var demographicFormDomainId = demographicFormDomain.indexOf('.com') !== -1 ? demographicFormDomain.split('.')[1] : demographicFormDomain; 33 34 function pickAPublication() { 35 return siteName.toLowerCase() === 'education dive' ? 'Education Dive: Higher Ed' : siteName; 36 } 37 38 function decideIfUserIsSubscribed(usersAudienceArray) { 39 var siteNameLower = siteName.toLowerCase(); 40 // Almost all Lytics sub list follow this format, but there is one expectation with education dive and it maps to two Lytics list. 41 // So, to make this more generic, the method loops through all the possible Lytics audience that map to a certain dive. 42 var validSubscriberList = ['any_$_subs'.replace('$', siteNameLower.replace(' ', '_').replace('-', ''))]; 43 var isSubscriber = false; 44 45 if (siteNameLower === 'education dive') { 46 validSubscriberList = ['any_higher_ed_dive_subs', 'any_k12_dive_subs']; 47 } 48 49 for (var i = 0; i < validSubscriberList.length; i++) { 50 if (usersAudienceArray.indexOf(validSubscriberList[i]) > -1) { 51 isSubscriber = true; 52 break; 53 } 54 } 55 56 return isSubscriber; 57 } 58 59 function firstElementUpper(element) { 60 return element[0].toUpperCase() + element.slice(1); 61 } 62 63 function fieldSlugToLabel(fieldSlug) { 64 return firstElementUpper(fieldSlug.replace('dive_', '').replace('_', ' ')); 65 } 66 // FORM FIELDS 67 function constructField(type, label, name) { 68 var field = { 69 'type': type, 70 'label': label, 71 'name': name, 72 }; 73 74 return field; 75 } 76 77 function constructDisplayField(fieldSlug, boxLocation) { 78 var type = fieldSlug.indexOf('dive_') > -1 ? 'select' : 'text'; 79 return constructField(type, fieldSlugToLabel(fieldSlug), fieldSlug + '_' + boxLocation); 80 } 81 82 function getDemographicFormId() { 83 return demographicFormDomainId + '-' + demographicFormVariant + '-' + spot + '-box'; 84 } 85 86 // default success and failure messages 87 var messages = { 88 success: {
89 headline: window.gettext('Thank you!'), 90 msg: successusMessageValue, 91 delay: 0, 92 }, 93 error: { 94 headline: window.gettext('Error'), 95 msg: window.gettext('There was an issue submitting your form.'), 96 delay: 0, 97 }, 98 }; 99 100 function hasAtLeastOneValue(dataArray) { 101 return dataArray.reduce(function(acc, cur) { 102 return acc || cur.value !== ''; 103 }, false); 104 } 105 106 /* eslint-disable */ 107 var confirmActionValue = { 108 waitForAsyncResponse: true, 109 callback: function callbackFunction(event, payload, cb) { 110 var $errorMessageElement = $('.demo-box-error'); 111 var payloadData = payload.data; 112 113 // if either value is submitted, success 114 if (hasAtLeastOneValue(payloadData)) { 115 // remove error message 116 $errorMessageElement.remove(); 117 118 var jsonRequest = { 119 'lytics_id': lyticsID, 120 }; 121 var varsToUpdateAsJson = {}; 122 var formConfiguration = ''; 123 for (var i = 0; i < payloadData.length; i++) { 124 formConfiguration += (formConfiguration === '' ? '' : ', ') + payloadData[i].name; 125 if (payloadData[i].value.length !== 0) { 126 varsToUpdateAsJson[payloadData[i].name] = payloadData[i].value; 127 formConfiguration += ' filled'; 128 } else { 129 formConfiguration += ' blank'; 130 } 131 } 132 jsonRequest.fields_to_update = varsToUpdateAsJson; 133 $.ajax({ 134 type: 'POST', 135 url: '/push-to-sailthru/', 136 dataType: 'json', 137 data: { 138 data: JSON.stringify(jsonRequest), 139 }, 140 success: function() { 141 addSuccessClassToAllForms(); 142 } 143 }); 144 // if no values submitted, display an error if not already added 145 } else if ($errorMessageElement.length === 0) { 146 var errorMessage = '<span class="demo-box-error">Oops! The form is empty.</span>'; 147 $(thisID + ' .pf-widget-message').after(errorMessage); 148 } 149 }, 150 }; 151 152 // evaluate the data submitted 153 function createFormOptions() { 154 var defaultOptions = { 155 id: getDemographicFormId(), 156 layout: 'inline', 157 position: thisID, 158 headline: headlineValue, 159 msg: messageValue, 160 okMessage: okMessageValue, 161 formElements: fieldsToDisplay, 162 formStates: messages, 163 confirmAction: confirmActionValue, 164 theme: 'custom', 165 }; 166 167 if (spot.toLowerCase() === 'sidebar') { 168 defaultOptions['className'] = 'demographic-form-sidebar'; 169 defaultOptions['image'] = '/static/img/demographic_form/demographics-collector-sidebar.png'; 170 171 // Custom JS properties to add styles, which do not affect the Lytics call 172 defaultOptions['customButton'] = 'button button--full-width'; 173 } 174 175 if (spot.toLowerCase() === 'inline') { 176 defaultOptions['className'] = 'demographic-form-inline'; 177 defaultOptions['image'] = '/static/img/demographic_form/demographics-collector-sidebar.png'; 178 179 // Custom JS properties to add styles, which do not affect the Lytics call 180 defaultOptions['customButton'] = 'button button--medium'; 181 defaultOptions['includePolygon'] = true; 182 defaultOptions['isImageWrapped'] = true; 183 } 184 185 return defaultOptions; 186 } 187 188 function hideSignUpForm() { 189 // hide sidebar and body inline instances 190 $(thisID).prev().hide(); 191 // hide pre-footer instance 192 $('.pre-footer .signup').hide(); 193 } 194 195 function showLyticsForm() { 196 var formOptions = createFormOptions(); 197 var pathforaForm = new pathfora.Form(formOptions); 198 pathfora.initializeWidgets([pathforaForm]); 199 $(thisID).diveformify(pickAPublication(), 200 { 201 'strict_mode': true, 202 'initial_values' : initialValues 203 }); 204 addCustomStyle(formOptions); 205 hideSignUpForm(); 206 $(thisID).show(); 207 } 208 209 function removeLyticsButtonStyleAndAddCustomClass(cssSelector, buttonType) { 210 var button = $(cssSelector + ' .pf-widget-btn.pf-widget-ok'); 211 $(button).addClass(buttonType); 212 $(button).removeClass('pf-widget-btn'); 213 } 214 215 function addSuccessClassToAllForms() { 216 $('[class*=demographic-form]').addClass('success').parent().addClass('success'); 217 } 218 219 function addImageWrapper(cssSelector) { 220 $(cssSelector + ' img').wrap('<div class="pf-img-wrapper"></div>'); 221 } 222 223 function addPrivacyPolicy(cssSelector) { 224 var privacyPolicy = '<p class="help-text help-text--small">' + 225 '<a class="pf-privacy-policy" href="https://www.informatechtarget.com/privacy-policy/" target="_blank">Privacy policy</a></p>'; 226 $(cssSelector + ' .pf-widget-content').append(privacyPolicy); 227 } 228 229 function addCustomStyle(config) { 230 // Add the privacy link to all Lytics forms 231 var cssSelector = config.position; 232 var buttonClass = config.customButton; 233 var includePolygon = config.includePolygon; 234 var isImageWrapped = config.isImageWrapped; 235 236 addPrivacyPolicy(cssSelector); 237 removeLyticsButtonStyleAndAddCustomClass(cssSelector, buttonClass); 238 239 if (includePolygon) { 240 // This is just a standard white polygon that appears on top of an image for an inline form. 241 $(cssSelector).append('<div class="pf-polygon"></div>'); 242 } 243 244 if (isImageWrapped) { 245 addImageWrapper(cssSelector); 246 } 247 248 if (config.formElements.length === 1 && config.position === '#demographics-box-inline') { 249 $(config.position + ' button').addClass('special-button'); 250 } 251 } 252 253 function createScript(appendChildToTagName, scriptObject) { 254 // appends a given scriptObject at mentioned tagName 255 document.getElementsByTagName(appendChildToTagName)[0].appendChild(scriptObject); 256 } 257 258 function loadJs(jsFilePath, async_, defer) { 259 // This function returns a deferred object which is resolved once the js is loaded. 260 var loadJsDeferred = $.Deferred(); 261 var scriptObject = document.createElement('script'); 262 scriptObject.async = async_; 263 scriptObject.defer = defer; 264 scriptObject.type = 'text/javascript'; 265 scriptObject.src = jsFilePath; 266 scriptObject.onload = loadJsDeferred.resolve; 267 createScript('head', scriptObject); 268 return loadJsDeferred; 269 } 270 271 function decideLyticsFormsToDisplay(id){ 272 lyticsID = id; 273 $.ajax({ 274 type: 'POST', 275 url: '/decide-lytics-forms-to-display/', 276 dataType: 'json', 277 data: { 278 lytics_token: id, 279 }, 280 success: function setFieldsToDisplay(demographicData) { 281 282 // populate the set of fields to display 283 if (!$.isEmptyObject(demographicData)) { 284 // temporary it media signup experiment 285 userEmail = demographicData.email; 286 // check that the email is present since it's needed for the one-click IT media signup 287 if (userEmail && userEmail.length > 0 && showSignupFormForITMedia()) { 288 return; 289 } 290 291 var formConfiguration = ''; 292 293 demographicFormVariant = demographicData.hasOwnProperty('variant') ? demographicData.variant : ''; 294 295 var demographicFormFields = demographicData.hasOwnProperty('fields') ? demographicData.fields : {}; 296 for (var i = 0; i < demographicFormFields.length; i++) { 297 var fieldName = demographicFormFields[i].name; 298 // These names are tightly coupled to Lytic's User Web Pathfora LQL 299 // statement. In short, if you change here, you'll need to update the LQL statement as well. 300 fieldsToDisplay.push(constructDisplayField(fieldName, spot)); 301 initialValues[fieldName] = demographicFormFields[i].value; 302 formConfiguration += (formConfiguration === '' ? '' : ', ') + f
302ieldName + '_' + spot; 303 } 304 305 // Create demographic form 306 showLyticsForm(); 307 var $CompanyName = $('#company_name_' + spot); 308 if (!$CompanyName.val()) { 309 $CompanyName.val(initialValues.company_name); 310 } 311 } 312 }, 313 }); 314 } 315 316 function loadDependencies(){ 317 // loads all dependencies before pushing data to demographics ask form 318 var loadDiveFormFieldsJsDeferred = loadJs('/static/job_graph/dive-form-fields.js', 1, null); 319 var loadJobGraphJsDeferred = loadJs('//d1tawsf5ksqu42.cloudfront.net/job_graph/job_graph_v2.js', 1, null); 320 var loadPathfora = loadJs('https://c.lytics.io/static/pathfora.min.js', 1, null); 321 //Loads dive-form-fields.js and job_graph_v2.js in parallel 322 //when resolved trigger decideLyticsFormsToDisplay function 323 $.when( loadDiveFormFieldsJsDeferred, loadJobGraphJsDeferred, loadPathfora ) 324 .done(function() { 325 jstag.getid(decideLyticsFormsToDisplay); 326 }); 327 } 328 329 var setupDemographicForm = function(profile) { 330 userAudiences = profile.data.user.segments 331 var userInValidSubscriberList = decideIfUserIsSubscribed(userAudiences); 332 if (userInValidSubscriberList) { 333 loadDependencies(); 334 } 335 }; 336 337 // lytics could not be loaded due to something like the adblocker/cookie banner, so check to see if it's defined. 338 if (typeof jstag !== 'undefined') { 339 jstag.call('entityReady', setupDemographicForm); 340 } 341 342 function alternateSessions() { 343 // Persist a flag in local storage that allows us to alternate between light and dark reading signups each browser 344 // session if a user is subscribed to both cio and cybersecurity dives 345 if (!sessionStorage.getItem('sessionStarted')) { 346 sessionStorage.setItem('sessionStarted', 'true'); 347 348 let last = localStorage.getItem('alternateExperience'); 349 let current = last === 'lightreading' ? 'darkreading' : 'lightreading'; 350 351 localStorage.setItem('alternateExperience', current); 352 } 353 354 return localStorage.getItem('alternateExperience') 355 } 356 357 function displayITMediaSignupForm(it_pub) { 358 // Fill in the values for the IT Media Signup form depending on if we want to show 359 // the light reading or dark reading signup 360 361 // values for the IT Media Signup form 362 const itSignup = {} 363 364 if (it_pub === 'lightreading') { 365 itSignup['headline_industry'] = 'IT' 366 itSignup['pub_logo'] = 'lightreading_black.png' 367 itSignup['site_name'] = 'Light Reading' 368 itSignup['pub_link'] = 'https://www.lightreading.com/' 369 itSignup['site_copy'] = 'Get exclusive insights on emerging telecom trends, actionable network strategies, and the latest in 5G, fiber and cloud innovationsâdirect to your inbox.' 370 itSignup['img_classname'] = 'light-reading-logo' 371 } else if (it_pub === 'darkreading') { 372 itSignup['headline_industry'] = 'cybersecurity' 373 itSignup['pub_logo'] = 'darkreading_black.png' 374 itSignup['site_name'] = 'Dark Reading' 375 itSignup['pub_link'] = 'https://www.darkreading.com/' 376 itSignup['site_copy'] = 'Get exclusive insights on cyber threats, essential security intelligence, and the latest trends in cyber defenseâdirect to your inbox.' 377 itSignup['img_classname'] = "dark-reading-logo" 378 } 379 380 const itSignupWrapper = $('.sidebar-signup-box-it-pub') 381 382 // Update signup form values 383 itSignupWrapper.find('a.it_signup_pub_link').attr('href', `${itSignup['pub_link']}`) // any signup links 384 itSignupWrapper.find('span.it_signup_pub_link').text(itSignup['pub_link']) // signup link text 385 itSignupWrapper.find('.it_signup_site_name').text(itSignup['site_name']) 386 itSignupWrapper.find('#it_media_signup_button').attr('data-sailthru-list-name', itSignup['site_name']) 387 itSignupWrapper.find('.sidebar-signup-box-it-pub__related-publication__logo').attr({ 388 'src': `https://d12v9rtnomnebu.cloudfront.net/logo/publications/${itSignup['pub_logo']}`, 389 'alt': `${itSignup['site_name']} Pub Logo` 390 }).addClass( 391 `${itSignup['img_classname']}` 392 ) 393 itSignupWrapper.find('.it_signup_headline_industry').text(itSignup['headline_industry']) 394 itSignupWrapper.find('.it_signup_site_name').text(itSignup['site_name']) 395 itSignupWrapper.find('.it_signup_copy').text(itSignup['site_copy']) 396 397 // show IT Media form 398 hideSignUpForm(); 399 $('.sidebar-signup-box').hide(); 400 itSignupWrapper.show(); 401 } 402 403 /* 404 * Code for IT Media Sidebar Signup Experiment 405 */ 406 function showSignupFormForITMedia() { 407 // Check if we want to show the IT signup form in place of the demographics form. 408 // Form display logic: 409 // - if the user is subscribed to cio, show the Light Reading signup 410 // - if the user is subscribed to cyber, show the Dark Reading signup 411 // - if the user is subscribed to both cio and cyber, alternate between the Light and 412 // Dark reading signups based on the browser session 413 414 const isCioSubscriber = userAudiences.includes('any_cio_dive_subs') 415 const isCyberSubscriber = userAudiences.includes('any_cybersecurity_dive_subs') 416 417 if (isCioSubscriber && isCyberSubscriber) { 418 displayITMediaSignupForm(alternateSessions()) 419 } else if (isCioSubscriber) { 420 displayITMediaSignupForm('lightreading') 421 } else if (isCyberSubscriber) { 422 displayITMediaSignupForm('darkreading') 423 } else { 424 return false // user is not a subscriber of cio and/or cyber 425 } 426 427 sailthruSource = 'sidebar-recirculation'; // update the sailthru source for the user signup 428 return true // user is a subscriber of cio and/or cyber 429 } 430 431 function getSignupPostData(sailthruListName) { 432 let postData = { 433 email: userEmail, 434 list_name: sailthruListName, 435 subscribe_requested: '1', // 1 to subscribe, 0 to unsubscribe 436 source: sailthruSource, 437 }; 438 439 // Add optional UTM parameters if available
440 var urlParams = new URLSearchParams(window.location.search); 441 for (const param of ['utm_medium', 'utm_source', 'utm_campaign']) { 442 if (urlParams.get(param)) { 443 postData[param] = urlParams.get(param); 444 } 445 } 446 447 if (document.referrer) { 448 postData.referrer = document.referrer; 449 } 450 451 return postData; 452 } 453 454 function signupUser(postData) { 455 // Submit data via AJAX POST request 456 $.ajax({ 457 url: '/apisignup/', 458 type: 'POST', 459 data: postData, 460 traditional: false, 461 success: function(response) { 462 $('.sidebar-signup-box-it-pub__sign-up-form').hide(); 463 $('.sidebar-signup-box-it-pub__success-message').show(); 464 $('.sidebar-signup-box-spinner').hide(); 465 }, 466 error: function(xhr, status, error) { 467 // this is just an experiment, so its ok for now 468 // for future, what if it fails? 469 } 470 }); 471 } 472 473 $('#it_media_signup_button').click(function() { 474 $('.sidebar-signup-box-spinner').show(); 475 const sailthruListName = $(this).attr('data-sailthru-list-name'); 476 const postData = getSignupPostData(sailthruListName); 477 signupUser(postData); 478 }); 479}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.