Controllers.prototype.manageCampaign = (function () { return newManageCampaign; function newManageCampaign(c, t, d, rCb, tmplCpm, cmpVal, dftVal) { return new ManageCampaign(c, t, d, rCb, tmplCpm, cmpVal, dftVal); } function ManageCampaign(core, target, data, returnCB, templateCampaign, campaignValue, draftValue) { var uf = null, bf = null, events = new EventManager, focusDefined = isDefined(campaignValue), template = getTemplate(focusDefined && !templateCampaign, draftValue), appsList = null, data = null, element = null, elements = null, forecast = null; new DataManager(core.userID, focusDefined || !!draftValue, campaignValue, draftValue, templateCampaign, init); this.exit = exit; function Data(d, id, core) { var impBudget = getValue(d, "impBudget", 0); // Not accessed by the UI this.id = d.id || id; // Main this.active = getValue(d, 'active', true); this.archived = getValue(d, 'archived'); this.name = getValue(d, 'name'); this.notes = getValue(d, 'notes'); this.budget = getValue(d, "budget", 0); this.impBudget = impBudget; this.budgetType = impBudget == 0 ? "credits" : "impressions"; this.optBucket = getValue(d, 'optBucket', 0); this.keywords = getValue(d, 'keywords', []); this.placement = getValue(d, 'placement'); this.scheduled = getValue(d, 'scheduled', false); this.start = getValue(d, 'start', 0); this.end = getValue(d, 'end', 0); // Segments this.segments = getValue(d, 'segments'); // Adgroups this.adGroups = getValue(d, 'adGroups'); // Misc not related to a standard campaign this.type = getValue(d, 'type', 'meteora'); this.remoteID = getValue(d, 'remoteID'); // Apps this.apps = getApps(d); var originalKeys = Object.keys(this); // used by the clean func // Used by JS front-end for certain messages and notifications. // This data will not be processed by the back-end. this.originalBudget = getValue(d, "budget", -1); this.originalBudgetType = getValue(d, "budgetType", ""); this.originalStart = getValue(d, "start"); this.originalEnd = getValue(d, "end"); this.billing = getValue(d, "billing", null); this.profiles = getValue(d, "profiles", null); this.pixelFired = didThePixelFireToday(d.lastPixelHit); this.brand = core.brand; // Full lists this.lists = d.lists; this.ioState = d.ioState.has === true; this.forecast = (d.campaignForecast || d.campaignDraftForecast || { forecast: null }).forecast; // clean returns a clean Data object without all the extra things we add to it for the UI this.clean = function (d) { // Can't wait for typescript var cp = {}; loop(originalKeys, function (_, key) { var val = d[key]; if (val != null) { cp[key] = val; } }); return cp; }; function getValue(d, k, r) { if (r === undefined) r = null; if (isDefined(d) && isDefined(d[k])) return d[k]; else return r; } function getApps(d) { var empty = !isDefined(d.apps), apps = getValue(d, 'apps', {}); if (campaignValue === null && draftValue === null || empty) { setApp('pacing'); setApp('geography'); setApp('weather'); setApp('college'); setApp('domainTargeting'); setApp('frequencyCap'); setApp('contentCategories'); setApp('ipTargeting'); } var qv = getQueryVariables(); if (!!qv.apps) { var qvApps = qv.apps.split(","); loop(qvApps, function (k, v) { setApp(v); }); } return apps; function setApp(k) { if (!isDefined(apps[k])) { apps[k] = {}; } } } } function init(error, d) { appsList = d['apps/list/short'] || {}; if (d.campaignDraftForecast) { forecast = d.campaignDraftForecast.forecast; } else if (d.campaignForecast) { forecast = d.campaignForecast.forecast; } if (hasElements(d.adGroupsList) && hasElements(d.segmentsList)) { element = getElementFromString(template); data = new Data(getFocusObj(d), campaignValue, core); setElements(d); setFns(d); setTabbedWin(data, d); events.add(element.querySelector('.saveButton'), 'click', save); if (!!elements.addAnother) events.add(elements.addAnother, 'click', addAnother); if (!!elements.saveDraft) events.add(elements.saveDraft, 'click', draft); target.appendChild(element); } else { element = baseFresh(d, target).element; } returnCB(); function hasElements(arr) { return isArray(arr) && arr.length > 0; } } function getFocusObj(d) { var o = {}; if (!!d.campaigns) { o = d.campaigns.data; } else if (!!d.campaignTemplate) { o = d.campaignTemplate.settings; } o.billing = d.billing; o.profiles = d.profiles; o.lastPixelHit = d.lastPixelHit; o.lists = { 'segments': getSegments(d.segmentsList, d.proximityList.data, d.evegmentsList, d.kochava, d.ifaSegments), 'adGroups': d.adGroupsList }; o.ioState = d.ioState; return o; } function getSegments(retargeting, proximity, event, koch, ifas) { var segs = []; loop(koch, function (_, v) { segs.push(new Segment(v.id, v.name, "kochava")); }); loop((ifas || {}).data, function (_, v) { segs.push(new Segment(v.id, v.name, "ifa")); }); loop(retargeting, function (_, v) { segs.push(new Segment(v.id, v.data.name, "retargeting")); }); loop(proximity, function (id, v) { segs.push(new Segment(id, v.name, "proximity")); }); loop(event, function (_, v) { segs.push(new Segment(v.segmentId, v.name, "event")); }); return segs; } function setElements(d) { elements = new Elements(element, { 'sidePanel': { "target": baseSidePanel(data, element.querySelector('section'), forecast, d.statement) }, 'section': { 'selector': 'section' }, 'tabbedWindow': {}, 'main': {}, 'segments': {}, 'adGroups': {}, 'apps': {}, 'saveDraft': { 'selector': '.draftButton' }, 'addAnother': { 'selector': '.addAnother' }, }); } function setFns(d) { uf = new UtilityFns(elements.sidePanel.refresh, core, removeApp, getAppName, d); bf = new BaseFns(updateTabs); } function setTabbedWin(d) { var a = []; a.push(new Tab(d, elements, 'main', 'Main', baseMain, uf, bf, campaignValue)); a.push(new Tab(d, elements, 'segments', 'Segments', baseSegments, uf, bf, campaignValue)); a.push(new Tab(d, elements, 'adGroups', 'Ad Groups', baseAdGroups, uf, bf, campaignValue)); a.push(new Tab(d, elements, 'apps', 'Apps', baseApps, uf, bf, campaignValue)); elements.tabbedWindow = new TabbedContainer(elements.section, { 'data': a, 'config': { 'prepend': true } }, tabSelected); } // Need to look into this and see if it actually works. function tabSelected(e) { if (e === 3) elements.apps.set(); } function updateTabs(e) { if (e === 0) { elements.tabbedWindow.hideTab(1); } else { elements.tabbedWindow.showTab(1); } } function getAppName(key, def) { var app = appsList[key]; return isDefined(app) ? app.name : (def || 'N/A'); // it should "always" be defined, but in case it isn't, don't crash. } function removeApp(key) { if (!!campaignValue) { apiRequest("DELETE", "campaign/app/byKey/" + key, campaignValue, null, removeAppCb); } else { removeAppCb({ 'key': key }, 200); } } function removeAppCb(e, s) { if (s !== 200) { uf.setError("There was an error removing this app") return; } if (!e.key) { return; } delete data.apps[e.key]; var t = elements.apps.getApp(e.key) if (!!t && !!t.exit) { t.exit(); } } function save(e) { preventDefault(e); saveCampaign(data, core.userID, templateCampaign, campaignValue, draftValue, uf); } function draft(e) { preventDefault(e); saveDraft(data, core.userID, draftValue, uf); } // This works just like save at the moment. We need to adjust this so that it clears the input fields and start the // campaign creation process over after saving. function addAnother(e) { preventDefault(e); saveCampaign(data, core.userID, templateCampaign, campaignValue, draftValue, uf); } function exit() { removeChild(target, element); if (events) events.reset(); if (elements) elements.exit(); } } function DataManager(uid, fd, cmpV, dftV, tmplCmp, fn) { var count = 0, total = fd ? 2 : 1, d = {}, apiReq = { 'adGroupsList': null, 'billing': null, 'profiles': null, 'apps/list/short': null, 'proximityList': { "source": "segments/proximity/byOwner" }, 'segmentsList': null, 'evegmentsList': null, 'domainList': null, "ioState": null, 'system/apps/list': { 'noID': true } }, editReq = {}; if (core.isAdmin || core.brand.name === 'Sito Mobile') { apiReq.kochava = { source: 'segments/kochava', noID: true, }; apiReq.ifaSegments = { source: 'ifaSegmentsList' }; } if (tmplCmp) { editReq.campaignTemplate = null; } else if (!!dftV) { editReq.campaigns = { 'source': 'campaignDraft', 'noID': true, 'args': dftV }; editReq.campaignDraftForecast = { 'source': 'campaignDraftForecast', 'noID': true, 'args': dftV }; } else { editReq.campaigns = { 'source': 'campaigns/byCID' }; editReq.campaignForecast = null; // TODO: Need to replace this with statement endpoint editReq.statement = { 'source': 'statements/byCID', 'args': cmpV, 'noID': true }; } new ApiManager(apiReq, uid, check); if (fd) new ApiManager(editReq, cmpV, check); function check(e, v) { if (isDefined(v)) setValues(v); if (++count == total) fn(null, d); } function setValues(v) { for (var k in v) { d[k] = v[k]; } } } function Segment(id, label, type) { this.id = id || ""; this.label = label || ""; this.type = type || ""; } function BaseFns(ut) { this.updateTabs = ut; } function UtilityFns(rfsh, core, removeApp, getAppName, d) { var notif = core.notifications; this.refreshSidePanel = rfsh; this.notif = notif; this.setSuccess = notif.setSuccess; this.setError = notif.setError; this.setMissingFields = notif.setMissingFields; this.removeApp = removeApp; this.getAppName = getAppName; this.data = d; this.core = core; } function Tab(d, eles, k, ttl, fn, uf, bf, cmpV) { var fnv = fn(d, cmpV, uf, bf); this.key = k; this.title = ttl; this.element = fnv.getE(); eles[k] = fnv; } function didThePixelFireToday(pixel) { if (!pixel || typeof pixel !== 'string' || pixel.length < 10) { return false; } // In case the user's timezone is too far ahead return (Date.now() - Date.parse(pixel.substr(0, 10))) / (24 * 3600 * 1000) < 2; } function getTemplate(editState, draftValue) { var actionName = editState === true ? 'Edit' : 'Create', updateName = editState === true ? 'Save' : 'Create', header = '

' + actionName + ' Campaign

', section = '
', addAnother = editState === true || !!draftValue ? '' : 'Add another campaign', draft = editState === true ? '' : 'Save draft' + (!draftValue ? '' : ''), footer = ''; return '
' + header + section + footer + '
'; } })();