PageSourceSearch

https://www.smartcitiesdive.com/static/js/subscriberDemographicsForm.js?520919230926

js smartcitiesdive.com collected 2026-09-24 08:31:29 UTC 18,030 bytes, 479 lines download raw bytes

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.