Skip to content

Instantly share code, notes, and snippets.

@jesseditson
Created July 10, 2015 20:39
Show Gist options
  • Save jesseditson/63a2387bfec72dc35bb1 to your computer and use it in GitHub Desktop.
Save jesseditson/63a2387bfec72dc35bb1 to your computer and use it in GitHub Desktop.
module swapper first pass
This file has been truncated, but you can view the full file.
---------- diff:
app.js
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
/** @class */
var AdvisorApp = new Marionette.Application({
templates: (AdvisorApp || {}).templates
});
AdvisorApp.addRegions({
mainRegion: '#main-region',
headerRegion: '#header-region',
footerRegion: '#footer-region',
alertRegion: '#alert-region'
});
/** @returns {String} of the current route */
AdvisorApp.getCurrentRoute = function() {
return Backbone.history.fragment;
};
AdvisorApp.alert = function(options) {
AdvisorApp.alertRegion.show(new AdvisorApp.module('Common').AlertView({
model: new Backbone.Model(options)
}));
};
/**
* @param {String} route
* @param {Object} options
*/
AdvisorApp.navigate = function(route, options) {
options = options || {};
Backbone.history.navigate(route, options);
AdvisorApp.trigger('router:navigate');
AdvisorApp.trigger('usage:pageview');
};
/** @class */
AdvisorApp.AppRouter = Backbone.Router.extend({
routes: {
'': 'showHome'
},
'showHome': function() {
AdvisorApp.navigate('initiative', {replace: true, trigger: true});
}
});
/**
* @param {Backbone.Router} router
* @param {String} route
* @param {Array} options
*/
Backbone.history.on('route', function(router, route, options) {
AdvisorApp.trigger('usage:pageview');
});
/** @param {Object} options */
AdvisorApp.addInitializer(function(options) {
AdvisorApp.request('session:entity:set', options);
// Legacy support for modules that rely on this resolving.
// TODO: Remove this.
AdvisorApp.initializingSession = new $.Deferred();
AdvisorApp.initializingSession.resolve();
});
AdvisorApp.on('start', function() {
AdvisorApp.appRouter = new AdvisorApp.AppRouter();
if (Backbone.history) {
Backbone.history.start();
}
});
AdvisorApp.on('permissionDenied', function() {
AdvisorApp.mainRegion.show(new AdvisorApp.module('Common.Views').PermissionDenied());
});
+ module.exports = AdvisorApp;
---------- diff:
common/_highchart-view.js
+ /* global dependencies */
+ var moment = require('moment');
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
// TODO: Remove the underscore from the front of this filename once we have a proper import solution.
// TODO: This "base" chart is getting a little too specific. Should break out into smaller extended chunks soon.
- AdvisorApp.module('Common.Views', function(Views) {
- Views.HighchartView = Marionette.LayoutView.extend({
- template: Handlebars.templates.Chart,
+ /* module definition */
+ var Views = {};
- defaultOptions: {
- /** Query Params in collection fetch URL */
- collectionFilters: null,
- // TODO: Grab this from element on the page that shares same value so if and when the styleguide is update
- // so is this!
- /** Default chart colors */
- colors: ['#2FA580'],
- /** Default metric from collection's models to graph in chart. */
- metric: 'kpi',
- showToolbar: false
- },
- /** Chart will not render until loading is set to false. */
- loading: true,
- /*
- The chartRegion isn't used in a traditional Marionette.LayoutView way here. I could get away without
- defining it here at all. However I am defining it to illustrate to you, dear reader, that we are rendering
- two regions in this view. The toolbarRegion is rendered in the normal way. The chartRegion is not.
- It is passed as the el on the Highcharts options object. Again, defining here is merely semnatic.
- */
- regions: {
- chartRegion: '.highchart',
- toolbarRegion: '.chart-toolbar'
- },
- initialize: function(options) {
- this.options = this.getOptions(options);
- this.chartToolbar = new Views.ChartToolbar(this.getChartToolbarOptions());
- this.bindEventListeners();
- this.fetchCollection();
- },
- /**
- * @param {Object} options
- * @returns {Object}
- */
- getOptions: function(options) {
- return _.defaults(options || {}, this.defaultOptions);
- },
- /**
- * @returns {Object}
- */
- getChartToolbarOptions: function() {
- return {
- metric: this.options.metric,
- parentView: this,
- platform: this.options.platform,
- showDateRangePicker: true,
- showMetricPicker: true,
- startDate: this.options.startDate,
- endDate: this.options.endDate,
- campaign: this.options.campaign,
- showCustomReport: this.options.showCustomReport
- };
- },
- bindEventListeners: function() {
- var triggerChangeMetric = _.bind(this.trigger, this, 'changeMetric');
- var triggerChangeDateRange = _.bind(this.trigger, this, 'changeDateRange');
- var triggerError = _.bind(this.trigger, this, 'error');
- this.listenTo(this.chartToolbar, 'changeMetric', triggerChangeMetric);
- this.listenTo(this.chartToolbar, 'changeDateRange', triggerChangeDateRange);
- this.listenTo(this.chartToolbar, 'error', triggerError);
- this.on('changeMetric', this.changeMetric, this);
- this.on('changeDateRange', this.changeDateRange, this);
- },
- /**
- * @returns {Backbone.View} this
- */
- onShow: function() {
- this.createChartForMetric();
- if (this.options.showToolbar) {
- this.toolbarRegion.show(this.chartToolbar);
- }
- return this;
- },
- /**
- * @param {Object} options
- */
- createChartForMetric: function(options) {
- this.chart = new Highcharts.Chart(this.getHighchartsOptions(options));
- this.chart.showLoading('Loading data...');
- },
- /**
- * @param {Object|undefined} options for Highcharts. Only passed in if we actually have series data yet.
- * If still loading then nothing is passed in.
- * @returns {Object} of highcharts options.
- */
- getHighchartsOptions: function(options) {
- var baseOptions = {
- colors: this.options.colors,
- chart: {
- type: 'column',
- /*
- At this point this view's template is not attached to the DOM so we need to use Backbone's
- scoped jQuery selector to access the not-yet-attached-to-the-DOM element for the
- #renderTo property.
- */
- renderTo: this.$(this.chartRegion.el).get(0),
- height: 300
- },
- exporting: {
- enabled: false
- },
- credits: {
- enabled: false
- },
- legend: {
- enabled: false
- },
- yAxis: {
- min: 0,
- maxPadding: 0.05,
- endOnTick: true,
- title: null
- },
- xAxis: {
- type: 'datetime',
- labels: {
- overflow: 'justify'
- }
- },
- plotOptions: {
- series: {
- marker: {
- enabled: false
- },
- lineWidth: 1.9,
- states: {
- hover: {
- lineWidth: 1.9
- }
+ Views.HighchartView = Marionette.LayoutView.extend({
+ template: Handlebars.templates.Chart,
+ defaultOptions: {
+ /** Query Params in collection fetch URL */
+ collectionFilters: null,
+ // TODO: Grab this from element on the page that shares same value so if and when the styleguide is update
+ // so is this!
+ /** Default chart colors */
+ colors: ['#2FA580'],
+ /** Default metric from collection's models to graph in chart. */
+ metric: 'kpi',
+ showToolbar: false
+ },
+ /** Chart will not render until loading is set to false. */
+ loading: true,
+ /*
+ The chartRegion isn't used in a traditional Marionette.LayoutView way here. I could get away without
+ defining it here at all. However I am defining it to illustrate to you, dear reader, that we are rendering
+ two regions in this view. The toolbarRegion is rendered in the normal way. The chartRegion is not.
+ It is passed as the el on the Highcharts options object. Again, defining here is merely semnatic.
+ */
+ regions: {
+ chartRegion: '.highchart',
+ toolbarRegion: '.chart-toolbar'
+ },
+ initialize: function(options) {
+ this.options = this.getOptions(options);
+ this.chartToolbar = new Views.ChartToolbar(this.getChartToolbarOptions());
+ this.bindEventListeners();
+ this.fetchCollection();
+ },
+ /**
+ * @param {Object} options
+ * @returns {Object}
+ */
+ getOptions: function(options) {
+ return _.defaults(options || {}, this.defaultOptions);
+ },
+ /**
+ * @returns {Object}
+ */
+ getChartToolbarOptions: function() {
+ return {
+ metric: this.options.metric,
+ parentView: this,
+ platform: this.options.platform,
+ showDateRangePicker: true,
+ showMetricPicker: true,
+ startDate: this.options.startDate,
+ endDate: this.options.endDate,
+ campaign: this.options.campaign,
+ showCustomReport: this.options.showCustomReport
+ };
+ },
+ bindEventListeners: function() {
+ var triggerChangeMetric = _.bind(this.trigger, this, 'changeMetric');
+ var triggerChangeDateRange = _.bind(this.trigger, this, 'changeDateRange');
+ var triggerError = _.bind(this.trigger, this, 'error');
+ this.listenTo(this.chartToolbar, 'changeMetric', triggerChangeMetric);
+ this.listenTo(this.chartToolbar, 'changeDateRange', triggerChangeDateRange);
+ this.listenTo(this.chartToolbar, 'error', triggerError);
+ this.on('changeMetric', this.changeMetric, this);
+ this.on('changeDateRange', this.changeDateRange, this);
+ },
+ /**
+ * @returns {Backbone.View} this
+ */
+ onShow: function() {
+ this.createChartForMetric();
+ if (this.options.showToolbar) {
+ this.toolbarRegion.show(this.chartToolbar);
+ }
+ return this;
+ },
+ /**
+ * @param {Object} options
+ */
+ createChartForMetric: function(options) {
+ this.chart = new Highcharts.Chart(this.getHighchartsOptions(options));
+ this.chart.showLoading('Loading data...');
+ },
+ /**
+ * @param {Object|undefined} options for Highcharts. Only passed in if we actually have series data yet.
+ * If still loading then nothing is passed in.
+ * @returns {Object} of highcharts options.
+ */
+ getHighchartsOptions: function(options) {
+ var baseOptions = {
+ colors: this.options.colors,
+ chart: {
+ type: 'column',
+ /*
+ At this point this view's template is not attached to the DOM so we need to use Backbone's
+ scoped jQuery selector to access the not-yet-attached-to-the-DOM element for the
+ #renderTo property.
+ */
+ renderTo: this.$(this.chartRegion.el).get(0),
+ height: 300
+ },
+ exporting: {
+ enabled: false
+ },
+ credits: {
+ enabled: false
+ },
+ legend: {
+ enabled: false
+ },
+ yAxis: {
+ min: 0,
+ maxPadding: 0.05,
+ endOnTick: true,
+ title: null
+ },
+ xAxis: {
+ type: 'datetime',
+ labels: {
+ overflow: 'justify'
+ }
+ },
+ plotOptions: {
+ series: {
+ marker: {
+ enabled: false
+ },
+ lineWidth: 1.9,
+ states: {
+ hover: {
+ lineWidth: 1.9
}
}
- },
- title: null,
- series: []
- };
- options = options || {};
- return $.extend(true, baseOptions, options);
- },
- /**
- * @param {$.Event} metric from the click event.
+ }
+ },
+ title: null,
+ series: []
+ };
+ options = options || {};
+ return $.extend(true, baseOptions, options);
+ },
+ /**
+ * @param {$.Event} metric from the click event.
+ */
+ changeMetric: function(metric) {
+ this.options.metric = metric;
+ this.drawChart();
+ },
+ /**
+ * @param {String} event
+ * @param {Object} dates
+ */
+ changeDateRange: function(dateRanges) {
+ var startDate = this.getDateString(dateRanges.startDate);
+ var endDate = this.getDateString(dateRanges.endDate);
+ /** If the date range is the same that's already selected then don't re-fetch */
+ /**
+ * PLATFORM DEPENDECY CHECK
+ * Twitter charts need the start parameter returned as 'start__gte' & 'start__lte'
+ * Would be nice to update one of the endpoints so that we can use the same code here.
*/
- changeMetric: function(metric) {
- this.options.metric = metric;
- this.drawChart();
- },
- /**
- * @param {String} event
- * @param {Object} dates
+ if (this.options.platform === 'facebook') {
+ if (this.options.collectionFilters.date_start__gte === startDate &&
+ this.options.collectionFilters.date_start__lte === endDate) {
+ return;
+ }
+ this.options.collectionFilters.date_start__gte = startDate;
+ this.options.collectionFilters.date_start__lte = endDate;
+ } else {
+ if (this.options.collectionFilters.start__gte === startDate &&
+ this.options.collectionFilters.start__lte === endDate) {
+ return;
+ }
+ this.options.collectionFilters.start__gte = startDate;
+ this.options.collectionFilters.start__lte = endDate;
+ }
+ this.fetchCollection();
+ },
+ /**
+ * @param {Moment|String} date
+ * @returns {String}
+ */
+ getDateString: function(date) {
+ var format = this.options.format || 'YYYY-MM-DD';
+ return moment.isMoment(date) ? date.format(format) : moment(date).format(format);
+ },
+ fetchCollection: function() {
+ var startDate;
+ var endDate;
+ this.loading = true;
+ /**
+ * PLATFORM DEPENDECY CHECK
+ * Twitter charts need the start parameter returned as 'start__gte' & 'start__lte'
+ * Would be nice to update one of the endpoints so that we can use the same code here.
*/
- changeDateRange: function(dateRanges) {
- var startDate = this.getDateString(dateRanges.startDate);
- var endDate = this.getDateString(dateRanges.endDate);
- /** If the date range is the same that's already selected then don't re-fetch */
+ if (this.options.platform == 'facebook') {
/**
- * PLATFORM DEPENDECY CHECK
- * Twitter charts need the start parameter returned as 'start__gte' & 'start__lte'
- * Would be nice to update one of the endpoints so that we can use the same code here.
+ * If we do not have a start and end date on the collectionFilters then these are the defaults. This is
+ * clearly a kind of gross bandaid. Would LOVE to kill this code soon.
*/
- if (this.options.platform === 'facebook') {
- if (this.options.collectionFilters.date_start__gte === startDate &&
- this.options.collectionFilters.date_start__lte === endDate) {
- return;
- }
- this.options.collectionFilters.date_start__gte = startDate;
- this.options.collectionFilters.date_start__lte = endDate;
- } else {
- if (this.options.collectionFilters.start__gte === startDate &&
- this.options.collectionFilters.start__lte === endDate) {
- return;
- }
- this.options.collectionFilters.start__gte = startDate;
- this.options.collectionFilters.start__lte = endDate;
+ if (!this.options.collectionFilters.date_start__gte && !this.options.collectionFilters.date_start__lte) {
+ this.options.collectionFilters.date_start__gte = moment().startOf('day').subtract(7, 'days');
+ this.options.collectionFilters.date_start__lte = moment().endOf('day');
}
- this.fetchCollection();
- },
- /**
- * @param {Moment|String} date
- * @returns {String}
- */
- getDateString: function(date) {
- var format = this.options.format || 'YYYY-MM-DD';
- return moment.isMoment(date) ? date.format(format) : moment(date).format(format);
- },
- fetchCollection: function() {
- var startDate;
- var endDate;
- this.loading = true;
+ /**
+ * If the collectionFilters's start and end dates aren't strings then convert them to strings.
+ */
+ if (!_.isString(this.options.collectionFilters.date_start__gte) ||
+ !_.isString(this.options.collectionFilters.date_start__lte)) {
+ this.options.collectionFilters.date_start__gte =
+ this.getDateString(this.options.collectionFilters.date_start__gte);
+ this.options.collectionFilters.date_start__lte =
+ this.getDateString(this.options.collectionFilters.date_start__lte);
+ }
+ } else {
/**
- * PLATFORM DEPENDECY CHECK
- * Twitter charts need the start parameter returned as 'start__gte' & 'start__lte'
- * Would be nice to update one of the endpoints so that we can use the same code here.
+ * If we do not have a start and end date on the collectionFilters then these are the defaults. This is
+ * clearly a kind of gross bandaid. Would LOVE to kill this code soon.
*/
- if (this.options.platform == 'facebook') {
- /**
- * If we do not have a start and end date on the collectionFilters then these are the defaults. This is
- * clearly a kind of gross bandaid. Would LOVE to kill this code soon.
- */
- if (!this.options.collectionFilters.date_start__gte && !this.options.collectionFilters.date_start__lte) {
- this.options.collectionFilters.date_start__gte = moment().startOf('day').subtract(7, 'days');
- this.options.collectionFilters.date_start__lte = moment().endOf('day');
- }
- /**
- * If the collectionFilters's start and end dates aren't strings then convert them to strings.
- */
- if (!_.isString(this.options.collectionFilters.date_start__gte) ||
- !_.isString(this.options.collectionFilters.date_start__lte)) {
- this.options.collectionFilters.date_start__gte =
- this.getDateString(this.options.collectionFilters.date_start__gte);
- this.options.collectionFilters.date_start__lte =
- this.getDateString(this.options.collectionFilters.date_start__lte);
- }
- } else {
- /**
- * If we do not have a start and end date on the collectionFilters then these are the defaults. This is
- * clearly a kind of gross bandaid. Would LOVE to kill this code soon.
- */
- if (!this.options.collectionFilters.start__gte && !this.options.collectionFilters.start__lte) {
- this.options.collectionFilters.start__gte = moment().startOf('day').subtract(7, 'days');
- this.options.collectionFilters.start__lte = moment().endOf('day');
- }
- /**
- * If the collectionFilters's start and end dates aren't strings then convert them to strings.
- */
- if (!_.isString(this.options.collectionFilters.start__gte) ||
- !_.isString(this.options.collectionFilters.start__lte)) {
- this.options.collectionFilters.start__gte =
- this.getDateString(this.options.collectionFilters.start__gte);
- this.options.collectionFilters.start__lte =
- this.getDateString(this.options.collectionFilters.start__lte);
- }
+ if (!this.options.collectionFilters.start__gte && !this.options.collectionFilters.start__lte) {
+ this.options.collectionFilters.start__gte = moment().startOf('day').subtract(7, 'days');
+ this.options.collectionFilters.start__lte = moment().endOf('day');
}
- this.collection.fetch({
- data: this.options.collectionFilters,
- success: _(this.onFetchSuccess).bind(this),
- /*
- When the model data returns from the server, it uses set to (intelligently) merge the fetched
- models, unless you pass `{reset: true}`, in which case the collection will be (efficiently) reset.
- */
- reset: true,
- cache: false
- });
- },
- /**
- * @param {Backbone.Collection} collection returned from server
- * @param {Object} response from server
- * @param {Object} options passed to fetch request
+ /**
+ * If the collectionFilters's start and end dates aren't strings then convert them to strings.
+ */
+ if (!_.isString(this.options.collectionFilters.start__gte) ||
+ !_.isString(this.options.collectionFilters.start__lte)) {
+ this.options.collectionFilters.start__gte =
+ this.getDateString(this.options.collectionFilters.start__gte);
+ this.options.collectionFilters.start__lte =
+ this.getDateString(this.options.collectionFilters.start__lte);
+ }
+ }
+ this.collection.fetch({
+ data: this.options.collectionFilters,
+ success: _(this.onFetchSuccess).bind(this),
+ /*
+ When the model data returns from the server, it uses set to (intelligently) merge the fetched
+ models, unless you pass `{reset: true}`, in which case the collection will be (efficiently) reset.
+ */
+ reset: true,
+ cache: false
+ });
+ },
+ /**
+ * @param {Backbone.Collection} collection returned from server
+ * @param {Object} response from server
+ * @param {Object} options passed to fetch request
+ */
+ onFetchSuccess: function(collection, response, options) {
+ this.loading = false;
+ /**
+ * @param {Backbone.Model} model
*/
- onFetchSuccess: function(collection, response, options) {
- this.loading = false;
- /**
- * @param {Backbone.Model} model
- */
- collection.each(function(model) {
- model.set(this.setOnSuccess(model));
- }, this);
- this.drawChart();
- },
- drawChart: function() {
- /** Human readable metric name mapping for the chart. */
- var nameMapping = {
- app_clicks: 'App Clicks',
- app_engagements: 'App Engagements',
- clicks: 'Clicks',
- engagements: 'Engagements',
- engagement_rate: 'Engagement Rate',
- impressions: 'Impressions',
- kpi: 'KPI',
- kpi_primary_events: 'Results',
- kpi_primary_spend: 'Spend',
- spend: 'Spend'
- };
- var options = {};
- options.series = [];
- options.series.push({
- name: nameMapping[this.options.metric],
- data: this.collection.getHighchartsSeriesData(this.options.metric, this.options.platform)
- });
- /** Give special attention to monetary metrics. */
- if (_.contains(['kpi', 'kpi_primary_spend', 'spend'], this.options.metric)) {
- // NOTE: Using extend rather than direct assignment because less change of overriding desired options.
- $.extend(true, options, {
- yAxis: {
- labels: {
- formatter: function() {
- return '$' + this.axis.defaultLabelFormatter.call(this);
- }
- }
- },
- tooltip: {
+ collection.each(function(model) {
+ model.set(this.setOnSuccess(model));
+ }, this);
+ this.drawChart();
+ },
+ drawChart: function() {
+ /** Human readable metric name mapping for the chart. */
+ var nameMapping = {
+ app_clicks: 'App Clicks',
+ app_engagements: 'App Engagements',
+ clicks: 'Clicks',
+ engagements: 'Engagements',
+ engagement_rate: 'Engagement Rate',
+ impressions: 'Impressions',
+ kpi: 'KPI',
+ kpi_primary_events: 'Results',
+ kpi_primary_spend: 'Spend',
+ spend: 'Spend'
+ };
+ var options = {};
+ options.series = [];
+ options.series.push({
+ name: nameMapping[this.options.metric],
+ data: this.collection.getHighchartsSeriesData(this.options.metric, this.options.platform)
+ });
+ /** Give special attention to monetary metrics. */
+ if (_.contains(['kpi', 'kpi_primary_spend', 'spend'], this.options.metric)) {
+ // NOTE: Using extend rather than direct assignment because less change of overriding desired options.
+ $.extend(true, options, {
+ yAxis: {
+ labels: {
formatter: function() {
- return Highcharts.dateFormat('%a %d %b', this.point.x) +
- '<br /><b>' + this.point.series.name + '</b>: ' + accounting.formatMoney(this.point.y);
+ return '$' + this.axis.defaultLabelFormatter.call(this);
}
}
- });
- }
- /** Give special attention to percentage metrics. */
- if (_.contains(['engagement_rate'], this.options.metric)) {
- // NOTE: Using extend rather than direct assignment because less change of overriding desired options.
- $.extend(true, options, {
- yAxis: {
- labels: {
- formatter: function() {
- return this.axis.defaultLabelFormatter.call(this) + '%';
- }
- }
- },
- tooltip: {
+ },
+ tooltip: {
+ formatter: function() {
+ return Highcharts.dateFormat('%a %d %b', this.point.x) +
+ '<br /><b>' + this.point.series.name + '</b>: ' + accounting.formatMoney(this.point.y);
+ }
+ }
+ });
+ }
+ /** Give special attention to percentage metrics. */
+ if (_.contains(['engagement_rate'], this.options.metric)) {
+ // NOTE: Using extend rather than direct assignment because less change of overriding desired options.
+ $.extend(true, options, {
+ yAxis: {
+ labels: {
formatter: function() {
- return Highcharts.dateFormat('%a %d %b', this.point.x) +
- '<br /><b>' + this.point.series.name + '</b>: ' + (this.point.y).toFixed(2) + '%';
+ return this.axis.defaultLabelFormatter.call(this) + '%';
}
}
- });
- }
- this.createChartForMetric(options);
- this.chart.hideLoading();
+ },
+ tooltip: {
+ formatter: function() {
+ return Highcharts.dateFormat('%a %d %b', this.point.x) +
+ '<br /><b>' + this.point.series.name + '</b>: ' + (this.point.y).toFixed(2) + '%';
+ }
+ }
+ });
}
- });
+ this.createChartForMetric(options);
+ this.chart.hideLoading();
+ }
});
+ module.exports = Views;
---------- diff:
common/_metric-dropdown-view.js
- // TODO: Remove the underscore from the front of this filename once we have a proper import solution.
- AdvisorApp.module('Common.Views', function(Views, App) {
- this.MetricDropdown = Marionette.ItemView.extend({
- template: Handlebars.templates.Dropdown,
+ /* global dependencies */
+ var $ = require('jquery');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- events: {
- 'click .metric-selector a': 'newSelection'
- },
- /** Default list of Facebook AND Twitter metrics this view can graph in its chart. */
- metrics: {
- clicks: 'Clicks',
- impressions: 'Impressions',
- kpi: 'KPI',
- kpi_primary_events: 'Results',
- kpi_primary_spend: 'Spend',
- spend: 'Spend',
- engagements: 'Engagements',
- engagement_rate: 'Engagement Rate',
- app_clicks: 'App Clicks',
- app_engagements: 'App Engagements'
- },
+ // TODO: Remove the underscore from the front of this filename once we have a proper import solution.
+ /* module definition */
+ var Views = {};
- /**
- * @returns {Object} of variables we want accessible in our Handlebar Template.
- */
- templateHelpers: function() {
- return {
- controlValue: this.metrics[this.options.metric],
- metrics: this.platformMetrics
- };
- },
+ this.MetricDropdown = Marionette.ItemView.extend({
+ template: Handlebars.templates.Dropdown,
+ events: {
+ 'click .metric-selector a': 'newSelection'
+ },
+ /** Default list of Facebook AND Twitter metrics this view can graph in its chart. */
+ metrics: {
+ clicks: 'Clicks',
+ impressions: 'Impressions',
+ kpi: 'KPI',
+ kpi_primary_events: 'Results',
+ kpi_primary_spend: 'Spend',
+ spend: 'Spend',
+ engagements: 'Engagements',
+ engagement_rate: 'Engagement Rate',
+ app_clicks: 'App Clicks',
+ app_engagements: 'App Engagements'
+ },
+ /**
+ * @returns {Object} of variables we want accessible in our Handlebar Template.
+ */
+ templateHelpers: function() {
+ return {
+ controlValue: this.metrics[this.options.metric],
+ metrics: this.platformMetrics
+ };
+ },
+ newSelection: function(event) {
+ event.preventDefault();
+ this.options.metric = $(event.target).data('metric');
+ this.trigger('changeMetric', this.options.metric);
+ this.render();
+ App.trigger('usage:pageevent', 'View Performance', 'Daily trend in ' + this.metrics[this.options.metric]);
+ }
+ });
- newSelection: function(event) {
- event.preventDefault();
- this.options.metric = $(event.target).data('metric');
- this.trigger('changeMetric', this.options.metric);
- this.render();
- App.trigger('usage:pageevent', 'View Performance', 'Daily trend in ' + this.metrics[this.options.metric]);
- }
- });
- });
+ module.exports = Views;
---------- diff:
common/alert.js
- AdvisorApp.module('Common', function(Alert) {
- Alert.AlertView = Marionette.ItemView.extend({
- template: Handlebars.templates.alert,
- events: {
- 'closed.bs.alert': 'destroy'
- },
- initialize: function() {
- this.render();
- },
- templateHelpers: function() {
- this.setTypeOnOptions();
- return this.options;
- },
- setTypeOnOptions: function() {
- var alertTypes = _.arrayToDictionary(['success', 'warning', 'danger']);
- var type = this.model.get('type');
+ /* global dependencies */
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- type = alertTypes[type] ? type : 'info';
- // Would be cool to use Default object on this model instead but we
- // don't know what model will be passed in so we have to hack it instead.
- this.model.set('type', type);
- }
- });
+ /* module definition */
+ var Alert = {};
+ Alert.AlertView = Marionette.ItemView.extend({
+ template: Handlebars.templates.alert,
+ events: {
+ 'closed.bs.alert': 'destroy'
+ },
+ initialize: function() {
+ this.render();
+ },
+ templateHelpers: function() {
+ this.setTypeOnOptions();
+ return this.options;
+ },
+ setTypeOnOptions: function() {
+ var alertTypes = _.arrayToDictionary(['success', 'warning', 'danger']);
+ var type = this.model.get('type');
+ type = alertTypes[type] ? type : 'info';
+ // Would be cool to use Default object on this model instead but we
+ // don't know what model will be passed in so we have to hack it instead.
+ this.model.set('type', type);
+ }
});
+ module.exports = Alert;
---------- diff:
common/basic-view.js
- AdvisorApp.module('Common', function(Module) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- Module.BasicView = Marionette.ItemView.extend({
- template: _.template('<p><%= text %></p>'),
- templateHelpers: function() {
- return this.options;
- }
- });
+ /* module definition */
+ var Module = {};
+ Module.BasicView = Marionette.ItemView.extend({
+ template: _.template('<p><%= text %></p>'),
+ templateHelpers: function() {
+ return this.options;
+ }
});
+ module.exports = Module;
---------- diff:
common/behaviors.js
- AdvisorApp.module('Common.Behaviors', function(Behaviors, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var moment = require('moment');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- /**
- * @class
- */
- Behaviors.DateRangeValidate = Marionette.Behavior.extend({
- defaults: {
- startPicker: '.start-picker-sc',
- endPicker: '.end-picker-sc'
- },
+ /* module definition */
+ var Behaviors = {};
+ Behaviors.DateRangeValidate = Marionette.Behavior.extend({
+ defaults: {
+ startPicker: '.start-picker-sc',
+ endPicker: '.end-picker-sc'
+ },
+ /**
+ * @returns {Object}
+ */
+ ui: function() {
+ return {
+ startPicker: this.options.startPicker,
+ endPicker: this.options.endPicker
+ };
+ },
+ /**
+ * @param {$.Event} e from the click event.
+ */
+ changeValidLimit: function(e) {
+ var behaviorUI = e.data.behavior.ui;
+ var startPicker = behaviorUI.startPicker;
+ var endPicker = behaviorUI.endPicker;
+ var startDate = startPicker.find('input').val();
+ var endDate = endPicker.find('input').val();
+ endPicker.datepicker('setStartDate', startDate);
+ // We must specify the format here. See: https://github.com/moment/moment/issues/1407
+ if (moment(endDate, 'MM/DD/YYYY').isBefore(startDate, 'MM/DD/YYYY')) {
+ endPicker.datepicker('update', '');
+ }
+ },
+ onRender: function() {
+ this.ui.endPicker.datepicker({
+ todayHighlight: true
+ });
+ this.ui.startPicker.datepicker({
+ todayHighlight: true
+ }).on('changeDate', { behavior: this }, this.changeValidLimit);
+ }
+ });
+ Behaviors.NewCampaignDateRangeValidate = Behaviors.DateRangeValidate.extend({
+ onRender: function() {
/**
- * @returns {Object}
+ * Limit the dates availble for a Campaign to be within the dates of the Initiative.
*/
- ui: function() {
- return {
- startPicker: this.options.startPicker,
- endPicker: this.options.endPicker
- };
- },
+ var self = this;
+ this.ui.insertion_order_select.on('change', function(e) {
+ var insertion_order = self.ui.insertion_order_select.select2('data');
+ var initiative = self.view.model.get('initiative');
+ if (!_.isNull(insertion_order)) {
+ var ioStartDate = moment(insertion_order.start_date, 'YYYY-MM-DD').toDate();
+ var ioEndDate = moment(insertion_order.end_date, 'YYYY-MM-DD').toDate();
+ var initiativeStartDate = moment(initiative.start_date, 'YYYY-MM-DD').toDate();
+ var initiativeEndDate = moment(initiative.end_date, 'YYYY-MM-DD').toDate();
+ var startDate = _.max([ioStartDate, initiativeStartDate]);
+ var endDate = _.min([ioEndDate, initiativeEndDate]);
+ self.ui.endPicker.datepicker({
+ endDate: endDate,
+ startDate: startDate,
+ todayHighlight: true
+ }).on('changeDate', { behavior: self }, self.changeValidLimit);
+ self.ui.startPicker.datepicker({
+ endDate: endDate,
+ startDate: startDate,
+ todayHighlight: true
+ }).on('changeDate', { behavior: self }, self.changeValidLimit);
+ /** Default the Start/End Dates to the Initiative Start/End Dates */
+ self.ui.endPicker.datepicker('setDate', endDate);
+ self.ui.startPicker.datepicker('setDate', startDate);
+ }
+ });
+ }
+ });
+ Behaviors.CurrencyValidate = Marionette.Behavior.extend({
+ events: {
/**
- * @param {$.Event} e from the click event.
+ * Unformat string on focus
+ * @param {$.Event} e from the focus event
*/
- changeValidLimit: function(e) {
- var behaviorUI = e.data.behavior.ui;
- var startPicker = behaviorUI.startPicker;
- var endPicker = behaviorUI.endPicker;
- var startDate = startPicker.find('input').val();
- var endDate = endPicker.find('input').val();
- endPicker.datepicker('setStartDate', startDate);
- // We must specify the format here. See: https://github.com/moment/moment/issues/1407
- if (moment(endDate, 'MM/DD/YYYY').isBefore(startDate, 'MM/DD/YYYY')) {
- endPicker.datepicker('update', '');
- }
+ 'focus [data-currency]': function(e) {
+ /** Variables */
+ var target = e.currentTarget;
+ var value = $(target).val();
+ /** Remove everything except numbers and decimal mark */
+ var unformatted = accounting.unformat(value);
+ /** Set value */
+ $(target).val(unformatted || undefined);
},
- onRender: function() {
- this.ui.endPicker.datepicker({
- todayHighlight: true
- });
- this.ui.startPicker.datepicker({
- todayHighlight: true
- }).on('changeDate', { behavior: this }, this.changeValidLimit);
- }
- });
- /**
- * @class
- */
- Behaviors.NewCampaignDateRangeValidate = Behaviors.DateRangeValidate.extend({
- onRender: function() {
- /**
- * Limit the dates availble for a Campaign to be within the dates of the Initiative.
- */
- var self = this;
- this.ui.insertion_order_select.on('change', function(e) {
- var insertion_order = self.ui.insertion_order_select.select2('data');
- var initiative = self.view.model.get('initiative');
- if (!_.isNull(insertion_order)) {
- var ioStartDate = moment(insertion_order.start_date, 'YYYY-MM-DD').toDate();
- var ioEndDate = moment(insertion_order.end_date, 'YYYY-MM-DD').toDate();
- var initiativeStartDate = moment(initiative.start_date, 'YYYY-MM-DD').toDate();
- var initiativeEndDate = moment(initiative.end_date, 'YYYY-MM-DD').toDate();
- var startDate = _.max([ioStartDate, initiativeStartDate]);
- var endDate = _.min([ioEndDate, initiativeEndDate]);
- self.ui.endPicker.datepicker({
- endDate: endDate,
- startDate: startDate,
- todayHighlight: true
- }).on('changeDate', { behavior: self }, self.changeValidLimit);
- self.ui.startPicker.datepicker({
- endDate: endDate,
- startDate: startDate,
- todayHighlight: true
- }).on('changeDate', { behavior: self }, self.changeValidLimit);
- /** Default the Start/End Dates to the Initiative Start/End Dates */
- self.ui.endPicker.datepicker('setDate', endDate);
- self.ui.startPicker.datepicker('setDate', startDate);
- }
- });
- }
- });
- /**
- * @class
- */
- Behaviors.CurrencyValidate = Marionette.Behavior.extend({
- events: {
- /**
- * Unformat string on focus
- * @param {$.Event} e from the focus event
- */
- 'focus [data-currency]': function(e) {
- /** Variables */
- var target = e.currentTarget;
- var value = $(target).val();
- /** Remove everything except numbers and decimal mark */
- var unformatted = accounting.unformat(value);
- /** Set value */
- $(target).val(unformatted || undefined);
- },
- /**
- * Format currency on blur
- * @param {$.Event} e from the blur event
- */
- 'blur [data-currency]': function(e) {
- /** Variables */
- var target = e.currentTarget;
- var value = $(target).val();
- /** Default currency options */
- var defaults = accounting.settings.currency;
- /** Updated currency options if data attribute is available */
- var targetData = $(target).data();
- var updates = {
- symbol: targetData['currency-symbol'],
- decimal: targetData['currency-decimal'],
- thousand: targetData['currency-thousand'],
- precision: targetData['currency-precision'],
- format: targetData['currency-format']
- };
- /** Merged currency options */
- var options = $.extend({}, defaults, updates);
- // Is number
- var isNumber = value || (value === 0);
- /** Format value */
- var formatted = isNumber && accounting.formatMoney(value, options) || undefined;
- /** Set value */
- $(target).val(formatted);
- },
- /**
- * Only accept numbers and two decimal places
- * @param {$.Event} e from the keypress event
- */
- 'keypress [data-numeric]': function(e) {
- /** Variables */
- var target = e.currentTarget;
- var value = $(target).val();
- var keycode = e.keyCode || e.charCode;
- // If it's a backspace (8), allow it and move on.
- if (keycode === 8) {
- return;
- }
- var input = String.fromCharCode(keycode);
- var index = target.selectionStart;
- /** Updated string */
- var updated = value.substring(0, index) + input + value.substring(index);
- /** Expression */
- var expr = new RegExp(/^(\d+)?(?:\.\d{0,2})?$/);
- var invalid = !expr.test(updated);
- /** Precent default */
- if (invalid) {
- e.preventDefault();
- }
- },
- /**
- * DEPRECATED
- *
- * @param {$.Event} e from the click event.
- * @returns {Boolean|Undefined}
- */
- 'keyup .currency-sc': function(e) {
- var $target = $(e.currentTarget);
- var value = $target.val();
- var indexOfDecimal = value.indexOf('.');
- var maxLengthWithDecimal = indexOfDecimal + 3;
- if (isNaN(value.replace(/,/g, ''))) {
- $target.val(accounting.formatNumber(value));
- return false;
- }
- if (indexOfDecimal > -1 && (value.length > maxLengthWithDecimal)) {
- $target.val(value.substring(0, maxLengthWithDecimal));
- }
- }
- }
- });
- Behaviors.FadeIn = Marionette.Behavior.extend({
- onShow: function() {
- this.$el.css('opacity', 0).animate({
- opacity: 1
- }, 400);
- }
- });
- /**
- * @class
- */
- Behaviors.DisplayError = Marionette.Behavior.extend({
- defaults: {
- errorContainerSel: '#error',
- genericErrorMsg: 'There was an error.'
- },
- /**
- * @returns {Object}
+ /**
+ * Format currency on blur
+ * @param {$.Event} e from the blur event
*/
- ui: function() {
- return {
- errorContainer: this.options.errorContainerSel
+ 'blur [data-currency]': function(e) {
+ /** Variables */
+ var target = e.currentTarget;
+ var value = $(target).val();
+ /** Default currency options */
+ var defaults = accounting.settings.currency;
+ /** Updated currency options if data attribute is available */
+ var targetData = $(target).data();
+ var updates = {
+ symbol: targetData['currency-symbol'],
+ decimal: targetData['currency-decimal'],
+ thousand: targetData['currency-thousand'],
+ precision: targetData['currency-precision'],
+ format: targetData['currency-format']
};
+ /** Merged currency options */
+ var options = $.extend({}, defaults, updates);
+ // Is number
+ var isNumber = value || (value === 0);
+ /** Format value */
+ var formatted = isNumber && accounting.formatMoney(value, options) || undefined;
+ /** Set value */
+ $(target).val(formatted);
},
- /**
- * @param {String} errorText
+ /**
+ * Only accept numbers and two decimal places
+ * @param {$.Event} e from the keypress event
*/
- onError: function(errorText) {
- $(this.ui.errorContainer).html(errorText || this.options.genericErrorMsg).removeClass('hidden');
+ 'keypress [data-numeric]': function(e) {
+ /** Variables */
+ var target = e.currentTarget;
+ var value = $(target).val();
+ var keycode = e.keyCode || e.charCode;
+ // If it's a backspace (8), allow it and move on.
+ if (keycode === 8) {
+ return;
+ }
+ var input = String.fromCharCode(keycode);
+ var index = target.selectionStart;
+ /** Updated string */
+ var updated = value.substring(0, index) + input + value.substring(index);
+ /** Expression */
+ var expr = new RegExp(/^(\d+)?(?:\.\d{0,2})?$/);
+ var invalid = !expr.test(updated);
+ /** Precent default */
+ if (invalid) {
+ e.preventDefault();
+ }
},
- onClearError: function() {
- $(this.ui.errorContainer).empty().addClass('hidden');
+ /**
+ * DEPRECATED
+ *
+ * @param {$.Event} e from the click event.
+ * @returns {Boolean|Undefined}
+ */
+ 'keyup .currency-sc': function(e) {
+ var $target = $(e.currentTarget);
+ var value = $target.val();
+ var indexOfDecimal = value.indexOf('.');
+ var maxLengthWithDecimal = indexOfDecimal + 3;
+ if (isNaN(value.replace(/,/g, ''))) {
+ $target.val(accounting.formatNumber(value));
+ return false;
+ }
+ if (indexOfDecimal > -1 && (value.length > maxLengthWithDecimal)) {
+ $target.val(value.substring(0, maxLengthWithDecimal));
+ }
}
- });
- App.addInitializer(function() {
- Marionette.Behaviors.behaviorsLookup = function() {
- return Behaviors;
+ }
+ });
+ Behaviors.FadeIn = Marionette.Behavior.extend({
+ onShow: function() {
+ this.$el.css('opacity', 0).animate({
+ opacity: 1
+ }, 400);
+ }
+ });
+ Behaviors.DisplayError = Marionette.Behavior.extend({
+ defaults: {
+ errorContainerSel: '#error',
+ genericErrorMsg: 'There was an error.'
+ },
+ /**
+ * @returns {Object}
+ */
+ ui: function() {
+ return {
+ errorContainer: this.options.errorContainerSel
};
+ },
+ /**
+ * @param {String} errorText
+ */
+ onError: function(errorText) {
+ $(this.ui.errorContainer).html(errorText || this.options.genericErrorMsg).removeClass('hidden');
+ },
+ onClearError: function() {
+ $(this.ui.errorContainer).empty().addClass('hidden');
+ }
+ });
+ App.addInitializer(function() {
+ Marionette.Behaviors.behaviorsLookup = function() {
+ return Behaviors;
+ };
+ });
- });
- }, moment);
+ module.exports = Behaviors;
---------- diff:
common/chart-toolbar.js
- AdvisorApp.module('Common.Views', function(Views) {
- this.ChartToolbar = Marionette.LayoutView.extend({
- template: Handlebars.templates.ChartToolbar,
+ /* global dependencies */
+ var moment = require('moment');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- regions: {
- dateRangePickerRegion: '.date-range-picker',
- metricPickerRegion: '.metric-picker',
- customReportRegion: '.custom-report'
- },
- // UGH. I hate if statement code like this. Improve when you can.
- initialize: function() {
- if (this.options.showDateRangePicker) {
- this.setupDateRangePicker();
- }
+ /* module definition */
+ var Views = {};
- if (this.options.showMetricPicker) {
- this.setupMetricPicker();
- }
- /** Hiding CUSTOM REPORT button until functionality is updated */
- // if (this.options.showCustomReport) {
- // this.setupCustomReport();
- // }
- },
- setupDateRangePicker: function() {
- var dateRanges = AdvisorApp.module('Common.Views').DateRangePicker.prototype.defaults.dateRanges;
- this.dateRangeView = new (AdvisorApp.module('Common.Views')).DateRangePicker({
- dateRanges: _.omit(dateRanges, 'Yesterday', 'Today', 'Ending today', 'Ending next 7 days'),
- end: this.options.endDate,
- start: this.options.startDate
- });
- this.listenTo(this.dateRangeView.model, 'change:start change:end', this.changeDateRange);
- this.changeDateRange(this.dateRangeView.model);
- },
- getMoment: function(date) {
- return moment.isMoment(date) ? date : moment(date);
- },
- /**
- * @param {Backbone.Model} model
- */
- changeDateRange: function(model) {
- this.trigger('changeDateRange', {
- startDate: model.get('start'),
- endDate: model.get('end')
- });
- },
- setupMetricPicker: function() {
- var platform = _.string.capitalize(this.options.platform, true);
- this.metricPickerView = new AdvisorApp[platform].Views.MetricDropdown({
- metric: this.options.metric
- });
- this.listenTo(this.metricPickerView, 'changeMetric', function(metric) {
- this.trigger('changeMetric', metric);
- });
- },
- setupCustomReport: function() {
- this.customReportView = new (AdvisorApp.module('Common.Views')).CustomReportButton({
- campaign: this.options.campaign
- });
- this.listenTo(this.customReportView, 'error', _.bind(this.trigger, this, 'error'));
- },
- onShow: function() {
- if (this.options.showDateRangePicker) {
- this.dateRangePickerRegion.show(this.dateRangeView);
- }
- if (this.options.showMetricPicker) {
- this.metricPickerRegion.show(this.metricPickerView);
- }
- /** Hiding CUSTOM REPORT button until functionality is updated */
- // if (this.options.showCustomReport) {
- // this.customReportRegion.show(this.customReportView);
- // }
+ this.ChartToolbar = Marionette.LayoutView.extend({
+ template: Handlebars.templates.ChartToolbar,
+ regions: {
+ dateRangePickerRegion: '.date-range-picker',
+ metricPickerRegion: '.metric-picker',
+ customReportRegion: '.custom-report'
+ },
+ // UGH. I hate if statement code like this. Improve when you can.
+ initialize: function() {
+ if (this.options.showDateRangePicker) {
+ this.setupDateRangePicker();
}
- });
+ if (this.options.showMetricPicker) {
+ this.setupMetricPicker();
+ }
+ /** Hiding CUSTOM REPORT button until functionality is updated */
+ // if (this.options.showCustomReport) {
+ // this.setupCustomReport();
+ // }
+ },
+ setupDateRangePicker: function() {
+ var dateRanges = AdvisorApp.module('Common.Views').DateRangePicker.prototype.defaults.dateRanges;
+ this.dateRangeView = new (AdvisorApp.module('Common.Views')).DateRangePicker({
+ dateRanges: _.omit(dateRanges, 'Yesterday', 'Today', 'Ending today', 'Ending next 7 days'),
+ end: this.options.endDate,
+ start: this.options.startDate
+ });
+ this.listenTo(this.dateRangeView.model, 'change:start change:end', this.changeDateRange);
+ this.changeDateRange(this.dateRangeView.model);
+ },
+ getMoment: function(date) {
+ return moment.isMoment(date) ? date : moment(date);
+ },
+ /**
+ * @param {Backbone.Model} model
+ */
+ changeDateRange: function(model) {
+ this.trigger('changeDateRange', {
+ startDate: model.get('start'),
+ endDate: model.get('end')
+ });
+ },
+ setupMetricPicker: function() {
+ var platform = _.string.capitalize(this.options.platform, true);
+ this.metricPickerView = new AdvisorApp[platform].Views.MetricDropdown({
+ metric: this.options.metric
+ });
+ this.listenTo(this.metricPickerView, 'changeMetric', function(metric) {
+ this.trigger('changeMetric', metric);
+ });
+ },
+ setupCustomReport: function() {
+ this.customReportView = new (AdvisorApp.module('Common.Views')).CustomReportButton({
+ campaign: this.options.campaign
+ });
+ this.listenTo(this.customReportView, 'error', _.bind(this.trigger, this, 'error'));
+ },
+ onShow: function() {
+ if (this.options.showDateRangePicker) {
+ this.dateRangePickerRegion.show(this.dateRangeView);
+ }
+ if (this.options.showMetricPicker) {
+ this.metricPickerRegion.show(this.metricPickerView);
+ }
+ /** Hiding CUSTOM REPORT button until functionality is updated */
+ // if (this.options.showCustomReport) {
+ // this.customReportRegion.show(this.customReportView);
+ // }
+ }
});
+ module.exports = Views;
---------- diff:
common/daterange-picker-control.js
- AdvisorApp.module('Common.Views', function(Views, AdvisorApp) {
- /**
- * @class
- */
- Views.DateRangePicker = Marionette.ItemView.extend({
- className: 'btn btn-sm dropdown',
- template: Handlebars.templates.DateRangePickerToolbar,
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var moment = require('moment');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- defaults: {
- maxDate: moment().endOf('month').add(1, 'years'),
- dateFormat: 'MMM D, YYYY',
- direction: 'left',
- dateRanges: {
- 'All time': [
- moment({ year: 2010, month: 0, day: 1, hour: 0, minute: 0, second:0 }),
- moment().endOf('day').add(1, 'years')
- ],
- 'Yesterday': [
- moment().startOf('day').subtract(1, 'days'),
- moment().endOf('day').subtract(1, 'days')
- ],
- 'Today': [
- moment().startOf('day'),
- moment().endOf('day')
- ],
- 'Last 7 days': [
- moment().startOf('day').subtract(7, 'days'),
- moment().endOf('day')
- ],
- 'Ending today': [
- moment().startOf('day'),
- moment().endOf('day')
- ],
- 'Ending next 7 days': [
- moment().startOf('day'),
- moment().endOf('day').add(7, 'days')
- ],
- 'Month to date': [
- moment().startOf('month'),
- moment().endOf('day')
- ]
- }
- },
- modelEvents: {
- 'change': 'updateDateRange'
- },
+ /* module definition */
+ var Views = {};
- /**
- * @param {Object} options
- */
- initialize: function(options) {
- this.model = new Backbone.Model();
- options = options || {};
- if (options.start && options.end) {
- this.model.set({
- start: options.start,
- end: options.end,
- label: options.label || 'Custom range'
- });
- } else {
- this.model.set({
- start: moment({ year: 2010, month: 0, day: 1, hour: 0, minute: 0, second: 0 }),
- end: moment().endOf('day').add(1, 'years'),
- label: 'All time'
- });
- }
- this.options = _.defaults(options, this.defaults);
- },
- /**
- * @returns {Marionette.ItemView} this
- */
- onShow: function() {
- this.$el.daterangepicker(
- this.getDateRangePickerOptions(), _(this.onDateChange).bind(this)
- );
- this.updateDateRange();
- return this;
- },
- /**
- * @returns {Object}
- */
- getDateRangePickerOptions: function() {
- return {
- applyClass: 'btn-sm btn-primary',
- buttonClasses: ['btn btn-default'],
- cancelClass: 'btn-sm',
- endDate: this.model.get('end'),
- format: this.options.dateFormat,
- maxDate: this.options.maxDate,
- opens: this.options.direction,
- ranges: this.options.dateRanges,
- startDate: this.model.get('start'),
- timePicker: false
- };
- },
- /**
- * @param {Date} start
- * @param {Date} end
- * @param {String} label
- */
- onDateChange: function(start, end, label) {
+ Views.DateRangePicker = Marionette.ItemView.extend({
+ className: 'btn btn-sm dropdown',
+ template: Handlebars.templates.DateRangePickerToolbar,
+ defaults: {
+ maxDate: moment().endOf('month').add(1, 'years'),
+ dateFormat: 'MMM D, YYYY',
+ direction: 'left',
+ dateRanges: {
+ 'All time': [
+ moment({ year: 2010, month: 0, day: 1, hour: 0, minute: 0, second:0 }),
+ moment().endOf('day').add(1, 'years')
+ ],
+ 'Yesterday': [
+ moment().startOf('day').subtract(1, 'days'),
+ moment().endOf('day').subtract(1, 'days')
+ ],
+ 'Today': [
+ moment().startOf('day'),
+ moment().endOf('day')
+ ],
+ 'Last 7 days': [
+ moment().startOf('day').subtract(7, 'days'),
+ moment().endOf('day')
+ ],
+ 'Ending today': [
+ moment().startOf('day'),
+ moment().endOf('day')
+ ],
+ 'Ending next 7 days': [
+ moment().startOf('day'),
+ moment().endOf('day').add(7, 'days')
+ ],
+ 'Month to date': [
+ moment().startOf('month'),
+ moment().endOf('day')
+ ]
+ }
+ },
+ modelEvents: {
+ 'change': 'updateDateRange'
+ },
+ /**
+ * @param {Object} options
+ */
+ initialize: function(options) {
+ this.model = new Backbone.Model();
+ options = options || {};
+ if (options.start && options.end) {
this.model.set({
- label: label,
- start: start,
- end: end
+ start: options.start,
+ end: options.end,
+ label: options.label || 'Custom range'
});
- },
- onDestroy: function() {
- this.$el.data('daterangepicker').remove();
- },
- /**
- * Manually updates the value and triggers a change event.
- */
- updateDateRange: function() {
- var start = this.model.get('start');
- var end = this.model.get('end');
- // If dates occur on the same day, just display one date
- var sameDay = moment(start).isSame(end, 'day');
- // Check if dates occur in the same year, so we can omit start year
- var sameYear = moment(start).isSame(end, 'year');
- var startFormat = sameYear ? 'MMM D' : this.options.dateFormat;
- if (this.model.get('label') === 'All time') {
- this.$('.daterange-value').text('All time');
- } else if (sameDay) {
- this.$('.daterange-value').text(end.format(this.options.dateFormat));
- } else {
- this.$('.daterange-value').text(
- start.format(startFormat) + ' - ' +
- end.format(this.options.dateFormat)
- );
- }
+ } else {
+ this.model.set({
+ start: moment({ year: 2010, month: 0, day: 1, hour: 0, minute: 0, second: 0 }),
+ end: moment().endOf('day').add(1, 'years'),
+ label: 'All time'
+ });
}
- });
+ this.options = _.defaults(options, this.defaults);
+ },
+ /**
+ * @returns {Marionette.ItemView} this
+ */
+ onShow: function() {
+ this.$el.daterangepicker(
+ this.getDateRangePickerOptions(), _(this.onDateChange).bind(this)
+ );
+ this.updateDateRange();
+ return this;
+ },
+ /**
+ * @returns {Object}
+ */
+ getDateRangePickerOptions: function() {
+ return {
+ applyClass: 'btn-sm btn-primary',
+ buttonClasses: ['btn btn-default'],
+ cancelClass: 'btn-sm',
+ endDate: this.model.get('end'),
+ format: this.options.dateFormat,
+ maxDate: this.options.maxDate,
+ opens: this.options.direction,
+ ranges: this.options.dateRanges,
+ startDate: this.model.get('start'),
+ timePicker: false
+ };
+ },
+ /**
+ * @param {Date} start
+ * @param {Date} end
+ * @param {String} label
+ */
+ onDateChange: function(start, end, label) {
+ this.model.set({
+ label: label,
+ start: start,
+ end: end
+ });
+ },
+ onDestroy: function() {
+ this.$el.data('daterangepicker').remove();
+ },
+ /**
+ * Manually updates the value and triggers a change event.
+ */
+ updateDateRange: function() {
+ var start = this.model.get('start');
+ var end = this.model.get('end');
+ // If dates occur on the same day, just display one date
+ var sameDay = moment(start).isSame(end, 'day');
+ // Check if dates occur in the same year, so we can omit start year
+ var sameYear = moment(start).isSame(end, 'year');
+ var startFormat = sameYear ? 'MMM D' : this.options.dateFormat;
+ if (this.model.get('label') === 'All time') {
+ this.$('.daterange-value').text('All time');
+ } else if (sameDay) {
+ this.$('.daterange-value').text(end.format(this.options.dateFormat));
+ } else {
+ this.$('.daterange-value').text(
+ start.format(startFormat) + ' - ' +
+ end.format(this.options.dateFormat)
+ );
+ }
+ }
});
+ module.exports = Views;
---------- diff:
common/empty-view.js
- AdvisorApp.module('Common.Views', function(Views, AdvisorApp) {
- Views.EmptyCollection = Marionette.ItemView.extend({
- template: Handlebars.templates.EmptyCollection,
- options: {
- message: 'No results found'
- },
- templateHelpers: function() {
- return this.options;
- }
- });
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Views = {};
+ Views.EmptyCollection = Marionette.ItemView.extend({
+ template: Handlebars.templates.EmptyCollection,
+ options: {
+ message: 'No results found'
+ },
+ templateHelpers: function() {
+ return this.options;
+ }
});
+ module.exports = Views;
---------- diff:
common/entity-form-view.js
- AdvisorApp.module('Common.EntitiesForm', function(EntitiesForm, AdvisorApp) {
- /**
- * @class
- */
- EntitiesForm.View = Marionette.ItemView.extend({
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- events: {
- 'submit form': 'saveFormDataToModel'
- },
- /**
- * Noop meant to be overridden.
- */
- onSubmitSuccess: function() {
+ /* module definition */
+ var EntitiesForm = {};
- },
- /**
- * Noop meant to be overridden.
- */
- onSubmitError: function() {
- },
- /**
- * Can override if need to santize form data before setting the model
- * @param {Object} data
- * @returns {Object}
- */
- formToModel: function(data) {
- return data;
- },
- /**
- * Can override if need to santize model data before setting the form
- * @returns {Object}
- */
- modelToForm: function() {
- return this.model.toJSON();
- },
- /**
- * @returns {Object}
- */
- getFormData: function() {//not sanitized, which can be obtained from model
- return Backbone.Syphon.serialize(this) || {};
- },
- /**
- * @param {$.Event} e from the click event.
- * @returns {Boolean} false
- */
- saveFormDataToModel: function(e) {
- e.preventDefault();
- var data = this.getFormData();
- var self = this;
- this.model.set(this.formToModel(data));
- this.model.save(null, {
- success: function(model, response) {
+ EntitiesForm.View = Marionette.ItemView.extend({
+ events: {
+ 'submit form': 'saveFormDataToModel'
+ },
+ /**
+ * Noop meant to be overridden.
+ */
+ onSubmitSuccess: function() {
+ },
+ /**
+ * Noop meant to be overridden.
+ */
+ onSubmitError: function() {
+ },
+ /**
+ * Can override if need to santize form data before setting the model
+ * @param {Object} data
+ * @returns {Object}
+ */
+ formToModel: function(data) {
+ return data;
+ },
+ /**
+ * Can override if need to santize model data before setting the form
+ * @returns {Object}
+ */
+ modelToForm: function() {
+ return this.model.toJSON();
+ },
+ /**
+ * @returns {Object}
+ */
+ getFormData: function() {//not sanitized, which can be obtained from model
+ return Backbone.Syphon.serialize(this) || {};
+ },
+ /**
+ * @param {$.Event} e from the click event.
+ * @returns {Boolean} false
+ */
+ saveFormDataToModel: function(e) {
+ e.preventDefault();
+ var data = this.getFormData();
+ var self = this;
+ this.model.set(this.formToModel(data));
+ this.model.save(null, {
+ success: function(model, response) {
+ self.onSubmitSuccess(response);
+ },
+ error: function(model, response) { // could be OK status 201
+ if (response.status === 201) { // created
self.onSubmitSuccess(response);
- },
- error: function(model, response) { // could be OK status 201
- if (response.status === 201) { // created
- self.onSubmitSuccess(response);
- } else {
- self.onSubmitError(response);
- }
+ } else {
+ self.onSubmitError(response);
}
- });
- return false;
- },
+ }
+ });
+ return false;
+ },
+ onRender: function() {
+ Backbone.Syphon.deserialize(this, this.modelToForm());
+ }
+ });
- onRender: function() {
- Backbone.Syphon.deserialize(this, this.modelToForm());
- }
- });
- });
+ module.exports = EntitiesForm;
---------- diff:
common/facebook-highchart-view.js
- AdvisorApp.module('Facebook.Views', function(Views) {
- Views.HighchartView = AdvisorApp.module('Common.Views').HighchartView.extend({
- options: {
- platform: 'facebook'
- },
+ /* global dependencies */
+ var AdvisorApp = require('../app');
- getters: {
- /**
- * @param {Backbone.Model}
- * @returns {Number}
- */
- kpi: function(model) {
- var calculation;
- if (model.get('kpi_primary_events') <= 0) {
- return 0;
- }
+ /* module definition */
+ var Views = {};
- calculation = model.get('kpi_primary_spend') / model.get('kpi_primary_events');
- calculation = calculation * 100;
- calculation = Math.round(calculation);
- calculation = calculation / 100;
- return calculation;
- }
- },
+ Views.HighchartView = AdvisorApp.module('Common.Views').HighchartView.extend({
+ options: {
+ platform: 'facebook'
+ },
+ getters: {
/**
- * @returns {Object} of attributes to set on the model
+ * @param {Backbone.Model}
+ * @returns {Number}
*/
- setOnSuccess: function(model) {
- return {
- kpi: this.getters.kpi(model)
- };
+ kpi: function(model) {
+ var calculation;
+ if (model.get('kpi_primary_events') <= 0) {
+ return 0;
+ }
+ calculation = model.get('kpi_primary_spend') / model.get('kpi_primary_events');
+ calculation = calculation * 100;
+ calculation = Math.round(calculation);
+ calculation = calculation / 100;
+ return calculation;
}
- });
+ },
+ /**
+ * @returns {Object} of attributes to set on the model
+ */
+ setOnSuccess: function(model) {
+ return {
+ kpi: this.getters.kpi(model)
+ };
+ }
});
+ module.exports = Views;
---------- diff:
common/facebook-metric-dropdown-view.js
- AdvisorApp.module('Facebook.Views', function(Views) {
- this.MetricDropdown = AdvisorApp.module('Common.Views').MetricDropdown.extend({
- /** Defaults list of Facebook campaign chart filter options */
- platformMetrics: [
- { name: 'clicks', value: 'Clicks' },
- { name: 'impressions', value: 'Impressions' },
- { name: 'kpi', value: 'KPI' },
- { name: 'kpi_primary_events', value: 'Results' },
- { name: 'kpi_primary_spend', value: 'Spend' }
- ]
- });
+ /* global dependencies */
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Views = {};
+ this.MetricDropdown = AdvisorApp.module('Common.Views').MetricDropdown.extend({
+ /** Defaults list of Facebook campaign chart filter options */
+ platformMetrics: [
+ { name: 'clicks', value: 'Clicks' },
+ { name: 'impressions', value: 'Impressions' },
+ { name: 'kpi', value: 'KPI' },
+ { name: 'kpi_primary_events', value: 'Results' },
+ { name: 'kpi_primary_spend', value: 'Spend' }
+ ]
});
+ module.exports = Views;
---------- diff:
common/file-or-url.js
- AdvisorApp.module('Common', function(Module) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- Module.FileOrUrl = Marionette.ItemView.extend({
- template: Handlebars.templates.FileOrUrl,
- className: 'file-or-url',
+ /* module definition */
+ var Module = {};
- events: {
- 'click .file-selector-btn': 'selectFile'
- },
- bindings: {
- '#file-selector-url': {
- observe: 'url',
- onSet: function(url) {
- this.onFileUrlChange(url);
- return url;
- }
- },
- '#file-selector-input': {
- observe: 'file',
- onSet: function(file) {
- this.onFileChange(file);
- return file;
- }
+ Module.FileOrUrl = Marionette.ItemView.extend({
+ template: Handlebars.templates.FileOrUrl,
+ className: 'file-or-url',
+ events: {
+ 'click .file-selector-btn': 'selectFile'
+ },
+ bindings: {
+ '#file-selector-url': {
+ observe: 'url',
+ onSet: function(url) {
+ this.onFileUrlChange(url);
+ return url;
}
},
- ui: {
- 'fileSelector': '#file-selector-input',
- 'fileSelectorFacade': '.file-selector-panel input[type=text]'
- },
- initialize: function() {
- this.model = new Backbone.Model();
- },
- selectFile: function(e) {
- e.preventDefault();
- this.ui.fileSelector.trigger('click');
- },
- onChange: function() {
- this.trigger('change', this.model);
- },
- /** @param {string} url */
- onFileUrlChange: function(url) {
- if (!url) {
- return;
+ '#file-selector-input': {
+ observe: 'file',
+ onSet: function(file) {
+ this.onFileChange(file);
+ return file;
}
- this.model.unset('file');
- this.onChange();
- },
- /** @param {File} file */
- onFileChange: function(file) {
- if (!file) {
- return;
- }
- var filename = this.ui.fileSelector.val().split('\\').pop();
- this.ui.fileSelectorFacade.val(filename);
- this.model.unset('url');
- this.onChange();
- },
- /*
- * This method is implemented so this view also supports
- * CreationControls' method for getting values from subviews
- * @returns {File|string}
- */
- value: function() {
- return this.model.get('url') || this.model.get('file');
- },
- onRender: function() {
- this.stickit(this.model, this.bindings);
- },
- /** @returns {object} */
- templateHelpers: function() {
- return {
- label: this.options.label,
- placeholder: this.options.placeholder
- };
}
- });
+ },
+ ui: {
+ 'fileSelector': '#file-selector-input',
+ 'fileSelectorFacade': '.file-selector-panel input[type=text]'
+ },
+ initialize: function() {
+ this.model = new Backbone.Model();
+ },
+ selectFile: function(e) {
+ e.preventDefault();
+ this.ui.fileSelector.trigger('click');
+ },
+ onChange: function() {
+ this.trigger('change', this.model);
+ },
+ /** @param {string} url */
+ onFileUrlChange: function(url) {
+ if (!url) {
+ return;
+ }
+ this.model.unset('file');
+ this.onChange();
+ },
+ /** @param {File} file */
+ onFileChange: function(file) {
+ if (!file) {
+ return;
+ }
+ var filename = this.ui.fileSelector.val().split('\\').pop();
+ this.ui.fileSelectorFacade.val(filename);
+ this.model.unset('url');
+ this.onChange();
+ },
+ /*
+ * This method is implemented so this view also supports
+ * CreationControls' method for getting values from subviews
+ * @returns {File|string}
+ */
+ value: function() {
+ return this.model.get('url') || this.model.get('file');
+ },
+ onRender: function() {
+ this.stickit(this.model, this.bindings);
+ },
+ /** @returns {object} */
+ templateHelpers: function() {
+ return {
+ label: this.options.label,
+ placeholder: this.options.placeholder
+ };
+ }
+ });
- });
+ module.exports = Module;
---------- diff:
common/image-selector.js
- AdvisorApp.module('Common', function(Module, AdvisorApp) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- /**
- * This is useful as setting an <img> src to empty doesn't
- * actually clear the image. Now we can just set the <img> src
- * to a single pixel gif to clear.
- * @type {string}
- */
- var singlePixel = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
- Module.ImageSelector = Marionette.ItemView.extend({
- template: Handlebars.templates.ImageSelector,
+ /* module definition */
+ var Module = {};
- className: 'image-selector',
- events: {
- 'click .image-selector-upload-btn': 'selectFile',
- 'click .remove-image': 'removeImage',
- 'change @ui.imageSelector': 'onFileSelected',
- 'drop': 'onDrop',
- 'dragover': 'ignoreEvent',
- 'dragenter': 'ignoreEvent'
- },
- ui: {
- 'addImageContent': '.image-selector-add-content',
- 'imageContent': '.image-selector-image-content',
- 'imageSelector': '.image-selector-input',
- 'image': 'img'
- },
- /**
- * Simulates a click on the hidden file input
- * so we can render our own file input selector
- * @param {jQuery.Event} e
- */
- selectFile: function(e) {
- e.preventDefault();
- this.ui.imageSelector.trigger('click');
- },
- /**
- * Set the file to null, clear the <img> tag with a single pixel
- * and show the original file selector ui
- */
- removeImage: function() {
- this.file = null;
- this.hideImage();
- this.ui.image.attr('src', singlePixel);
- },
- hideImage: function() {
- this.ui.addImageContent.show();
- this.ui.imageContent.hide();
- },
- showImage: function() {
- this.ui.addImageContent.hide();
- this.ui.imageContent.show();
- },
- /**
- * Given a file, render it if it's an image in an <img> tag
- * @param {File} file
- */
- renderImageFile: function(file) {
- // Ensure the file is an image
- if (!/^image\//.test(file.type)) {
- return;
- }
- this.file = file;
- var reader = new FileReader();
- reader.onload = _(function(e) {
- this.ui.image.attr('src', e.target.result);
- this.trigger('change', file);
- }).bind(this);
- reader.readAsDataURL(file);
- this.showImage();
- },
- /**
- * When a file is selected via the <input type="file">
- * @param {jQuery.Event} e
- */
- onFileSelected: function(e) {
- var fileInput = this.ui.imageSelector.get(0);
- var file = fileInput.files[0];
- this.renderImageFile(file);
- },
- /** @param {jQuery.Event} */
- ignoreEvent: function(e) {
- e.stopPropagation();
- e.preventDefault();
- },
- /**
- * When a file is drag/dropped onto the control
- * @param {jQuery.Event} e
- */
- onDrop: function(e) {
- this.ignoreEvent(e);
- if (!e.originalEvent.dataTransfer) {
- return;
- }
- var files = e.originalEvent.dataTransfer.files;
- this.renderImageFile(files[0]);
- },
- onRender: function() {
- // This will prevent chrome (and others) from opening a new tab
- // with the drag/dropped image if you miss the drop target
- document.body.addEventListener('drop', this.ignoreEvent);
- document.body.addEventListener('dragover', this.ignoreEvent);
- },
- onBeforeDestroy: function() {
- document.body.removeEventListener('drop');
- document.body.removeEventListener('dragover');
+ var singlePixel = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
+ Module.ImageSelector = Marionette.ItemView.extend({
+ template: Handlebars.templates.ImageSelector,
+ className: 'image-selector',
+ events: {
+ 'click .image-selector-upload-btn': 'selectFile',
+ 'click .remove-image': 'removeImage',
+ 'change @ui.imageSelector': 'onFileSelected',
+ 'drop': 'onDrop',
+ 'dragover': 'ignoreEvent',
+ 'dragenter': 'ignoreEvent'
+ },
+ ui: {
+ 'addImageContent': '.image-selector-add-content',
+ 'imageContent': '.image-selector-image-content',
+ 'imageSelector': '.image-selector-input',
+ 'image': 'img'
+ },
+ /**
+ * Simulates a click on the hidden file input
+ * so we can render our own file input selector
+ * @param {jQuery.Event} e
+ */
+ selectFile: function(e) {
+ e.preventDefault();
+ this.ui.imageSelector.trigger('click');
+ },
+ /**
+ * Set the file to null, clear the <img> tag with a single pixel
+ * and show the original file selector ui
+ */
+ removeImage: function() {
+ this.file = null;
+ this.hideImage();
+ this.ui.image.attr('src', singlePixel);
+ },
+ hideImage: function() {
+ this.ui.addImageContent.show();
+ this.ui.imageContent.hide();
+ },
+ showImage: function() {
+ this.ui.addImageContent.hide();
+ this.ui.imageContent.show();
+ },
+ /**
+ * Given a file, render it if it's an image in an <img> tag
+ * @param {File} file
+ */
+ renderImageFile: function(file) {
+ // Ensure the file is an image
+ if (!/^image\//.test(file.type)) {
+ return;
}
- });
+ this.file = file;
+ var reader = new FileReader();
+ reader.onload = _(function(e) {
+ this.ui.image.attr('src', e.target.result);
+ this.trigger('change', file);
+ }).bind(this);
+ reader.readAsDataURL(file);
+ this.showImage();
+ },
+ /**
+ * When a file is selected via the <input type="file">
+ * @param {jQuery.Event} e
+ */
+ onFileSelected: function(e) {
+ var fileInput = this.ui.imageSelector.get(0);
+ var file = fileInput.files[0];
+ this.renderImageFile(file);
+ },
+ /** @param {jQuery.Event} */
+ ignoreEvent: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+ /**
+ * When a file is drag/dropped onto the control
+ * @param {jQuery.Event} e
+ */
+ onDrop: function(e) {
+ this.ignoreEvent(e);
+ if (!e.originalEvent.dataTransfer) {
+ return;
+ }
+ var files = e.originalEvent.dataTransfer.files;
+ this.renderImageFile(files[0]);
+ },
+ onRender: function() {
+ // This will prevent chrome (and others) from opening a new tab
+ // with the drag/dropped image if you miss the drop target
+ document.body.addEventListener('drop', this.ignoreEvent);
+ document.body.addEventListener('dragover', this.ignoreEvent);
+ },
+ onBeforeDestroy: function() {
+ document.body.removeEventListener('drop');
+ document.body.removeEventListener('dragover');
+ }
+ });
- });
+ module.exports = Module;
---------- diff:
common/language.js
- AdvisorApp.module('Common.Language', function(Language, AdvisorApp) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var moment = require('moment');
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Language = {};
+ var timeZoneOffsetMins = moment().utcOffset() * -1;
+ var utils = {
+ getPlatformIcon: function(platform) {
+ platform = (platform || '').toLowerCase();
+ var $icon = $('<i/>', {
+ 'class': 'fa fa-fw fa-' + platform + '-square',
+ 'title': platform
+ });
+ return $icon;
+ }
+ };
+ Language.en = {
+ objective_classic: {
+ 'APP INSTALLS': 'App Installs',
+ 'APP_INSTALLS': 'App Installs',
+ 'CLICKS': 'Clicks to Website',
+ 'ENGAGEMENT': 'Post Engagement',
+ 'FANS': 'Page Likes',
+ 'FB_PIXEL_CONVERSIONS': 'Website Conversions',
+ 'OFFSITE CLICKS': 'Clicks to Website',
+ 'OFFSITE CONVERSIONS': 'Website Conversions',
+ 'VIDEO VIEWS': 'Video Views',
+ 'VIDEO_VIEWS': 'Video Views',
+ 'LINK_CLICKS': 'Clicks to Website'
+ },
+ objective: {
+ 'MOBILE_APP_INSTALLS': 'App Installs',
+ 'MOBILE_APP_ENGAGEMENT': 'App Engagement',
+ 'WEBSITE_CLICKS': 'Clicks to Website',
+ 'EVENT_RESPONSES': 'Event Responses',
+ 'OFFER_CLAIMS': 'Offer Claims',
+ 'PAGE_LIKES': 'Page Likes',
+ 'POST_ENGAGEMENT': 'Post Engagement',
+ 'VIDEO_VIEWS': 'Video Views',
+ 'VIDEO VIEWS': 'Video Views',
+ 'FB_PIXEL_CONVERSIONS': 'Website Conversions',
+ 'WEBSITE_CONVERSIONS': 'Website Conversions',
+ 'NONE': 'None'
+ },
+ twitter_objective: {
+ 'APP_ENGAGEMENTS': 'App Engagements',
+ 'APP_INSTALLS': 'App Installs',
+ 'CUSTOM': 'Custom',
+ 'TWEET_ENGAGEMENTS': 'Engagements',
+ 'FOLLOWERS': 'Followers',
+ 'LEAD_GENERATION': 'Leads on Twitter',
+ 'VIDEO_VIEWS': 'Video Views on Twitter',
+ 'WEBSITE_CLICKS': 'Website Clicks/Conversions'
+ },
+ instagram_objective: {
+ 'VIDEO_VIEWS': 'Video Views on Instagram',
+ 'WEBSITE_CLICKS': 'Website Clicks/Conversions'
+ },
+ budget_adjustment: {
+ 'REALLOCATION': 'Reallocation',
+ 'MAKE_GOOD': 'Make Good',
+ 'VALUE_ADD_OR_COUPON': 'Value Add or Coupon',
+ 'OTHER': 'Other'
+ },
+ facebook_kpi: {
+ 'CPC': 'Per click',
+ 'CPM': 'Per impression',
+ 'CPofferclaim': 'Per offer claim',
+ 'CPlike': 'Per like',
+ 'CPeventresponse': 'Per event response',
+ 'CPE': 'Per engagement',
+ 'CPSE': 'Per storied engagement',
+ 'CPcomment': 'Per comment',
+ 'CPshare': 'Per share',
+ 'CPvideoview': 'Per video view',
+ 'CPvideoplay': 'Per video play',
+ 'CPlinkclick': 'Per link click',
+ 'CPconversion': 'Per conversion',
+ 'ROAS': 'Return on spend',
+ 'CPLC': 'Per link click',
+ 'CPI': 'Per install'
+ },
+ twitter_kpi: {
+ 'CPC': 'Per click',
+ 'CPM': 'Per 1000 impressions',
+ 'CPE': 'Per engagement',
+ 'CPI': 'Per install',
+ 'CPF': 'Per follower',
+ 'CPRT': 'Per retweet',
+ 'CPFV': 'Per favorite',
+ 'CPR': 'Per reply',
+ 'CPCE': 'Per card engagement',
+ 'CPLC': 'Per link click',
+ 'CPofferclaim': 'Per offer claim',
+ 'CPlike': 'Per like',
+ 'CPeventresponse': 'Per event response',
+ 'CPVV': 'Per video view',
+ 'CPA': 'Per action',
+ 'CPAE': 'Per app engagement',
+ 'CPL': 'Per lead'
+ },
+ instagram_kpi: {
+ 'CPC': 'Per click',
+ 'CPM': 'Per impression',
+ 'CPvideoview': 'Per video view',
+ 'CPvideoplay': 'Per video play'
+ },
+ facebook_kpi_actions: {
+ 'CPC': { singular:'Click', plural: 'Clicks'},
+ 'CPM': { singular:'Impression', plural: 'Impressions'},
+ 'CPofferclaim': { singular:'Offer Claim', plural: 'Offer Claims'},
+ 'CPlike': { singular:'Like', plural: 'Likes'},
+ 'CPeventresponse': { singular:'Event Response', plural: 'Event Responses'},
+ 'CPE': { singular:'Engagement', plural: 'Engagements'},
+ 'CPSE': { singular:'Storied Engagement', plural: 'Storied Engagements'},
+ 'CPcomment': { singular:'Comment', plural: 'Comments'},
+ 'CPshare': { singular:'Share', plural: 'Shares'},
+ 'CPvideoview': { singular:'Video View', plural: 'Video Views'},
+ 'CPvideoplay': { singular:'Video Play', plural: 'Video Plays'},
+ 'CPlinkclick': { singular:'Link Click', plural: 'Link Clicks'},
+ 'CPconversion': { singular:'Conversion', plural: 'Conversions'},
+ 'ROAS': { singular:'Return on Ad Spend', plural: 'Return on Ad Spend'},
+ 'CPLC': { singular:'Link Click', plural: 'Link Clicks'},
+ 'CPI': { singular:'Install', plural: 'Installs'}
+ },
+ twitter_kpi_actions: {
+ 'CPC': { singular:'Click', plural: 'Clicks'},
+ 'CPM': { singular:'Impression', plural: 'Impressions'},
+ 'CPE': { singular:'Engagement', plural: 'Engagements'},
+ 'CPI': { singular:'Install', plural: 'Installs'},
+ 'CPF': { singular:'Follower', plural: 'Followers'},
+ 'CPretweet': { singular:'Retweet', plural: 'Retweets'},
+ 'CPfavorite': { singular:'Favorite', plural: 'Favorites'},
+ 'CPreply': { singular:'Reply', plural: 'Replies'},
+ 'CPcardengagement': { singular:'Card Engagement', plural: 'Card Engagements'},
+ 'CPLC': { singular:'Link Click', plural: 'Link Clicks'},
+ 'CPofferclaim': { singular:'Offer Claim', plural: 'Offer Claims'},
+ 'CPlike': { singular:'Like', plural: 'Likes'},
+ 'CPeventresponse': { singular:'Event Response', plural: 'Event Responses'},
+ 'CPVV': { singular:'Video View', plural: 'Video Views'},
+ 'CPA': { singular:'Action', plural: 'Actions'}
+ },
+ kpi: {
+ 'FB_PIXEL_CONVERSIONS': 'Per Conversion',
+ 'OFFSITE CONVERSIONS': 'Per Conversion',
+ 'ENGAGEMENT': 'CPE',
+ 'FANS': 'Per Like',
+ 'CLICKS': 'Per Click',
+ 'OFFSITE CLICKS': 'Per Click',
+ 'APP_INSTALLS': 'Per Install',
+ 'APP INSTALLS': 'Per Install',
+ 'VIDEO_VIEWS': 'Per View',
+ 'VIDEO VIEWS': 'Per View'
+ },
+ results: {
+ 'FB_PIXEL_CONVERSIONS': 'Conversions',
+ 'OFFSITE CONVERSIONS': 'Conversions',
+ 'ENGAGEMENT': 'Engagements',
+ 'FANS': 'Page Likes',
+ 'CLICKS': 'Link Clicks',
+ 'OFFSITE CLICKS': 'Link Clicks',
+ 'APP_INSTALLS': 'App Installs',
+ 'APP INSTALLS': 'App Installs',
+ 'VIDEO_VIEWS': 'Video Views',
+ 'VIDEO VIEWS': 'Video Views'
+ },
+ initiativeStatus: {
+ 'ACTIVE': '<i class="fa fa-active-sc"></i>',
+ 'INACTIVE': '<i class="fa fa-inactive-sc"></i>',
+ 'COMPLETED': '<i class="fa fa-complete-sc"></i>'
+ },
+ initiativeFilterLabel: {
+ //date filters
+ 'ALL_TIME': 'All time',
+ 'ENDING_TODAY': 'Ending today',
+ 'ENDING_NEXT_7_DAYS': 'Ending next 7 days',
+ //general filters
+ 'ALL': 'All',
+ 'ACTIVE': 'Active',
+ 'INACTIVE': 'Inactive',
+ 'COMPLETE': 'Complete',
+ 'OFF_PACE': 'Off pace'
+ },
+ campaignFilters: [
+ { 'type': 'ALL', 'label': 'All' },
+ { 'type': 'OVER', 'label': 'Over pacing' },
+ { 'type': 'UNDER', 'label': 'Under pacing' },
+ { 'type': 'ON_DECK', 'label': 'On deck' },
+ { 'type': 'LIVE', 'label': 'Live' },
+ { 'type': 'PAUSED', 'label': 'Paused' },
+ { 'type': 'NO_ADS', 'label': 'Empty' },
+ { 'type': 'EXHAUSTED', 'label': 'Exhausted' },
+ { 'type': 'COMPLETED', 'label': 'Completed' },
+ { 'type': 'IN_PROGRESS', 'label': 'In progress' }
+ ],
+ creativeFilters: [
+ { 'type': 'all', 'label': 'All' },
+ { 'type': 'photo', 'label': 'Page post photo' },
+ { 'type': 'status', 'label': 'Page post text' },
+ { 'type': 'link', 'label': 'Page post link' },
+ { 'type': 'video', 'label': 'Page post video' },
+ { 'type': 'offer', 'label': 'Offer' }
+ ],
+ facebookPlacementOptions: [
+ { label: 'DESKTOP', value: 'desktop' },
+ { label: 'FEED', value: 'feed' },
+ { label: 'DESKTOP FEED', value: 'desktopfeed' },
+ { label: 'MOBILE FEED', value: 'mobilefeed' },
+ { label: 'RIGHT COLUMN', value: 'rightcolumn' },
+ { label: 'HOME', value: 'home' }
+ ],
+ twitterPlacementOptions: [
+ { label: 'TIMELINE', value: 'TWITTER_TIMELINE' },
+ { label: 'SEARCH', value: 'TWITTER_SEARCH' }
+ ],
+ instagramPlacementOptions: [
+ { label: 'INSTRAGRAM STREAM', value: 'instagramstream' }
+ ],
+ campaignStatusActions: [
+ { 'type': 'pause', 'label': 'Pause' },
+ { 'type': 'activate', 'label': 'Resume' }
+ ],
+ targetingGenderOptions: [
+ { label: 'Male', value: 'male' },
+ { label: 'Female', value: 'female' }
+ ],
+ initiativeEditMessages: (function() {
+ var warningIcon = '<i class="fa fa-exclamation-triangle"></i>';
+ return {
+ 'UNSAVED': warningIcon + ' Unsaved changes.',
+ 'SAVE_ERROR': warningIcon + ' Error. Changes were not saved.'
+ };
+ })(),
/**
- * moment().zone() deprecated for moment.utcOffset().
- * Unlike .zone(), .utcOffset() returns the real offset from UTC, not the reverse offset.
+ * @param {Number} value
+ * @returns {String}
*/
- var timeZoneOffsetMins = moment().utcOffset() * -1;
- var utils = {
- getPlatformIcon: function(platform) {
- platform = (platform || '').toLowerCase();
- var $icon = $('<i/>', {
- 'class': 'fa fa-fw fa-' + platform + '-square',
- 'title': platform
- });
- return $icon;
+ getInitiativePaceMsg: function(value) {
+ if (value === 0) {
+ return 'All campaigns on pace';
+ } else {
+ return value + ' campaign' + (value > 1 ? 's' : '') + ' off pace';
}
- };
- Language.en = {
- objective_classic: {
- 'APP INSTALLS': 'App Installs',
- 'APP_INSTALLS': 'App Installs',
- 'CLICKS': 'Clicks to Website',
- 'ENGAGEMENT': 'Post Engagement',
- 'FANS': 'Page Likes',
- 'FB_PIXEL_CONVERSIONS': 'Website Conversions',
- 'OFFSITE CLICKS': 'Clicks to Website',
- 'OFFSITE CONVERSIONS': 'Website Conversions',
- 'VIDEO VIEWS': 'Video Views',
- 'VIDEO_VIEWS': 'Video Views',
- 'LINK_CLICKS': 'Clicks to Website'
- },
- objective: {
- 'MOBILE_APP_INSTALLS': 'App Installs',
- 'MOBILE_APP_ENGAGEMENT': 'App Engagement',
- 'WEBSITE_CLICKS': 'Clicks to Website',
- 'EVENT_RESPONSES': 'Event Responses',
- 'OFFER_CLAIMS': 'Offer Claims',
- 'PAGE_LIKES': 'Page Likes',
- 'POST_ENGAGEMENT': 'Post Engagement',
- 'VIDEO_VIEWS': 'Video Views',
- 'VIDEO VIEWS': 'Video Views',
- 'FB_PIXEL_CONVERSIONS': 'Website Conversions',
- 'WEBSITE_CONVERSIONS': 'Website Conversions',
- 'NONE': 'None'
- },
- twitter_objective: {
- 'APP_ENGAGEMENTS': 'App Engagements',
- 'APP_INSTALLS': 'App Installs',
- 'CUSTOM': 'Custom',
- 'TWEET_ENGAGEMENTS': 'Engagements',
- 'FOLLOWERS': 'Followers',
- 'LEAD_GENERATION': 'Leads on Twitter',
- 'VIDEO_VIEWS': 'Video Views on Twitter',
- 'WEBSITE_CLICKS': 'Website Clicks/Conversions'
- },
- instagram_objective: {
- 'VIDEO_VIEWS': 'Video Views on Instagram',
- 'WEBSITE_CLICKS': 'Website Clicks/Conversions'
- },
- budget_adjustment: {
- 'REALLOCATION': 'Reallocation',
- 'MAKE_GOOD': 'Make Good',
- 'VALUE_ADD_OR_COUPON': 'Value Add or Coupon',
- 'OTHER': 'Other'
- },
- facebook_kpi: {
- 'CPC': 'Per click',
- 'CPM': 'Per impression',
- 'CPofferclaim': 'Per offer claim',
- 'CPlike': 'Per like',
- 'CPeventresponse': 'Per event response',
- 'CPE': 'Per engagement',
- 'CPSE': 'Per storied engagement',
- 'CPcomment': 'Per comment',
- 'CPshare': 'Per share',
- 'CPvideoview': 'Per video view',
- 'CPvideoplay': 'Per video play',
- 'CPlinkclick': 'Per link click',
- 'CPconversion': 'Per conversion',
- 'ROAS': 'Return on spend',
- 'CPLC': 'Per link click',
- 'CPI': 'Per install'
- },
- twitter_kpi: {
- 'CPC': 'Per click',
- 'CPM': 'Per 1000 impressions',
- 'CPE': 'Per engagement',
- 'CPI': 'Per install',
- 'CPF': 'Per follower',
- 'CPRT': 'Per retweet',
- 'CPFV': 'Per favorite',
- 'CPR': 'Per reply',
- 'CPCE': 'Per card engagement',
- 'CPLC': 'Per link click',
- 'CPofferclaim': 'Per offer claim',
- 'CPlike': 'Per like',
- 'CPeventresponse': 'Per event response',
- 'CPVV': 'Per video view',
- 'CPA': 'Per action',
- 'CPAE': 'Per app engagement',
- 'CPL': 'Per lead'
- },
- instagram_kpi: {
- 'CPC': 'Per click',
- 'CPM': 'Per impression',
- 'CPvideoview': 'Per video view',
- 'CPvideoplay': 'Per video play'
- },
- facebook_kpi_actions: {
- 'CPC': { singular:'Click', plural: 'Clicks'},
- 'CPM': { singular:'Impression', plural: 'Impressions'},
- 'CPofferclaim': { singular:'Offer Claim', plural: 'Offer Claims'},
- 'CPlike': { singular:'Like', plural: 'Likes'},
- 'CPeventresponse': { singular:'Event Response', plural: 'Event Responses'},
- 'CPE': { singular:'Engagement', plural: 'Engagements'},
- 'CPSE': { singular:'Storied Engagement', plural: 'Storied Engagements'},
- 'CPcomment': { singular:'Comment', plural: 'Comments'},
- 'CPshare': { singular:'Share', plural: 'Shares'},
- 'CPvideoview': { singular:'Video View', plural: 'Video Views'},
- 'CPvideoplay': { singular:'Video Play', plural: 'Video Plays'},
- 'CPlinkclick': { singular:'Link Click', plural: 'Link Clicks'},
- 'CPconversion': { singular:'Conversion', plural: 'Conversions'},
- 'ROAS': { singular:'Return on Ad Spend', plural: 'Return on Ad Spend'},
- 'CPLC': { singular:'Link Click', plural: 'Link Clicks'},
- 'CPI': { singular:'Install', plural: 'Installs'}
- },
- twitter_kpi_actions: {
- 'CPC': { singular:'Click', plural: 'Clicks'},
- 'CPM': { singular:'Impression', plural: 'Impressions'},
- 'CPE': { singular:'Engagement', plural: 'Engagements'},
- 'CPI': { singular:'Install', plural: 'Installs'},
- 'CPF': { singular:'Follower', plural: 'Followers'},
- 'CPretweet': { singular:'Retweet', plural: 'Retweets'},
- 'CPfavorite': { singular:'Favorite', plural: 'Favorites'},
- 'CPreply': { singular:'Reply', plural: 'Replies'},
- 'CPcardengagement': { singular:'Card Engagement', plural: 'Card Engagements'},
- 'CPLC': { singular:'Link Click', plural: 'Link Clicks'},
- 'CPofferclaim': { singular:'Offer Claim', plural: 'Offer Claims'},
- 'CPlike': { singular:'Like', plural: 'Likes'},
- 'CPeventresponse': { singular:'Event Response', plural: 'Event Responses'},
- 'CPVV': { singular:'Video View', plural: 'Video Views'},
- 'CPA': { singular:'Action', plural: 'Actions'}
- },
- kpi: {
- 'FB_PIXEL_CONVERSIONS': 'Per Conversion',
- 'OFFSITE CONVERSIONS': 'Per Conversion',
- 'ENGAGEMENT': 'CPE',
- 'FANS': 'Per Like',
- 'CLICKS': 'Per Click',
- 'OFFSITE CLICKS': 'Per Click',
- 'APP_INSTALLS': 'Per Install',
- 'APP INSTALLS': 'Per Install',
- 'VIDEO_VIEWS': 'Per View',
- 'VIDEO VIEWS': 'Per View'
- },
- results: {
- 'FB_PIXEL_CONVERSIONS': 'Conversions',
- 'OFFSITE CONVERSIONS': 'Conversions',
- 'ENGAGEMENT': 'Engagements',
- 'FANS': 'Page Likes',
- 'CLICKS': 'Link Clicks',
- 'OFFSITE CLICKS': 'Link Clicks',
- 'APP_INSTALLS': 'App Installs',
- 'APP INSTALLS': 'App Installs',
- 'VIDEO_VIEWS': 'Video Views',
- 'VIDEO VIEWS': 'Video Views'
- },
- initiativeStatus: {
- 'ACTIVE': '<i class="fa fa-active-sc"></i>',
- 'INACTIVE': '<i class="fa fa-inactive-sc"></i>',
- 'COMPLETED': '<i class="fa fa-complete-sc"></i>'
- },
- initiativeFilterLabel: {
- //date filters
- 'ALL_TIME': 'All time',
- 'ENDING_TODAY': 'Ending today',
- 'ENDING_NEXT_7_DAYS': 'Ending next 7 days',
- //general filters
- 'ALL': 'All',
- 'ACTIVE': 'Active',
- 'INACTIVE': 'Inactive',
- 'COMPLETE': 'Complete',
- 'OFF_PACE': 'Off pace'
- },
- campaignFilters: [
- { 'type': 'ALL', 'label': 'All' },
- { 'type': 'OVER', 'label': 'Over pacing' },
- { 'type': 'UNDER', 'label': 'Under pacing' },
- { 'type': 'ON_DECK', 'label': 'On deck' },
- { 'type': 'LIVE', 'label': 'Live' },
- { 'type': 'PAUSED', 'label': 'Paused' },
- { 'type': 'NO_ADS', 'label': 'Empty' },
- { 'type': 'EXHAUSTED', 'label': 'Exhausted' },
- { 'type': 'COMPLETED', 'label': 'Completed' },
- { 'type': 'IN_PROGRESS', 'label': 'In progress' }
- ],
- creativeFilters: [
- { 'type': 'all', 'label': 'All' },
- { 'type': 'photo', 'label': 'Page post photo' },
- { 'type': 'status', 'label': 'Page post text' },
- { 'type': 'link', 'label': 'Page post link' },
- { 'type': 'video', 'label': 'Page post video' },
- { 'type': 'offer', 'label': 'Offer' }
- ],
- facebookPlacementOptions: [
- { label: 'DESKTOP', value: 'desktop' },
- { label: 'FEED', value: 'feed' },
- { label: 'DESKTOP FEED', value: 'desktopfeed' },
- { label: 'MOBILE FEED', value: 'mobilefeed' },
- { label: 'RIGHT COLUMN', value: 'rightcolumn' },
- { label: 'HOME', value: 'home' }
- ],
- twitterPlacementOptions: [
- { label: 'TIMELINE', value: 'TWITTER_TIMELINE' },
- { label: 'SEARCH', value: 'TWITTER_SEARCH' }
- ],
- instagramPlacementOptions: [
- { label: 'INSTRAGRAM STREAM', value: 'instagramstream' }
- ],
- campaignStatusActions: [
- { 'type': 'pause', 'label': 'Pause' },
- { 'type': 'activate', 'label': 'Resume' }
- ],
- targetingGenderOptions: [
- { label: 'Male', value: 'male' },
- { label: 'Female', value: 'female' }
- ],
- initiativeEditMessages: (function() {
- var warningIcon = '<i class="fa fa-exclamation-triangle"></i>';
- return {
- 'UNSAVED': warningIcon + ' Unsaved changes.',
- 'SAVE_ERROR': warningIcon + ' Error. Changes were not saved.'
- };
- })(),
- /**
- * @param {Number} value
- * @returns {String}
- */
- getInitiativePaceMsg: function(value) {
- if (value === 0) {
- return 'All campaigns on pace';
- } else {
- return value + ' campaign' + (value > 1 ? 's' : '') + ' off pace';
- }
- },
- /**
- * @param {Number} value
- * @returns {String}
- */
- getInitiativeCampaignToggleMsg: function(value) {
- if (value === 0) {
- return '<button type="button" class="btn btn-link" disabled="disabled" >No campaigns</button>';
- } else {
- return '<button type="button" class="btn btn-link secondary-sc view-campaigns-sc campaign-toggle-sc collapsed">' +
- '<span class="view-campaigns-indicator-sc"><i class="fa fa-plus"></i> View </span>' +
- '<span class="hide-campaigns-indicator-sc"><i class="fa fa-times"></i> Hide </span>' +
- value + ' campaign' + (value > 1 ? 's' : '') +
- '</button>';
- }
- },
- /**
- * @param {Number} value
- * @returns {String}
- */
- getInsertionOrderOpportunitiesToggleMsg: function(value) {
- if (value === 0) {
- return '<button type="button" class="btn btn-link" disabled="disabled" >No opportunities</button>';
- } else {
- return '<button type="button" class="btn btn-link secondary-sc view-opportunities-sc opportunity-toggle-sc collapsed">' +
- '<span class="view-opportunities-indicator-sc"><i class="fa fa-plus"></i> View </span>' +
- '<span class="hide-opportunities-indicator-sc"><i class="fa fa-times"></i> Hide </span>' +
- value + ' opportunit' + (value > 1 ? 'ies' : 'y') +
- '</button>';
- }
- },
- /**
- * @param {String} UTCDate
- * @param {String} format DateTime format you wish to convert UTCDate into.
- * @returns {String} Passed in UTCDate converted to format.
- */
- getLocalDateFromUTC: function(UTCDate, format) {
- return moment(UTCDate)
- .subtract('minutes', timeZoneOffsetMins)
- .format(format || 'M/D/YY, h:mm a');
- },
- /**
- * @param {Object} options - Required properties: platform, kpi, value (count)
- * @returns {string} The label that follows kpi count for the platform.
- */
- getKPICountLabel: function(options) {
- var actionMap;
- var actionValues;
- var valueType;
- var value;
- options = options || {};
- switch (options.platform) {
- case 'FACEBOOK':
- actionMap = this.facebook_kpi_actions;
- break;
- case 'TWITTER':
- actionMap = this.twitter_kpi_actions;
- break;
- }
- if (actionMap) {
- actionValues = actionMap[options.kpi];
- }
- if (actionValues) {
- valueType = (options.value === 1) ? 'singular' : 'plural';
- value = actionValues[valueType];
- }
- return value || '';
- },
- getAccountNameHTML: function(account) {
- var $icon = utils.getPlatformIcon(account.platform);
- var noNameLabel = 'No name (' + account.account_id + ')';
- var $name = $('<span/>', {
- 'text': account.display_name || noNameLabel
- });
- return $name.prepend($icon);
- },
- getAssetNameHTML: function(asset) {
- var $img = $('<img/>', {
- 'src': asset.picture
- });
- var $name = $('<span/>', {
- 'class': 'sc-asset',
- 'text': asset.name
- });
- return $name.prepend($img);
- },
- getUserNameHTML: function(user) {
- var $avatar;
- var $name;
- var displayName = user.first_name + ' ' + user.last_name;
- if (!displayName.trim()) {
- displayName = user.email;
- }
- $avatar = $('<img/>', {
- src: user.image_url
- });
- $name = $('<span/>', {
- 'class': 'sc-user',
- 'html': displayName
- });
- return $name.prepend($avatar);
- },
- getPixelNameHTML: function(pixel) {
- var $icon = utils.getPlatformIcon(pixel.platform);
- var $name = $('<span/>', {
- 'text': pixel.name
- });
- return $name.prepend($icon);
- },
- getPlacementOptions: function(platform) {
- switch (platform) {
- case 'FACEBOOK':
- return this.facebookPlacementOptions;
- case 'TWITTER':
- return this.twitterPlacementOptions;
- case 'INSTAGRAM':
- return this.instagramPlacementOptions;
- }
+ },
+ /**
+ * @param {Number} value
+ * @returns {String}
+ */
+ getInitiativeCampaignToggleMsg: function(value) {
+ if (value === 0) {
+ return '<button type="button" class="btn btn-link" disabled="disabled" >No campaigns</button>';
+ } else {
+ return '<button type="button" class="btn btn-link secondary-sc view-campaigns-sc campaign-toggle-sc collapsed">' +
+ '<span class="view-campaigns-indicator-sc"><i class="fa fa-plus"></i> View </span>' +
+ '<span class="hide-campaigns-indicator-sc"><i class="fa fa-times"></i> Hide </span>' +
+ value + ' campaign' + (value > 1 ? 's' : '') +
+ '</button>';
}
- };
+ },
+ /**
+ * @param {Number} value
+ * @returns {String}
+ */
+ getInsertionOrderOpportunitiesToggleMsg: function(value) {
+ if (value === 0) {
+ return '<button type="button" class="btn btn-link" disabled="disabled" >No opportunities</button>';
+ } else {
+ return '<button type="button" class="btn btn-link secondary-sc view-opportunities-sc opportunity-toggle-sc collapsed">' +
+ '<span class="view-opportunities-indicator-sc"><i class="fa fa-plus"></i> View </span>' +
+ '<span class="hide-opportunities-indicator-sc"><i class="fa fa-times"></i> Hide </span>' +
+ value + ' opportunit' + (value > 1 ? 'ies' : 'y') +
+ '</button>';
+ }
+ },
+ /**
+ * @param {String} UTCDate
+ * @param {String} format DateTime format you wish to convert UTCDate into.
+ * @returns {String} Passed in UTCDate converted to format.
+ */
+ getLocalDateFromUTC: function(UTCDate, format) {
+ return moment(UTCDate)
+ .subtract('minutes', timeZoneOffsetMins)
+ .format(format || 'M/D/YY, h:mm a');
+ },
+ /**
+ * @param {Object} options - Required properties: platform, kpi, value (count)
+ * @returns {string} The label that follows kpi count for the platform.
+ */
+ getKPICountLabel: function(options) {
+ var actionMap;
+ var actionValues;
+ var valueType;
+ var value;
+ options = options || {};
+ switch (options.platform) {
+ case 'FACEBOOK':
+ actionMap = this.facebook_kpi_actions;
+ break;
+ case 'TWITTER':
+ actionMap = this.twitter_kpi_actions;
+ break;
+ }
+ if (actionMap) {
+ actionValues = actionMap[options.kpi];
+ }
+ if (actionValues) {
+ valueType = (options.value === 1) ? 'singular' : 'plural';
+ value = actionValues[valueType];
+ }
+ return value || '';
+ },
+ getAccountNameHTML: function(account) {
+ var $icon = utils.getPlatformIcon(account.platform);
+ var noNameLabel = 'No name (' + account.account_id + ')';
+ var $name = $('<span/>', {
+ 'text': account.display_name || noNameLabel
+ });
+ return $name.prepend($icon);
+ },
+ getAssetNameHTML: function(asset) {
+ var $img = $('<img/>', {
+ 'src': asset.picture
+ });
+ var $name = $('<span/>', {
+ 'class': 'sc-asset',
+ 'text': asset.name
+ });
+ return $name.prepend($img);
+ },
+ getUserNameHTML: function(user) {
+ var $avatar;
+ var $name;
+ var displayName = user.first_name + ' ' + user.last_name;
+ if (!displayName.trim()) {
+ displayName = user.email;
+ }
+ $avatar = $('<img/>', {
+ src: user.image_url
+ });
+ $name = $('<span/>', {
+ 'class': 'sc-user',
+ 'html': displayName
+ });
+ return $name.prepend($avatar);
+ },
+ getPixelNameHTML: function(pixel) {
+ var $icon = utils.getPlatformIcon(pixel.platform);
+ var $name = $('<span/>', {
+ 'text': pixel.name
+ });
+ return $name.prepend($icon);
+ },
+ getPlacementOptions: function(platform) {
+ switch (platform) {
+ case 'FACEBOOK':
+ return this.facebookPlacementOptions;
+ case 'TWITTER':
+ return this.twitterPlacementOptions;
+ case 'INSTAGRAM':
+ return this.instagramPlacementOptions;
+ }
+ }
+ };
- }, moment);
+ module.exports = Language;
---------- diff:
common/loading-indicator-view.js
- AdvisorApp.module('Common.Views', function(Views, AdvisorApp) {
- /**
- * @class
- */
- Views.LoadingIndicator = Marionette.ItemView.extend({
- className: 'btn btn-loading-indicator',
- events: {
- 'click': 'triggerFetch'
- },
- modelEvents: {
- 'request': 'fetching',
- 'sync reset': 'fetched',
- 'error': 'error'
- },
- template: _.template('<i class="fa fa-fw fa-refresh text-muted"></i>'),
- triggerFetch: function() {
- this.model.fetch();
- },
- fetching: function() {
- var icon = 'fa fa-fw fa-spin fa-refresh text-muted';
- $('i', this.el).removeClass().addClass(icon);
- },
- fetched: function() {
- var icon = 'fa fa-fw fa-check-circle-o text-muted';
- $('i', this.el).removeClass().addClass(icon);
- },
- error: function(model, response) {
- var icon = 'fa fa-fw fa-exclamation-circle text-danger';
- $('i', this.el).removeClass().addClass(icon);
- }
- });
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Views = {};
+ Views.LoadingIndicator = Marionette.ItemView.extend({
+ className: 'btn btn-loading-indicator',
+ events: {
+ 'click': 'triggerFetch'
+ },
+ modelEvents: {
+ 'request': 'fetching',
+ 'sync reset': 'fetched',
+ 'error': 'error'
+ },
+ template: _.template('<i class="fa fa-fw fa-refresh text-muted"></i>'),
+ triggerFetch: function() {
+ this.model.fetch();
+ },
+ fetching: function() {
+ var icon = 'fa fa-fw fa-spin fa-refresh text-muted';
+ $('i', this.el).removeClass().addClass(icon);
+ },
+ fetched: function() {
+ var icon = 'fa fa-fw fa-check-circle-o text-muted';
+ $('i', this.el).removeClass().addClass(icon);
+ },
+ error: function(model, response) {
+ var icon = 'fa fa-fw fa-exclamation-circle text-danger';
+ $('i', this.el).removeClass().addClass(icon);
+ }
});
+ module.exports = Views;
---------- diff:
common/loading-view.js
- AdvisorApp.module('Common.Views', function(Views, AdvisorApp) {
- Views.Loading = Marionette.ItemView.extend({
- className: 'sc-loading-view text-center',
- template: Handlebars.templates.LoadingView,
- templateHelpers: function() {
- return this.options;
- },
- behaviors: {
- FadeIn: {}
- }
- });
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Views = {};
+ Views.Loading = Marionette.ItemView.extend({
+ className: 'sc-loading-view text-center',
+ template: Handlebars.templates.LoadingView,
+ templateHelpers: function() {
+ return this.options;
+ },
+ behaviors: {
+ FadeIn: {}
+ }
});
+ module.exports = Views;
---------- diff:
common/modal.js
- AdvisorApp.module('Common', function(Modal, App) {
- /**
- * Usage:
- * To display yourView using this App Modal:
- * App.modalRegion.show(yourView);
- *
- * To close the modal, either click on 'x' of the modal,
- * Or trigger the 'modal:close' method:
- * yourView.triggerMethod('modal:close');
- */
+ /* global dependencies */
+ var $ = require('jquery');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
- /**
- * View to be used in ModalRegion
- */
- var ModalView = Marionette.LayoutView.extend({
- template: Handlebars.templates.modalTemplate,
- regions: {
- 'bodyRegion': '#sc-modal-body'
- },
- ui: {
- 'title': '#sc-modal-title',
- 'modalWindow': '#sc-modal'
- },
- $modalWindow: null,
- events: {
- 'hidden.bs.modal': 'destroy'
- },
- childEvents: {
- 'modal:close': 'close'
- },
- close: function() {
- this.$modalWindow.modal('hide');
- },
- onRender: function() {
- var view = this.getOption('view');
- if (!view) {
- return this;
- }
- this.ui.title.text(view.getOption('title'));
- var classList = view.getOption('classList');
- if (classList) {
- this.ui.modalWindow.find('.modal-dialog').addClass(classList);
- }
- this.bodyRegion.show(view);
- this.$modalWindow = this.ui.modalWindow.modal('show');
+ /* module definition */
+ var Modal = {};
+ var ModalView = Marionette.LayoutView.extend({
+ template: Handlebars.templates.modalTemplate,
+ regions: {
+ 'bodyRegion': '#sc-modal-body'
+ },
+ ui: {
+ 'title': '#sc-modal-title',
+ 'modalWindow': '#sc-modal'
+ },
+ $modalWindow: null,
+ events: {
+ 'hidden.bs.modal': 'destroy'
+ },
+ childEvents: {
+ 'modal:close': 'close'
+ },
+ close: function() {
+ this.$modalWindow.modal('hide');
+ },
+ onRender: function() {
+ var view = this.getOption('view');
+ if (!view) {
return this;
- },
- templateHelpers: function() {
- return this.getOption('view').modalConfigurations || {};
}
- });
- /**
- * ModalRegion used to display a modal
- */
- var ModalRegion = Marionette.Region.extend({
- el: '#modal-region',
- attachHtml: function(view) {
- var modalView = new ModalView({
- view: view
- });
- this.$el.append(modalView.render().$el);
- },
- onBeforeSwapOut: function(view) {
- // Close out existing modals
- view.triggerMethod('modal:close');
+ this.ui.title.text(view.getOption('title'));
+ var classList = view.getOption('classList');
+ if (classList) {
+ this.ui.modalWindow.find('.modal-dialog').addClass(classList);
}
- });
+ this.bodyRegion.show(view);
+ this.$modalWindow = this.ui.modalWindow.modal('show');
+ return this;
+ },
+ templateHelpers: function() {
+ return this.getOption('view').modalConfigurations || {};
+ }
+ });
+ var ModalRegion = Marionette.Region.extend({
+ el: '#modal-region',
+ attachHtml: function(view) {
+ var modalView = new ModalView({
+ view: view
+ });
+ this.$el.append(modalView.render().$el);
+ },
+ onBeforeSwapOut: function(view) {
+ // Close out existing modals
+ view.triggerMethod('modal:close');
+ }
+ });
+ App.on('before:start', function() {
+ $('<div id="modal-region"/>').appendTo('body');
+ App.modalRegion = new ModalRegion();
+ });
- /**
- * App.modalRegion is added on App start
- */
- App.on('before:start', function() {
- $('<div id="modal-region"/>').appendTo('body');
- App.modalRegion = new ModalRegion();
- });
- });
+ module.exports = Modal;
---------- diff:
common/pacing-view.js
- AdvisorApp.module('Common.Views', function(Views) {
- /**
- * @class
- */
- Views.Pacing = Backbone.View.extend({
- template: Handlebars.templates.Pacing,
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- className: 'sc-pacing-cell',
- /**
- * @returns {Backbone.View} This view.
- */
- render: function() {
- this.$el.html(this.template(this.getTemplateOptions()));
+ /* module definition */
+ var Views = {};
- return this;
- },
+ Views.Pacing = Backbone.View.extend({
+ template: Handlebars.templates.Pacing,
+ className: 'sc-pacing-cell',
+ /**
+ * @returns {Backbone.View} This view.
+ */
+ render: function() {
+ this.$el.html(this.template(this.getTemplateOptions()));
+ return this;
+ },
+ getTemplateOptions: function() {
+ return Helpers.getPacingOptions(this.model);
+ }
+ });
- getTemplateOptions: function() {
- return Helpers.getPacingOptions(this.model);
- }
- });
- });
+ module.exports = Views;
---------- diff:
common/permission-denied-view.js
- AdvisorApp.module('Common.Views', function() {
- this.PermissionDenied = Marionette.ItemView.extend({
- template: Handlebars.templates.PermissionDenied
- });
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Permission-denied-view = {};
+ this.PermissionDenied = Marionette.ItemView.extend({
+ template: Handlebars.templates.PermissionDenied
});
+ module.exports = Permission-denied-view;
---------- diff:
common/twitter-highchart-view.js
- AdvisorApp.module('Twitter.Views', function(Views) {
- Views.HighchartView = AdvisorApp.module('Common.Views').HighchartView.extend({
- options: {
- platform: 'twitter'
- },
+ /* global dependencies */
+ var AdvisorApp = require('../app');
- getters: {
- /**
- * @param {Backbone.Model}
- * @returns {Number}
- */
- twitterSpend: function(model) {
- return (model.get('billed_charge_local_micro') / 1000000.0);
- },
- /**
- * @param {Backbone.Model}
- * @returns {Number}
- */
- twitterEngagements: function(model) {
- return model.get('kpi_primary_events');
- },
+ /* module definition */
+ var Views = {};
- /**
- * @param {Backbone.Model}
- * @returns {Number}
- */
- twitterEngagementRate: function(model) {
- var impressions = model.get('impressions');
- if (impressions > 0) {
- return model.get('kpi_primary_events') / impressions;
- }
- return 0;
- }
- },
+ Views.HighchartView = AdvisorApp.module('Common.Views').HighchartView.extend({
+ options: {
+ platform: 'twitter'
+ },
+ getters: {
/**
- * @returns {Object} of attributes to set on the model
+ * @param {Backbone.Model}
+ * @returns {Number}
*/
- setOnSuccess: function(model) {
- return {
- engagement_rate: this.getters.twitterEngagementRate(model),
- engagements: this.getters.twitterEngagements(model),
- spend: this.getters.twitterSpend(model)
- };
+ twitterSpend: function(model) {
+ return (model.get('billed_charge_local_micro') / 1000000.0);
+ },
+ /**
+ * @param {Backbone.Model}
+ * @returns {Number}
+ */
+ twitterEngagements: function(model) {
+ return model.get('kpi_primary_events');
+ },
+ /**
+ * @param {Backbone.Model}
+ * @returns {Number}
+ */
+ twitterEngagementRate: function(model) {
+ var impressions = model.get('impressions');
+ if (impressions > 0) {
+ return model.get('kpi_primary_events') / impressions;
+ }
+ return 0;
}
- });
+ },
+ /**
+ * @returns {Object} of attributes to set on the model
+ */
+ setOnSuccess: function(model) {
+ return {
+ engagement_rate: this.getters.twitterEngagementRate(model),
+ engagements: this.getters.twitterEngagements(model),
+ spend: this.getters.twitterSpend(model)
+ };
+ }
});
+ module.exports = Views;
---------- diff:
common/twitter-metric-dropdown-view.js
- AdvisorApp.module('Twitter.Views', function(Views) {
- this.MetricDropdown = AdvisorApp.module('Common.Views').MetricDropdown.extend({
- /** Defaults list of Twitter campaign chart filter options */
- platformMetrics: [
- { name: 'spend', value: 'Spend' },
- { name: 'impressions', value: 'Impressions' },
- { name: 'engagements', value: 'Engagements' },
- { name: 'engagement_rate', value: 'Engagement Rate' },
- { name: 'app_clicks', value: 'App Clicks' },
- { name: 'app_engagements', value: 'App Engagements' }
- ]
- });
+ /* global dependencies */
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Views = {};
+ this.MetricDropdown = AdvisorApp.module('Common.Views').MetricDropdown.extend({
+ /** Defaults list of Twitter campaign chart filter options */
+ platformMetrics: [
+ { name: 'spend', value: 'Spend' },
+ { name: 'impressions', value: 'Impressions' },
+ { name: 'engagements', value: 'Engagements' },
+ { name: 'engagement_rate', value: 'Engagement Rate' },
+ { name: 'app_clicks', value: 'App Clicks' },
+ { name: 'app_engagements', value: 'App Engagements' }
+ ]
});
+ module.exports = Views;
---------- diff:
config/backbone-tastypie.js
/**
* Backbone-tastypie.js 0.2.0
* (c) 2011 Paul Uithol
*
* Backbone-tastypie may be freely distributed under the MIT license.
* Add or override Backbone.js functionality, for compatibility with django-tastypie.
* Depends on Backbone (and thus on Underscore as well): https://github.com/documentcloud/backbone.
*/
( function( root, factory ) {
// Set up Backbone-relational for the environment. Start with AMD.
if ( typeof define === 'function' && define.amd ) {
define( [ 'exports', 'backbone', 'underscore' ], factory );
}
// Next for Node.js or CommonJS.
else if ( typeof exports !== 'undefined' ) {
factory( exports, require( 'backbone' ), require( 'underscore' ) );
}
// Finally, as a browser global. Use `root` here as it references `window`.
else {
factory( root, root.Backbone, root._ );
}
}( this, function( exports, Backbone, _ ) {
"use strict";
Backbone.Tastypie = {
apiKey: {
username: '',
key: ''
},
constructSetUrl: function( ids ) {
return 'set/' + ids.join( ';' ) + '/';
},
csrfToken: '',
defaultOptions: {},
doGetOnEmptyPostResponse: true,
doGetOnEmptyPutResponse: true,
idAttribute: 'id'
};
Backbone.Model.prototype.idAttribute = Backbone.Tastypie.idAttribute;
/**
* Override Backbone's sync function, to do a GET upon receiving a HTTP CREATED.
* This requires 2 requests to do a create, so you may want to use some other method in production.
* Modified from http://joshbohde.com/blog/backbonejs-and-django
*/
/*
Backbone.oldSync = Backbone.sync;
Backbone.sync = function( method, model, options ) {
var headers = {},
options = _.defaults( options || {}, Backbone.Tastypie.defaultOptions );
if ( Backbone.Tastypie.apiKey && Backbone.Tastypie.apiKey.username ) {
headers[ 'Authorization' ] = 'ApiKey ' + Backbone.Tastypie.apiKey.username + ':' + Backbone.Tastypie.apiKey.key;
}
if ( Backbone.Tastypie.csrfToken ) {
headers[ 'X-CSRFToken' ] = Backbone.Tastypie.csrfToken;
}
// Keep `headers` for a potential second request
headers = _.extend( headers, options.headers );
options.headers = headers;
if ( ( method === 'create' && Backbone.Tastypie.doGetOnEmptyPostResponse ) ||
( method === 'update' && Backbone.Tastypie.doGetOnEmptyPutResponse ) ) {
var dfd = new $.Deferred();
// Set up 'success' handling
var success = options.success;
dfd.done( function( resp, textStatus, xhr ) {
_.isFunction( success ) && success( resp );
});
options.success = function( resp, textStatus, xhr ) {
// If create is successful but doesn't return a response, fire an extra GET.
// Otherwise, resolve the deferred (which triggers the original 'success' callbacks).
if ( !resp && ( xhr.status === 201 || xhr.status === 202 || xhr.status === 204 ) ) { // 201 CREATED, 202 ACCEPTED or 204 NO CONTENT; response null or empty.
options = _.defaults( {
url: xhr.getResponseHeader( 'Location' ) || model.url(),
headers: headers,
success: dfd.resolve,
error: dfd.reject
},
Backbone.Tastypie.defaultOptions
);
return Backbone.ajax( options );
}
else {
return dfd.resolveWith( options.context || options, [ resp, textStatus, xhr ] );
}
};
// Set up 'error' handling
var error = options.error;
dfd.fail( function( xhr, textStatus, errorThrown ) {
_.isFunction( error ) && error( xhr.responseText );
});
options.error = function( xhr, textStatus, errorText ) {
dfd.rejectWith( options.context || options, [ xhr, textStatus, xhr.responseText ] );
};
// Create the request, and make it accessibly by assigning it to the 'request' property on the deferred
dfd.request = Backbone.oldSync( method, model, options );
return dfd;
}
return Backbone.oldSync( method, model, options );
};
*/
Backbone.Model.prototype.url = function() {
// Use the 'resource_uri' if possible
var url = this.get( 'resource_uri' );
// If there's no idAttribute, use the 'urlRoot'. Fallback to try to have the collection construct a url.
// Explicitly add the 'id' attribute if the model has one.
if ( !url ) {
url = _.result( this, 'urlRoot' ) || ( this.collection && _.result( this.collection, 'url' ) );
if ( url && this.has( 'id' ) ) {
url = addSlash( url ) + this.get( 'id' );
}
}
url = url && addSlash( url );
return url || null;
};
/**
* Return the first entry in 'data.objects' if it exists and is an array, or else just plain 'data'.
*
* @param {object} data
*/
Backbone.Model.prototype.parse = function( data ) {
return data && data.objects && ( _.isArray( data.objects ) ? data.objects[ 0 ] : data.objects ) || data;
};
/**
* Return 'data.objects' if it exists.
* If present, the 'data.meta' object is assigned to the 'collection.meta' var.
*
* @param {object} data
*/
Backbone.Collection.prototype.parse = function( data ) {
if ( data && data.meta ) {
this.meta = data.meta;
}
return data && data.objects || data;
};
/**
* Construct a url for the collection, or for a set of models in the collection if the `models` param is used.
* Will attempt to use its own `urlRoot` first; if that doesn't yield a result, attempts to use the `urlRoot`
* on models in the collection.
*
* @param {Backbone.Model[]|string[]} [models]
*/
Backbone.Collection.prototype.url = function( models ) {
var url = _.result( this, 'urlRoot' );
// If the collection doesn't specify an url, try to obtain one from a model in the collection
if ( !url ) {
var model = this.models.length && this.models[ 0 ];
url = model && _.result( model, 'urlRoot' );
}
if ( !url ) {
url = _.result( this.model.prototype, 'urlRoot' );
}
url = url && addSlash( url );
// Build a url to retrieve a set of models. This assume the last part of each model's idAttribute contains
// the model's id. Will work when idAttribute is set to 'resource_uri' (default), but for a plain 'id' as well.
if ( models && models.length ) {
var ids = _.map( models, function( model ) {
var id = model instanceof Backbone.Model ? model.url() : model,
parts = _.compact( id.split( '/' ) );
return parts[ parts.length - 1 ];
});
url += Backbone.Tastypie.constructSetUrl( ids );
}
return url || null;
};
var addSlash = function( str ) {
return str + ( ( str.length > 0 && str.charAt( str.length - 1 ) === '/' ) ? '' : '/' );
};
}));
---------- diff:
config/datagridview.js
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
(function() {
Backbone.SCDataTable = {};
Backbone.SCDataTable.Paginator = Backgrid.Extension.Paginator.extend({
/**
* Override Paginator default control lables (default ones are misaligned)
* @type {Object}
*/
controls: {
rewind: {
label: '&laquo;',
title: 'First'
},
back: {
label: '&lsaquo;',
title: 'Previous'
},
forward: {
label: '&rsaquo;',
title: 'Next'
},
fastForward: {
label: '&raquo;',
title: 'Last'
}
}
});
Backbone.SCDataTable.View = Backbone.View.extend({
defaults: {
tableClass: 'table backgrid',
showPagination: true
},
initialize: function(options) {
this.options = _.extend({}, this.defaults, options);
this.collection = this.options.collection;
this.columns = this.options.columns;
this.grid = new Backgrid.Grid({
className: this.options.tableClass,
columns: this.columns,
collection: this.collection,
emptyText: 'Loading ...'
});
if (this.options.showPagination) {
this.paginator = new Backbone.SCDataTable.Paginator({
collection: this.collection,
goBackFirstOnSort: false
});
} else {
this.collection.state.pageSize = 0;
}
this.listenTo(this.collection, 'backgrid:force-reset', this.fetchCollection);
this.listenTo(this.collection, 'reset error', this.render);
this.listenTo(this.collection, 'sync', this.fetched);
},
fetchCollection: function() {
this.collection.fetch({
reset: true,
/**
* This is a hack fix for when we get empty 304s when calling
* a django server with USE_ETAGS=True. By setting cache = false
* jquery will throw query param _=<random_number> to prevent
* caching of the request.
*
* TODO: Figure out how to gracefully handle empty 304 requests
* or turn off USE_ETAGS on servers (currently on fbas)
*/
cache: false,
data: this.options.collectionFilters
});
},
fetched: function() {
if (this.collection.length === 0) {
this.grid.body.$el.find('.empty td').text('No results');
}
},
render: function() {
this.$el.html(
$('<div/>', { 'class': 'datagrid-wrapper' }).html(this.grid.render().$el)
);
if (this.options.showPagination) {
this.$el.append(
$('<div/>', { 'class': 'panel-footer' }).html(this.paginator.render().$el)
);
}
return this;
},
remove: function() {
if (this.options.showPagination) {
this.paginator.remove();
}
this.grid.remove();
Backbone.View.prototype.remove.call(this);
}
});
Backbone.TastypiePageableCollection = Backbone.PageableCollection.extend({
state: {
firstPage: 0,
pageSize: 20
},
queryParams: {
pageSize: 'limit',
currentPage: null,
totalPages: null,
totalRecords: null,
offset: function() {
return this.state.currentPage * this.state.pageSize;
},
order_by: function() {
if (_.isNull(this.state.sortKey)) {
return null;
}
if (this.state.order < 0) {
return this.state.sortKey;
}
return '-' + this.state.sortKey;
}
},
collectionFilters: {},
resetFilters: function(filtersObj) {
// remove all past filters and reset with fitersObj's none-empty values.
this.queryParams = _(this.queryParams).omit(_.keys(this.collectionFilters));
this.collectionFilters = {};
if (!_.isEmpty(filtersObj)) {
this.collectionFilters = _(filtersObj).omit(function(value) { return !value; });
_.extend(this.queryParams, this.collectionFilters);
}
this.getFirstPage();
},
parseRecords: function(response) {
return response.objects;
},
parseState: function(response, queryParams, state, options) {
var newState = _.clone(state);
var serverState = response.meta;
var limit = serverState.limit;
if (!_.isUndefined(serverState)) {
_.chain(queryParams)
.omit('directions')
.each(function(value, key) {
var serverVal = serverState[value];
if (!_.isUndefined(serverVal) && !_.isNull(serverVal)) {
newState[key] = serverState[value];
}
});
if (limit) {
newState.currentPage = serverState.offset / limit;
newState.totalPages = Math.ceil(serverState.total_count / limit);
} else {
newState.pageSize = 1;
}
newState.totalRecords = serverState.total_count;
}
return newState;
},
/** @param {Object} options */
setCollectionFilter: function(options) {
var newFilters = _.chain(this.queryParams)
.clone()
.omit(options.clear || [])
.extend(options.value)
.value();
this.resetFilters(newFilters);
}
});
})();
---------- diff:
config/handlebars-helpers.js
+ /* global dependencies */
+ var moment = require('moment');
+ var AdvisorApp = require('../app');
+ var _ = require('underscore');
(function() {
if (!Handlebars || !_) {
return;
}
Handlebars.registerHelper('debug', function(optionalValue) {
console.log('Debug Context of this');
console.log('=============');
console.log(this);
if (optionalValue) {
console.log('Debug Context of Optional Value');
console.log('=============');
console.log(optionalValue);
}
});
Handlebars.registerHelper('ifCurrentView', function(objectView, currentView, options) {
if (objectView === currentView) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
Handlebars.registerHelper('hasFilterOptions', function(filters, options) {
if (_.isEmpty(filters)) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
Handlebars.registerHelper('localTimezone', function() {
return new Handlebars.SafeString(AdvisorApp.timezone);
});
Handlebars.registerHelper('objective', function(term, isClassic, options) {
if (!term) {
return '';
}
var result;
var objectiveMap;
var iTerm = term.toLowerCase();
if (isClassic) {
objectiveMap = AdvisorApp.module('Common.Language').en.objective_classic;
} else {
objectiveMap = AdvisorApp.module('Common.Language').en.objective;
}
// Keys case-sensitive result:
// result = objectiveMap[term];
// Keys case-insensitive result:
_.each(objectiveMap, function(val, key) {
if (key.toLowerCase() === iTerm) {
result = val;
}
});
if (!result) {
return term;
} else {
return new Handlebars.SafeString(result);
}
});
Handlebars.registerHelper('canCreateInitiatives', function(options) {
return AdvisorApp.request('session:grant:entities:can_create_initiatives') ? options.fn(this) : options.inverse(this);
});
Handlebars.registerHelper('canManageInitiative', function(brand_id, initiative_id, options) {
return AdvisorApp.request('session:grant:entities:can_manage_brand_or_initiative', brand_id, initiative_id) ? options.fn(this) : options.inverse(this);
});
Handlebars.registerHelper('toLowerCase', function(str) {
return str.toLowerCase();
});
Handlebars.registerHelper('formatDate', function(datetime, format) {
var date = moment(datetime);
format = _.isString(format) && !_.isEmpty(format) ? format : 'MMM Do YYYY';
return new Handlebars.SafeString(date.format(format));
});
Handlebars.registerHelper('initiativeDetailDate', function(datetime, options) {
var date = moment(datetime);
return new Handlebars.SafeString(date.format('M/D/YY'));
});
Handlebars.registerHelper('percentage', function(amount, options) {
if (!amount) {
return 'N/A';
}
var result = Math.round(amount) + '%';
return new Handlebars.SafeString(result);
});
Handlebars.registerHelper('formatDollar', function(val) {
var result = Math.round(val * 100) / 100;
return new Handlebars.SafeString(result);
});
Handlebars.registerHelper('add_commas', function(number) {
return numeral(number).format('0,0');
});
Handlebars.registerHelper('kpi_results', function(objective) {
return AdvisorApp.module('Common.Language').en.results[objective];
});
Handlebars.registerHelper('kpi', function(objective) {
return AdvisorApp.module('Common.Language').en.kpi[objective];
});
Handlebars.registerHelper('language', function(key, value) {
return AdvisorApp.module('Common.Language').en[key][value];
});
Handlebars.registerHelper('initiativePacing', function(value) {
return AdvisorApp.module('Common.Language').en.getInitiativePaceMsg(value);
});
Handlebars.registerHelper('initiativeCampaignToggle', function(value) {
return AdvisorApp.module('Common.Language').en.getInitiativeCampaignToggleMsg(value);
});
Handlebars.registerHelper('insertionOrderOpportunitiesToggle', function(value) {
return AdvisorApp.module('Common.Language').en.getInsertionOrderOpportunitiesToggleMsg(value);
});
Handlebars.registerHelper('accountName', function() {
return AdvisorApp.module('Common.Language').en.getAccountNameHTML(this).html();
});
Handlebars.registerHelper('assetName', function() {
return AdvisorApp.module('Common.Language').en.getAssetNameHTML(this).html();
});
Handlebars.registerHelper('percentOf', function(value, outOf) {
return accounting.formatNumber([value / outOf] * 100, 0) + '%';
});
Handlebars.registerHelper('endingIn', function(value) {
var endDate = moment(value).endOf('day');
if (!value || !endDate.isValid()) {
return {
'label': 'Ending in',
'value': 'N/A'
};
}
var diffFromNow = endDate.diff(moment(), 'hours', true);
var diffAsDuration = moment.duration(diffFromNow, 'hours').humanize();
var isPast = diffFromNow < 0;
return {
'label': isPast ? 'Ended' : 'Ending in',
'value': diffAsDuration +
(isPast ? ' ago ' : ' ') +
'<small>' + endDate.format('MM/DD/YYYY') + '</small>'
};
});
Handlebars.registerHelper('is_website_conversion', function(options) {
if (AdvisorApp.module('Common.Language').en.objective[this.objective] === 'Website Conversions') {
return options.fn(this);
} else {
return options.inverse(this);
}
});
// ex: {{formatMoney spend_lifetime symbol='€' precision='0'}}
Handlebars.registerHelper('formatMoney', function(val, options) {
var opts = _.extend({
symbol: '$',
precision: 2
}, options.hash);
return accounting.formatMoney(val, opts.symbol, opts.precision);
});
Handlebars.registerHelper('log', function(something) {
console.log(something);
});
Handlebars.registerHelper('humanize', function(string) {
return _.string.humanize(string);
});
Handlebars.registerHelper('titleize', function(string) {
string = _.string.humanize(string);
return _.string.titleize(string);
});
// Add a hint to the browser for breaking a line after an underscore
Handlebars.registerHelper('breakUnderscores', function(string) {
string = Handlebars.Utils.escapeExpression(string);
string = string.replace('_', '_&#8203;');
return new Handlebars.SafeString(string);
});
Handlebars.registerHelper('last', function(array) {
return array ? array[array.length - 1] : array;
});
Handlebars.registerHelper('first', function(array) {
return array ? array[0] : array;
});
Handlebars.registerHelper('is', function(value, key, options) {
return (value === key) ? options.fn(this) : options.inverse(this);
});
Handlebars.registerHelper('isNot', function(value, key, options) {
return (value !== key) ? options.fn(this) : options.inverse(this);
});
Handlebars.registerHelper('ifShowTargetingAgeRange', function(platform, options) {
return (platform === 'FACEBOOK') ? options.fn(this) : options.inverse(this);
});
Handlebars.registerHelper('ifShowTracking', function(platform, options) {
return (platform === 'FACEBOOK') ? options.fn(this) : options.inverse(this);
});
})();
---------- diff:
config/helpers.js
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var $ = require('jquery');
+ var AdvisorApp = require('../app');
+ var _ = require('underscore');
/**
* A place for global helper methods.
* Find yourself writing the same thing over and over across multiple files and classes? Put it here!
*/
/**
* Returns `false` if a value is undefined, null, or an empty string.
* @param value This can be anything.
* @returns boolean based on whether or not the `value` param is `undefined`, `null` or an empty string.
*/
var Helpers = Helpers || {};
// TODO: Ask Luke if we should include a `NaN` check here.
Helpers.valueIsValid = function(value) {
if (typeof value === 'undefined' || _.isNull(value) || value === '') {
return false;
}
return true;
};
/**
* Adds a class to the passed input to notify the user that the entered value is invalid.
* Disables the related form's submit button and displays the message when hovering over the input.
* @param (Object) JQuery input object
* @param (Object) JQuery submit button object
* @param (String) text to be displayed when hovering over input
*/
Helpers.invalidInput = function(input, submit, message) {
input.addClass('input-error');
input.tooltip({ 'trigger':'hover', 'title': message});
submit.attr('disabled', true);
};
/**
* Removes the error class from the passed input object and enables the related form's submit button.
* @param (Object) JQuery input object
* @param (Object) JQuery submit button object
*/
Helpers.validInput = function(input, submit) {
input.removeClass('input-error');
input.tooltip('destroy');
submit.attr('disabled', false);
};
/**
* Handles bulk actions for Campaign(s)
* @param {String} the action to be performed on the selected Campaigns
* @param {Backgrid} grid containing collection of Campaigns
* @param {Object} collection of Campaigns that have been selected to have action taken on them
*/
Helpers.performCampaignBulkActions = function(action, viewGrid, selectedCampaigns, loadingText) {
if (!_.isEmpty(selectedCampaigns)) {
AdvisorApp.modalRegion.show(new AdvisorApp.module('Common.Views').Loading({loadingText: loadingText}));
$.ajax({
type: 'POST',
contentType: 'application/json',
url: '/api/advisor/v1/campaign/' + action + '/',
data: JSON.stringify({
'id__in': _.pluck(selectedCampaigns, 'id').toString()
}),
success: function(data, status, jqXHR) {
AdvisorApp.modalRegion.currentView.triggerMethod('modal:close');
viewGrid.clearSelectedModels();
/**
* Currently, the database does not return the updated objects in bulk action
* responses, so we need to fetch the collection to display any updates. If
* that should change down the road, this fetch() can most likely be removed.
*/
viewGrid.collection.fetch();
},
error: function(data, status, jqXHR) {
AdvisorApp.modalRegion.currentView.triggerMethod('modal:close');
}
});
}
};
/**
* Our custom underscore methods!
*/
_.mixin({
updateObject: function(obj, key, value) {
obj[key] = value;
return obj;
},
capitalizeString: function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
},
/**
* @param {object} obj
* @returns {boolean}
*/
hasFile: function(obj) {
return _.some(obj, function(val) {
if (val instanceof window.File) {
return true;
}
return _.isObject(val) ? _.hasFile(val) : false;
});
},
/**
* @param {Array.<Object>} columns Array of Objects we are going to sort.
* @param {Array.<String>} order List of strings corresponding to the name key on the objects in the columns array.
* @param {String} key to sort order on.
* @returns {Array.<Object>} List of sorted columns.
*/
sortArrayOfObjects: function(arrayOfObjects, order, key) {
return _(arrayOfObjects).map(function(object, index, arrayOfObjects) {
return _(arrayOfObjects).findWhere(_.updateObject({}, key, order[index]));
});
},
/**
* Returns the current backbone history fragment without a trailing / if there is one.
* Yes, I know this could be written as a ternary but it gets kind of gnarly. Thought a simple
* if statement was more readable.
* @returns {String}
*/
getFragmentWithoutTrailingSlash: function() {
var fragment = Backbone.history.fragment;
if (fragment.charAt(fragment.length - 1) === '/') {
fragment = fragment.slice(0, -1);
}
return fragment;
},
hasTruthyProperties: function(object, keys) {
return !!object && _.every(keys, function(key) {
return object.hasOwnProperty(key) && !!object[key];
});
},
/**
* @param {Array} array
* @param {String} key
* @returns {Object}
*/
arrayToDictionary: function(array, key) {
return _.reduce(array, function(dict, item) {
if (key) {
dict[item[key]] = item;
} else {
dict[item] = true;
}
return dict;
}, {});
}
});
/**
* @param {String} status
* @param {Number} pacing
* @returns {String} Hex color value
*/
Helpers.getPacingProgressBarColor = function(status, pacing) {
// return gray for paused objects
if (status && status.toLowerCase() === 'paused') {
return '#B0B0B1';
}
// if pacing over/under then return red
if (pacing <= 0.4 || pacing >= 0.6) {
return '#E45651';
}
// if pacing is ok, return green
return '#2FA580';
};
/**
* @param {Backbone.Model} model
* @returns {Object} Options for template
*/
Helpers.getPacingOptions = function(model) {
var pacing = model.get('pacing');
// Temporary fix to handle Twitter Accounts (which does not have the new pacing logic)
if (pacing > 1) pacing = 1;
if (pacing < 0) pacing = 0;
var status = model.has('campaign_status') ? model.get('campaign_status') : model.get('status');
return {
spend: accounting.formatMoney(model.get('spend_lifetime')),
budget: accounting.formatMoney(model.get('budget')),
progressBarColor: Helpers.getPacingProgressBarColor(status, pacing),
sliderPosition: Math.round(pacing * 100) - 6,
PacingValue: pacing
};
};
/**
* Adds Underscore String methods as a global on Undersore. See: https://github.com/epeli/underscore.string
* We must use document.ready because... we do... sadly.
*/
$(document).on('ready', function() {
_.string = window.s.exports();
});
---------- diff:
config/initialize-app.js
+ /* global dependencies */
+ var AdvisorApp = require('../app');
+ var $ = require('jquery');
var initializeApp = function() {
var fetchSession = function() {
var defer = $.Deferred();
$.ajax({
url: '/api/bouncer/v1/auth/user/current/'
}).done(function(data) {
if (window.Raven) { Raven.setUserContext(data); }
defer.resolve(data);
}).fail(function() {
sessionStorage.setItem('referrer', document.location.origin + document.location.pathname);
window.location.replace('/accounts/#login');
});
return defer.promise();
};
var fetchTeams = function() {
var defer = $.Deferred();
$.ajax({
url: '/api/advisor/v1/team/'
}).done(function(data) {
defer.resolve(data.objects);
});
return defer.promise();
};
var fetchGrants = function() {
var defer = $.Deferred();
$.ajax({
url: '/api/advisor/v1/grant/?ancestry=true&limit=0'
}).done(function(data) {
defer.resolve(data.objects);
});
return defer.promise();
};
fetchSession().done(function(user) {
$.when(fetchTeams(), fetchGrants()).done(function(teams, grants) {
AdvisorApp.start({
user: user,
teams: teams,
grants: grants
});
});
});
};
---------- diff:
config/jquery-config.js
+ /* global dependencies */
+ var $ = require('jquery');
(function($) {
if (!$) {return;}
//TODO: remove this once there's a base api config for tastypie
/*
* make urls API friendly
*/
$.ajaxPrefilter(function(options) {//Before sending each ajax request, modify options, options are the ajax request options.
if (!(/([\?].*)|\/$/).test(options.url)) {
options.url = options.url + '/'; //add trailing slash if missing to avoid a redirect, no slash if query
}
});
$.fn.extend({
/**
* Creates query string from an object.
* This is needed because dossier api is very sensitive:
* requires trailing slash, no ? if no params, no & if no extra params, no empty param value etc
*/
generateQueryString: function(paramObj) {
var queryMap = {};
var queryStr = '';
if (paramObj && !$.isEmptyObject(paramObj)) {
$.each(paramObj, function(key, value) {
if (value) {
queryMap[key] = value;
}
});
queryStr = $.param(queryMap);
if (queryStr.length > 0) {
queryStr = '?' + queryStr;
}
}
return queryStr;
}
});
})($);
---------- diff:
config/moment.js
+ /* global dependencies */
+ var moment = require('moment');
(function(moment) {
var abbrs = {
EST: 'Eastern Time',
EDT: 'Eastern Time',
CST: 'Central Time',
CDT: 'Central Time',
MST: 'Mountain Time',
MDT: 'Mountain Time',
PST: 'Pacific Time',
PDT: 'Pacific Time'
};
moment.fn.zoneName = function() {
var abbr = this.zoneAbbr();
return abbrs[abbr] || abbr;
};
})(moment);
---------- diff:
config/select2-config.js
+ /* global dependencies */
+ var _ = require('underscore');
+ var $ = require('jquery');
(function($, _) {
if (!($ && $.fn.select2 && _)) {return;}
/*
Search an entity.
options properties:
entityURl, [placeholder, filter, $dependsOn, queryKey,
resetOnInit(true by default), callback(called with entity on select)]
*/
$.fn.search = function(options) {
var defaults = {
resetOnInit: true,
minimumInputLength: 1,
queryKey: 'search',
placeholder: 'search',
filter: {},
$dependsOn: null,
limit: 20
};
var self = this;
var settings = _.defaults({}, options, defaults);
var defaultNameFormatter = function(entity) {
return entity.name;
};
var nameFormatter = $.isFunction(settings.formatName) ?
settings.formatName :
defaultNameFormatter;
var selectionFormatterWithCallback = function(entity) {
settings.callback(entity);
return nameFormatter(entity);
};
var selectionFormatter = $.isFunction(settings.callback) ?
selectionFormatterWithCallback :
nameFormatter;
var $dependsOn = settings.$dependsOn;
var searchQuery = {
format: 'json',
limit: settings.limit
};
this.select2({
placeholder: settings.placeholder,
minimumInputLength: settings.minimumInputLength,
ajax: {
url: settings.entityURl,
dataType: 'json',
data: function(term, page) {
searchQuery[settings.queryKey] = term;
searchQuery.offset = page ? (page - 1) * settings.limit : 0;
return _.extend({}, settings.filter, searchQuery);
},
results: function(data, page) {
var more = (page * settings.limit) < data.meta.total_count;
return {
results: data.objects,
more: more
};
}
},
formatResult: nameFormatter,
formatSelection: selectionFormatter
});
this.on('reset', function() {
self.select2('val', '').trigger('change');
});
//If depended-on field changes, reset this field,
//and if depended-on field is empty, disable this field.
if ($dependsOn) {
$dependsOn.on('change', function() {
self.trigger('reset');
});
this.on('reset', function() {
self.select2('readonly', ($dependsOn.val() ? false : true));
});
}
if (settings.resetOnInit) {
self.trigger('reset');
}
return this;
};
/**
* Search an entity by name
*/
$.fn.searchByName = function(options) {
_.extend(options, {
queryKey: 'name__icontains',
placeholder: 'search by name'
});
return this.search(options);
};
})($, _);
---------- diff:
entities/_base.js
- AdvisorApp.module('Entities', function() {
- this.__collectionRequests = {};
+ /* global dependencies */
+ var _ = require('underscore');
+ var $ = require('jquery');
+ var AdvisorApp = require('../app');
- /**
- * Fetch model
- * @param {Backbone.Model} Model - model class
- * @param {Number} id - model id
- * @param {Boolean} cache - allow objects to be retrieved from cache
- * @return {$.Promise}
- */
- this.fetchEntity = function(Model, id, cache) {
- var defer = $.Deferred();
- var model = new Model({ id: id});
- model.fetch({
- cache: (cache ? true : false),
- success: function(model) {
- defer.resolve(model);
- },
- error: function(model, response) {
- defer.reject(response);
- }
- });
+ /* module definition */
+ var _base = {};
- return defer.promise();
- };
- /**
- * Fetch collection
- * @param {Backbone.Collection} Collection - collection class
- * @param {Object} options - Backbone.Collection fetch options (except success and error)
- * @return {$.Promise}
- */
- this.fetchEntities = function(Collection, options) {
- options = options || {};
- // Abort existing queries for this collection
- var previous = this.__collectionRequests[Collection.prototype.url];
- if (previous && previous.readyState < 4) {
- previous.abort();
+ this.__collectionRequests = {};
+ this.fetchEntity = function(Model, id, cache) {
+ var defer = $.Deferred();
+ var model = new Model({ id: id});
+ model.fetch({
+ cache: (cache ? true : false),
+ success: function(model) {
+ defer.resolve(model);
+ },
+ error: function(model, response) {
+ defer.reject(response);
}
+ });
+ return defer.promise();
+ };
+ this.fetchEntities = function(Collection, options) {
+ options = options || {};
+ // Abort existing queries for this collection
+ var previous = this.__collectionRequests[Collection.prototype.url];
+ if (previous && previous.readyState < 4) {
+ previous.abort();
+ }
+ var defer = $.Deferred();
+ var collection = new Collection();
+ var request = collection.fetch(_.extend(options, {
+ success: function(collection) {
+ defer.resolve(collection);
+ },
+ error: function(collection, response) {
+ defer.reject(response);
+ }
+ }));
+ this.__collectionRequests[Collection.prototype.url] = request;
+ return defer.promise();
+ };
- var defer = $.Deferred();
- var collection = new Collection();
- var request = collection.fetch(_.extend(options, {
- success: function(collection) {
- defer.resolve(collection);
- },
- error: function(collection, response) {
- defer.reject(response);
- }
- }));
- this.__collectionRequests[Collection.prototype.url] = request;
- return defer.promise();
- };
- });
+ module.exports = _base;
---------- diff:
entities/agencylocation.js
- AdvisorApp.module('Entities', function(Entities, App) {
- var AgencyLocation = Backbone.Model.extend({
- url: function() {
- return '/api/advisor/v1/agencylocation/';
- },
- initialize: function(attr) {
- if (attr && attr.agency) {
- this.set({ 'label': attr.agency.name + ' (' + attr.name + ')' });
- }
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
+ /* module definition */
+ var Entities = {};
+ var AgencyLocation = Backbone.Model.extend({
+ url: function() {
+ return '/api/advisor/v1/agencylocation/';
+ },
+ initialize: function(attr) {
+ if (attr && attr.agency) {
+ this.set({ 'label': attr.agency.name + ' (' + attr.name + ')' });
}
- });
- var AgencyLocationCollection = Backbone.Collection.extend({
- model: AgencyLocation,
- url: function() {
- return '/api/advisor/v1/agencylocation/' +
- $.fn.generateQueryString(this.filters);
+ }
+ });
+ var AgencyLocationCollection = Backbone.Collection.extend({
+ model: AgencyLocation,
+ url: function() {
+ return '/api/advisor/v1/agencylocation/' +
+ $.fn.generateQueryString(this.filters);
+ },
+ initialize: function(options) {
+ this.filters = options.query;
+ },
+ parse: function(data) {
+ this.meta = data.meta;
+ return data.objects;
+ }
+ });
+ var getAgencyLocationEntities = function(options) {
+ var agencyLocationCollection = new AgencyLocationCollection(options);
+ var defer = $.Deferred();
+ agencyLocationCollection.fetch({
+ success: function(collection) { // collection, response, options
+ defer.resolve(collection);
},
- initialize: function(options) {
- this.filters = options.query;
- },
- parse: function(data) {
- this.meta = data.meta;
- return data.objects;
+ error: function(collection, response) { // collection, response, options
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ App.reqres.setHandler('agencylocation:entities', function(options) {
+ return getAgencyLocationEntities(options);
+ });
- var getAgencyLocationEntities = function(options) {
- var agencyLocationCollection = new AgencyLocationCollection(options);
- var defer = $.Deferred();
- agencyLocationCollection.fetch({
- success: function(collection) { // collection, response, options
- defer.resolve(collection);
- },
- error: function(collection, response) { // collection, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- App.reqres.setHandler('agencylocation:entities', function(options) {
- return getAgencyLocationEntities(options);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/analytics.js
+ /* global dependencies */
+ var $ = require('jquery');
+ var moment = require('moment');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
// NOTE: Twitter Analytics is currently mapping to a Funding Instrument.
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var Analytics;
- var FacebookAnalytics;
- var TwitterAnalytics;
- var AnalyticsPageableCollection;
- var FacebookAnalyticsPageableCollection;
- var TwitterAnalyticsPageableCollection;
- var getFacebookAnalyticsEntity;
+ /* module definition */
+ var Entities = {};
- Analytics = Backbone.Model.extend({
- /**
- * @returns {Number|String}
- */
- setCPM: function(spend, impressions) {
- return impressions > 0 ? parseFloat(spend / (impressions / 1000)).toFixed(2) : '';
+ var Analytics;
+ var FacebookAnalytics;
+ var TwitterAnalytics;
+ var AnalyticsPageableCollection;
+ var FacebookAnalyticsPageableCollection;
+ var TwitterAnalyticsPageableCollection;
+ var getFacebookAnalyticsEntity;
+ Analytics = Backbone.Model.extend({
+ /**
+ * @returns {Number|String}
+ */
+ setCPM: function(spend, impressions) {
+ return impressions > 0 ? parseFloat(spend / (impressions / 1000)).toFixed(2) : '';
+ },
+ /**
+ * @returns {Number|String}
+ */
+ setCPC: function(spend, clicks) {
+ return clicks > 0 ? parseFloat(spend / clicks).toFixed(2) : '';
+ },
+ /**
+ * @returns {Number|String}
+ */
+ setCTR: function(clicks, impressions) {
+ return impressions > 0 ? Number(clicks / impressions) : '';
+ }
+ });
+ FacebookAnalytics = Analytics.extend({
+ url: '/api/facebook/ads/v1/dailyadstat/aggregate/'
+ });
+ TwitterAnalytics = Analytics.extend({
+ url: '/api/twitter/ads/v1/campaigndashboardanalytics/aggregate/'
+ });
+ AnalyticsPageableCollection = Backbone.TastypiePageableCollection.extend({
+ state: {
+ firstPage: 0,
+ pageSize: 0
+ },
+ /**
+ * NOTE: This method and logic assumes ONE metric per chart. Will need to update if we wish to use more than one
+ * per chart.
+ * @param {String} metric to use in chart
+ * @returns {Arra.<Array>} An array of arrays. 1st place in array in a unix timestamp, 2nd place is the metric.
+ */
+ getHighchartsSeriesData: function(metric, platform) {
+ var startDates = platform === 'facebook' ? this.pluck('date_start') : this.pluck('start');
+ /** Unix Timestamps in miliseconds. AKA Unix Timestamps * 1000 */
+ var unixTimestamps = _(startDates).map(function(dateTime) { return moment.utc(dateTime).valueOf(); });
+ return _.zip(unixTimestamps, this.pluck(metric));
+ }
+ });
+ FacebookAnalyticsPageableCollection = AnalyticsPageableCollection.extend({
+ url: FacebookAnalytics.prototype.url
+ });
+ TwitterAnalyticsPageableCollection = AnalyticsPageableCollection.extend({
+ url: TwitterAnalytics.prototype.url
+ });
+ getFacebookAnalyticsEntity = function(campaignID, startDate, endDate) {
+ var analytics = new FacebookAnalytics();
+ var defer = $.Deferred();
+ analytics.fetch({
+ // Since we're hitting facebook-ads-service, we need to
+ // turn off caching as we don't currently handle the empty
+ // 304 respones that it occasionally provides
+ cache: false,
+ data: {
+ adgroup__adcampaign__campaign__advisor_lineitem_id: campaignID,
+ date_start__gte: startDate,
+ date_start__lte: endDate
},
/**
- * @returns {Number|String}
+ * @param {Backbone.Model} model
+ * @param {Object} response
+ * @param {Object} options
*/
- setCPC: function(spend, clicks) {
- return clicks > 0 ? parseFloat(spend / clicks).toFixed(2) : '';
+ success: function(model) {
+ defer.resolve(model);
},
/**
- * @returns {Number|String}
+ * @param {Backbone.Model} model
+ * @param {Object} response
+ * @param {Object} options
*/
- setCTR: function(clicks, impressions) {
- return impressions > 0 ? Number(clicks / impressions) : '';
+ error: function(model, response) {
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ AdvisorApp.reqres.setHandler('reports:facebookanalytics:entity', function(id, query) {
+ return getFacebookAnalyticsEntity(id, query);
+ });
+ AdvisorApp.reqres.setHandler('reports:facebookanalytics:PageableEntities', function() {
+ return FacebookAnalyticsPageableCollection;
+ });
+ AdvisorApp.reqres.setHandler('reports:twitteranalytics:PageableEntities', function() {
+ return TwitterAnalyticsPageableCollection;
+ });
- FacebookAnalytics = Analytics.extend({
- url: '/api/facebook/ads/v1/dailyadstat/aggregate/'
- });
- TwitterAnalytics = Analytics.extend({
- url: '/api/twitter/ads/v1/campaigndashboardanalytics/aggregate/'
- });
- AnalyticsPageableCollection = Backbone.TastypiePageableCollection.extend({
- state: {
- firstPage: 0,
- pageSize: 0
- },
- /**
- * NOTE: This method and logic assumes ONE metric per chart. Will need to update if we wish to use more than one
- * per chart.
- * @param {String} metric to use in chart
- * @returns {Arra.<Array>} An array of arrays. 1st place in array in a unix timestamp, 2nd place is the metric.
- */
- getHighchartsSeriesData: function(metric, platform) {
- var startDates = platform === 'facebook' ? this.pluck('date_start') : this.pluck('start');
- /** Unix Timestamps in miliseconds. AKA Unix Timestamps * 1000 */
- var unixTimestamps = _(startDates).map(function(dateTime) { return moment.utc(dateTime).valueOf(); });
- return _.zip(unixTimestamps, this.pluck(metric));
- }
- });
- FacebookAnalyticsPageableCollection = AnalyticsPageableCollection.extend({
- url: FacebookAnalytics.prototype.url
- });
- TwitterAnalyticsPageableCollection = AnalyticsPageableCollection.extend({
- url: TwitterAnalytics.prototype.url
- });
- /**
- * @param {Number} campaignID ID of campaign to fetch.
- * @param {String} startDate In format YYYY-MM-DD
- * @param {String} endDate In format YYYY-MM-DD NOTE: Currently not used!
- */
- getFacebookAnalyticsEntity = function(campaignID, startDate, endDate) {
- var analytics = new FacebookAnalytics();
- var defer = $.Deferred();
- analytics.fetch({
- // Since we're hitting facebook-ads-service, we need to
- // turn off caching as we don't currently handle the empty
- // 304 respones that it occasionally provides
- cache: false,
- data: {
- adgroup__adcampaign__campaign__advisor_lineitem_id: campaignID,
- date_start__gte: startDate,
- date_start__lte: endDate
- },
- /**
- * @param {Backbone.Model} model
- * @param {Object} response
- * @param {Object} options
- */
- success: function(model) {
- defer.resolve(model);
- },
- /**
- * @param {Backbone.Model} model
- * @param {Object} response
- * @param {Object} options
- */
- error: function(model, response) {
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- /**
- * Require query for fields, since API doesn't return all
- * @param {Number} id ID of entity to fetch.
- * @param {String} query Typically a startDate in the format YYYY-MM-DD
- * @returns {Backbone.Model}
- */
- // TODO: Create request handlers for the Twitter Entities.
- AdvisorApp.reqres.setHandler('reports:facebookanalytics:entity', function(id, query) {
- return getFacebookAnalyticsEntity(id, query);
- });
- /**
- * @returns {Backbone.Collection} For Facebook Analytics
- */
- AdvisorApp.reqres.setHandler('reports:facebookanalytics:PageableEntities', function() {
- return FacebookAnalyticsPageableCollection;
- });
- /**
- * @returns {Backbone.Collection} For Twitter Analytics
- */
- AdvisorApp.reqres.setHandler('reports:twitteranalytics:PageableEntities', function() {
- return TwitterAnalyticsPageableCollection;
- });
- });
+ module.exports = Entities;
---------- diff:
entities/audience.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- var Audience = Backbone.Model.extend({
- urlRoot: '/api/audiences/v1/audience/',
- initialize: function() {
- this.on('add', function() {
- var BrandEntity = App.request('brand:Entity');
- var brandEntity = new BrandEntity({ id: this.get('brand_id') });
- brandEntity.fetch().done(function(brand) {
- this.set('brand', brand);
- this.trigger('brandUpdated');
- });
- }, this);
- }
- });
+ /* module definition */
+ var Entities = {};
- var AudienceCollection = Backbone.TastypiePageableCollection.extend({
- model: Audience,
- url: Audience.prototype.urlRoot + 'dashboard/',
- state: {
- firstPage: 0,
- pageSize: 100,
- sortKey: 'size',
- order: 1
+ var Audience = Backbone.Model.extend({
+ urlRoot: '/api/audiences/v1/audience/',
+ initialize: function() {
+ this.on('add', function() {
+ var BrandEntity = App.request('brand:Entity');
+ var brandEntity = new BrandEntity({ id: this.get('brand_id') });
+ brandEntity.fetch().done(function(brand) {
+ this.set('brand', brand);
+ this.trigger('brandUpdated');
+ });
+ }, this);
+ }
+ });
+ var AudienceCollection = Backbone.TastypiePageableCollection.extend({
+ model: Audience,
+ url: Audience.prototype.urlRoot + 'dashboard/',
+ state: {
+ firstPage: 0,
+ pageSize: 100,
+ sortKey: 'size',
+ order: 1
+ }
+ });
+ var AudienceReachEstimate = Backbone.Model.extend({
+ url: Audience.prototype.urlRoot + 'reachestimate/',
+ state: {
+ firstPage: 0,
+ pageSize: 100
+ }
+ });
+ var getAudienceEntity = function(id) {
+ var audience = new Audience({ id: id });
+ var defer = $.Deferred();
+ audience.fetch({
+ success: function(model) { //model, response, options
+ defer.resolve(model);
+ },
+ error: function(model, response) { //model, response, options
+ defer.reject(response);
}
});
- var AudienceReachEstimate = Backbone.Model.extend({
- url: Audience.prototype.urlRoot + 'reachestimate/',
- state: {
- firstPage: 0,
- pageSize: 100
+ return defer.promise();
+ };
+ var getAudienceReachEstimate = function(ids) {
+ var audience = new AudienceReachEstimate();
+ var defer = $.Deferred();
+ audience.fetch({
+ data: $.param({ elements:ids }),
+ success: function(model) { //model, response, options
+ defer.resolve(model);
+ },
+ error: function(model, response) { //model, response, options
+ defer.reject(response);
}
});
- var getAudienceEntity = function(id) {
- var audience = new Audience({ id: id });
- var defer = $.Deferred();
- audience.fetch({
- success: function(model) { //model, response, options
- defer.resolve(model);
- },
- error: function(model, response) { //model, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- var getAudienceReachEstimate = function(ids) {
- var audience = new AudienceReachEstimate();
- var defer = $.Deferred();
- audience.fetch({
- data: $.param({ elements:ids }),
- success: function(model) { //model, response, options
- defer.resolve(model);
- },
- error: function(model, response) { //model, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- var getAudienceEntities = function(options) {
- var audienceCollection = new AudienceCollection([], options);
- var defer = $.Deferred();
- audienceCollection.fetch({
- data: options.data,
- success: function(collection) { //collection, response, options
- defer.resolve(collection);
- },
- error: function(collection, response) { //collection, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- var createElement = function(name, ids) {
- var element = new Audience();
- var defer = $.Deferred();
- element.save({
- name: name,
- elements: ids,
- success: function(model, response, options) {
- defer.resolve(model);
- },
- error: function(model, response, options) {
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- App.reqres.setHandler('audience:entity', function(id) {
- if (id) {
- return getAudienceEntity(id);
+ return defer.promise();
+ };
+ var getAudienceEntities = function(options) {
+ var audienceCollection = new AudienceCollection([], options);
+ var defer = $.Deferred();
+ audienceCollection.fetch({
+ data: options.data,
+ success: function(collection) { //collection, response, options
+ defer.resolve(collection);
+ },
+ error: function(collection, response) { //collection, response, options
+ defer.reject(response);
}
});
- App.reqres.setHandler('audience:reachestimate:entity', function(ids) {
- if (ids) {
- return getAudienceReachEstimate(ids);
+ return defer.promise();
+ };
+ var createElement = function(name, ids) {
+ var element = new Audience();
+ var defer = $.Deferred();
+ element.save({
+ name: name,
+ elements: ids,
+ success: function(model, response, options) {
+ defer.resolve(model);
+ },
+ error: function(model, response, options) {
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ App.reqres.setHandler('audience:entity', function(id) {
+ if (id) {
+ return getAudienceEntity(id);
+ }
+ });
+ App.reqres.setHandler('audience:reachestimate:entity', function(ids) {
+ if (ids) {
+ return getAudienceReachEstimate(ids);
+ }
+ });
+ App.reqres.setHandler('audience:entities', function(options) {
+ return getAudienceEntities(options);
+ });
+ App.reqres.setHandler('audience:Entity', function() {
+ return Audience;
+ });
+ App.reqres.setHandler('audience:Entities', function() {
+ return AudienceCollection;
+ });
+ App.reqres.setHandler('audience:create-element:entity', function(name, ids) {
+ if (name && ids) {
+ return createElement(name, ids);
+ }
+ });
- App.reqres.setHandler('audience:entities', function(options) {
- return getAudienceEntities(options);
- });
- App.reqres.setHandler('audience:Entity', function() {
- return Audience;
- });
- App.reqres.setHandler('audience:Entities', function() {
- return AudienceCollection;
- });
- App.reqres.setHandler('audience:create-element:entity', function(name, ids) {
- if (name && ids) {
- return createElement(name, ids);
- }
- });
- });
+ module.exports = Entities;
---------- diff:
entities/brand.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- var Brand = Backbone.Model.extend({
- url: function() {
- return '/api/advisor/v1/brand/' + this.id + '/';
+ /* module definition */
+ var Entities = {};
+ var Brand = Backbone.Model.extend({
+ url: function() {
+ return '/api/advisor/v1/brand/' + this.id + '/';
+ }
+ });
+ var BrandCollection = Backbone.Collection.extend({
+ model: Brand,
+ url: function() {
+ return '/api/advisor/v1/brand/' + $.fn.generateQueryString(this.filters);
+ },
+ initialize: function(options) {
+ options = options || {};
+ this.filters = options.query;
+ },
+ parse: function(data) {
+ this.meta = data.meta;
+ return data.objects;
+ }
+ });
+ var getBrand = function(id) {
+ var brand = new Brand({ id: id });
+ var defer = $.Deferred();
+ brand.fetch({
+ cache: true,
+ success: function(model) { //model, response, options
+ defer.resolve(model);
+ },
+ error: function(model, response) { //model, response, options
+ defer.reject(response);
}
});
- var BrandCollection = Backbone.Collection.extend({
- model: Brand,
- url: function() {
- return '/api/advisor/v1/brand/' + $.fn.generateQueryString(this.filters);
+ return defer.promise();
+ };
+ var getBrandCollection = function(options) {
+ var brandCollection = new BrandCollection(options);
+ var defer = $.Deferred();
+ brandCollection.fetch({
+ success: function(collection) { // collection, response, options
+ defer.resolve(collection);
},
- initialize: function(options) {
- options = options || {};
- this.filters = options.query;
- },
- parse: function(data) {
- this.meta = data.meta;
- return data.objects;
+ error: function(collection, response) { // collection, response, options
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ App.reqres.setHandler('brand:entity', getBrand);
+ App.reqres.setHandler('brand:entities', getBrandCollection);
+ App.reqres.setHandler('brand:Entity', function() {
+ return Brand;
+ });
+ App.reqres.setHandler('brand:Entities', function() {
+ return BrandCollection;
+ });
- var getBrand = function(id) {
- var brand = new Brand({ id: id });
- var defer = $.Deferred();
- brand.fetch({
- cache: true,
- success: function(model) { //model, response, options
- defer.resolve(model);
- },
- error: function(model, response) { //model, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- var getBrandCollection = function(options) {
- var brandCollection = new BrandCollection(options);
- var defer = $.Deferred();
- brandCollection.fetch({
- success: function(collection) { // collection, response, options
- defer.resolve(collection);
- },
- error: function(collection, response) { // collection, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- App.reqres.setHandler('brand:entity', getBrand);
- App.reqres.setHandler('brand:entities', getBrandCollection);
- App.reqres.setHandler('brand:Entity', function() {
- return Brand;
- });
- App.reqres.setHandler('brand:Entities', function() {
- return BrandCollection;
- });
- });
+ module.exports = Entities;
---------- diff:
entities/creative.js
- AdvisorApp.module('Entities', function(Module) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- function ImplementInSubclass() {
- throw new Error('Not implemented');
- }
- var Creative = Backbone.Model.extend({
- constructor: function(attributes, options) {
- var campaignId = options ? options.campaignId : attributes.campaignId;
+ /* module definition */
+ var Module = {};
- // Campaign ID is required for saving creatives to advisor-service
- // hence this fail fast approach.
- // Currently, campaignId is only used in generating a url for the
- // backbone model
- if (!campaignId) {
- throw new Error('Creatives require a campaignId');
+ function ImplementInSubclass() {
+ throw new Error('Not implemented');
+ }
+ var Creative = Backbone.Model.extend({
+ constructor: function(attributes, options) {
+ var campaignId = options ? options.campaignId : attributes.campaignId;
+ // Campaign ID is required for saving creatives to advisor-service
+ // hence this fail fast approach.
+ // Currently, campaignId is only used in generating a url for the
+ // backbone model
+ if (!campaignId) {
+ throw new Error('Creatives require a campaignId');
+ }
+ Backbone.Model.call(this, attributes, options);
+ this.mappedViews = _(this.views || {}).reduce(function(views, key, field) {
+ var options = {};
+ /**
+ * Allow for custom view options. For example,
+ * instead of only doing
+ * 'views': {
+ * 'picture': 'image',
+ * 'post_body': 'body'
+ * }
+ * where the 'post_body' field is rendered as a body (textfield)
+ * view, we can now do
+ * 'views': {
+ * 'picture': 'image',
+ * 'post_body': {
+ * view: 'body',
+ * placeholder: 'This is the post body section',
+ * optional: true
+ * }
+ * }
+ * which allows us to override the default view options
+ * of a 'body' view.
+ */
+ if (_.isObject(key)) {
+ options = _.omit(key, 'view');
+ key = key.view;
}
+ views[field] = _.extend({}, this.getViewForKey(key), options);
+ return views;
+ }, {}, this);
+ },
+ /** @returns {window.FormData} */
+ formData: function() {
+ return _(this.toJSON()).reduce(function(data, val, key) {
+ data.append(key, val);
+ return data;
+ }, new window.FormData());
+ },
+ /** @returns {object} */
+ getViews: function() {
+ return this.mappedViews;
+ },
+ /** @returns {string} */
+ platform: ImplementInSubclass,
+ /** @returns {object} */
+ build: ImplementInSubclass,
+ /** @returns {object} */
+ preview: ImplementInSubclass,
+ /**
+ * @param {string} type -- view type
+ * @returns {object}
+ */
+ getViewForKey: ImplementInSubclass,
+ /** @returns {object} */
+ toJSON: function() {
+ var json = Backbone.Model.prototype.toJSON.apply(this, arguments);
+ // Remove the campaignId reference as it's not used for
+ // saving or previewing
+ var ignoredKeys = ['campaignId'];
+ return _(json).reduce(function(obj, val, key) {
+ if (!_.isUndefined(val) && !_(ignoredKeys).contains(key)) {
+ obj[key] = val && _.isFunction(val.toJSON) ? val.toJSON() : val;
+ }
+ return obj;
+ }, {});
+ }
+ });
+ Module.Creative = Creative;
- Backbone.Model.call(this, attributes, options);
- this.mappedViews = _(this.views || {}).reduce(function(views, key, field) {
- var options = {};
- /**
- * Allow for custom view options. For example,
- * instead of only doing
- * 'views': {
- * 'picture': 'image',
- * 'post_body': 'body'
- * }
- * where the 'post_body' field is rendered as a body (textfield)
- * view, we can now do
- * 'views': {
- * 'picture': 'image',
- * 'post_body': {
- * view: 'body',
- * placeholder: 'This is the post body section',
- * optional: true
- * }
- * }
- * which allows us to override the default view options
- * of a 'body' view.
- */
- if (_.isObject(key)) {
- options = _.omit(key, 'view');
- key = key.view;
- }
- views[field] = _.extend({}, this.getViewForKey(key), options);
- return views;
- }, {}, this);
- },
- /** @returns {window.FormData} */
- formData: function() {
- return _(this.toJSON()).reduce(function(data, val, key) {
- data.append(key, val);
- return data;
- }, new window.FormData());
- },
- /** @returns {object} */
- getViews: function() {
- return this.mappedViews;
- },
- /** @returns {string} */
- platform: ImplementInSubclass,
- /** @returns {object} */
- build: ImplementInSubclass,
- /** @returns {object} */
- preview: ImplementInSubclass,
- /**
- * @param {string} type -- view type
- * @returns {object}
- */
- getViewForKey: ImplementInSubclass,
- /** @returns {object} */
- toJSON: function() {
- var json = Backbone.Model.prototype.toJSON.apply(this, arguments);
- // Remove the campaignId reference as it's not used for
- // saving or previewing
- var ignoredKeys = ['campaignId'];
- return _(json).reduce(function(obj, val, key) {
- if (!_.isUndefined(val) && !_(ignoredKeys).contains(key)) {
- obj[key] = val && _.isFunction(val.toJSON) ? val.toJSON() : val;
- }
- return obj;
- }, {});
- }
- });
- Module.Creative = Creative;
- });
+ module.exports = Module;
---------- diff:
entities/lineitem.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- /**
- * Model and Collection definitions.
- */
+ /* global dependencies */
+ var moment = require('moment');
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- var Lineitem = Backbone.Model.extend({
- url: function() {
- return '/api/v2/lineitem/' + this.id + '/' + this.query;
+ /* module definition */
+ var Entities = {};
+ var Lineitem = Backbone.Model.extend({
+ url: function() {
+ return '/api/v2/lineitem/' + this.id + '/' + this.query;
+ },
+ initialize: function(attr) {
+ this.query = $.fn.generateQueryString(attr.query || {});
+ },
+ dateDisplay: function() {
+ // var format = 'MMMM Do YYYY, h:mma';
+ var format = 'MMM Do YYYY';
+ var date = this.parsedStart().format(format);
+ if (this.parsedEnd()) {
+ date += ' - ' + this.parsedEnd().format(format);
+ }
+ return date;
+ },
+ parsedStart: function() {
+ if (this.get('start_date')) {
+ return moment(this.get('start_date'));
+ } else {
+ return moment().startOf('hour');
+ }
+ },
+ parsedEnd: function() {
+ if (this.get('end_date')) {
+ return moment(this.get('end_date'));
+ }
+ }
+ });
+ var getLineitemEntity = function(id, query) {
+ var lineitem = new Lineitem({ id: id, query: query });
+ var defer = $.Deferred();
+ lineitem.fetch({
+ success: function(model) { //model, response, options
+ defer.resolve(model);
},
- initialize: function(attr) {
- this.query = $.fn.generateQueryString(attr.query || {});
- },
- dateDisplay: function() {
- // var format = 'MMMM Do YYYY, h:mma';
- var format = 'MMM Do YYYY';
- var date = this.parsedStart().format(format);
- if (this.parsedEnd()) {
- date += ' - ' + this.parsedEnd().format(format);
- }
- return date;
- },
- parsedStart: function() {
- if (this.get('start_date')) {
- return moment(this.get('start_date'));
- } else {
- return moment().startOf('hour');
- }
- },
- parsedEnd: function() {
- if (this.get('end_date')) {
- return moment(this.get('end_date'));
- }
+ error: function(model, response) { //model, response, options
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ AdvisorApp.reqres.setHandler('lineitem:entity', function(id, query) {
+ return getLineitemEntity(id, query);
+ });
- /**
- * Get Entit(y/ies) methods
- */
- var getLineitemEntity = function(id, query) {
- var lineitem = new Lineitem({ id: id, query: query });
- var defer = $.Deferred();
- lineitem.fetch({
- success: function(model) { //model, response, options
- defer.resolve(model);
- },
- error: function(model, response) { //model, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- /**
- * Handler Definitions
- */
- // require query for fields, since API doesn't return all
- AdvisorApp.reqres.setHandler('lineitem:entity', function(id, query) {
- return getLineitemEntity(id, query);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/report.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- var excluded_columns_options = [
- 'Impressions',
- 'Reach',
- 'Frequency',
- 'Clicks',
- 'CTR',
- 'Page Likes',
- 'Offsite Conversions',
- 'Spent',
- 'Client Spent',
- 'Client Revenue',
- 'Page Engagements',
- 'App Installs',
- 'App Engagements',
- 'Checkouts',
- 'Add-to-Carts',
- 'Offsite Page Views',
- 'Other Conversions',
- 'Registrations',
- 'Leads',
- 'Photo Views',
- 'Link Clicks',
- 'Shares',
- 'Post Likes',
- 'Comments',
- 'Votes',
- 'Follows',
- 'Mentions',
- 'Clicks to Play Video',
- 'Receive Offer',
- 'App Stories',
- 'Gift Sales',
- 'Recommendations',
- 'Video Views',
- 'Check Ins',
- 'Click to Engagement Rate',
- 'Engagements Per Like',
- 'Impressions Per Like',
- 'Engagement Rate',
- 'Cost Per App Install',
- 'Cost Per Checkout',
- 'Cost Per Add to Cart',
- 'Cost Per Other Conversion',
- 'Cost Per Lead',
- 'Cost Per Registration',
- 'Cost Per Page View',
- 'Client Cost Per App Install',
- 'Client Cost Per Checkout',
- 'Client Cost Per Add to Cart',
- 'Client Cost Per Other Conversion',
- 'Client Cost Per Lead',
- 'Client Cost Per Registration',
- 'Client Cost Per Page View',
- 'Click to App Install Rate',
- 'Click to Checkout Rate',
- 'Click to Add to Cart Rate',
- 'Click to Other Conversion Rate',
- 'Click to Lead Rate',
- 'Click to Registration Rate',
- 'Click to Page View Rate',
- 'Client ROI',
- 'eCPE',
- 'eCPF'
- ];
- var excluded_sections_options = [
- 'Daily Performance',
- 'Creative Image',
- 'Glossary',
- 'Gender',
- 'Age',
- 'Creative Body',
- 'Creative Title',
- 'Gender Age',
- 'Creative Combos',
- 'Creative Category',
- 'Behaviors',
- 'Broad Categories',
- 'Page Targeting',
- 'Ad Type',
- 'Custom Audiences',
- 'Fans Ad Type',
- 'Interests by Ad Type',
- 'Geo'
- ];
+ /* module definition */
+ var Entities = {};
- var ReportConfig = Backbone.Model.extend({
- urlRoot: '/api/reports/v2/reportconfiguration/creative-insights/run/',
- excluded_columns_options: excluded_columns_options,
- excluded_sections_options: excluded_sections_options,
- defaults: {
- 'excluded_columns': [],
- 'excluded_sections': [],
- 'download': 'true'
- },
- initialize: function(options) {
- var lineitemId;
- options = options || {};
- lineitemId = options.lineitemId;
- if (lineitemId) {
- this.setDefaultsFromLineitem(lineitemId);
- }
- },
- setDefaultsFromLineitem: function(lineitemId) {
- var self = this;
- App.request('lineitem:entity', lineitemId, {
- 'fields': ReportConfig.getRequiredLineItemFields()
- })
- .done(function(lineitem) {
- self.set({
- lineitem_id: lineitem.get('id'),
- title: lineitem.get('name'),
- start_date: lineitem.get('start_date'),
- end_date: lineitem.get('end_date'),
- payout_action: lineitem.get('payout_action'),
- post_imp_attribution_multiplier: lineitem.get('post_imp_attribution_multiplier'),
- post_imp_attribution: lineitem.get('post_imp_attribution'),
- post_click_attribution_multiplier: lineitem.get('post_click_attribution_multiplier'),
- post_click_attribution: lineitem.get('post_click_attribution')
- });
- })
- .fail(function() { //response
- //console.log('handle failed LI fetch: ' + response.statusText);
+ var excluded_columns_options = [
+ 'Impressions',
+ 'Reach',
+ 'Frequency',
+ 'Clicks',
+ 'CTR',
+ 'Page Likes',
+ 'Offsite Conversions',
+ 'Spent',
+ 'Client Spent',
+ 'Client Revenue',
+ 'Page Engagements',
+ 'App Installs',
+ 'App Engagements',
+ 'Checkouts',
+ 'Add-to-Carts',
+ 'Offsite Page Views',
+ 'Other Conversions',
+ 'Registrations',
+ 'Leads',
+ 'Photo Views',
+ 'Link Clicks',
+ 'Shares',
+ 'Post Likes',
+ 'Comments',
+ 'Votes',
+ 'Follows',
+ 'Mentions',
+ 'Clicks to Play Video',
+ 'Receive Offer',
+ 'App Stories',
+ 'Gift Sales',
+ 'Recommendations',
+ 'Video Views',
+ 'Check Ins',
+ 'Click to Engagement Rate',
+ 'Engagements Per Like',
+ 'Impressions Per Like',
+ 'Engagement Rate',
+ 'Cost Per App Install',
+ 'Cost Per Checkout',
+ 'Cost Per Add to Cart',
+ 'Cost Per Other Conversion',
+ 'Cost Per Lead',
+ 'Cost Per Registration',
+ 'Cost Per Page View',
+ 'Client Cost Per App Install',
+ 'Client Cost Per Checkout',
+ 'Client Cost Per Add to Cart',
+ 'Client Cost Per Other Conversion',
+ 'Client Cost Per Lead',
+ 'Client Cost Per Registration',
+ 'Client Cost Per Page View',
+ 'Click to App Install Rate',
+ 'Click to Checkout Rate',
+ 'Click to Add to Cart Rate',
+ 'Click to Other Conversion Rate',
+ 'Click to Lead Rate',
+ 'Click to Registration Rate',
+ 'Click to Page View Rate',
+ 'Client ROI',
+ 'eCPE',
+ 'eCPF'
+ ];
+ var excluded_sections_options = [
+ 'Daily Performance',
+ 'Creative Image',
+ 'Glossary',
+ 'Gender',
+ 'Age',
+ 'Creative Body',
+ 'Creative Title',
+ 'Gender Age',
+ 'Creative Combos',
+ 'Creative Category',
+ 'Behaviors',
+ 'Broad Categories',
+ 'Page Targeting',
+ 'Ad Type',
+ 'Custom Audiences',
+ 'Fans Ad Type',
+ 'Interests by Ad Type',
+ 'Geo'
+ ];
+ var ReportConfig = Backbone.Model.extend({
+ urlRoot: '/api/reports/v2/reportconfiguration/creative-insights/run/',
+ excluded_columns_options: excluded_columns_options,
+ excluded_sections_options: excluded_sections_options,
+ defaults: {
+ 'excluded_columns': [],
+ 'excluded_sections': [],
+ 'download': 'true'
+ },
+ initialize: function(options) {
+ var lineitemId;
+ options = options || {};
+ lineitemId = options.lineitemId;
+ if (lineitemId) {
+ this.setDefaultsFromLineitem(lineitemId);
+ }
+ },
+ setDefaultsFromLineitem: function(lineitemId) {
+ var self = this;
+ App.request('lineitem:entity', lineitemId, {
+ 'fields': ReportConfig.getRequiredLineItemFields()
+ })
+ .done(function(lineitem) {
+ self.set({
+ lineitem_id: lineitem.get('id'),
+ title: lineitem.get('name'),
+ start_date: lineitem.get('start_date'),
+ end_date: lineitem.get('end_date'),
+ payout_action: lineitem.get('payout_action'),
+ post_imp_attribution_multiplier: lineitem.get('post_imp_attribution_multiplier'),
+ post_imp_attribution: lineitem.get('post_imp_attribution'),
+ post_click_attribution_multiplier: lineitem.get('post_click_attribution_multiplier'),
+ post_click_attribution: lineitem.get('post_click_attribution')
});
- },
- validate: function(attrs) { //attrs, options
- var errors = {};
- if (new Date(attrs.end_date) < new Date(attrs.start_date)) {
- errors.end_date = 'End Date cannot be before Start Date';
- }
- if (!_.isEmpty(errors)) {
- return errors;
- }
+ })
+ .fail(function() { //response
+ //console.log('handle failed LI fetch: ' + response.statusText);
+ });
+ },
+ validate: function(attrs) { //attrs, options
+ var errors = {};
+ if (new Date(attrs.end_date) < new Date(attrs.start_date)) {
+ errors.end_date = 'End Date cannot be before Start Date';
}
- }, {
- getRequiredLineItemFields: function() {
- return [
- 'id', 'name', 'start_date', 'end_date',
- 'post_click_attribution', 'post_click_attribution_multiplier',
- 'post_imp_attribution', 'post_imp_attribution_multiplier',
- 'payout_action'
- ].join(',');
+ if (!_.isEmpty(errors)) {
+ return errors;
}
- });
- // Run Report(s)
- var Report = Backbone.Model.extend({
- urlRoot: '/api/reports/v2/reportrun/',
- initialize: function() {
- this.downloading = false;
- },
- pollForStatus: function() {
- var self = this;
- (function poll() {
- self.fetch({
- success: function(report, response) { //model, response, options
- switch (report.get('status')) {
- case 'completed':
- self.trigger('poll:status:complete');
- break;
- case 'error':
- self.trigger('poll:status:error', response);
- break;
- default://received,
- self.pollTimeout = setTimeout(poll, 5000);
- break;
- }
- },
- error: function(model, response) { //model, response, options
- self.trigger('poll:status:error', response);
+ }
+ }, {
+ getRequiredLineItemFields: function() {
+ return [
+ 'id', 'name', 'start_date', 'end_date',
+ 'post_click_attribution', 'post_click_attribution_multiplier',
+ 'post_imp_attribution', 'post_imp_attribution_multiplier',
+ 'payout_action'
+ ].join(',');
+ }
+ });
+ var Report = Backbone.Model.extend({
+ urlRoot: '/api/reports/v2/reportrun/',
+ initialize: function() {
+ this.downloading = false;
+ },
+ pollForStatus: function() {
+ var self = this;
+ (function poll() {
+ self.fetch({
+ success: function(report, response) { //model, response, options
+ switch (report.get('status')) {
+ case 'completed':
+ self.trigger('poll:status:complete');
+ break;
+ case 'error':
+ self.trigger('poll:status:error', response);
+ break;
+ default://received,
+ self.pollTimeout = setTimeout(poll, 5000);
+ break;
}
- });
- })();
- return this;
- },
- stopPollingForStatus: function() {
- var timeout = this.pollTimeout;
- if (timeout) {
- clearTimeout(timeout);
- }
- return this;
- },
- download: function() {
- var self = this;
- this.trigger('download:start');
- this.downloading = true;
- this.pollForStatus();
- this.once('poll:status:complete', function() {
- self.off('poll:status:error');
- self.trigger('download:complete');
- self.downloading = false;
- location.href = self.get('resource_uri') + 'export/?export_format=excel';
+ },
+ error: function(model, response) { //model, response, options
+ self.trigger('poll:status:error', response);
+ }
});
- this.once('poll:status:error', function(response) {
- self.off('poll:status:complete');
- self.trigger('download:error', response);
- self.downloading = false;
- });
- return this;
- },
- stopDownload: function() {
- if (this.downloading) {
- this.stopPollingForStatus();
- this.off('poll:status:error');
- this.off('poll:status:complete');
- this.downloading = false;
- }
- return this;
+ })();
+ return this;
+ },
+ stopPollingForStatus: function() {
+ var timeout = this.pollTimeout;
+ if (timeout) {
+ clearTimeout(timeout);
}
- });
- var ReportCollection = Backbone.Collection.extend({
- model: Report,
- url: function() {
- return '/api/reports/v2/reportrun/' +
- $.fn.generateQueryString(this.filters);
+ return this;
+ },
+ download: function() {
+ var self = this;
+ this.trigger('download:start');
+ this.downloading = true;
+ this.pollForStatus();
+ this.once('poll:status:complete', function() {
+ self.off('poll:status:error');
+ self.trigger('download:complete');
+ self.downloading = false;
+ location.href = self.get('resource_uri') + 'export/?export_format=excel';
+ });
+ this.once('poll:status:error', function(response) {
+ self.off('poll:status:complete');
+ self.trigger('download:error', response);
+ self.downloading = false;
+ });
+ return this;
+ },
+ stopDownload: function() {
+ if (this.downloading) {
+ this.stopPollingForStatus();
+ this.off('poll:status:error');
+ this.off('poll:status:complete');
+ this.downloading = false;
+ }
+ return this;
+ }
+ });
+ var ReportCollection = Backbone.Collection.extend({
+ model: Report,
+ url: function() {
+ return '/api/reports/v2/reportrun/' +
+ $.fn.generateQueryString(this.filters);
+ },
+ initialize: function(options) {
+ this.filters = $.extend({ 'order_by': '-created'}, options.query);
+ },
+ parse: function(data) {
+ this.meta = data.meta;
+ return data.objects;
+ },
+ comparator: function(model) {
+ return -Date.parse(model.get('created'));
+ },
+ autoRefresh: function(seconds) {
+ var self = this;
+ var interval = seconds * 1000;
+ var refresh = function() { self.fetch(); };
+ this._refreshInterval = setInterval(refresh, interval);
+ return this;
+ },
+ stopAutoRefresh: function() {
+ var interval = this._refreshInterval;
+ if (interval) {
+ clearInterval(interval);
+ }
+ return this;
+ }
+ });
+ var getReportEntity = function(id) {
+ var report = new Report({ id: id });
+ var defer = $.Deferred();
+ report.fetch({
+ success: function(model) { //model, response, options
+ defer.resolve(model);
},
- initialize: function(options) {
- this.filters = $.extend({ 'order_by': '-created'}, options.query);
- },
- parse: function(data) {
- this.meta = data.meta;
- return data.objects;
- },
- comparator: function(model) {
- return -Date.parse(model.get('created'));
- },
- autoRefresh: function(seconds) {
- var self = this;
- var interval = seconds * 1000;
- var refresh = function() { self.fetch(); };
- this._refreshInterval = setInterval(refresh, interval);
- return this;
- },
- stopAutoRefresh: function() {
- var interval = this._refreshInterval;
- if (interval) {
- clearInterval(interval);
- }
- return this;
+ error: function(model, response) { //model, response, options
+ defer.reject(response);
}
});
- var getReportEntity = function(id) {
- var report = new Report({ id: id });
- var defer = $.Deferred();
- report.fetch({
- success: function(model) { //model, response, options
- defer.resolve(model);
- },
- error: function(model, response) { //model, response, options
- defer.reject(response);
+ return defer.promise();
+ };
+ var getReportEntities = function(options) {
+ var reportCollection = new ReportCollection(options);
+ var defer = $.Deferred();
+ reportCollection.fetch({
+ success: function(collection) { //collection, response, options
+ var intervalInSeconds = options.refreshInterval;
+ if (intervalInSeconds) {
+ collection.autoRefresh(intervalInSeconds);
}
- });
- return defer.promise();
- };
- var getReportEntities = function(options) {
- var reportCollection = new ReportCollection(options);
- var defer = $.Deferred();
- reportCollection.fetch({
- success: function(collection) { //collection, response, options
- var intervalInSeconds = options.refreshInterval;
- if (intervalInSeconds) {
- collection.autoRefresh(intervalInSeconds);
- }
- defer.resolve(collection);
- },
- error: function(collection, response) { //collection, response, options
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- App.reqres.setHandler('report:excluded_columns_options', function() {
- return excluded_columns_options;
- });
- App.reqres.setHandler('report:excluded_sections_options', function() {
- return excluded_sections_options;
- });
- //options.refreshInterval: (int) interval in seconds
- //options.query: query data for the fetch
- App.reqres.setHandler('report:entities', function(options) {
- return getReportEntities(options);
- });
- App.reqres.setHandler('report:entity', function(id) {
- if (id) {
- return getReportEntity(id);
+ defer.resolve(collection);
+ },
+ error: function(collection, response) { //collection, response, options
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ App.reqres.setHandler('report:excluded_columns_options', function() {
+ return excluded_columns_options;
+ });
+ App.reqres.setHandler('report:excluded_sections_options', function() {
+ return excluded_sections_options;
+ });
+ App.reqres.setHandler('report:entities', function(options) {
+ return getReportEntities(options);
+ });
+ App.reqres.setHandler('report:entity', function(id) {
+ if (id) {
+ return getReportEntity(id);
+ }
+ });
+ App.reqres.setHandler('reportConfig:Entity', function() {
+ return ReportConfig;
+ });
- App.reqres.setHandler('reportConfig:Entity', function() {
- return ReportConfig;
- });
- });
+ module.exports = Entities;
---------- diff:
entities/reports.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var ReportsLineItem = Backbone.Model.extend({
- url: function() {
- return '/api/facebook/ads/v1/campaign/adv_' + this.get('id') + '/';
- },
- dateDisplay: function() {
- // var format = 'MMMM Do YYYY, h:mma';
- var format = 'MMM Do YYYY';
- var date = this.parsedStart().format(format);
- if (this.parsedEnd()) {
- date += ' - ' + this.parsedEnd().format(format);
- }
- return date;
- },
- parsedStart: function() {
- if (this.get('start_date')) {
- return moment(this.get('start_date'));
- } else {
- return moment().startOf('hour');
- }
- },
- parsedEnd: function() {
- if (this.get('end_date')) {
- return moment(this.get('end_date'));
- }
- }
- });
+ /* global dependencies */
+ var $ = require('jquery');
+ var moment = require('moment');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- var getReportLineItemEntity = function(campaignID) {
- var reportsLineItem = new ReportsLineItem({ id: campaignID });
- var defer = $.Deferred();
- reportsLineItem.fetch({
- data: $.param({ get_status:true}),
- success: function(model) { //model, response, options
- defer.resolve(model);
- },
- error: function(model, response) { //model, response, options
- defer.reject(response);
- }
- });
+ /* module definition */
+ var Entities = {};
- return defer.promise();
- };
- AdvisorApp.reqres.setHandler('reports:lineitem:entity', function(id, query) { //require query for fields, since API doesn't return all
- return getReportLineItemEntity(id, query);
+ var ReportsLineItem = Backbone.Model.extend({
+ url: function() {
+ return '/api/facebook/ads/v1/campaign/adv_' + this.get('id') + '/';
+ },
+ dateDisplay: function() {
+ // var format = 'MMMM Do YYYY, h:mma';
+ var format = 'MMM Do YYYY';
+ var date = this.parsedStart().format(format);
+ if (this.parsedEnd()) {
+ date += ' - ' + this.parsedEnd().format(format);
+ }
+ return date;
+ },
+ parsedStart: function() {
+ if (this.get('start_date')) {
+ return moment(this.get('start_date'));
+ } else {
+ return moment().startOf('hour');
+ }
+ },
+ parsedEnd: function() {
+ if (this.get('end_date')) {
+ return moment(this.get('end_date'));
+ }
+ }
+ });
+ var getReportLineItemEntity = function(campaignID) {
+ var reportsLineItem = new ReportsLineItem({ id: campaignID });
+ var defer = $.Deferred();
+ reportsLineItem.fetch({
+ data: $.param({ get_status:true}),
+ success: function(model) { //model, response, options
+ defer.resolve(model);
+ },
+ error: function(model, response) { //model, response, options
+ defer.reject(response);
+ }
});
+ return defer.promise();
+ };
+ AdvisorApp.reqres.setHandler('reports:lineitem:entity', function(id, query) { //require query for fields, since API doesn't return all
+ return getReportLineItemEntity(id, query);
});
+ module.exports = Entities;
---------- diff:
entities/session.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- /**
- * Model & Collection definitions
- */
- var UserSession = Backbone.Model.extend({
- url: '/api/bouncer/v1/auth/user/current/'
- });
+ /* global dependencies */
+ var moment = require('moment');
+ var _ = require('underscore');
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../app');
- var Team = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/team/'
- });
- var Teams = Backbone.Collection.extend({
- url: '/api/advisor/v1/team/',
- model: Team
- });
+ /* module definition */
+ var Entities = {};
- var Grant = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/grant/'
- });
- var Grants = Backbone.Collection.extend({
- url: '/api/advisor/v1/grant/?ancestry=true&limit=0',
- model: Grant,
- parse: function(response) {
- return response.objects;
+ var UserSession = Backbone.Model.extend({
+ url: '/api/bouncer/v1/auth/user/current/'
+ });
+ var Team = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/team/'
+ });
+ var Teams = Backbone.Collection.extend({
+ url: '/api/advisor/v1/team/',
+ model: Team
+ });
+ var Grant = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/grant/'
+ });
+ var Grants = Backbone.Collection.extend({
+ url: '/api/advisor/v1/grant/?ancestry=true&limit=0',
+ model: Grant,
+ parse: function(response) {
+ return response.objects;
+ }
+ });
+ var _getDefaultSelectedTeam = function(teams) {
+ var advisorTeamId = $.cookie('advisor.team');
+ var advisorTeam = !isNaN(advisorTeamId) && teams.get(advisorTeamId);
+ return advisorTeam || teams.findWhere({ slug: 'socialcode' }) || teams.at(0);
+ };
+ var fetchUserSession = function() {
+ var userSession = new UserSession();
+ var defer = $.Deferred();
+ userSession.fetch({
+ success: function(collection) {
+ defer.resolve(collection);
+ },
+ error: function(collection, response) {
+ clearSession();
+ defer.reject(response);
}
});
- /**
- * Getter & Setter methods
- */
- /**
- * @param {Backbone.Collection} teams
- * @returns {Backbone.Model}
- */
- var _getDefaultSelectedTeam = function(teams) {
- var advisorTeamId = $.cookie('advisor.team');
- var advisorTeam = !isNaN(advisorTeamId) && teams.get(advisorTeamId);
- return advisorTeam || teams.findWhere({ slug: 'socialcode' }) || teams.at(0);
- };
- /** @returns {$.promise} */
- var fetchUserSession = function() {
- var userSession = new UserSession();
- var defer = $.Deferred();
- userSession.fetch({
- success: function(collection) {
- defer.resolve(collection);
- },
- error: function(collection, response) {
- clearSession();
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- /** @returns {$.promise} */
- var fetchTeams = function() {
- var teams = new Teams();
- var defer = $.Deferred();
- teams.fetch({
- success: function(collection) {
- defer.resolve(collection);
- },
- error: function(collection, response) {
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- /** @returns {Backbone.Model} */
- var currentTeam = function(session) {
- return session.get('selectedTeam');
- };
- var clearSession = function() {
- sessionStorage.setItem('referrer', document.location.origin + document.location.pathname);
- window.location.replace('/accounts/#login');
- };
- var userCanManage = function(id) {
- var item = JSON.parse(localStorage.getItem(id));
- if (_.isNull(item)) { return null; }
- var oneDayInMilliseconds = 86400000;
- var nowTimestamp = moment().unix() * 1000;
- var storageTimestamp = Number(item.timestamp);
- var oneDayLater = Number(item.timestamp) + oneDayInMilliseconds;
- var hasNotExpired = moment(nowTimestamp).isBetween(storageTimestamp, oneDayLater);
- if (!hasNotExpired) { return null; }
- return item.canManage;
- };
- var API = {
- getSession: function() {
- return this.session;
+ return defer.promise();
+ };
+ var fetchTeams = function() {
+ var teams = new Teams();
+ var defer = $.Deferred();
+ teams.fetch({
+ success: function(collection) {
+ defer.resolve(collection);
},
- initializeSession: function(options) {
- this.session = new UserSession(options.user);
- this.teams = new Teams(options.teams);
- this.grants = new Grants(options.grants);
- this.session.set('selectedTeam', _getDefaultSelectedTeam(this.teams));
- return this.session;
- },
- setSession: function(session) {
- this.session = session;
- return this.session;
- },
- getGrants: function() {
- return this.grants;
- },
- getTeams: function() {
- return this.teams;
- },
- getCurrentTeam: function() {
- return currentTeam(this.session);
- },
- clearSession: function() {
- clearSession();
+ error: function(collection, response) {
+ defer.reject(response);
}
- };
- /**
- * Event handlers
- */
- AdvisorApp.reqres.setHandler('session:entity', function() {
- return API.getSession();
});
- AdvisorApp.reqres.setHandler('session:entity:set', function(options) {
- return API.initializeSession(options);
+ return defer.promise();
+ };
+ var currentTeam = function(session) {
+ return session.get('selectedTeam');
+ };
+ var clearSession = function() {
+ sessionStorage.setItem('referrer', document.location.origin + document.location.pathname);
+ window.location.replace('/accounts/#login');
+ };
+ var userCanManage = function(id) {
+ var item = JSON.parse(localStorage.getItem(id));
+ if (_.isNull(item)) { return null; }
+ var oneDayInMilliseconds = 86400000;
+ var nowTimestamp = moment().unix() * 1000;
+ var storageTimestamp = Number(item.timestamp);
+ var oneDayLater = Number(item.timestamp) + oneDayInMilliseconds;
+ var hasNotExpired = moment(nowTimestamp).isBetween(storageTimestamp, oneDayLater);
+ if (!hasNotExpired) { return null; }
+ return item.canManage;
+ };
+ var API = {
+ getSession: function() {
+ return this.session;
+ },
+ initializeSession: function(options) {
+ this.session = new UserSession(options.user);
+ this.teams = new Teams(options.teams);
+ this.grants = new Grants(options.grants);
+ this.session.set('selectedTeam', _getDefaultSelectedTeam(this.teams));
+ return this.session;
+ },
+ setSession: function(session) {
+ this.session = session;
+ return this.session;
+ },
+ getGrants: function() {
+ return this.grants;
+ },
+ getTeams: function() {
+ return this.teams;
+ },
+ getCurrentTeam: function() {
+ return currentTeam(this.session);
+ },
+ clearSession: function() {
+ clearSession();
+ }
+ };
+ AdvisorApp.reqres.setHandler('session:entity', function() {
+ return API.getSession();
+ });
+ AdvisorApp.reqres.setHandler('session:entity:set', function(options) {
+ return API.initializeSession(options);
+ });
+ AdvisorApp.reqres.setHandler('session:entity:clear', function() {
+ return API.clearSession();
+ });
+ AdvisorApp.reqres.setHandler('session:grant:entities', function() {
+ return API.getGrants();
+ });
+ AdvisorApp.reqres.setHandler('session:grant:entities:can_create_initiatives', function() {
+ /* true if a user has manage granted on the current team or one of its brands */
+ var grants = AdvisorApp.request('session:grant:entities');
+ var team = currentTeam(API.session);
+ var requirements = { manage: true, team_id: team.id, initiative_id: null };
+ return !!grants.findWhere(requirements);
+ });
+ AdvisorApp.reqres.setHandler('session:grant:entities:can_manage_brand_or_initiative', function(brand_id, initiative_id) {
+ var hasPermission = userCanManage(initiative_id);
+ if (!_.isNull(hasPermission)) { return hasPermission; }
+ /* true if a user has manage granted on the current team or this initiative or the initiative's brand */
+ var grants = AdvisorApp.request('session:grant:entities');
+ var team = currentTeam(API.session);
+ var passCases = [
+ { manage: true, team_id: team.id, initiative_id: null },
+ { manage: true, brand_id: brand_id, initiative_id: null },
+ { manage: true, initiative_id: initiative_id }
+ ];
+ hasPermission = !!_(passCases).filter(function(caseObj) {
+ return grants.findWhere(caseObj);
+ }).length;
+ var json = JSON.stringify({
+ canManage: hasPermission,
+ timestamp: JSON.stringify(moment().unix() * 1000)
});
+ localStorage.setItem(initiative_id, json);
+ return hasPermission;
+ });
+ AdvisorApp.reqres.setHandler('session:team:entities', function() {
+ return API.getTeams();
+ });
+ AdvisorApp.reqres.setHandler('session:team:selected', function() {
+ return API.getCurrentTeam();
+ });
- AdvisorApp.reqres.setHandler('session:entity:clear', function() {
- return API.clearSession();
- });
- AdvisorApp.reqres.setHandler('session:grant:entities', function() {
- return API.getGrants();
- });
- AdvisorApp.reqres.setHandler('session:grant:entities:can_create_initiatives', function() {
- /* true if a user has manage granted on the current team or one of its brands */
- var grants = AdvisorApp.request('session:grant:entities');
- var team = currentTeam(API.session);
- var requirements = { manage: true, team_id: team.id, initiative_id: null };
- return !!grants.findWhere(requirements);
- });
- AdvisorApp.reqres.setHandler('session:grant:entities:can_manage_brand_or_initiative', function(brand_id, initiative_id) {
- var hasPermission = userCanManage(initiative_id);
- if (!_.isNull(hasPermission)) { return hasPermission; }
- /* true if a user has manage granted on the current team or this initiative or the initiative's brand */
- var grants = AdvisorApp.request('session:grant:entities');
- var team = currentTeam(API.session);
- var passCases = [
- { manage: true, team_id: team.id, initiative_id: null },
- { manage: true, brand_id: brand_id, initiative_id: null },
- { manage: true, initiative_id: initiative_id }
- ];
- hasPermission = !!_(passCases).filter(function(caseObj) {
- return grants.findWhere(caseObj);
- }).length;
- var json = JSON.stringify({
- canManage: hasPermission,
- timestamp: JSON.stringify(moment().unix() * 1000)
- });
- localStorage.setItem(initiative_id, json);
- return hasPermission;
- });
- AdvisorApp.reqres.setHandler('session:team:entities', function() {
- return API.getTeams();
- });
- AdvisorApp.reqres.setHandler('session:team:selected', function() {
- return API.getCurrentTeam();
- });
- });
+ module.exports = Entities;
---------- diff:
entities/user.js
- AdvisorApp.module('Entities', function(Entities, App, Backbone) {
+ /* global dependencies */
+ var AdvisorApp = require('../app');
- /**
- * Model and Collection definitions
- */
- var User = Backbone.Model.extend({
- /**
- * TODO: change endpoint to '/api/bouncer/v2/user/' when it's in prod.
- * Note:it will have no superuser flag.
- */
- urlRoot: '/api/bouncer/v1/auth/user/'
- });
- var UserCollection = Backbone.Collection.extend({
- model: User,
- url: User.prototype.urlRoot
- });
+ /* module definition */
+ var Entities = {};
- var UserPageableCollection = Backbone.TastypiePageableCollection.extend({
- model: User,
- url: User.prototype.urlRoot
- });
+ var User = Backbone.Model.extend({
/**
- * Fetch user
- * @return {$.Promise}
+ * TODO: change endpoint to '/api/bouncer/v2/user/' when it's in prod.
+ * Note:it will have no superuser flag.
*/
- var getUser = function(id) {
- return Entities.fetchEntity(User, id);
- };
+ urlRoot: '/api/bouncer/v1/auth/user/'
+ });
+ var UserCollection = Backbone.Collection.extend({
+ model: User,
+ url: User.prototype.urlRoot
+ });
+ var UserPageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: User,
+ url: User.prototype.urlRoot
+ });
+ var getUser = function(id) {
+ return Entities.fetchEntity(User, id);
+ };
+ var getUserCollection = function(options) {
+ return Entities.fetchEntities(UserCollection, options);
+ };
+ App.reqres.setHandler('user:entity', function(id) {
+ return getUser(id);
+ });
+ App.reqres.setHandler('user:entities', function(options) {
+ return getUserCollection(options);
+ });
+ App.reqres.setHandler('user:Entity', function() {
+ return User;
+ });
+ App.reqres.setHandler('user:Entities', function() {
+ return UserCollection;
+ });
+ App.reqres.setHandler('user:PageableEntities', function() {
+ return UserPageableCollection;
+ });
- /**
- * Fetch user collection
- * @return {$.Promise}
- */
- var getUserCollection = function(options) {
- return Entities.fetchEntities(UserCollection, options);
- };
- /**
- * Handler definitions
- */
- App.reqres.setHandler('user:entity', function(id) {
- return getUser(id);
- });
- App.reqres.setHandler('user:entities', function(options) {
- return getUserCollection(options);
- });
- App.reqres.setHandler('user:Entity', function() {
- return User;
- });
- App.reqres.setHandler('user:Entities', function() {
- return UserCollection;
- });
- App.reqres.setHandler('user:PageableEntities', function() {
- return UserPageableCollection;
- });
- });
+ module.exports = Entities;
---------- diff:
common/react/modelView.jsx
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
/**
* BackboneModelView
* a higher order component for composing react components that respond to changes in a backbone model.
*/
- AdvisorApp.module('BackboneModelView', function(View, App, Backbone, Marionette, $, _, React) {
+ /* module definition */
+ var View = {};
- var backboneModelComponent = function(Component, model, preventPreload) {
- var modelType = model instanceof Backbone.Model ? 'model' : model instanceof Backbone.Collection ? 'collection' : null;
- if (!modelType) {
- throw new Error('Invalid model passed to backbone model view.');
+ var backboneModelComponent = function(Component, model, preventPreload) {
+ var modelType = model instanceof Backbone.Model ? 'model' : model instanceof Backbone.Collection ? 'collection' : null;
+ if (!modelType) {
+ throw new Error('Invalid model passed to backbone model view.');
+ }
+ var BackboneComponent = React.createClass({
+ getInitialState: function() {
+ var state = {};
+ state[modelType] = model;
+ return state;
+ },
+ componentDidMount: function() {
+ if (!preventPreload) {
+ this.updateModel();
+ }
+ },
+ updateModel: function() {
+ this.state[modelType].fetch(this.props).then(this.modelUpdated, this.onError);
+ },
+ modelUpdated: function() {
+ var state = {};
+ state[modelType] = model;
+ this.setState(state);
+ },
+ onError: function(e) {
+ this.setState({
+ error: e
+ }) // TODO: show an error instead
+ throw new Error(e.statusText);
+ },
+ render: function() {
+ return <Component {...this.props} {...this.state}/>;
}
- var BackboneComponent = React.createClass({
- getInitialState: function() {
- var state = {};
- state[modelType] = model;
- return state;
- },
- componentDidMount: function() {
- if (!preventPreload) {
- this.updateModel();
- }
- },
- updateModel: function() {
- this.state[modelType].fetch(this.props).then(this.modelUpdated, this.onError);
- },
- modelUpdated: function() {
- var state = {};
- state[modelType] = model;
- this.setState(state);
- },
- onError: function(e) {
- this.setState({
- error: e
- }) // TODO: show an error instead
- throw new Error(e.statusText);
- },
- render: function() {
- return <Component {...this.props} {...this.state}/>;
- }
- });
- return BackboneComponent;
- };
+ });
+ return BackboneComponent;
+ };
+ this.create = backboneModelComponent;
- this.create = backboneModelComponent;
- }, React);
+ module.exports = View;
---------- diff:
config/backgrid/bugfixes.js
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var _ = require('underscore');
(function() {
// Extending to include Backgrid Github pull #499 to fix problem with 'sort' event not
// being fired for server-side collections.
_.extend(Backgrid.Body.prototype, {
sort: function(column, direction) {
var collection = this.collection;
var order;
var callback;
var comparator;
if (!_.contains(['ascending', 'descending', null], direction)) {
throw new RangeError('direction must be one of "ascending", "descending" or `null`');
}
if (_.isString(column)) {
column = this.columns.findWhere({
name: column
});
}
switch (direction) {
case 'ascending':
order = -1;
break;
case 'descending':
order = 1;
break;
default:
order = null;
}
callback = order ? column.sortValue() : function(model) {
return model.cid.replace('c', '') * 1;
};
comparator = this.makeComparator(column.get('name'), order, callback);
if (Backbone.PageableCollection && collection instanceof Backbone.PageableCollection) {
collection.setSorting(order && column.get('name'), order, {
sortValue: column.sortValue()
});
if (collection.fullCollection) {
// If order is null, pageable will remove the comparator on both sides,
// in this case the default insertion order comparator needs to be
// attached to get back to the order before sorting.
if (_.isNull(collection.fullCollection.comparator)) {
collection.fullCollection.comparator = comparator;
}
collection.fullCollection.sort();
collection.trigger('backgrid:sorted', column, direction, collection);
} else {
// Trigger a sort event on the collection, which currently does not
// trigger this event for server-side collections.
// Backgrid GitHub pull #499
collection.trigger('sort');
collection.fetch({
reset: true,
success: function() {
collection.trigger('backgrid:sorted', column, direction, collection);
}
});
}
} else {
collection.comparator = comparator;
collection.sort();
collection.trigger('backgrid:sorted', column, direction, collection);
}
column.set('direction', direction);
return this;
}
});
// Prune selected list of models during pagination (workaround for backgrid and SS pagination)
Backgrid.Grid.prototype.getSelectedModels = function() {
var headerCells = this.header.row.cells;
var result = [];
var selectAllHeaderCell;
var selectedModels;
var collection;
var model;
_(headerCells).each(function(headerCell) {
if (headerCell instanceof Backgrid.Extension.SelectAllHeaderCell) {
selectAllHeaderCell = headerCell;
return false; // Breaks out of loop early.
}
});
if (selectAllHeaderCell) {
selectedModels = selectAllHeaderCell.selectedModels;
collection = this.collection.fullCollection || this.collection;
// TODO: Can we do this with `map` so we can avoid the `.push` call?
for (var modelId in selectedModels) {
if (selectedModels.hasOwnProperty(modelId)) {
model = collection.get(modelId);
if (typeof model === 'undefined') {
delete selectAllHeaderCell.selectedModels[modelId];
} else {
result.push(collection.get(modelId));
}
}
}
}
return result;
};
})();
---------- diff:
config/backgrid/cells.js
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
+ var $ = require('jquery');
+ var moment = require('moment');
+ var _ = require('underscore');
(function() {
// Default cells to not editable
Backgrid.Column.prototype.defaults.editable = false;
Backgrid.Column.prototype.defaults.hideable = true;
Backgrid.Column.prototype.defaults.disabled = false;
Backgrid.Column.prototype.defaults.defaultColumn = false;
Backgrid.Column.prototype.defaults.sortType = 'toggle';
Backgrid.Column.prototype.defaults.aggregationColumns = 'all';
/**
* @returns {Boolean} If true then cell is editable, if false then it is not editable.
*/
Backgrid.Cell.prototype.isEditable = function() {
// NOTE: In a perfect world we'd always check for user editable permissions, however to make this a little-bit
// bulletproof with previous code I'm allowing for this block to be skipped over if a
// #userCanManage method does not exist on the model.
if (_.isFunction(this.model.userCanManage) && !this.model.userCanManage() ||
!this.model.userCanManage ||
!this.model.getEditableCells) {
return false;
}
var allColumns = this.column.collection.pluck('name');
var editableCells = this.model.getEditableCells(allColumns);
var cellName = this.column.get('name');
var isEditable = _(editableCells).indexOf(cellName) !== -1;
// If this method is being invoked from a Backgrid Cell with a isValueEditable method on it and isEditable
// is true then run the isValueEditable method and the result of that will determine if the cell is edtaible.
// Otherwise simply return the current value of isEditable.
if (isEditable && this.isValueEditable) {
isEditable = this.isValueEditable();
}
return isEditable;
};
/**
* @returns {Boolean} If the cell's isEditable method returns true and the user has the correct permissions
* then this method returns true meaning editing is enabled, otherwise it returns false meaning editing is disabled.
*/
Backgrid.Cell.prototype.editingEnabled = function() {
return this.isEditable();
};
// Formatters
var CurrencyFormatter = Backgrid.Extension.CurrencyFormatter = function(options) {
_.extend(this, this.defaults, options || {});
};
CurrencyFormatter.prototype = new Backgrid.NumberFormatter();
_.extend(CurrencyFormatter.prototype, {
// Default settings for accounting.js
// TODO: DRY up with other implementations of this same section.
symbol: '$',
decimal: '.',
thousand: ',',
precision: 2,
format: '%s%v',
defaults: {
divider: 1
},
// Takes attribute value and formats for display to user
fromRaw: function(value, model) {
value = value / this.divider;
return accounting.formatMoney(value, {
symbol: this.symbol,
decimal: this.decimal,
thousand: this.thousand,
precision: this.precision,
format: this.format
});
},
// Takes human input and prepares to be set back to the model
toRaw: function(formattedValue, model) {
// Remove all but digits and periods
return accounting.unformat(formattedValue);
}
});
var SCCurrencyFormatter = _.extend({}, Backgrid.NumberFormatter.prototype, {
fromRaw: function(rawValue, model) {
if (!Helpers.valueIsValid(rawValue)) {
return '-';
}
return accounting.formatMoney(rawValue);
},
toRaw: function(formattedValue, model) {
return Backgrid.NumberFormatter.prototype.toRaw.call(this, formattedValue.substring(1));
}
});
var MicrodollarFormatter = Backgrid.Extension.MicrodollarFormatter = function(options) {
_.extend(this, this.defaults, options || {});
};
MicrodollarFormatter.prototype = new Backgrid.NumberFormatter();
_.extend(MicrodollarFormatter.prototype, {
currencyPrefix: '$',
currencySuffix: '',
// Takes attribute value and formats for display to user
fromRaw: function(number, model) {
number = number || 0;
number = (number / 1000000.0).toFixed(~~this.decimals);
var parts = number.split('.');
var integerPart = parts[0];
var decimalPart = parts[1] ? '.' + parts[1] : '';
return this.currencyPrefix +
integerPart.replace(this.HUMANIZED_NUM_RE, '$1' + this.orderSeparator) +
decimalPart +
this.currencySuffix;
},
// Takes human input and prepares to be set back to the model
toRaw: function(formattedData, model) {
// Remove all but digits and periods
var rawData = formattedData.replace(/[^\d.]/g, '');
var result = (rawData * 1).toFixed(~~this.decimals) * 1;
if (_.isNumber(result) && !_.isNaN(result)) {
return result * 1000000;
}
}
});
var SCBudgetFormatter = Backgrid.Extension.SCBudgetFormatter = function(options) {
_.extend(this, this.defaults, options || {});
};
SCBudgetFormatter.prototype = new Backgrid.StringFormatter();
_.extend(SCBudgetFormatter.prototype, {
// Default settings for accounting.js
// TODO: DRY up with other implementations of this same section.
format: {
symbol: '$',
decimal: '.',
thousand: ',',
precision: 2,
format: '%s%v'
},
// Takes attribute value and formats for display to user
fromRaw: function(value, model) {
var label = 'Lifetime';
value = model.get('lifetime_budget') / 100.0;
// Switch label to daily if there's no lifetime
if (value === 0) {
value = model.get('daily_budget') / 100.0;
label = 'Daily';
}
return accounting.formatMoney(value, this.format) + '<br /><small>' + label + '</small>';
},
// Takes human input and prepares to be set back to the model
toRaw: function(formattedValue, model) {
// Remove all but digits and periods, multiply by 100
return accounting.unformat(formattedValue) * 100;
}
});
var SCDateFormatter = Backgrid.Extension.SCDateFormatter = function(options) {
_.extend(this, this.defaults, options || {});
};
SCDateFormatter.prototype = new Backgrid.StringFormatter();
_.extend(SCDateFormatter.prototype, {
// Takes attribute value and formats for display to user
fromRaw: function(value, model) {
var dateTime = moment.utc(value).tz(this.timezone);
var content = '';
if (value && dateTime.isValid()) {
content = dateTime.format('M/D/YY <br /> <\\s\\m\\a\\l\\l>h:mma z</\\s\\m\\a\\l\\l>');
} else {
content = '<small>N/A</small>';
}
return '<div class="pull-left">' + content + '</div>';
}
});
// Cells
var LinkCell = Backgrid.Extension.LinkCell = Backgrid.StringCell.extend({
linkUri: function() {
return '#';
},
render: function() {
var value = this.formatter.fromRaw(this.model.get(this.column.get('name')));
var link = $('<a>', {
href: this.linkUri(),
text: value
});
this.$el.html(link);
this.delegateEvents();
return this;
}
});
var SCEditableStatusCell = Backgrid.Extension.SCEditableStatusCell = Backgrid.StringCell.extend({
template: Handlebars.templates.SCDropdownCell,
events: {
'click a.sc-dropdown-edit': 'itemClicked'
},
alignment: 'right',
startSpinner: function() {
this.$('i.fa.fa-status').attr('class', 'fa fa-fw fa-status fa-spinner fa-spin');
},
render: function() {
var value = this.model.get(this.column.get('name'));
var status = this.formatter.fromRaw(value, this.model).toLowerCase();
var status_class = 'fa-status-' + status.replace('_', '-');
var status_label = status.replace('_', ' ');
var status_icon = '<i class="fa fa-fw fa-status ' + status_class + '" title="' + status_label + '"></i>';
this.$el.addClass('sc-editable-status-cell').html(this.template({
editingEnabled: this.editingEnabled(),
value: status_icon,
choices: this.model.getStatusActions(),
attributeName: this.column.get('name'),
alignment: this.alignment
}));
this.delegateEvents();
return this;
},
itemClicked: function(e) {
var target = $(e.currentTarget);
var value = target.data('value');
var objectData = {};
var success = _(function() {
/**
* Currently, the database does not return the updated object(s) in bulk action
* responses, so we need to fetch the collection to display any updates. If
* that should change down the road, this fetch() can be replaced with: this.render().
*/
AdvisorApp.mainRegion.currentView.tableView.grid.collection.fetch();
}).bind(this);
var error = function(resp, xhr) {
console.log('Error with', resp, xhr.responseText);
};
objectData[this.column.get('name')] = this.formatter.toRaw(value);
this.startSpinner();
$.ajax({
type: 'POST',
contentType: 'application/json',
url: '/api/advisor/v1/campaign/' + value + '/',
data: JSON.stringify({
'id__in': this.model.get('id')
}),
success: success,
error: error
});
this.$('.dropdown').dropdown('toggle');
return false;
}
});
var SCStatusCell = Backgrid.Extension.SCStatusCell = Backgrid.StringCell.extend({
className: 'sc-status-cell',
render: function() {
var value = this.model.get(this.column.get('name'));
var status = this.formatter.fromRaw(value).toLowerCase();
var status_class = 'fa-status-' + status.replace('_', '-');
var status_label = status.replace('_', ' ');
this.$el.html($('<i/>', {
'class': 'fa fa-fw fa-status-' + status + ' ' + status_class,
title: status_label
}));
this.delegateEvents();
return this;
}
});
var SCTwitterStatusCell = Backgrid.Extension.SCTwitterStatusCell = Backgrid.StringCell.extend({
className: 'sc-status-cell',
render: function() {
var twitterStatusMap = [
'active', 'idle', 'paused', 'pending', 'exhausted', 'expired', 'cancelled', 'deleted', 'unknown'
];
var status = twitterStatusMap[this.model.get(this.column.get('name'))];
var status_class = 'fa-status-' + status;
this.$el.html($('<i/>', {
'class': 'fa fa-fw fa-status-' + status + ' ' + status_class,
title: status
}));
this.delegateEvents();
return this;
}
});
var SCInlineEditCell = Backgrid.Extension.SCInlineEditCell = Backgrid.Cell.extend({
events: {
'click .popover-trigger': 'togglePopover'
},
form: Handlebars.templates.SCInlineEditCell,
placement: 'left',
startSpinner: function() {
this.$('i.fa-pencil').removeClass('fa-pencil').addClass('fa-spinner fa-spin');
},
render: function() {
var value = this.formatter.fromRaw(this.model.get(this.column.get('name')), this.model);
var editLink = $('<div/>').addClass('sc-edit-link');
// Intentionally emptying here because we could end up in an inbetween state if we do not.
this.$el.empty().addClass('sc-inline-edit-cell');
if (_.isFunction(this.form)) {
this.form = this.form();
}
// TODO: Abstract this whole block out into several methods and/or templates. Way too much going on here.
if (!_.isNull(value)) {
// Preserve detail links, make pencil the only click target
if (this.linkUri) {
editLink.append(
$('<a/>')
.attr('href', this.linkUri())
.addClass('sc-edit-detail')
.html(value)
);
if (this.editingEnabled()) {
editLink.append(
$('<a/>')
.addClass('popover-trigger')
.html('<i class="fa fa-pencil sc-edit-icon"></i>')
);
}
this.$el.append(editLink);
} else {
var icon = '';
if (this.editingEnabled()) {
icon = '<i class="fa fa-pencil sc-edit-icon"></i>';
}
this.$el.append($('<span>')
.addClass('popover-trigger sc-edit-link')
.html(value + icon)
);
}
if (this.editingEnabled()) {
this.$('.sc-edit-link').addClass('enabled');
var popover = this.$('i.fa').popover({
html: true,
content: this.form,
placement: this.placement,
trigger: 'manual'
});
}
} else {
this.$el.append($('<span class="text-muted">').text('--'));
}
this.delegateEvents();
return this;
},
prepareForm: function() {
this.$('input').val(this.model.get(this.column.get('name'))).focus();
},
togglePopover: function(e) {
e.preventDefault();
e.stopPropagation();
if (this.$('div.popover').length === 0) {
// Close any existing popovers on the page first
$('i.fa.open').removeClass('open').popover('hide');
this.$('i.fa').addClass('open').popover('show');
this.$('a.close').on('click', this.closePopover);
this.$('form').on('submit', _(this.submit).bind(this));
this.prepareForm();
// Clicks outside of the popover should close it
$(document).on('click', this.closePopover);
this.$('.popover').off('click').on('click', function(e) {
e.stopPropagation(); // Prevent clicks inside from triggering document:click
});
} else {
this.closePopover();
}
},
closePopover: function(e) {
$('i.fa.open').popover('hide').removeClass('open');
},
submit: function(e) {
e.preventDefault();
var inputControl = this.$('input').not('.editor-ignore');
var newvalue = inputControl.val();
var attribute = inputControl.data('attribute') || this.column.get('name');
var objectData = {};
var success;
var error;
success = _(function() {
this.model.trigger('backgrid:force-reset');
}).bind(this);
error = function(resp, xhr) {
console.log('Error with', resp, xhr.responseText);
};
objectData[attribute] = this.formatter.toRaw(newvalue);
this.startSpinner();
this.model.save(objectData, {
silent: true,
patch: true,
success: success,
error: error
});
this.closePopover(null);
}
});
var SCDateTimeEditCell = Backgrid.Extension.SCDateTimeEditCell = SCInlineEditCell.extend({
form: Handlebars.templates.SCDateTimeEditCellForm,
timezone: 'America/New_York',
formatter: SCDateFormatter,
initialize: function(options) {
SCDateTimeEditCell.__super__.initialize.apply(this, arguments);
this.timezone = options.timezone || this.timezone;
this.formatter.timezone = this.timezone;
},
prepareForm: function() {
var currentValue = moment.utc(this.model.get(this.column.get('name'))).tz(this.timezone);
var $hiddenWidget = this.$('#datetime_value');
var $timeWidget = this.$('input.timepicker');
var $dateWidget = this.$('input.datepicker');
var updateDate;
if (currentValue.isValid()) {
$hiddenWidget.val(currentValue.toJSON());
$timeWidget.val(currentValue.format('h:mma'));
$dateWidget.val(currentValue.format('MM/DD/YYYY'));
}
$dateWidget.datepicker({
autoclose: true
});
$timeWidget.timepicker();
updateDate = function(e) {
var dateString = $dateWidget.val() + ' ' + $timeWidget.val();
var dateTime = moment.tz(dateString, 'MM/DD/YYYY h:mma', this.timezone);
$hiddenWidget.val(dateTime.toJSON());
};
$dateWidget.datepicker().on('changeDate', updateDate);
$timeWidget.timepicker().on('changeTime', updateDate);
$dateWidget.focus();
},
closePopover: function(e) {
if (e && !$(e.target).is('.day, .month, .year, .prev, .next, .datepicker-switch, li')) {
$('input.timepicker', this.el).timepicker('remove');
$('input.datepicker', this.el).datepicker('remove');
$('i.fa.open').popover('hide').removeClass('open');
}
}
});
var SCDateCell = Backgrid.Extension.SCDateCell = SCDateTimeEditCell.extend({
/**
* Checks if date is in the future.
* @returns {Boolean} true if the date is in the future, otherwise returns false.
*/
isValueEditable: function() {
var modelKey = this.column.get('name');
var dateString = this.model.get(modelKey);
var date = moment(dateString);
var now = moment();
return date.isAfter(now);
}
});
var CurrencyCell = Backgrid.Extension.CurrencyCell = Backgrid.NumberCell.extend({
className: 'currency-cell number-cell',
formatter: Backgrid.Extension.CurrencyFormatter,
divider: Backgrid.Extension.CurrencyFormatter.prototype.defaults.divider,
/**
* Initializes this cell and the number formatter.
* @param {Object} options
*/
initialize: function(options) {
CurrencyCell.__super__.initialize.apply(this, arguments);
var formatter = this.formatter;
formatter.divider = this.divider;
},
render: function() {
var value = this.model.get(this.column.get('name'));
this.$el.empty();
this.$el.text(this.formatter.fromRaw(value));
this.delegateEvents();
return this;
}
});
var MicrodollarCell = Backgrid.Extension.MicrodollarCell = Backgrid.NumberCell.extend({
className: 'microdollar-cell',
renderNullAsZero: false,
decimals: 2,
decimalSeparator: '.',
orderSeparator: ',',
valueOverride: null,
formatter: Backgrid.Extension.MicrodollarFormatter,
render: function() {
var value = this.valueOverride ? this.valueOverride() : this.model.get(this.column.get('name'));
this.$el.empty();
if (!_.isNull(value) || this.renderNullAsZero) {
this.$el.text(this.formatter.fromRaw(value));
}
this.delegateEvents();
return this;
}
});
var SCBudgetCell = Backgrid.Extension.SCBudgetCell = Backgrid.Extension.SCInlineEditCell.extend({
formatter: Backgrid.Extension.SCBudgetFormatter,
form: Handlebars.templates.SCBudgetCell,
prepareForm: function() {
var attribute = 'lifetime_budget';
var value = this.model.get(attribute) / 100.0;
var formattedValue;
if (value === 0) {
attribute = 'daily_budget';
value = this.model.get(attribute) / 100.0;
} else {
this.$('input').data('attribute', 'lifetime_budget');
}
formattedValue = accounting.formatMoney(value, {
format: '%v'
});
this.$('input').data('attribute', attribute).val(formattedValue);
}
});
var SCAccountName = Backgrid.Extension.SCAccountName = Backgrid.StringCell.extend({
render: function() {
var $name = AdvisorApp.module('Common.Language').en.getAccountNameHTML(this.model.toJSON());
this.$el.html($name);
return this;
}
});
/**
* Display avatar followed by either full name, or email (if no name)
*/
var SCUserName = Backgrid.Extension.SCUserName = Backgrid.StringCell.extend({
className: 'sc-user-name-cell',
render: function() {
var userNameHtml = AdvisorApp.module('Common.Language').en.getUserNameHTML(this.model.toJSON());
this.$el.html(userNameHtml);
return this;
}
});
var SCPixelName = Backgrid.Extension.SCPixelName = Backgrid.StringCell.extend({
render: function() {
var $name = AdvisorApp.module('Common.Language').en.getPixelNameHTML(this.model.toJSON());
this.$el.html($name);
return this;
}
});
/**
* Display asset profile picture followed by its name.
* Links to the asset in a new window.
*/
var SCAssetName = Backgrid.Extension.SCAssetName = Backgrid.StringCell.extend({
render: function() {
var model = this.model.toJSON();
var $link = $('<a/>', {
'href': model.url,
'html': AdvisorApp.module('Common.Language').en.getAssetNameHTML(model),
'target': '_blank'
});
this.$el.html($link);
this.delegateEvents();
return this;
}
});
// OLD
var SCResultsCell = Backgrid.Extension.SCResultsCell = Backgrid.StringCell.extend({
className: 'sc-results-cell number-cell',
numberFormatter: new Backgrid.NumberFormatter({
decimals: 0
}),
render: function() {
var value = this.model.get('objective');
var label = 'Events';
var cellHTML = this.numberFormatter.fromRaw(this.model.get('kpi_primary_events'));
cellHTML += ' <br /> <small>' + label + '</small>';
this.$el.html(cellHTML);
this.delegateEvents();
return this;
}
});
var SCObjectiveCell = Backgrid.Extension.SCObjectiveCell = Backgrid.StringCell.extend({
className: 'sc-objective-cell number-cell',
render: function() {
var value = this.model.get('objective');
var label = 'Per Event';
var cellHTML = accounting.formatMoney(this.model.get('kpi_primary'));
cellHTML += ' <br /> <small>' + label + '</small>';
this.$el.html(cellHTML);
this.delegateEvents();
return this;
}
});
var SCObjectiveTextCell = Backgrid.Extension.SCObjectiveTextCell = Backgrid.StringCell.extend({
className: 'string-cell-left',
render: function() {
var key = this.model.get('lineitem__payout_action');
var objective = key ? AdvisorApp.module('Common.Language').en.objective_classic[key.toUpperCase()] : '';
this.$el.html(objective);
this.delegateEvents();
return this;
}
});
var SCBidCell = Backgrid.Extension.SCBidCell = Backgrid.StringCell.extend({
className: 'sc-bid-cell number-cell',
// TODO: Seems like we should store this mapping somewhere else.
bidTypeLabels: {
'CPC': 'CPC',
'CPM': 'CPM',
'CPA': 'CPA',
'ABSOLUTE_OCPM': 'oCPM',
'MULTI_PREMIUM': 'Premium'
},
bidInfoMap: {
'CPC': 'CLICKS',
'CPM': 'IMPRESSIONS',
'CPA': 'ACTIONS'
},
render: function() {
var bidType = this.model.get('bid_type');
var label = this.bidTypeLabels[bidType];
var bidInfo = JSON.parse(this.model.get('bid_info'));
var key;
var value;
var cellHTML;
switch (bidType) {
case 'ABSOLUTE_OCPM':
case 'MULTI_PREMIUM':
var bidValue = 0;
var bidLabel = '';
$.each(bidInfo, function(key, value) {
if (value > bidValue) {
bidValue = value;
bidLabel = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
}
});
label += ' ' + bidLabel;
cellHTML = accounting.formatMoney(bidValue / 100.0);
break;
default: // CPC, CPM, CPA
key = this.bidInfoMap[bidType];
value = bidInfo[key];
cellHTML = accounting.formatMoney(value / 100.0);
}
cellHTML += ' <br /><small>' + label + '</small>';
this.$el.html(cellHTML);
this.delegateEvents();
return this;
}
});
var EndDateFormatter = Backgrid.Extension.EndDateFormatter = function(options) {
_.extend(this, this.defaults, options || {});
};
EndDateFormatter.prototype = new Backgrid.StringFormatter();
_.extend(EndDateFormatter.prototype, {
// Takes attribute value and formats for display to user
fromRaw: function(value, model) {
var dateTime = moment(value).endOf('day');
var daysLeft;
var label;
var hoursLeft;
if (value && dateTime.isValid()) {
// Since end date is supposed to be inclusive (e.g. an end date of 2015-12-31 means that
// it goes until the END of the 31st) we handle this by looking at the endOf day
daysLeft = dateTime.diff(moment(), 'days', true);
if (daysLeft < 1) {
hoursLeft = moment.duration(daysLeft, 'd').asHours();
label = hoursLeft < 0 ? 'Completed' : parseInt(hoursLeft, 10) + ' hours';
} else if (daysLeft < 2) {
label = parseInt(daysLeft, 10) + ' day';
} else {
label = parseInt(daysLeft, 10) + ' days';
}
return label + dateTime.format(' <br /> <\\s\\m\\a\\l\\l>MM/DD/YYYY</\\s\\m\\a\\l\\l>');
}
return '<small>N/A</small>';
}
});
var SCNumberHeaderCell = Backgrid.Extension.SCNumberHeaderCell = Backgrid.HeaderCell.extend({
onClick: function(e) {
e.preventDefault();
var column = this.column;
var collection = this.collection;
var event = 'backgrid:sort';
var sortable;
var sortType;
var cycleSort;
var toggleSort;
cycleSort = function(header, col) {
if (column.get('direction') === 'ascending') {
collection.trigger(event, col, 'descending');
} else if (column.get('direction') === 'descending') {
collection.trigger(event, col, null);
} else {
collection.trigger(event, col, 'descending');
}
};
toggleSort = function(header, col) {
if (column.get('direction') === 'descending') {
collection.trigger(event, col, 'ascending');
} else {
collection.trigger(event, col, 'descending');
}
};
sortable = Backgrid.callByNeed(column.sortable(), column, this.collection);
if (sortable) {
sortType = column.get('sortType');
if (sortType === 'toggle') {
toggleSort(this, column);
} else {
cycleSort(this, column);
}
}
}
});
var SCCurrencyCell = Backgrid.Extension.SCCurrencyCell = Backgrid.NumberCell.extend({
formatter: SCCurrencyFormatter
});
var CurrencyInCentsFormatter = _.extend({}, Backgrid.NumberFormatter.prototype, {
fromRaw: function(rawValue, model) {
if (!Helpers.valueIsValid(rawValue)) {
return '-';
}
rawValue = rawValue / 100;
return accounting.formatMoney(accounting.toFixed(rawValue, 2), {
precision: 0
});
}
});
var SCCurrencyInCentsCell = Backgrid.Extension.SCCurrencyInCentsCell = Backgrid.NumberCell.extend({
formatter: CurrencyInCentsFormatter
});
var ActionNumberFormatter = function() {
Backgrid.NumberFormatter.apply(this, arguments);
};
ActionNumberFormatter.prototype = _.extend({}, Backgrid.NumberFormatter.prototype, {
fromRaw: function(rawValue) {
if (!Helpers.valueIsValid(rawValue)) {
return '-';
}
this.decimals = 0;
rawValue = parseInt(Math.round(rawValue));
return Backgrid.NumberFormatter.prototype.fromRaw.call(this, rawValue);
}
});
var SCActionNumberCell = Backgrid.Extension.SCActionNumberCell = Backgrid.NumberCell.extend({
formatter: ActionNumberFormatter
});
var SCReachFormatter = function() {
Backgrid.NumberFormatter.apply(this, arguments);
};
SCReachFormatter.prototype = _.extend({}, Backgrid.NumberFormatter.prototype, {
fromRaw: function(rawValue) {
if (!Helpers.valueIsValid(rawValue)) {
return '-';
}
this.decimals = 0;
rawValue = parseInt(Math.round(rawValue));
return Backgrid.NumberFormatter.prototype.fromRaw.call(this, rawValue);
}
});
var SCReachCell = Backgrid.Extension.SCReachCell = Backgrid.NumberCell.extend({
formatter: SCReachFormatter
});
var SCFrequencyFormatter = function() {
Backgrid.NumberFormatter.apply(this, arguments);
};
SCFrequencyFormatter.prototype = _.extend({}, Backgrid.NumberFormatter.prototype, {
fromRaw: function(rawValue) {
var model_attributes = this.fromRaw.arguments[1].attributes;
var reach = model_attributes.reach;
if (!Helpers.valueIsValid(rawValue)) {
return 'Unavailable';
}
if (model_attributes.impressions === 0) {
return 0;
}
return Backgrid.NumberFormatter.prototype.fromRaw.call(this, rawValue);
}
});
var SCFrequencyCell = Backgrid.Extension.SCFrequencyCell = Backgrid.NumberCell.extend({
formatter: SCFrequencyFormatter
});
var DecimalFormatter = function() {
Backgrid.NumberFormatter.apply(this, arguments);
};
DecimalFormatter.prototype = _.extend({}, Backgrid.NumberFormatter.prototype, {
fromRaw: function(rawValue) {
if (!Helpers.valueIsValid(rawValue)) {
return '-';
}
return Backgrid.NumberFormatter.prototype.fromRaw.call(this, rawValue);
}
});
var SCDecimalCell = Backgrid.Extension.SCDecimalCell = Backgrid.NumberCell.extend({
formatter: DecimalFormatter
});
var ThreeDecimalPercentFormatter = function() {
Backgrid.NumberFormatter.apply(this, arguments);
};
ThreeDecimalPercentFormatter.prototype = _.extend({}, Backgrid.NumberFormatter.prototype, {
fromRaw: function(rawValue) {
if (!Helpers.valueIsValid(rawValue)) {
return '-';
}
this.decimals = 3;
return Backgrid.NumberFormatter.prototype.fromRaw.call(this, rawValue) + '%';
}
});
var SCThreeDecimalPercentCell = Backgrid.Extension.SCThreeDecimalPercentCell = Backgrid.NumberCell.extend({
formatter: ThreeDecimalPercentFormatter
});
var SCKPIResultsCell = Backgrid.Extension.SCKPIResultsCell = Backgrid.IntegerCell.extend({
formatter: Backgrid.NumberFormatter,
// TODO: DRY up with other render methods.
render: function() {
var cellHTML = this.formatter.fromRaw(this.model.get('kpi_primary_events'));
var label = AdvisorApp.module('Common.Language').en.results[this.model.get('objective')];
if (label) {
cellHTML += '<br /><small class="text-muted">' + label + '</small>';
}
this.$el.html(cellHTML);
this.delegateEvents();
return this;
}
});
var SCKPICell = Backgrid.Extension.SCKPICell = Backgrid.IntegerCell.extend({
formatter: CurrencyFormatter,
// TODO: DRY up with other render methods.
render: function() {
var cellHTML = accounting.formatMoney(this.model.get('kpi'));
var label = AdvisorApp.module('Common.Language').en.kpi[this.model.get('objective')];
if (label) {
cellHTML += '<br /><small class="text-muted">' + label + '</small>';
}
this.$el.html(cellHTML);
this.delegateEvents();
return this;
}
});
var SCAnalyticsDateCell = Backgrid.Extension.SCAnalyticsDateCell = Backgrid.DateCell.extend({
className: 'string-cell-left',
formatter: Backgrid.DatetimeFormatter,
render: function() {
var date = this.formatter.fromRaw(this.model.get('date_start'));
this.$el.html(moment(date).format('MM/DD/YY'));
this.delegateEvents();
return this;
}
});
var SCSelectRowCell = Backgrid.Extension.SCSelectRowCell = Backgrid.Extension.SelectRowCell.extend({
initialize: function(options) {
this.listenTo(this.model, 'advisor:audience-removed', function(model) {
this.$el.html('<i class="fa fa-plus-square fa-2x text-danger"></i>');
this.delegateEvents();
return this;
});
this.listenTo(this.model, 'advisor:model-selected', function(model) {
this.$el.html('<i class="fa fa-check-circle-o fa-2x text-success"></i>');
this.delegateEvents();
return this;
});
},
className: 'sc-select-plus-cell',
events: {
'click .fa-plus-square': 'selectRow'
},
selectRow: function() {
this.model.trigger('advisor:audience-selected', this.model, true);
this.$el.html('<i class="fa fa-check-circle-o fa-2x text-success"></i>');
this.delegateEvents();
return this;
},
render: function() {
this.$el.html('<i class="fa fa-plus-square fa-2x text-danger"></i>');
this.delegateEvents();
return this;
}
});
var SCAudienceSizeCell = Backgrid.Extension.SCAudienceSizeCell = Backgrid.IntegerCell.extend({
render: function() {
var cellHTML = numeral(this.model.get('size')).format('0.[000]a');
var updated = moment(this.model.get('updated_date')).format('MM/DD/YY');
if (updated) {
cellHTML += ' <br /> <small class="text-muted">' + updated + '</small>';
}
this.$el.html(cellHTML);
this.delegateEvents();
return this;
}
});
Backgrid.Extension.WrappedTextCell = Backgrid.Cell.extend({
render: function() {
var value = this.model.get(this.column.get('name'));
this.$el.css('white-space', 'normal').html(this.formatter.fromRaw(value));
this.delegateEvents();
return this;
}
});
var SCTotalSpendCell = Backgrid.Extension.SCTotalSpendCell = Backgrid.Extension.CurrencyCell.extend({
template: Handlebars.templates.SCTotalSpendCell,
className: 'sc-pacing-cell currency-cell',
render: function() {
this.$el.html(this.template(Helpers.getPacingOptions(this.model)));
this.delegateEvents();
return this;
}
});
var SCMicroObjectiveCell = Backgrid.Extension.SCMicroObjectiveCell = Backgrid.Extension.MicrodollarCell.extend({
className: 'sc-objective-cell number-cell',
render: function() {
var label = this.model.get('funding_instrument_id') ? 'Per Engagement' : 'Per Event';
var cellHTML = this.formatter.fromRaw(this.model.get('kpi_primary'));
cellHTML += ' <br /> <small>' + label + '</small>';
this.$el.html(cellHTML);
this.delegateEvents();
return this;
}
});
})();
---------- diff:
entities/advisor/account.js
- AdvisorApp.module('Entities', function() {
- /**
- * Model and Collection definitions
- */
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var Account = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/account/'
- });
- var AccountCollection = Backbone.Collection.extend({
- model: Account,
- url: Account.prototype.urlRoot
- });
+ /* module definition */
+ var Account = {};
- var AccountPageableCollection = Backbone.TastypiePageableCollection.extend({
- model: Account,
- url: Account.prototype.urlRoot
- });
+ var Account = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/account/'
+ });
+ var AccountCollection = Backbone.Collection.extend({
+ model: Account,
+ url: Account.prototype.urlRoot
+ });
+ var AccountPageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: Account,
+ url: Account.prototype.urlRoot
+ });
+ var getAccount = _(function(id) {
+ return this.fetchEntity(Account, id);
+ }).bind(this);
+ var getAccountCollection = _(function(options) {
+ return this.fetchEntities(AccountCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:account:entity', function(id) { return getAccount(id); }],
+ ['advisor:account:entities', function(options) { return getAccountCollection(options); }],
+ ['advisor:account:Entity', function() { return Account; }],
+ ['advisor:account:Entities', function() { return AccountCollection; }],
+ ['advisor:account:PageableEntities', function() { return AccountPageableCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Fetch account
- * @return {$.Promise}
- */
- var getAccount = _(function(id) {
- return this.fetchEntity(Account, id);
- }).bind(this);
- /**
- * Fetch account collection
- * @return {$.Promise}
- */
- var getAccountCollection = _(function(options) {
- return this.fetchEntities(AccountCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:account:entity', function(id) { return getAccount(id); }],
- ['advisor:account:entities', function(options) { return getAccountCollection(options); }],
- ['advisor:account:Entity', function() { return Account; }],
- ['advisor:account:Entities', function() { return AccountCollection; }],
- ['advisor:account:PageableEntities', function() { return AccountPageableCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Account;
---------- diff:
entities/advisor/activity.js
- AdvisorApp.module('Entities', function(Entities, App) {
- _.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection definitions.
- */
- var ActivityCollection = Backbone.Collection.extend({
- url: function() {
- return '/api/advisor/v1/serviceactivity/' + this.campaignId + '/all/';
- },
- initialize: function(options) {
- this.campaignId = options.campaignId;
- }
- });
+ /* module definition */
+ var Entities = {};
- /**
- * Fetch account collection
- * @return {$.Promise}
- */
- var getActivityEntities = _(function(options) {
- return this.fetchEntities(ActivityCollection, options);
- }).bind(this);
+ _.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
+ var ActivityCollection = Backbone.Collection.extend({
+ url: function() {
+ return '/api/advisor/v1/serviceactivity/' + this.campaignId + '/all/';
+ },
+ initialize: function(options) {
+ this.campaignId = options.campaignId;
+ }
+ });
+ var getActivityEntities = _(function(options) {
+ return this.fetchEntities(ActivityCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:activity:entities', function(options) { return getActivityEntities(options); }],
+ ['advisor:activity:Entities', function() { return ActivityCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Handler Definitions
- */
- var handlers = [
- ['advisor:activity:entities', function(options) { return getActivityEntities(options); }],
- ['advisor:activity:Entities', function() { return ActivityCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/advisor/asset.js
- AdvisorApp.module('Entities', function() {
- /**
- * Model and Collection definitions
- */
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var Asset = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/asset/'
- });
- var AssetCollection = Backbone.Collection.extend({
- model: Asset,
- url: Asset.prototype.urlRoot
- });
+ /* module definition */
+ var Asset = {};
- var AssetPageableCollection = Backbone.TastypiePageableCollection.extend({
- model: Asset,
- url: Asset.prototype.urlRoot
- });
+ var Asset = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/asset/'
+ });
+ var AssetCollection = Backbone.Collection.extend({
+ model: Asset,
+ url: Asset.prototype.urlRoot
+ });
+ var AssetPageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: Asset,
+ url: Asset.prototype.urlRoot
+ });
+ var getAsset = _(function(id) {
+ return this.fetchEntity(Asset, id);
+ }).bind(this);
+ var getAssetCollection = _(function(options) {
+ return this.fetchEntities(AssetCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:asset:entity', function(id) { return getAsset(id); }],
+ ['advisor:asset:entities', function(options) { return getAssetCollection(options); }],
+ ['advisor:asset:Entity', function() { return Asset; }],
+ ['advisor:asset:Entities', function() { return AssetCollection; }],
+ ['advisor:asset:PageableEntities', function() { return AssetPageableCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Fetch asset
- * @return {$.Promise}
- */
- var getAsset = _(function(id) {
- return this.fetchEntity(Asset, id);
- }).bind(this);
- /**
- * Fetch asset collection
- * @return {$.Promise}
- */
- var getAssetCollection = _(function(options) {
- return this.fetchEntities(AssetCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:asset:entity', function(id) { return getAsset(id); }],
- ['advisor:asset:entities', function(options) { return getAssetCollection(options); }],
- ['advisor:asset:Entity', function() { return Asset; }],
- ['advisor:asset:Entities', function() { return AssetCollection; }],
- ['advisor:asset:PageableEntities', function() { return AssetPageableCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Asset;
---------- diff:
entities/advisor/campaign.js
- AdvisorApp.module('Entities', function(Entities, App) {
- _.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection configurations.
- */
- /**
- * Model and Collection definitions.
+ /* module definition */
+ var Entities = {};
+ _.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
+ var Campaign = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/campaign/',
+ getStatusActions: function() {
+ var allActions = [
+ {status: 'LIVE', label: 'Pause', value: 'pause'},
+ {status: 'PAUSED', label: 'Resume', value: 'activate'}
+ ];
+ return _.where(allActions, {status: this.get('status')});
+ },
+ isStatusEditable: function() {
+ var status = this.get('status');
+ return (status === 'LIVE' || status === 'PAUSED');
+ },
+ userCanManage: function() {
+ var userHasPermissions = AdvisorApp.request(
+ 'session:grant:entities:can_manage_brand_or_initiative', this.get('brand_id'), this.get('initiative_id')
+ );
+ return userHasPermissions && this.isStatusEditable();
+ },
+ getEditableCells: function() {
+ return ['status'];
+ },
+ /**
+ * Simplify getting a lowercase version of the platform name
+ * @returns {string}
*/
- var Campaign = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/campaign/',
- getStatusActions: function() {
- var allActions = [
- {status: 'LIVE', label: 'Pause', value: 'pause'},
- {status: 'PAUSED', label: 'Resume', value: 'activate'}
- ];
- return _.where(allActions, {status: this.get('status')});
- },
- isStatusEditable: function() {
- var status = this.get('status');
- return (status === 'LIVE' || status === 'PAUSED');
- },
- userCanManage: function() {
- var userHasPermissions = AdvisorApp.request(
- 'session:grant:entities:can_manage_brand_or_initiative', this.get('brand_id'), this.get('initiative_id')
- );
- return userHasPermissions && this.isStatusEditable();
- },
- getEditableCells: function() {
- return ['status'];
- },
- /**
- * Simplify getting a lowercase version of the platform name
- * @returns {string}
- */
- getPlatform: function() {
- return this.get('platform').toLowerCase();
- },
- getEffectivePlatform: function() {
- var platform = this.get('platform') ? this.get('platform') : null;
- var subplatform = this.get('subplatform') ? this.get('subplatform') : null;
- var effectivePlatform = subplatform ? subplatform : platform;
- if (effectivePlatform) {
- effectivePlatform = effectivePlatform.toLowerCase();
- }
- return effectivePlatform;
- },
- /**
- * convert act_12345 -> 12345
- * @returns {string}
- */
- getPlatformAccountId: function() {
- return this.get('platform_account_id').substring(4);
- },
- validation: function() {
- return {
- platform_id: _(function(value) {
- var error;
- if (!value) {
- return 'Platform is a required field';
- }
- if (value === 'twitter' && !this.get('fi_id')) {
- return 'Funding Instrument is required for Twitter campaigns. ' +
- 'Please select an account and an associated funding instrument';
- }
- }).bind(this),
- start_date: _(function(value) {
- var start_date = new Date(value);
- var earliest_date = this.get('initiative').start_date;
- if (!value) {
- return 'Start Date is a required field';
- }
- if (start_date > new Date(this.get('end_date'))) {
- return 'End Date cannot be before Start Date';
- }
- if (start_date < new Date(earliest_date)) {
- return 'Start Date cannot be before ' + earliest_date;
- }
- }).bind(this),
- end_date: _(function(value) {
- var end_date = new Date(value);
- var latest_date = this.get('initiative').latest_date;
- if (!value) {
- return 'End Date is a required field';
- }
- if (end_date > new Date(latest_date)) {
- return 'End Date cannot be after ' + latest_date;
- }
- }).bind(this),
- budget_amount: _(function(value) {
- var max_budget = this.get('max_budget');
- if (!value) {
- return 'Budget Amount is a required field';
- }
- if (parseFloat(value) > max_budget) {
- return 'Budget Amount cannot be more than $' + max_budget;
- }
- }).bind(this),
- goal_value: _(function(value) {
- var goal_ceiling = this.get('goal_ceiling');
- if (!value) {
- return 'Goal Value is a required field';
- }
- if (parseFloat(value) > parseFloat(goal_ceiling)) {
- return 'Goal value cannot be more than goal ceiling ($' + goal_ceiling + ')';
- }
- }).bind(this)
- };
+ getPlatform: function() {
+ return this.get('platform').toLowerCase();
+ },
+ getEffectivePlatform: function() {
+ var platform = this.get('platform') ? this.get('platform') : null;
+ var subplatform = this.get('subplatform') ? this.get('subplatform') : null;
+ var effectivePlatform = subplatform ? subplatform : platform;
+ if (effectivePlatform) {
+ effectivePlatform = effectivePlatform.toLowerCase();
}
- });
- var CampaignCollection = Backbone.Collection.extend({
- model: Campaign,
- url: Campaign.prototype.urlRoot
- });
- var CampaignPageableCollection = Backbone.TastypiePageableCollection.extend({
- model: Campaign,
- url: Campaign.prototype.urlRoot
- });
- /**
- * Get Entit(y/ies) methods
+ return effectivePlatform;
+ },
+ /**
+ * convert act_12345 -> 12345
+ * @returns {string}
*/
+ getPlatformAccountId: function() {
+ return this.get('platform_account_id').substring(4);
+ },
+ validation: function() {
+ return {
+ platform_id: _(function(value) {
+ var error;
+ if (!value) {
+ return 'Platform is a required field';
+ }
+ if (value === 'twitter' && !this.get('fi_id')) {
+ return 'Funding Instrument is required for Twitter campaigns. ' +
+ 'Please select an account and an associated funding instrument';
+ }
+ }).bind(this),
+ start_date: _(function(value) {
+ var start_date = new Date(value);
+ var earliest_date = this.get('initiative').start_date;
+ if (!value) {
+ return 'Start Date is a required field';
+ }
+ if (start_date > new Date(this.get('end_date'))) {
+ return 'End Date cannot be before Start Date';
+ }
+ if (start_date < new Date(earliest_date)) {
+ return 'Start Date cannot be before ' + earliest_date;
+ }
+ }).bind(this),
+ end_date: _(function(value) {
+ var end_date = new Date(value);
+ var latest_date = this.get('initiative').latest_date;
+ if (!value) {
+ return 'End Date is a required field';
+ }
+ if (end_date > new Date(latest_date)) {
+ return 'End Date cannot be after ' + latest_date;
+ }
+ }).bind(this),
+ budget_amount: _(function(value) {
+ var max_budget = this.get('max_budget');
+ if (!value) {
+ return 'Budget Amount is a required field';
+ }
+ if (parseFloat(value) > max_budget) {
+ return 'Budget Amount cannot be more than $' + max_budget;
+ }
+ }).bind(this),
+ goal_value: _(function(value) {
+ var goal_ceiling = this.get('goal_ceiling');
+ if (!value) {
+ return 'Goal Value is a required field';
+ }
+ if (parseFloat(value) > parseFloat(goal_ceiling)) {
+ return 'Goal value cannot be more than goal ceiling ($' + goal_ceiling + ')';
+ }
+ }).bind(this)
+ };
+ }
+ });
+ var CampaignCollection = Backbone.Collection.extend({
+ model: Campaign,
+ url: Campaign.prototype.urlRoot
+ });
+ var CampaignPageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: Campaign,
+ url: Campaign.prototype.urlRoot
+ });
+ var getCampaign = _(function(id) {
+ return this.fetchEntity(Campaign, id, true);
+ }).bind(this);
+ var getCampaignCollection = _(function(options) {
+ return this.fetchEntities(CampaignCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:campaign:entity', function(id) { return getCampaign(id); }],
+ ['advisor:campaign:entities', function(options) { return getCampaignCollection(options); }],
+ ['advisor:campaign:Entity', function() { return Campaign; }],
+ ['advisor:campaign:Entities', function() { return CampaignCollection; }],
+ ['advisor:campaign:PageableEntities', function() { return CampaignPageableCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
+ Entities.Campaign = Campaign;
- /**
- * Fetch account
- * @return {$.Promise}
- */
- var getCampaign = _(function(id) {
- return this.fetchEntity(Campaign, id, true);
- }).bind(this);
- /**
- * Fetch account collection
- * @return {$.Promise}
- */
- var getCampaignCollection = _(function(options) {
- return this.fetchEntities(CampaignCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:campaign:entity', function(id) { return getCampaign(id); }],
- ['advisor:campaign:entities', function(options) { return getCampaignCollection(options); }],
- ['advisor:campaign:Entity', function() { return Campaign; }],
- ['advisor:campaign:Entities', function() { return CampaignCollection; }],
- ['advisor:campaign:PageableEntities', function() { return CampaignPageableCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- Entities.Campaign = Campaign;
- });
+ module.exports = Entities;
---------- diff:
entities/advisor/creative.js
- AdvisorApp.module('Entities', function(Module, App) {
- /**
- * Model and Collection definitions
- */
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var Creative = Module.Creative = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/creative/',
- initialize: function() {
- var selectable = new Backbone.Picky.Selectable(this);
- _.extend(this, selectable);
- this.on('selected deselected', function() {
- this.set('_selected', this.selected);
- });
- this.on('change:_selected', function(e) {
- if (this.get('_selected')) {
- this.select();
- } else {
- this.deselect();
- }
- });
- }
- });
+ /* module definition */
+ var Module = {};
- var CreativeCollection = Module.Creatives = Backbone.Collection.extend({
- model: Creative,
- url: Creative.prototype.urlRoot,
- initialize: function() {
- var multiSelect = new Backbone.Picky.MultiSelect(this);
- _.extend(this, multiSelect);
- }
- });
+ var Creative = Module.Creative = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/creative/',
+ initialize: function() {
+ var selectable = new Backbone.Picky.Selectable(this);
+ _.extend(this, selectable);
+ this.on('selected deselected', function() {
+ this.set('_selected', this.selected);
+ });
+ this.on('change:_selected', function(e) {
+ if (this.get('_selected')) {
+ this.select();
+ } else {
+ this.deselect();
+ }
+ });
+ }
+ });
+ var CreativeCollection = Module.Creatives = Backbone.Collection.extend({
+ model: Creative,
+ url: Creative.prototype.urlRoot,
+ initialize: function() {
+ var multiSelect = new Backbone.Picky.MultiSelect(this);
+ _.extend(this, multiSelect);
+ }
+ });
+ var getCreative = _(function(id) {
+ return this.fetchEntity(Creative, id);
+ }).bind(this);
+ var getCreativeCollection = _(function(options) {
+ return this.fetchEntities(CreativeCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:creative:entity', function(id) { return getCreative(id); }],
+ ['advisor:creative:entities', function(options) { return getCreativeCollection(options); }],
+ ['advisor:creative:Entity', function() { return Creative; }],
+ ['advisor:creative:Entities', function() { return CreativeCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
+ Module.GetCreativeFor = function(platform) {
+ switch (platform) {
+ case 'facebook': return Module.Facebook.AdCreative;
+ }
+ throw new Error(platform + ' does not have an AdCreative model');
+ };
+ Module.RefreshCreatives = function(platform, user, count) {
+ switch (platform) {
+ // This is currently done via the client since advisor-serivce has
+ // no notion of how many creatives are created, so we'd have
+ // to force a recrawl after each upload. This allows us to
+ // do a single force recrawl call.
+ // Perhaps some day we can allow for batch creative creation in
+ // the backend and move this request there.
+ case 'facebook': return $.get('/api/facebook/analytics/v1/facebookpage/' + user + '/sync-page-posts/?fetch-count=' + count);
+ // Else, just return deferred and resolve it
+ default:
+ var deferred = $.Deferred();
+ _.defer(deferred.resolve);
+ return deferred;
+ }
+ };
- /**
- * Fetch account
- * @return {$.Promise}
- */
- var getCreative = _(function(id) {
- return this.fetchEntity(Creative, id);
- }).bind(this);
- /**
- * Fetch account collection
- * @return {$.Promise}
- */
- var getCreativeCollection = _(function(options) {
- return this.fetchEntities(CreativeCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:creative:entity', function(id) { return getCreative(id); }],
- ['advisor:creative:entities', function(options) { return getCreativeCollection(options); }],
- ['advisor:creative:Entity', function() { return Creative; }],
- ['advisor:creative:Entities', function() { return CreativeCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- /**
- * @param {string} platform
- * @returns {Backbone.Model}
- */
- Module.GetCreativeFor = function(platform) {
- switch (platform) {
- case 'facebook': return Module.Facebook.AdCreative;
- }
- throw new Error(platform + ' does not have an AdCreative model');
- };
- /**
- * @param {string} platform
- * @param {string} user
- * @param {number} count
- * @returns {jQuery.Deferred}
- */
- Module.RefreshCreatives = function(platform, user, count) {
- switch (platform) {
- // This is currently done via the client since advisor-serivce has
- // no notion of how many creatives are created, so we'd have
- // to force a recrawl after each upload. This allows us to
- // do a single force recrawl call.
- // Perhaps some day we can allow for batch creative creation in
- // the backend and move this request there.
- case 'facebook': return $.get('/api/facebook/analytics/v1/facebookpage/' + user + '/sync-page-posts/?fetch-count=' + count);
- // Else, just return deferred and resolve it
- default:
- var deferred = $.Deferred();
- _.defer(deferred.resolve);
- return deferred;
- }
- };
- });
+ module.exports = Module;
---------- diff:
entities/advisor/demographicset.js
- AdvisorApp.module('Entities', function() {
- /**
- * Model and Collection definitions
- */
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var DemographicSet = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/demographicset/',
- // Translate from server response to a fresh object
- fromDemographicSet: function(demoSet) {
- this.set('gender', demoSet.get('gender'));
- this.refreshGenderSelection();
+ /* module definition */
+ var Demographicset = {};
- var age_ranges = _.map(demoSet.get('age_ranges'), function(obj) {
- return _.pick(obj, 'max_age', 'min_age');
+ var DemographicSet = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/demographicset/',
+ // Translate from server response to a fresh object
+ fromDemographicSet: function(demoSet) {
+ this.set('gender', demoSet.get('gender'));
+ this.refreshGenderSelection();
+ var age_ranges = _.map(demoSet.get('age_ranges'), function(obj) {
+ return _.pick(obj, 'max_age', 'min_age');
+ });
+ this.set('age_ranges', new Backbone.Collection(age_ranges));
+ var location_sets = _.map(demoSet.get('location_sets'), function(obj) {
+ return new Backbone.Model({
+ locations: new Backbone.Collection(obj.locations)
});
- this.set('age_ranges', new Backbone.Collection(age_ranges));
- var location_sets = _.map(demoSet.get('location_sets'), function(obj) {
- return new Backbone.Model({
- locations: new Backbone.Collection(obj.locations)
- });
- });
- this.set('location_sets', new Backbone.Collection(location_sets));
- this.set('placement', demoSet.get('placement'));
- },
- refreshGenderSelection: function() {
- switch (this.get('gender')) {
- case 'all':
- this.set('gender_selection', ['male', 'female']);
- break;
- case 'male':
- this.set('gender_selection', ['male']);
- break;
- case 'female':
- this.set('gender_selection', ['female']);
- break;
- default:
- this.set('gender_selection', []);
- }
+ });
+ this.set('location_sets', new Backbone.Collection(location_sets));
+ this.set('placement', demoSet.get('placement'));
+ },
+ refreshGenderSelection: function() {
+ switch (this.get('gender')) {
+ case 'all':
+ this.set('gender_selection', ['male', 'female']);
+ break;
+ case 'male':
+ this.set('gender_selection', ['male']);
+ break;
+ case 'female':
+ this.set('gender_selection', ['female']);
+ break;
+ default:
+ this.set('gender_selection', []);
}
- });
+ }
+ });
+ var DemographicSetCollection = Backbone.Collection.extend({
+ model: DemographicSet,
+ url: DemographicSet.prototype.urlRoot
+ });
+ var getDemographicSet = _(function(id) {
+ return this.fetchEntity(DemographicSet, id);
+ }).bind(this);
+ var getDemographicSetCollection = _(function(options) {
+ return this.fetchEntities(DemographicSetCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:demographicset:Entity', function() { return DemographicSet; }],
+ ['advisor:demographicset:Entities', function() { return DemographicSetCollection; }],
+ ['advisor:demographicset:entity', function(id) { return getDemographicSet(id); }],
+ ['advisor:demographicset:entities', function(options) { return getDemographicSetCollection(options); }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- var DemographicSetCollection = Backbone.Collection.extend({
- model: DemographicSet,
- url: DemographicSet.prototype.urlRoot
- });
- /**
- * Fetch demographic set
- * @return {$.Promise}
- */
- var getDemographicSet = _(function(id) {
- return this.fetchEntity(DemographicSet, id);
- }).bind(this);
- /**
- * Fetch demographic set collection
- * @return {$.Promise}
- */
- var getDemographicSetCollection = _(function(options) {
- return this.fetchEntities(DemographicSetCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:demographicset:Entity', function() { return DemographicSet; }],
- ['advisor:demographicset:Entities', function() { return DemographicSetCollection; }],
- ['advisor:demographicset:entity', function(id) { return getDemographicSet(id); }],
- ['advisor:demographicset:entities', function(options) { return getDemographicSetCollection(options); }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Demographicset;
---------- diff:
entities/advisor/initiative.js
- AdvisorApp.module('Entities', function() {
- /**
- * Model and Collection definitions.
- */
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var Initiative = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/initiative/',
- validate: function(attrs) {
- // TODO: Change this to use moment's #isBefore or #isAfter methods.
- if (new Date(attrs.end_date) < new Date(attrs.start_date)) {
- return { end_date: 'End Date cannot be before Start Date' };
- }
- },
- setBrand: function() {
- var brandID = this.get('brand_id');
- var onDoneFetch = function(brand) {
- this.set('brand', brand);
- };
- AdvisorApp.request('brand:entity', brandID)
- .done(_.bind(onDoneFetch, this));
- return this;
- },
- setFollowers: function() {
- var followersPageableEntities = new FollowerPageableCollection([], {
- urlPrefix: this.urlRoot + this.id
- });
- this.set('followers', followersPageableEntities);
- return this;
- },
- setAssets: function() {
- var assetsPageableEntities = new (AdvisorApp.request('advisor:asset:PageableEntities'))();
- assetsPageableEntities.url = this.urlRoot + this.id + '/asset/';
- this.set('_assets', assetsPageableEntities);
- return this;
- },
- fetchAssets: function() {
- var defer = $.Deferred();
- var assets = this.get('_assets');
- assets.fetch({
- success: function(collection) {
- defer.resolve(collection);
- },
- error: function(collection, response) {
- defer.reject(response);
- }
- });
+ /* module definition */
+ var Initiative = {};
- return defer.promise();
- },
- initialize: function() {
- this
- .setAssets()
- .setFollowers();
- this.on('add change:brand_id', this.setBrand, this);
- },
- doPatch: function(attributes, defer) {
- this.save(attributes, {
- contentType: 'application/json',
- patch: true,
- wait: true,
- success: function(model) {
- defer.resolve(model);
- },
- error: function(model, response) {
- defer.reject(model, response);
- }
- });
- },
- /**
- * Workaround for backend PATCH race conditions
- * @param {Object} attributes - model attributes to patch
- * @return {$.Promise}
- */
- patch: function(attributes) {
- var defer = $.Deferred();
- var lastDefer = this._lastPatchDefer;
- if (lastDefer) {
- lastDefer.then(function(model) {
- model.doPatch(attributes, defer);
- });
- } else {
- this.doPatch(attributes, defer);
+ var Initiative = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/initiative/',
+ validate: function(attrs) {
+ // TODO: Change this to use moment's #isBefore or #isAfter methods.
+ if (new Date(attrs.end_date) < new Date(attrs.start_date)) {
+ return { end_date: 'End Date cannot be before Start Date' };
+ }
+ },
+ setBrand: function() {
+ var brandID = this.get('brand_id');
+ var onDoneFetch = function(brand) {
+ this.set('brand', brand);
+ };
+ AdvisorApp.request('brand:entity', brandID)
+ .done(_.bind(onDoneFetch, this));
+ return this;
+ },
+ setFollowers: function() {
+ var followersPageableEntities = new FollowerPageableCollection([], {
+ urlPrefix: this.urlRoot + this.id
+ });
+ this.set('followers', followersPageableEntities);
+ return this;
+ },
+ setAssets: function() {
+ var assetsPageableEntities = new (AdvisorApp.request('advisor:asset:PageableEntities'))();
+ assetsPageableEntities.url = this.urlRoot + this.id + '/asset/';
+ this.set('_assets', assetsPageableEntities);
+ return this;
+ },
+ fetchAssets: function() {
+ var defer = $.Deferred();
+ var assets = this.get('_assets');
+ assets.fetch({
+ success: function(collection) {
+ defer.resolve(collection);
+ },
+ error: function(collection, response) {
+ defer.reject(response);
}
- this._lastPatchDefer = defer;
- return defer.promise();
- }
- });
- var InitiativeCollection = Backbone.TastypiePageableCollection.extend({
- model: Initiative,
- url: Initiative.prototype.urlRoot
- });
- var InitiativePageableCollection = Backbone.TastypiePageableCollection.extend({
- model: Initiative,
- url: Initiative.prototype.urlRoot
- });
- var FollowerPageableCollection = Backbone.TastypiePageableCollection.extend({
- url: function() {
- return this.urlPrefix + '/follower/';
- },
- initialize: function(models, options) {
- this.urlPrefix = options.urlPrefix;
- }
- });
+ });
+ return defer.promise();
+ },
+ initialize: function() {
+ this
+ .setAssets()
+ .setFollowers();
+ this.on('add change:brand_id', this.setBrand, this);
+ },
+ doPatch: function(attributes, defer) {
+ this.save(attributes, {
+ contentType: 'application/json',
+ patch: true,
+ wait: true,
+ success: function(model) {
+ defer.resolve(model);
+ },
+ error: function(model, response) {
+ defer.reject(model, response);
+ }
+ });
+ },
/**
- * Fetch initiative
+ * Workaround for backend PATCH race conditions
+ * @param {Object} attributes - model attributes to patch
* @return {$.Promise}
*/
- var fetchInitiative = _(function(id) {
- return this.fetchEntity(Initiative, id, true);
- }).bind(this);
+ patch: function(attributes) {
+ var defer = $.Deferred();
+ var lastDefer = this._lastPatchDefer;
+ if (lastDefer) {
+ lastDefer.then(function(model) {
+ model.doPatch(attributes, defer);
+ });
+ } else {
+ this.doPatch(attributes, defer);
+ }
+ this._lastPatchDefer = defer;
+ return defer.promise();
+ }
+ });
+ var InitiativeCollection = Backbone.TastypiePageableCollection.extend({
+ model: Initiative,
+ url: Initiative.prototype.urlRoot
+ });
+ var InitiativePageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: Initiative,
+ url: Initiative.prototype.urlRoot
+ });
+ var FollowerPageableCollection = Backbone.TastypiePageableCollection.extend({
+ url: function() {
+ return this.urlPrefix + '/follower/';
+ },
+ initialize: function(models, options) {
+ this.urlPrefix = options.urlPrefix;
+ }
+ });
+ var fetchInitiative = _(function(id) {
+ return this.fetchEntity(Initiative, id, true);
+ }).bind(this);
+ var fetchInitiativeCollection = _(function(options) {
+ return this.fetchEntities(InitiativeCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:initiative:entity', function(id) { return fetchInitiative(id); }],
+ ['advisor:initiative:entities', function(options) { return fetchInitiativeCollection(options); }],
+ ['advisor:initiative:Entity', function() { return Initiative; }],
+ ['advisor:initiative:Entities', function() { return InitiativeCollection; }],
+ ['advisor:initiative:PageableEntities', function() { return InitiativePageableCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Fetch initiative collection
- * @return {$.Promise}
- */
- var fetchInitiativeCollection = _(function(options) {
- return this.fetchEntities(InitiativeCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:initiative:entity', function(id) { return fetchInitiative(id); }],
- ['advisor:initiative:entities', function(options) { return fetchInitiativeCollection(options); }],
- ['advisor:initiative:Entity', function() { return Initiative; }],
- ['advisor:initiative:Entities', function() { return InitiativeCollection; }],
- ['advisor:initiative:PageableEntities', function() { return InitiativePageableCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Initiative;
---------- diff:
entities/advisor/insertionorder.js
- AdvisorApp.module('Entities', function(Entities, App) {
- /**
- * Model and Collection definitions.
- */
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var moment = require('moment');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var InsertionOrder = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/insertionorder/',
- validate: function(attrs) {
- if (moment(attrs.end_date).isBefore(attrs.start_date)) {
- return { end_date: 'End Date cannot be before Start Date' };
- }
- },
- setBrand: function() {
- var brandAssociations = this.get('brand_associations');
- var brandIds = _.pluck(brandAssociations, 'brand_id');
- var onFetch = _.bind(function(brandEntities) {
- this.set('brands', brandEntities);
- this.trigger('init:brands');
- }, this);
+ /* module definition */
+ var Entities = {};
- if (brandIds.length) {
- var brandEntitiesOptions = {
- query: {
- 'id__in': brandIds.join(',')
- }
- };
- AdvisorApp.request('brand:entities', brandEntitiesOptions)
- .done(onFetch);
- } else {
- // No brand available, pass an empty collection for consistency
- this.set('brands', new (App.request('brand:Entities'))());
- this.trigger('init:brands');
- }
- },
- setFollowers: function() {
- var followersPageableEntities = new FollowerPageableCollection([], {
- urlPrefix: this.urlRoot + this.id
- });
- this.set('followers', followersPageableEntities);
- return this;
- },
- initialize: function() {
- _.bindAll(this, 'setBrand');
- this.on('sync change:brand_associations', this.setBrand);
- if (this.collection) {
- // Load brands on page load/switch
- this.collection.on('sync', this.setBrand);
- }
- this.setFollowers();
- },
- getMediaBudget: function(totalBudget, payoutPercentage) {
- totalBudget = totalBudget || this.get('total_budget') || 0;
- payoutPercentage = payoutPercentage || this.get('payout_percentage') || 0;
- return totalBudget / (1 + payoutPercentage / 100);
- },
- getRevenue: function(totalBudget, payoutPercentage) {
- totalBudget = totalBudget || this.get('total_budget') || 0;
- payoutPercentage = payoutPercentage || this.get('payout_percentage') || 0;
- return totalBudget - this.getMediaBudget(totalBudget, payoutPercentage);
- },
- getStatus: function() {
- return this.get('is_confirmed') ? 'approved' : 'pending';
- },
- loadOpportunityUri: function() {
- var opportunities = this.get('opportunities');
- // Populate Salesforce URI
- _.each(opportunities, function(opportunity) {
- opportunity.salesforceUri = 'https://na9.salesforce.com/' + opportunity.salesforce_id;
- });
- },
- doPatch: function(attributes, defer) {
- this.save(attributes, {
- contentType: 'application/json',
- patch: true,
- wait: true,
- success: function(model) {
- defer.resolve(model);
- },
- error: function(model, response) {
- defer.reject(model, response);
+ var InsertionOrder = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/insertionorder/',
+ validate: function(attrs) {
+ if (moment(attrs.end_date).isBefore(attrs.start_date)) {
+ return { end_date: 'End Date cannot be before Start Date' };
+ }
+ },
+ setBrand: function() {
+ var brandAssociations = this.get('brand_associations');
+ var brandIds = _.pluck(brandAssociations, 'brand_id');
+ var onFetch = _.bind(function(brandEntities) {
+ this.set('brands', brandEntities);
+ this.trigger('init:brands');
+ }, this);
+ if (brandIds.length) {
+ var brandEntitiesOptions = {
+ query: {
+ 'id__in': brandIds.join(',')
}
- });
- },
- /**
- * Workaround for backend PATCH race conditions
- * @param {Object} attributes - model attributes to patch
- * @return {$.Promise}
- */
- patch: function(attributes) {
- var defer = $.Deferred();
- var lastDefer = this._lastPatchDefer;
- if (lastDefer) {
- lastDefer.then(function(model) {
- model.doPatch(attributes, defer);
- });
- } else {
- this.doPatch(attributes, defer);
- }
- this._lastPatchDefer = defer;
- return defer.promise();
+ };
+ AdvisorApp.request('brand:entities', brandEntitiesOptions)
+ .done(onFetch);
+ } else {
+ // No brand available, pass an empty collection for consistency
+ this.set('brands', new (App.request('brand:Entities'))());
+ this.trigger('init:brands');
}
- });
- var InsertionOrderCollection = Backbone.TastypiePageableCollection.extend({
- model: InsertionOrder,
- url: InsertionOrder.prototype.urlRoot
- });
- var InsertionOrderPageableCollection = Backbone.TastypiePageableCollection.extend({
- model: InsertionOrder,
- url: InsertionOrder.prototype.urlRoot
- });
- var FollowerPageableCollection = Backbone.TastypiePageableCollection.extend({
- url: function() {
- return this.urlPrefix + '/follower/';
- },
- initialize: function(models, options) {
- this.urlPrefix = options.urlPrefix;
+ },
+ setFollowers: function() {
+ var followersPageableEntities = new FollowerPageableCollection([], {
+ urlPrefix: this.urlRoot + this.id
+ });
+ this.set('followers', followersPageableEntities);
+ return this;
+ },
+ initialize: function() {
+ _.bindAll(this, 'setBrand');
+ this.on('sync change:brand_associations', this.setBrand);
+ if (this.collection) {
+ // Load brands on page load/switch
+ this.collection.on('sync', this.setBrand);
}
- });
+ this.setFollowers();
+ },
+ getMediaBudget: function(totalBudget, payoutPercentage) {
+ totalBudget = totalBudget || this.get('total_budget') || 0;
+ payoutPercentage = payoutPercentage || this.get('payout_percentage') || 0;
+ return totalBudget / (1 + payoutPercentage / 100);
+ },
+ getRevenue: function(totalBudget, payoutPercentage) {
+ totalBudget = totalBudget || this.get('total_budget') || 0;
+ payoutPercentage = payoutPercentage || this.get('payout_percentage') || 0;
+ return totalBudget - this.getMediaBudget(totalBudget, payoutPercentage);
+ },
+ getStatus: function() {
+ return this.get('is_confirmed') ? 'approved' : 'pending';
+ },
+ loadOpportunityUri: function() {
+ var opportunities = this.get('opportunities');
+ // Populate Salesforce URI
+ _.each(opportunities, function(opportunity) {
+ opportunity.salesforceUri = 'https://na9.salesforce.com/' + opportunity.salesforce_id;
+ });
+ },
+ doPatch: function(attributes, defer) {
+ this.save(attributes, {
+ contentType: 'application/json',
+ patch: true,
+ wait: true,
+ success: function(model) {
+ defer.resolve(model);
+ },
+ error: function(model, response) {
+ defer.reject(model, response);
+ }
+ });
+ },
/**
- * Fetch insertionorder
+ * Workaround for backend PATCH race conditions
+ * @param {Object} attributes - model attributes to patch
* @return {$.Promise}
*/
- var fetchInsertionOrder = _(function(id) {
- return this.fetchEntity(InsertionOrder, id);
- }).bind(this);
+ patch: function(attributes) {
+ var defer = $.Deferred();
+ var lastDefer = this._lastPatchDefer;
+ if (lastDefer) {
+ lastDefer.then(function(model) {
+ model.doPatch(attributes, defer);
+ });
+ } else {
+ this.doPatch(attributes, defer);
+ }
+ this._lastPatchDefer = defer;
+ return defer.promise();
+ }
+ });
+ var InsertionOrderCollection = Backbone.TastypiePageableCollection.extend({
+ model: InsertionOrder,
+ url: InsertionOrder.prototype.urlRoot
+ });
+ var InsertionOrderPageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: InsertionOrder,
+ url: InsertionOrder.prototype.urlRoot
+ });
+ var FollowerPageableCollection = Backbone.TastypiePageableCollection.extend({
+ url: function() {
+ return this.urlPrefix + '/follower/';
+ },
+ initialize: function(models, options) {
+ this.urlPrefix = options.urlPrefix;
+ }
+ });
+ var fetchInsertionOrder = _(function(id) {
+ return this.fetchEntity(InsertionOrder, id);
+ }).bind(this);
+ var fetchInsertionOrderCollection = _(function(options) {
+ return this.fetchEntities(InsertionOrderCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:insertionorder:entity', function(id) { return fetchInsertionOrder(id); }],
+ ['advisor:insertionorder:entities', function(options) { return fetchInsertionOrderCollection(options); }],
+ ['advisor:insertionorder:Entity', function() { return InsertionOrder; }],
+ ['advisor:insertionorder:Entities', function() { return InsertionOrderCollection; }],
+ ['advisor:insertionorder:PageableEntities', function() { return InsertionOrderPageableCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Fetch insertionorder collection
- * @return {$.Promise}
- */
- var fetchInsertionOrderCollection = _(function(options) {
- return this.fetchEntities(InsertionOrderCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:insertionorder:entity', function(id) { return fetchInsertionOrder(id); }],
- ['advisor:insertionorder:entities', function(options) { return fetchInsertionOrderCollection(options); }],
- ['advisor:insertionorder:Entity', function() { return InsertionOrder; }],
- ['advisor:insertionorder:Entities', function() { return InsertionOrderCollection; }],
- ['advisor:insertionorder:PageableEntities', function() { return InsertionOrderPageableCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/advisor/pixel.js
- AdvisorApp.module('Entities', function() {
- /**
- * Model and Collection definitions
- */
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var Pixel = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/pixel/'
- });
- var PixelCollection = Backbone.Collection.extend({
- model: Pixel,
- url: Pixel.prototype.urlRoot
- });
+ /* module definition */
+ var Pixel = {};
- var PixelPageableCollection = Backbone.TastypiePageableCollection.extend({
- model: Pixel,
- url: Pixel.prototype.urlRoot
- });
+ var Pixel = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/pixel/'
+ });
+ var PixelCollection = Backbone.Collection.extend({
+ model: Pixel,
+ url: Pixel.prototype.urlRoot
+ });
+ var PixelPageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: Pixel,
+ url: Pixel.prototype.urlRoot
+ });
+ var getPixel = _(function(id) {
+ return this.fetchEntity(Pixel, id);
+ }).bind(this);
+ var getPixelCollection = _(function(options) {
+ return this.fetchEntities(PixelCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:pixel:entity', function(id) { return getPixel(id); }],
+ ['advisor:pixel:entities', function(options) { return getPixelCollection(options); }],
+ ['advisor:pixel:Entity', function() { return Pixel; }],
+ ['advisor:pixel:Entities', function() { return PixelCollection; }],
+ ['advisor:pixel:PageableEntities', function() { return PixelPageableCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Fetch pixel
- * @return {$.Promise}
- */
- var getPixel = _(function(id) {
- return this.fetchEntity(Pixel, id);
- }).bind(this);
- /**
- * Fetch pixel collection
- * @return {$.Promise}
- */
- var getPixelCollection = _(function(options) {
- return this.fetchEntities(PixelCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:pixel:entity', function(id) { return getPixel(id); }],
- ['advisor:pixel:entities', function(options) { return getPixelCollection(options); }],
- ['advisor:pixel:Entity', function() { return Pixel; }],
- ['advisor:pixel:Entities', function() { return PixelCollection; }],
- ['advisor:pixel:PageableEntities', function() { return PixelPageableCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Pixel;
---------- diff:
entities/advisor/tag.js
- AdvisorApp.module('Entities', function() {
- /**
- * Model and Collection definitions
- */
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var Tag = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/viewtag/'
- });
- var TagCollection = Backbone.Collection.extend({
- model: Tag,
- url: Tag.prototype.urlRoot
- });
+ /* module definition */
+ var Tag = {};
- var TagPageableCollection = Backbone.TastypiePageableCollection.extend({
- model: Tag,
- url: Tag.prototype.urlRoot
- });
+ var Tag = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/viewtag/'
+ });
+ var TagCollection = Backbone.Collection.extend({
+ model: Tag,
+ url: Tag.prototype.urlRoot
+ });
+ var TagPageableCollection = Backbone.TastypiePageableCollection.extend({
+ model: Tag,
+ url: Tag.prototype.urlRoot
+ });
+ var getTag = _(function(id) {
+ return this.fetchEntity(Tag, id);
+ }).bind(this);
+ var getTagCollection = _(function(options) {
+ return this.fetchEntities(TagCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:tag:entity', function(id) { return getTag(id); }],
+ ['advisor:tag:entities', function(options) { return getTagCollection(options); }],
+ ['advisor:tag:Entity', function() { return Tag; }],
+ ['advisor:tag:Entities', function() { return TagCollection; }],
+ ['advisor:tag:PageableEntities', function() { return TagPageableCollection; }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Fetch tag
- * @return {$.Promise}
- */
- var getTag = _(function(id) {
- return this.fetchEntity(Tag, id);
- }).bind(this);
- /**
- * Fetch tag collection
- * @return {$.Promise}
- */
- var getTagCollection = _(function(options) {
- return this.fetchEntities(TagCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:tag:entity', function(id) { return getTag(id); }],
- ['advisor:tag:entities', function(options) { return getTagCollection(options); }],
- ['advisor:tag:Entity', function() { return Tag; }],
- ['advisor:tag:Entities', function() { return TagCollection; }],
- ['advisor:tag:PageableEntities', function() { return TagPageableCollection; }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Tag;
---------- diff:
entities/advisor/targetingset.js
- AdvisorApp.module('Entities', function() {
- /**
- * Model and Collection definitions
- */
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var TargetingSet = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/targetingset/',
- initialize: function() {
- var selectable = new Backbone.Picky.Selectable(this);
- _.extend(this, selectable);
- }
- });
- var TargetingSetCollection = Backbone.Collection.extend({
- model: TargetingSet,
- url: TargetingSet.prototype.urlRoot,
- initialize: function() {
- var singleSelect = new Backbone.Picky.SingleSelect(this);
- _.extend(this, singleSelect);
- }
- });
+ /* module definition */
+ var Targetingset = {};
- /**
- * Fetch targeting set
- * @return {$.Promise}
- */
- var getTargetingSet = _(function(id) {
- return this.fetchEntity(TargetingSet, id);
- }).bind(this);
+ var TargetingSet = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/targetingset/',
+ initialize: function() {
+ var selectable = new Backbone.Picky.Selectable(this);
+ _.extend(this, selectable);
+ }
+ });
+ var TargetingSetCollection = Backbone.Collection.extend({
+ model: TargetingSet,
+ url: TargetingSet.prototype.urlRoot,
+ initialize: function() {
+ var singleSelect = new Backbone.Picky.SingleSelect(this);
+ _.extend(this, singleSelect);
+ }
+ });
+ var getTargetingSet = _(function(id) {
+ return this.fetchEntity(TargetingSet, id);
+ }).bind(this);
+ var getTargetingSetCollection = _(function(options) {
+ return this.fetchEntities(TargetingSetCollection, options);
+ }).bind(this);
+ var handlers = [
+ ['advisor:targetingset:Entity', function() { return TargetingSet; }],
+ ['advisor:targetingset:Entities', function() { return TargetingSetCollection; }],
+ ['advisor:targetingset:entity', function(id) { return getTargetingSet(id); }],
+ ['advisor:targetingset:entities', function(options) { return getTargetingSetCollection(options); }]
+ ];
+ _(handlers).each(function(handler) {
+ AdvisorApp.reqres.setHandler(handler[0], handler[1]);
+ });
- /**
- * Fetch targeting set collection
- * @return {$.Promise}
- */
- var getTargetingSetCollection = _(function(options) {
- return this.fetchEntities(TargetingSetCollection, options);
- }).bind(this);
- /**
- * Handler definitions
- */
- var handlers = [
- ['advisor:targetingset:Entity', function() { return TargetingSet; }],
- ['advisor:targetingset:Entities', function() { return TargetingSetCollection; }],
- ['advisor:targetingset:entity', function(id) { return getTargetingSet(id); }],
- ['advisor:targetingset:entities', function(options) { return getTargetingSetCollection(options); }]
- ];
- _(handlers).each(function(handler) {
- AdvisorApp.reqres.setHandler(handler[0], handler[1]);
- });
- });
+ module.exports = Targetingset;
---------- diff:
entities/audience-profiler/audience.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var AudienceModel = Backbone.Model.extend({
- url: function() { return '/custom-audience/api/v2/customaudience/' + this.id; }
- });
- var AudienceCollection = Backbone.Collection.extend({
- initialize: function(obj) {
- if (obj) {
- this.ids = obj.ids;
- this.accountId = obj.accountId;
- }
- },
- url: function() {
- var url = '/custom-audience/api/v2/customaudience/';
- if (this.accountId) {
- url += '?account=' + this.accountId + '&format=json';
- } else if (this.ids) {
- url += '?id__in=' + this.ids.join(',') + '&format=json';
- }
- return url;
- },
- model: AudienceModel,
- parse: function(response) {
- return response.objects;
+ /* module definition */
+ var Entities = {};
+ var AudienceModel = Backbone.Model.extend({
+ url: function() { return '/custom-audience/api/v2/customaudience/' + this.id; }
+ });
+ var AudienceCollection = Backbone.Collection.extend({
+ initialize: function(obj) {
+ if (obj) {
+ this.ids = obj.ids;
+ this.accountId = obj.accountId;
}
- });
+ },
+ url: function() {
+ var url = '/custom-audience/api/v2/customaudience/';
+ if (this.accountId) {
+ url += '?account=' + this.accountId + '&format=json';
+ } else if (this.ids) {
+ url += '?id__in=' + this.ids.join(',') + '&format=json';
+ }
+ return url;
+ },
+ model: AudienceModel,
+ parse: function(response) {
+ return response.objects;
+ }
+ });
+ App.reqres.setHandler('audience-profiler:Audience', function() {
+ return AudienceModel;
+ });
+ App.reqres.setHandler('audience-profiler:Audiences', function() {
+ return AudienceCollection;
+ });
+ App.reqres.setHandler('audience-profiler:audience', function(attrs) {
+ return new AudienceModel(attrs).fetch();
+ });
+ App.reqres.setHandler('audience-profiler:audiences', function(attrs) {
+ return new AudienceCollection(attrs).fetch();
+ });
- App.reqres.setHandler('audience-profiler:Audience', function() {
- return AudienceModel;
- });
- App.reqres.setHandler('audience-profiler:Audiences', function() {
- return AudienceCollection;
- });
- App.reqres.setHandler('audience-profiler:audience', function(attrs) {
- return new AudienceModel(attrs).fetch();
- });
- App.reqres.setHandler('audience-profiler:audiences', function(attrs) {
- return new AudienceCollection(attrs).fetch();
- });
- });
+ module.exports = Entities;
---------- diff:
entities/audience-profiler/account.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var AccountModel = Backbone.Model.extend({
- url: function() { return '/api/facebook/analytics/v1/adaccount/' + this.id; }
- });
- var AccountCollection = Backbone.Collection.extend({
- url: '/api/facebook/analytics/v1/adaccount/?order_by=name',
- model: AccountModel,
- parse: function(response) {
- return response.objects;
- }
- });
+ /* module definition */
+ var Entities = {};
- App.reqres.setHandler('audience-profiler:Account', function() {
- return AccountModel;
- });
+ var AccountModel = Backbone.Model.extend({
+ url: function() { return '/api/facebook/analytics/v1/adaccount/' + this.id; }
+ });
+ var AccountCollection = Backbone.Collection.extend({
+ url: '/api/facebook/analytics/v1/adaccount/?order_by=name',
+ model: AccountModel,
+ parse: function(response) {
+ return response.objects;
+ }
+ });
+ App.reqres.setHandler('audience-profiler:Account', function() {
+ return AccountModel;
+ });
+ App.reqres.setHandler('audience-profiler:Accounts', function() {
+ return AccountCollection;
+ });
+ App.reqres.setHandler('audience-profiler:account', function(attrs) {
+ return new AccountModel(attrs).fetch();
+ });
+ App.reqres.setHandler('audience-profiler:accounts', function(attrs) {
+ return new AccountCollection(attrs).fetch();
+ });
- App.reqres.setHandler('audience-profiler:Accounts', function() {
- return AccountCollection;
- });
- App.reqres.setHandler('audience-profiler:account', function(attrs) {
- return new AccountModel(attrs).fetch();
- });
- App.reqres.setHandler('audience-profiler:accounts', function(attrs) {
- return new AccountCollection(attrs).fetch();
- });
- });
+ module.exports = Entities;
---------- diff:
entities/audience-profiler/chart.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var ChartModel = Backbone.Model.extend({}, {
- schema: {
- 'connection_frac': Number,
- 'connection_ids': String,
- 'connection_names': String,
- 'fb_frac': Number,
- 'id': String,
- 'index': Number,
- 'name': String,
- 'reach_US_FB': Number,
- 'reach_audience': Number,
- 'type': String
- }
- });
- var ChartCollection = Backbone.Collection.extend({
- initialize: function(obj) {
- obj = obj || {};
- this.accountId = obj.account && obj.account.get('account_id');
- this.connections = {};
- if (obj.pages) {
- this.connections.pages = _.keys(obj.pages);
- }
- if (obj.audiences) {
- this.connections.audiences = _.keys(obj.audiences);
- }
- if (!_.keys(this.connections).length) {
- throw new Error('Cannot draw charts without any pages or audiences.');
- } else if (!this.accountId) {
- throw new Error('Cannot draw charts without an account.');
- }
- },
- url: function() {
- return '/api/facebook/analytics/v1/facebookpage/audience-profiles/?account_id=' + this.accountId + _.reduce(_.keys(this.connections), function(str, conn) {
- var connection = this.connections[conn];
- return str + _.reduce(connection, function(str, val) {
- return str + '&' + conn + '=' + val;
- }, '');
- }, '', this);
- },
- model: ChartModel,
- parse: function(results) {
- this.baseReach = results.base_reach;
- this.baseTargeting = results.base_targeting;
- this.connectionReach = results.connection;
- this.connectionTargeting = results.connection_targeting;
- return results.datums;
+ /* module definition */
+ var Entities = {};
+ var ChartModel = Backbone.Model.extend({}, {
+ schema: {
+ 'connection_frac': Number,
+ 'connection_ids': String,
+ 'connection_names': String,
+ 'fb_frac': Number,
+ 'id': String,
+ 'index': Number,
+ 'name': String,
+ 'reach_US_FB': Number,
+ 'reach_audience': Number,
+ 'type': String
+ }
+ });
+ var ChartCollection = Backbone.Collection.extend({
+ initialize: function(obj) {
+ obj = obj || {};
+ this.accountId = obj.account && obj.account.get('account_id');
+ this.connections = {};
+ if (obj.pages) {
+ this.connections.pages = _.keys(obj.pages);
}
- });
+ if (obj.audiences) {
+ this.connections.audiences = _.keys(obj.audiences);
+ }
+ if (!_.keys(this.connections).length) {
+ throw new Error('Cannot draw charts without any pages or audiences.');
+ } else if (!this.accountId) {
+ throw new Error('Cannot draw charts without an account.');
+ }
+ },
+ url: function() {
+ return '/api/facebook/analytics/v1/facebookpage/audience-profiles/?account_id=' + this.accountId + _.reduce(_.keys(this.connections), function(str, conn) {
+ var connection = this.connections[conn];
+ return str + _.reduce(connection, function(str, val) {
+ return str + '&' + conn + '=' + val;
+ }, '');
+ }, '', this);
+ },
+ model: ChartModel,
+ parse: function(results) {
+ this.baseReach = results.base_reach;
+ this.baseTargeting = results.base_targeting;
+ this.connectionReach = results.connection;
+ this.connectionTargeting = results.connection_targeting;
+ return results.datums;
+ }
+ });
+ App.reqres.setHandler('audience-profiler:Chart', function() {
+ return ChartModel;
+ });
+ App.reqres.setHandler('audience-profiler:Charts', function() {
+ return ChartCollection;
+ });
+ App.reqres.setHandler('audience-profiler:chart', function(attrs) {
+ return new ChartModel(attrs).fetch();
+ });
+ App.reqres.setHandler('audience-profiler:charts', function(attrs) {
+ return new ChartCollection(attrs).fetch();
+ });
- App.reqres.setHandler('audience-profiler:Chart', function() {
- return ChartModel;
- });
- App.reqres.setHandler('audience-profiler:Charts', function() {
- return ChartCollection;
- });
- App.reqres.setHandler('audience-profiler:chart', function(attrs) {
- return new ChartModel(attrs).fetch();
- });
- App.reqres.setHandler('audience-profiler:charts', function(attrs) {
- return new ChartCollection(attrs).fetch();
- });
- });
+ module.exports = Entities;
---------- diff:
entities/audience-profiler/page.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var PageModel = Backbone.Model.extend({
- url: function() { return '/api/facebook/analytics/v1/facebookpage/' + this.id; }
- });
- var PageCollection = Backbone.Collection.extend({
- initialize: function(obj) {
- if (obj) {
- this.accountId = obj.accountId;
- this.ids = obj.ids;
- }
- },
- url: function() {
- var url = '/api/facebook/analytics/v1/facebookpage/';
- if (this.accountId) {
- url += '?accounts=' + this.accountId;
- } else if (this.ids) {
- url += '?id__in=' + this.ids.join(',');
- }
- return url;
- },
- model: PageModel,
- parse: function(response) {
- return response.objects;
+ /* module definition */
+ var Entities = {};
+ var PageModel = Backbone.Model.extend({
+ url: function() { return '/api/facebook/analytics/v1/facebookpage/' + this.id; }
+ });
+ var PageCollection = Backbone.Collection.extend({
+ initialize: function(obj) {
+ if (obj) {
+ this.accountId = obj.accountId;
+ this.ids = obj.ids;
}
- });
+ },
+ url: function() {
+ var url = '/api/facebook/analytics/v1/facebookpage/';
+ if (this.accountId) {
+ url += '?accounts=' + this.accountId;
+ } else if (this.ids) {
+ url += '?id__in=' + this.ids.join(',');
+ }
+ return url;
+ },
+ model: PageModel,
+ parse: function(response) {
+ return response.objects;
+ }
+ });
+ App.reqres.setHandler('audience-profiler:Page', function() {
+ return PageModel;
+ });
+ App.reqres.setHandler('audience-profiler:Pages', function() {
+ return PageCollection;
+ });
+ App.reqres.setHandler('audience-profiler:page', function(attrs) {
+ return new PageModel(attrs).fetch();
+ });
+ App.reqres.setHandler('audience-profiler:pages', function(attrs) {
+ return new PageCollection(attrs).fetch();
+ });
- App.reqres.setHandler('audience-profiler:Page', function() {
- return PageModel;
- });
- App.reqres.setHandler('audience-profiler:Pages', function() {
- return PageCollection;
- });
- App.reqres.setHandler('audience-profiler:page', function(attrs) {
- return new PageModel(attrs).fetch();
- });
- App.reqres.setHandler('audience-profiler:pages', function(attrs) {
- return new PageCollection(attrs).fetch();
- });
- });
+ module.exports = Entities;
---------- diff:
entities/facebook/_base.js
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
// NOTE: Due to the way Marionette's module system works (or doesn't) we have to throw
// an _ in front of the name of this file to ensure it is concatonated above the rest.
- AdvisorApp.module('Entities.Facebook.Base', function(Base, AdvisorApp) {
+ /* module definition */
+ var Base = {};
+ this.Model = Backbone.Model.extend({
/**
- * Model and Collection definitions.
+ * @returns {String} The parent lineItems status.
*/
- // NOTE: Never create an instance of this model. Use for extending only.
- this.Model = Backbone.Model.extend({
- /**
- * @returns {String} The parent lineItems status.
- */
- getLineItemStatus: function() {
- // NOTE: lineitem is defined on a model-by-model basis.
- return this.lineitem.get('status').toLowerCase();
+ getLineItemStatus: function() {
+ // NOTE: lineitem is defined on a model-by-model basis.
+ return this.lineitem.get('status').toLowerCase();
+ },
+ /**
+ * @param {Array.<String>} List of columns that are editable on this model.
+ * @returns {Array}
+ */
+ getEditableCells: function(columns) {
+ // You can't edit anything.
+ if (this.lineitem && this.getLineItemStatus() === 'deleted') {
+ return [];
+ }
+ // You can only edit the name.
+ if (this.getStatus() === 'archived') {
+ return ['name'];
+ }
+ // You can edit everything except the name.
+ return _(columns).without('name');
+ },
+ /**
+ * @returns {Array.<String>}
+ */
+ displayName: function() {
+ var splitName = this.get('name').split('--');
+ return splitName[splitName.length - 1];
+ }
+ });
+ this.Collection = Backbone.Collection.extend({
+ /** @param {Object} options */
+ initialize: function(options) {
+ this.filters = options.query;
+ }
+ });
+ this.getBaseEntity = function(id, Model) {
+ var defer = $.Deferred();
+ var model = new Model({ id: id });
+ model.fetch({
+ success: function(model) {
+ defer.resolve(model);
},
- /**
- * @param {Array.<String>} List of columns that are editable on this model.
- * @returns {Array}
- */
- getEditableCells: function(columns) {
- // You can't edit anything.
- if (this.lineitem && this.getLineItemStatus() === 'deleted') {
- return [];
- }
- // You can only edit the name.
- if (this.getStatus() === 'archived') {
- return ['name'];
- }
- // You can edit everything except the name.
- return _(columns).without('name');
- },
- /**
- * @returns {Array.<String>}
- */
- displayName: function() {
- var splitName = this.get('name').split('--');
- return splitName[splitName.length - 1];
+ error: function(collection, response) {
+ defer.reject(response);
}
});
- // NOTE: Never create an instance of this collection. Use for extending only.
- this.Collection = Backbone.Collection.extend({
- /** @param {Object} options */
- initialize: function(options) {
- this.filters = options.query;
+ return defer.promise();
+ };
+ this.getBaseEntities = function(options, Collection) {
+ var defer = $.Deferred();
+ var collection = new Collection(options);
+ collection.fetch({
+ data: this.filters,
+ success: function(collection) {
+ defer.resolve(collection);
+ },
+ error: function(collection, response) {
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ this.getPageableBaseEntities = function(Model, lineitem, statusChoices, userCanManage) {
+ var url = Model.prototype.urlRoot + 'dashboard/';
+ return Backbone.TastypiePageableCollection.extend({
+ model: Model.extend({
+ lineitem: lineitem,
+ userCanManage: userCanManage || false,
+ urlRoot: url
+ }),
+ url: url,
+ STATUS_CHOICES: statusChoices
+ });
+ };
- /**
- * Get Entit(y/ies) methods
- */
- /**
- * @param {Number} id
- * @param {Backbone.Model} Model
- * @returns {$.promise}
- */
- this.getBaseEntity = function(id, Model) {
- var defer = $.Deferred();
- var model = new Model({ id: id });
- model.fetch({
- success: function(model) {
- defer.resolve(model);
- },
- error: function(collection, response) {
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- /**
- * @param {Object} options
- * @param {Backbone.Collection} Collection
- * @returns {$.promise}
- */
- this.getBaseEntities = function(options, Collection) {
- var defer = $.Deferred();
- var collection = new Collection(options);
- collection.fetch({
- data: this.filters,
- success: function(collection) {
- defer.resolve(collection);
- },
- error: function(collection, response) {
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- /**
- * @param {Backbone.Model} Model
- * @param {Backbone.Model} lineitem
- * @param {Array.<Object>} statusChoices
- * @param {Boolean} userCanManage
- * @returns {Backbone.TastypiePageableCollection}
- */
- this.getPageableBaseEntities = function(Model, lineitem, statusChoices, userCanManage) {
- var url = Model.prototype.urlRoot + 'dashboard/';
- return Backbone.TastypiePageableCollection.extend({
- model: Model.extend({
- lineitem: lineitem,
- userCanManage: userCanManage || false,
- urlRoot: url
- }),
- url: url,
- STATUS_CHOICES: statusChoices
- });
- };
- });
+ module.exports = Base;
---------- diff:
entities/facebook/account.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var FacebookAccount = Backbone.Model.extend({
- url: function() {
- return '/api/facebook/ads/v1/adaccountadmin/';
- },
- initialize: function(attr) {
- if (attr && attr.agency) {
- this.set({'label': attr.agency.name + ' (' + attr.name + ')'});
- }
+ /* module definition */
+ var Entities = {};
+ var FacebookAccount = Backbone.Model.extend({
+ url: function() {
+ return '/api/facebook/ads/v1/adaccountadmin/';
+ },
+ initialize: function(attr) {
+ if (attr && attr.agency) {
+ this.set({'label': attr.agency.name + ' (' + attr.name + ')'});
}
- });
- var FacebookAccountCollection = Backbone.Collection.extend({
- model: FacebookAccount,
- url: function() {
- return '/api/facebook/ads/v1/adaccountadmin/' + $.fn.generateQueryString(this.filters);
+ }
+ });
+ var FacebookAccountCollection = Backbone.Collection.extend({
+ model: FacebookAccount,
+ url: function() {
+ return '/api/facebook/ads/v1/adaccountadmin/' + $.fn.generateQueryString(this.filters);
+ },
+ initialize: function(options) {
+ this.filters = options.query;
+ },
+ parse: function(data) {
+ this.meta = data.meta;
+ return data.objects;
+ }
+ });
+ var getFacebookAccountEntities = function(options) {
+ var facebookAccountCollection = new FacebookAccountCollection(options);
+ var defer = $.Deferred();
+ facebookAccountCollection.fetch({
+ success: function(collection) { //collection, response, options
+ defer.resolve(collection);
},
- initialize: function(options) {
- this.filters = options.query;
- },
- parse: function(data) {
- this.meta = data.meta;
- return data.objects;
+ error: function(collection, response) { //collection, response, options
+ console.log(response);
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ App.reqres.setHandler('facebook:account:entities', function(options) {
+ return getFacebookAccountEntities(options);
+ });
- var getFacebookAccountEntities = function(options) {
- var facebookAccountCollection = new FacebookAccountCollection(options);
- var defer = $.Deferred();
- facebookAccountCollection.fetch({
- success: function(collection) { //collection, response, options
- defer.resolve(collection);
- },
- error: function(collection, response) { //collection, response, options
- console.log(response);
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- App.reqres.setHandler('facebook:account:entities', function(options) {
- return getFacebookAccountEntities(options);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/facebook/adcampaign.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var FacebookAdCampaign;
- var FacebookAdCampaigns;
- var getFacebookAdCampaignEntity;
- var getFacebookAdCampaignEntities;
- var getFacebookPageableAdCampaignEntities;
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection definitions.
- */
- FacebookAdCampaign = Entities.Facebook.Base.Model.extend({
- urlRoot: '/api/facebook/ads/v1/adcampaign/',
- /**
- * @returns {String} The current model's status.
- */
- getStatus: function() {
- return this.get('campaign_group_status').toLowerCase();
- }
- });
+ /* module definition */
+ var Entities = {};
- FacebookAdCampaigns = Entities.Facebook.Base.Collection.extend({
- url: FacebookAdCampaign.prototype.urlRoot,
- model: FacebookAdCampaign
- });
+ var FacebookAdCampaign;
+ var FacebookAdCampaigns;
+ var getFacebookAdCampaignEntity;
+ var getFacebookAdCampaignEntities;
+ var getFacebookPageableAdCampaignEntities;
+ FacebookAdCampaign = Entities.Facebook.Base.Model.extend({
+ urlRoot: '/api/facebook/ads/v1/adcampaign/',
/**
- * Get Entit(y/ies) methods
+ * @returns {String} The current model's status.
*/
+ getStatus: function() {
+ return this.get('campaign_group_status').toLowerCase();
+ }
+ });
+ FacebookAdCampaigns = Entities.Facebook.Base.Collection.extend({
+ url: FacebookAdCampaign.prototype.urlRoot,
+ model: FacebookAdCampaign
+ });
+ getFacebookAdCampaignEntity = function(campaignID) {
+ return Entities.Facebook.Base.getBaseEntity(campaignID, FacebookAdCampaign);
+ };
+ getFacebookAdCampaignEntities = function(options) {
+ return Entities.Facebook.Base.getBaseEntities(options, FacebookAdCampaigns);
+ };
+ getFacebookPageableAdCampaignEntities = function(lineitem) {
+ var statusChoices = [
+ { label: 'Active', value: 'ACTIVE' },
+ { label: 'Paused', value: 'PAUSED' }
+ ];
+ return Entities.Facebook.Base.getPageableBaseEntities(FacebookAdCampaign, lineitem, statusChoices);
+ };
+ AdvisorApp.reqres.setHandler('facebook:adcampaign:entity', function(adcampaignID) {
+ return getFacebookAdCampaignEntity(adcampaignID);
+ });
+ AdvisorApp.reqres.setHandler('facebook:adcampaign:entities', function(options) {
+ return getFacebookAdCampaignEntities(options);
+ });
+ AdvisorApp.reqres.setHandler('facebook:adcampaign:PageableEntities', function(lineitem) {
+ return getFacebookPageableAdCampaignEntities(lineitem);
+ });
- getFacebookAdCampaignEntity = function(campaignID) {
- return Entities.Facebook.Base.getBaseEntity(campaignID, FacebookAdCampaign);
- };
- getFacebookAdCampaignEntities = function(options) {
- return Entities.Facebook.Base.getBaseEntities(options, FacebookAdCampaigns);
- };
- getFacebookPageableAdCampaignEntities = function(lineitem) {
- var statusChoices = [
- { label: 'Active', value: 'ACTIVE' },
- { label: 'Paused', value: 'PAUSED' }
- ];
- return Entities.Facebook.Base.getPageableBaseEntities(FacebookAdCampaign, lineitem, statusChoices);
- };
- /**
- * Handler Definitions
- */
- AdvisorApp.reqres.setHandler('facebook:adcampaign:entity', function(adcampaignID) {
- return getFacebookAdCampaignEntity(adcampaignID);
- });
- AdvisorApp.reqres.setHandler('facebook:adcampaign:entities', function(options) {
- return getFacebookAdCampaignEntities(options);
- });
- AdvisorApp.reqres.setHandler('facebook:adcampaign:PageableEntities', function(lineitem) {
- return getFacebookPageableAdCampaignEntities(lineitem);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/facebook/adgroup.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var FacebookAdGroup;
- var FacebookAdGroups;
- var getFacebookAdGroupEntity;
- var getFacebookAdGroupEntities;
- var getFacebookPageableAdGroupEntities;
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection definitions.
- */
- FacebookAdGroup = Entities.Facebook.Base.Model.extend({
- urlRoot: '/api/facebook/ads/v1/adgroup/',
- /**
- * @returns {String} The current model's status.
- */
- getStatus: function() {
- return this.get('adgroup_status').toLowerCase();
- }
- });
+ /* module definition */
+ var Entities = {};
- FacebookAdGroups = Entities.Facebook.Base.Collection.extend({
- url: FacebookAdGroup.prototype.urlRoot,
- model: FacebookAdGroup
- });
+ var FacebookAdGroup;
+ var FacebookAdGroups;
+ var getFacebookAdGroupEntity;
+ var getFacebookAdGroupEntities;
+ var getFacebookPageableAdGroupEntities;
+ FacebookAdGroup = Entities.Facebook.Base.Model.extend({
+ urlRoot: '/api/facebook/ads/v1/adgroup/',
/**
- * Get Entit(y/ies) methods
+ * @returns {String} The current model's status.
*/
+ getStatus: function() {
+ return this.get('adgroup_status').toLowerCase();
+ }
+ });
+ FacebookAdGroups = Entities.Facebook.Base.Collection.extend({
+ url: FacebookAdGroup.prototype.urlRoot,
+ model: FacebookAdGroup
+ });
+ getFacebookAdGroupEntity = function(adGroupID) {
+ return Entities.Facebook.Base.getBaseEntity(adGroupID, FacebookAdGroup);
+ };
+ getFacebookAdGroupEntities = function(options) {
+ return Entities.Facebook.Base.getBaseEntities(options, FacebookAdGroups);
+ };
+ getFacebookPageableAdGroupEntities = function(adsetId, userCanManage) {
+ var statusChoices = [
+ { label: 'Active', value: 'ACTIVE' },
+ { label: 'Paused', value: 'PAUSED' }
+ ];
+ return Entities.Facebook.Base.getPageableBaseEntities(FacebookAdGroup, adsetId, statusChoices, userCanManage);
+ };
+ AdvisorApp.reqres.setHandler('facebook:adgroup:entity', function(adGroupID) {
+ return getFacebookAdGroupEntity(adGroupID);
+ });
+ AdvisorApp.reqres.setHandler('facebook:adgroup:entities', function(options) {
+ return getFacebookAdGroupEntities(options);
+ });
+ AdvisorApp.reqres.setHandler('facebook:adgroup:PageableEntities', function(adsetId, userCanManage) {
+ return getFacebookPageableAdGroupEntities(adsetId, userCanManage);
+ });
- getFacebookAdGroupEntity = function(adGroupID) {
- return Entities.Facebook.Base.getBaseEntity(adGroupID, FacebookAdGroup);
- };
- getFacebookAdGroupEntities = function(options) {
- return Entities.Facebook.Base.getBaseEntities(options, FacebookAdGroups);
- };
- getFacebookPageableAdGroupEntities = function(adsetId, userCanManage) {
- var statusChoices = [
- { label: 'Active', value: 'ACTIVE' },
- { label: 'Paused', value: 'PAUSED' }
- ];
- return Entities.Facebook.Base.getPageableBaseEntities(FacebookAdGroup, adsetId, statusChoices, userCanManage);
- };
- /**
- * Handler Definitions
- */
- AdvisorApp.reqres.setHandler('facebook:adgroup:entity', function(adGroupID) {
- return getFacebookAdGroupEntity(adGroupID);
- });
- AdvisorApp.reqres.setHandler('facebook:adgroup:entities', function(options) {
- return getFacebookAdGroupEntities(options);
- });
- AdvisorApp.reqres.setHandler('facebook:adgroup:PageableEntities', function(adsetId, userCanManage) {
- return getFacebookPageableAdGroupEntities(adsetId, userCanManage);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/facebook/adset.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var FacebookAdSet;
- var FacebookAdSets;
- var getFacebookAdSetEntity;
- var getFacebookAdSetEntities;
- var getFacebookPageableAdSetEntities;
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection definitions.
- */
- FacebookAdSet = Entities.Facebook.Base.Model.extend({
- urlRoot: '/api/facebook/ads/v1/adset/',
- /**
- * @returns {String} The current model's status.
- */
- getStatus: function() {
- return this.get('campaign_status').toLowerCase();
- }
- });
+ /* module definition */
+ var Entities = {};
- FacebookAdSets = Entities.Facebook.Base.Collection.extend({
- url: FacebookAdSet.prototype.urlRoot,
- model: FacebookAdSet
- });
+ var FacebookAdSet;
+ var FacebookAdSets;
+ var getFacebookAdSetEntity;
+ var getFacebookAdSetEntities;
+ var getFacebookPageableAdSetEntities;
+ FacebookAdSet = Entities.Facebook.Base.Model.extend({
+ urlRoot: '/api/facebook/ads/v1/adset/',
/**
- * Get Entit(y/ies) methods
+ * @returns {String} The current model's status.
*/
+ getStatus: function() {
+ return this.get('campaign_status').toLowerCase();
+ }
+ });
+ FacebookAdSets = Entities.Facebook.Base.Collection.extend({
+ url: FacebookAdSet.prototype.urlRoot,
+ model: FacebookAdSet
+ });
+ getFacebookAdSetEntity = function(adSetID) {
+ return Entities.Facebook.Base.getBaseEntity(adSetID, FacebookAdSet);
+ };
+ getFacebookAdSetEntities = function(options) {
+ return Entities.Facebook.Base.getBaseEntities(options, FacebookAdSets);
+ };
+ getFacebookPageableAdSetEntities = function(lineitem, userCanManage) {
+ var statusChoices = [
+ { label: 'Active', value: 'ACTIVE' },
+ { label: 'Paused', value: 'PAUSED' },
+ { label: 'Archived', value: 'ARCHIVED' }
+ ];
+ return Entities.Facebook.Base.getPageableBaseEntities(FacebookAdSet, lineitem, statusChoices, userCanManage);
+ };
+ AdvisorApp.reqres.setHandler('facebook:adset:entity', function(adSetID) {
+ return getFacebookAdSetEntity(adSetID);
+ });
+ AdvisorApp.reqres.setHandler('facebook:adset:entities', function(options) {
+ return getFacebookAdSetEntities(options);
+ });
+ AdvisorApp.reqres.setHandler('facebook:adset:Entity', function() {
+ return FacebookAdSet;
+ });
+ AdvisorApp.reqres.setHandler('facebook:adset:Entities', function() {
+ return FacebookAdSets;
+ });
+ AdvisorApp.reqres.setHandler('facebook:adset:PageableEntities', function(lineitem, userCanManage) {
+ return getFacebookPageableAdSetEntities(lineitem, userCanManage);
+ });
- getFacebookAdSetEntity = function(adSetID) {
- return Entities.Facebook.Base.getBaseEntity(adSetID, FacebookAdSet);
- };
- getFacebookAdSetEntities = function(options) {
- return Entities.Facebook.Base.getBaseEntities(options, FacebookAdSets);
- };
- getFacebookPageableAdSetEntities = function(lineitem, userCanManage) {
- var statusChoices = [
- { label: 'Active', value: 'ACTIVE' },
- { label: 'Paused', value: 'PAUSED' },
- { label: 'Archived', value: 'ARCHIVED' }
- ];
- return Entities.Facebook.Base.getPageableBaseEntities(FacebookAdSet, lineitem, statusChoices, userCanManage);
- };
- /**
- * Handler Definitions
- */
- AdvisorApp.reqres.setHandler('facebook:adset:entity', function(adSetID) {
- return getFacebookAdSetEntity(adSetID);
- });
- AdvisorApp.reqres.setHandler('facebook:adset:entities', function(options) {
- return getFacebookAdSetEntities(options);
- });
- AdvisorApp.reqres.setHandler('facebook:adset:Entity', function() {
- return FacebookAdSet;
- });
- AdvisorApp.reqres.setHandler('facebook:adset:Entities', function() {
- return FacebookAdSets;
- });
- AdvisorApp.reqres.setHandler('facebook:adset:PageableEntities', function(lineitem, userCanManage) {
- return getFacebookPageableAdSetEntities(lineitem, userCanManage);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/facebook/creative.js
- AdvisorApp.module('Entities.Facebook', function(Module) {
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
- /** @type {object} */
- Module.AdCreative = {
- /** @returns {object} */
- getTypes: function() {
- return {
- 'Page post': {
- 'Photo': 'photo',
- 'Video': 'video',
- 'Link': 'pagelink',
- 'Status': 'status'
- },
- 'Ad': {
- 'Fan': 'fan',
- 'Link': 'link',
- 'Event': 'event'
- }
- };
- },
- /**
- * @param {string} type
- * @returns {AdvisorApp.module('Entities').AdCreative}
- */
- getCreative: function(type) {
- switch (type) {
- case 'photo': return Module.PhotoPost;
- case 'video': return Module.Video;
- case 'fan': return Module.Creatives.Fan;
- case 'pagelink': return Module.LinkPost;
- case 'link': return Module.Creatives.Link;
- case 'event': return Module.Creatives.Event;
- case 'status': return Module.StatusPost;
+ /* module definition */
+ var Module = {};
+ Module.AdCreative = {
+ /** @returns {object} */
+ getTypes: function() {
+ return {
+ 'Page post': {
+ 'Photo': 'photo',
+ 'Video': 'video',
+ 'Link': 'pagelink',
+ 'Status': 'status'
+ },
+ 'Ad': {
+ 'Fan': 'fan',
+ 'Link': 'link',
+ 'Event': 'event'
}
- throw new Error(type + ' is not a valid creative type');
- },
- /** @returns {string} */
- getPlatform: function() {
- return 'facebook';
+ };
+ },
+ /**
+ * @param {string} type
+ * @returns {AdvisorApp.module('Entities').AdCreative}
+ */
+ getCreative: function(type) {
+ switch (type) {
+ case 'photo': return Module.PhotoPost;
+ case 'video': return Module.Video;
+ case 'fan': return Module.Creatives.Fan;
+ case 'pagelink': return Module.LinkPost;
+ case 'link': return Module.Creatives.Link;
+ case 'event': return Module.Creatives.Event;
+ case 'status': return Module.StatusPost;
}
- };
+ throw new Error(type + ' is not a valid creative type');
+ },
+ /** @returns {string} */
+ getPlatform: function() {
+ return 'facebook';
+ }
+ };
- });
+ module.exports = Module;
---------- diff:
entities/twitter/_base.js
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
// NOTE: Due to the way Marionette's module system works (or doesn't) we have to throw
// an _ in front of the name of this file to ensure it is concatonated above the rest.
- AdvisorApp.module('Entities.Twitter.Base', function(Base, AdvisorApp) {
+ /* module definition */
+ var Base = {};
- /**
- * Model and Collection definitions.
- */
- // NOTE: Never create an instance of this collection. Use for extending only.
- this.Collection = Backbone.Collection.extend({
- initialize: function(options) {
- this.filters = options.query;
+ this.Collection = Backbone.Collection.extend({
+ initialize: function(options) {
+ this.filters = options.query;
+ }
+ });
+ this.getBaseEntity = function(id, Model) {
+ var defer = $.Deferred();
+ var model = new Model({ id: id });
+ model.fetch({
+ success: function(model) {
+ defer.resolve(model);
+ },
+ error: function(collection, response) {
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ this.getBaseEntities = function(options, Collection) {
+ var defer = $.Deferred();
+ var collection = new Collection(options);
+ collection.fetch({
+ data: this.filters,
+ success: function(collection) {
+ defer.resolve(collection);
+ },
+ error: function(collection, response) {
+ defer.reject(response);
+ }
+ });
+ return defer.promise();
+ };
+ this.getPageableBaseEntities = function(Model, userCanManage) {
+ return Backbone.TastypiePageableCollection.extend({
+ model: Model.extend({ userCanManage: userCanManage }),
+ // NOTE: Alternative could be to store URL string in var. Opted to use prototype here instead.
+ // Feel free to change if you hate this.
+ url: Model.prototype.urlRoot + 'dashboard/'
+ });
+ };
- /**
- * Get Entit(y/ies) methods
- */
- this.getBaseEntity = function(id, Model) {
- var defer = $.Deferred();
- var model = new Model({ id: id });
- model.fetch({
- success: function(model) {
- defer.resolve(model);
- },
- error: function(collection, response) {
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- this.getBaseEntities = function(options, Collection) {
- var defer = $.Deferred();
- var collection = new Collection(options);
- collection.fetch({
- data: this.filters,
- success: function(collection) {
- defer.resolve(collection);
- },
- error: function(collection, response) {
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- this.getPageableBaseEntities = function(Model, userCanManage) {
- return Backbone.TastypiePageableCollection.extend({
- model: Model.extend({ userCanManage: userCanManage }),
- // NOTE: Alternative could be to store URL string in var. Opted to use prototype here instead.
- // Feel free to change if you hate this.
- url: Model.prototype.urlRoot + 'dashboard/'
- });
- };
- });
+ module.exports = Base;
---------- diff:
entities/twitter/account.js
- AdvisorApp.module('Entities', function(Entities, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var TwitterAccount = Backbone.Model.extend({
- url: function() {
- return '/api/twitter/ads/v1/twitteraccountadmin/';
- },
- initialize: function(attr) {
- if (attr && attr.agency) {
- this.set({ 'label': attr.agency.name + ' (' + attr.name + ')' });
- }
+ /* module definition */
+ var Entities = {};
+ var TwitterAccount = Backbone.Model.extend({
+ url: function() {
+ return '/api/twitter/ads/v1/twitteraccountadmin/';
+ },
+ initialize: function(attr) {
+ if (attr && attr.agency) {
+ this.set({ 'label': attr.agency.name + ' (' + attr.name + ')' });
}
- });
- var TwitterAccountCollection = Backbone.Collection.extend({
- model: TwitterAccount,
- url: function() {
- return '/api/twitter/ads/v1/twitteraccountadmin/' +
- $.fn.generateQueryString(this.filters);
+ }
+ });
+ var TwitterAccountCollection = Backbone.Collection.extend({
+ model: TwitterAccount,
+ url: function() {
+ return '/api/twitter/ads/v1/twitteraccountadmin/' +
+ $.fn.generateQueryString(this.filters);
+ },
+ initialize: function(options) {
+ this.filters = options.query;
+ },
+ parse: function(data) {
+ this.meta = data.meta;
+ return data.objects;
+ }
+ });
+ var getTwitterAccountEntities = function(options) {
+ var twitterAccountCollection = new TwitterAccountCollection(options);
+ var defer = $.Deferred();
+ twitterAccountCollection.fetch({
+ success: function(collection) { //collection, response, options
+ defer.resolve(collection);
},
- initialize: function(options) {
- this.filters = options.query;
- },
- parse: function(data) {
- this.meta = data.meta;
- return data.objects;
+ error: function(collection, response) { //collection, response, options
+ console.log(response);
+ defer.reject(response);
}
});
+ return defer.promise();
+ };
+ App.reqres.setHandler('twitter:account:entities', function(options) {
+ return getTwitterAccountEntities(options);
+ });
- var getTwitterAccountEntities = function(options) {
- var twitterAccountCollection = new TwitterAccountCollection(options);
- var defer = $.Deferred();
- twitterAccountCollection.fetch({
- success: function(collection) { //collection, response, options
- defer.resolve(collection);
- },
- error: function(collection, response) { //collection, response, options
- console.log(response);
- defer.reject(response);
- }
- });
- return defer.promise();
- };
- App.reqres.setHandler('twitter:account:entities', function(options) {
- return getTwitterAccountEntities(options);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/twitter/campaign.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var TwitterCampaign;
- var TwitterCampaigns;
- var getTwitterCampaignEntity;
- var getTwitterCampaignEntities;
- var getTwitterPageableCampaignEntities;
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection definitions.
- */
- TwitterCampaign = Backbone.Model.extend({
- urlRoot: '/api/twitter/ads/v1/twittercampaign/'
- });
+ /* module definition */
+ var Entities = {};
- TwitterCampaigns = Entities.Twitter.Base.Collection.extend({
- model: TwitterCampaign,
- url: TwitterCampaign.prototype.urlRoot
- });
+ var TwitterCampaign;
+ var TwitterCampaigns;
+ var getTwitterCampaignEntity;
+ var getTwitterCampaignEntities;
+ var getTwitterPageableCampaignEntities;
+ TwitterCampaign = Backbone.Model.extend({
+ urlRoot: '/api/twitter/ads/v1/twittercampaign/'
+ });
+ TwitterCampaigns = Entities.Twitter.Base.Collection.extend({
+ model: TwitterCampaign,
+ url: TwitterCampaign.prototype.urlRoot
+ });
+ getTwitterCampaignEntity = function(campaignID) {
+ return Entities.Twitter.Base.getBaseEntity(campaignID, TwitterCampaign);
+ };
+ getTwitterCampaignEntities = function(options) {
+ return Entities.Twitter.Base.getBaseEntities(options, TwitterCampaigns);
+ };
+ getTwitterPageableCampaignEntities = function(userCanManage) {
+ return Entities.Twitter.Base.getPageableBaseEntities(TwitterCampaign, userCanManage);
+ };
+ AdvisorApp.reqres.setHandler('twitter:campaign:entity', function(campaignID) {
+ return getTwitterCampaignEntity(campaignID);
+ });
+ AdvisorApp.reqres.setHandler('twitter:campaign:entities', function(options) {
+ return getTwitterCampaignEntities(options);
+ });
+ AdvisorApp.reqres.setHandler('twitter:campaign:PageableEntities', function(options, userCanManage) {
+ return getTwitterPageableCampaignEntities(options, userCanManage);
+ });
- /**
- * Get Entit(y/ies) methods
- */
- getTwitterCampaignEntity = function(campaignID) {
- return Entities.Twitter.Base.getBaseEntity(campaignID, TwitterCampaign);
- };
- getTwitterCampaignEntities = function(options) {
- return Entities.Twitter.Base.getBaseEntities(options, TwitterCampaigns);
- };
- getTwitterPageableCampaignEntities = function(userCanManage) {
- return Entities.Twitter.Base.getPageableBaseEntities(TwitterCampaign, userCanManage);
- };
- /**
- * Handler Definitions
- */
- AdvisorApp.reqres.setHandler('twitter:campaign:entity', function(campaignID) {
- return getTwitterCampaignEntity(campaignID);
- });
- AdvisorApp.reqres.setHandler('twitter:campaign:entities', function(options) {
- return getTwitterCampaignEntities(options);
- });
- AdvisorApp.reqres.setHandler('twitter:campaign:PageableEntities', function(options, userCanManage) {
- return getTwitterPageableCampaignEntities(options, userCanManage);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/twitter/adgroup.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var TwitterAdGroup;
- var TwitterAdGroups;
- var getTwitterAdGroupEntity;
- var getTwitterAdGroupEntities;
- var getTwitterPageableAdGroupEntities;
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection definitions.
- */
- TwitterAdGroup = Backbone.Model.extend({
- urlRoot: '/api/twitter/ads/v1/twitterlineitem/'
- });
+ /* module definition */
+ var Entities = {};
- TwitterAdGroups = Entities.Twitter.Base.Collection.extend({
- model: TwitterAdGroup,
- url: TwitterAdGroup.prototype.urlRoot
- });
+ var TwitterAdGroup;
+ var TwitterAdGroups;
+ var getTwitterAdGroupEntity;
+ var getTwitterAdGroupEntities;
+ var getTwitterPageableAdGroupEntities;
+ TwitterAdGroup = Backbone.Model.extend({
+ urlRoot: '/api/twitter/ads/v1/twitterlineitem/'
+ });
+ TwitterAdGroups = Entities.Twitter.Base.Collection.extend({
+ model: TwitterAdGroup,
+ url: TwitterAdGroup.prototype.urlRoot
+ });
+ getTwitterAdGroupEntity = function(adgroupID) {
+ return Entities.Twitter.Base.getBaseEntity(adgroupID, TwitterAdGroup);
+ };
+ getTwitterAdGroupEntities = function(options) {
+ return Entities.Twitter.Base.getBaseEntities(options, TwitterAdGroups);
+ };
+ getTwitterPageableAdGroupEntities = function(userCanManage) {
+ return Entities.Twitter.Base.getPageableBaseEntities(TwitterAdGroup, userCanManage);
+ };
+ AdvisorApp.reqres.setHandler('twitter:adgroup:entity', function(adgroupID) {
+ return getTwitterAdGroupEntity(adgroupID);
+ });
+ AdvisorApp.reqres.setHandler('twitter:adgroup:entities', function(options) {
+ return getTwitterAdGroupEntities(options);
+ });
+ AdvisorApp.reqres.setHandler('twitter:adgroup:PageableEntities', function(userCanManage) {
+ return getTwitterPageableAdGroupEntities(userCanManage);
+ });
- /**
- * Get Entit(y/ies) methods
- */
- getTwitterAdGroupEntity = function(adgroupID) {
- return Entities.Twitter.Base.getBaseEntity(adgroupID, TwitterAdGroup);
- };
- getTwitterAdGroupEntities = function(options) {
- return Entities.Twitter.Base.getBaseEntities(options, TwitterAdGroups);
- };
- getTwitterPageableAdGroupEntities = function(userCanManage) {
- return Entities.Twitter.Base.getPageableBaseEntities(TwitterAdGroup, userCanManage);
- };
- /**
- * Handler Definitions
- */
- AdvisorApp.reqres.setHandler('twitter:adgroup:entity', function(adgroupID) {
- return getTwitterAdGroupEntity(adgroupID);
- });
- AdvisorApp.reqres.setHandler('twitter:adgroup:entities', function(options) {
- return getTwitterAdGroupEntities(options);
- });
- AdvisorApp.reqres.setHandler('twitter:adgroup:PageableEntities', function(userCanManage) {
- return getTwitterPageableAdGroupEntities(userCanManage);
- });
- });
+ module.exports = Entities;
---------- diff:
entities/twitter/fundinginstrument.js
- AdvisorApp.module('Entities', function(Entities, AdvisorApp) {
- var TwitterFundingInstrument;
- var TwitterFundingInstruments;
- var getTwitterFundingInstrumentEntity;
- var getTwitterFundingInstrumentEntities;
- var getTwitterPageableFundingInstrumentEntities;
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- /**
- * Model and Collection definitions.
- */
- TwitterFundingInstrument = Backbone.Model.extend({
- urlRoot: '/api/advisor/v1/twitterfundinginstrument/',
- initialize: function(attr) {
- if (attr && attr.agency) {
- this.set({
- label: attr.agency.name + ' (' + attr.name + ')'
- });
- }
- }
- });
+ /* module definition */
+ var Entities = {};
- TwitterFundingInstruments = Entities.Twitter.Base.Collection.extend({
- model: TwitterFundingInstrument,
- url: function() {
- return TwitterFundingInstrument.prototype.urlRoot + $.fn.generateQueryString(this.filters);
- },
- parse: function(data) {
- this.meta = data.meta;
- return data.objects;
+ var TwitterFundingInstrument;
+ var TwitterFundingInstruments;
+ var getTwitterFundingInstrumentEntity;
+ var getTwitterFundingInstrumentEntities;
+ var getTwitterPageableFundingInstrumentEntities;
+ TwitterFundingInstrument = Backbone.Model.extend({
+ urlRoot: '/api/advisor/v1/twitterfundinginstrument/',
+ initialize: function(attr) {
+ if (attr && attr.agency) {
+ this.set({
+ label: attr.agency.name + ' (' + attr.name + ')'
+ });
}
- });
+ }
+ });
+ TwitterFundingInstruments = Entities.Twitter.Base.Collection.extend({
+ model: TwitterFundingInstrument,
+ url: function() {
+ return TwitterFundingInstrument.prototype.urlRoot + $.fn.generateQueryString(this.filters);
+ },
+ parse: function(data) {
+ this.meta = data.meta;
+ return data.objects;
+ }
+ });
+ getTwitterFundingInstrumentEntity = function(fundingInstrumentID) {
+ return Entities.Twitter.Base.getBaseEntity(fundingInstrumentID, TwitterFundingInstrument);
+ };
+ getTwitterFundingInstrumentEntities = function(options) {
+ return Entities.Twitter.Base.getBaseEntities(options, TwitterFundingInstruments);
+ };
+ getTwitterPageableFundingInstrumentEntities = function() {
+ return Entities.Twitter.Base.getPageableBaseEntities(TwitterFundingInstrument);
+ };
+ AdvisorApp.reqres.setHandler('twitter:fundinginstrument:entity', function(fundingInstrumentId) {
+ return getTwitterFundingInstrumentEntity(fundingInstrumentId);
+ });
+ AdvisorApp.reqres.setHandler('twitter:fundinginstrument:entities', function(options) {
+ return getTwitterFundingInstrumentEntities(options);
+ });
+ AdvisorApp.reqres.setHandler('twitter:fundinginstrument:PageableEntities', function() {
+ return getTwitterPageableFundingInstrumentEntities();
+ });
- /**
- * Get Entit(y/ies) methods
- */
- getTwitterFundingInstrumentEntity = function(fundingInstrumentID) {
- return Entities.Twitter.Base.getBaseEntity(fundingInstrumentID, TwitterFundingInstrument);
- };
- getTwitterFundingInstrumentEntities = function(options) {
- return Entities.Twitter.Base.getBaseEntities(options, TwitterFundingInstruments);
- };
- getTwitterPageableFundingInstrumentEntities = function() {
- return Entities.Twitter.Base.getPageableBaseEntities(TwitterFundingInstrument);
- };
- /**
- * Handler Definitions
- */
- AdvisorApp.reqres.setHandler('twitter:fundinginstrument:entity', function(fundingInstrumentId) {
- return getTwitterFundingInstrumentEntity(fundingInstrumentId);
- });
- AdvisorApp.reqres.setHandler('twitter:fundinginstrument:entities', function(options) {
- return getTwitterFundingInstrumentEntities(options);
- });
- AdvisorApp.reqres.setHandler('twitter:fundinginstrument:PageableEntities', function() {
- return getTwitterPageableFundingInstrumentEntities();
- });
- });
+ module.exports = Entities;
---------- diff:
modules/adcreation/adcreation-app.js
- /** @namespace AdvisorApp.module('AdCreationApp') */
- AdvisorApp.module('AdCreationApp', function(Module, App) {
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var _ = require('underscore');
+ var moment = require('moment');
+ var Backbone = require('backbone');
+ var $ = require('jquery');
+ var AdvisorApp = require('../../app');
- /**
- * Create controller APIs for App.module('AdCreationApp')
- */
- var API = {
+ /** @namespace AdvisorApp.module('AdCreationApp') */
+ /* module definition */
+ var Module = {};
- permission: function(campaign) {
- /** Variables */
- var session = 'session:grant:entities:can_manage_brand_or_initiative';
- var brandId = campaign.get('brand_id');
- var initiativeId = campaign.get('initiative_id');
- /** Return */
- return App.request(session, brandId, initiativeId);
- },
- showBulkModal: function(iID, cID) {
- /** Clear the module record */
- Module.record(null);
- /** Show loading gif */
- AdvisorApp.modalRegion.show(new (App.module('Common.Views')).Loading());
- /** Store IDs on Module's record */
- Module.record('initiative:id', iID);
- Module.record('campaign:id', cID);
- /** Fetch the entities */
- $.when(
- AdvisorApp.request('advisor:campaign:entity', cID)
- ).done(function(campaign) {
- /** Store entities on Module's record */
- Module.record('campaign', campaign);
- var model = new Backbone.Model();
- var view = new Module.BulkUpload.View({ model: model });
- AdvisorApp.modalRegion.show(view);
+ var API = {
+ permission: function(campaign) {
+ /** Variables */
+ var session = 'session:grant:entities:can_manage_brand_or_initiative';
+ var brandId = campaign.get('brand_id');
+ var initiativeId = campaign.get('initiative_id');
+ /** Return */
+ return App.request(session, brandId, initiativeId);
+ },
+ showBulkModal: function(iID, cID) {
+ /** Clear the module record */
+ Module.record(null);
+ /** Show loading gif */
+ AdvisorApp.modalRegion.show(new (App.module('Common.Views')).Loading());
+ /** Store IDs on Module's record */
+ Module.record('initiative:id', iID);
+ Module.record('campaign:id', cID);
+ /** Fetch the entities */
+ $.when(
+ AdvisorApp.request('advisor:campaign:entity', cID)
+ ).done(function(campaign) {
+ /** Store entities on Module's record */
+ Module.record('campaign', campaign);
+ var model = new Backbone.Model();
+ var view = new Module.BulkUpload.View({ model: model });
+ AdvisorApp.modalRegion.show(view);
+ });
+ },
+ showModal: function(iID, cID) {
+ /** Update the URL */
+ App.navigate('/initiative/' + iID + '/campaign/' + cID + '/adgen/');
+ /** Clear the module record */
+ Module.record(null);
+ /** Show loading gif */
+ AdvisorApp.modalRegion.show(new (App.module('Common.Views')).Loading());
+ /** Store IDs on Module's record */
+ Module.record('initiative:id', iID);
+ Module.record('campaign:id', cID);
+ /** Fetch the entities */
+ $.when(
+ AdvisorApp.request('advisor:initiative:entity', iID),
+ AdvisorApp.request('advisor:campaign:entity', cID)
+ ).done(function(initiative, campaign) {
+ /** Store entities on Module's record */
+ Module.record('initiative', initiative);
+ Module.record('campaign', campaign);
+ var startDate = moment.utc(campaign.get('start_date')).format('MM/DD/YYYY');
+ var endDate = moment.utc(campaign.get('end_date')).format('MM/DD/YYYY');
+ var model = new Backbone.Model({
+ endDate: endDate,
+ startDate: startDate
});
- },
- showModal: function(iID, cID) {
- /** Update the URL */
- App.navigate('/initiative/' + iID + '/campaign/' + cID + '/adgen/');
- /** Clear the module record */
- Module.record(null);
- /** Show loading gif */
- AdvisorApp.modalRegion.show(new (App.module('Common.Views')).Loading());
- /** Store IDs on Module's record */
- Module.record('initiative:id', iID);
- Module.record('campaign:id', cID);
- /** Fetch the entities */
- $.when(
- AdvisorApp.request('advisor:initiative:entity', iID),
- AdvisorApp.request('advisor:campaign:entity', cID)
- ).done(function(initiative, campaign) {
- /** Store entities on Module's record */
- Module.record('initiative', initiative);
- Module.record('campaign', campaign);
- var startDate = moment.utc(campaign.get('start_date')).format('MM/DD/YYYY');
- var endDate = moment.utc(campaign.get('end_date')).format('MM/DD/YYYY');
- var model = new Backbone.Model({
- endDate: endDate,
- startDate: startDate
- });
- var view = new Module.PreCreation.View({ model: model });
- AdvisorApp.modalRegion.show(view);
- });
- },
- showForm: function(attributes) {
- /** Common controller */
- var controller = new Module.Controller();
- var campaign = Module.record('campaign');
- /** Trigger permission denied if use lacks privilege */
- if (!API.permission(campaign)) {
- return App.trigger('permissionDenied');
- }
- /** Add preloader view */
- var preloader = new App.module('Common.Views').Loading();
- var region = controller.region.main();
- region.show(preloader);
- controller.model(campaign.get('resource_uri'));
- /** Register module listeners */
- _({
- 'controller': controller,
- 'account': campaign.getPlatformAccountId(),
- 'channel': channel,
- 'channel:upload': channel.upload,
- 'channel:launch': channel.launch
- }).each(function(val, key) {
- Module.record(key, val);
- });
- /** Set the appropriate attributes on the adcluster model. */
- Module.record('model:adcluster').set({
- end_date: moment.utc(attributes.endDate, 'MM/DD/YYYY').toDate(),
- name: attributes.name,
- start_date: moment.utc(attributes.startDate, 'MM/DD/YYYY').toDate()
- });
- /** Kick off initial view */
- this.trigger('adcreation:initial:show');
- },
+ var view = new Module.PreCreation.View({ model: model });
+ AdvisorApp.modalRegion.show(view);
+ });
+ },
+ showForm: function(attributes) {
+ /** Common controller */
+ var controller = new Module.Controller();
+ var campaign = Module.record('campaign');
+ /** Trigger permission denied if use lacks privilege */
+ if (!API.permission(campaign)) {
+ return App.trigger('permissionDenied');
+ }
+ /** Add preloader view */
+ var preloader = new App.module('Common.Views').Loading();
+ var region = controller.region.main();
+ region.show(preloader);
+ controller.model(campaign.get('resource_uri'));
+ /** Register module listeners */
+ _({
+ 'controller': controller,
+ 'account': campaign.getPlatformAccountId(),
+ 'channel': channel,
+ 'channel:upload': channel.upload,
+ 'channel:launch': channel.launch
+ }).each(function(val, key) {
+ Module.record(key, val);
+ });
+ /** Set the appropriate attributes on the adcluster model. */
+ Module.record('model:adcluster').set({
+ end_date: moment.utc(attributes.endDate, 'MM/DD/YYYY').toDate(),
+ name: attributes.name,
+ start_date: moment.utc(attributes.startDate, 'MM/DD/YYYY').toDate()
+ });
+ /** Kick off initial view */
+ this.trigger('adcreation:initial:show');
+ },
+ /**
+ * This is useful if you ever want show a view from a different
+ * module (like creative creation) and then come back
+ * to the adgen process.
+ *
+ * Could be useful to make this even more flexible so you could
+ * come back to any specific view and just render the container
+ * and navigation. For now, just going to bring the user back to
+ * the initial creative selection screen.
+ */
+ showInitial: function() {
+ App.trigger([
+ 'adcreation:container:show',
+ 'adcreation:navigation:show',
+ 'adcreation:creative:show'
+ ].join(' '));
+ },
+ /** @returns {object} */
+ getTotals: function() {
+ var adcollections = App.module('AdCreationApp').record('collection:adcollections');
+ var adcluster = App.module('AdCreationApp').record('model:adcluster');
+ var creatives = adcluster.get('ad_creatives');
+ return {
+ ads: adcollections.length * creatives.length,
+ adsets: adcollections.length
+ };
+ },
+ showContainer: function() {
+ /** Controllers */
+ var Controller = Module.record('controller');
+ var Container = Module.Container.Controller;
+ /** Container view */
+ var container = new Container({
+ region: Controller.region.main()
+ });
+ },
+ hideContainer: function() {
+ /** Controllers */
+ var Controller = Module.record('controller');
+ Controller.region.main().empty();
+ },
+ hideNavigation: function() {
+ /** Controllers */
+ var Controller = Module.record('controller');
+ /** Remove the view from the region */
+ Controller.region.navigation().empty();
+ },
+ showNavigation: function() {
+ /** Controllers */
+ var Controller = Module.record('controller');
+ var Navigation = Module.Navigation.Controller;
+ /** Navigation view */
+ var navigation = new Navigation({
+ region: Controller.region.navigation()
+ });
+ },
+ /** @returns {Boolean} */
+ hasEntities: function() {
+ return !!(Module.record('initiative') && Module.record('campaign'));
+ },
+ showCreative: function() {
+ API.showController('creative', Module.Creative.Controller);
+ },
+ showCreateCreative: function(options) {
+ /** Hide current adgen views */
+ API.hideNavigation();
+ API.hideContainer();
+ /** Controllers */
+ var Controller = Module.record('controller');
+ var campaign = Module.record('campaign');
+ var CreateCreative = App.module('CreativeApp.Creation').Controller;
+ /** CreateCreative view */
+ var createCreative = new CreateCreative(_.extend(options, {
+ campaign: Module.record('campaign'),
+ initiative: Module.record('initiative')
+ }));
+ Controller.uri('create-creative');
+ },
+ showDemographic: function() {
+ API.showController('demographic', Module.Demographic.Controller);
+ },
+ showTargeting: function() {
+ AdvisorApp.trigger('notReadyToSave');
+ var options = { initiative: Module.record('initiative') };
+ API.showController('targeting', Module.Targeting.Controller, options);
+ },
+ showUpload: function() {
+ /** Trigger upload */
+ Module.record('channel:upload').save();
+ },
+ showBudget: function() {
/**
- * This is useful if you ever want show a view from a different
- * module (like creative creation) and then come back
- * to the adgen process.
- *
- * Could be useful to make this even more flexible so you could
- * come back to any specific view and just render the container
- * and navigation. For now, just going to bring the user back to
- * the initial creative selection screen.
+ * Inserts the totals into the navigation sidebar. I know this is a bit hacky, but
+ * currently there is no good clean way to insert here without simply using jQuery.
*/
- showInitial: function() {
- App.trigger([
- 'adcreation:container:show',
- 'adcreation:navigation:show',
- 'adcreation:creative:show'
- ].join(' '));
- },
- /** @returns {object} */
- getTotals: function() {
- var adcollections = App.module('AdCreationApp').record('collection:adcollections');
- var adcluster = App.module('AdCreationApp').record('model:adcluster');
- var creatives = adcluster.get('ad_creatives');
- return {
- ads: adcollections.length * creatives.length,
- adsets: adcollections.length
+ var totals = API.getTotals();
+ $('.total-ad-sets').text(totals.adsets);
+ $('.total-ads').text(totals.ads);
+ API.showController('budget', Module.Budget.Controller);
+ },
+ showLaunch: function() {
+ /** Trigger upload */
+ Module.record('channel:launch').save();
+ },
+ showCreation: function() {
+ API.showController('creation', Module.Creation.Controller);
+ },
+ /**
+ * @param {string} name
+ * @param {Marionette.Object} ViewController
+ * @param {Object=} options
+ */
+ showController: function(name, ViewController, options) {
+ /** Controllers */
+ var Controller = Module.record('controller');
+ /** View */
+ var view = new ViewController(_.extend({
+ region: Controller.region.content()
+ }, options));
+ /** Settings */
+ Controller.scene(name);
+ Controller.uri(name);
+ },
+ showReview: function() {
+ var review = new Module.Review.View({ collection: Module.record('collection:adcollections') });
+ Module.record('review', review);
+ AdvisorApp.modalRegion.show(review);
+ },
+ showToolbar: function(options) {
+ /** Campaign and initiative */
+ var campaign = Module.record('campaign');
+ var initiative = Module.record('initiative');
+ /** Extend default navigation with custom options */
+ var navigation = _.extend(options, {
+ showNavToolbar: true,
+ subNavs: [{
+ platform: campaign.getPlatform(),
+ iName: initiative.get('name'),
+ cName: campaign.get('name')
+ }]
+ });
+ /** Trigger navigation */
+ App.trigger('header:toolbar:show', navigation);
+ }
+ };
+ var channel = {
+ upload: {
+ save: function() {
+ /** Disable view */
+ App.trigger('adcreation:disable');
+ /** Trigger save */
+ Module.record('controller').scene('upload-save');
+ Module.record('model:adcluster').make();
+ /** Analytics */
+ App.trigger('usage:pageevent', 'Ad Creation', 'Uploaded');
+ },
+ success: function() {
+ /** Triger success and show budget view */
+ App.trigger('adcreation:enable');
+ App.trigger('adcreation:budget:show');
+ },
+ failure: function() {
+ /** Trigger failure */
+ Module.record('controller').scene('upload-failure');
+ App.trigger('adcreation:enable');
+ }
+ },
+ launch: {
+ save: function() {
+ /** Disable view */
+ App.trigger('adcreation:disable');
+ /** Trigger save */
+ Module.record('controller').scene('launch-save');
+ Module.record('collection:adcollections').launch();
+ /** Analytics */
+ App.trigger('usage:pageevent', 'Ad Creation', 'Launched');
+ },
+ success: function() {
+ /** Trigger success */
+ Module.record('controller').scene('launch-success');
+ },
+ failure: function() {
+ /** Trigger failure */
+ Module.record('controller').scene('launch-failure');
+ App.trigger('adcreation:enable');
+ }
+ },
+ message: function(message) {
+ Module.record('model:generic').set('message', message);
+ },
+ disable: function() {
+ /** Region */
+ var content = Module.record('controller').region.main();
+ /** Add disabled class to module view */
+ $('.sc-adcreation', content.el).addClass('disabled');
+ },
+ enable: function() {
+ /** Region */
+ var content = Module.record('controller').region.main();
+ /** Remove disabled class from module view */
+ $('.sc-adcreation', content.el).removeClass('disabled');
+ }
+ };
+ Module.Router = Marionette.AppRouter.extend({
+ initialize: function() {
+ _.chain(this.appRoutes)
+ .values()
+ .without('showModal')
+ .each(function(methodName) {
+ this.getOption('controller')[methodName] = ensureEntitiesWrapper(this.getOption('controller')[methodName]);
+ }, this);
+ function ensureEntitiesWrapper(method) {
+ return function(iID, cID) {
+ if (this.hasEntities()) {
+ method();
+ } else {
+ this.showModal(iID, cID);
+ }
};
- },
- showContainer: function() {
- /** Controllers */
- var Controller = Module.record('controller');
- var Container = Module.Container.Controller;
- /** Container view */
- var container = new Container({
- region: Controller.region.main()
- });
- },
- hideContainer: function() {
- /** Controllers */
- var Controller = Module.record('controller');
- Controller.region.main().empty();
- },
- hideNavigation: function() {
- /** Controllers */
- var Controller = Module.record('controller');
- /** Remove the view from the region */
- Controller.region.navigation().empty();
- },
- showNavigation: function() {
- /** Controllers */
- var Controller = Module.record('controller');
- var Navigation = Module.Navigation.Controller;
- /** Navigation view */
- var navigation = new Navigation({
- region: Controller.region.navigation()
- });
- },
- /** @returns {Boolean} */
- hasEntities: function() {
- return !!(Module.record('initiative') && Module.record('campaign'));
- },
- showCreative: function() {
- API.showController('creative', Module.Creative.Controller);
- },
- showCreateCreative: function(options) {
- /** Hide current adgen views */
- API.hideNavigation();
- API.hideContainer();
- /** Controllers */
- var Controller = Module.record('controller');
- var campaign = Module.record('campaign');
- var CreateCreative = App.module('CreativeApp.Creation').Controller;
- /** CreateCreative view */
- var createCreative = new CreateCreative(_.extend(options, {
- campaign: Module.record('campaign'),
- initiative: Module.record('initiative')
- }));
- Controller.uri('create-creative');
- },
- showDemographic: function() {
- API.showController('demographic', Module.Demographic.Controller);
- },
- showTargeting: function() {
- AdvisorApp.trigger('notReadyToSave');
- var options = { initiative: Module.record('initiative') };
- API.showController('targeting', Module.Targeting.Controller, options);
- },
- showUpload: function() {
- /** Trigger upload */
- Module.record('channel:upload').save();
- },
- showBudget: function() {
- /**
- * Inserts the totals into the navigation sidebar. I know this is a bit hacky, but
- * currently there is no good clean way to insert here without simply using jQuery.
- */
- var totals = API.getTotals();
- $('.total-ad-sets').text(totals.adsets);
- $('.total-ads').text(totals.ads);
- API.showController('budget', Module.Budget.Controller);
- },
- showLaunch: function() {
- /** Trigger upload */
- Module.record('channel:launch').save();
- },
- showCreation: function() {
- API.showController('creation', Module.Creation.Controller);
- },
- /**
- * @param {string} name
- * @param {Marionette.Object} ViewController
- * @param {Object=} options
- */
- showController: function(name, ViewController, options) {
- /** Controllers */
- var Controller = Module.record('controller');
- /** View */
- var view = new ViewController(_.extend({
- region: Controller.region.content()
- }, options));
- /** Settings */
- Controller.scene(name);
- Controller.uri(name);
- },
- showReview: function() {
- var review = new Module.Review.View({ collection: Module.record('collection:adcollections') });
- Module.record('review', review);
- AdvisorApp.modalRegion.show(review);
- },
- showToolbar: function(options) {
- /** Campaign and initiative */
- var campaign = Module.record('campaign');
- var initiative = Module.record('initiative');
- /** Extend default navigation with custom options */
- var navigation = _.extend(options, {
- showNavToolbar: true,
- subNavs: [{
- platform: campaign.getPlatform(),
- iName: initiative.get('name'),
- cName: campaign.get('name')
- }]
- });
- /** Trigger navigation */
- App.trigger('header:toolbar:show', navigation);
}
- };
- /**
- * Create channel for App.module('AdCreationApp')
- */
- var channel = {
- upload: {
- save: function() {
- /** Disable view */
- App.trigger('adcreation:disable');
- /** Trigger save */
- Module.record('controller').scene('upload-save');
- Module.record('model:adcluster').make();
- /** Analytics */
- App.trigger('usage:pageevent', 'Ad Creation', 'Uploaded');
- },
- success: function() {
- /** Triger success and show budget view */
- App.trigger('adcreation:enable');
- App.trigger('adcreation:budget:show');
- },
- failure: function() {
- /** Trigger failure */
- Module.record('controller').scene('upload-failure');
- App.trigger('adcreation:enable');
- }
- },
- launch: {
- save: function() {
- /** Disable view */
- App.trigger('adcreation:disable');
- /** Trigger save */
- Module.record('controller').scene('launch-save');
- Module.record('collection:adcollections').launch();
- /** Analytics */
- App.trigger('usage:pageevent', 'Ad Creation', 'Launched');
- },
- success: function() {
- /** Trigger success */
- Module.record('controller').scene('launch-success');
- },
- failure: function() {
- /** Trigger failure */
- Module.record('controller').scene('launch-failure');
- App.trigger('adcreation:enable');
- }
- },
- message: function(message) {
- Module.record('model:generic').set('message', message);
- },
- disable: function() {
- /** Region */
- var content = Module.record('controller').region.main();
- /** Add disabled class to module view */
- $('.sc-adcreation', content.el).addClass('disabled');
- },
- enable: function() {
- /** Region */
- var content = Module.record('controller').region.main();
- /** Remove disabled class from module view */
- $('.sc-adcreation', content.el).removeClass('disabled');
- }
- };
- /**
- * Create routes for App.module('AdCreationApp')
- */
- Module.Router = Marionette.AppRouter.extend({
- initialize: function() {
- _.chain(this.appRoutes)
- .values()
- .without('showModal')
- .each(function(methodName) {
- this.getOption('controller')[methodName] = ensureEntitiesWrapper(this.getOption('controller')[methodName]);
- }, this);
- function ensureEntitiesWrapper(method) {
- return function(iID, cID) {
- if (this.hasEntities()) {
- method();
- } else {
- this.showModal(iID, cID);
- }
- };
- }
- },
- appRoutes: {
- '(/)initiative/:iID/campaign/:cID/adgen(/)': 'showModal',
- '(/)initiative/:iID/campaign/:cID/adgen/creative(/)': 'showCreative',
- '(/)initiative/:iID/campaign/:cID/adgen/demographic(/)': 'showDemographic',
- '(/)initiative/:iID/campaign/:cID/adgen/targeting(/)': 'showTargeting',
- '(/)initiative/:iID/campaign/:cID/adgen/budget(/)': 'showBudget',
- '(/)initiative/:iID/campaign/:cID/adgen/creation(/)': 'showCreation'
- }
+ },
+ appRoutes: {
+ '(/)initiative/:iID/campaign/:cID/adgen(/)': 'showModal',
+ '(/)initiative/:iID/campaign/:cID/adgen/creative(/)': 'showCreative',
+ '(/)initiative/:iID/campaign/:cID/adgen/demographic(/)': 'showDemographic',
+ '(/)initiative/:iID/campaign/:cID/adgen/targeting(/)': 'showTargeting',
+ '(/)initiative/:iID/campaign/:cID/adgen/budget(/)': 'showBudget',
+ '(/)initiative/:iID/campaign/:cID/adgen/creation(/)': 'showCreation'
+ }
+ });
+ App.on('adcreation:bulk_modal:show', API.showBulkModal);
+ App.on('adcreation:modal:show', API.showModal);
+ App.on('adcreation:form:show', API.showForm);
+ App.on('adcreation:initial:show', API.showInitial);
+ App.on('adcreation:review:show', API.showReview);
+ App.on('adcreation:container:show', API.showContainer);
+ App.on('adcreation:navigation:show', API.showNavigation);
+ App.on('adcreation:creative:show', API.showCreative);
+ App.on('adcreation:demographic:show', API.showDemographic);
+ App.on('adcreation:targeting:show', API.showTargeting);
+ App.on('adcreation:upload:show', API.showUpload);
+ App.on('adcreation:budget:show', API.showBudget);
+ App.on('adcreation:launch:show', API.showLaunch);
+ App.on('adcreation:creation:show', API.showCreation);
+ App.on('adcreation:creative:create:show', API.showCreateCreative);
+ App.on('adcreation:toolbar:show', API.showToolbar);
+ App.on('adcreation:disable', channel.disable);
+ App.on('adcreation:enable', channel.enable);
+ App.addInitializer(function() {
+ /** Create router */
+ var router = new Module.Router({
+ controller: API
});
+ });
+ Module.getTotals = API.getTotals;
- /**
- * Listeners
- */
- App.on('adcreation:bulk_modal:show', API.showBulkModal);
- App.on('adcreation:modal:show', API.showModal);
- App.on('adcreation:form:show', API.showForm);
- App.on('adcreation:initial:show', API.showInitial);
- App.on('adcreation:review:show', API.showReview);
- App.on('adcreation:container:show', API.showContainer);
- App.on('adcreation:navigation:show', API.showNavigation);
- App.on('adcreation:creative:show', API.showCreative);
- App.on('adcreation:demographic:show', API.showDemographic);
- App.on('adcreation:targeting:show', API.showTargeting);
- App.on('adcreation:upload:show', API.showUpload);
- App.on('adcreation:budget:show', API.showBudget);
- App.on('adcreation:launch:show', API.showLaunch);
- App.on('adcreation:creation:show', API.showCreation);
- App.on('adcreation:creative:create:show', API.showCreateCreative);
- App.on('adcreation:toolbar:show', API.showToolbar);
- App.on('adcreation:disable', channel.disable);
- App.on('adcreation:enable', channel.enable);
- /**
- * App initializer
- */
- App.addInitializer(function() {
- /** Create router */
- var router = new Module.Router({
- controller: API
- });
- });
- /** @returns {object} */
- Module.getTotals = API.getTotals;
- });
+ module.exports = Module;
---------- diff:
modules/audience/audience-app.js
- AdvisorApp.module('AudienceModule', function(AudienceModule, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- var router;
- /**
- * @class
- */
- var Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'audience/new': 'createAudience',
- 'audience': 'listAudiences',
- 'audience::audienceID/detail': 'audienceDetail'
- }
- });
+ /* module definition */
+ var AudienceModule = {};
- var controller = {
- createAudience: function() {
- AudienceModule.moduleChannel.commands.execute('create:load', {
- region: App.mainRegion
- });
- },
- listAudiences: function() {
- var sessionAttributes = AdvisorApp.module('Entities').session.attributes;
- var isInternal = false;
- /**
- * @param {Backbone.Model} model
- */
- _.each(sessionAttributes.teams.models, function(model) {
- if (model.get('name') === 'SocialCode') isInternal = true;
- });
- if (sessionAttributes.is_superuser || isInternal) {
- AudienceModule.moduleChannel.commands.execute('list:load', {
- region: App.mainRegion
- });
- }
- },
+ var router;
+ var Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'audience/new': 'createAudience',
+ 'audience': 'listAudiences',
+ 'audience::audienceID/detail': 'audienceDetail'
+ }
+ });
+ var controller = {
+ createAudience: function() {
+ AudienceModule.moduleChannel.commands.execute('create:load', {
+ region: App.mainRegion
+ });
+ },
+ listAudiences: function() {
+ var sessionAttributes = AdvisorApp.module('Entities').session.attributes;
+ var isInternal = false;
/**
- * @param {Number} audienceID
+ * @param {Backbone.Model} model
*/
- audienceDetail: function(audienceID) {
- AudienceModule.moduleChannel.commands.execute('detail:load', {
- region: App.mainRegion,
- audienceID: audienceID
+ _.each(sessionAttributes.teams.models, function(model) {
+ if (model.get('name') === 'SocialCode') isInternal = true;
+ });
+ if (sessionAttributes.is_superuser || isInternal) {
+ AudienceModule.moduleChannel.commands.execute('list:load', {
+ region: App.mainRegion
});
}
- };
- AudienceModule.moduleChannel = Backbone.Wreqr.radio.channel('audienceModule');
- App.module('AudienceModule').addInitializer(function() {
- var moduleChannel = this.moduleChannel;
- router = new Router({
- controller: controller
+ },
+ /**
+ * @param {Number} audienceID
+ */
+ audienceDetail: function(audienceID) {
+ AudienceModule.moduleChannel.commands.execute('detail:load', {
+ region: App.mainRegion,
+ audienceID: audienceID
});
- moduleChannel.vent.on('list:show', function() {
- router.navigate('audience/grid');
- controller.listAudiences();
- });
- moduleChannel.vent.on('create:show', function() {
- router.navigate('audience/new');
- controller.createAudience();
- });
- moduleChannel.vent.on('detail:show', function(audienceID) {
- router.navigate('audience/:' + audienceID + '/detail');
- controller.audienceDetail(audienceID);
- });
+ }
+ };
+ AudienceModule.moduleChannel = Backbone.Wreqr.radio.channel('audienceModule');
+ App.module('AudienceModule').addInitializer(function() {
+ var moduleChannel = this.moduleChannel;
+ router = new Router({
+ controller: controller
});
- App.module('AudienceModule').on('start', function() {
- //handle current route
- var currentRoute = App.getCurrentRoute() || '';
- var handlerKey = router.appRoutes[currentRoute];
- var routeHandler = controller[handlerKey];
- if ($.isFunction(routeHandler)) {
- routeHandler();
- }
+ moduleChannel.vent.on('list:show', function() {
+ router.navigate('audience/grid');
+ controller.listAudiences();
});
- this.startWithParent = false;
- App.addInitializer(function() {
- App.initializingSession.done(function() {
- App.module('AudienceModule').start();
- });
+ moduleChannel.vent.on('create:show', function() {
+ router.navigate('audience/new');
+ controller.createAudience();
});
+ moduleChannel.vent.on('detail:show', function(audienceID) {
+ router.navigate('audience/:' + audienceID + '/detail');
+ controller.audienceDetail(audienceID);
+ });
+ });
+ App.module('AudienceModule').on('start', function() {
+ //handle current route
+ var currentRoute = App.getCurrentRoute() || '';
+ var handlerKey = router.appRoutes[currentRoute];
+ var routeHandler = controller[handlerKey];
+ if ($.isFunction(routeHandler)) {
+ routeHandler();
+ }
+ });
+ this.startWithParent = false;
+ App.addInitializer(function() {
+ App.initializingSession.done(function() {
+ App.module('AudienceModule').start();
+ });
+ });
- });
+ module.exports = AudienceModule;
---------- diff:
modules/campaign/campaign-app.js
- AdvisorApp.module('CampaignModule', function(CampaignModule, App) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var router;
- var moduleChannel;
- /**
- * Method that is ran on initialization of the CampaignModule.
- */
- var initializer = function() {
- router = new this.Router({
- controller: this.Controller
- });
+ /* module definition */
+ var CampaignModule = {};
- this.moduleChannel = Backbone.Wreqr.radio.channel('campaignModule');
- this.moduleChannel.vent.on('list:show', function() {
- router.navigate('campaign');
- this.Controller.listCampaigns();
- }, this);
- /**
- * @param {Number} initiativeId
- */
- this.moduleChannel.vent.on('create:show', function(initiativeId) {
- this.Controller.createCampaign(initiativeId);
- }, this);
- /**
- * @param {Number} initiativeId
- * @param {Number} campaignId
- */
- this.moduleChannel.vent.on('edit:show', function(initiativeId, campaignId) {
- router.navigate('initiative/' + initiativeId + '/campaign/' + campaignId + '/edit');
- this.Controller.editCampaign(initiativeId, campaignId);
- }, this);
- /**
- * @param {Number} initiativeId
- * @param {Number} campaignId
- */
- this.moduleChannel.vent.on('campaign:show', function(initiativeId, campaignId) {
- router.navigate('initiative/' + initiativeId + '/campaign/' + campaignId);
- this.Controller.showCampaignAdSetDetail(initiativeId, campaignId);
- }, this);
- };
- /**
- * Method that is ran on start of CampaignModule.
+ var router;
+ var moduleChannel;
+ var initializer = function() {
+ router = new this.Router({
+ controller: this.Controller
+ });
+ this.moduleChannel = Backbone.Wreqr.radio.channel('campaignModule');
+ this.moduleChannel.vent.on('list:show', function() {
+ router.navigate('campaign');
+ this.Controller.listCampaigns();
+ }, this);
+ /**
+ * @param {Number} initiativeId
*/
- var onStart = function() {
- // handle current route
- var currentRoute = App.getCurrentRoute() || '';
- var handlerKey = router.appRoutes[currentRoute];
- var routeHandler = this.Controller[handlerKey];
- if ($.isFunction(routeHandler)) {
- routeHandler();
- }
- };
- AdvisorApp.module('CampaignModule').addInitializer(_(initializer).bind(this));
- AdvisorApp.module('CampaignModule').on('start', _(onStart).bind(this));
- this.startWithParent = false;
- /**
- * Initializes the CampaignModule on start of the AdvisorApp.
+ this.moduleChannel.vent.on('create:show', function(initiativeId) {
+ this.Controller.createCampaign(initiativeId);
+ }, this);
+ /**
+ * @param {Number} initiativeId
+ * @param {Number} campaignId
*/
- AdvisorApp.addInitializer(function() {
- AdvisorApp.initializingSession.done(function() {
- AdvisorApp.module('CampaignModule').start();
- });
+ this.moduleChannel.vent.on('edit:show', function(initiativeId, campaignId) {
+ router.navigate('initiative/' + initiativeId + '/campaign/' + campaignId + '/edit');
+ this.Controller.editCampaign(initiativeId, campaignId);
+ }, this);
+ /**
+ * @param {Number} initiativeId
+ * @param {Number} campaignId
+ */
+ this.moduleChannel.vent.on('campaign:show', function(initiativeId, campaignId) {
+ router.navigate('initiative/' + initiativeId + '/campaign/' + campaignId);
+ this.Controller.showCampaignAdSetDetail(initiativeId, campaignId);
+ }, this);
+ };
+ var onStart = function() {
+ // handle current route
+ var currentRoute = App.getCurrentRoute() || '';
+ var handlerKey = router.appRoutes[currentRoute];
+ var routeHandler = this.Controller[handlerKey];
+ if ($.isFunction(routeHandler)) {
+ routeHandler();
+ }
+ };
+ AdvisorApp.module('CampaignModule').addInitializer(_(initializer).bind(this));
+ AdvisorApp.module('CampaignModule').on('start', _(onStart).bind(this));
+ this.startWithParent = false;
+ AdvisorApp.addInitializer(function() {
+ AdvisorApp.initializingSession.done(function() {
+ AdvisorApp.module('CampaignModule').start();
});
+ });
- });
+ module.exports = CampaignModule;
---------- diff:
modules/campaign/controller.js
- AdvisorApp.module('CampaignModule', function(CampaignModule) {
- this.Controller = {
- /**
- * @param {Number} intiativeId of the campaign's parent initiative.
- * @param {Number} campaignId of the campaign who's details we are showing.
- */
- showCampaignAdSetDetail: function(initiativeId, campaignId, adGenInfo) {
- CampaignModule.moduleChannel.commands.execute('adsetdetail:load', {
- initiativeId: initiativeId,
- campaignId: campaignId,
- adGenInfo: adGenInfo
- });
- },
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
- /**
- * @param {Number} intiativeId of the campaign's parent initiative.
- * @param {Number} campaignId of the campaign who's details we are showing.
- */
- showCampaignAudiencesDetail: function(initiativeId, campaignId) {
- CampaignModule.moduleChannel.commands.execute('audiencesdetail:load', {
- initiativeId: initiativeId,
- campaignId: campaignId
- });
- },
- /**
- * @param {Number} intiativeId of the campaign's parent initiative.
- * @param {Number} campaignId of the campaign who's details we are showing.
- */
- showCampaignAdsDetail: function(initiativeId, campaignId) {
- CampaignModule.moduleChannel.commands.execute('adsdetail:load', {
- initiativeId: initiativeId,
- campaignId: campaignId
- });
- },
+ /* module definition */
+ var CampaignModule = {};
- /**
- * @param {Number} intiativeId of the campaign's parent initiative.
- * @param {Number} campaignId of the campaign who's details we are showing.
- * @param {Number} adsetId of the adset who's adgroups we are showing.
- */
- showCampaignAdGroupDetail: function(initiativeId, campaignId, adsetId) {
- CampaignModule.moduleChannel.commands.execute('adgroupdetail:load', {
- initiativeId: initiativeId,
- campaignId: campaignId,
- adsetId: adsetId
- });
- },
+ this.Controller = {
+ /**
+ * @param {Number} intiativeId of the campaign's parent initiative.
+ * @param {Number} campaignId of the campaign who's details we are showing.
+ */
+ showCampaignAdSetDetail: function(initiativeId, campaignId, adGenInfo) {
+ CampaignModule.moduleChannel.commands.execute('adsetdetail:load', {
+ initiativeId: initiativeId,
+ campaignId: campaignId,
+ adGenInfo: adGenInfo
+ });
+ },
+ /**
+ * @param {Number} intiativeId of the campaign's parent initiative.
+ * @param {Number} campaignId of the campaign who's details we are showing.
+ */
+ showCampaignAudiencesDetail: function(initiativeId, campaignId) {
+ CampaignModule.moduleChannel.commands.execute('audiencesdetail:load', {
+ initiativeId: initiativeId,
+ campaignId: campaignId
+ });
+ },
+ /**
+ * @param {Number} intiativeId of the campaign's parent initiative.
+ * @param {Number} campaignId of the campaign who's details we are showing.
+ */
+ showCampaignAdsDetail: function(initiativeId, campaignId) {
+ CampaignModule.moduleChannel.commands.execute('adsdetail:load', {
+ initiativeId: initiativeId,
+ campaignId: campaignId
+ });
+ },
+ /**
+ * @param {Number} intiativeId of the campaign's parent initiative.
+ * @param {Number} campaignId of the campaign who's details we are showing.
+ * @param {Number} adsetId of the adset who's adgroups we are showing.
+ */
+ showCampaignAdGroupDetail: function(initiativeId, campaignId, adsetId) {
+ CampaignModule.moduleChannel.commands.execute('adgroupdetail:load', {
+ initiativeId: initiativeId,
+ campaignId: campaignId,
+ adsetId: adsetId
+ });
+ },
+ /**
+ * Lists all of our campaigns in Advisor.
+ */
+ listCampaigns: function() {
+ CampaignModule.moduleChannel.commands.execute('campaigns:list:load', {
+ region: AdvisorApp.mainRegion
+ });
+ },
+ /**
+ * @param {Number} initiativeId of the initiative we are going to create a new campaign on.
+ */
+ createCampaign: function(initiativeId) {
+ CampaignModule.moduleChannel.commands.execute('create:load', {
+ region: AdvisorApp.mainRegion,
+ initiativeId: initiativeId
+ });
+ },
+ /**
+ * @param {Number} initiativeID
+ * @param {Number} campaignID
+ */
+ editCampaign: function(initiativeID, campaignID) {
+ CampaignModule.moduleChannel.commands.execute('edit:load', {
+ region: AdvisorApp.mainRegion,
+ initiativeID: initiativeID,
+ campaignID: campaignID
+ });
+ }
+ };
- /**
- * Lists all of our campaigns in Advisor.
- */
- listCampaigns: function() {
- CampaignModule.moduleChannel.commands.execute('campaigns:list:load', {
- region: AdvisorApp.mainRegion
- });
- },
- /**
- * @param {Number} initiativeId of the initiative we are going to create a new campaign on.
- */
- createCampaign: function(initiativeId) {
- CampaignModule.moduleChannel.commands.execute('create:load', {
- region: AdvisorApp.mainRegion,
- initiativeId: initiativeId
- });
- },
- /**
- * @param {Number} initiativeID
- * @param {Number} campaignID
- */
- editCampaign: function(initiativeID, campaignID) {
- CampaignModule.moduleChannel.commands.execute('edit:load', {
- region: AdvisorApp.mainRegion,
- initiativeID: initiativeID,
- campaignID: campaignID
- });
- }
- };
- });
+ module.exports = CampaignModule;
---------- diff:
modules/campaign/router.js
- AdvisorApp.module('CampaignModule', function() {
- this.Router = Marionette.AppRouter.extend({
- // NOTE: (/) on the end of the url path allows for both a trailing slash and no trailing slash.
- // Meaning `initiative/2/campaign/new` and `initiative/2/campaign/new/` go to the same place.
- appRoutes: {
- 'initiative/:id/campaign/new(/)': 'createCampaign', // This must come before campaign/:id
- 'initiative/:id/campaign/:id(/)': 'showCampaignAdSetDetail',
- 'initiative/:id/campaign/:id/ads(/)': 'showCampaignAdsDetail',
- 'initiative/:id/campaign/:id/audiences(/)': 'showCampaignAudiencesDetail',
- 'initiative/:id/campaign/:id/adset/:id(/)': 'showCampaignAdGroupDetail',
- 'initiative/:id/campaign/:id/edit(/)': 'editCampaign',
- 'campaigns(/)': 'listCampaigns'
- }
- });
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
+ /* module definition */
+ var Router = {};
+ this.Router = Marionette.AppRouter.extend({
+ // NOTE: (/) on the end of the url path allows for both a trailing slash and no trailing slash.
+ // Meaning `initiative/2/campaign/new` and `initiative/2/campaign/new/` go to the same place.
+ appRoutes: {
+ 'initiative/:id/campaign/new(/)': 'createCampaign', // This must come before campaign/:id
+ 'initiative/:id/campaign/:id(/)': 'showCampaignAdSetDetail',
+ 'initiative/:id/campaign/:id/ads(/)': 'showCampaignAdsDetail',
+ 'initiative/:id/campaign/:id/audiences(/)': 'showCampaignAudiencesDetail',
+ 'initiative/:id/campaign/:id/adset/:id(/)': 'showCampaignAdGroupDetail',
+ 'initiative/:id/campaign/:id/edit(/)': 'editCampaign',
+ 'campaigns(/)': 'listCampaigns'
+ }
});
+ module.exports = Router;
---------- diff:
modules/audience-profiler/audience-profiler-app.js
- AdvisorApp.module('AudienceProfilerModule', function(AudienceProfilerModule, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- var router;
- /**
- * @class
- */
- var Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'audience-profiler': 'audienceProfiler'
- }
- });
+ /* module definition */
+ var AudienceProfilerModule = {};
- var controller = {
- audienceProfiler: function() {
- // TODO: perhaps add some access control.
- AudienceProfilerModule.moduleChannel.commands.execute('audience-profiler:load', {
- region: App.mainRegion
- });
- }
- };
- AudienceProfilerModule.moduleChannel = Backbone.Wreqr.radio.channel('audienceProfileModule');
- App.module('AudienceProfilerModule').addInitializer(function() {
- var moduleChannel = this.moduleChannel;
- router = new Router({
- controller: controller
+ var router;
+ var Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'audience-profiler': 'audienceProfiler'
+ }
+ });
+ var controller = {
+ audienceProfiler: function() {
+ // TODO: perhaps add some access control.
+ AudienceProfilerModule.moduleChannel.commands.execute('audience-profiler:load', {
+ region: App.mainRegion
});
- moduleChannel.vent.on('audience-profiler:show', function() {
- router.navigate('audience-profiler');
- controller.audienceProfiler();
- });
+ }
+ };
+ AudienceProfilerModule.moduleChannel = Backbone.Wreqr.radio.channel('audienceProfileModule');
+ App.module('AudienceProfilerModule').addInitializer(function() {
+ var moduleChannel = this.moduleChannel;
+ router = new Router({
+ controller: controller
});
- App.module('AudienceProfilerModule').on('start', function() {
- // handle current route
- var currentRoute = App.getCurrentRoute() || '';
- var handlerKey = router.appRoutes[currentRoute];
- var routeHandler = controller[handlerKey];
- if ($.isFunction(routeHandler)) {
- routeHandler();
- }
+ moduleChannel.vent.on('audience-profiler:show', function() {
+ router.navigate('audience-profiler');
+ controller.audienceProfiler();
});
- this.startWithParent = false;
- App.addInitializer(function() {
- App.initializingSession.done(function() {
- App.module('AudienceProfilerModule').start();
- });
+ });
+ App.module('AudienceProfilerModule').on('start', function() {
+ // handle current route
+ var currentRoute = App.getCurrentRoute() || '';
+ var handlerKey = router.appRoutes[currentRoute];
+ var routeHandler = controller[handlerKey];
+ if ($.isFunction(routeHandler)) {
+ routeHandler();
+ }
+ });
+ this.startWithParent = false;
+ App.addInitializer(function() {
+ App.initializingSession.done(function() {
+ App.module('AudienceProfilerModule').start();
});
+ });
- });
+ module.exports = AudienceProfilerModule;
---------- diff:
modules/creative/creative-app.js
- AdvisorApp.module('CreativeApp', function(Creative, AdvisorApp) {
- /**
- * @class
- */
- Creative.Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'creatives/new/{platform}': 'newCreative',
- 'creatives(/)': 'listCreatives'
- }
- });
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- var API = {
- /**
- * @param {Object} options
- */
- listCreatives: function(options) {
- return Creative.List.Controller.listCreatives(options);
- },
- newCreative: function(platform) {
- return Creative.Creation.Controller.newCreative({
- platform: platform
- });
- }
- };
+ /* module definition */
+ var Creative = {};
+ Creative.Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'creatives/new/{platform}': 'newCreative',
+ 'creatives(/)': 'listCreatives'
+ }
+ });
+ var API = {
/**
* @param {Object} options
*/
- AdvisorApp.reqres.setHandler('creative:list:view', API.listCreatives);
- AdvisorApp.on('creative:list', API.listCreatives);
- AdvisorApp.on('creative:new', API.newCreative);
- AdvisorApp.addInitializer(function() {
- var router = new Creative.Router({
- controller: API
+ listCreatives: function(options) {
+ return Creative.List.Controller.listCreatives(options);
+ },
+ newCreative: function(platform) {
+ return Creative.Creation.Controller.newCreative({
+ platform: platform
});
+ }
+ };
+ AdvisorApp.reqres.setHandler('creative:list:view', API.listCreatives);
+ AdvisorApp.on('creative:list', API.listCreatives);
+ AdvisorApp.on('creative:new', API.newCreative);
+ AdvisorApp.addInitializer(function() {
+ var router = new Creative.Router({
+ controller: API
});
});
+ module.exports = Creative;
---------- diff:
modules/header/header-app.js
- AdvisorApp.module('HeaderApp', function() {
- /**
- * Create controller APIs for AdvisorApp.module('HeaderApp')
- */
- var API = {
- listHeader: function() {
- this.List.Controller.listHeader();
- },
- showToolbar: function(toolbarView) {
- this.List.Controller.showToolbar(toolbarView);
- }
- };
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../app');
- /**
- * Listeners
- */
- this.on('start', _(API.listHeader).bind(this));
- AdvisorApp.on('header:toolbar:show', _(API.showToolbar).bind(this));
- });
+ /* module definition */
+ var Header-app = {};
+ var API = {
+ listHeader: function() {
+ this.List.Controller.listHeader();
+ },
+ showToolbar: function(toolbarView) {
+ this.List.Controller.showToolbar(toolbarView);
+ }
+ };
+ this.on('start', _(API.listHeader).bind(this));
+ AdvisorApp.on('header:toolbar:show', _(API.showToolbar).bind(this));
+ module.exports = Header-app;
---------- diff:
modules/initiative/initiative-app.js
- AdvisorApp.module('InitiativeModule', function(InitiativeModule, App) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- var router;
+ /* module definition */
+ var InitiativeModule = {};
+ var router;
+ var Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'initiative(/)': 'showInitiativeCards',
+ 'initiative/:initiativeID(/)': 'initiativeDetail',
+ 'initiative/:initiativeID/edit(/)': 'editInitiative'
+ }
+ });
+ var controller = {
+ createInitiative: function() {
+ InitiativeModule.moduleChannel.commands.execute('create:load');
+ },
+ showInitiativeCards: function() {
+ InitiativeModule.moduleChannel.commands.execute('cards:load', {
+ region: App.mainRegion
+ });
+ },
/**
- * @class
+ * @param {Number} initiativeID
*/
- var Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'initiative(/)': 'showInitiativeCards',
- 'initiative/:initiativeID(/)': 'initiativeDetail',
- 'initiative/:initiativeID/edit(/)': 'editInitiative'
- }
- });
- var controller = {
- createInitiative: function() {
- InitiativeModule.moduleChannel.commands.execute('create:load');
- },
- showInitiativeCards: function() {
- InitiativeModule.moduleChannel.commands.execute('cards:load', {
- region: App.mainRegion
- });
- },
- /**
- * @param {Number} initiativeID
- */
- initiativeDetail: function(initiativeID) {
- InitiativeModule.moduleChannel.commands.execute('detail:load', {
- region: App.mainRegion,
- initiativeID: initiativeID
- });
- },
- /**
- * @param {Number} initiativeID
- */
- editInitiative: function(initiativeID) {
- InitiativeModule.moduleChannel.commands.execute('edit:load', {
- region: App.mainRegion,
- initiativeID: initiativeID
- });
- },
- /**
- * @param {Object} options
- */
- selectAssets: function(options) {
- InitiativeModule.moduleChannel.commands.execute('assets:card:selector', options);
- }
- };
- InitiativeModule.moduleChannel = Backbone.Wreqr.radio.channel('initiativeModule');
- App.module('InitiativeModule').addInitializer(function() {
- var moduleChannel = this.moduleChannel;
- router = new Router({
- controller: controller
+ initiativeDetail: function(initiativeID) {
+ InitiativeModule.moduleChannel.commands.execute('detail:load', {
+ region: App.mainRegion,
+ initiativeID: initiativeID
});
- moduleChannel.vent.on('cards:show', function() {
- router.navigate('initiative');
- controller.showInitiativeCards();
+ },
+ /**
+ * @param {Number} initiativeID
+ */
+ editInitiative: function(initiativeID) {
+ InitiativeModule.moduleChannel.commands.execute('edit:load', {
+ region: App.mainRegion,
+ initiativeID: initiativeID
});
- moduleChannel.vent.on('create:show', function() {
- controller.createInitiative();
- });
- /**
- * @param {Number} initiativeID
- */
- moduleChannel.vent.on('detail:show', function(initiativeID) {
- router.navigate('initiative/' + initiativeID);
- controller.initiativeDetail(initiativeID);
- });
- /**
- * @param {Number} initiativeID
- */
- moduleChannel.vent.on('edit:show', function(initiativeID) {
- router.navigate('initiative/' + initiativeID + '/edit');
- controller.editInitiative(initiativeID);
- });
- /**
- * @param {Object} options
- */
- moduleChannel.vent.on('initiative:assets:select', function(options) {
- controller.selectAssets(options);
- });
+ },
+ /**
+ * @param {Object} options
+ */
+ selectAssets: function(options) {
+ InitiativeModule.moduleChannel.commands.execute('assets:card:selector', options);
+ }
+ };
+ InitiativeModule.moduleChannel = Backbone.Wreqr.radio.channel('initiativeModule');
+ App.module('InitiativeModule').addInitializer(function() {
+ var moduleChannel = this.moduleChannel;
+ router = new Router({
+ controller: controller
});
- App.module('InitiativeModule').on('start', function() {
- //handle current route
- var currentRoute = App.getCurrentRoute() || '';
- if (/^initiative/.test(currentRoute)) {
- _.any(Backbone.history.handlers, function(handler) {
- if (handler.route.test(currentRoute)) {
- handler.callback(currentRoute);
- return true;
- }
- });
- }
+ moduleChannel.vent.on('cards:show', function() {
+ router.navigate('initiative');
+ controller.showInitiativeCards();
});
- this.startWithParent = false;
- App.addInitializer(function() {
- App.initializingSession.done(function() {
- App.module('InitiativeModule').start();
+ moduleChannel.vent.on('create:show', function() {
+ controller.createInitiative();
+ });
+ /**
+ * @param {Number} initiativeID
+ */
+ moduleChannel.vent.on('detail:show', function(initiativeID) {
+ router.navigate('initiative/' + initiativeID);
+ controller.initiativeDetail(initiativeID);
+ });
+ /**
+ * @param {Number} initiativeID
+ */
+ moduleChannel.vent.on('edit:show', function(initiativeID) {
+ router.navigate('initiative/' + initiativeID + '/edit');
+ controller.editInitiative(initiativeID);
+ });
+ /**
+ * @param {Object} options
+ */
+ moduleChannel.vent.on('initiative:assets:select', function(options) {
+ controller.selectAssets(options);
+ });
+ });
+ App.module('InitiativeModule').on('start', function() {
+ //handle current route
+ var currentRoute = App.getCurrentRoute() || '';
+ if (/^initiative/.test(currentRoute)) {
+ _.any(Backbone.history.handlers, function(handler) {
+ if (handler.route.test(currentRoute)) {
+ handler.callback(currentRoute);
+ return true;
+ }
});
+ }
+ });
+ this.startWithParent = false;
+ App.addInitializer(function() {
+ App.initializingSession.done(function() {
+ App.module('InitiativeModule').start();
});
+ });
- });
+ module.exports = InitiativeModule;
---------- diff:
modules/insertion/insertion-app.js
- AdvisorApp.module('InsertionOrderModule', function(InsertionOrderModule, App) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- var router;
+ /* module definition */
+ var InsertionOrderModule = {};
+ var router;
+ var Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'insertionorder(/)': 'showInsertionOrderCards',
+ 'insertionorder/:insertionOrderID(/)': 'insertionOrderDetail'
+ }
+ });
+ var controller = {
+ createInsertion: function() {
+ InsertionOrderModule.moduleChannel.commands.execute('create:load');
+ },
+ showInsertionOrderCards: function() {
+ InsertionOrderModule.moduleChannel.commands.execute('cards:load', {
+ region: App.mainRegion
+ });
+ },
/**
- * @class
+ * @param {Number} insertionOrderID
*/
- var Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'insertionorder(/)': 'showInsertionOrderCards',
- 'insertionorder/:insertionOrderID(/)': 'insertionOrderDetail'
- }
- });
- var controller = {
- createInsertion: function() {
- InsertionOrderModule.moduleChannel.commands.execute('create:load');
- },
- showInsertionOrderCards: function() {
- InsertionOrderModule.moduleChannel.commands.execute('cards:load', {
- region: App.mainRegion
- });
- },
- /**
- * @param {Number} insertionOrderID
- */
- insertionOrderDetail: function(insertionOrderID) {
- InsertionOrderModule.moduleChannel.commands.execute('detail:load', {
- region: App.mainRegion,
- insertionOrderID: insertionOrderID
- });
- }
- };
- InsertionOrderModule.moduleChannel = Backbone.Wreqr.radio.channel('insertionOrderModule');
- App.module('InsertionOrderModule').addInitializer(function() {
- var moduleChannel = this.moduleChannel;
- router = new Router({
- controller: controller
+ insertionOrderDetail: function(insertionOrderID) {
+ InsertionOrderModule.moduleChannel.commands.execute('detail:load', {
+ region: App.mainRegion,
+ insertionOrderID: insertionOrderID
});
- moduleChannel.vent.on('cards:show', function() {
- router.navigate('insertionorder');
- controller.showInsertionCards();
- });
- moduleChannel.vent.on('list:show', function() {
- router.navigate('insertionorder/grid');
- controller.listInsertions();
- });
- moduleChannel.vent.on('create:show', function() {
- controller.createInsertion();
- });
- /**
- * @param {Number} insertionOrderID
- */
- moduleChannel.vent.on('detail:show', function(insertionOrderID) {
- router.navigate('insertionorder/' + insertionOrderID);
- controller.insertionDetail(insertionOrderID);
- });
+ }
+ };
+ InsertionOrderModule.moduleChannel = Backbone.Wreqr.radio.channel('insertionOrderModule');
+ App.module('InsertionOrderModule').addInitializer(function() {
+ var moduleChannel = this.moduleChannel;
+ router = new Router({
+ controller: controller
});
- App.module('InsertionOrderModule').on('start', function() {
- //handle current route
- var currentRoute = App.getCurrentRoute() || '';
- if (/^insertionorder/.test(currentRoute)) {
- _.any(Backbone.history.handlers, function(handler) {
- if (handler.route.test(currentRoute)) {
- handler.callback(currentRoute);
- return true;
- }
- });
- }
+ moduleChannel.vent.on('cards:show', function() {
+ router.navigate('insertionorder');
+ controller.showInsertionCards();
});
- this.startWithParent = false;
- App.addInitializer(function() {
- App.initializingSession.done(function() {
- App.module('InsertionOrderModule').start();
+ moduleChannel.vent.on('list:show', function() {
+ router.navigate('insertionorder/grid');
+ controller.listInsertions();
+ });
+ moduleChannel.vent.on('create:show', function() {
+ controller.createInsertion();
+ });
+ /**
+ * @param {Number} insertionOrderID
+ */
+ moduleChannel.vent.on('detail:show', function(insertionOrderID) {
+ router.navigate('insertionorder/' + insertionOrderID);
+ controller.insertionDetail(insertionOrderID);
+ });
+ });
+ App.module('InsertionOrderModule').on('start', function() {
+ //handle current route
+ var currentRoute = App.getCurrentRoute() || '';
+ if (/^insertionorder/.test(currentRoute)) {
+ _.any(Backbone.history.handlers, function(handler) {
+ if (handler.route.test(currentRoute)) {
+ handler.callback(currentRoute);
+ return true;
+ }
});
+ }
+ });
+ this.startWithParent = false;
+ App.addInitializer(function() {
+ App.initializingSession.done(function() {
+ App.module('InsertionOrderModule').start();
});
+ });
- });
+ module.exports = InsertionOrderModule;
---------- diff:
modules/custom-report/custom-report-button.js
- AdvisorApp.module('Common.Views', function(Views, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var moment = require('moment');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- this.CustomReportButton = Marionette.ItemView.extend({
- template: Handlebars.templates.CustomReportButton,
- events: {
- 'click .custom-report-button': 'showModal',
- 'click .error': 'downloadCurrentReport'
- },
+ /* module definition */
+ var Views = {};
- initialize: function(opts) {
- if (opts) {
- this.options = _.extend(this.options || {}, opts);
+ this.CustomReportButton = Marionette.ItemView.extend({
+ template: Handlebars.templates.CustomReportButton,
+ events: {
+ 'click .custom-report-button': 'showModal',
+ 'click .error': 'downloadCurrentReport'
+ },
+ initialize: function(opts) {
+ if (opts) {
+ this.options = _.extend(this.options || {}, opts);
+ }
+ },
+ save: function(params, e) {
+ var columnOptions = App.request('report:excluded_columns_options');
+ // invert our selection, the server wants excluded columns.
+ var excluded = columnOptions.filter(function(m) {
+ return !_.contains(params.metrics, m);
+ });
+ var data = {
+ sc_campaign_id: this.options.campaign.get('id'),
+ start_date: moment(params.dateRange[0]).format('YYYY-MM-DD'),
+ end_date: moment(params.dateRange[1]).format('YYYY-MM-DD'),
+ excluded_columns: excluded,
+ email: params.emails.join(','),
+ // TODO: if this is specified, it should create a new report. Need an API Endpoint for that, or to make sure the current endpoint will save them.
+ title: params.name
+ };
+ this.currentData = data;
+ this.downloadCurrentReport();
+ },
+ downloadCurrentReport: function(e) {
+ if (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ this.setError(false);
+ this.setLoading(true);
+ var data = this.currentData;
+ var self = this;
+ var err = function(xhr, response) {
+ // this is triggered either by calling directly, or in the case of failed xhr it is called with the status second.
+ if (!response) {
+ response = xhr;
}
- },
- save: function(params, e) {
- var columnOptions = App.request('report:excluded_columns_options');
- // invert our selection, the server wants excluded columns.
- var excluded = columnOptions.filter(function(m) {
- return !_.contains(params.metrics, m);
- });
- var data = {
- sc_campaign_id: this.options.campaign.get('id'),
- start_date: moment(params.dateRange[0]).format('YYYY-MM-DD'),
- end_date: moment(params.dateRange[1]).format('YYYY-MM-DD'),
- excluded_columns: excluded,
- email: params.emails.join(','),
- // TODO: if this is specified, it should create a new report. Need an API Endpoint for that, or to make sure the current endpoint will save them.
- title: params.name
- };
- this.currentData = data;
- this.downloadCurrentReport();
- },
- downloadCurrentReport: function(e) {
- if (e) {
- e.stopPropagation();
- e.preventDefault();
+ // when calling report:entity, the response does not contain an error and is an object. This handles that case.
+ if (_.isObject(response)) {
+ console.error('Bad response: ', response);
+ response = 'Unknown error.';
}
- this.setError(false);
- this.setLoading(true);
- var data = this.currentData;
- var self = this;
- var err = function(xhr, response) {
- // this is triggered either by calling directly, or in the case of failed xhr it is called with the status second.
- if (!response) {
- response = xhr;
- }
- // when calling report:entity, the response does not contain an error and is an object. This handles that case.
- if (_.isObject(response)) {
- console.error('Bad response: ', response);
- response = 'Unknown error.';
- }
- var error = new Error(response);
- self.setError(error);
- self.trigger('error', error);
- };
- $.ajax({
- type: 'POST',
- url: '/api/reports/v2/reportconfiguration/campaign-wrapup-v1/run/',
- data: $.param(data, true)
- }).then(function(r, status, xhr) {
- var location = xhr.getResponseHeader('location');
- var id = location.match(/\/([^\/]+)\/$/);
- if (id) {
- App.request('report:entity', id[1]).then(function(report) {
- report.download();
- report.on('download:complete', _.bind(self.setLoading, self, false));
- report.on('download:error', err);
- }, err);
- } else {
- err('invalid location returned from /run endpoint.');
- }
- }, err);
- },
- setLoading: function(loading) {
- this.$('.spinner').toggle(loading);
- this.$('.icon').toggle(!loading);
- },
- setError: function(error) {
- this.setLoading(false);
- this.$('.icon').toggle(!error);
- var errEl = this.$('.error').toggle(error);
- if (error) {
- errEl.tooltip({ 'trigger':'hover', 'title': error.message + '\nClick to retry.'});
+ var error = new Error(response);
+ self.setError(error);
+ self.trigger('error', error);
+ };
+ $.ajax({
+ type: 'POST',
+ url: '/api/reports/v2/reportconfiguration/campaign-wrapup-v1/run/',
+ data: $.param(data, true)
+ }).then(function(r, status, xhr) {
+ var location = xhr.getResponseHeader('location');
+ var id = location.match(/\/([^\/]+)\/$/);
+ if (id) {
+ App.request('report:entity', id[1]).then(function(report) {
+ report.download();
+ report.on('download:complete', _.bind(self.setLoading, self, false));
+ report.on('download:error', err);
+ }, err);
+ } else {
+ err('invalid location returned from /run endpoint.');
}
- },
+ }, err);
+ },
+ setLoading: function(loading) {
+ this.$('.spinner').toggle(loading);
+ this.$('.icon').toggle(!loading);
+ },
+ setError: function(error) {
+ this.setLoading(false);
+ this.$('.icon').toggle(!error);
+ var errEl = this.$('.error').toggle(error);
+ if (error) {
+ errEl.tooltip({ 'trigger':'hover', 'title': error.message + '\nClick to retry.'});
+ }
+ },
+ /**
+ * showModal - Show the selection modal.
+ * Perhaps could be broken in to it's own view, but this works for now and is easy to remove.
+ *
+ * @param {Event} event a jQuery event.
+ */
+ showModal: function(event) {
+ event.preventDefault();
+ this.setError(false);
+ var customReportView = new App.module('Common.Views').CustomReport(this.options.campaign);
+ this.listenTo(customReportView, 'save', this.save);
+ AdvisorApp.modalRegion.show(customReportView);
+ }
+ });
- /**
- * showModal - Show the selection modal.
- * Perhaps could be broken in to it's own view, but this works for now and is easy to remove.
- *
- * @param {Event} event a jQuery event.
- */
- showModal: function(event) {
- event.preventDefault();
- this.setError(false);
- var customReportView = new App.module('Common.Views').CustomReport(this.options.campaign);
- this.listenTo(customReportView, 'save', this.save);
- AdvisorApp.modalRegion.show(customReportView);
- }
- });
- }, moment);
+ module.exports = Views;
---------- diff:
modules/custom-report/custom-report-dropdown-view.js
- // TODO: Remove the underscore from the front of this filename once we have a proper import solution.
- AdvisorApp.module('Common.Views', function(Views, App) {
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var M = Backbone.Model.extend();
- var ReportsCollection = Backbone.Collection.extend({
- url: '/custom-reports-fixture',
- fetch: function() {
- var d = $.Deferred();
- var self = this;
- setTimeout(function() {
- var model = new M({
- name: 'Reach, CTR, Clicks',
- metrics: [
- 'Reach',
- 'CTR',
- 'Link Clicks'
- ]
- });
- self.add(model);
- d.resolve({objects: [model.toJSON()]});
- }, 200);
- return d;
- },
- parse: function(o) {
- return o.objects;
- },
- model: Backbone.Model.extend()
- });
- var collection = new ReportsCollection();
+ // TODO: Remove the underscore from the front of this filename once we have a proper import solution.
+ /* module definition */
+ var Views = {};
- this.CustomReportDropdown = Marionette.ItemView.extend({
- collection: collection,
- template: Handlebars.templates.CustomReportDropdown,
- events: {
- 'click .report-selector a': 'newSelection'
- },
- getItems: function() {
- return [{
- name: '+ New Report'
- }].concat(this.collection.invoke('toJSON'));
- },
- /**
- * @returns {Object} of variables we want accessible in our Handlebar Template.
- */
- templateHelpers: function() {
- return {
- controlValue: this.selectedItem(),
- reports: this.getItems()
- };
- },
- onShow: function() {
- var self = this;
- this.collection.fetch().then(function(obj) {
- self.render();
- self.trigger('loaded');
+ var M = Backbone.Model.extend();
+ var ReportsCollection = Backbone.Collection.extend({
+ url: '/custom-reports-fixture',
+ fetch: function() {
+ var d = $.Deferred();
+ var self = this;
+ setTimeout(function() {
+ var model = new M({
+ name: 'Reach, CTR, Clicks',
+ metrics: [
+ 'Reach',
+ 'CTR',
+ 'Link Clicks'
+ ]
});
- },
+ self.add(model);
+ d.resolve({objects: [model.toJSON()]});
+ }, 200);
+ return d;
+ },
+ parse: function(o) {
+ return o.objects;
+ },
+ model: Backbone.Model.extend()
+ });
+ var collection = new ReportsCollection();
+ this.CustomReportDropdown = Marionette.ItemView.extend({
+ collection: collection,
+ template: Handlebars.templates.CustomReportDropdown,
+ events: {
+ 'click .report-selector a': 'newSelection'
+ },
+ getItems: function() {
+ return [{
+ name: '+ New Report'
+ }].concat(this.collection.invoke('toJSON'));
+ },
+ /**
+ * @returns {Object} of variables we want accessible in our Handlebar Template.
+ */
+ templateHelpers: function() {
+ return {
+ controlValue: this.selectedItem(),
+ reports: this.getItems()
+ };
+ },
+ onShow: function() {
+ var self = this;
+ this.collection.fetch().then(function(obj) {
+ self.render();
+ self.trigger('loaded');
+ });
+ },
+ selectedItem: function() {
+ return this.getItems()[this.options.selectedIndex || 0];
+ },
+ newSelection: function(event) {
+ event.preventDefault();
+ this.options.selectedIndex = $(event.target).data('index');
+ this.trigger('change', this.selectedItem());
+ this.render();
+ }
+ });
- selectedItem: function() {
- return this.getItems()[this.options.selectedIndex || 0];
- },
- newSelection: function(event) {
- event.preventDefault();
- this.options.selectedIndex = $(event.target).data('index');
- this.trigger('change', this.selectedItem());
- this.render();
- }
- });
- });
+ module.exports = Views;
---------- diff:
modules/custom-report/custom-report.js
- AdvisorApp.module('Common.Views', function(Views, App) {
+ /* global dependencies */
+ var moment = require('moment');
+ var _ = require('underscore');
+ var $ = require('jquery');
+ var Marionette = require('marionette');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../app');
- var MetricsCollection = Backbone.Collection.extend({
- url: '/api/reports/v2/reportconfiguration/creative-insights/',
- parse: function(obj) {
- return obj.parameters_schema.properties.excluded_columns.items.enum;
- },
- model: Backbone.Model.extend({
- parse: function(str) {
- return {
- name: str
- };
- }
- })
- });
- var collection = new MetricsCollection();
+ /* module definition */
+ var Views = {};
- var SelectButtonsView = Marionette.ItemView.extend({
- collection: collection,
- template: Handlebars.templates.CustomReportSelectButtons,
- events: {
- 'click .select-all': 'toggleSelectAll',
- 'click .metric': 'toggleMetric'
- },
- currentReport: {},
- metrics: [],
- templateHelpers: function() {
+ var MetricsCollection = Backbone.Collection.extend({
+ url: '/api/reports/v2/reportconfiguration/creative-insights/',
+ parse: function(obj) {
+ return obj.parameters_schema.properties.excluded_columns.items.enum;
+ },
+ model: Backbone.Model.extend({
+ parse: function(str) {
return {
- selectedReport: this.selectedReport
+ name: str
};
- },
- /**
- * Select buttons
- */
- toggleSelectAll: function() {
- var m = this.$('.metric');
- if (this.allSelected()) {
- m.removeClass('selected');
- } else {
- m.addClass('selected');
- }
+ }
+ })
+ });
+ var collection = new MetricsCollection();
+ var SelectButtonsView = Marionette.ItemView.extend({
+ collection: collection,
+ template: Handlebars.templates.CustomReportSelectButtons,
+ events: {
+ 'click .select-all': 'toggleSelectAll',
+ 'click .metric': 'toggleMetric'
+ },
+ currentReport: {},
+ metrics: [],
+ templateHelpers: function() {
+ return {
+ selectedReport: this.selectedReport
+ };
+ },
+ /**
+ * Select buttons
+ */
+ toggleSelectAll: function() {
+ var m = this.$('.metric');
+ if (this.allSelected()) {
+ m.removeClass('selected');
+ } else {
+ m.addClass('selected');
+ }
+ this.refreshSelectAll();
+ },
+ toggleMetric: function(e) {
+ if (!this.currentReport.metrics) {
+ $(e.target).toggleClass('selected');
this.refreshSelectAll();
- },
- toggleMetric: function(e) {
- if (!this.currentReport.metrics) {
- $(e.target).toggleClass('selected');
- this.refreshSelectAll();
- }
- },
- allSelected: function() {
- return this.$('.metric').not('.selected').length === 0;
- },
- refreshSelectAll: function() {
- this.metrics = _.map(this.$('.selected.metric').get(), function(el) { return $(el).text(); });
- var b = this.$('.select-all');
- var all = this.allSelected();
- if (all) {
- b.addClass('selected');
- } else {
- b.removeClass('selected');
- }
- },
- setCurrentReport: function(report) {
- this.metrics = report.metrics || [];
- if (!report.metrics) {
- // report.data is not set on the new report option, so we're making a new report.
- this.$el.find('.metric-button').show().find('.btn').removeClass('selected');
- } else {
- this.$el.find('.metric-button').each(function() {
- var el = $(this);
- if (_.contains(report.metrics, el.data('metric'))) {
- el.show().find('.btn').addClass('selected');
- } else {
- el.hide();
- }
- });
- }
- },
- fetchMetrics: function() {
- var opts = App.request('report:excluded_columns_options');
- var self = this;
- _.each(opts, function(o) {
- self.collection.add(new Backbone.Model({name: o}));
+ }
+ },
+ allSelected: function() {
+ return this.$('.metric').not('.selected').length === 0;
+ },
+ refreshSelectAll: function() {
+ this.metrics = _.map(this.$('.selected.metric').get(), function(el) { return $(el).text(); });
+ var b = this.$('.select-all');
+ var all = this.allSelected();
+ if (all) {
+ b.addClass('selected');
+ } else {
+ b.removeClass('selected');
+ }
+ },
+ setCurrentReport: function(report) {
+ this.metrics = report.metrics || [];
+ if (!report.metrics) {
+ // report.data is not set on the new report option, so we're making a new report.
+ this.$el.find('.metric-button').show().find('.btn').removeClass('selected');
+ } else {
+ this.$el.find('.metric-button').each(function() {
+ var el = $(this);
+ if (_.contains(report.metrics, el.data('metric'))) {
+ el.show().find('.btn').addClass('selected');
+ } else {
+ el.hide();
+ }
});
- // FIXME: decide if we want this to be our local data (above), or do the below (which will fetch it from the server)
- // this.collection
- // .fetch()
- // .then(_.bind(this.render, this));
- },
- onShow: function() {
- this.fetchMetrics();
}
- });
+ },
+ fetchMetrics: function() {
+ var opts = App.request('report:excluded_columns_options');
+ var self = this;
+ _.each(opts, function(o) {
+ self.collection.add(new Backbone.Model({name: o}));
+ });
+ // FIXME: decide if we want this to be our local data (above), or do the below (which will fetch it from the server)
+ // this.collection
+ // .fetch()
+ // .then(_.bind(this.render, this));
+ },
+ onShow: function() {
+ this.fetchMetrics();
+ }
+ });
+ this.CustomReport = Marionette.LayoutView.extend({
+ collection: collection,
+ template: Handlebars.templates.CustomReport,
+ initialize: function(campaign) {
+ this.campaign = campaign;
+ },
+ regions: {
+ reportDropdownRegion: '.report-dropdown',
+ selectDateRegion: '.select-date',
+ selectButtonsRegion: '.select-buttons'
+ },
+ events: {
+ 'click .add-email': 'addEmail',
+ 'click .save-download': 'save'
+ },
+ options: {
+ title: 'customize report',
+ classList: 'custom-report-modal'
+ },
+ addEmail: function(e) {
+ e.preventDefault();
+ this.$('.add-email').hide();
+ this.$('.email-field').show().find('input').focus();
+ },
+ setupDateRangePicker: function() {
+ var dateRanges = AdvisorApp.module('Common.Views').DateRangePicker.prototype.defaults.dateRanges;
+ this.dateRangeView = new (AdvisorApp.module('Common.Views')).DateRangePicker({
+ dateRanges: _.omit(dateRanges, 'Yesterday', 'Today', 'Ending today', 'Ending next 7 days'),
+ end: moment(this.campaign.get('end_date')),
+ start: moment(this.campaign.get('start_date'))
+ });
+ this.listenTo(this.dateRangeView.model, 'change:start change:end', this.changeDateRange);
+ this.changeDateRange(this.dateRangeView.model);
+ },
+ getMoment: function(date) {
+ return moment.isMoment(date) ? date : moment(date);
+ },
+ changeDateRange: function(model) {
+ this.selectedDateRange = [model.get('start'), model.get('end')];
+ },
+ /**
+ * report picker
+ */
+ setupReportPicker: function() {
+ this.reportPickerView = new (AdvisorApp.module('Common.Views')).CustomReportDropdown();
+ this.listenTo(this.reportPickerView, 'change', _.bind(this.reportSelected, this));
+ },
+ reportSelected: function(report) {
+ if (this.selectedReport && this.selectedReport.name === report.name) {
+ return;
+ }
+ this.selectedReport = report;
+ // update our state based on the selected report
+ if (!report.metrics) {
+ this.$('.report-title-row').show();
+ } else {
+ this.$('.report-title-row').hide();
+ }
+ this.selectButtonsView.setCurrentReport(report);
+ },
+ /**
+ * save - saves the current report.
+ */
+ save: function(e) {
+ e.preventDefault();
+ var reportName = this.$('.report-name').val();
+ var trim = function(s) { return s.trim(); };
+ var emails = _.map(this.$('.report-emails').val().split(','), trim);
+ var metrics = this.selectButtonsView.metrics;
+ // if (reportName.length) {
+ // // TODO: save this report
+ // }
+ var data = {
+ name: reportName,
+ emails: emails,
+ metrics: metrics,
+ dateRange: this.selectedDateRange
+ };
+ this.trigger('save', data);
+ this.triggerMethod('modal:close');
+ },
+ /**
+ * Rendering
+ */
+ onRender: function() {
+ this.setupReportPicker();
+ this.setupDateRangePicker();
+ },
+ onShow: function() {
+ this.selectButtonsView = new SelectButtonsView();
+ this.selectButtonsRegion.show(this.selectButtonsView);
+ this.reportDropdownRegion.show(this.reportPickerView);
+ this.selectDateRegion.show(this.dateRangeView);
+ this.reportSelected(this.reportPickerView.selectedItem());
+ }
+ });
- this.CustomReport = Marionette.LayoutView.extend({
- collection: collection,
- template: Handlebars.templates.CustomReport,
- initialize: function(campaign) {
- this.campaign = campaign;
- },
- regions: {
- reportDropdownRegion: '.report-dropdown',
- selectDateRegion: '.select-date',
- selectButtonsRegion: '.select-buttons'
- },
- events: {
- 'click .add-email': 'addEmail',
- 'click .save-download': 'save'
- },
- options: {
- title: 'customize report',
- classList: 'custom-report-modal'
- },
- addEmail: function(e) {
- e.preventDefault();
- this.$('.add-email').hide();
- this.$('.email-field').show().find('input').focus();
- },
- setupDateRangePicker: function() {
- var dateRanges = AdvisorApp.module('Common.Views').DateRangePicker.prototype.defaults.dateRanges;
- this.dateRangeView = new (AdvisorApp.module('Common.Views')).DateRangePicker({
- dateRanges: _.omit(dateRanges, 'Yesterday', 'Today', 'Ending today', 'Ending next 7 days'),
- end: moment(this.campaign.get('end_date')),
- start: moment(this.campaign.get('start_date'))
- });
- this.listenTo(this.dateRangeView.model, 'change:start change:end', this.changeDateRange);
- this.changeDateRange(this.dateRangeView.model);
- },
- getMoment: function(date) {
- return moment.isMoment(date) ? date : moment(date);
- },
- changeDateRange: function(model) {
- this.selectedDateRange = [model.get('start'), model.get('end')];
- },
- /**
- * report picker
- */
- setupReportPicker: function() {
- this.reportPickerView = new (AdvisorApp.module('Common.Views')).CustomReportDropdown();
- this.listenTo(this.reportPickerView, 'change', _.bind(this.reportSelected, this));
- },
- reportSelected: function(report) {
- if (this.selectedReport && this.selectedReport.name === report.name) {
- return;
- }
- this.selectedReport = report;
- // update our state based on the selected report
- if (!report.metrics) {
- this.$('.report-title-row').show();
- } else {
- this.$('.report-title-row').hide();
- }
- this.selectButtonsView.setCurrentReport(report);
- },
- /**
- * save - saves the current report.
- */
- save: function(e) {
- e.preventDefault();
- var reportName = this.$('.report-name').val();
- var trim = function(s) { return s.trim(); };
- var emails = _.map(this.$('.report-emails').val().split(','), trim);
- var metrics = this.selectButtonsView.metrics;
- // if (reportName.length) {
- // // TODO: save this report
- // }
- var data = {
- name: reportName,
- emails: emails,
- metrics: metrics,
- dateRange: this.selectedDateRange
- };
- this.trigger('save', data);
- this.triggerMethod('modal:close');
- },
- /**
- * Rendering
- */
- onRender: function() {
- this.setupReportPicker();
- this.setupDateRangePicker();
- },
- onShow: function() {
- this.selectButtonsView = new SelectButtonsView();
- this.selectButtonsRegion.show(this.selectButtonsView);
- this.reportDropdownRegion.show(this.reportPickerView);
- this.selectDateRegion.show(this.dateRangeView);
- this.reportSelected(this.reportPickerView.selectedItem());
- }
- });
- }, moment);
+ module.exports = Views;
---------- diff:
modules/labs/labs-app.js
- AdvisorApp.module('LabsModule', function(LabsModule, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- var router;
- /**
- * @class
- */
- var Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'labs': 'labsHome'
- }
- });
+ /* module definition */
+ var LabsModule = {};
- var controller = {
- labsHome: function() {
- var sessionAttributes = AdvisorApp.module('Entities').session.attributes;
- var isInternal = false;
- /**
- * @param {Backbone.Model} model
- */
- _.each(sessionAttributes.teams.models, function(model) {
- if (model.get('name') === 'SocialCode') isInternal = true;
+ var router;
+ var Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'labs': 'labsHome'
+ }
+ });
+ var controller = {
+ labsHome: function() {
+ var sessionAttributes = AdvisorApp.module('Entities').session.attributes;
+ var isInternal = false;
+ /**
+ * @param {Backbone.Model} model
+ */
+ _.each(sessionAttributes.teams.models, function(model) {
+ if (model.get('name') === 'SocialCode') isInternal = true;
+ });
+ if (sessionAttributes.is_superuser || isInternal) {
+ LabsModule.moduleChannel.commands.execute('labs:load', {
+ region: App.mainRegion
});
- if (sessionAttributes.is_superuser || isInternal) {
- LabsModule.moduleChannel.commands.execute('labs:load', {
- region: App.mainRegion
- });
- }
}
- };
- LabsModule.moduleChannel = Backbone.Wreqr.radio.channel('labsModule');
- App.module('LabsModule').addInitializer(function() {
- var moduleChannel = this.moduleChannel;
- router = new Router({
- controller: controller
- });
- moduleChannel.vent.on('labs:show', function() {
- router.navigate('labs');
- controller.labsHome();
- });
+ }
+ };
+ LabsModule.moduleChannel = Backbone.Wreqr.radio.channel('labsModule');
+ App.module('LabsModule').addInitializer(function() {
+ var moduleChannel = this.moduleChannel;
+ router = new Router({
+ controller: controller
});
- App.module('LabsModule').on('start', function() {
- // handle current route
- var currentRoute = App.getCurrentRoute() || '';
- var handlerKey = router.appRoutes[currentRoute];
- var routeHandler = controller[handlerKey];
- if ($.isFunction(routeHandler)) {
- routeHandler();
- }
+ moduleChannel.vent.on('labs:show', function() {
+ router.navigate('labs');
+ controller.labsHome();
});
- this.startWithParent = false;
- App.addInitializer(function() {
- App.initializingSession.done(function() {
- App.module('LabsModule').start();
- });
+ });
+ App.module('LabsModule').on('start', function() {
+ // handle current route
+ var currentRoute = App.getCurrentRoute() || '';
+ var handlerKey = router.appRoutes[currentRoute];
+ var routeHandler = controller[handlerKey];
+ if ($.isFunction(routeHandler)) {
+ routeHandler();
+ }
+ });
+ this.startWithParent = false;
+ App.addInitializer(function() {
+ App.initializingSession.done(function() {
+ App.module('LabsModule').start();
});
+ });
- });
+ module.exports = LabsModule;
---------- diff:
modules/reports/reports-dashboard-app.js
- AdvisorApp.module('ReportsApp', function(Reports, AdvisorApp) {
- /**
- * @class
- */
- Reports.Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'reports(/)': 'showReports',
- 'reports/campaign::campaignID(/)': 'showCampaign',
- 'reports/campaign::campaignID/analytics(/)': 'showAnalytics'
- }
- });
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- var API = {
- showReports: function() {
- Reports.Show.Controller.showReports();
- },
- /**
- * @param {Number} campaignID
- */
- showCampaign: function(campaignID) {
- Reports.Show.Controller.showReports(campaignID);
- },
- /**
- * @param {Number} campaignID
- */
- showAnalytics: function(campaignID) {
- Reports.Show.Controller.showAnalytics(campaignID);
- }
- };
- AdvisorApp.on('reports:show', function() {
- AdvisorApp.navigate('reports');
- API.showReports();
- });
+ /* module definition */
+ var Reports = {};
- AdvisorApp.addInitializer(function() {
- var router = new Reports.Router({
- controller: API
- });
+ Reports.Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'reports(/)': 'showReports',
+ 'reports/campaign::campaignID(/)': 'showCampaign',
+ 'reports/campaign::campaignID/analytics(/)': 'showAnalytics'
+ }
+ });
+ var API = {
+ showReports: function() {
+ Reports.Show.Controller.showReports();
+ },
+ /**
+ * @param {Number} campaignID
+ */
+ showCampaign: function(campaignID) {
+ Reports.Show.Controller.showReports(campaignID);
+ },
+ /**
+ * @param {Number} campaignID
+ */
+ showAnalytics: function(campaignID) {
+ Reports.Show.Controller.showAnalytics(campaignID);
+ }
+ };
+ AdvisorApp.on('reports:show', function() {
+ AdvisorApp.navigate('reports');
+ API.showReports();
+ });
+ AdvisorApp.addInitializer(function() {
+ var router = new Reports.Router({
+ controller: API
});
+ });
- });
+ module.exports = Reports;
---------- diff:
modules/usagetracking/usagetracking-app.js
- AdvisorApp.module('UsageTrackingApp', function(UsageTracking, AdvisorApp) {
- var API = {
- trackPageview: function() {
- if (window.ga) {
- // Update page in the tracker
- ga('set', 'page', '/' + AdvisorApp.getCurrentRoute());
+ /* global dependencies */
+ var AdvisorApp = require('../../app');
- // Send page view to Google Analytics
- ga('send', 'pageview');
- }
- },
- sendPageEvent: function(category, action, label) {
- // Send custom events to Google Analytics
- // Google analytics event tracking accepts several parameters
- if (window.ga) {
- ga('send', 'event', category, action, label);
- }
- }
- };
- AdvisorApp.on('usage:pageview', function() {
- API.trackPageview();
- });
+ /* module definition */
+ var UsageTracking = {};
- AdvisorApp.on('usage:pageevent', function() {
- API.sendPageEvent.apply(API, arguments);
- });
+ var API = {
+ trackPageview: function() {
+ if (window.ga) {
+ // Update page in the tracker
+ ga('set', 'page', '/' + AdvisorApp.getCurrentRoute());
+ // Send page view to Google Analytics
+ ga('send', 'pageview');
+ }
+ },
+ sendPageEvent: function(category, action, label) {
+ // Send custom events to Google Analytics
+ // Google analytics event tracking accepts several parameters
+ if (window.ga) {
+ ga('send', 'event', category, action, label);
+ }
+ }
+ };
+ AdvisorApp.on('usage:pageview', function() {
+ API.trackPageview();
});
+ AdvisorApp.on('usage:pageevent', function() {
+ API.sendPageEvent.apply(API, arguments);
+ });
+ module.exports = UsageTracking;
---------- diff:
modules/report/report-app.js
- AdvisorApp.module('ReportModule', function(ReportModule, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../app');
- /**
- * @class
- */
- var Router = Marionette.AppRouter.extend({
- appRoutes: {
- 'report/new/lineitem::lineitemId': 'createReport'
- }
- });
+ /* module definition */
+ var ReportModule = {};
+ var Router = Marionette.AppRouter.extend({
+ appRoutes: {
+ 'report/new/lineitem::lineitemId': 'createReport'
+ }
+ });
+ var ReportLayout = Backbone.Marionette.LayoutView.extend({
+ template: Handlebars.templates.reportLayout,
+ regions: {
+ formRegion: '#report-form-container',
+ listRegion: '#report-list-container'
+ }
+ });
+ var controller = {
/**
- * @class
+ * @param {Number} lineitemId
*/
- var ReportLayout = Backbone.Marionette.LayoutView.extend({
- template: Handlebars.templates.reportLayout,
- regions: {
- formRegion: '#report-form-container',
- listRegion: '#report-list-container'
- }
- });
- var controller = {
- /**
- * @param {Number} lineitemId
- */
- createReport: function(lineitemId) {
- ReportModule.moduleChannel.commands.execute('create:load', {
- query: {lineitemId: lineitemId},
- region: App.mainRegion
- });
- //Feature waiting on backend ticket:ADV-813
- //when ready delete above 4 lines and uncomment lines below
- // var reportLayout = new ReportLayout();
- // App.mainRegion.show(reportLayout);
- // ReportModule.moduleChannel.commands.execute('create:load', {
- // query: {lineitemId: lineitemId},
- // region: reportLayout.formRegion
- // });
- // ReportModule.moduleChannel.commands.execute('list:load', {
- // query: {lineitem_id: lineitemId},
- // region: reportLayout.listRegion
- // });
- }
- };
- ReportModule.moduleChannel = Backbone.Wreqr.radio.channel('reportModule');
- App.addInitializer(function() {
- var router = new Router({
- controller: controller
+ createReport: function(lineitemId) {
+ ReportModule.moduleChannel.commands.execute('create:load', {
+ query: {lineitemId: lineitemId},
+ region: App.mainRegion
});
+ //Feature waiting on backend ticket:ADV-813
+ //when ready delete above 4 lines and uncomment lines below
+ // var reportLayout = new ReportLayout();
+ // App.mainRegion.show(reportLayout);
+ // ReportModule.moduleChannel.commands.execute('create:load', {
+ // query: {lineitemId: lineitemId},
+ // region: reportLayout.formRegion
+ // });
+ // ReportModule.moduleChannel.commands.execute('list:load', {
+ // query: {lineitem_id: lineitemId},
+ // region: reportLayout.listRegion
+ // });
+ }
+ };
+ ReportModule.moduleChannel = Backbone.Wreqr.radio.channel('reportModule');
+ App.addInitializer(function() {
+ var router = new Router({
+ controller: controller
});
+ });
- });
+ module.exports = ReportModule;
---------- diff:
entities/facebook/creatives/adcreative.js
- AdvisorApp.module('Entities.Facebook.Creatives', function(Module) {
- // TODO: Remove this when we get a new module loading
- // system
- var Creative = AdvisorApp.module('Entities').Creative;
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- /**
- * The following models are based on schemas from
- * https://github.com/facebook/facebook-python-ads-sdk
- *
- * The goal was to define the default attributes for
- * easy reference.
- *
- * Further reading on AdCreatives can be found here:
- * https://developers.facebook.com/docs/marketing-api/adcreative/v2.3
- */
- var AdCreative = Creative.extend({
- defaults: {
- /** @type {string} */
- actor_id: undefined,
- actor_image_hash: undefined,
- actor_name: undefined,
- /** @type {string} body of the ad */
- body: undefined,
- /** @type {string} */
- call_to_action_type: undefined,
- filename: undefined,
- /** @type {boolean} */
- follow_redirect: undefined,
- id: undefined,
- /** @type {object} */
- image_crops: undefined,
- /** @type {string} */
- image_file: undefined,
- /** @type {string} */
- image_hash: undefined,
- /** @type {string} */
- image_url: undefined,
- link_deep_link_url: undefined,
- /** @type {string} */
- link_url: undefined,
- /** @type {string} */
- name: undefined,
- object_id: undefined,
- /** @type {string} */
- object_store_url: undefined,
- /** @type {string} */
- object_story_id: undefined,
- /** @type {object} */
- object_story_spec: undefined,
- object_type: undefined,
- /** @type {string} */
- object_url: undefined,
- preview_url: undefined,
- product_set_id: undefined,
- thumbnail_url: undefined,
- /** @type {string} */
- title: undefined,
- /** @type {string} */
- url_tags: undefined,
- video_id: undefined
- },
- /** @returns {string} */
- platform: _.constant('facebook'),
+ /* module definition */
+ var Module = {};
- /** @returns {string} */
- urlRoot: function() {
- var url = AdvisorApp.module('Entities').Campaign.prototype.urlRoot;
- return url + this.get('campaignId') + '/fb_creative';
- },
- /**
- * @param {string} type -- view type
- * @returns {object}
- */
- getViewForKey: function(type) {
- return {
- 'title': {
- text: 'Link headline',
- view: 'text',
- placeholder: 'Wowza!',
- optional: true,
- constraint: Module.constraints('title')
- },
- 'body': {
- text: 'Post text',
- view: 'textarea',
- placeholder: 'This post is great!',
- optional: true,
- constraint: Module.constraints('body')
- },
- 'message': {
- text: 'Post text',
- view: 'textarea',
- placeholder: 'This post is great!',
- optional: true,
- constraint: Module.constraints('message')
- },
- 'call_to_action': {
- text: 'Call to action',
- view: 'dropdown',
- values: Module.Values.call_to_action,
- optional: true
- },
- 'caption': {
- text: 'Display link',
- view: 'text',
- optional: true,
- constraint: Module.constraints('caption')
- },
- 'description': {
- text: 'Description',
- view: 'text',
- optional: true,
- constraint: Module.constraints('description')
- },
- 'image': {
- view: 'image',
- hideHeader: true
- },
- 'video': {
- view: 'video',
- text: 'Video File'
- },
- 'thumbnail': {
- view: 'image',
- text: 'Thumbnail'
- },
- 'link_url': {
- text: 'Destination URL',
- view: 'text',
- placeholder: 'http://example.com'
- },
- 'countries': {
- text: 'Countries',
- view: 'text',
- optional: true,
- suggestions: {
- uri: '/api/advisor/v1/locationset/suggestions/',
- params: {
- platform: 'FACEBOOK',
- type: 'locations',
- location_types: 'country'
- }
+ var Creative = AdvisorApp.module('Entities').Creative;
+ var AdCreative = Creative.extend({
+ defaults: {
+ /** @type {string} */
+ actor_id: undefined,
+ actor_image_hash: undefined,
+ actor_name: undefined,
+ /** @type {string} body of the ad */
+ body: undefined,
+ /** @type {string} */
+ call_to_action_type: undefined,
+ filename: undefined,
+ /** @type {boolean} */
+ follow_redirect: undefined,
+ id: undefined,
+ /** @type {object} */
+ image_crops: undefined,
+ /** @type {string} */
+ image_file: undefined,
+ /** @type {string} */
+ image_hash: undefined,
+ /** @type {string} */
+ image_url: undefined,
+ link_deep_link_url: undefined,
+ /** @type {string} */
+ link_url: undefined,
+ /** @type {string} */
+ name: undefined,
+ object_id: undefined,
+ /** @type {string} */
+ object_store_url: undefined,
+ /** @type {string} */
+ object_story_id: undefined,
+ /** @type {object} */
+ object_story_spec: undefined,
+ object_type: undefined,
+ /** @type {string} */
+ object_url: undefined,
+ preview_url: undefined,
+ product_set_id: undefined,
+ thumbnail_url: undefined,
+ /** @type {string} */
+ title: undefined,
+ /** @type {string} */
+ url_tags: undefined,
+ video_id: undefined
+ },
+ /** @returns {string} */
+ platform: _.constant('facebook'),
+ /** @returns {string} */
+ urlRoot: function() {
+ var url = AdvisorApp.module('Entities').Campaign.prototype.urlRoot;
+ return url + this.get('campaignId') + '/fb_creative';
+ },
+ /**
+ * @param {string} type -- view type
+ * @returns {object}
+ */
+ getViewForKey: function(type) {
+ return {
+ 'title': {
+ text: 'Link headline',
+ view: 'text',
+ placeholder: 'Wowza!',
+ optional: true,
+ constraint: Module.constraints('title')
+ },
+ 'body': {
+ text: 'Post text',
+ view: 'textarea',
+ placeholder: 'This post is great!',
+ optional: true,
+ constraint: Module.constraints('body')
+ },
+ 'message': {
+ text: 'Post text',
+ view: 'textarea',
+ placeholder: 'This post is great!',
+ optional: true,
+ constraint: Module.constraints('message')
+ },
+ 'call_to_action': {
+ text: 'Call to action',
+ view: 'dropdown',
+ values: Module.Values.call_to_action,
+ optional: true
+ },
+ 'caption': {
+ text: 'Display link',
+ view: 'text',
+ optional: true,
+ constraint: Module.constraints('caption')
+ },
+ 'description': {
+ text: 'Description',
+ view: 'text',
+ optional: true,
+ constraint: Module.constraints('description')
+ },
+ 'image': {
+ view: 'image',
+ hideHeader: true
+ },
+ 'video': {
+ view: 'video',
+ text: 'Video File'
+ },
+ 'thumbnail': {
+ view: 'image',
+ text: 'Thumbnail'
+ },
+ 'link_url': {
+ text: 'Destination URL',
+ view: 'text',
+ placeholder: 'http://example.com'
+ },
+ 'countries': {
+ text: 'Countries',
+ view: 'text',
+ optional: true,
+ suggestions: {
+ uri: '/api/advisor/v1/locationset/suggestions/',
+ params: {
+ platform: 'FACEBOOK',
+ type: 'locations',
+ location_types: 'country'
}
- },
- 'languages': {
- text: 'Languages',
- view: 'text',
- optional: true,
- suggestions: {
- uri: '/api/advisor/v1/targetingsubset/suggestions/',
- params: {
- platform: 'FACEBOOK',
- targeting_type: 'locales'
- }
+ }
+ },
+ 'languages': {
+ text: 'Languages',
+ view: 'text',
+ optional: true,
+ suggestions: {
+ uri: '/api/advisor/v1/targetingsubset/suggestions/',
+ params: {
+ platform: 'FACEBOOK',
+ targeting_type: 'locales'
}
}
- }[type];
- }
- }, {
- /**
- * @param {string} type
- * @returns {string}
- */
- getDefaultAdFormat: function(type) {
- switch (type) {
- case 'photo':
- case 'link':
- case 'video':
- case 'status': return 'DESKTOP_FEED_STANDARD';
- case 'non-page-post-link':
- case 'event':
- case 'fan': return 'RIGHT_COLUMN_STANDARD';
- default: throw new Error(type + ' does not have an ad_format');
}
+ }[type];
+ }
+ }, {
+ /**
+ * @param {string} type
+ * @returns {string}
+ */
+ getDefaultAdFormat: function(type) {
+ switch (type) {
+ case 'photo':
+ case 'link':
+ case 'video':
+ case 'status': return 'DESKTOP_FEED_STANDARD';
+ case 'non-page-post-link':
+ case 'event':
+ case 'fan': return 'RIGHT_COLUMN_STANDARD';
+ default: throw new Error(type + ' does not have an ad_format');
}
+ }
+ });
+ Module.AdCreative = AdCreative;
- });
- Module.AdCreative = AdCreative;
- });
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/apagepost.js
- AdvisorApp.module('Entities.Facebook', function(Module) {
- // Please forgive me for naming this file "apagepost.js",
- // I needed the PagePost entity loaded after AdCreative but before
- // the individual creatives, and after fighting with Marionette,
- // leveraging alphabetical order was the only mechanism I found
- // that would work.
- //
- //
- // TODO: Get rid of this when we move to a different module
- // loading system
- var AdCreative = AdvisorApp.module('Entities.Facebook.Creatives').AdCreative;
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- var PagePost = AdCreative.extend({
- /** @returns {string} */
- urlRoot: function() {
- var url = AdvisorApp.module('Entities').Campaign.prototype.urlRoot;
- return url + this.get('campaignId') + '/pagepost';
- },
+ /* module definition */
+ var Module = {};
- constructor: function() {
- AdCreative.apply(this, arguments);
- _.extend(this.mappedViews, this.getDefaultViews());
- },
+ var AdCreative = AdvisorApp.module('Entities.Facebook.Creatives').AdCreative;
+ var PagePost = AdCreative.extend({
+ /** @returns {string} */
+ urlRoot: function() {
+ var url = AdvisorApp.module('Entities').Campaign.prototype.urlRoot;
+ return url + this.get('campaignId') + '/pagepost';
+ },
+ constructor: function() {
+ AdCreative.apply(this, arguments);
+ _.extend(this.mappedViews, this.getDefaultViews());
+ },
+ /**
+ * @param {object} options
+ */
+ initialize: function(options) {
+ this.campaignId = options.campaignId;
+ },
+ /**
+ * @param {object} model
+ * @returns {Backbone.Model}
+ */
+ build: function(model) {
+ var postdata = _.pick(model,
+ 'picture',
+ 'video',
+ 'name',
+ 'caption',
+ 'description',
+ 'link',
+ 'message',
+ 'call_to_action');
+ var type = { post_type: this.type };
+ var defaultdata = {
+ page: model.asset.id,
+ countries: model.countries,
+ languages: model.languages
+ };
+ return this.set(_.extend(postdata, type, defaultdata));
+ },
+ /**
+ * @param {object} model
+ * @returns {object}
+ */
+ preview: function(model) {
+ var post = this.build(model).toJSON();
+ var defaultAdFormat = AdCreative.getDefaultAdFormat(post.post_type);
+ return {
+ creative: { object_id: model.asset.id },
+ post: _.omit(post, 'post_type', 'page'),
+ ad_format: model.ad_format || defaultAdFormat
+ };
+ },
+ /** @returns {object} */
+ getDefaultViews: function() {
+ return {
+ countries: this.getViewForKey('countries'),
+ languages: this.getViewForKey('languages')
+ };
+ }
+ });
+ Module.PagePost = PagePost;
- /**
- * @param {object} options
- */
- initialize: function(options) {
- this.campaignId = options.campaignId;
- },
- /**
- * @param {object} model
- * @returns {Backbone.Model}
- */
- build: function(model) {
- var postdata = _.pick(model,
- 'picture',
- 'video',
- 'name',
- 'caption',
- 'description',
- 'link',
- 'message',
- 'call_to_action');
- var type = { post_type: this.type };
- var defaultdata = {
- page: model.asset.id,
- countries: model.countries,
- languages: model.languages
- };
- return this.set(_.extend(postdata, type, defaultdata));
- },
- /**
- * @param {object} model
- * @returns {object}
- */
- preview: function(model) {
- var post = this.build(model).toJSON();
- var defaultAdFormat = AdCreative.getDefaultAdFormat(post.post_type);
- return {
- creative: { object_id: model.asset.id },
- post: _.omit(post, 'post_type', 'page'),
- ad_format: model.ad_format || defaultAdFormat
- };
- },
- /** @returns {object} */
- getDefaultViews: function() {
- return {
- countries: this.getViewForKey('countries'),
- languages: this.getViewForKey('languages')
- };
- }
- });
- Module.PagePost = PagePost;
- });
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/constraints.js
- AdvisorApp.module('Entities.Facebook.Creatives', function(Module) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- Module.constraints = function(id) {
- var rules = {
- // Length has to be between 1 and 25 characters
- 0: new RegExp(/^.{25,}$/),
+ /* module definition */
+ var Module = {};
- // Length has to be between 1 and 90 characters
- 1: new RegExp(/^.{90,}$/),
- // Words cannot be greater than 30 characters in length
- 2: new RegExp(/\w{30,}$/),
- // Cannot start with punctuation \/!.?-*(),;:
- 3: new RegExp(/^[!?.\-*(),;:\\/]/),
- // Cannot have duplicate consecutive punctuation characters
- 4: new RegExp(/[!?\-*\(),;:\\/].+/),
- // Cannot have duplicate consecutive periods with the exception of 3 periods
- 5: new RegExp(/^[.]{2}(?![.])|[^.][.]{2}(?![.])|[.]{4,}/),
- // Only 2 consecutive 1 character words are allowed
- 6: new RegExp(/\b(\w{1})\s+\1\b/),
- // Cannot consist entirely of capital letters
- 7: new RegExp(/^(?!.*[a-z]).+$/),
- // Double spacing is not allowed
- 8: new RegExp(/\s\s+/),
- // IPA Symbols are not allowed (exception: ə, ɚ, ɛ, ɜ, ɝ, ɞ, ɟ)
- 9: new RegExp(/[\u0250-\u0258\u0260-\u02AF]+/),
- // Standalone diacritical marks are not allowed
- 10: new RegExp(/[\u0300-\u036F]+/),
- // Supscript characters with the exception of ™ and ℠ are not allowed
- 11: new RegExp(/[\u2070-\u209F]+/),
- // Subscript characters are not allowed
- 12: new RegExp(/[\u2080-\u209F]+/),
- // The following characters ^~_={}[]|<> are not allowed
- 13: new RegExp(/[\u003C-\u003E\u005B\u005D-\u005F\u007B-\u007E]+/)
- };
- var types = {
- 'title': [0, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13],
- 'body': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
- 'message': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
- 'caption': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
- 'description': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
- };
- var make = function(selection) {
- /** Get expressions */
- var pick = _.pick(rules, selection);
- var source = _.pluck(pick, 'source');
- /** Join expressions */
- var merged = source.join('|');
- /** Create expressions */
- return new RegExp(merged);
- };
- /** Create expression from type */
- var type = types[id];
- return make.call(this, type);
+ Module.constraints = function(id) {
+ var rules = {
+ // Length has to be between 1 and 25 characters
+ 0: new RegExp(/^.{25,}$/),
+ // Length has to be between 1 and 90 characters
+ 1: new RegExp(/^.{90,}$/),
+ // Words cannot be greater than 30 characters in length
+ 2: new RegExp(/\w{30,}$/),
+ // Cannot start with punctuation \/!.?-*(),;:
+ 3: new RegExp(/^[!?.\-*(),;:\\/]/),
+ // Cannot have duplicate consecutive punctuation characters
+ 4: new RegExp(/[!?\-*\(),;:\\/].+/),
+ // Cannot have duplicate consecutive periods with the exception of 3 periods
+ 5: new RegExp(/^[.]{2}(?![.])|[^.][.]{2}(?![.])|[.]{4,}/),
+ // Only 2 consecutive 1 character words are allowed
+ 6: new RegExp(/\b(\w{1})\s+\1\b/),
+ // Cannot consist entirely of capital letters
+ 7: new RegExp(/^(?!.*[a-z]).+$/),
+ // Double spacing is not allowed
+ 8: new RegExp(/\s\s+/),
+ // IPA Symbols are not allowed (exception: ə, ɚ, ɛ, ɜ, ɝ, ɞ, ɟ)
+ 9: new RegExp(/[\u0250-\u0258\u0260-\u02AF]+/),
+ // Standalone diacritical marks are not allowed
+ 10: new RegExp(/[\u0300-\u036F]+/),
+ // Supscript characters with the exception of ™ and ℠ are not allowed
+ 11: new RegExp(/[\u2070-\u209F]+/),
+ // Subscript characters are not allowed
+ 12: new RegExp(/[\u2080-\u209F]+/),
+ // The following characters ^~_={}[]|<> are not allowed
+ 13: new RegExp(/[\u003C-\u003E\u005B\u005D-\u005F\u007B-\u007E]+/)
};
+ var types = {
+ 'title': [0, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13],
+ 'body': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
+ 'message': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
+ 'caption': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
+ 'description': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
+ };
+ var make = function(selection) {
+ /** Get expressions */
+ var pick = _.pick(rules, selection);
+ var source = _.pluck(pick, 'source');
+ /** Join expressions */
+ var merged = source.join('|');
+ /** Create expressions */
+ return new RegExp(merged);
+ };
+ /** Create expression from type */
+ var type = types[id];
+ return make.call(this, type);
+ };
- });
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/event.js
- AdvisorApp.module('Entities.Facebook.Creatives', function(Module) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- Module.Event = Module.AdCreative.extend({
- views: {
- 'image_file': 'image',
- 'body': 'body',
- 'title': 'title'
- },
- type: 'event',
+ /* module definition */
+ var Module = {};
- /**
- * @param {object} model
- * @returns {AdvisorApp.module('Entities.Facebook.Creatives').AdCreative}
- */
- build: function(model) {
- var object_id = model.asset.id;
- if (_.isString(model.image_file)) {
- model.image_url = model.image_file;
- delete model.image_file;
- }
- delete model.asset;
- this.set(model);
- return this.set('object_id', object_id);
- },
- /**
- * @param {object} model
- * @returns {object}
- */
- preview: function(model) {
- return {
- creative: this.build.call(this.clone(), model).toJSON(),
- ad_format: Module.AdCreative.getDefaultAdFormat('event')
- };
+ Module.Event = Module.AdCreative.extend({
+ views: {
+ 'image_file': 'image',
+ 'body': 'body',
+ 'title': 'title'
+ },
+ type: 'event',
+ /**
+ * @param {object} model
+ * @returns {AdvisorApp.module('Entities.Facebook.Creatives').AdCreative}
+ */
+ build: function(model) {
+ var object_id = model.asset.id;
+ if (_.isString(model.image_file)) {
+ model.image_url = model.image_file;
+ delete model.image_file;
}
- });
+ delete model.asset;
+ this.set(model);
+ return this.set('object_id', object_id);
+ },
+ /**
+ * @param {object} model
+ * @returns {object}
+ */
+ preview: function(model) {
+ return {
+ creative: this.build.call(this.clone(), model).toJSON(),
+ ad_format: Module.AdCreative.getDefaultAdFormat('event')
+ };
+ }
+ });
- });
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/link.js
- AdvisorApp.module('Entities.Facebook.Creatives', function(Module) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- Module.Link = Module.AdCreative.extend({
- views: {
- 'object_url': 'link_url',
- 'image_file': 'image',
- 'body': 'body',
- 'title': 'title'
- },
- /**
- * @param {object} model
- * @returns {AdvisorApp.module('Entities.Facebook.Creatives').AdCreative}
- */
- build: function(model) {
- delete model.asset;
- if (_.isString(model.image_file)) {
- model.image_url = model.image_file;
- delete model.image_file;
- }
- return this.set(model);
- },
+ /* module definition */
+ var Module = {};
- /**
- * @param {object} model
- * @returns {object}
- */
- preview: function(model) {
- return {
- creative: this.build.call(this.clone(), model).toJSON(),
- ad_format: Module.AdCreative.getDefaultAdFormat('non-page-post-link')
- };
+ Module.Link = Module.AdCreative.extend({
+ views: {
+ 'object_url': 'link_url',
+ 'image_file': 'image',
+ 'body': 'body',
+ 'title': 'title'
+ },
+ /**
+ * @param {object} model
+ * @returns {AdvisorApp.module('Entities.Facebook.Creatives').AdCreative}
+ */
+ build: function(model) {
+ delete model.asset;
+ if (_.isString(model.image_file)) {
+ model.image_url = model.image_file;
+ delete model.image_file;
}
- });
+ return this.set(model);
+ },
+ /**
+ * @param {object} model
+ * @returns {object}
+ */
+ preview: function(model) {
+ return {
+ creative: this.build.call(this.clone(), model).toJSON(),
+ ad_format: Module.AdCreative.getDefaultAdFormat('non-page-post-link')
+ };
+ }
+ });
- });
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/fan.js
- AdvisorApp.module('Entities.Facebook.Creatives', function(Module) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- Module.Fan = Module.AdCreative.extend({
- views: {
- 'image_file': 'image',
- 'body': 'body',
- 'title': 'title'
- },
- type: 'fan',
+ /* module definition */
+ var Module = {};
- /**
- * @param {object} model
- * @returns {AdvisorApp.module('Entities.Facebook.Creatives').AdCreative}
- */
- build: function(model) {
- var object_id = model.asset.id;
- if (_.isString(model.image_file)) {
- model.image_url = model.image_file;
- delete model.image_file;
- }
- delete model.asset;
- this.set(model);
- return this.set('object_id', object_id);
- },
- /**
- * @param {object} model
- * @returns {object}
- */
- preview: function(model) {
- return {
- creative: this.build.call(this.clone(), model).toJSON(),
- ad_format: Module.AdCreative.getDefaultAdFormat('fan')
- };
+ Module.Fan = Module.AdCreative.extend({
+ views: {
+ 'image_file': 'image',
+ 'body': 'body',
+ 'title': 'title'
+ },
+ type: 'fan',
+ /**
+ * @param {object} model
+ * @returns {AdvisorApp.module('Entities.Facebook.Creatives').AdCreative}
+ */
+ build: function(model) {
+ var object_id = model.asset.id;
+ if (_.isString(model.image_file)) {
+ model.image_url = model.image_file;
+ delete model.image_file;
}
- });
+ delete model.asset;
+ this.set(model);
+ return this.set('object_id', object_id);
+ },
+ /**
+ * @param {object} model
+ * @returns {object}
+ */
+ preview: function(model) {
+ return {
+ creative: this.build.call(this.clone(), model).toJSON(),
+ ad_format: Module.AdCreative.getDefaultAdFormat('fan')
+ };
+ }
+ });
- });
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/page-link.js
- AdvisorApp.module('Entities.Facebook', function(Module) {
+ /* global dependencies */
+ var AdvisorApp = require('../../../app');
- Module.LinkPost = Module.PagePost.extend({
- views: {
- 'name': 'title',
- 'link': 'link_url',
- 'message': 'body',
- 'picture': 'image',
- 'call_to_action': 'call_to_action',
- 'caption': 'caption',
- 'description': 'description'
- },
- type: 'link'
- });
+ /* module definition */
+ var Module = {};
+ Module.LinkPost = Module.PagePost.extend({
+ views: {
+ 'name': 'title',
+ 'link': 'link_url',
+ 'message': 'body',
+ 'picture': 'image',
+ 'call_to_action': 'call_to_action',
+ 'caption': 'caption',
+ 'description': 'description'
+ },
+ type: 'link'
});
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/photo.js
- AdvisorApp.module('Entities.Facebook', function(Module) {
+ /* global dependencies */
+ var AdvisorApp = require('../../../app');
- Module.PhotoPost = Module.PagePost.extend({
- views: {
- 'message': 'body',
- 'picture': 'image'
- },
- type: 'photo'
- });
+ /* module definition */
+ var Module = {};
+ Module.PhotoPost = Module.PagePost.extend({
+ views: {
+ 'message': 'body',
+ 'picture': 'image'
+ },
+ type: 'photo'
});
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/specs.js
- AdvisorApp.module('Entities.Facebook.Creatives.Specs', function(Module) {
- var AdCreative = AdvisorApp.module('Entities.Facebook.Creatives').AdCreative;
+ /* global dependencies */
+ var AdvisorApp = require('../../../app');
- /**
- * The following models are based on schemas from
- * https://github.com/facebook/facebook-python-ads-sdk
- *
- * The goal was to define the default attributes for
- * easy reference.
- *
- * Further reading on AdCreatives can be found here:
- * https://developers.facebook.com/docs/marketing-api/adcreative/v2.3
- */
- Module.Story = AdCreative.extend({
- defaults: {
- link_data: undefined,
- offer_data: undefined,
- page_id: undefined,
- photo_data: undefined,
- template_data: undefined,
- text_data: undefined,
- video_data: undefined
- }
- });
- Module.Photo = AdCreative.extend({
- defaults: {
- caption: undefined,
- url: undefined
- }
- });
+ /* module definition */
+ var Module = {};
- Module.Video = AdCreative.extend({
- defaults: {
- call_to_action: undefined,
- description: undefined,
- image_url: undefined,
- title: undefined,
- video_id: undefined
- }
- });
+ var AdCreative = AdvisorApp.module('Entities.Facebook.Creatives').AdCreative;
+ Module.Story = AdCreative.extend({
+ defaults: {
+ link_data: undefined,
+ offer_data: undefined,
+ page_id: undefined,
+ photo_data: undefined,
+ template_data: undefined,
+ text_data: undefined,
+ video_data: undefined
+ }
+ });
+ Module.Photo = AdCreative.extend({
+ defaults: {
+ caption: undefined,
+ url: undefined
+ }
+ });
+ Module.Video = AdCreative.extend({
+ defaults: {
+ call_to_action: undefined,
+ description: undefined,
+ image_url: undefined,
+ title: undefined,
+ video_id: undefined
+ }
+ });
+ Module.Link = AdCreative.extend({
+ defaults: {
+ call_to_action: undefined,
+ caption: undefined,
+ child_attachments: undefined,
+ description: undefined,
+ image_hash: undefined,
+ image_crops: undefined,
+ link: undefined,
+ message: undefined,
+ multi_share_optimized: undefined,
+ name: undefined,
+ picture: undefined
+ }
+ });
- Module.Link = AdCreative.extend({
- defaults: {
- call_to_action: undefined,
- caption: undefined,
- child_attachments: undefined,
- description: undefined,
- image_hash: undefined,
- image_crops: undefined,
- link: undefined,
- message: undefined,
- multi_share_optimized: undefined,
- name: undefined,
- picture: undefined
- }
- });
- });
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/status.js
- AdvisorApp.module('Entities.Facebook', function(Module) {
+ /* global dependencies */
+ var AdvisorApp = require('../../../app');
- Module.StatusPost = Module.PagePost.extend({
- views: {
- 'message': 'body'
- },
- type: 'status'
- });
+ /* module definition */
+ var Module = {};
+ Module.StatusPost = Module.PagePost.extend({
+ views: {
+ 'message': 'body'
+ },
+ type: 'status'
});
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/values.js
- AdvisorApp.module('Entities.Facebook.Creatives.Values', function(Module) {
+ /* global dependencies */
+ var AdvisorApp = require('../../../app');
- Module.call_to_action = [
- 'None',
- 'Shop Now',
- 'Book Travel',
- 'Learn More',
- 'Sign Up',
- 'Download',
- 'Watch More'
- ];
- });
+ /* module definition */
+ var Module = {};
+ Module.call_to_action = [
+ 'None',
+ 'Shop Now',
+ 'Book Travel',
+ 'Learn More',
+ 'Sign Up',
+ 'Download',
+ 'Watch More'
+ ];
+ module.exports = Module;
---------- diff:
entities/facebook/creatives/video.js
- AdvisorApp.module('Entities.Facebook', function(Module) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- Module.Video = Module.PagePost.extend({
- views: {
- 'video': 'video',
- 'thumbnail': 'thumbnail',
- 'title': 'title',
- 'message': 'message',
- 'call_to_action': 'call_to_action',
- 'link': {
- view: 'link_url',
- text: 'Call to action URL',
- optional: true
- },
- 'caption': {
- view: 'message',
- text: 'Call to action caption'
- }
- },
- type: 'video',
+ /* module definition */
+ var Module = {};
- // Don't show a preview
- preview: _.constant(null)
- });
+ Module.Video = Module.PagePost.extend({
+ views: {
+ 'video': 'video',
+ 'thumbnail': 'thumbnail',
+ 'title': 'title',
+ 'message': 'message',
+ 'call_to_action': 'call_to_action',
+ 'link': {
+ view: 'link_url',
+ text: 'Call to action URL',
+ optional: true
+ },
+ 'caption': {
+ view: 'message',
+ text: 'Call to action caption'
+ }
+ },
+ type: 'video',
+ // Don't show a preview
+ preview: _.constant(null)
+ });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/age/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Age', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Age = require('./controller');
- /** Modules */
- var Age = App.module('AdCreationApp.Age');
- /** Get age view */
- var age = new Age.View({
- collection: options.collection
- });
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Show view in region */
- (options.region).show(age);
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Age = AdCreationApp.Age;
+ /** Get age view */
+ var age = new Age.View({
+ collection: options.collection
+ });
+ /** Show view in region */
+ (options.region).show(age);
+ // Destroy this controller when its view is destroyed
+ this.listenTo(age, 'destroy', this.destroy);
+ }
+ });
- // Destroy this controller when its view is destroyed
- this.listenTo(age, 'destroy', this.destroy);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/age/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Age', function(Module, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- Module.ChildView = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.age'),
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** Template class */
- className: 'child-view',
+ Module.ChildView = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.age'),
+ /** Template class */
+ className: 'child-view',
+ /** Template helpers */
+ templateHelpers: {
+ ageRange: _.range(13, 66)
+ },
+ /** Events */
+ events: {
+ 'click .btn-remove': 'removeModel',
+ 'click .lower a': 'setValue',
+ 'click .higher a': 'setValue'
+ },
+ /** Data binding */
+ bindings: {
+ '.lower .value': 'min_age',
+ '.higher .value': 'max_age'
+ },
+ /** @memberof App.module('AdCreationApp.Views').Age */
+ onShow: function() {
+ /** Add stickit */
+ this.stickit();
+ },
+ /** @memberof App.module('AdCreationApp.Views').Age */
+ removeModel: function() {
+ /** Remove current model */
+ this.model.collection.remove(this.model);
+ },
+ /** @memberof App.module('AdCreationApp.Views').Age */
+ setValue: function(event) {
+ /** Element */
+ var el = $(event.target).closest('.btn-group');
+ /** Close popup */
+ el.removeClass('open');
+ /** Add value to model */
+ var name = el.hasClass('lower') && 'min_age' || 'max_age';
+ var value = $(event.target).data('value');
+ this.model.set(name, value);
+ /** Disable event */
+ return false;
+ }
+ });
+ Module.EmptyView = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.ageEmpty')
+ });
+ Module.View = Marionette.CollectionView.extend({
+ /** Template class */
+ className: 'collection-view',
+ /** Views */
+ childView: Module.ChildView
+ });
- /** Template helpers */
- templateHelpers: {
- ageRange: _.range(13, 66)
- },
- /** Events */
- events: {
- 'click .btn-remove': 'removeModel',
- 'click .lower a': 'setValue',
- 'click .higher a': 'setValue'
- },
- /** Data binding */
- bindings: {
- '.lower .value': 'min_age',
- '.higher .value': 'max_age'
- },
- /** @memberof App.module('AdCreationApp.Views').Age */
- onShow: function() {
- /** Add stickit */
- this.stickit();
- },
- /** @memberof App.module('AdCreationApp.Views').Age */
- removeModel: function() {
- /** Remove current model */
- this.model.collection.remove(this.model);
- },
- /** @memberof App.module('AdCreationApp.Views').Age */
- setValue: function(event) {
- /** Element */
- var el = $(event.target).closest('.btn-group');
- /** Close popup */
- el.removeClass('open');
- /** Add value to model */
- var name = el.hasClass('lower') && 'min_age' || 'max_age';
- var value = $(event.target).data('value');
- this.model.set(name, value);
- /** Disable event */
- return false;
- }
- });
- Module.EmptyView = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.ageEmpty')
- });
- Module.View = Marionette.CollectionView.extend({
- /** Template class */
- className: 'collection-view',
- /** Views */
- childView: Module.ChildView
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/budget/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Budget', function(Module, App) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- this.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Budget = require('./controller');
- /** Modules */
- var adcollection = App.module('AdCreationApp').record('collection:adcollections');
- var Budget = App.module('AdCreationApp.Budget');
- /** Get creative view */
- var view = new Budget.View({
- collection: adcollection,
- creatives: this.getCreatives()
- });
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Show view */
- (options.region).show(view);
- /** Listeners */
- this.listenTo(view, 'destroy', this.destroy);
- // Show toolbar
- App.trigger('adcreation:toolbar:show', {
- viewName: 'budget',
- events: {
- 'click #budget-bulk a[data-action=budget]': this.showBulk
- }
+ this.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var adcollection = App.module('AdCreationApp').record('collection:adcollections');
+ var Budget = AdCreationApp.Budget;
+ /** Get creative view */
+ var view = new Budget.View({
+ collection: adcollection,
+ creatives: this.getCreatives()
+ });
+ /** Show view */
+ (options.region).show(view);
+ /** Listeners */
+ this.listenTo(view, 'destroy', this.destroy);
+ // Show toolbar
+ App.trigger('adcreation:toolbar:show', {
+ viewName: 'budget',
+ events: {
+ 'click #budget-bulk a[data-action=budget]': this.showBulk
+ }
+ });
+ /** Analytics */
+ AdvisorApp.trigger('usage:pageevent', 'Ad Creation', 'Add Budget');
+ },
+ getCreatives: function() {
+ /** Storage */
+ var storage = App.module('AdCreationApp').record('model:generic');
+ /** Get creatives and ids */
+ var creatives = storage.get('_creatives');
+ var creativeIDs = storage.get('_ad_creatives');
+ /** Create creatives collection */
+ var creativeCollection = new Backbone.Collection(creativeIDs);
+ /** Add creatives to ids */
+ creativeCollection.each(function(obj) {
+ var creative = _.findWhere(creatives, {
+ id: obj.get('creative_id')
});
- /** Analytics */
- AdvisorApp.trigger('usage:pageevent', 'Ad Creation', 'Add Budget');
- },
- getCreatives: function() {
- /** Storage */
- var storage = App.module('AdCreationApp').record('model:generic');
- /** Get creatives and ids */
- var creatives = storage.get('_creatives');
- var creativeIDs = storage.get('_ad_creatives');
- /** Create creatives collection */
- var creativeCollection = new Backbone.Collection(creativeIDs);
- /** Add creatives to ids */
- creativeCollection.each(function(obj) {
- var creative = _.findWhere(creatives, {
- id: obj.get('creative_id')
- });
- obj.set('creative', creative);
- });
- /** Return */
- return creativeCollection;
- },
- /** @memberof App.module('AdCreationApp.Container').Controller */
- showBulk: function(event) {
- var adcollections = App.module('AdCreationApp').record('collection:adcollections');
- var hasSelectedAdsets = !!adcollections.where({_selected: true}).length;
- /** If no adsets selected, show an error modal */
- if (!hasSelectedAdsets) {
- App.modalRegion.show(new AdvisorApp.module('Common').BasicView({
- title: 'No Ad Set selected',
- text: 'Please select Ad Sets before setting bulk budget and bids.'
- }));
- } else {
- App.modalRegion.show(new Module.BidView({
- button: true,
- model: new Backbone.Model(),
- title: 'Set budget and bids'
- }));
- }
- /** Prevent default */
- event.preventDefault();
+ obj.set('creative', creative);
+ });
+ /** Return */
+ return creativeCollection;
+ },
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ showBulk: function(event) {
+ var adcollections = App.module('AdCreationApp').record('collection:adcollections');
+ var hasSelectedAdsets = !!adcollections.where({_selected: true}).length;
+ /** If no adsets selected, show an error modal */
+ if (!hasSelectedAdsets) {
+ App.modalRegion.show(new AdvisorApp.module('Common').BasicView({
+ title: 'No Ad Set selected',
+ text: 'Please select Ad Sets before setting bulk budget and bids.'
+ }));
+ } else {
+ App.modalRegion.show(new Module.BidView({
+ button: true,
+ model: new Backbone.Model(),
+ title: 'Set budget and bids'
+ }));
}
+ /** Prevent default */
+ event.preventDefault();
+ }
+ });
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/budget/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Budget', function(Module, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- /**
- * Bid view
- */
- Module.BidView = Marionette.ItemView.extend({
- title: 'Set budget and bids',
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** Handlebar template */
- template: App.module('templates.adcreation.budgetBid'),
- className: 'sc-adcreation-modal',
- /** Template helpers */
- templateHelpers: function() {
- return _(this.options).extend({ title: this.getOption('title') });
+ Module.BidView = Marionette.ItemView.extend({
+ title: 'Set budget and bids',
+ /** Handlebar template */
+ template: App.module('templates.adcreation.budgetBid'),
+ className: 'sc-adcreation-modal',
+ /** Template helpers */
+ templateHelpers: function() {
+ return _(this.options).extend({ title: this.getOption('title') });
+ },
+ /** UI */
+ ui: {
+ /** Bid dropdown */
+ 'bidType': '#bid button .value',
+ 'bidSelection': '#bid .dropdown-menu',
+ /** Budget dropdown */
+ 'budgetType': '#budget button .value',
+ 'budgetSelection': '#budget .dropdown-menu',
+ /** Bid */
+ 'daily': 'input.daily-budget',
+ 'lifetime': 'input.lifetime-budget',
+ /** Budget inputs */
+ 'actions': 'input.actions',
+ 'reach': 'input.reach',
+ 'clicks': 'input.clicks',
+ 'social': 'input.social',
+ 'impressions': 'input.impressions',
+ /** Update button */
+ 'update': '.btn-update'
+ },
+ /** Events */
+ events: {
+ 'click @ui.bidSelection': 'setBid',
+ 'click @ui.budgetSelection': 'setBudget',
+ 'click @ui.update': 'update'
+ },
+ /** Data binding */
+ bindings: {
+ '@ui.bidType': 'bid_type',
+ '@ui.budgetType': 'budget_type',
+ '@ui.daily': 'daily_budget',
+ '@ui.lifetime': 'lifetime_budget',
+ '@ui.actions': {
+ key: 'ACTIONS',
+ observe: 'bid_info',
+ onSet: 'setArray',
+ onGet: 'getArray'
},
- /** UI */
- ui: {
- /** Bid dropdown */
- 'bidType': '#bid button .value',
- 'bidSelection': '#bid .dropdown-menu',
- /** Budget dropdown */
- 'budgetType': '#budget button .value',
- 'budgetSelection': '#budget .dropdown-menu',
- /** Bid */
- 'daily': 'input.daily-budget',
- 'lifetime': 'input.lifetime-budget',
- /** Budget inputs */
- 'actions': 'input.actions',
- 'reach': 'input.reach',
- 'clicks': 'input.clicks',
- 'social': 'input.social',
- 'impressions': 'input.impressions',
- /** Update button */
- 'update': '.btn-update'
+ '@ui.reach': {
+ key: 'REACH',
+ observe: 'bid_info',
+ onSet: 'setArray',
+ onGet: 'getArray'
},
- /** Events */
- events: {
- 'click @ui.bidSelection': 'setBid',
- 'click @ui.budgetSelection': 'setBudget',
- 'click @ui.update': 'update'
+ '@ui.clicks': {
+ key: 'CLICKS',
+ observe: 'bid_info',
+ onSet: 'setArray',
+ onGet: 'getArray'
},
- /** Data binding */
- bindings: {
- '@ui.bidType': 'bid_type',
- '@ui.budgetType': 'budget_type',
- '@ui.daily': 'daily_budget',
- '@ui.lifetime': 'lifetime_budget',
- '@ui.actions': {
- key: 'ACTIONS',
- observe: 'bid_info',
- onSet: 'setArray',
- onGet: 'getArray'
- },
- '@ui.reach': {
- key: 'REACH',
- observe: 'bid_info',
- onSet: 'setArray',
- onGet: 'getArray'
- },
- '@ui.clicks': {
- key: 'CLICKS',
- observe: 'bid_info',
- onSet: 'setArray',
- onGet: 'getArray'
- },
- '@ui.social': {
- key: 'SOCIAL',
- observe: 'bid_info',
- onSet: 'setArray',
- onGet: 'getArray'
- },
- '@ui.impressions': {
- key: 'IMPRESSIONS',
- observe: 'bid_info',
- onSet: 'setArray',
- onGet: 'getArray'
- }
+ '@ui.social': {
+ key: 'SOCIAL',
+ observe: 'bid_info',
+ onSet: 'setArray',
+ onGet: 'getArray'
},
- /** Behaviors */
- behaviors: {
- CurrencyValidate: {}
- },
- initialize: function() {
- // Keeping it a very short debounce since we get lots of
- // calls to toggleInput, which I'm assuming is due to multiple
- // change events occurring simultaneously. Well, as simulataneous
- // as javascript really allows.
- this.toggleInputs = _.debounce(this.toggleInputs, 50);
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- onShow: function() {
- /** Use UI keys in bindings */
- this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
- /** Add stickit */
- this.stickit();
- /** Select first item in dropdown lists */
- this.$('.dropdown-menu li:first-child a').trigger('click');
- /** Listen to model change */
- this.listenTo(this.model, 'all', this.toggleInputs);
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- update: function(event) {
- /** Modules */
- var Collection = App.module('AdCreationApp').record('collection:adcollections');
- /** Model */
- var model = this.model;
- /** Get selected models */
- var selected = Collection.where({ _selected: true });
- /** Create a collection of selected models */
- selected = new Backbone.Collection(selected);
- /** Add attributes to collection models */
- selected.invoke('set', model.attributes);
- /** This causes the bid/budget inputs to reformat to dollars */
- $('.sc-budget input').each(function() {
- this.focus();
- this.blur();
- });
- /** Close modal */
- this.triggerMethod('modal:close');
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- setBid: function(event) {
- /** Close popup */
- var parent = $(event.currentTarget).parent();
- parent.removeClass('open');
- /** Set bid type */
- this.model.set({
- bid_type: $(event.target).data('title'),
- bid_selector: $(event.target).data('selector')
- });
- /** Clear bid */
- this.model.unset('bid_info');
- /** Toggle inputs */
- this.toggleInputs();
- /** Stop propagation */
- return false;
- },
- setBudget: function(event) {
- /** Close popup */
- var parent = $(event.currentTarget).parent();
- parent.removeClass('open');
- /** Set budget type */
- this.model.set({
- budget_type: $(event.target).data('title'),
- budget_selector: $(event.target).data('selector')
- });
- /** Clear budget */
- this.model.unset('daily_budget');
- this.model.unset('lifetime_budget');
- /** Toggle inputs */
- this.toggleInputs();
- /** Stop propagation */
- return false;
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- toggleInputs: function() {
- /** Selectors */
- var budget = this.model.get('budget_selector');
- var bid = this.model.get('bid_selector');
- var selectors = this.$('[data-selector]');
- /** Create a string of selectors */
- var list = selectors.map(function() {
- return $(this).data('selector');
- }).get().join(',');
- /** */
- this.$(list).hide();
- this.$(budget).show();
- this.$(bid).show();
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- setArray: function(value, opts) {
- /** Get values */
- var values = this.model.get(opts.observe) || {};
- /** Add value */
- values[opts.key] = this.setCent(value);
- // Unfortunately Backbone does not fire a change event when it
- // updates a model's attribute if the attribute is an array. Stickit
- // intelligently just adds to the array so no change event is fired.
- // The "wiring" between the input and the model's attribute works
- // great. There just isn't a `change` event that is fired. We have
- // to fire this manually here then so that we can check and see if
- // the user has filled in all the proper inputs before allowing
- // them to "Save & Continue".
- this.model.trigger('change', this.model);
- /** Return values */
- return values;
- },
- getArray: function(value, opts) {
- /** Get bid from key */
- var amount = value && value[opts.key] || undefined;
- /** Return value */
- return value && this.getDollar(amount);
- },
- setCent: function(value) {
- /** Convert dollars to cent */
- return value && parseInt(accounting.unformat(value) * 100, 10);
- },
- getDollar: function(value) {
- /** Convert cents to dollars */
- return value && accounting.unformat(value) / 100;
+ '@ui.impressions': {
+ key: 'IMPRESSIONS',
+ observe: 'bid_info',
+ onSet: 'setArray',
+ onGet: 'getArray'
}
- });
- /**
- * Creatives
- */
- Module.CreativesView = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.adCreative'),
- className: 'ad-creative-preview'
- });
- /**
- * Layout view
- */
- Module.LayoutView = Marionette.CompositeView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.budget'),
- /** Child views */
- childView: Module.CreativesView,
- childViewContainer: '#ads-container',
- /** UI */
- ui: {
- /** Information */
- 'creativesCount': 'span.creatives-count',
- 'totalReach': 'span.total-reach',
- 'audienceSummary': 'span.audience',
- /** Buttons */
- 'update': 'button.btn-update',
- 'checkbox': 'input[type=checkbox]',
- /** View container */
- 'bid': '#bid-view',
- /** Ad previews */
- 'toggleAds': 'button.toggle-ads',
- 'adsContainer': '#ads-container'
+ },
+ /** Behaviors */
+ behaviors: {
+ CurrencyValidate: {}
+ },
+ initialize: function() {
+ // Keeping it a very short debounce since we get lots of
+ // calls to toggleInput, which I'm assuming is due to multiple
+ // change events occurring simultaneously. Well, as simulataneous
+ // as javascript really allows.
+ this.toggleInputs = _.debounce(this.toggleInputs, 50);
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ onShow: function() {
+ /** Use UI keys in bindings */
+ this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
+ /** Add stickit */
+ this.stickit();
+ /** Select first item in dropdown lists */
+ this.$('.dropdown-menu li:first-child a').trigger('click');
+ /** Listen to model change */
+ this.listenTo(this.model, 'all', this.toggleInputs);
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ update: function(event) {
+ /** Modules */
+ var Collection = App.module('AdCreationApp').record('collection:adcollections');
+ /** Model */
+ var model = this.model;
+ /** Get selected models */
+ var selected = Collection.where({ _selected: true });
+ /** Create a collection of selected models */
+ selected = new Backbone.Collection(selected);
+ /** Add attributes to collection models */
+ selected.invoke('set', model.attributes);
+ /** This causes the bid/budget inputs to reformat to dollars */
+ $('.sc-budget input').each(function() {
+ this.focus();
+ this.blur();
+ });
+ /** Close modal */
+ this.triggerMethod('modal:close');
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ setBid: function(event) {
+ /** Close popup */
+ var parent = $(event.currentTarget).parent();
+ parent.removeClass('open');
+ /** Set bid type */
+ this.model.set({
+ bid_type: $(event.target).data('title'),
+ bid_selector: $(event.target).data('selector')
+ });
+ /** Clear bid */
+ this.model.unset('bid_info');
+ /** Toggle inputs */
+ this.toggleInputs();
+ /** Stop propagation */
+ return false;
+ },
+ setBudget: function(event) {
+ /** Close popup */
+ var parent = $(event.currentTarget).parent();
+ parent.removeClass('open');
+ /** Set budget type */
+ this.model.set({
+ budget_type: $(event.target).data('title'),
+ budget_selector: $(event.target).data('selector')
+ });
+ /** Clear budget */
+ this.model.unset('daily_budget');
+ this.model.unset('lifetime_budget');
+ /** Toggle inputs */
+ this.toggleInputs();
+ /** Stop propagation */
+ return false;
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ toggleInputs: function() {
+ /** Selectors */
+ var budget = this.model.get('budget_selector');
+ var bid = this.model.get('bid_selector');
+ var selectors = this.$('[data-selector]');
+ /** Create a string of selectors */
+ var list = selectors.map(function() {
+ return $(this).data('selector');
+ }).get().join(',');
+ /** */
+ this.$(list).hide();
+ this.$(budget).show();
+ this.$(bid).show();
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ setArray: function(value, opts) {
+ /** Get values */
+ var values = this.model.get(opts.observe) || {};
+ /** Add value */
+ values[opts.key] = this.setCent(value);
+ // Unfortunately Backbone does not fire a change event when it
+ // updates a model's attribute if the attribute is an array. Stickit
+ // intelligently just adds to the array so no change event is fired.
+ // The "wiring" between the input and the model's attribute works
+ // great. There just isn't a `change` event that is fired. We have
+ // to fire this manually here then so that we can check and see if
+ // the user has filled in all the proper inputs before allowing
+ // them to "Save & Continue".
+ this.model.trigger('change', this.model);
+ /** Return values */
+ return values;
+ },
+ getArray: function(value, opts) {
+ /** Get bid from key */
+ var amount = value && value[opts.key] || undefined;
+ /** Return value */
+ return value && this.getDollar(amount);
+ },
+ setCent: function(value) {
+ /** Convert dollars to cent */
+ return value && parseInt(accounting.unformat(value) * 100, 10);
+ },
+ getDollar: function(value) {
+ /** Convert cents to dollars */
+ return value && accounting.unformat(value) / 100;
+ }
+ });
+ Module.CreativesView = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.adCreative'),
+ className: 'ad-creative-preview'
+ });
+ Module.LayoutView = Marionette.CompositeView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.budget'),
+ /** Child views */
+ childView: Module.CreativesView,
+ childViewContainer: '#ads-container',
+ /** UI */
+ ui: {
+ /** Information */
+ 'creativesCount': 'span.creatives-count',
+ 'totalReach': 'span.total-reach',
+ 'audienceSummary': 'span.audience',
+ /** Buttons */
+ 'update': 'button.btn-update',
+ 'checkbox': 'input[type=checkbox]',
+ /** View container */
+ 'bid': '#bid-view',
+ /** Ad previews */
+ 'toggleAds': 'button.toggle-ads',
+ 'adsContainer': '#ads-container'
+ },
+ /** Events */
+ events: {
+ 'click @ui.update': 'update',
+ 'click @ui.toggleAds': 'creatives',
+ 'change @ui.checkbox': 'selection'
+ },
+ /** Data binding */
+ bindings: {
+ '@ui.totalReach': {
+ observe: 'reach',
+ onGet: accounting.formatNumber
},
- /** Events */
- events: {
- 'click @ui.update': 'update',
- 'click @ui.toggleAds': 'creatives',
- 'change @ui.checkbox': 'selection'
- },
- /** Data binding */
- bindings: {
- '@ui.totalReach': {
- observe: 'reach',
- onGet: accounting.formatNumber
- },
- '@ui.audienceSummary': '_audience'
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- onShow: function() {
- /** Use UI keys in bindings */
- this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
- /** Add stickit */
- this.stickit();
- /** Get reach */
- this.model.getReach();
- /** Number of creatives and hide creative container */
- $(this.ui.creativesCount).text(this.collection.length);
- $(this.ui.adsContainer).hide();
- /** Create bid view */
- var bidView = new Module.BidView({
- button: false,
- collection: this.collection,
- model: this.model
- });
- bidView.model = this.model;
- /** Create bid region */
- var region = new Backbone.Marionette.Region({
- el: this.ui.bid
- });
- region.show(bidView);
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- selection: function(event) {
- /** Check if checkbox is checked */
- var isSelected = $(event.target).is(':checked');
- /** Add selection status to model */
- this.model.set('_selected', isSelected);
- },
- /** @memberof App.module('AdCreationApp.Views').Budget */
- update: function(event) {
- /** Variables */
- var button = this.$('button.btn-update');
- var attributes = this.model.attributes;
- /** Functions */
- var updating = function() {
- button.removeClass('failed updating');
- button.addClass('updating');
- };
- var success = function() {
- button.removeClass('failed updating');
- };
- var failed = function() {
- button.removeClass('failed updating');
- button.addClass('failed');
- };
- /** Add class */
- updating();
- /** Save model */
- this.model.save(attributes, {
- success: success,
- error: failed
- });
- },
- creatives: function(event) {
- /** Rotate icon */
- $('i.fa', this.ui.toggleAds).toggleClass('fa-rotate-45');
- /** Toggle ads container */
- var toggle = $(this.ui.adsContainer).toggle().is(':visible');
- /** Parse facebook markup */
- if (toggle) {
- FB.XFBML.parse(this.ui.adsContainer[0]);
- }
+ '@ui.audienceSummary': '_audience'
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ onShow: function() {
+ /** Use UI keys in bindings */
+ this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
+ /** Add stickit */
+ this.stickit();
+ /** Get reach */
+ this.model.getReach();
+ /** Number of creatives and hide creative container */
+ $(this.ui.creativesCount).text(this.collection.length);
+ $(this.ui.adsContainer).hide();
+ /** Create bid view */
+ var bidView = new Module.BidView({
+ button: false,
+ collection: this.collection,
+ model: this.model
+ });
+ bidView.model = this.model;
+ /** Create bid region */
+ var region = new Backbone.Marionette.Region({
+ el: this.ui.bid
+ });
+ region.show(bidView);
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ selection: function(event) {
+ /** Check if checkbox is checked */
+ var isSelected = $(event.target).is(':checked');
+ /** Add selection status to model */
+ this.model.set('_selected', isSelected);
+ },
+ /** @memberof App.module('AdCreationApp.Views').Budget */
+ update: function(event) {
+ /** Variables */
+ var button = this.$('button.btn-update');
+ var attributes = this.model.attributes;
+ /** Functions */
+ var updating = function() {
+ button.removeClass('failed updating');
+ button.addClass('updating');
+ };
+ var success = function() {
+ button.removeClass('failed updating');
+ };
+ var failed = function() {
+ button.removeClass('failed updating');
+ button.addClass('failed');
+ };
+ /** Add class */
+ updating();
+ /** Save model */
+ this.model.save(attributes, {
+ success: success,
+ error: failed
+ });
+ },
+ creatives: function(event) {
+ /** Rotate icon */
+ $('i.fa', this.ui.toggleAds).toggleClass('fa-rotate-45');
+ /** Toggle ads container */
+ var toggle = $(this.ui.adsContainer).toggle().is(':visible');
+ /** Parse facebook markup */
+ if (toggle) {
+ FB.XFBML.parse(this.ui.adsContainer[0]);
}
+ }
+ });
+ Module.EmptyView = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.budgetEmpty')
+ });
+ Module.View = Marionette.CompositeView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.budgetBidWrapper'),
+ childViewContainer: '.collection',
+ /** Creatives array */
+ creatives: [],
+ /** Views */
+ childView: Module.LayoutView,
+ emptyView: Module.EmptyView,
+ ui: {
+ 'selectAll': '#select-all'
+ },
+ events: {
+ 'change': 'optionSelected',
+ 'change @ui.selectAll': 'selectAll'
+ },
+ collectionEvents: {
+ 'change': 'canContinue'
+ },
+ /** This triggers a click on all of the checkboxes on the child views. */
+ selectAll: function() {
+ this.$childViewContainer.find('input[type=checkbox]').trigger('click');
+ },
+ optionSelected: function(event) {
+ var $target = $(event.target);
+ var adCollectionId = Number($target.val());
+ var model = this.collection.get(adCollectionId);
+ var method = $target.is(':checked') ? 'add' : 'remove';
+ this.selectedAdCollections[method](model);
+ },
+ /** Add options to child view */
+ childViewOptions: function(model, index) {
+ return {
+ collection: this.getOption('creatives')
+ };
+ },
+ getSelectedAdcollections: function() {
+ return this.selectedAdCollections;
+ },
+ /** Initializer */
+ initialize: function(options) {
+ this.selectedAdCollections = new Backbone.Collection([], {
+ // TODO: Change this to leverage our record method or...
+ // change the others to this style.
+ model: AdvisorApp.module('AdCreationApp.Models').AdCollection
+ });
+ },
+ /** Enables and disables the continue button based on whether or not all bids and budgets are set. */
+ canContinue: function() {
+ App.module('AdCreationApp').trigger('bid-and-budget-can-continue', this.collection.allModelsBidAndBudgetAreValid());
+ }
+ });
- });
- /**
- * Empty view
- */
- Module.EmptyView = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.budgetEmpty')
- });
- /**
- * Default view
- */
- Module.View = Marionette.CompositeView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.budgetBidWrapper'),
- childViewContainer: '.collection',
- /** Creatives array */
- creatives: [],
- /** Views */
- childView: Module.LayoutView,
- emptyView: Module.EmptyView,
- ui: {
- 'selectAll': '#select-all'
- },
- events: {
- 'change': 'optionSelected',
- 'change @ui.selectAll': 'selectAll'
- },
- collectionEvents: {
- 'change': 'canContinue'
- },
- /** This triggers a click on all of the checkboxes on the child views. */
- selectAll: function() {
- this.$childViewContainer.find('input[type=checkbox]').trigger('click');
- },
- optionSelected: function(event) {
- var $target = $(event.target);
- var adCollectionId = Number($target.val());
- var model = this.collection.get(adCollectionId);
- var method = $target.is(':checked') ? 'add' : 'remove';
- this.selectedAdCollections[method](model);
- },
- /** Add options to child view */
- childViewOptions: function(model, index) {
- return {
- collection: this.getOption('creatives')
- };
- },
- getSelectedAdcollections: function() {
- return this.selectedAdCollections;
- },
- /** Initializer */
- initialize: function(options) {
- this.selectedAdCollections = new Backbone.Collection([], {
- // TODO: Change this to leverage our record method or...
- // change the others to this style.
- model: AdvisorApp.module('AdCreationApp.Models').AdCollection
- });
- },
- /** Enables and disables the continue button based on whether or not all bids and budgets are set. */
- canContinue: function() {
- App.module('AdCreationApp').trigger('bid-and-budget-can-continue', this.collection.allModelsBidAndBudgetAreValid());
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/audience/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp.Audience') */
- AdvisorApp.module('AdCreationApp.Audience', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Audience').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Audience = require('./controller');
- /** Modules */
- var Audience = App.module('AdCreationApp.Audience');
- /** Get creative view */
- var audience = new Audience.View({
- collection: options.collection
- });
+ /** @namespace AdvisorApp.module('AdCreationApp.Audience') */
+ /* module definition */
+ var Module = {};
- /** Show view in region */
- (options.region).show(audience);
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Audience').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Audience = AdCreationApp.Audience;
+ /** Get creative view */
+ var audience = new Audience.View({
+ collection: options.collection
+ });
+ /** Show view in region */
+ (options.region).show(audience);
+ // Destroy this controller when its view is destroyed
+ this.listenTo(audience, 'destroy', this.destroy);
+ }
+ });
- // Destroy this controller when its view is destroyed
- this.listenTo(audience, 'destroy', this.destroy);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/audience/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Audience', function(Module, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- Module.ChildView = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.audience'),
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** Set classname */
- className: 'audience-item',
- /** UI */
- ui: {
- 'remove': '[data-action="audience:delete"]'
+ Module.ChildView = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.audience'),
+ /** Set classname */
+ className: 'audience-item',
+ /** UI */
+ ui: {
+ 'remove': '[data-action="audience:delete"]'
+ },
+ /** Events */
+ events: {
+ 'click @ui.remove': 'removeModel'
+ },
+ /** @memberof App.module('AdCreationApp.Views').Audience */
+ preference: {
+ 'custom_audiences': {},
+ 'user_adclusters': {},
+ 'life_events': {},
+ 'politics': {},
+ 'markets': {},
+ 'industries': {},
+ 'income': {},
+ 'net_worth': {},
+ 'home_type': {},
+ 'home_ownership': {},
+ 'home_value': {},
+ 'ethnic_affinity': {},
+ 'generation': {},
+ 'household_composition': {},
+ 'moms': {},
+ 'office_type': {},
+ 'family_statuses': {},
+ 'user_device': {},
+ 'user_os': {},
+ 'partner_categories': {},
+ 'behaviors': {
+ minimumInputLength: 0
},
- /** Events */
- events: {
- 'click @ui.remove': 'removeModel'
+ 'education_schools': {
+ minimumInputLength: 2
},
- /** @memberof App.module('AdCreationApp.Views').Audience */
- preference: {
- 'custom_audiences': {},
- 'user_adclusters': {},
- 'life_events': {},
- 'politics': {},
- 'markets': {},
- 'industries': {},
- 'income': {},
- 'net_worth': {},
- 'home_type': {},
- 'home_ownership': {},
- 'home_value': {},
- 'ethnic_affinity': {},
- 'generation': {},
- 'household_composition': {},
- 'moms': {},
- 'office_type': {},
- 'family_statuses': {},
- 'user_device': {},
- 'user_os': {},
- 'partner_categories': {},
- 'behaviors': {
- minimumInputLength: 0
- },
- 'education_schools': {
- minimumInputLength: 2
- },
- 'education_majors': {
- minimumInputLength: 2
- },
- 'interests': {
- minimumInputLength: 2
- },
- 'locales': {
- minimumInputLength: 1
- },
- 'work_employers': {
- minimumInputLength: 2
- },
- 'work_positions': {
- minimumInputLength: 2
- }
+ 'education_majors': {
+ minimumInputLength: 2
},
- /** @memberof App.module('AdCreationApp.Views').Audience */
- onShow: function() {
- /** Modules */
- var Controller = App.module('AdCreationApp').record('controller');
- var Account = App.module('AdCreationApp').record('account');
- /** Model attributes */
- var model = {
- include: this.model.get('include'),
- exclude: this.model.get('exclude'),
- types: this.model.get('targeting_type')
- };
- // Set up mini view
- this.collectionsChanged();
- this.listenTo(model.include, 'all', this.collectionsChanged);
- this.listenTo(model.exclude, 'all', this.collectionsChanged);
- /** Data object */
- var data = $.extend(true, {
- platform: 'FACEBOOK',
- targeting_type: model.types,
- account_id: Account
- }, this.model.get('additional_info'));
- var options = $.extend(true, {
- data: data,
- minimumInputLength: 0
- }, this.preference[model.types]);
- /** Create multiselect */
- Controller.multiselect(this.$('input.include'), model.include, options);
- Controller.multiselect(this.$('input.exclude'), model.exclude, options);
+ 'interests': {
+ minimumInputLength: 2
},
- collectionsChanged: function() {
- var miniTemplate = App.module('templates.adcreation.audienceMiniItem');
- this.$('.sc-unfocused-view').html(miniTemplate({
- title: this.model.get('title'),
- include: this.model.get('include').toJSON(),
- exclude: this.model.get('exclude').toJSON()
- }));
+ 'locales': {
+ minimumInputLength: 1
},
- /** @memberof App.module('AdCreationApp.Views').Audience */
- removeModel: function(event) {
- event.preventDefault();
- event.stopPropagation();
- /** Remove current model */
- this.model.collection.remove(this.model);
+ 'work_employers': {
+ minimumInputLength: 2
},
- onDestroy: function() {
- // Clean up Select2 widgets
- this.$('input.include, input.exclude').each(function() {
- $(this).select2('destroy');
- });
+ 'work_positions': {
+ minimumInputLength: 2
}
+ },
+ /** @memberof App.module('AdCreationApp.Views').Audience */
+ onShow: function() {
+ /** Modules */
+ var Controller = App.module('AdCreationApp').record('controller');
+ var Account = App.module('AdCreationApp').record('account');
+ /** Model attributes */
+ var model = {
+ include: this.model.get('include'),
+ exclude: this.model.get('exclude'),
+ types: this.model.get('targeting_type')
+ };
+ // Set up mini view
+ this.collectionsChanged();
+ this.listenTo(model.include, 'all', this.collectionsChanged);
+ this.listenTo(model.exclude, 'all', this.collectionsChanged);
+ /** Data object */
+ var data = $.extend(true, {
+ platform: 'FACEBOOK',
+ targeting_type: model.types,
+ account_id: Account
+ }, this.model.get('additional_info'));
+ var options = $.extend(true, {
+ data: data,
+ minimumInputLength: 0
+ }, this.preference[model.types]);
+ /** Create multiselect */
+ Controller.multiselect(this.$('input.include'), model.include, options);
+ Controller.multiselect(this.$('input.exclude'), model.exclude, options);
+ },
+ collectionsChanged: function() {
+ var miniTemplate = App.module('templates.adcreation.audienceMiniItem');
+ this.$('.sc-unfocused-view').html(miniTemplate({
+ title: this.model.get('title'),
+ include: this.model.get('include').toJSON(),
+ exclude: this.model.get('exclude').toJSON()
+ }));
+ },
+ /** @memberof App.module('AdCreationApp.Views').Audience */
+ removeModel: function(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ /** Remove current model */
+ this.model.collection.remove(this.model);
+ },
+ onDestroy: function() {
+ // Clean up Select2 widgets
+ this.$('input.include, input.exclude').each(function() {
+ $(this).select2('destroy');
+ });
+ }
+ });
+ Module.EmptyView = Marionette.ItemView.extend({
+ /** Set tag and classname */
+ className: 'audience-item audience-empty',
+ /** Handlebar template */
+ template: App.module('templates.adcreation.audienceEmpty')
+ });
+ Module.View = Marionette.CollectionView.extend({
+ /** Set classname */
+ className: 'audience-group',
+ /** Views */
+ childView: Module.ChildView,
+ emptyView: Module.EmptyView
+ });
- });
- Module.EmptyView = Marionette.ItemView.extend({
- /** Set tag and classname */
- className: 'audience-item audience-empty',
- /** Handlebar template */
- template: App.module('templates.adcreation.audienceEmpty')
- });
- Module.View = Marionette.CollectionView.extend({
- /** Set classname */
- className: 'audience-group',
- /** Views */
- childView: Module.ChildView,
- emptyView: Module.EmptyView
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/bulk-upload/view.js
- AdvisorApp.module('AdCreationApp.BulkUpload', function(Module, App, Backbone, Marionette, $, _) {
- this.View = Marionette.ItemView.extend({
- template: AdvisorApp.module('templates.adcreation.bulkUpload'),
+ /* global dependencies */
+ var AdvisorApp = require('../../../app');
- bulkUploadDropzone: {},
- campaign: {},
+ /* module definition */
+ var Module = {};
- ui: {
- 'submit': '#submit',
- 'fileuploadzone': '#dropzone-bulkupload'
- },
+ this.View = Marionette.ItemView.extend({
+ template: AdvisorApp.module('templates.adcreation.bulkUpload'),
+ bulkUploadDropzone: {},
+ campaign: {},
+ ui: {
+ 'submit': '#submit',
+ 'fileuploadzone': '#dropzone-bulkupload'
+ },
+ events: {
+ 'click @ui.submit': 'submit'
+ },
+ modalConfigurations: {
+ title: 'New ads'
+ },
+ behaviors: {
+ DisplayError: {}
+ },
+ initialize: function() {
+ window.Dropzone.autoDiscover = false;
+ this.campaign = App.module('AdCreationApp').record('campaign');
+ },
+ onAttach: function() {
+ var self = this;
+ this.bulkUploadDropzone = new window.Dropzone('#dropzone-bulkupload', {
+ maxFiles: 1,
+ maxFilesize: 10, //mb
+ addRemoveLinks: false,
+ url: '/api/advisor/v1/adcluster/upload_bulk_adcollections/',
+ autoProcessQueue: false, //used for stopping auto processing uploads
+ paramName: 'file',
+ accept: function(file, done) {
+ //used for enabling the submit button if file exist
+ self.ui.submit.removeAttr('disabled').removeClass('disabled');
+ done();
+ },
+ complete: function(e) {
+ self.triggerMethod('modal:close');
+ },
+ sending: function(file, xhr, formData) {
+ formData.append('campaign', self.campaign.attributes.resource_uri);
+ }
+ });
+ },
+ /** Does two things--closes the modal and tell the app to show the form. */
+ submit: function(event) {
+ event.preventDefault();
+ this.bulkUploadDropzone.processQueue();
+ //this.triggerMethod('modal:close');
+ }
+ });
- events: {
- 'click @ui.submit': 'submit'
- },
- modalConfigurations: {
- title: 'New ads'
- },
- behaviors: {
- DisplayError: {}
- },
- initialize: function() {
- window.Dropzone.autoDiscover = false;
- this.campaign = App.module('AdCreationApp').record('campaign');
- },
- onAttach: function() {
- var self = this;
- this.bulkUploadDropzone = new window.Dropzone('#dropzone-bulkupload', {
- maxFiles: 1,
- maxFilesize: 10, //mb
- addRemoveLinks: false,
- url: '/api/advisor/v1/adcluster/upload_bulk_adcollections/',
- autoProcessQueue: false, //used for stopping auto processing uploads
- paramName: 'file',
- accept: function(file, done) {
- //used for enabling the submit button if file exist
- self.ui.submit.removeAttr('disabled').removeClass('disabled');
- done();
- },
- complete: function(e) {
- self.triggerMethod('modal:close');
- },
- sending: function(file, xhr, formData) {
- formData.append('campaign', self.campaign.attributes.resource_uri);
- }
- });
- },
- /** Does two things--closes the modal and tell the app to show the form. */
- submit: function(event) {
- event.preventDefault();
- this.bulkUploadDropzone.processQueue();
- //this.triggerMethod('modal:close');
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/creation/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Creation', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var CreativeApp.Creation = require('../../creative/creation/controller');
- /** Modules */
- var Controller = App.module('AdCreationApp').record('controller');
- var Creation = App.module('CreativeApp.Creation');
- /** Model */
- var model = new Backbone.Model();
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Get creative view */
- var view = new Creation.View({
- model: model
- });
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Controller = App.module('AdCreationApp').record('controller');
+ var Creation = CreativeApp.Creation;
+ /** Model */
+ var model = new Backbone.Model();
+ /** Get creative view */
+ var view = new Creation.View({
+ model: model
+ });
+ /** Show view */
+ (options.region).show(view);
+ // Destroy this controller when its view is destroyed
+ this.listenTo(view, 'destroy', this.destroy);
+ }
+ });
- /** Show view */
- (options.region).show(view);
- // Destroy this controller when its view is destroyed
- this.listenTo(view, 'destroy', this.destroy);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/creative/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Creative', function(Module, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Creative = require('./controller');
- /** Modules */
- var Creative = App.module('AdCreationApp.Creative');
- var Controller = App.module('AdCreationApp').record('controller');
- /** Get creative layout view */
- this.view = new Creative.View();
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Selected creative model */
- this.selectedCreatives = App.module('AdCreationApp').record('model:adcluster');
- // Generic storage, for selected creatives
- this.storage = App.module('AdCreationApp').record('model:generic');
- /** Show view */
- (options.region).show(this.view);
- /** Analytics */
- App.trigger('usage:pageevent', 'Ad Creation', 'Add New Creative');
- /** Fetch list of assets for initiative */
- var initiative = App.module('AdCreationApp').record('initiative');
- var campaign = App.module('AdCreationApp').record('campaign');
- var assets = initiative.fetchAssets();
- var objective = campaign.get('objective');
- /** */
- var getTypes = function(type) {
- /** List of types */
- var types = ({
- page_likes: ['event'],
- post_engagement: ['photo', 'status'],
- website_conversions: ['domain', 'link'],
- mobile_app_installs: ['application', 'store_item'],
- mobile_app_engagement: ['application', 'store_item'],
- website_clicks: ['domain'],
- video_views: ['video'],
- event_responses: [],
- offer_claims: []
- });
- /** Get list of creative types */
- var objectiveType = type.toLowerCase();
- var typeArray = types[objectiveType];
- /** Return comma separated list of types */
- return typeArray && typeArray.join(',');
- };
- // Set up our parameters to pass to the creatives list
- this.params = {
- type__in: getTypes(objective)
- };
- this.displayMode = 'table';
- /** Get & show creative toolbar view */
- var toolbarViewOptions = this.getHeaderToolbarOptions(initiative);
- /** Get the initiative module. */
- var initiativeChannel = Backbone.Wreqr.radio.channel('initiativeModule');
- App.trigger('adcreation:toolbar:show', toolbarViewOptions);
- // Draw the asset selector
- initiativeChannel.vent.trigger('initiative:assets:select', {
- region: this.view.assets,
- initiative: initiative
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Creative = AdCreationApp.Creative;
+ var Controller = App.module('AdCreationApp').record('controller');
+ /** Get creative layout view */
+ this.view = new Creative.View();
+ /** Selected creative model */
+ this.selectedCreatives = App.module('AdCreationApp').record('model:adcluster');
+ // Generic storage, for selected creatives
+ this.storage = App.module('AdCreationApp').record('model:generic');
+ /** Show view */
+ (options.region).show(this.view);
+ /** Analytics */
+ App.trigger('usage:pageevent', 'Ad Creation', 'Add New Creative');
+ /** Fetch list of assets for initiative */
+ var initiative = App.module('AdCreationApp').record('initiative');
+ var campaign = App.module('AdCreationApp').record('campaign');
+ var assets = initiative.fetchAssets();
+ var objective = campaign.get('objective');
+ /** */
+ var getTypes = function(type) {
+ /** List of types */
+ var types = ({
+ page_likes: ['event'],
+ post_engagement: ['photo', 'status'],
+ website_conversions: ['domain', 'link'],
+ mobile_app_installs: ['application', 'store_item'],
+ mobile_app_engagement: ['application', 'store_item'],
+ website_clicks: ['domain'],
+ video_views: ['video'],
+ event_responses: [],
+ offer_claims: []
});
- // Listen for asset selection, then ask the creatives view to draw
- this.listenTo(initiativeChannel.vent, 'initiative:assets:selected', function(asset) {
- this.selectedAsset = asset;
- this.showCreatives();
- });
- // Listen for creative selection, then store it
- this.listenTo(AdvisorApp.vent, 'creatives:list:selected', this.selectCreative);
- // If the toolbar emits a search, draw a new creatives view.
- this.listenTo(AdvisorApp, 'creatives:search', this.onSearch);
- // If the toolbar emits a filter selection, draw a new creatives views.
- this.listenTo(AdvisorApp, 'creatives:filter', this.filterCreatives);
- // Listen for a display mode change from the toolbar
- this.listenTo(AdvisorApp, 'creatives:mode', this.changeMode);
- // Listen to create creative button press
- this.listenTo(AdvisorApp, 'creatives:create', this.showCreateCreatives);
- // Destroy this controller when its LayoutView is destroyed
- this.listenTo(this.view, 'destroy', this.destroy);
- },
- showCreateCreatives: function() {
- // Navigate to creative creation page
- App.trigger('adcreation:creative:create:show', {
- restore: function() {
- App.trigger('adcreation:initial:show');
- }
- });
- },
- selectCreative: function(selected) {
- // Store selected creatives, so we can render them later
- this.storage.set('_creatives', selected);
- var adCreatives = _.map(selected, function(obj) {
- return { 'creative_id': obj.id };
- });
- // Assign the id of selected creatives onto the adcluster
- this.selectedCreatives.set('ad_creatives', adCreatives);
- },
- onSearch: function(query) {
- this.params.query = query;
+ /** Get list of creative types */
+ var objectiveType = type.toLowerCase();
+ var typeArray = types[objectiveType];
+ /** Return comma separated list of types */
+ return typeArray && typeArray.join(',');
+ };
+ // Set up our parameters to pass to the creatives list
+ this.params = {
+ type__in: getTypes(objective)
+ };
+ this.displayMode = 'table';
+ /** Get & show creative toolbar view */
+ var toolbarViewOptions = this.getHeaderToolbarOptions(initiative);
+ /** Get the initiative module. */
+ var initiativeChannel = Backbone.Wreqr.radio.channel('initiativeModule');
+ App.trigger('adcreation:toolbar:show', toolbarViewOptions);
+ // Draw the asset selector
+ initiativeChannel.vent.trigger('initiative:assets:select', {
+ region: this.view.assets,
+ initiative: initiative
+ });
+ // Listen for asset selection, then ask the creatives view to draw
+ this.listenTo(initiativeChannel.vent, 'initiative:assets:selected', function(asset) {
+ this.selectedAsset = asset;
this.showCreatives();
- },
- showCreatives: function() {
- var creativesView = AdvisorApp.request('creative:list:view', {
- asset: this.selectedAsset,
- displaymode: this.displayMode,
- params: this.params,
- region: this.view.creatives
- });
- },
- filterByDate: function(model) {
- this.params.created_at__gte = model.get('start').format('YYYY-MM-DD');
- this.params.created_at__lte = model.get('end').format('YYYY-MM-DD');
- this.showCreatives();
- },
- filterCreatives: function(type) {
- if (type === 'all') {
- delete this.params.type;
- } else {
- this.params.type = type;
+ });
+ // Listen for creative selection, then store it
+ this.listenTo(AdvisorApp.vent, 'creatives:list:selected', this.selectCreative);
+ // If the toolbar emits a search, draw a new creatives view.
+ this.listenTo(AdvisorApp, 'creatives:search', this.onSearch);
+ // If the toolbar emits a filter selection, draw a new creatives views.
+ this.listenTo(AdvisorApp, 'creatives:filter', this.filterCreatives);
+ // Listen for a display mode change from the toolbar
+ this.listenTo(AdvisorApp, 'creatives:mode', this.changeMode);
+ // Listen to create creative button press
+ this.listenTo(AdvisorApp, 'creatives:create', this.showCreateCreatives);
+ // Destroy this controller when its LayoutView is destroyed
+ this.listenTo(this.view, 'destroy', this.destroy);
+ },
+ showCreateCreatives: function() {
+ // Navigate to creative creation page
+ App.trigger('adcreation:creative:create:show', {
+ restore: function() {
+ App.trigger('adcreation:initial:show');
}
- this.showCreatives();
- },
- changeMode: function(mode) {
- this.displayMode = mode;
- this.showCreatives();
- },
- /**
- * The configuration object for the header toolbar got *really* big and gnarly.
- * Moved it to its own method for readability and sanity
- * @param {Backbone.Model} initiative
- * @returns {Object}
- */
- getHeaderToolbarOptions: function(initiative) {
- var events = {
- 'keyup #creative-search': function(event) {
- AdvisorApp.trigger('creatives:search', $(event.target).val());
- },
- 'submit form': function(event) {
- event.preventDefault();
- },
- 'click #creative-filter .dropdown-menu a': function(event) {
- event.preventDefault();
- var $selectedFilterEl = $(event.currentTarget);
- this.$('li').removeClass('active');
- $selectedFilterEl.parent('li').addClass('active');
- this.$('.control-value').text($selectedFilterEl.text());
- AdvisorApp.trigger('creatives:filter', $selectedFilterEl.data('filterType'));
- },
- 'change input[name=viewmode]': function(e) {
- AdvisorApp.trigger('creatives:mode', $(e.currentTarget).val());
- },
- 'click #create-creative': function(event) {
- AdvisorApp.trigger('creatives:create', event);
- }
- };
- var dateFilterView = new (AdvisorApp.module('Common.Views')).DateRangePicker();
- this.listenTo(dateFilterView.model, 'change', this.filterByDate);
- return {
- events: events,
- filterOptions: AdvisorApp.module('Common.Language').en.creativeFilters,
- viewName: 'creative',
- dateFilterView: dateFilterView
- };
+ });
+ },
+ selectCreative: function(selected) {
+ // Store selected creatives, so we can render them later
+ this.storage.set('_creatives', selected);
+ var adCreatives = _.map(selected, function(obj) {
+ return { 'creative_id': obj.id };
+ });
+ // Assign the id of selected creatives onto the adcluster
+ this.selectedCreatives.set('ad_creatives', adCreatives);
+ },
+ onSearch: function(query) {
+ this.params.query = query;
+ this.showCreatives();
+ },
+ showCreatives: function() {
+ var creativesView = AdvisorApp.request('creative:list:view', {
+ asset: this.selectedAsset,
+ displaymode: this.displayMode,
+ params: this.params,
+ region: this.view.creatives
+ });
+ },
+ filterByDate: function(model) {
+ this.params.created_at__gte = model.get('start').format('YYYY-MM-DD');
+ this.params.created_at__lte = model.get('end').format('YYYY-MM-DD');
+ this.showCreatives();
+ },
+ filterCreatives: function(type) {
+ if (type === 'all') {
+ delete this.params.type;
+ } else {
+ this.params.type = type;
}
- });
+ this.showCreatives();
+ },
+ changeMode: function(mode) {
+ this.displayMode = mode;
+ this.showCreatives();
+ },
+ /**
+ * The configuration object for the header toolbar got *really* big and gnarly.
+ * Moved it to its own method for readability and sanity
+ * @param {Backbone.Model} initiative
+ * @returns {Object}
+ */
+ getHeaderToolbarOptions: function(initiative) {
+ var events = {
+ 'keyup #creative-search': function(event) {
+ AdvisorApp.trigger('creatives:search', $(event.target).val());
+ },
+ 'submit form': function(event) {
+ event.preventDefault();
+ },
+ 'click #creative-filter .dropdown-menu a': function(event) {
+ event.preventDefault();
+ var $selectedFilterEl = $(event.currentTarget);
+ this.$('li').removeClass('active');
+ $selectedFilterEl.parent('li').addClass('active');
+ this.$('.control-value').text($selectedFilterEl.text());
+ AdvisorApp.trigger('creatives:filter', $selectedFilterEl.data('filterType'));
+ },
+ 'change input[name=viewmode]': function(e) {
+ AdvisorApp.trigger('creatives:mode', $(e.currentTarget).val());
+ },
+ 'click #create-creative': function(event) {
+ AdvisorApp.trigger('creatives:create', event);
+ }
+ };
+ var dateFilterView = new (AdvisorApp.module('Common.Views')).DateRangePicker();
+ this.listenTo(dateFilterView.model, 'change', this.filterByDate);
+ return {
+ events: events,
+ filterOptions: AdvisorApp.module('Common.Language').en.creativeFilters,
+ viewName: 'creative',
+ dateFilterView: dateFilterView
+ };
+ }
+ });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/creative/view.js
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
/** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Creative', function(Module, App) {
+ /* module definition */
+ var Module = {};
- Module.View = Marionette.LayoutView.extend({
- template: App.module('templates.adcreation.creative'),
- regions: {
- 'assets': '#assets-region',
- 'creatives': '#creatives-region'
- }
- });
+ Module.View = Marionette.LayoutView.extend({
+ template: App.module('templates.adcreation.creative'),
+ regions: {
+ 'assets': '#assets-region',
+ 'creatives': '#creatives-region'
+ }
+ });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/container/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Container', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Container = require('./controller');
- /** Modules */
- var Container = App.module('AdCreationApp.Container');
- /** Get views */
- var view = new Container.View();
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Show view in region */
- this.view = (options.region).show(view) && view;
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Container = AdCreationApp.Container;
+ /** Get views */
+ var view = new Container.View();
+ /** Show view in region */
+ this.view = (options.region).show(view) && view;
+ // Destroy this controller when its view is destroyed
+ this.listenTo(view, 'destroy', this.destroy);
+ }
+ });
- // Destroy this controller when its view is destroyed
- this.listenTo(view, 'destroy', this.destroy);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/container/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Container', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.View = Backbone.Marionette.LayoutView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.container'),
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** Regions */
- regions: {
- contentRegion: '[data-region=content]',
- navigationRegion: '[data-region=navigation]'
- }
+ Module.View = Backbone.Marionette.LayoutView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.container'),
+ /** Regions */
+ regions: {
+ contentRegion: '[data-region=content]',
+ navigationRegion: '[data-region=navigation]'
+ }
+ });
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/common/controller.js
+ /* global dependencies */
+ var _ = require('underscore');
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
/** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp', function(Module, App) {
+ /* module definition */
+ var Module = {};
- /**
- * Controller
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /**
+ * Model
*/
- Module.Controller = Backbone.Marionette.Object.extend({
- /**
- * Model
- */
- model: function(resource) {
- /** Create models & collections */
- var generic = new Module.Models.Generic();
- var adcluster = new Module.Models.AdCluster();
- var adcollections = new Module.Models.AdCollections();
- /** Add resource to adcluster */
- adcluster.set('campaign', resource);
- /** Record models & collections */
- Module.record('model:generic', generic);
- Module.record('model:adcluster', adcluster);
- Module.record('collection:adcollections', adcollections);
+ model: function(resource) {
+ /** Create models & collections */
+ var generic = new Module.Models.Generic();
+ var adcluster = new Module.Models.AdCluster();
+ var adcollections = new Module.Models.AdCollections();
+ /** Add resource to adcluster */
+ adcluster.set('campaign', resource);
+ /** Record models & collections */
+ Module.record('model:generic', generic);
+ Module.record('model:adcluster', adcluster);
+ Module.record('collection:adcollections', adcollections);
+ },
+ /**
+ * Region
+ */
+ region: {
+ main: function() {
+ return App.getRegion('mainRegion');
},
- /**
- * Region
- */
- region: {
- main: function() {
- return App.getRegion('mainRegion');
+ navigation: function() {
+ return App.getRegion('mainRegion').currentView.getRegion('navigationRegion');
+ },
+ content: function() {
+ return App.getRegion('mainRegion').currentView.getRegion('contentRegion');
+ }
+ },
+ /**
+ * Scenes
+ */
+ scene: function(key) {
+ Module.record('model:generic').set('scene', key);
+ },
+ /**
+ * URI
+ */
+ uri: function(value) {
+ /** URI structure */
+ var url = '/initiative/:iID/campaign/:cID/adgen/:scene/';
+ /** IDs */
+ var iID = Module.record('initiative:id');
+ var cID = Module.record('campaign:id');
+ /** Replace string in URI */
+ url = url.replace(':iID', iID)
+ .replace(':cID', cID)
+ .replace(':scene', value);
+ /** Navigate to URI */
+ App.navigate(url);
+ },
+ /**
+ * Multiselect for select2
+ */
+ multiselect: function(el, dataModel, customOptions) {
+ // Disable this input until we have a chance to initialize
+ el.prop('disabled', true);
+ // Variables
+ var ENDPOINT = '/api/advisor/v1/targetingsubset/suggestions/';
+ var PARAMETERS = customOptions.data;
+ // Reference
+ var model = this.model;
+ var suggestions = this.suggestions;
+ // Default options
+ var options = {
+ placeholder: el.attr('placeholder'),
+ minimumInputLength: 2,
+ multiple: true,
+ containerCssClass: 'sc-adcreation',
+ dropdownCssClass: 'sc-adcreation',
+ // Setter to add ajax object
+ set async(isAJAX) {
+ if (isAJAX) {
+ // Default ajax object
+ this.ajax = {
+ url: ENDPOINT,
+ quietMillis: 250,
+ data: onData,
+ results: onResults
+ };
+ } else delete this.ajax;
+ },
+ formatSelection: function(obj) {
+ return obj.text || obj.name;
},
- navigation: function() {
- return App.getRegion('mainRegion').currentView.getRegion('navigationRegion');
- },
- content: function() {
- return App.getRegion('mainRegion').currentView.getRegion('contentRegion');
+ get async() {
+ return;
}
- },
- /**
- * Scenes
+ };
+ // Extend options with customOptions
+ $.extend(true, options, customOptions);
+ // Check if this should be asynchronous call
+ var isAsync = (customOptions.minimumInputLength || options.minimumInputLength) > 0;
+ /**
+ * Listeners
*/
- scene: function(key) {
- Module.record('model:generic').set('scene', key);
- },
- /**
- * URI
+ var onData = function(query) {
+ return $.extend({ 'q': query, 'limit': 6 }, PARAMETERS);
+ };
+ var onResults = function(response) {
+ var data = response.objects && response.objects.data || response.objects || [];
+ return {
+ results: suggestions.format(data)
+ };
+ };
+ /**
+ * Initializer
*/
- uri: function(value) {
- /** URI structure */
- var url = '/initiative/:iID/campaign/:cID/adgen/:scene/';
- /** IDs */
- var iID = Module.record('initiative:id');
- var cID = Module.record('campaign:id');
- /** Replace string in URI */
- url = url.replace(':iID', iID)
- .replace(':cID', cID)
- .replace(':scene', value);
- /** Navigate to URI */
- App.navigate(url);
- },
- /**
- * Multiselect for select2
- */
- multiselect: function(el, dataModel, customOptions) {
- // Disable this input until we have a chance to initialize
- el.prop('disabled', true);
- // Variables
- var ENDPOINT = '/api/advisor/v1/targetingsubset/suggestions/';
- var PARAMETERS = customOptions.data;
- // Reference
- var model = this.model;
- var suggestions = this.suggestions;
- // Default options
- var options = {
- placeholder: el.attr('placeholder'),
- minimumInputLength: 2,
- multiple: true,
- containerCssClass: 'sc-adcreation',
- dropdownCssClass: 'sc-adcreation',
- // Setter to add ajax object
- set async(isAJAX) {
- if (isAJAX) {
- // Default ajax object
- this.ajax = {
- url: ENDPOINT,
- quietMillis: 250,
- data: onData,
- results: onResults
- };
- } else delete this.ajax;
- },
- formatSelection: function(obj) {
- return obj.text || obj.name;
- },
- get async() {
- return;
- }
- };
- // Extend options with customOptions
- $.extend(true, options, customOptions);
- // Check if this should be asynchronous call
- var isAsync = (customOptions.minimumInputLength || options.minimumInputLength) > 0;
- /**
- * Listeners
+ var initialize = function(opts, data) {
+ // Enable the control
+ el.prop('disabled', false);
+ // Add default options to element
+ el.select2(opts)
+ .select2('data', data.toJSON());
+ // Listeners
+ el.on('select2-selecting', function(event) {
+ dataModel.add(event.choice);
+ });
+ el.on('select2-removing', function(event) {
+ dataModel.remove(dataModel.get(event.choice.id));
+ });
+ /**
+ * HACK: Needs to be fixed when Select2 ^4.0.0 comes out
+ Select2 v3.5.2 doesn't return the currently selected element
+ when "select2-selecting" is triggered, instead returns the input
+ element. The jQuery handleObj returns the selector as undefined
+ but expected to return the li#select2-result object.
+ Therefor we need to listen to "select2-open", find the .select2-results
+ element that was created. Remove previously created listeners and listen
+ to clicks. Capture the currentTarget and toggle class.
+ Should ideally be written like this:
+ el.on('select2-selecting', function(event) {
+ $(event.target).toggleClass('select2-visible');
+ });
*/
- var onData = function(query) {
- return $.extend({ 'q': query, 'limit': 6 }, PARAMETERS);
- };
- var onResults = function(response) {
- var data = response.objects && response.objects.data || response.objects || [];
- return {
- results: suggestions.format(data)
- };
- };
- /**
- * Initializer
- */
- var initialize = function(opts, data) {
- // Enable the control
- el.prop('disabled', false);
- // Add default options to element
- el.select2(opts)
- .select2('data', data.toJSON());
- // Listeners
- el.on('select2-selecting', function(event) {
- dataModel.add(event.choice);
- });
- el.on('select2-removing', function(event) {
- dataModel.remove(dataModel.get(event.choice.id));
- });
- /**
- * HACK: Needs to be fixed when Select2 ^4.0.0 comes out
- Select2 v3.5.2 doesn't return the currently selected element
- when "select2-selecting" is triggered, instead returns the input
- element. The jQuery handleObj returns the selector as undefined
- but expected to return the li#select2-result object.
- Therefor we need to listen to "select2-open", find the .select2-results
- element that was created. Remove previously created listeners and listen
- to clicks. Capture the currentTarget and toggle class.
- Should ideally be written like this:
- el.on('select2-selecting', function(event) {
- $(event.target).toggleClass('select2-visible');
- });
- */
- el.on('select2-open', function(event) {
- // Listen to on clicks
- $('.select2-results').off('click').on('click', '.select2-result-with-children', function(event) {
- // Toogle group
- $('.select2-result-sub', event.currentTarget).first().toggle();
- event.stopPropagation();
- });
- });
- };
- // Check if this is a asynchronous call
- if (isAsync) {
- // Initialize asynchronous select
- options.async = true;
+ el.on('select2-open', function(event) {
+ // Listen to on clicks
+ $('.select2-results').off('click').on('click', '.select2-result-with-children', function(event) {
+ // Toogle group
+ $('.select2-result-sub', event.currentTarget).first().toggle();
+ event.stopPropagation();
+ });
+ });
+ };
+ // Check if this is a asynchronous call
+ if (isAsync) {
+ // Initialize asynchronous select
+ options.async = true;
+ initialize(options, dataModel);
+ } else {
+ // Fetch data and return promise
+ var promise = $.get(ENDPOINT, PARAMETERS);
+ // Set options data
+ promise.done(function(response) {
+ options.data = onResults(response);
+ });
+ // Initialize select
+ promise.always(function() {
+ options.async = false;
initialize(options, dataModel);
- } else {
- // Fetch data and return promise
- var promise = $.get(ENDPOINT, PARAMETERS);
- // Set options data
- promise.done(function(response) {
- options.data = onResults(response);
- });
- // Initialize select
- promise.always(function() {
- options.async = false;
- initialize(options, dataModel);
- });
+ });
+ }
+ },
+ /**
+ * Format suggestions endpoint for Select2
+ */
+ suggestions: {
+ format: function(obj) {
+ // Variables
+ var tree = {};
+ // Loop trough array and create object tree
+ for (var i = 0; i < obj.length; i++) {
+ this.build(tree, obj[i]);
}
+ // Convert object array to true array
+ return this.toArray(tree);
+ },
+ build: function(tree, obj) {
+ // Get paths and group name
+ var path = obj.path;
+ var group = path && path.shift() || obj.name;
+ // Check if object has a child
+ var hasChildren = path && path.length;
+ // Create array if missing
+ tree[group] = tree[group] || [];
+ // Build child group parent obj has children
+ tree[group] = hasChildren ? this.build(tree[group], obj) : obj;
+ // Return tree
+ return tree;
+ },
+ toArray: function(obj) {
+ // Variables
+ var tree = [];
+ // Loop through object
+ for (var key in obj) {
+ // Check object type
+ var isArray = obj[key] instanceof Array;
+ // Add group or object
+ var block = isArray ? {
+ id: key,
+ text: key,
+ children: this.toArray(obj[key]),
+ disabled: true
+ } : obj[key];
+ // Add needed elements in case they are missing
+ block.text = block.text || key;
+ block.id = block.id || obj[key].key || obj[key].name;
+ // Push to tree
+ tree.push(block);
+ }
+ // Return tree
+ return _.sortBy(tree, 'text');
+ }
+ }
+ });
+ Module.record = function(key, value) {
+ /** Variables */
+ var proto = Module.record.prototype;
+ var isSet = (key && value !== undefined) && 'set';
+ var isGet = (key && value === undefined) && 'get';
+ /** Create db obect */
+ proto.db = proto.db || {};
+ /** Actions */
+ switch (isSet || isGet) {
+ case 'set':
+ return (proto.db[key] = value) && value;
+ case 'get':
+ return proto.db[key] || null;
+ default:
+ delete proto.db;
+ }
+ };
- },
- /**
- * Format suggestions endpoint for Select2
- */
- suggestions: {
- format: function(obj) {
- // Variables
- var tree = {};
- // Loop trough array and create object tree
- for (var i = 0; i < obj.length; i++) {
- this.build(tree, obj[i]);
- }
- // Convert object array to true array
- return this.toArray(tree);
- },
- build: function(tree, obj) {
- // Get paths and group name
- var path = obj.path;
- var group = path && path.shift() || obj.name;
- // Check if object has a child
- var hasChildren = path && path.length;
- // Create array if missing
- tree[group] = tree[group] || [];
- // Build child group parent obj has children
- tree[group] = hasChildren ? this.build(tree[group], obj) : obj;
- // Return tree
- return tree;
- },
- toArray: function(obj) {
- // Variables
- var tree = [];
- // Loop through object
- for (var key in obj) {
- // Check object type
- var isArray = obj[key] instanceof Array;
- // Add group or object
- var block = isArray ? {
- id: key,
- text: key,
- children: this.toArray(obj[key]),
- disabled: true
- } : obj[key];
- // Add needed elements in case they are missing
- block.text = block.text || key;
- block.id = block.id || obj[key].key || obj[key].name;
- // Push to tree
- tree.push(block);
- }
- // Return tree
- return _.sortBy(tree, 'text');
- }
- }
- });
- /**
- * Triggers
- */
- Module.record = function(key, value) {
- /** Variables */
- var proto = Module.record.prototype;
- var isSet = (key && value !== undefined) && 'set';
- var isGet = (key && value === undefined) && 'get';
- /** Create db obect */
- proto.db = proto.db || {};
- /** Actions */
- switch (isSet || isGet) {
- case 'set':
- return (proto.db[key] = value) && value;
- case 'get':
- return proto.db[key] || null;
- default:
- delete proto.db;
- }
- };
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/common/views.js
- /** @namespace AdvisorApp.module('AdCreationApp.Models') */
- AdvisorApp.module('AdCreationApp.Views', function(Module, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- Module.SetSelectorView = Marionette.LayoutView.extend({
- template: _.template('<div class="selector-region"></div>'),
- regions: {
- content: '.selector-region'
- },
- onShow: function() {
- var request = this.getOption('request');
- var loadingView = new App.module('Common.Views').LoadingView();
- // Show a loading spinner while our collection loads
- this.content.show(loadingView);
+ /** @namespace AdvisorApp.module('AdCreationApp.Models') */
+ /* module definition */
+ var Module = {};
- // When sets request is finished, show the collection view
- request.done(function(collection) {
- var view = new Module.SetsView({
- collection: collection,
- placeholder: this.getOption('placeholder')
- });
- this.listenTo(view, 'set:select', this.selectSet);
- this.content.show(view);
- });
- },
- selectSet: function(model) {
- this.trigger('set:select', model);
- this.triggerMethod('modal:close');
- }
- });
- Module.SetView = Marionette.ItemView.extend({
- tagName: 'li',
- className: 'list-group-item',
- template: App.module('templates.adcreation.setsItem'),
- triggers: {
- 'click a': 'set:select'
- }
- });
- Module.SetsView = Marionette.CompositeView.extend({
- template: App.module('templates.adcreation.setsCollection'),
- childViewContainer: '.list-group',
- childView: Module.SetView,
- emptyView: App.module('Common.Views').EmptyCollection,
- childEvents: {
- 'set:select': function(args) {
- // Signal to listening views to load the model
- this.trigger('set:select', args.model);
- // Close the modal
- this.triggerMethod('modal:close');
- }
- },
- templateHelpers: function() {
- return {
+ Module.SetSelectorView = Marionette.LayoutView.extend({
+ template: _.template('<div class="selector-region"></div>'),
+ regions: {
+ content: '.selector-region'
+ },
+ onShow: function() {
+ var request = this.getOption('request');
+ var loadingView = new App.module('Common.Views').LoadingView();
+ // Show a loading spinner while our collection loads
+ this.content.show(loadingView);
+ // When sets request is finished, show the collection view
+ request.done(function(collection) {
+ var view = new Module.SetsView({
+ collection: collection,
placeholder: this.getOption('placeholder')
- };
- }
- });
- Module.SaveSetView = Marionette.ItemView.extend({
- template: App.module('templates.adcreation.setsSave'),
- ui: {
- 'nameInput': 'form input[name=name]'
- },
- events: {
- 'submit form': 'saveSet'
- },
- templateHelpers: function() {
- return {
- button_label: this.getOption('button_label')
- };
- },
- saveSet: function(event) {
- event.preventDefault();
- var initiative = AdvisorApp.module('AdCreationApp').record('initiative');
- // Create a copy of the preset, otherwise our
- // ads would be linked to a set that could change
- var _model = new Backbone.Model($.extend(true, {}, this.model.toJSON()));
- _model.save({
- name: this.ui.nameInput.val(),
- brand_id: initiative.get('brand_id')
- }, {
- url: this.model.url()
});
+ this.listenTo(view, 'set:select', this.selectSet);
+ this.content.show(view);
+ });
+ },
+ selectSet: function(model) {
+ this.trigger('set:select', model);
+ this.triggerMethod('modal:close');
+ }
+ });
+ Module.SetView = Marionette.ItemView.extend({
+ tagName: 'li',
+ className: 'list-group-item',
+ template: App.module('templates.adcreation.setsItem'),
+ triggers: {
+ 'click a': 'set:select'
+ }
+ });
+ Module.SetsView = Marionette.CompositeView.extend({
+ template: App.module('templates.adcreation.setsCollection'),
+ childViewContainer: '.list-group',
+ childView: Module.SetView,
+ emptyView: App.module('Common.Views').EmptyCollection,
+ childEvents: {
+ 'set:select': function(args) {
+ // Signal to listening views to load the model
+ this.trigger('set:select', args.model);
+ // Close the modal
this.triggerMethod('modal:close');
}
- });
+ },
+ templateHelpers: function() {
+ return {
+ placeholder: this.getOption('placeholder')
+ };
+ }
});
+ Module.SaveSetView = Marionette.ItemView.extend({
+ template: App.module('templates.adcreation.setsSave'),
+ ui: {
+ 'nameInput': 'form input[name=name]'
+ },
+ events: {
+ 'submit form': 'saveSet'
+ },
+ templateHelpers: function() {
+ return {
+ button_label: this.getOption('button_label')
+ };
+ },
+ saveSet: function(event) {
+ event.preventDefault();
+ var initiative = AdvisorApp.module('AdCreationApp').record('initiative');
+ // Create a copy of the preset, otherwise our
+ // ads would be linked to a set that could change
+ var _model = new Backbone.Model($.extend(true, {}, this.model.toJSON()));
+ _model.save({
+ name: this.ui.nameInput.val(),
+ brand_id: initiative.get('brand_id')
+ }, {
+ url: this.model.url()
+ });
+ this.triggerMethod('modal:close');
+ }
+ });
+ module.exports = Module;
---------- diff:
modules/adcreation/geography/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Geography', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Geography = require('./controller');
- /** Modules */
- var Geography = App.module('AdCreationApp.Geography');
- /** Get creative view */
- var view = new Geography.View({
- collection: options.collection
- });
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Show view in region */
- (options.region).show(view);
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Geography = AdCreationApp.Geography;
+ /** Get creative view */
+ var view = new Geography.View({
+ collection: options.collection
+ });
+ /** Show view in region */
+ (options.region).show(view);
+ // Destroy this controller when its view is destroyed
+ this.listenTo(view, 'destroy', this.destroy);
+ }
+ });
- // Destroy this controller when its view is destroyed
- this.listenTo(view, 'destroy', this.destroy);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/geography/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Geography', function(Module, App) {
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- Module.ChildView = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.geography'),
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** Template class */
- className: 'child-view',
+ Module.ChildView = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.geography'),
+ /** Template class */
+ className: 'child-view',
+ /** Events */
+ events: {
+ 'click .btn-remove': 'removeModel'
+ },
+ /** @memberof App.module('AdCreationApp.Views').Geography */
+ onShow: function() {
+ /** Modules */
+ var Controller = App.module('AdCreationApp').record('controller');
+ var Account = App.module('AdCreationApp').record('account');
+ /** Add select2 */
+ var model = this.model.get('locations');
+ var options = {
+ data: {
+ account_id: Account,
+ platform: 'FACEBOOK',
+ targeting_type: 'locations'
+ },
+ formatSelection: function(obj) {
+ return obj.name + '<i>' + obj.type + '</i>';
+ }
+ };
+ Controller.multiselect(this.$('input'), model, options);
+ },
+ /** @memberof App.module('AdCreationApp.Views').Geography */
+ removeModel: function() {
+ /** Remove current model */
+ this.model.collection.remove(this.model);
+ }
+ });
+ Module.EmptyView = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.geographyEmpty')
+ });
+ Module.View = Marionette.CollectionView.extend({
+ /** Template class */
+ className: 'collection-view',
+ /** Views */
+ childView: Module.ChildView
+ });
- /** Events */
- events: {
- 'click .btn-remove': 'removeModel'
- },
- /** @memberof App.module('AdCreationApp.Views').Geography */
- onShow: function() {
- /** Modules */
- var Controller = App.module('AdCreationApp').record('controller');
- var Account = App.module('AdCreationApp').record('account');
- /** Add select2 */
- var model = this.model.get('locations');
- var options = {
- data: {
- account_id: Account,
- platform: 'FACEBOOK',
- targeting_type: 'locations'
- },
- formatSelection: function(obj) {
- return obj.name + '<i>' + obj.type + '</i>';
- }
- };
- Controller.multiselect(this.$('input'), model, options);
- },
- /** @memberof App.module('AdCreationApp.Views').Geography */
- removeModel: function() {
- /** Remove current model */
- this.model.collection.remove(this.model);
- }
- });
- Module.EmptyView = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.geographyEmpty')
- });
- Module.View = Marionette.CollectionView.extend({
- /** Template class */
- className: 'collection-view',
- /** Views */
- childView: Module.ChildView
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/demographic/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Demographic', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- this.Controller = Backbone.Marionette.Object.extend({
- /** @memberof AdvisorApp.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Demographic = require('./controller');
- /** Modules */
- var Controller = AdvisorApp.module('AdCreationApp').record('controller');
- var Demographic = AdvisorApp.module('AdCreationApp.Demographic');
- /** Grab initiative so we can use the brand id */
- var initiative = AdvisorApp.module('AdCreationApp').record('initiative');
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Model */
- var model = AdvisorApp.module('AdCreationApp').record('model:adcluster');
- // Collection class for creating a new demo set on first run
- var DemographicSets = AdvisorApp.request('advisor:demographicset:Entities');
- model.set({
- 'demographic_sets': model.get('demographic_sets') || new DemographicSets({
- brand_id: initiative.get('brand_id')
- })
+ this.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof AdvisorApp.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Controller = AdvisorApp.module('AdCreationApp').record('controller');
+ var Demographic = AdCreationApp.Demographic;
+ /** Grab initiative so we can use the brand id */
+ var initiative = AdvisorApp.module('AdCreationApp').record('initiative');
+ /** Model */
+ var model = AdvisorApp.module('AdCreationApp').record('model:adcluster');
+ // Collection class for creating a new demo set on first run
+ var DemographicSets = AdvisorApp.request('advisor:demographicset:Entities');
+ model.set({
+ 'demographic_sets': model.get('demographic_sets') || new DemographicSets({
+ brand_id: initiative.get('brand_id')
+ })
+ });
+ /** Get creative view */
+ var view = new Demographic.View({
+ 'model': model.get('demographic_sets').at(0)
+ });
+ /** create collection */
+ view.model.set({
+ 'age_ranges': view.model.get('age_ranges') || new Backbone.Collection(),
+ 'location_sets': view.model.get('location_sets') || new Backbone.Collection()
+ });
+ /** Add listeners */
+ this.listeners(view);
+ /** Show view */
+ (options.region).show(view);
+ // Destroy this controller when its LayoutView is destroyed
+ this.listenTo(view, 'destroy', this.destroy);
+ AdvisorApp.trigger('adcreation:toolbar:show', { viewName: 'demographic' });
+ /** Analytics */
+ AdvisorApp.trigger('usage:pageevent', 'Ad Creation', 'Add Demographic');
+ },
+ /** @memberof AdvisorApp.module('AdCreationApp.Container').Controller */
+ listeners: function(view) {
+ view.on('age:show', function() {
+ /** Modules */
+ var Age = AdvisorApp.module('AdCreationApp.Age');
+ /** Create age view */
+ var age = new Age.Controller({
+ 'collection': this.model.get('age_ranges'),
+ 'region': this.getRegion('ageRegion')
});
- /** Get creative view */
- var view = new Demographic.View({
- 'model': model.get('demographic_sets').at(0)
+ });
+ view.on('age:add', function() {
+ /** Add model to collection */
+ this.model.get('age_ranges').add({
+ min_age: 13,
+ max_age: 65
});
- /** create collection */
- view.model.set({
- 'age_ranges': view.model.get('age_ranges') || new Backbone.Collection(),
- 'location_sets': view.model.get('location_sets') || new Backbone.Collection()
+ /** Show view */
+ this.trigger('age:show');
+ });
+ view.on('geography:show', function() {
+ /** Modules */
+ var Geography = AdvisorApp.module('AdCreationApp.Geography');
+ /** Modules */
+ var geography = new Geography.Controller({
+ collection: this.model.get('location_sets'),
+ region: this.getRegion('geoRegion')
});
- /** Add listeners */
- this.listeners(view);
- /** Show view */
- (options.region).show(view);
- // Destroy this controller when its LayoutView is destroyed
- this.listenTo(view, 'destroy', this.destroy);
- AdvisorApp.trigger('adcreation:toolbar:show', { viewName: 'demographic' });
- /** Analytics */
- AdvisorApp.trigger('usage:pageevent', 'Ad Creation', 'Add Demographic');
- },
- /** @memberof AdvisorApp.module('AdCreationApp.Container').Controller */
- listeners: function(view) {
- view.on('age:show', function() {
- /** Modules */
- var Age = AdvisorApp.module('AdCreationApp.Age');
- /** Create age view */
- var age = new Age.Controller({
- 'collection': this.model.get('age_ranges'),
- 'region': this.getRegion('ageRegion')
- });
+ });
+ view.on('geography:add', function() {
+ /** Add collection to collection */
+ this.model.get('location_sets').add({
+ 'locations': new Backbone.Collection()
});
- view.on('age:add', function() {
- /** Add model to collection */
- this.model.get('age_ranges').add({
- min_age: 13,
- max_age: 65
- });
- /** Show view */
- this.trigger('age:show');
+ /** Show view */
+ this.trigger('geography:show');
+ });
+ view.on('demoset:save', function(args) {
+ var saveView = new (AdvisorApp.module('AdCreationApp.Views')).SaveSetView({
+ title: 'Save demographic set',
+ model: args.model,
+ button_label: 'Save demographic set'
});
- view.on('geography:show', function() {
- /** Modules */
- var Geography = AdvisorApp.module('AdCreationApp.Geography');
- /** Modules */
- var geography = new Geography.Controller({
- collection: this.model.get('location_sets'),
- region: this.getRegion('geoRegion')
- });
+ AdvisorApp.modalRegion.show(saveView);
+ });
+ view.on('demoset:load', function(args) {
+ var initiative = AdvisorApp.module('AdCreationApp').record('initiative');
+ var demoSets = AdvisorApp.request('advisor:demographicset:entities', {
+ data: {
+ name__exists: true,
+ limit: 0,
+ brand_id: initiative.get('brand_id')
+ }
});
- view.on('geography:add', function() {
- /** Add collection to collection */
- this.model.get('location_sets').add({
- 'locations': new Backbone.Collection()
+ demoSets.done(function(sets) {
+ var setView = new (AdvisorApp.module('AdCreationApp.Views')).SetsView({
+ title: 'Select Demographic Set',
+ placeholder: 'Search Demographic Sets',
+ collection: sets
});
- /** Show view */
- this.trigger('geography:show');
- });
- view.on('demoset:save', function(args) {
- var saveView = new (AdvisorApp.module('AdCreationApp.Views')).SaveSetView({
- title: 'Save demographic set',
- model: args.model,
- button_label: 'Save demographic set'
+ setView.on('set:select', function(model) {
+ view.resetFromModel(model);
});
- AdvisorApp.modalRegion.show(saveView);
+ AdvisorApp.modalRegion.show(setView);
});
+ });
+ }
+ });
- view.on('demoset:load', function(args) {
- var initiative = AdvisorApp.module('AdCreationApp').record('initiative');
- var demoSets = AdvisorApp.request('advisor:demographicset:entities', {
- data: {
- name__exists: true,
- limit: 0,
- brand_id: initiative.get('brand_id')
- }
- });
- demoSets.done(function(sets) {
- var setView = new (AdvisorApp.module('AdCreationApp.Views')).SetsView({
- title: 'Select Demographic Set',
- placeholder: 'Search Demographic Sets',
- collection: sets
- });
- setView.on('set:select', function(model) {
- view.resetFromModel(model);
- });
- AdvisorApp.modalRegion.show(setView);
- });
- });
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/demographic/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Demographic', function(Module, App) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.View = Backbone.Marionette.LayoutView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.demographic'),
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** Template helpers */
- templateHelpers: {
- gender: [{
- value: 'male',
- title: 'Male'
- }, {
- value: 'female',
- title: 'Female',
- seperate: 'or'
- }, {
- value: 'none',
- title: 'No preference'
- }],
- placement: [{
- value: 'desktopfeed',
- title: 'Desktop'
- }, {
- value: 'mobilefeed',
- title: 'Mobile'
- }, {
- value: 'rightcolumn',
- title: 'Right rail'
+ Module.View = Backbone.Marionette.LayoutView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.demographic'),
+ /** Template helpers */
+ templateHelpers: {
+ gender: [{
+ value: 'male',
+ title: 'Male'
+ }, {
+ value: 'female',
+ title: 'Female',
+ seperate: 'or'
+ }, {
+ value: 'none',
+ title: 'No preference'
+ }],
+ placement: [{
+ value: 'desktopfeed',
+ title: 'Desktop'
+ }, {
+ value: 'mobilefeed',
+ title: 'Mobile'
+ }, {
+ value: 'rightcolumn',
+ title: 'Right rail'
+ }]
+ },
+ /** UI */
+ ui: {
+ 'ageRegion': '[data-id=age] .audience-values',
+ 'geoRegion': '[data-id=geography] .audience-values',
+ 'age': '[data-id=age] .btn-add',
+ 'geo': '[data-id=geography] .btn-add',
+ 'gender': '[data-id=gender] button',
+ 'male': '[data-id=gender] button[value=male]',
+ 'female': '[data-id=gender] button[value=female]',
+ 'noPreference': '[data-id=gender] button[value=none]',
+ 'placement': '[data-id=placement] button',
+ 'desktop': '[data-id=placement] button[value=desktopfeed]',
+ 'mobile': '[data-id=placement] button[value=mobilefeed]',
+ 'rightRail': '[data-id=placement] button[value=rightcolumn]',
+ 'loadSet': '[data-action=loadset]',
+ 'saveSet': '[data-action=saveset]'
+ },
+ /** Triggers */
+ triggers: {
+ 'click @ui.age': 'age:add',
+ 'click @ui.geo': 'geography:add',
+ 'click @ui.loadSet': 'demoset:load',
+ 'click @ui.saveSet': 'demoset:save'
+ },
+ /** Events */
+ events: {
+ 'click @ui.gender': 'setGender',
+ 'click @ui.placement': 'setPlacement'
+ },
+ /** Regions */
+ regions: {
+ ageRegion: '@ui.ageRegion',
+ geoRegion: '@ui.geoRegion'
+ },
+ /** Data binding */
+ bindings: {
+ /** Gender */
+ '@ui.male': {
+ attributes: [{
+ value: 'male',
+ observe: 'gender_selection',
+ name: 'class',
+ onGet: 'getState'
}]
},
- /** UI */
- ui: {
- 'ageRegion': '[data-id=age] .audience-values',
- 'geoRegion': '[data-id=geography] .audience-values',
- 'age': '[data-id=age] .btn-add',
- 'geo': '[data-id=geography] .btn-add',
- 'gender': '[data-id=gender] button',
- 'male': '[data-id=gender] button[value=male]',
- 'female': '[data-id=gender] button[value=female]',
- 'noPreference': '[data-id=gender] button[value=none]',
- 'placement': '[data-id=placement] button',
- 'desktop': '[data-id=placement] button[value=desktopfeed]',
- 'mobile': '[data-id=placement] button[value=mobilefeed]',
- 'rightRail': '[data-id=placement] button[value=rightcolumn]',
- 'loadSet': '[data-action=loadset]',
- 'saveSet': '[data-action=saveset]'
+ '@ui.female': {
+ attributes: [{
+ value: 'female',
+ observe: 'gender_selection',
+ name: 'class',
+ onGet: 'getState'
+ }]
},
- /** Triggers */
- triggers: {
- 'click @ui.age': 'age:add',
- 'click @ui.geo': 'geography:add',
- 'click @ui.loadSet': 'demoset:load',
- 'click @ui.saveSet': 'demoset:save'
+ '@ui.noPreference': {
+ attributes: [{
+ value: 'none',
+ observe: 'gender_selection',
+ name: 'class',
+ onGet: 'getState'
+ }]
},
- /** Events */
- events: {
- 'click @ui.gender': 'setGender',
- 'click @ui.placement': 'setPlacement'
+ /** Placement */
+ '@ui.desktop': {
+ attributes: [{
+ value: 'desktopfeed',
+ observe: 'placement',
+ name: 'class',
+ onGet: 'getState'
+ }]
},
- /** Regions */
- regions: {
- ageRegion: '@ui.ageRegion',
- geoRegion: '@ui.geoRegion'
+ '@ui.mobile': {
+ attributes: [{
+ value: 'mobilefeed',
+ observe: 'placement',
+ name: 'class',
+ onGet: 'getState'
+ }]
},
- /** Data binding */
- bindings: {
- /** Gender */
- '@ui.male': {
- attributes: [{
- value: 'male',
- observe: 'gender_selection',
- name: 'class',
- onGet: 'getState'
- }]
- },
- '@ui.female': {
- attributes: [{
- value: 'female',
- observe: 'gender_selection',
- name: 'class',
- onGet: 'getState'
- }]
- },
- '@ui.noPreference': {
- attributes: [{
- value: 'none',
- observe: 'gender_selection',
- name: 'class',
- onGet: 'getState'
- }]
- },
- /** Placement */
- '@ui.desktop': {
- attributes: [{
- value: 'desktopfeed',
- observe: 'placement',
- name: 'class',
- onGet: 'getState'
- }]
- },
- '@ui.mobile': {
- attributes: [{
- value: 'mobilefeed',
- observe: 'placement',
- name: 'class',
- onGet: 'getState'
- }]
- },
- '@ui.rightRail': {
- attributes: [{
- value: 'rightcolumn',
- observe: 'placement',
- name: 'class',
- onGet: 'getState'
- }]
- }
- },
- childEvents: {
- 'demoset:select': 'setDemographics'
- },
- /** @memberof App.module('AdCreationApp.Views').Demographic */
- initialize: function(options) {
- },
- /** @memberof App.module('AdCreationApp.Views').Demographic */
- onShow: function() {
- /** Use UI keys in bindings */
- this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
- /** Trigger age and geography */
- this.trigger('age:show');
- this.trigger('geography:show');
- /** Add stickit */
- this.stickit();
- },
- /** @memberof App.module('AdCreationApp.Views').Demographic */
- getState: function(value, options) {
- /** Toogle active if model contains value */
- return _.contains(value, options.value) ? 'active' : '';
- },
- /** @memberof App.module('AdCreationApp.Views').Demographic */
- getArray: function(name, value) {
- /** Variables */
- var array = this.model.get(name) || [];
- var exclude = _.contains(array, value);
- /** Toogle value in array */
- if (exclude) {
- return _.without(array, value);
- } else {
- return _.union(array, [value]);
- }
- },
- /** @memberof App.module('AdCreationApp.Views').Demographic */
- setGender: function(event) {
- /** Variables */
- var value = event.currentTarget.value;
- var array = this.getArray('gender_selection', value);
- /** Collect genders */
- var gender = {
- 0: (this.templateHelpers).gender[0].value,
- 1: (this.templateHelpers).gender[1].value,
- 2: (this.templateHelpers).gender[2].value
- };
- /** Disabled selection base on value */
- var toogle = (value === gender[2]) ? [gender[0], gender[1]] : gender[2];
- /** Gender selections */
- var selections = _.without.apply(this, [array].concat(toogle));
- /** Check if genders ara selected */
- var isMale = _.contains(selections, gender[0]);
- var isFemale = _.contains(selections, gender[1]);
- /** Set model value */
- this.model.set({
- 'gender_selection': selections
- });
- this.model.set({
- 'gender': isMale && isFemale && 'all' || selections.join()
- });
- },
- /** @memberof App.module('AdCreationApp.Views').Demographic */
- setPlacement: function(event) {
- /** Variables */
- var value = event.currentTarget.value;
- var array = this.getArray('placement', value);
- /** Set model value */
- this.model.set({
- 'placement': array
- });
- },
- resetFromModel: function(model) {
- this.model.fromDemographicSet(model);
- this.trigger('age:show');
- this.trigger('geography:show');
+ '@ui.rightRail': {
+ attributes: [{
+ value: 'rightcolumn',
+ observe: 'placement',
+ name: 'class',
+ onGet: 'getState'
+ }]
}
+ },
+ childEvents: {
+ 'demoset:select': 'setDemographics'
+ },
+ /** @memberof App.module('AdCreationApp.Views').Demographic */
+ initialize: function(options) {
+ },
+ /** @memberof App.module('AdCreationApp.Views').Demographic */
+ onShow: function() {
+ /** Use UI keys in bindings */
+ this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
+ /** Trigger age and geography */
+ this.trigger('age:show');
+ this.trigger('geography:show');
+ /** Add stickit */
+ this.stickit();
+ },
+ /** @memberof App.module('AdCreationApp.Views').Demographic */
+ getState: function(value, options) {
+ /** Toogle active if model contains value */
+ return _.contains(value, options.value) ? 'active' : '';
+ },
+ /** @memberof App.module('AdCreationApp.Views').Demographic */
+ getArray: function(name, value) {
+ /** Variables */
+ var array = this.model.get(name) || [];
+ var exclude = _.contains(array, value);
+ /** Toogle value in array */
+ if (exclude) {
+ return _.without(array, value);
+ } else {
+ return _.union(array, [value]);
+ }
+ },
+ /** @memberof App.module('AdCreationApp.Views').Demographic */
+ setGender: function(event) {
+ /** Variables */
+ var value = event.currentTarget.value;
+ var array = this.getArray('gender_selection', value);
+ /** Collect genders */
+ var gender = {
+ 0: (this.templateHelpers).gender[0].value,
+ 1: (this.templateHelpers).gender[1].value,
+ 2: (this.templateHelpers).gender[2].value
+ };
+ /** Disabled selection base on value */
+ var toogle = (value === gender[2]) ? [gender[0], gender[1]] : gender[2];
+ /** Gender selections */
+ var selections = _.without.apply(this, [array].concat(toogle));
+ /** Check if genders ara selected */
+ var isMale = _.contains(selections, gender[0]);
+ var isFemale = _.contains(selections, gender[1]);
+ /** Set model value */
+ this.model.set({
+ 'gender_selection': selections
+ });
+ this.model.set({
+ 'gender': isMale && isFemale && 'all' || selections.join()
+ });
+ },
+ /** @memberof App.module('AdCreationApp.Views').Demographic */
+ setPlacement: function(event) {
+ /** Variables */
+ var value = event.currentTarget.value;
+ var array = this.getArray('placement', value);
+ /** Set model value */
+ this.model.set({
+ 'placement': array
+ });
+ },
+ resetFromModel: function(model) {
+ this.model.fromDemographicSet(model);
+ this.trigger('age:show');
+ this.trigger('geography:show');
+ }
+ });
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/navigation/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Navigation', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var AdvisorApp = require('../../../app');
- Module.Controller = Backbone.Marionette.Object.extend({
- /** @memberof App.module('AdCreationApp.Container').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Navigation = require('./controller');
- /** Modules */
- var Controller = App.module('AdCreationApp').record('controller');
- var Navigation = App.module('AdCreationApp.Navigation');
- /** Get creative view */
- var view = new Navigation.View({
- model: App.module('AdCreationApp').record('model:generic'),
- campaign: App.module('AdCreationApp').record('campaign')
- });
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Trigger all view events on App */
- view.on('all', function(event) {
- App.trigger(event);
- });
+ Module.Controller = Backbone.Marionette.Object.extend({
+ /** @memberof App.module('AdCreationApp.Container').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var Controller = App.module('AdCreationApp').record('controller');
+ var Navigation = AdCreationApp.Navigation;
+ /** Get creative view */
+ var view = new Navigation.View({
+ model: App.module('AdCreationApp').record('model:generic'),
+ campaign: App.module('AdCreationApp').record('campaign')
+ });
+ /** Trigger all view events on App */
+ view.on('all', function(event) {
+ App.trigger(event);
+ });
+ /** Show view */
+ (options.region).show(view);
+ // Destroy this controller when its view is destroyed
+ this.listenTo(view, 'destroy', this.destroy);
+ }
+ });
- /** Show view */
- (options.region).show(view);
- // Destroy this controller when its view is destroyed
- this.listenTo(view, 'destroy', this.destroy);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/navigation/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Navigation', function(Module, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- // NOTE: The model that back's this view is a plain ol' vanilla Backbone Model lovingly called `generic`.
- // It can be found at `AdvisorApp.module('AdCreationApp').record('model:generic');`
- Module.View = Marionette.ItemView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.navigation'),
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** UI */
- ui: {
- 'sidebar': 'div.sc-sidebar-default',
- 'creativeEdit': '[data-current*=creative] .btn-edit',
- 'creativeContinue': '[data-current*=creative] .btn-continue',
- 'demographicEdit': '[data-current*=demographic] .btn-edit',
- 'demographicContinue': '[data-current*=demographic] .btn-continue',
- 'targetingEdit': '[data-current*=targeting] .btn-edit',
- 'targetingContinue': '[data-current*=targeting] .btn-continue',
- 'budgetEdit': '[data-current*=budget] .btn-edit',
- 'budgetContinue': '[data-current*=budget] .btn-continue'
- },
- /** Triggers */
- triggers: {
- 'click @ui.creativeEdit': 'adcreation:creative:show',
- 'click @ui.creativeContinue': 'adcreation:demographic:show',
- 'click @ui.demographicEdit': 'adcreation:demographic:show',
- 'click @ui.demographicContinue': 'adcreation:targeting:show',
- 'click @ui.targetingEdit': 'adcreation:targeting:show',
- 'click @ui.targetingContinue': 'adcreation:upload:show',
- 'click @ui.budgetEdit': 'adcreation:budget:show',
- 'click @ui.budgetContinue': 'adcreation:review:show'
- },
- /** Data bindings */
- bindings: {
- /** Navigation binding */
- navigation: {
- '[data-scene]': {
- attributes: [{
- observe: 'scene',
- name: 'data-scene'
- }]
- },
- '.message': 'message',
- // Disable creatives continue button when non selected
- 'div[data-current="creative"] .btn-continue': {
- observe: 'creatives',
- update: function($el, val, model, options) {
- $el.toggleClass('disabled', !val);
- }
- },
- '.creatives': {
- observe: 'creatives',
- onGet: 'expectNumber'
- },
- '.gender': {
- observe: 'gender',
- onGet: 'expectNumber'
- },
- '.age': {
- observe: 'age',
- onGet: 'expectNumber'
- },
- '.geography': {
- observe: 'geography',
- onGet: 'expectNumber'
- },
- '.placement': {
- observe: 'placement',
- onGet: 'expectNumber'
- },
- '.targeting-set': {
- observe: 'targeting_set',
- onGet: 'expectNumber'
- },
- '.ad-sets': {
- observe: 'ad_sets',
- onGet: 'expectNumber'
- },
- '.total-ads': {
- observe: 'total_ads',
- onGet: 'expectNumber'
- },
- '.dropdown': {
- observe: 'status'
+ Module.View = Marionette.ItemView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.navigation'),
+ /** UI */
+ ui: {
+ 'sidebar': 'div.sc-sidebar-default',
+ 'creativeEdit': '[data-current*=creative] .btn-edit',
+ 'creativeContinue': '[data-current*=creative] .btn-continue',
+ 'demographicEdit': '[data-current*=demographic] .btn-edit',
+ 'demographicContinue': '[data-current*=demographic] .btn-continue',
+ 'targetingEdit': '[data-current*=targeting] .btn-edit',
+ 'targetingContinue': '[data-current*=targeting] .btn-continue',
+ 'budgetEdit': '[data-current*=budget] .btn-edit',
+ 'budgetContinue': '[data-current*=budget] .btn-continue'
+ },
+ /** Triggers */
+ triggers: {
+ 'click @ui.creativeEdit': 'adcreation:creative:show',
+ 'click @ui.creativeContinue': 'adcreation:demographic:show',
+ 'click @ui.demographicEdit': 'adcreation:demographic:show',
+ 'click @ui.demographicContinue': 'adcreation:targeting:show',
+ 'click @ui.targetingEdit': 'adcreation:targeting:show',
+ 'click @ui.targetingContinue': 'adcreation:upload:show',
+ 'click @ui.budgetEdit': 'adcreation:budget:show',
+ 'click @ui.budgetContinue': 'adcreation:review:show'
+ },
+ /** Data bindings */
+ bindings: {
+ /** Navigation binding */
+ navigation: {
+ '[data-scene]': {
+ attributes: [{
+ observe: 'scene',
+ name: 'data-scene'
+ }]
+ },
+ '.message': 'message',
+ // Disable creatives continue button when non selected
+ 'div[data-current="creative"] .btn-continue': {
+ observe: 'creatives',
+ update: function($el, val, model, options) {
+ $el.toggleClass('disabled', !val);
}
+ },
+ '.creatives': {
+ observe: 'creatives',
+ onGet: 'expectNumber'
+ },
+ '.gender': {
+ observe: 'gender',
+ onGet: 'expectNumber'
+ },
+ '.age': {
+ observe: 'age',
+ onGet: 'expectNumber'
+ },
+ '.geography': {
+ observe: 'geography',
+ onGet: 'expectNumber'
+ },
+ '.placement': {
+ observe: 'placement',
+ onGet: 'expectNumber'
+ },
+ '.targeting-set': {
+ observe: 'targeting_set',
+ onGet: 'expectNumber'
+ },
+ '.ad-sets': {
+ observe: 'ad_sets',
+ onGet: 'expectNumber'
+ },
+ '.total-ads': {
+ observe: 'total_ads',
+ onGet: 'expectNumber'
+ },
+ '.dropdown': {
+ observe: 'status'
}
- },
- /** @memberof App.module('AdCreationApp.Views').Navigation */
- initialize: function(options) {
- this.campaign = options.campaign;
- this.positionSidebar = _.debounce(_.bind(this.positionSidebar, this), 200);
- this.listenTo(AdvisorApp.module('AdCreationApp'), 'bid-and-budget-can-continue', function(bool) {
- this.ui.budgetContinue.toggleClass('disabled', !bool);
+ }
+ },
+ /** @memberof App.module('AdCreationApp.Views').Navigation */
+ initialize: function(options) {
+ this.campaign = options.campaign;
+ this.positionSidebar = _.debounce(_.bind(this.positionSidebar, this), 200);
+ this.listenTo(AdvisorApp.module('AdCreationApp'), 'bid-and-budget-can-continue', function(bool) {
+ this.ui.budgetContinue.toggleClass('disabled', !bool);
+ });
+ // When our window resizes, detect whether sidebar needs to be able to scroll;
+ $(window).on('resize', this.positionSidebar);
+ },
+ positionSidebar: function() {
+ // Remove fixed positioning if our sidebar is taller
+ // than our workable window height
+ var windowHeight = $(window).height() - 60;
+ var sideHeight = this.ui.sidebar.height();
+ if (sideHeight > windowHeight) {
+ this.ui.sidebar.removeClass('sc-sidebar-fixed');
+ } else {
+ this.ui.sidebar.addClass('sc-sidebar-fixed');
+ }
+ },
+ /** @memberof App.module('AdCreationApp.Views').Navigation */
+ onShow: function() {
+ // Modules
+ var controller = App.module('AdCreationApp').record('controller');
+ var navigation = App.module('AdCreationApp').record('model:generic');
+ var adcluster = App.module('AdCreationApp').record('model:adcluster');
+ this.bindEventListeners(adcluster, navigation);
+ // Update sidebar position based on window size
+ this.positionSidebar();
+ },
+ /**
+ * @param {Backbone.Model} adcluster
+ * @param {Backbone.Model} navigation
+ */
+ bindEventListeners: function(adcluster, navigation) {
+ /** Events */
+ adcluster.on('change:ad_creatives', function(model, collection) {
+ navigation.set('creatives', collection.length);
+ });
+ adcluster.on('change:demographic_sets', function(model, collection) {
+ collection.on('change:gender_selection', function(model, collection) {
+ navigation.set('gender', collection.length);
});
- // When our window resizes, detect whether sidebar needs to be able to scroll;
- $(window).on('resize', this.positionSidebar);
- },
- positionSidebar: function() {
- // Remove fixed positioning if our sidebar is taller
- // than our workable window height
- var windowHeight = $(window).height() - 60;
- var sideHeight = this.ui.sidebar.height();
- if (sideHeight > windowHeight) {
- this.ui.sidebar.removeClass('sc-sidebar-fixed');
- } else {
- this.ui.sidebar.addClass('sc-sidebar-fixed');
- }
- },
- /** @memberof App.module('AdCreationApp.Views').Navigation */
- onShow: function() {
- // Modules
- var controller = App.module('AdCreationApp').record('controller');
- var navigation = App.module('AdCreationApp').record('model:generic');
- var adcluster = App.module('AdCreationApp').record('model:adcluster');
- this.bindEventListeners(adcluster, navigation);
- // Update sidebar position based on window size
- this.positionSidebar();
- },
- /**
- * @param {Backbone.Model} adcluster
- * @param {Backbone.Model} navigation
- */
- bindEventListeners: function(adcluster, navigation) {
- /** Events */
- adcluster.on('change:ad_creatives', function(model, collection) {
- navigation.set('creatives', collection.length);
- });
- adcluster.on('change:demographic_sets', function(model, collection) {
- collection.on('change:gender_selection', function(model, collection) {
- navigation.set('gender', collection.length);
+ collection.on('change:age_ranges', function(model, collection) {
+ collection.on('add remove', function(model, collection) {
+ navigation.set('age', collection.length);
});
- collection.on('change:age_ranges', function(model, collection) {
- collection.on('add remove', function(model, collection) {
- navigation.set('age', collection.length);
- });
- });
- collection.on('change:location_sets', function(model, collection) {
- collection.on('add remove', function(model, collection) {
- navigation.set('geography', collection.length);
- });
- });
- collection.on('change:placement', function(model, collection) {
- navigation.set('placement', collection.length);
- });
});
- adcluster.on('change:targeting_sets', function(model, collection) {
+ collection.on('change:location_sets', function(model, collection) {
collection.on('add remove', function(model, collection) {
- navigation.set('targeting_set', collection.length);
+ navigation.set('geography', collection.length);
});
});
+ collection.on('change:placement', function(model, collection) {
+ navigation.set('placement', collection.length);
+ });
+ });
+ adcluster.on('change:targeting_sets', function(model, collection) {
+ collection.on('add remove', function(model, collection) {
+ navigation.set('targeting_set', collection.length);
+ });
+ });
+ /** Add stickit */
+ this.stickit(navigation, this.bindings.navigation);
+ },
+ expectNumber: function(value) {
+ return value || 0;
+ },
+ templateHelpers: function() {
+ return {
+ campaign: this.campaign.toJSON()
+ };
+ },
+ onDestroy: function() {
+ // Destroy our resize listener when the view is swapped out
+ $(window).off('resize', this.positionSidebar);
+ }
+ });
- /** Add stickit */
- this.stickit(navigation, this.bindings.navigation);
- },
- expectNumber: function(value) {
- return value || 0;
- },
- templateHelpers: function() {
- return {
- campaign: this.campaign.toJSON()
- };
- },
- onDestroy: function() {
- // Destroy our resize listener when the view is swapped out
- $(window).off('resize', this.positionSidebar);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/pre-creation/view.js
- AdvisorApp.module('AdCreationApp.PreCreation', function() {
- this.View = Marionette.ItemView.extend({
- template: AdvisorApp.module('templates.adcreation.preAdCreation'),
+ /* global dependencies */
+ var _ = require('underscore');
+ var moment = require('moment');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- ui: {
- 'endDate': '#end_date',
- 'name': '#name',
- 'startDate': '#start_date',
- 'submit': '#submit'
- },
- bindings: {
- '@ui.name': {
- observe: 'name'
- },
- '@ui.endDate': {
- observe: 'endDate',
- attributes: [{
- name: 'data-date-start-date',
- observe: 'startDate',
- onGet: 'updateDatepickers'
- }]
- },
- '@ui.startDate': {
- observe: 'startDate',
- attributes: [{
- name: 'data-date-end-date',
- observe: 'endDate',
- onGet: 'updateDatepickers'
- }]
- }
- },
+ /* module definition */
+ var View = {};
- events: {
- 'click @ui.submit': 'submit'
+ this.View = Marionette.ItemView.extend({
+ template: AdvisorApp.module('templates.adcreation.preAdCreation'),
+ ui: {
+ 'endDate': '#end_date',
+ 'name': '#name',
+ 'startDate': '#start_date',
+ 'submit': '#submit'
+ },
+ bindings: {
+ '@ui.name': {
+ observe: 'name'
},
- modelEvents: {
- 'change:name': 'enableSubmitButton'
+ '@ui.endDate': {
+ observe: 'endDate',
+ attributes: [{
+ name: 'data-date-start-date',
+ observe: 'startDate',
+ onGet: 'updateDatepickers'
+ }]
},
- modalConfigurations: {
- title: 'New ads'
- },
- behaviors: {
- DisplayError: {}
- },
- initialize: function() {
- this.model.set({
- campaignEndDate: this.model.get('endDate'), // This is read only.
- campaignStartDate: this.model.get('startDate') // This is read only.
- });
- },
- updateDatepickers: function() {
- function getDate(string) {
- return moment(string, 'MM/DD/YYYY').toDate();
- }
- var startDate = getDate(this.model.get('startDate'));
- var endDate = getDate(this.model.get('endDate'));
- this.ui.endDate.datepicker('setStartDate', startDate);
- this.ui.startDate.datepicker('setEndDate', endDate);
- },
- // I'm intentionally using jQuery here instead of re-rendering the template.
- // This is to avoid inefficient re-rendering on every single keypress.
- enableSubmitButton: function() {
- this.ui.submit.toggleClass('disabled', _.isEmpty(this.model.get('name')));
- },
- onShow: function() {
- /** This allows us to use UI keys in bindings */
- this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
- this.stickit();
- },
- /** Does two things--closes the modal and tell the app to show the form. */
- submit: function(event) {
- event.preventDefault();
- this.triggerMethod('modal:close');
- AdvisorApp.trigger('adcreation:form:show', this.model.toJSON());
+ '@ui.startDate': {
+ observe: 'startDate',
+ attributes: [{
+ name: 'data-date-end-date',
+ observe: 'endDate',
+ onGet: 'updateDatepickers'
+ }]
}
- });
+ },
+ events: {
+ 'click @ui.submit': 'submit'
+ },
+ modelEvents: {
+ 'change:name': 'enableSubmitButton'
+ },
+ modalConfigurations: {
+ title: 'New ads'
+ },
+ behaviors: {
+ DisplayError: {}
+ },
+ initialize: function() {
+ this.model.set({
+ campaignEndDate: this.model.get('endDate'), // This is read only.
+ campaignStartDate: this.model.get('startDate') // This is read only.
+ });
+ },
+ updateDatepickers: function() {
+ function getDate(string) {
+ return moment(string, 'MM/DD/YYYY').toDate();
+ }
+ var startDate = getDate(this.model.get('startDate'));
+ var endDate = getDate(this.model.get('endDate'));
+ this.ui.endDate.datepicker('setStartDate', startDate);
+ this.ui.startDate.datepicker('setEndDate', endDate);
+ },
+ // I'm intentionally using jQuery here instead of re-rendering the template.
+ // This is to avoid inefficient re-rendering on every single keypress.
+ enableSubmitButton: function() {
+ this.ui.submit.toggleClass('disabled', _.isEmpty(this.model.get('name')));
+ },
+ onShow: function() {
+ /** This allows us to use UI keys in bindings */
+ this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
+ this.stickit();
+ },
+ /** Does two things--closes the modal and tell the app to show the form. */
+ submit: function(event) {
+ event.preventDefault();
+ this.triggerMethod('modal:close');
+ AdvisorApp.trigger('adcreation:form:show', this.model.toJSON());
+ }
});
+ module.exports = View;
---------- diff:
modules/adcreation/review/view.js
- AdvisorApp.module('AdCreationApp.Review', function() {
- this.View = Marionette.ItemView.extend({
- template: AdvisorApp.module('templates.adcreation.review'),
- className: 'sc-adcreation-modal clearfix',
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- ui: {
- 'errorMessage': '.alert-danger',
- 'statusDropdown': 'select',
- 'upload': 'button'
- },
- events: {
- 'click @ui.upload': 'upload'
- },
+ /* module definition */
+ var View = {};
- collectionEvents: {
- 'failure': 'showErrorMessage'
- },
+ this.View = Marionette.ItemView.extend({
+ template: AdvisorApp.module('templates.adcreation.review'),
+ className: 'sc-adcreation-modal clearfix',
+ ui: {
+ 'errorMessage': '.alert-danger',
+ 'statusDropdown': 'select',
+ 'upload': 'button'
+ },
+ events: {
+ 'click @ui.upload': 'upload'
+ },
+ collectionEvents: {
+ 'failure': 'showErrorMessage'
+ },
+ modalConfigurations: {
+ size: 'modal-sm',
+ title: 'Set status and upload'
+ },
+ onShow: function() {
+ /** This allows us to use UI keys in bindings */
+ this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
+ this.ui.statusDropdown.select2(this.getSelect2Options());
+ },
+ upload: function(event) {
+ event.preventDefault();
+ /** Set status on the collection. */
+ var status = this.ui.statusDropdown.val();
+ this.collection.invoke('set', { status: status });
+ /** Trigger the action launch process. */
+ AdvisorApp.trigger('adcreation:launch:show');
+ },
+ /** @returns {Object} */
+ getSelect2Options: function() {
+ return {
+ allowClear: true,
+ minimumResultsForSearch: -1, // Negative value permanently hides the search field.
+ placeholder: 'Select a status'
+ };
+ },
+ /** @returns {object} */
+ templateHelpers: function() {
+ var model = AdvisorApp.module('AdCreationApp').record('model:generic');
+ var totals = AdvisorApp.module('AdCreationApp').getTotals();
+ return {
+ totalAds: totals.ads,
+ totalAdSets: totals.adsets,
+ uri: model.get('adclusterUri')
+ };
+ },
+ /** @param {String} message */
+ showErrorMessage: function(message) {
+ this.ui.errorMessage.removeClass('hidden').text(message);
+ }
+ });
- modalConfigurations: {
- size: 'modal-sm',
- title: 'Set status and upload'
- },
- onShow: function() {
- /** This allows us to use UI keys in bindings */
- this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
- this.ui.statusDropdown.select2(this.getSelect2Options());
- },
- upload: function(event) {
- event.preventDefault();
- /** Set status on the collection. */
- var status = this.ui.statusDropdown.val();
- this.collection.invoke('set', { status: status });
- /** Trigger the action launch process. */
- AdvisorApp.trigger('adcreation:launch:show');
- },
- /** @returns {Object} */
- getSelect2Options: function() {
- return {
- allowClear: true,
- minimumResultsForSearch: -1, // Negative value permanently hides the search field.
- placeholder: 'Select a status'
- };
- },
- /** @returns {object} */
- templateHelpers: function() {
- var model = AdvisorApp.module('AdCreationApp').record('model:generic');
- var totals = AdvisorApp.module('AdCreationApp').getTotals();
- return {
- totalAds: totals.ads,
- totalAdSets: totals.adsets,
- uri: model.get('adclusterUri')
- };
- },
- /** @param {String} message */
- showErrorMessage: function(message) {
- this.ui.errorMessage.removeClass('hidden').text(message);
- }
- });
- });
+ module.exports = View;
---------- diff:
modules/adcreation/targeting/controller.js
- /** @namespace AdvisorApp.module('AdCreationApp').Controller */
- AdvisorApp.module('AdCreationApp.Targeting', function(Module, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- this.Controller = Marionette.Object.extend({
- /** @memberof AdvisorApp.module('AdCreationApp.Targeting').Controller */
- initialize: function(options) {
+ /* local dependencies */
+ var AdCreationApp.Targeting = require('./controller');
- /** Modules */
- var controller = App.module('AdCreationApp').record('controller');
- var AdCluster = App.module('AdCreationApp').record('model:adcluster');
- var Targeting = App.module('AdCreationApp.Targeting');
- /** Get creative view */
- var view = new Targeting.View({
- 'model': AdCluster
- });
+ /** @namespace AdvisorApp.module('AdCreationApp').Controller */
+ /* module definition */
+ var Module = {};
- /** Create targeting set */
- var TargetingSets = App.request('advisor:targetingset:Entities');
- var targetingSets = view.model.get('targeting_sets') || new TargetingSets();
- /** create collection */
- view.model.set({
- 'flexible': false,
- 'targeting_sets': targetingSets
+ this.Controller = Marionette.Object.extend({
+ /** @memberof AdvisorApp.module('AdCreationApp.Targeting').Controller */
+ initialize: function(options) {
+ /** Modules */
+ var controller = App.module('AdCreationApp').record('controller');
+ var AdCluster = App.module('AdCreationApp').record('model:adcluster');
+ var Targeting = AdCreationApp.Targeting;
+ /** Get creative view */
+ var view = new Targeting.View({
+ 'model': AdCluster
+ });
+ /** Create targeting set */
+ var TargetingSets = App.request('advisor:targetingset:Entities');
+ var targetingSets = view.model.get('targeting_sets') || new TargetingSets();
+ /** create collection */
+ view.model.set({
+ 'flexible': false,
+ 'targeting_sets': targetingSets
+ });
+ /** Add listeners */
+ this.addListeners(view, options.initiative);
+ /** Destroy this controller when its LayoutView is destroyed */
+ this.listenTo(view, 'destroy', this.destroy);
+ /** Triggers */
+ App.trigger('adcreation:toolbar:show', { viewName: 'targeting' });
+ App.trigger('usage:pageevent', 'Ad Creation', 'Add Targeting');
+ /** Show view */
+ (options.region).show(view);
+ },
+ /** @memberof AdvisorApp.module('AdCreationApp.Targeting').Controller */
+ addListeners: function(view, initiative) {
+ /** Variables */
+ var Group = App.module('AdCreationApp.Targeting.Group');
+ var targeting = view.model.get('targeting_sets');
+ var region = view.getRegion('targetingRegion');
+ var onShow = function(flexible) {
+ /** Create age view */
+ var group = new Group.Controller({
+ 'collection': targeting,
+ 'region': region,
+ 'initiative': initiative
});
- /** Add listeners */
- this.addListeners(view, options.initiative);
- /** Destroy this controller when its LayoutView is destroyed */
- this.listenTo(view, 'destroy', this.destroy);
- /** Triggers */
- App.trigger('adcreation:toolbar:show', { viewName: 'targeting' });
- App.trigger('usage:pageevent', 'Ad Creation', 'Add Targeting');
- /** Show view */
- (options.region).show(view);
- },
- /** @memberof AdvisorApp.module('AdCreationApp.Targeting').Controller */
- addListeners: function(view, initiative) {
- /** Variables */
- var Group = App.module('AdCreationApp.Targeting.Group');
- var targeting = view.model.get('targeting_sets');
- var region = view.getRegion('targetingRegion');
- var onShow = function(flexible) {
- /** Create age view */
- var group = new Group.Controller({
- 'collection': targeting,
- 'region': region,
- 'initiative': initiative
+ /** Show at least one targeting set */
+ if (!targeting.length) {
+ view.trigger('targeting:add', flexible);
+ }
+ };
+ var onAdd = function(flexible) {
+ /** Create targeting subset */
+ var subset = new Backbone.Collection();
+ /** Add model to collection */
+ var model = targeting.add({
+ 'targeting_subsets': subset,
+ 'flexible': flexible
+ });
+ /** Select this targeting set for focused targeting view */
+ model.select();
+ /** Listener */
+ subset.on('all', function() {
+ /** Get targeting types */
+ var types = function(obj) {
+ return obj.get('targeting_type');
+ };
+ /** Set current selections */
+ model.set({
+ 'selection': subset.map(types)
});
+ });
+ };
+ /** Listeners */
+ view.on('targeting:show', onShow);
+ view.on('targeting:add', onAdd);
+ }
+ });
- /** Show at least one targeting set */
- if (!targeting.length) {
- view.trigger('targeting:add', flexible);
- }
- };
- var onAdd = function(flexible) {
- /** Create targeting subset */
- var subset = new Backbone.Collection();
- /** Add model to collection */
- var model = targeting.add({
- 'targeting_subsets': subset,
- 'flexible': flexible
- });
- /** Select this targeting set for focused targeting view */
- model.select();
- /** Listener */
- subset.on('all', function() {
- /** Get targeting types */
- var types = function(obj) {
- return obj.get('targeting_type');
- };
- /** Set current selections */
- model.set({
- 'selection': subset.map(types)
- });
- });
- };
- /** Listeners */
- view.on('targeting:show', onShow);
- view.on('targeting:add', onAdd);
- }
- });
- });
+ module.exports = Module;
---------- diff:
modules/adcreation/targeting/view.js
- /** @namespace AdvisorApp.module('AdCreationApp.Views') */
- AdvisorApp.module('AdCreationApp.Targeting', function(Module, App) {
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- Module.View = Marionette.LayoutView.extend({
- /** Handlebar template */
- template: App.module('templates.adcreation.targeting'),
+ /** @namespace AdvisorApp.module('AdCreationApp.Views') */
+ /* module definition */
+ var Module = {};
- /** UI */
- ui: {
- 'targetingRegion': '[data-region=targeting]',
- 'add': '.new-targeting',
- 'flexible': '.flexible',
- 'new': '.new-targeting .text'
+ Module.View = Marionette.LayoutView.extend({
+ /** Handlebar template */
+ template: App.module('templates.adcreation.targeting'),
+ /** UI */
+ ui: {
+ 'targetingRegion': '[data-region=targeting]',
+ 'add': '.new-targeting',
+ 'flexible': '.flexible',
+ 'new': '.new-targeting .text'
+ },
+ /** Events */
+ events: {
+ 'click @ui.flexible': 'toggleTargetingType',
+ 'click @ui.add': 'addTargeting'
+ },
+ /** Data binding */
+ bindings: {
+ '@ui.flexible': {
+ observe: 'flexible',
+ onGet: function(value) {
+ return value ? 'Flexible targeting' : 'Default targeting';
+ }
},
- /** Events */
- events: {
- 'click @ui.flexible': 'toggleTargetingType',
- 'click @ui.add': 'addTargeting'
- },
- /** Data binding */
- bindings: {
- '@ui.flexible': {
- observe: 'flexible',
- onGet: function(value) {
- return value ? 'Flexible targeting' : 'Default targeting';
- }
- },
- '@ui.new': {
- observe: 'flexible',
- onGet: function(value) {
- return value ? 'Add attribute set' : 'Add targeting set';
- }
+ '@ui.new': {
+ observe: 'flexible',
+ onGet: function(value) {
+ return value ? 'Add attribute set' : 'Add targeting set';
}
- },
- /** Regions */
- regions: {
- targetingRegion: '@ui.targetingRegion'
- },
- /** @memberof App.module('AdCreationApp.Views').Targeting */
- onShow: function() {
- /** Use UI keys in bindings */
- this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
- /** Add stickit */
- this.stickit();
- /** Trigger targeting */
- this.trigger('targeting:show');
- },
- /** @memberof App.module('AdCreationApp.Views').Targeting */
- toggleTargetingType: function(event) {
- event.stopPropagation();
- /** Get flexible type */
- var flexible = !this.model.get('flexible');
- /** Add flexible attibute to model */
- this.model.set({
- 'flexible': flexible
- });
- /** Add empty attribute set */
- this.trigger('targeting:show', flexible);
- },
- /** @memberof App.module('AdCreationApp.Views').Targeting */
- addTargeting: function(event) {
- event.stopPropagation();
- /** Get flexible type */
- var flexible = this.model.get('flexible');
- /** Add attribute set */
- this.trigger('targeting:add', flexible);
}
+ },
+ /** Regions */
+ regions: {
+ targetingRegion: '@ui.targetingRegion'
+ },
+ /** @memberof App.module('AdCreationApp.Views').Targeting */
+ onShow: function() {
+ /** Use UI keys in bindings */
+ this.bindings = Marionette.normalizeUIKeys(this.bindings, this._uiBindings);
+ /** Add stickit */
+ this.stickit();
+ /** Trigger targeting */
+ this.trigger('targeting:show');
+ },
+ /** @memberof App.module('AdCreationApp.Views').Targeting */
+ toggleTargetingType: function(event) {
+ event.stopPropagation();
+ /** Get flexible type */
+ var flexible = !this.model.get('flexible');
+ /** Add flexible attibute to model */
+ this.model.set({
+ 'flexible': flexible
+ });
+ /** Add empty attribute set */
+ this.trigger('targeting:show', flexible);
+ },
+ /** @memberof App.module('AdCreationApp.Views').Targeting */
+ addTargeting: function(event) {
+ event.stopPropagation();
+ /** Get flexible type */
+ var flexible = this.model.get('flexible');
+ /** Add attribute set */
+ this.trigger('targeting:add', flexible);
+ }
+ });
- });
- });
+ module.exports = Module;
---------- diff:
modules/audience/detail/detail-controller.js
- AdvisorApp.module('AudienceModule.Detail', function(Detail, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var $ = require('jquery');
+ var AdvisorApp = require('../../../app');
- var moduleChannel;
- var audienceCampaigns;
- var detailView;
- var detailColumns = [
- {
- name: 'name',
- label: 'Campaign',
- sortable: false,
- cell: 'string'
- },
- {
- name: 'start_date',
- label: 'Start Date',
- sortable: false,
- headerCell: Backgrid.HeaderCell.extend({
- className: 'number-cell'
- }),
- cell: Backgrid.Extension.SCDateCell
- },
- {
- name: 'end_date',
- label: 'End Date',
- sortable: false,
- headerCell: Backgrid.HeaderCell.extend({
- className: 'number-cell'
- }),
- cell: Backgrid.Extension.SCDateCell
- },
- {
- name: 'budget',
- label: 'Budget',
- sortable: false,
- headerCell: Backgrid.HeaderCell.extend({
- className: 'number-cell'
- }),
- cell: Backgrid.Extension.CurrencyCell.extend({
- className: 'sc-budget-cell'
- })
- }
- ];
- var loadDetailView = function(options) {
- options = options || {};
- var region = options.region;
- if (!region) { return; }
- $.when(App.request('audience:entity', options.audienceID)).done(function(audienceModel) {
- var audienceCampaigns = new (App.request('campaign:PageableEntities'))();
- var collectionFilters = {
- 'audience':options.audienceID
- };
+ /* module definition */
+ var Detail = {};
- detailView = new (moduleChannel.reqres.request('detail:View'))({
- breadCrumbs: [
- { name: 'Audiences', linkUri: '#audience'},
- { name: audienceModel.get('name') }
- ],
- model: audienceModel,
- collection: audienceCampaigns,
- collectionFilters: collectionFilters,
- columns: detailColumns
- });
- detailView.on(detailViewEvents);
- region.show(detailView);
+ var moduleChannel;
+ var audienceCampaigns;
+ var detailView;
+ var detailColumns = [
+ {
+ name: 'name',
+ label: 'Campaign',
+ sortable: false,
+ cell: 'string'
+ },
+ {
+ name: 'start_date',
+ label: 'Start Date',
+ sortable: false,
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'number-cell'
+ }),
+ cell: Backgrid.Extension.SCDateCell
+ },
+ {
+ name: 'end_date',
+ label: 'End Date',
+ sortable: false,
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'number-cell'
+ }),
+ cell: Backgrid.Extension.SCDateCell
+ },
+ {
+ name: 'budget',
+ label: 'Budget',
+ sortable: false,
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'number-cell'
+ }),
+ cell: Backgrid.Extension.CurrencyCell.extend({
+ className: 'sc-budget-cell'
+ })
+ }
+ ];
+ var loadDetailView = function(options) {
+ options = options || {};
+ var region = options.region;
+ if (!region) { return; }
+ $.when(App.request('audience:entity', options.audienceID)).done(function(audienceModel) {
+ var audienceCampaigns = new (App.request('campaign:PageableEntities'))();
+ var collectionFilters = {
+ 'audience':options.audienceID
+ };
+ detailView = new (moduleChannel.reqres.request('detail:View'))({
+ breadCrumbs: [
+ { name: 'Audiences', linkUri: '#audience'},
+ { name: audienceModel.get('name') }
+ ],
+ model: audienceModel,
+ collection: audienceCampaigns,
+ collectionFilters: collectionFilters,
+ columns: detailColumns
});
- };
- var detailViewEvents = {
- 'show': function() {
- var options = this.options;
- var self = this;
- this.campaignTable = new Backbone.SCDataTable.View({
- model: options.model,
- collection: options.collection,
- collectionFilters: options.collectionFilters,
- columns: options.columns
- });
- this.breadcrumbs.show(this.crumbsView);
- this.campaignRegion.show(this.campaignTable);
- this.campaignTable.fetchCollection();
- },
- 'add:user': function() {
- moduleChannel.vent.trigger('add:user');
- }
- };
- App.module('AudienceModule').addInitializer(function() {
- moduleChannel = this.moduleChannel;
- moduleChannel.commands.setHandler('detail:load', loadDetailView);
+ detailView.on(detailViewEvents);
+ region.show(detailView);
});
+ };
+ var detailViewEvents = {
+ 'show': function() {
+ var options = this.options;
+ var self = this;
+ this.campaignTable = new Backbone.SCDataTable.View({
+ model: options.model,
+ collection: options.collection,
+ collectionFilters: options.collectionFilters,
+ columns: options.columns
+ });
+ this.breadcrumbs.show(this.crumbsView);
+ this.campaignRegion.show(this.campaignTable);
+ this.campaignTable.fetchCollection();
+ },
+ 'add:user': function() {
+ moduleChannel.vent.trigger('add:user');
+ }
+ };
+ App.module('AudienceModule').addInitializer(function() {
+ moduleChannel = this.moduleChannel;
+ moduleChannel.commands.setHandler('detail:load', loadDetailView);
+ });
- }, Backgrid);
+ module.exports = Detail;
---------- diff:
modules/audience/detail/detail-view.js
- AdvisorApp.module('AudienceModule.Detail', function(Detail, App) {
+ /* global dependencies */
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- var AudienceModule;
- var moduleChannel;
- var DetailLayout = Marionette.LayoutView.extend({
- template: Handlebars.templates.audienceDetail,
- regions: {
- breadcrumbs: '#breadcrumbs',
- campaignRegion: '#campaign-table',
- targetingRegion: '#targeting-table',
- accountsRegion: '#accounts-table',
- trackingRegion: '#tracking-table',
- usersRegion: '#users-contacts-table'
- },
- ui: {
- newCampaign: '#new-campaign',
- addTarget: '#add-target',
- addAccount: '#add-account',
- addTracking: '#add-tracking',
- addUser: '#add-user'
- },
- triggers: {
- 'click @ui.addUser': 'add:user'
- },
- initialize: function(options) {
- this.options = options;
- this.crumbsView = new App.BrowserApp.module('Common.Views').BreadcrumbsView({
- collection: new Backbone.Collection(this.options.breadCrumbs)
- });
- }
- });
+ /* module definition */
+ var Detail = {};
- App.module('AudienceModule').addInitializer(function() {
- AudienceModule = App.module('AudienceModule');
- moduleChannel = AudienceModule.moduleChannel;
- moduleChannel.reqres.setHandler('detail:View', function() {
- return DetailLayout;
+ var AudienceModule;
+ var moduleChannel;
+ var DetailLayout = Marionette.LayoutView.extend({
+ template: Handlebars.templates.audienceDetail,
+ regions: {
+ breadcrumbs: '#breadcrumbs',
+ campaignRegion: '#campaign-table',
+ targetingRegion: '#targeting-table',
+ accountsRegion: '#accounts-table',
+ trackingRegion: '#tracking-table',
+ usersRegion: '#users-contacts-table'
+ },
+ ui: {
+ newCampaign: '#new-campaign',
+ addTarget: '#add-target',
+ addAccount: '#add-account',
+ addTracking: '#add-tracking',
+ addUser: '#add-user'
+ },
+ triggers: {
+ 'click @ui.addUser': 'add:user'
+ },
+ initialize: function(options) {
+ this.options = options;
+ this.crumbsView = new App.BrowserApp.module('Common.Views').BreadcrumbsView({
+ collection: new Backbone.Collection(this.options.breadCrumbs)
});
+ }
+ });
+ App.module('AudienceModule').addInitializer(function() {
+ AudienceModule = App.module('AudienceModule');
+ moduleChannel = AudienceModule.moduleChannel;
+ moduleChannel.reqres.setHandler('detail:View', function() {
+ return DetailLayout;
});
+ });
- });
+ module.exports = Detail;
---------- diff:
modules/audience/list/list-controller.js
- AdvisorApp.module('AudienceModule.List', function(List, App) {
- var AudienceModule;
- var moduleChannel;
- var audienceModel;
- var audienceEntities;
- var listView;
+ /* global dependencies */
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- var audienceListColumns = [
- {
- name: '',
- label: '',
- cell: Backgrid.Extension.SCSelectRowCell,
- headerCell: 'select-all'
- },
- {
- name: 'name',
- label: 'Name',
- cell: Backgrid.StringCell,
- headerCell: Backgrid.HeaderCell.extend({
- className: 'string-cell'
- })
- },
- {
- name: 'key',
- label: 'Type',
- cell: Backgrid.StringCell,
- headerCell: Backgrid.HeaderCell.extend({
- className: 'string-cell'
- })
- },
- {
- name: 'value',
- label: 'Values',
- cell: Backgrid.Extension.WrappedTextCell,
- headerCell: Backgrid.HeaderCell.extend({
- className: 'string-cell'
- })
- },
- {
- name: 'size',
- label: 'Size (US)',
- cell: Backgrid.Extension.SCAudienceSizeCell,
- headerCell: Backgrid.HeaderCell.extend({
- className: 'audience-size'
- })
- }
- ];
- var loadListView = function(options) {
- options = options || {};
- var region = options.region;
- if (!region) { return; }
- audienceModel = new (App.request('audience:Entity'))();
- audienceModel.set({totalReach:0, reachIds:[]});
- audienceEntities = new (App.request('audience:Entities'))();
+ /* module definition */
+ var List = {};
- _.defaults(options, {
- checkReachEstimate: true,
- model: audienceModel,
- collection: audienceEntities,
- columns: audienceListColumns
+ var AudienceModule;
+ var moduleChannel;
+ var audienceModel;
+ var audienceEntities;
+ var listView;
+ var audienceListColumns = [
+ {
+ name: '',
+ label: '',
+ cell: Backgrid.Extension.SCSelectRowCell,
+ headerCell: 'select-all'
+ },
+ {
+ name: 'name',
+ label: 'Name',
+ cell: Backgrid.StringCell,
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'string-cell'
+ })
+ },
+ {
+ name: 'key',
+ label: 'Type',
+ cell: Backgrid.StringCell,
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'string-cell'
+ })
+ },
+ {
+ name: 'value',
+ label: 'Values',
+ cell: Backgrid.Extension.WrappedTextCell,
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'string-cell'
+ })
+ },
+ {
+ name: 'size',
+ label: 'Size (US)',
+ cell: Backgrid.Extension.SCAudienceSizeCell,
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'audience-size'
+ })
+ }
+ ];
+ var loadListView = function(options) {
+ options = options || {};
+ var region = options.region;
+ if (!region) { return; }
+ audienceModel = new (App.request('audience:Entity'))();
+ audienceModel.set({totalReach:0, reachIds:[]});
+ audienceEntities = new (App.request('audience:Entities'))();
+ _.defaults(options, {
+ checkReachEstimate: true,
+ model: audienceModel,
+ collection: audienceEntities,
+ columns: audienceListColumns
+ });
+ listView = new (moduleChannel.reqres.request('list:View'))(options);
+ listView.on(listViewEvents);
+ region.show(listView);
+ };
+ var listViewEvents = {
+ 'show': function() {
+ var options = this.options;
+ this.tableView = new Backbone.SCDataTable.View({
+ collection: options.collection,
+ collectionFilters: options.collectionFilters,
+ columns: options.columns,
+ model: options.model,
+ checkReachEstimate: options.checkReachEstimate
});
- listView = new (moduleChannel.reqres.request('list:View'))(options);
- listView.on(listViewEvents);
- region.show(listView);
- };
- var listViewEvents = {
- 'show': function() {
- var options = this.options;
- this.tableView = new Backbone.SCDataTable.View({
- collection: options.collection,
- collectionFilters: options.collectionFilters,
- columns: options.columns,
- model: options.model,
- checkReachEstimate: options.checkReachEstimate
- });
- this.tableRegion.show(this.tableView);
- this.createAudienceView = new (moduleChannel.reqres.request('create-audience:View'))();
- this.newAudienceRegion.show(this.createAudienceView);
- var audienceTypes = AdvisorApp.module('Common.Language').en.audienceTypes;
- this.audienceNavBarView = new (moduleChannel.reqres.request('audience-navbar:View'))({
- audienceTypes: audienceTypes
- });
- this.audienceNavBarRegion.show(this.audienceNavBarView);
- $('#audience-brand-filter', this.el).select2({
- width: '250',
- placeholder: 'Filter by brand',
- minimumInputLength: 1,
- allowClear: true,
- ajax: {
- url: '/api/advisor/v1/brand/',
- dataType: 'json',
- quietMillis: 200,
- data: function(term, offset) {
- return {
- limit: 0,
- // team: session.currentTeam.id,
- name__icontains: term
- };
- },
- results: function(data, page) {
- var clients = [];
- _.each(data.objects, function(object) {
- clients.push({id: object.id, name: object.name});
- });
- return { results: clients };
- }
+ this.tableRegion.show(this.tableView);
+ this.createAudienceView = new (moduleChannel.reqres.request('create-audience:View'))();
+ this.newAudienceRegion.show(this.createAudienceView);
+ var audienceTypes = AdvisorApp.module('Common.Language').en.audienceTypes;
+ this.audienceNavBarView = new (moduleChannel.reqres.request('audience-navbar:View'))({
+ audienceTypes: audienceTypes
+ });
+ this.audienceNavBarRegion.show(this.audienceNavBarView);
+ $('#audience-brand-filter', this.el).select2({
+ width: '250',
+ placeholder: 'Filter by brand',
+ minimumInputLength: 1,
+ allowClear: true,
+ ajax: {
+ url: '/api/advisor/v1/brand/',
+ dataType: 'json',
+ quietMillis: 200,
+ data: function(term, offset) {
+ return {
+ limit: 0,
+ // team: session.currentTeam.id,
+ name__icontains: term
+ };
},
- formatResult: function(client) {
- return '<div class="select2-user-result">' + client.name + '</div>';
- },
- formatSelection: function(client, container) {
- return 'Showing only <b>' + client.name + '</b>';
- },
- initSelection: function(element, callback) {
- var data = $(element).val().split(';');
- callback({id: data[0], name: data[1]});
+ results: function(data, page) {
+ var clients = [];
+ _.each(data.objects, function(object) {
+ clients.push({id: object.id, name: object.name});
+ });
+ return { results: clients };
}
- });
+ },
+ formatResult: function(client) {
+ return '<div class="select2-user-result">' + client.name + '</div>';
+ },
+ formatSelection: function(client, container) {
+ return 'Showing only <b>' + client.name + '</b>';
+ },
+ initSelection: function(element, callback) {
+ var data = $(element).val().split(';');
+ callback({id: data[0], name: data[1]});
+ }
+ });
+ (function($) {
+ $.fn.innerstaticHeight = function() {
+ var heightOfOuterfixed = $('#audience-fixed').height();
+ var offset = $('#audience-scrollable-grid').offset();
+ var topOfScrollableGrid = offset.top;
+ // Subtract 56 to account for css making height of tags input 90px (i.e.: adding 56px)
+ var scrollablelHeight = heightOfOuterfixed - topOfScrollableGrid - 56;
+ $('#audience-scrollable-grid').css('height', scrollablelHeight);
+ };
+ })(jQuery);
+ $(document).ready(
+ function() {
+ $('#audience-scrollable-grid').innerstaticHeight();
+ }
+ );
+ $('.audience-size').addClass('descending');
+ this.totalReachView = new (moduleChannel.reqres.request('total-reach:View'))({
+ model: options.model
+ });
+ this.totalReachRegion.show(this.totalReachView);
+ $('#audience-tags').select2({
+ tags: [],
+ tokenSeparators: [','],
+ allowClear: true,
+ initSelection: function(element, callback) {}
+ });
+ //filter by Interests on initial load of data
+ $('#audience-filter-text').html('Filtered by <b>Interests</b> <span class="filter-status-sc"></span><i class="fa fa-caret-down"></i>');
+ _.extend(this.tableView.grid.collection.queryParams, {
+ key: 'INTERESTS'
+ });
+ this.tableView.fetchCollection();
+ },
+ 'create:audience': function() {
+ moduleChannel.vent.trigger('create:show');
+ }
+ };
+ App.module('AudienceModule').addInitializer(function() {
+ moduleChannel = this.moduleChannel;
+ moduleChannel.commands.setHandler('list:load', loadListView);
+ });
- (function($) {
- $.fn.innerstaticHeight = function() {
- var heightOfOuterfixed = $('#audience-fixed').height();
- var offset = $('#audience-scrollable-grid').offset();
- var topOfScrollableGrid = offset.top;
- // Subtract 56 to account for css making height of tags input 90px (i.e.: adding 56px)
- var scrollablelHeight = heightOfOuterfixed - topOfScrollableGrid - 56;
- $('#audience-scrollable-grid').css('height', scrollablelHeight);
- };
- })(jQuery);
- $(document).ready(
- function() {
- $('#audience-scrollable-grid').innerstaticHeight();
- }
- );
- $('.audience-size').addClass('descending');
- this.totalReachView = new (moduleChannel.reqres.request('total-reach:View'))({
- model: options.model
- });
- this.totalReachRegion.show(this.totalReachView);
- $('#audience-tags').select2({
- tags: [],
- tokenSeparators: [','],
- allowClear: true,
- initSelection: function(element, callback) {}
- });
- //filter by Interests on initial load of data
- $('#audience-filter-text').html('Filtered by <b>Interests</b> <span class="filter-status-sc"></span><i class="fa fa-caret-down"></i>');
- _.extend(this.tableView.grid.collection.queryParams, {
- key: 'INTERESTS'
- });
- this.tableView.fetchCollection();
- },
- 'create:audience': function() {
- moduleChannel.vent.trigger('create:show');
- }
- };
- App.module('AudienceModule').addInitializer(function() {
- moduleChannel = this.moduleChannel;
- moduleChannel.commands.setHandler('list:load', loadListView);
- });
- }, Backgrid);
+ module.exports = List;
---------- diff:
modules/audience/list/list-view.js
- AdvisorApp.module('AudienceModule.List', function(List, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- var AudienceModule;
- var moduleChannel;
- var CreateAudienceView = Marionette.ItemView.extend({
- modelEvents: {
+ /* module definition */
+ var List = {};
- },
- events: {
- 'click #new-audience-save': 'createSet',
- 'change #audience-tags': 'removeElement',
- 'keyup #audience-name': 'validateName'
- },
- tagName: 'div',
- className: 'tr-container',
- template: Handlebars.templates.createAudience,
- removeElement: function(e) {
- if (e && e.removed) {
- var removedID = e.removed.id;
- var audienceView = App.mainRegion.currentView;
- var reachIds = audienceView.totalReachView.model.get('reachIds');
- var index = reachIds.indexOf(removedID);
- if (index > -1) {
- reachIds.splice(index, 1);
- }
- _.each(audienceView.tableView.grid.collection.models, function(model) {
- if (model.get('id') === removedID) {
- model.trigger('advisor:audience-removed', model);
- }
- });
- audienceView.toggleButtons(reachIds);
- audienceView.updateReachTotal(reachIds);
+ var AudienceModule;
+ var moduleChannel;
+ var CreateAudienceView = Marionette.ItemView.extend({
+ modelEvents: {
+ },
+ events: {
+ 'click #new-audience-save': 'createSet',
+ 'change #audience-tags': 'removeElement',
+ 'keyup #audience-name': 'validateName'
+ },
+ tagName: 'div',
+ className: 'tr-container',
+ template: Handlebars.templates.createAudience,
+ removeElement: function(e) {
+ if (e && e.removed) {
+ var removedID = e.removed.id;
+ var audienceView = App.mainRegion.currentView;
+ var reachIds = audienceView.totalReachView.model.get('reachIds');
+ var index = reachIds.indexOf(removedID);
+ if (index > -1) {
+ reachIds.splice(index, 1);
}
- },
- validateName: _.debounce(function() {
- var nameInput = $('#audience-name');
- var nameValue = $('#audience-name').val();
- var nameInvalidIcon = $('.name-invalid');
- $.ajax({
- type: 'GET',
- url:'/api/audiences/v1/element/',
- dataType:'json',
- data: {
- name: nameValue
+ _.each(audienceView.tableView.grid.collection.models, function(model) {
+ if (model.get('id') === removedID) {
+ model.trigger('advisor:audience-removed', model);
}
- }).done(function(data, status, jqXHR) {
- if (data.objects.length > 0) {
- nameInput.addClass('name-error').css('padding-left', '20px');
- nameInput.tooltip({ 'trigger':'hover', 'title': 'Enter a unique Name for this Set'});
- nameInvalidIcon.removeClass('hide');
- } else {
- nameInput.removeClass('name-error').css('padding-left', '');
- nameInput.tooltip('destroy');
- nameInvalidIcon.addClass('hide');
- }
- App.mainRegion.currentView.createAudienceView.validateSaveBtn(nameValue);
});
- }, 200),
- validateSaveBtn: function(name) {
- var saveBtn = $('#new-audience-save');
- var elements = $('#s2id_audience-tags').select2('data');
- if (name !== '' && elements.length >= 2 && !$('#audience-name').hasClass('name-error')) {
- saveBtn.removeClass('disabled');
+ audienceView.toggleButtons(reachIds);
+ audienceView.updateReachTotal(reachIds);
+ }
+ },
+ validateName: _.debounce(function() {
+ var nameInput = $('#audience-name');
+ var nameValue = $('#audience-name').val();
+ var nameInvalidIcon = $('.name-invalid');
+ $.ajax({
+ type: 'GET',
+ url:'/api/audiences/v1/element/',
+ dataType:'json',
+ data: {
+ name: nameValue
+ }
+ }).done(function(data, status, jqXHR) {
+ if (data.objects.length > 0) {
+ nameInput.addClass('name-error').css('padding-left', '20px');
+ nameInput.tooltip({ 'trigger':'hover', 'title': 'Enter a unique Name for this Set'});
+ nameInvalidIcon.removeClass('hide');
} else {
- saveBtn.addClass('disabled');
+ nameInput.removeClass('name-error').css('padding-left', '');
+ nameInput.tooltip('destroy');
+ nameInvalidIcon.addClass('hide');
}
- },
- createSet: function(e) {
- var form = e.target.parentElement.parentElement;
- var name = form[0].value;
- var data = $('#s2id_audience-tags').select2('data');
- var elementIds = [];
- _.each(data, function(element) {
- elementIds.push(element.id);
+ App.mainRegion.currentView.createAudienceView.validateSaveBtn(nameValue);
+ });
+ }, 200),
+ validateSaveBtn: function(name) {
+ var saveBtn = $('#new-audience-save');
+ var elements = $('#s2id_audience-tags').select2('data');
+ if (name !== '' && elements.length >= 2 && !$('#audience-name').hasClass('name-error')) {
+ saveBtn.removeClass('disabled');
+ } else {
+ saveBtn.addClass('disabled');
+ }
+ },
+ createSet: function(e) {
+ var form = e.target.parentElement.parentElement;
+ var name = form[0].value;
+ var data = $('#s2id_audience-tags').select2('data');
+ var elementIds = [];
+ _.each(data, function(element) {
+ elementIds.push(element.id);
+ });
+ // Clear selections, de-select models in backgrid and disable Export button
+ $('#s2id_audience-tags').select2('data', '');
+ App.mainRegion.currentView.tableView.grid.clearSelectedModels();
+ $('#export-btn').addClass('disabled');
+ // Make call to create new Set
+ App.request('audience:create-element:entity', name, elementIds.toString());
+ // Clear out the IDs in the totalReachView model
+ App.mainRegion.currentView.totalReachView.model.set({ 'totalReach':numeral(0).format('0.[000]a'), reachIds:[]});
+ }
+ });
+ var TotalReachView = Marionette.ItemView.extend({
+ modelEvents: {
+ 'change': 'totalChanged'
+ },
+ tagName: 'div',
+ className: 'tr-container',
+ template: Handlebars.templates.totalReach,
+ totalChanged: function() {
+ this.render();
+ }
+ });
+ var AudienceNavBarView = Marionette.ItemView.extend({
+ events: {
+ 'click .audience-type-filter': 'filterAudienceByType',
+ 'change #audienceBrandFilter': 'filterAudienceByBrand'
+ },
+ tagName: 'div',
+ className: 'audience-filters',
+ template: Handlebars.templates.audienceNavBar,
+ onShow: function() {
+ this.$el.html(this.template({
+ audienceTypes: this.options.audienceTypes
+ }));
+ },
+ filterAudienceByType: function(e) {
+ var filter = $(e.target).data('filter');
+ var label = e.currentTarget.text;
+ var filterText = '';
+ var elementsView = App.mainRegion.currentView.tableView;
+ var queryParams = elementsView.grid.collection.queryParams;
+ if (filter === 'clear') {
+ filterText = 'Filter by type <span class="filter-status-sc"></span><i class="fa fa-caret-down"></i>';
+ delete queryParams.key;
+ } else {
+ filterText = 'Filtered by <b>' + label + '</b> <span class="filter-status-sc"></span><i class="fa fa-caret-down"></i>';
+ _.extend(queryParams, {
+ key: filter
});
- // Clear selections, de-select models in backgrid and disable Export button
- $('#s2id_audience-tags').select2('data', '');
- App.mainRegion.currentView.tableView.grid.clearSelectedModels();
- $('#export-btn').addClass('disabled');
- // Make call to create new Set
- App.request('audience:create-element:entity', name, elementIds.toString());
- // Clear out the IDs in the totalReachView model
- App.mainRegion.currentView.totalReachView.model.set({ 'totalReach':numeral(0).format('0.[000]a'), reachIds:[]});
}
- });
- var TotalReachView = Marionette.ItemView.extend({
- modelEvents: {
- 'change': 'totalChanged'
- },
- tagName: 'div',
- className: 'tr-container',
- template: Handlebars.templates.totalReach,
- totalChanged: function() {
- this.render();
+ $('#audience-filter-text').html(filterText);
+ elementsView.grid.collection.state.currentPage = 0; //Reset to first page of collection
+ elementsView.fetchCollection();
+ },
+ filterAudienceByBrand: function(e) {
+ var tableView = App.mainRegion.currentView.tableView;
+ if (e.added) {
+ _.extend(tableView.grid.collection.queryParams, { brand_id:e.added.id});
+ } else {
+ tableView.grid.collection.queryParams = _.omit(tableView.grid.collection.queryParams, 'brand_id');
+ tableView.options.collectionFilters = _.omit(tableView.options.collectionFilters, 'brand_id');
}
- });
- var AudienceNavBarView = Marionette.ItemView.extend({
- events: {
- 'click .audience-type-filter': 'filterAudienceByType',
- 'change #audienceBrandFilter': 'filterAudienceByBrand'
- },
- tagName: 'div',
- className: 'audience-filters',
- template: Handlebars.templates.audienceNavBar,
- onShow: function() {
- this.$el.html(this.template({
- audienceTypes: this.options.audienceTypes
- }));
- },
- filterAudienceByType: function(e) {
- var filter = $(e.target).data('filter');
- var label = e.currentTarget.text;
- var filterText = '';
- var elementsView = App.mainRegion.currentView.tableView;
- var queryParams = elementsView.grid.collection.queryParams;
- if (filter === 'clear') {
- filterText = 'Filter by type <span class="filter-status-sc"></span><i class="fa fa-caret-down"></i>';
- delete queryParams.key;
- } else {
- filterText = 'Filtered by <b>' + label + '</b> <span class="filter-status-sc"></span><i class="fa fa-caret-down"></i>';
- _.extend(queryParams, {
- key: filter
- });
- }
- $('#audience-filter-text').html(filterText);
- elementsView.grid.collection.state.currentPage = 0; //Reset to first page of collection
- elementsView.fetchCollection();
- },
- filterAudienceByBrand: function(e) {
- var tableView = App.mainRegion.currentView.tableView;
- if (e.added) {
- _.extend(tableView.grid.collection.queryParams, { brand_id:e.added.id});
- } else {
- tableView.grid.collection.queryParams = _.omit(tableView.grid.collection.queryParams, 'brand_id');
- tableView.options.collectionFilters = _.omit(tableView.options.collectionFilters, 'brand_id');
- }
- tableView.grid.collection.state.currentPage = 0; //Reset to first page of collection
- tableView.fetchCollection();
+ tableView.grid.collection.state.currentPage = 0; //Reset to first page of collection
+ tableView.fetchCollection();
+ }
+ });
+ var ListLayout = Marionette.LayoutView.extend({
+ initialize: function() {
+ var groupsSelected = 0;
+ },
+ tagName: 'div',
+ className: 'audience',
+ template: Handlebars.templates.audienceList,
+ regions: {
+ newAudienceRegion: '#new-audience',
+ totalReachRegion: '#total-reach',
+ audienceNavBarRegion: '#audience-navbar',
+ tableRegion: '#audience-table'
+ },
+ ui: {
+ createAudience: '#create-audience'
+ },
+ triggers: {
+ 'click @ui.createAudience': 'create:audience'
+ },
+ events: {
+ 'click .ic-search-btn': 'search',
+ 'click #export-btn': 'showExport'
+ },
+ collectionEvents: {
+ 'advisor:audience-selected': 'addElement',
+ 'scdatatable:render': 'setSelectedIcon'
+ },
+ showExport: function(e) {
+ var data = $('#s2id_audience-tags').select2('data');
+ var exportNames = '';
+ _.each(data, function(element) {
+ exportNames += element.name + '<br />';
+ });
+ $('#audience-export-modal').modal('show');
+ $('#audience-export-modal-label').text('Audience export');
+ $('#audience-export-content').append(exportNames);
+ //Add handler to clear data when modal is closed
+ $('#audience-export-modal').on('hidden.bs.modal', function() {
+ $('#audience-export-content').empty();
+ });
+ },
+ search: _.debounce(function() {
+ _.extend(this.tableView.grid.collection.queryParams, {
+ q: $('#audiences').val()
+ });
+ this.tableView.collection.state.currentPage = 0; //Reset to first page of collection
+ this.tableView.fetchCollection();
+ }),
+ addElement: function(model, checked) {
+ var audienceId = model.get('id');
+ var reachIds = App.mainRegion.currentView.totalReachView.model.get('reachIds');
+ var data = $('#s2id_audience-tags').select2('data');
+ if (!_.contains(reachIds, audienceId)) {
+ reachIds.push(audienceId);
+ data.push({ id:model.get('id'), name:model.get('name'), text:model.get('key') + '::' + model.get('name')});
+ $('#s2id_audience-tags').select2('data', data);
+ this.toggleButtons(reachIds);
+ this.updateReachTotal(reachIds);
}
- });
- var ListLayout = Marionette.LayoutView.extend({
- initialize: function() {
- var groupsSelected = 0;
- },
- tagName: 'div',
- className: 'audience',
- template: Handlebars.templates.audienceList,
- regions: {
- newAudienceRegion: '#new-audience',
- totalReachRegion: '#total-reach',
- audienceNavBarRegion: '#audience-navbar',
- tableRegion: '#audience-table'
- },
- ui: {
- createAudience: '#create-audience'
- },
- triggers: {
- 'click @ui.createAudience': 'create:audience'
- },
- events: {
- 'click .ic-search-btn': 'search',
- 'click #export-btn': 'showExport'
- },
- collectionEvents: {
- 'advisor:audience-selected': 'addElement',
- 'scdatatable:render': 'setSelectedIcon'
- },
- showExport: function(e) {
- var data = $('#s2id_audience-tags').select2('data');
- var exportNames = '';
- _.each(data, function(element) {
- exportNames += element.name + '<br />';
- });
- $('#audience-export-modal').modal('show');
- $('#audience-export-modal-label').text('Audience export');
- $('#audience-export-content').append(exportNames);
- //Add handler to clear data when modal is closed
- $('#audience-export-modal').on('hidden.bs.modal', function() {
- $('#audience-export-content').empty();
- });
- },
- search: _.debounce(function() {
- _.extend(this.tableView.grid.collection.queryParams, {
- q: $('#audiences').val()
- });
- this.tableView.collection.state.currentPage = 0; //Reset to first page of collection
- this.tableView.fetchCollection();
- }),
- addElement: function(model, checked) {
- var audienceId = model.get('id');
+ },
+ setSelectedIcon: function() {
+ if (AdvisorApp.mainRegion.currentView.totalReachView) {
var reachIds = App.mainRegion.currentView.totalReachView.model.get('reachIds');
- var data = $('#s2id_audience-tags').select2('data');
- if (!_.contains(reachIds, audienceId)) {
- reachIds.push(audienceId);
- data.push({ id:model.get('id'), name:model.get('name'), text:model.get('key') + '::' + model.get('name')});
- $('#s2id_audience-tags').select2('data', data);
- this.toggleButtons(reachIds);
- this.updateReachTotal(reachIds);
- }
- },
- setSelectedIcon: function() {
- if (AdvisorApp.mainRegion.currentView.totalReachView) {
- var reachIds = App.mainRegion.currentView.totalReachView.model.get('reachIds');
- var models = AdvisorApp.mainRegion.currentView.tableView.grid.collection.models;
- _.each(models, function(model) {
- if (_.contains(reachIds, model.get('id'))) {
- model.trigger('advisor:model-selected', model);
- }
- });
- }
- },
- toggleButtons: function(reachIds) {
- var saveBtn = $('#new-audience-save');
- var exportBtn = $('#export-btn');
- var nameValid = false;
- if ($('#audience-name')[0].value !== '' && !$('#audience-name').hasClass('name-error')) nameValid = true;
- if (reachIds.length >= 2) {
- if (nameValid) {
- saveBtn.removeClass('disabled');
- } else {
- saveBtn.addClass('disabled');
+ var models = AdvisorApp.mainRegion.currentView.tableView.grid.collection.models;
+ _.each(models, function(model) {
+ if (_.contains(reachIds, model.get('id'))) {
+ model.trigger('advisor:model-selected', model);
}
- exportBtn.removeClass('disabled');
+ });
+ }
+ },
+ toggleButtons: function(reachIds) {
+ var saveBtn = $('#new-audience-save');
+ var exportBtn = $('#export-btn');
+ var nameValid = false;
+ if ($('#audience-name')[0].value !== '' && !$('#audience-name').hasClass('name-error')) nameValid = true;
+ if (reachIds.length >= 2) {
+ if (nameValid) {
+ saveBtn.removeClass('disabled');
} else {
saveBtn.addClass('disabled');
- exportBtn.addClass('disabled');
}
- },
- updateReachTotal: function(reachIds) {
- $('#total-reach-value').html('<i class="fa fa-spinner fa-spin"></i>');
- var reachView = App.mainRegion.currentView.totalReachView;
- // Update Total Reach display value
- if (reachIds.length === 0) {
- App.mainRegion.currentView.totalReachView.model.set({ 'totalReach':numeral(0).format('0.[000]a'), reachIds:reachIds});
+ exportBtn.removeClass('disabled');
+ } else {
+ saveBtn.addClass('disabled');
+ exportBtn.addClass('disabled');
+ }
+ },
+ updateReachTotal: function(reachIds) {
+ $('#total-reach-value').html('<i class="fa fa-spinner fa-spin"></i>');
+ var reachView = App.mainRegion.currentView.totalReachView;
+ // Update Total Reach display value
+ if (reachIds.length === 0) {
+ App.mainRegion.currentView.totalReachView.model.set({ 'totalReach':numeral(0).format('0.[000]a'), reachIds:reachIds});
+ reachView.render();
+ } else {
+ App.request('audience:reachestimate:entity', reachIds.toString()).done(function(estimateModel) {
+ App.mainRegion.currentView.totalReachView.model.set({
+ 'totalReach': numeral(estimateModel.get('reach')).format('0.[000]a'),
+ reachIds: reachIds
+ });
reachView.render();
- } else {
- App.request('audience:reachestimate:entity', reachIds.toString()).done(function(estimateModel) {
- App.mainRegion.currentView.totalReachView.model.set({
- 'totalReach': numeral(estimateModel.get('reach')).format('0.[000]a'),
- reachIds: reachIds
- });
- reachView.render();
- });
- }
+ });
}
+ }
+ });
+ App.module('AudienceModule').addInitializer(function() {
+ AudienceModule = App.module('AudienceModule');
+ moduleChannel = AudienceModule.moduleChannel;
+ moduleChannel.reqres.setHandler('list:View', function() {
+ return ListLayout;
});
- App.module('AudienceModule').addInitializer(function() {
- AudienceModule = App.module('AudienceModule');
- moduleChannel = AudienceModule.moduleChannel;
- moduleChannel.reqres.setHandler('list:View', function() {
- return ListLayout;
- });
- moduleChannel.reqres.setHandler('create-audience:View', function() {
- return CreateAudienceView;
- });
- moduleChannel.reqres.setHandler('total-reach:View', function() {
- return TotalReachView;
- });
- moduleChannel.reqres.setHandler('audience-navbar:View', function() {
- return AudienceNavBarView;
- });
+ moduleChannel.reqres.setHandler('create-audience:View', function() {
+ return CreateAudienceView;
});
+ moduleChannel.reqres.setHandler('total-reach:View', function() {
+ return TotalReachView;
+ });
+ moduleChannel.reqres.setHandler('audience-navbar:View', function() {
+ return AudienceNavBarView;
+ });
+ });
- });
+ module.exports = List;
---------- diff:
modules/audience/new/new-controller.js
- AdvisorApp.module('AudienceModule.New', function(New, App) {
+ /* global dependencies */
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- var AudienceModule;
- var moduleChannel;
- var currentView;
- var audienceEntity;
- var brandEntity;
- var searchElements = [];
- var selectedTeamId;
- var loadCreateView = function(options) {
- options = options || {};
- var region = options.region;
- if (!region) { return; }
- audienceEntity = new (App.request('audience:Entity'))();
- setTeamId();
- setCurrentView();
- region.show(currentView);
- };
+ /* module definition */
+ var New = {};
- var setCurrentView = function() {
- currentView = new (moduleChannel.reqres.request('create:View'))({
- model: audienceEntity
+ var AudienceModule;
+ var moduleChannel;
+ var currentView;
+ var audienceEntity;
+ var brandEntity;
+ var searchElements = [];
+ var selectedTeamId;
+ var loadCreateView = function(options) {
+ options = options || {};
+ var region = options.region;
+ if (!region) { return; }
+ audienceEntity = new (App.request('audience:Entity'))();
+ setTeamId();
+ setCurrentView();
+ region.show(currentView);
+ };
+ var setCurrentView = function() {
+ currentView = new (moduleChannel.reqres.request('create:View'))({
+ model: audienceEntity
+ });
+ currentView.on(currentViewEvents);
+ };
+ var currentViewEvents = {
+ 'before:destroy': function() {
+ handleCurrentViewClose();
+ },
+ 'show': function() {
+ activateSearchElements();
+ this.stickit(audienceEntity, currentView.bindings);
+ },
+ 'reset:form': function() {
+ currentView.triggerMethod('ClearError');
+ _.each(searchElements, function(el) {
+ el.trigger('reset');
});
- currentView.on(currentViewEvents);
- };
- var currentViewEvents = {
- 'before:destroy': function() {
- handleCurrentViewClose();
+ },
+ 'save': function() {
+ currentView.triggerMethod('ClearError');
+ saveAudience();
+ },
+ 'cancel': function() {
+ moduleChannel.vent.trigger('cards:show');
+ }
+ };
+ var setTeamId = function() {
+ var selectedTeam = App.request('session:team:selected');
+ selectedTeamId = selectedTeam && selectedTeam.id;
+ };
+ var saveAudience = function() {
+ audienceEntity.save(null, {
+ success: function(model) {//model,response
+ moduleChannel.vent.trigger('create:post:success', model);
},
- 'show': function() {
- activateSearchElements();
- this.stickit(audienceEntity, currentView.bindings);
- },
- 'reset:form': function() {
- currentView.triggerMethod('ClearError');
- _.each(searchElements, function(el) {
- el.trigger('reset');
- });
- },
- 'save': function() {
- currentView.triggerMethod('ClearError');
- saveAudience();
- },
- 'cancel': function() {
- moduleChannel.vent.trigger('cards:show');
- }
- };
- var setTeamId = function() {
- var selectedTeam = App.request('session:team:selected');
- selectedTeamId = selectedTeam && selectedTeam.id;
- };
- var saveAudience = function() {
- audienceEntity.save(null, {
- success: function(model) {//model,response
+ error: function(model, response) {
+ if (response.status === 201) {//created
moduleChannel.vent.trigger('create:post:success', model);
- },
- error: function(model, response) {
- if (response.status === 201) {//created
- moduleChannel.vent.trigger('create:post:success', model);
- } else {
- moduleChannel.vent.trigger('create:post:error', response);
- }
+ } else {
+ moduleChannel.vent.trigger('create:post:error', response);
}
+ }
+ });
+ };
+ var switchBrand = function(brand) {
+ brandEntity = brand;
+ audienceEntity.set({
+ 'management_fee': brand && (brand.payout_percent || '')
+ });
+ updateAgencyOptions();
+ };
+ var updateAgencyOptions = function() {
+ resetAgencyLocations();
+ if (!brandEntity) {
+ return;
+ }
+ App.request('agencylocation:entities', {
+ query: {
+ team__id: selectedTeamId,
+ brands: brandEntity.id
+ }
+ })
+ .done(function(agencyLocations) {
+ currentView.addBinding(audienceEntity, currentView.selectors.agency, {
+ observe: 'agency_location_id',
+ selectOptions:{
+ collection: agencyLocations,
+ labelPath: 'label',
+ valuePath: 'id',
+ defaultOption: {label: 'None', value: null}
+ }
});
- };
- var switchBrand = function(brand) {
- brandEntity = brand;
+ //default to first location
audienceEntity.set({
- 'management_fee': brand && (brand.payout_percent || '')
+ 'agency_location_id': (agencyLocations.length > 0 ? agencyLocations.at(0).id : null)
});
- updateAgencyOptions();
- };
- var updateAgencyOptions = function() {
- resetAgencyLocations();
- if (!brandEntity) {
- return;
- }
- App.request('agencylocation:entities', {
- query: {
- team__id: selectedTeamId,
- brands: brandEntity.id
+ currentView.ui.agency.attr('disabled', false);
+ });
+ };
+ var resetAgencyLocations = function() {
+ currentView.unstickit(audienceEntity, currentView.selectors.agency);
+ currentView.ui.agency.off().attr('disabled', true).find('option:not(:first)').remove();
+ audienceEntity.set({
+ 'agency_location_id': null
+ });
+ };
+ var activateSearchElements = function() {
+ searchElements.push(
+ currentView.ui.corporation.searchByName({
+ entityURl: '/api/advisor/v1/corporation/',
+ filter: {
+ team__id: selectedTeamId
}
})
- .done(function(agencyLocations) {
- currentView.addBinding(audienceEntity, currentView.selectors.agency, {
- observe: 'agency_location_id',
- selectOptions:{
- collection: agencyLocations,
- labelPath: 'label',
- valuePath: 'id',
- defaultOption: {label: 'None', value: null}
- }
- });
- //default to first location
- audienceEntity.set({
- 'agency_location_id': (agencyLocations.length > 0 ? agencyLocations.at(0).id : null)
- });
- currentView.ui.agency.attr('disabled', false);
- });
- };
- var resetAgencyLocations = function() {
- currentView.unstickit(audienceEntity, currentView.selectors.agency);
- currentView.ui.agency.off().attr('disabled', true).find('option:not(:first)').remove();
- audienceEntity.set({
- 'agency_location_id': null
- });
- };
- var activateSearchElements = function() {
- searchElements.push(
- currentView.ui.corporation.searchByName({
- entityURl: '/api/advisor/v1/corporation/',
- filter: {
- team__id: selectedTeamId
- }
- })
- );
- searchElements.push(
- currentView.ui.brand.searchByName({
- entityURl: '/api/advisor/v1/brand/',
- filter: {
- corporation__id: function() {
- return currentView.ui.corporation.val();
- },
- team__id: selectedTeamId
+ );
+ searchElements.push(
+ currentView.ui.brand.searchByName({
+ entityURl: '/api/advisor/v1/brand/',
+ filter: {
+ corporation__id: function() {
+ return currentView.ui.corporation.val();
},
- $dependsOn: currentView.ui.corporation,
- callback: function(brand) {
- switchBrand(brand);
- }
- })
- );
- currentView.ui.brand.on('reset', function() {
- switchBrand(null);
- });
- };
- var saveSuccess = function(model) {
- moduleChannel.vent.trigger('detail:show', model.get('id'));
- };
- var saveError = function() { // response
- currentView.triggerMethod('Error');//pass in responseText, when backend changes it to user friendly messages
- };
- var handleCurrentViewClose = function() {
- audienceEntity = null;
- currentView.off(currentViewEvents);
- currentView.unstickit();
- _.each(searchElements, function(el) {
- el.select2('destroy');
- el.off();
- });
- return true;
- };
- App.module('AudienceModule').addInitializer(function() {
- moduleChannel = this.moduleChannel;
- moduleChannel.commands.setHandler('create:load', loadCreateView);
- moduleChannel.vent.on('create:post:success', saveSuccess);
- moduleChannel.vent.on('create:post:error', saveError);
+ team__id: selectedTeamId
+ },
+ $dependsOn: currentView.ui.corporation,
+ callback: function(brand) {
+ switchBrand(brand);
+ }
+ })
+ );
+ currentView.ui.brand.on('reset', function() {
+ switchBrand(null);
});
+ };
+ var saveSuccess = function(model) {
+ moduleChannel.vent.trigger('detail:show', model.get('id'));
+ };
+ var saveError = function() { // response
+ currentView.triggerMethod('Error');//pass in responseText, when backend changes it to user friendly messages
+ };
+ var handleCurrentViewClose = function() {
+ audienceEntity = null;
+ currentView.off(currentViewEvents);
+ currentView.unstickit();
+ _.each(searchElements, function(el) {
+ el.select2('destroy');
+ el.off();
+ });
+ return true;
+ };
+ App.module('AudienceModule').addInitializer(function() {
+ moduleChannel = this.moduleChannel;
+ moduleChannel.commands.setHandler('create:load', loadCreateView);
+ moduleChannel.vent.on('create:post:success', saveSuccess);
+ moduleChannel.vent.on('create:post:error', saveError);
+ });
- });
+ module.exports = New;
---------- diff:
modules/audience/new/new-view.js
- AdvisorApp.module('AudienceModule.New', function(New, App) {
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- var AudienceModule;
- var moduleChannel;
- var View = Marionette.ItemView.extend({
+ /* module definition */
+ var New = {};
- template: Handlebars.templates.audienceForm,
- className: 'audience-form-sc container',
- ui: {
- corporation: '#corporation',
- brand: '#brand_id',
- agency: '#agency_location_id',
- name: '#name',
- cancel: '#cancel',
- feePercent: '#management_fee',
- feeDollar: '#management_fee_dollar',
- totalBudget: '#total_budget',
- mediaBudget: '#media_budget'
+ var AudienceModule;
+ var moduleChannel;
+ var View = Marionette.ItemView.extend({
+ template: Handlebars.templates.audienceForm,
+ className: 'audience-form-sc container',
+ ui: {
+ corporation: '#corporation',
+ brand: '#brand_id',
+ agency: '#agency_location_id',
+ name: '#name',
+ cancel: '#cancel',
+ feePercent: '#management_fee',
+ feeDollar: '#management_fee_dollar',
+ totalBudget: '#total_budget',
+ mediaBudget: '#media_budget'
+ },
+ triggers: {
+ 'click @ui.cancel': 'cancel',
+ 'submit form': 'save'
+ },
+ bindings: {
+ '#media_budget': {
+ observe: ['total_budget', 'management_fee'],
+ onGet: function(values) {
+ var totBudget = values[0] || 0;
+ var mgtFeePct = values[1] || 0;
+ return accounting.formatMoney(totBudget * (100 - mgtFeePct) / 100);
+ }
},
- triggers: {
- 'click @ui.cancel': 'cancel',
- 'submit form': 'save'
+ '#total_budget': {
+ observe: ['total_budget'],
+ onSet: function(value) {
+ return accounting.unformat(value);
+ }
},
- bindings: {
- '#media_budget': {
- observe: ['total_budget', 'management_fee'],
- onGet: function(values) {
- var totBudget = values[0] || 0;
- var mgtFeePct = values[1] || 0;
- return accounting.formatMoney(totBudget * (100 - mgtFeePct) / 100);
- }
- },
- '#total_budget': {
- observe: ['total_budget'],
- onSet: function(value) {
- return accounting.unformat(value);
- }
- },
- '#management_fee': {
- observe: ['management_fee'],
- onSet: function(value) {
- return parseFloat(value);
- }
- },
- '#management_fee_dollar': {
- observe: ['total_budget', 'management_fee'],
- onGet: function(values) {
- var totBudget = values[0] || 0;
- var mgtFeePct = values[1] || 0;
- return accounting.formatMoney(totBudget * mgtFeePct / 100);
- }
- },
- '#brand_id': 'brand_id',
- '#name': 'name',
- '#io': 'io',
- '#start_date': 'start_date',
- '#end_date': 'end_date'
+ '#management_fee': {
+ observe: ['management_fee'],
+ onSet: function(value) {
+ return parseFloat(value);
+ }
},
- selectors: {
- agency: '#agency_location_id'
+ '#management_fee_dollar': {
+ observe: ['total_budget', 'management_fee'],
+ onGet: function(values) {
+ var totBudget = values[0] || 0;
+ var mgtFeePct = values[1] || 0;
+ return accounting.formatMoney(totBudget * mgtFeePct / 100);
+ }
},
- behaviors: {
- DateRangeValidate: {},
- CurrencyValidate: {},
- DisplayError: {}
- }
+ '#brand_id': 'brand_id',
+ '#name': 'name',
+ '#io': 'io',
+ '#start_date': 'start_date',
+ '#end_date': 'end_date'
+ },
+ selectors: {
+ agency: '#agency_location_id'
+ },
+ behaviors: {
+ DateRangeValidate: {},
+ CurrencyValidate: {},
+ DisplayError: {}
+ }
+ });
+ App.module('AudienceModule').addInitializer(function() {
+ AudienceModule = App.module('AudienceModule');
+ moduleChannel = AudienceModule.moduleChannel;
+ moduleChannel.reqres.setHandler('create:View', function() {
+ return View;
});
+ });
- App.module('AudienceModule').addInitializer(function() {
- AudienceModule = App.module('AudienceModule');
- moduleChannel = AudienceModule.moduleChannel;
- moduleChannel.reqres.setHandler('create:View', function() {
- return View;
- });
- });
- });
+ module.exports = New;
---------- diff:
modules/campaign/card-views/activity.js
- AdvisorApp.module('CampaignModule.Views', function(Views) {
- this.Activity = Marionette.ItemView.extend({
- template: Handlebars.templates.ActivityCard,
- events: {
- 'click #activity-label': 'expandCollapseIcon'
- },
- /** @param {$.Event} e from the click event. */
- expandCollapseIcon: function(e) {
- if (e.currentTarget.className === 'collapsed') {
- $('#activity-label i').removeClass('fa-plus').addClass('fa-times');
- } else {
- $('#activity-label i').removeClass('fa-times').addClass('fa-plus');
- }
+ /* global dependencies */
+ var $ = require('jquery');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
+ /* module definition */
+ var Views = {};
+ this.Activity = Marionette.ItemView.extend({
+ template: Handlebars.templates.ActivityCard,
+ events: {
+ 'click #activity-label': 'expandCollapseIcon'
+ },
+ /** @param {$.Event} e from the click event. */
+ expandCollapseIcon: function(e) {
+ if (e.currentTarget.className === 'collapsed') {
+ $('#activity-label i').removeClass('fa-plus').addClass('fa-times');
+ } else {
+ $('#activity-label i').removeClass('fa-times').addClass('fa-plus');
}
- });
+ }
});
+ module.exports = Views;
---------- diff:
modules/campaign/card-views/chart.js
+ /* global dependencies */
+ var moment = require('moment');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
/**
* NOTE! In the CampaignModule this view is nested inside of the Summary view.
*/
- AdvisorApp.module('CampaignModule.Views', function(Views) {
- this.Chart = Marionette.LayoutView.extend({
- template: Handlebars.templates.CampaignChartCard,
+ /* module definition */
+ var Views = {};
- regions: {
- chart: '.highcharts-container'
- },
+ this.Chart = Marionette.LayoutView.extend({
+ template: Handlebars.templates.CampaignChartCard,
+ regions: {
+ chart: '.highcharts-container'
+ },
+ initialize: function() {
+ var platform = _.string.titleize(this.options.platform);
+ this.highchartView = new AdvisorApp[platform].Views.HighchartView(this.getChartOptions());
+ },
+ onShow: function() {
+ this.chart.show(this.highchartView);
+ },
+ /**
+ * @returns {Object} HighchartView options to be used to configure chart.
+ */
+ getChartOptions: function() {
+ var session = AdvisorApp.reqres.getHandler('session:entity')();
+ var showCustomReport = this.options.platform === 'facebook' && session.get('is_staff');
+ return {
+ metric: this.options.platform === 'facebook' ? 'kpi' : 'engagements',
+ collection: this.getChartCollection(),
+ collectionFilters: this.getCollectionFilters(this.options),
+ platform: this.options.platform,
+ showToolbar: true,
+ startDate: moment(this.model.get('start_date')),
+ endDate: moment(this.model.get('end_date')).isAfter(moment()) ? moment().endOf('day') : moment(this.model.get('end_date')).endOf('day'),
+ showCustomReport: showCustomReport,
+ campaign: this.model
+ };
+ },
+ /**
+ * @returns {Backbone.Collection} for Highcharts. Contains an important `seriesData` method for Highcharts.
+ */
+ getChartCollection: function() {
+ var platform = _.string.titleize(this.options.platform);
+ var Collection = AdvisorApp.request('reports:' + this.options.platform + 'analytics:PageableEntities');
+ return new Collection();
+ }
+ });
- initialize: function() {
- var platform = _.string.titleize(this.options.platform);
- this.highchartView = new AdvisorApp[platform].Views.HighchartView(this.getChartOptions());
- },
- onShow: function() {
- this.chart.show(this.highchartView);
- },
- /**
- * @returns {Object} HighchartView options to be used to configure chart.
- */
- getChartOptions: function() {
- var session = AdvisorApp.reqres.getHandler('session:entity')();
- var showCustomReport = this.options.platform === 'facebook' && session.get('is_staff');
- return {
- metric: this.options.platform === 'facebook' ? 'kpi' : 'engagements',
- collection: this.getChartCollection(),
- collectionFilters: this.getCollectionFilters(this.options),
- platform: this.options.platform,
- showToolbar: true,
- startDate: moment(this.model.get('start_date')),
- endDate: moment(this.model.get('end_date')).isAfter(moment()) ? moment().endOf('day') : moment(this.model.get('end_date')).endOf('day'),
- showCustomReport: showCustomReport,
- campaign: this.model
- };
- },
- /**
- * @returns {Backbone.Collection} for Highcharts. Contains an important `seriesData` method for Highcharts.
- */
- getChartCollection: function() {
- var platform = _.string.titleize(this.options.platform);
- var Collection = AdvisorApp.request('reports:' + this.options.platform + 'analytics:PageableEntities');
- return new Collection();
- }
- });
- });
+ module.exports = Views;
---------- diff:
modules/campaign/card-views/children.js
+ /* global dependencies */
+ var moment = require('moment');
+ var _ = require('underscore');
+ var Backbone = require('backbone');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
// NOTE: The columns for this table view are defined in a big object at the bottom.
- AdvisorApp.module('CampaignModule.Views', function(Views) {
- this.Children = Marionette.LayoutView.extend({
- template: Handlebars.templates.ChildrenCard,
+ /* module definition */
+ var Views = {};
- regions: {
- tableRegion: '.children'
- },
- /**
- * This card loads with either an adset or an adgroup initially.
- * It can then be changed out using the nav to show diffrent data.
- */
- initialize: function() {
- if (this.getOption('level') === 'adgroup' && this.getOption('adset') instanceof Backbone.Model) {
- this.model = this.getOption('adset'); // OMG I'm so sorry. This is a hack. Deal with it for now.
+ this.Children = Marionette.LayoutView.extend({
+ template: Handlebars.templates.ChildrenCard,
+ regions: {
+ tableRegion: '.children'
+ },
+ /**
+ * This card loads with either an adset or an adgroup initially.
+ * It can then be changed out using the nav to show diffrent data.
+ */
+ initialize: function() {
+ if (this.getOption('level') === 'adgroup' && this.getOption('adset') instanceof Backbone.Model) {
+ this.model = this.getOption('adset'); // OMG I'm so sorry. This is a hack. Deal with it for now.
+ }
+ this.initializeChildViews();
+ },
+ initializeChildViews: function() {
+ this.childTable = new Backbone.SCDataTable.View(this.getChildTableOptions());
+ this.childTable.fetchCollection();
+ },
+ onShow: function() {
+ this.tableRegion.show(this.childTable);
+ },
+ /**
+ * I hate this method. It's a gross hack for something that should be nicer but alas is not. TODO: Kill when you can.
+ * @returns {String}
+ */
+ getUri: function() {
+ return this.getOption('level') === 'adgroup' ? _.getFragmentWithoutTrailingSlash() + '/' : this.getOption('uri');
+ },
+ /** @returns {Object} of variables we want accessible in our Handlebar Template. */
+ templateHelpers: function() {
+ return {
+ active: this.getNavActiveStates(),
+ hasPermission: this.getPermission(),
+ level: this.getOption('level'),
+ uri: this.getUri()
+ };
+ },
+ getPermission: function() {
+ // TODO: Hard-coded lists are gross. Would be nice to fetch from server at some point.
+ var whiteListOfInitiativeIds = [8786];
+ return _(whiteListOfInitiativeIds).contains(this.model.get('initiative_id'));
+ },
+ /** @returns {Object} */
+ getNavActiveStates: function() {
+ var nav = {
+ adset: false,
+ ads: false,
+ audiences: false
+ };
+ nav[this.getOption('level')] = true;
+ return nav;
+ },
+ /** @returns {Object} of child table configuration options. */
+ getChildTableOptions: function() {
+ var platform = _.string.capitalize(this.getOption('platform'));
+ var level = _.string.capitalize(this.getOption('level'));
+ var getColumnsMethodName = 'get' + platform + level + 'Columns';
+ var setParamsMethodName = 'set' + platform + level + 'ParamsOnCollectionFilter';
+ var childTableOptions = {
+ model: this.model,
+ collection: this.collection,
+ columns: this[getColumnsMethodName](),
+ showPagination: true,
+ collectionFilters: {
+ start_date: moment(this.model.get('start_date')).startOf('day').format('YYYY-MM-DD'),
+ end_date: moment(this.model.get('end_date')).endOf('day').format('YYYY-MM-DD')
}
- this.initializeChildViews();
- },
- initializeChildViews: function() {
- this.childTable = new Backbone.SCDataTable.View(this.getChildTableOptions());
- this.childTable.fetchCollection();
- },
- onShow: function() {
- this.tableRegion.show(this.childTable);
- },
- /**
- * I hate this method. It's a gross hack for something that should be nicer but alas is not. TODO: Kill when you can.
- * @returns {String}
- */
- getUri: function() {
- return this.getOption('level') === 'adgroup' ? _.getFragmentWithoutTrailingSlash() + '/' : this.getOption('uri');
- },
- /** @returns {Object} of variables we want accessible in our Handlebar Template. */
- templateHelpers: function() {
- return {
- active: this.getNavActiveStates(),
- hasPermission: this.getPermission(),
- level: this.getOption('level'),
- uri: this.getUri()
- };
- },
- getPermission: function() {
- // TODO: Hard-coded lists are gross. Would be nice to fetch from server at some point.
- var whiteListOfInitiativeIds = [8786];
- return _(whiteListOfInitiativeIds).contains(this.model.get('initiative_id'));
- },
- /** @returns {Object} */
- getNavActiveStates: function() {
- var nav = {
- adset: false,
- ads: false,
- audiences: false
- };
- nav[this.getOption('level')] = true;
- return nav;
- },
- /** @returns {Object} of child table configuration options. */
- getChildTableOptions: function() {
- var platform = _.string.capitalize(this.getOption('platform'));
- var level = _.string.capitalize(this.getOption('level'));
- var getColumnsMethodName = 'get' + platform + level + 'Columns';
- var setParamsMethodName = 'set' + platform + level + 'ParamsOnCollectionFilter';
- var childTableOptions = {
- model: this.model,
- collection: this.collection,
- columns: this[getColumnsMethodName](),
- showPagination: true,
- collectionFilters: {
- start_date: moment(this.model.get('start_date')).startOf('day').format('YYYY-MM-DD'),
- end_date: moment(this.model.get('end_date')).endOf('day').format('YYYY-MM-DD')
- }
- };
- /** Add Start and End Date to collection.queryParams so paging will work. */
- _.extend(childTableOptions.collection.queryParams, childTableOptions.collectionFilters);
- /** Facebook and Twitter require different properties on the collectionFilter at each level. */
- this[setParamsMethodName](childTableOptions.collection.queryParams);
- return childTableOptions;
- },
- /** @param {Object} queryParams */
- setFacebookAdsetParamsOnCollectionFilter: function(queryParams) {
- queryParams.adcampaign = this.model.get('platform_campaigns')[0].platform_campaign_id;
- },
- /** @param {Object} queryParams */
- setFacebookAdgroupParamsOnCollectionFilter: function(queryParams) {
- queryParams.adset = Number(this.model.get('id'));
- },
- /** @param {Object} queryParams */
- setFacebookAdsParamsOnCollectionFilter: function(queryParams) {
- this.setFacebookAdsetParamsOnCollectionFilter(queryParams);
- },
- /** @param {Object} queryParams */
- setFacebookAudiencesParamsOnCollectionFilter: function(queryParams) {
- // We have to attach the proper campaign ID here manually.
- queryParams.campaign_id = this.model.get('id');
- },
- /** @param {Object} queryParams */
- setTwitterAdsetParamsOnCollectionFilter: function(queryParams) {
- queryParams.funding_instrument__id = this.model.get('twitter_funding_instrument');
- },
- /** @param {Object} queryParams */
- setTwitterAdgroupParamsOnCollectionFilter: function(queryParams) {
- queryParams.campaign = this.model.get('id');
- },
- /** @param {Object} queryParams */
- setTwitterAdsParamsOnCollectionFilter: function(queryParams) {
- this.setTwitterAdgroupParamsOnCollectionFilter(queryParams);
- },
- /** @param {Object} queryParams */
- setTwitterAudiencesParamsOnCollectionFilter: function(queryParams) {
- // We have to attach the proper campaign ID here manually.
- queryParams.campaign_id = this.model.get('id');
- },
- /** @returns {Array.<Object>} */
- filterAndSortColumns: function(columns) {
- var order = this.getColumnOrder();
- return _.chain(columns)
- .filter(function(column) { return _(order).contains(column.name); })
- .sortArrayOfObjects(order, 'name')
- .value();
- },
- /** @returns {Array.<Object>} List of child columns. */
- getFacebookAdsetColumns: function() {
- var columns = this.uniqueColumns.concat([
- {
- name: 'name',
- label: 'Ad set',
- cell: Backgrid.Extension.SCInlineEditCell.extend({
- linkUri: function() {
- return '#' + _.getFragmentWithoutTrailingSlash() + '/adset/' + this.model.id;
- }
- })
- },
- {
- name: 'kpi_primary',
- label: 'Performance',
- cell: Backgrid.Extension.SCObjectiveCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary number-cell' })
- },
- {
- name: 'kpi_primary_events',
- label: 'Results',
- cell: Backgrid.Extension.SCResultsCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
- }
- ]);
- return this.filterAndSortColumns(columns);
- },
- /** @returns {Array.<Object>} List of child columns. */
- getFacebookAdgroupColumns: function() {
- var columns = this.uniqueColumns.concat([
- {
- name: 'name',
- label: 'Ad group',
- cell: 'string'
- },
- {
- name: 'kpi_primary_events',
- label: 'Results',
- cell: Backgrid.Extension.SCResultsCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
- },
- {
- name: 'kpi_primary',
- label: 'Performance',
- cell: Backgrid.Extension.SCObjectiveCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary number-cell' })
- }
- ]);
- return this.filterAndSortColumns(columns);
- },
- /** @returns {Array.<Object>} List of child columns. */
- getFacebookAdsColumns: function() {
- return this.getFacebookAdgroupColumns();
- },
- /** @returns {Array.<Object>} List of child columns. */
- getFacebookAudiencesColumns: function() {
- var columns = this.uniqueColumns.concat([
- {
- name: 'name',
- label: 'Audience',
- cell: 'string'
- },
- {
- name: 'kpi_primary_events',
- label: 'Results',
- cell: Backgrid.Extension.SCResultsCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
- },
- {
- name: 'kpi_primary',
- label: 'Performance',
- cell: Backgrid.Extension.SCObjectiveCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary number-cell' })
- },
- {
- name: 'spend',
- label: 'Spend',
- cell: Backgrid.Extension.CurrencyCell.extend({ className: 'microdollar-cell' }),
- headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
- }
- ]);
- return this.filterAndSortColumns(columns);
- },
- /** @returns {Array.<Object>} List of child columns. */
- getTwitterAdsetColumns: function() {
- var columns = this.uniqueColumns.concat([
- {
- name: 'name',
- label: 'Campaign',
- cell: Backgrid.Extension.SCInlineEditCell.extend({
- linkUri: function() {
- return '#' + _.getFragmentWithoutTrailingSlash() + '/adset/' + this.model.id;
- }
- })
- },
- {
- name: 'kpi_primary_events',
- label: 'Eng',
- cell: 'integer'
- },
- {
- name: 'kpi_primary',
- label: 'CPE',
- cell: Backgrid.Extension.SCMicroObjectiveCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'total_budget number-cell' })
- },
- {
- name: 'spend',
- label: 'Spend',
- cell: Backgrid.Extension.MicrodollarCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
- }
- ]);
- return this.filterAndSortColumns(columns);
- },
- /** @returns {Array.<Object>} List of child columns. */
- getTwitterAdgroupColumns: function() {
- var columns = this.uniqueColumns.concat([
- {
- name: 'name',
- label: 'Ad group',
- cell: 'string'
- },
- {
- name: 'kpi_primary_events',
- label: 'Engagements',
- cell: Backgrid.NumberCell.extend({decimals: 0})
- },
- {
- name: 'kpi_primary',
- label: 'CPE',
- cell: Backgrid.Extension.SCMicroObjectiveCell
- },
- {
- name: 'spend',
- label: 'Spend',
- cell: Backgrid.Extension.MicrodollarCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
- }
- ]);
- return this.filterAndSortColumns(columns);
- },
- /** @returns {Array.<Object>} List of child columns. */
- getTwitterAdsColumns: function() {
- return this.getTwitterAdgroupColumns();
- },
- /** @returns {Array.<Object>} List of child columns. */
- getTwitterAudiencesColumns: function() {
- var columns = this.uniqueColumns.concat([
- {
- name: 'name',
- label: 'Audiences',
- cell: 'string'
- },
- {
- name: 'kpi_primary_events',
- label: 'Eng',
- cell: 'integer'
- },
- {
- name: 'kpi_primary',
- label: 'Performance',
- cell: Backgrid.Extension.SCMicroObjectiveCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'total_budget number-cell' })
- },
- {
- name: 'spend',
- label: 'Spend',
- cell: Backgrid.Extension.MicrodollarCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
- }
- ]);
- return this.filterAndSortColumns(columns);
- },
- /**
- * This method returns the proper order of the columns to show in the children object browser depending on the
- * current platform and what level we are at. If you add or remove a column please adjust here.
- * @returns {Array.<String>}
- */
- getColumnOrder: function() {
- // I'm defining these up here so I Don't Repeat Myself below.
- // Even though ads order is only used twice and the adset order once
- // it seemed like a good practice for future-proofing.
- var facebookAdsetOrder = [
- 'name',
- 'kpi_primary',
- 'kpi_primary_events',
- 'reach',
- 'lifetime_budget',
- 'kpi_primary_spend',
- 'start_time',
- 'end_time',
- 'campaign_status'
- ];
- var facebookAdsOrder = [
- 'name',
- 'kpi_primary_events',
- 'kpi_primary',
- 'reach',
- 'frequency',
- 'kpi_primary_spend',
- 'bid_info'
- ];
- var facebookAudiencesOrder = [
- 'name',
- 'size',
- 'kpi_primary',
- 'kpi_primary_events',
- 'lifetime_budget',
- 'spend',
- 'status'
- ];
- var twitterAdsetOrder = [
- 'name',
- 'start_time',
- 'end_time',
- 'daily_budget_amount_local_micro',
- 'spend',
- 'kpi_primary_events',
- 'kpi_primary',
- 'status'
- ];
- var twitterAdsOrder = [
- 'campaign__name',
- 'placement_type',
- 'goal_settings',
- 'spend',
- 'bid_amount_local_micro',
- 'kpi_primary_events',
- 'kpi_primary',
- 'status'
- ];
- var twitterAudiencesOrder = [
- 'name',
- 'size',
- 'kpi_primary',
- 'kpi_primary_events',
- 'lifetime_budget',
- 'spend',
- 'status'
- ];
- var columnOrder = {
- facebook: {
- adset: facebookAdsetOrder,
- adgroup: facebookAdsOrder,
- ads: facebookAdsOrder,
- audiences: facebookAudiencesOrder
- },
- twitter: {
- adset: twitterAdsetOrder,
- adgroup: twitterAdsOrder,
- ads: twitterAdsOrder,
- audiences: twitterAudiencesOrder
- }
- };
- return columnOrder[this.getOption('platform')][this.getOption('level')];
- },
- /**
- * Every column in here has a unique name property that is used exactly as is and never overridden. Any columns
- * that share the same name attribute but it's other properties are different are defined in each platform and
- * type method.
- *
- * TODO: This could be DRYed up. But would require some real work which I don't have time for at the moment.
- * If you're inspired feel free to simplify this section.
- */
- uniqueColumns: [
+ };
+ /** Add Start and End Date to collection.queryParams so paging will work. */
+ _.extend(childTableOptions.collection.queryParams, childTableOptions.collectionFilters);
+ /** Facebook and Twitter require different properties on the collectionFilter at each level. */
+ this[setParamsMethodName](childTableOptions.collection.queryParams);
+ return childTableOptions;
+ },
+ /** @param {Object} queryParams */
+ setFacebookAdsetParamsOnCollectionFilter: function(queryParams) {
+ queryParams.adcampaign = this.model.get('platform_campaigns')[0].platform_campaign_id;
+ },
+ /** @param {Object} queryParams */
+ setFacebookAdgroupParamsOnCollectionFilter: function(queryParams) {
+ queryParams.adset = Number(this.model.get('id'));
+ },
+ /** @param {Object} queryParams */
+ setFacebookAdsParamsOnCollectionFilter: function(queryParams) {
+ this.setFacebookAdsetParamsOnCollectionFilter(queryParams);
+ },
+ /** @param {Object} queryParams */
+ setFacebookAudiencesParamsOnCollectionFilter: function(queryParams) {
+ // We have to attach the proper campaign ID here manually.
+ queryParams.campaign_id = this.model.get('id');
+ },
+ /** @param {Object} queryParams */
+ setTwitterAdsetParamsOnCollectionFilter: function(queryParams) {
+ queryParams.funding_instrument__id = this.model.get('twitter_funding_instrument');
+ },
+ /** @param {Object} queryParams */
+ setTwitterAdgroupParamsOnCollectionFilter: function(queryParams) {
+ queryParams.campaign = this.model.get('id');
+ },
+ /** @param {Object} queryParams */
+ setTwitterAdsParamsOnCollectionFilter: function(queryParams) {
+ this.setTwitterAdgroupParamsOnCollectionFilter(queryParams);
+ },
+ /** @param {Object} queryParams */
+ setTwitterAudiencesParamsOnCollectionFilter: function(queryParams) {
+ // We have to attach the proper campaign ID here manually.
+ queryParams.campaign_id = this.model.get('id');
+ },
+ /** @returns {Array.<Object>} */
+ filterAndSortColumns: function(columns) {
+ var order = this.getColumnOrder();
+ return _.chain(columns)
+ .filter(function(column) { return _(order).contains(column.name); })
+ .sortArrayOfObjects(order, 'name')
+ .value();
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getFacebookAdsetColumns: function() {
+ var columns = this.uniqueColumns.concat([
{
- name: 'lifetime_budget',
- label: 'Budget',
- cell: Backgrid.Extension.SCBudgetCell.extend({
- isEditable: function() {
- return false;
+ name: 'name',
+ label: 'Ad set',
+ cell: Backgrid.Extension.SCInlineEditCell.extend({
+ linkUri: function() {
+ return '#' + _.getFragmentWithoutTrailingSlash() + '/adset/' + this.model.id;
}
- }),
- headerCell: Backgrid.HeaderCell.extend({
- className: 'lifetime_budget number-cell'
})
},
{
- name: 'start_time',
- label: 'Start',
- cell: Backgrid.Extension.SCDateCell
+ name: 'kpi_primary',
+ label: 'Performance',
+ cell: Backgrid.Extension.SCObjectiveCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary number-cell' })
},
{
- name: 'end_time',
- label: 'End',
- cell: Backgrid.Extension.SCDateCell
+ name: 'kpi_primary_events',
+ label: 'Results',
+ cell: Backgrid.Extension.SCResultsCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
+ }
+ ]);
+ return this.filterAndSortColumns(columns);
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getFacebookAdgroupColumns: function() {
+ var columns = this.uniqueColumns.concat([
+ {
+ name: 'name',
+ label: 'Ad group',
+ cell: 'string'
},
{
- name: 'campaign_status',
- label: 'Status',
- cell: Backgrid.Extension.SCStatusCell
+ name: 'kpi_primary_events',
+ label: 'Results',
+ cell: Backgrid.Extension.SCResultsCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
},
{
- name: 'kpi_primary_spend',
- label: 'Spend',
- cell: Backgrid.Extension.CurrencyCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
+ name: 'kpi_primary',
+ label: 'Performance',
+ cell: Backgrid.Extension.SCObjectiveCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary number-cell' })
+ }
+ ]);
+ return this.filterAndSortColumns(columns);
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getFacebookAdsColumns: function() {
+ return this.getFacebookAdgroupColumns();
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getFacebookAudiencesColumns: function() {
+ var columns = this.uniqueColumns.concat([
+ {
+ name: 'name',
+ label: 'Audience',
+ cell: 'string'
},
{
- name: 'reach',
- label: 'Reach',
- cell: Backgrid.Extension.SCReachCell,
+ name: 'kpi_primary_events',
+ label: 'Results',
+ cell: Backgrid.Extension.SCResultsCell,
headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
},
{
- name: 'status',
- label: 'Status',
- cell: Backgrid.Extension.SCStatusCell
+ name: 'kpi_primary',
+ label: 'Performance',
+ cell: Backgrid.Extension.SCObjectiveCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary number-cell' })
},
{
- name: 'frequency',
- label: 'Frequency',
- cell: Backgrid.Extension.SCFrequencyCell,
+ name: 'spend',
+ label: 'Spend',
+ cell: Backgrid.Extension.CurrencyCell.extend({ className: 'microdollar-cell' }),
headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
+ }
+ ]);
+ return this.filterAndSortColumns(columns);
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getTwitterAdsetColumns: function() {
+ var columns = this.uniqueColumns.concat([
+ {
+ name: 'name',
+ label: 'Campaign',
+ cell: Backgrid.Extension.SCInlineEditCell.extend({
+ linkUri: function() {
+ return '#' + _.getFragmentWithoutTrailingSlash() + '/adset/' + this.model.id;
+ }
+ })
},
{
- name: 'bid_info',
- label: 'Max Bid',
- cell: Backgrid.Extension.SCBidCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'bid_info number-cell' })
+ name: 'kpi_primary_events',
+ label: 'Eng',
+ cell: 'integer'
},
{
- name: 'daily_budget_amount_local_micro',
- label: 'Budget',
+ name: 'kpi_primary',
+ label: 'CPE',
+ cell: Backgrid.Extension.SCMicroObjectiveCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'total_budget number-cell' })
+ },
+ {
+ name: 'spend',
+ label: 'Spend',
cell: Backgrid.Extension.MicrodollarCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'budget' })
+ headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
+ }
+ ]);
+ return this.filterAndSortColumns(columns);
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getTwitterAdgroupColumns: function() {
+ var columns = this.uniqueColumns.concat([
+ {
+ name: 'name',
+ label: 'Ad group',
+ cell: 'string'
},
{
- name: 'bid_amount_local_micro',
- label: 'Bid',
- cell: Backgrid.Extension.MicrodollarCell
+ name: 'kpi_primary_events',
+ label: 'Engagements',
+ cell: Backgrid.NumberCell.extend({decimals: 0})
},
{
- name: 'campaign__name',
- label: 'Ad Group',
- cell: 'string'
+ name: 'kpi_primary',
+ label: 'CPE',
+ cell: Backgrid.Extension.SCMicroObjectiveCell
},
{
- name: 'placement_type',
- label: 'Placement Type',
+ name: 'spend',
+ label: 'Spend',
+ cell: Backgrid.Extension.MicrodollarCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
+ }
+ ]);
+ return this.filterAndSortColumns(columns);
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getTwitterAdsColumns: function() {
+ return this.getTwitterAdgroupColumns();
+ },
+ /** @returns {Array.<Object>} List of child columns. */
+ getTwitterAudiencesColumns: function() {
+ var columns = this.uniqueColumns.concat([
+ {
+ name: 'name',
+ label: 'Audiences',
cell: 'string'
},
{
- name: 'goal_settings',
- label: 'Goal',
- cell: 'string'
+ name: 'kpi_primary_events',
+ label: 'Eng',
+ cell: 'integer'
},
{
- name: 'size',
- label: 'Size',
- cell: Backgrid.Extension.SCReachCell,
- headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
+ name: 'kpi_primary',
+ label: 'Performance',
+ cell: Backgrid.Extension.SCMicroObjectiveCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'total_budget number-cell' })
+ },
+ {
+ name: 'spend',
+ label: 'Spend',
+ cell: Backgrid.Extension.MicrodollarCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
}
- ]
- });
+ ]);
+ return this.filterAndSortColumns(columns);
+ },
+ /**
+ * This method returns the proper order of the columns to show in the children object browser depending on the
+ * current platform and what level we are at. If you add or remove a column please adjust here.
+ * @returns {Array.<String>}
+ */
+ getColumnOrder: function() {
+ // I'm defining these up here so I Don't Repeat Myself below.
+ // Even though ads order is only used twice and the adset order once
+ // it seemed like a good practice for future-proofing.
+ var facebookAdsetOrder = [
+ 'name',
+ 'kpi_primary',
+ 'kpi_primary_events',
+ 'reach',
+ 'lifetime_budget',
+ 'kpi_primary_spend',
+ 'start_time',
+ 'end_time',
+ 'campaign_status'
+ ];
+ var facebookAdsOrder = [
+ 'name',
+ 'kpi_primary_events',
+ 'kpi_primary',
+ 'reach',
+ 'frequency',
+ 'kpi_primary_spend',
+ 'bid_info'
+ ];
+ var facebookAudiencesOrder = [
+ 'name',
+ 'size',
+ 'kpi_primary',
+ 'kpi_primary_events',
+ 'lifetime_budget',
+ 'spend',
+ 'status'
+ ];
+ var twitterAdsetOrder = [
+ 'name',
+ 'start_time',
+ 'end_time',
+ 'daily_budget_amount_local_micro',
+ 'spend',
+ 'kpi_primary_events',
+ 'kpi_primary',
+ 'status'
+ ];
+ var twitterAdsOrder = [
+ 'campaign__name',
+ 'placement_type',
+ 'goal_settings',
+ 'spend',
+ 'bid_amount_local_micro',
+ 'kpi_primary_events',
+ 'kpi_primary',
+ 'status'
+ ];
+ var twitterAudiencesOrder = [
+ 'name',
+ 'size',
+ 'kpi_primary',
+ 'kpi_primary_events',
+ 'lifetime_budget',
+ 'spend',
+ 'status'
+ ];
+ var columnOrder = {
+ facebook: {
+ adset: facebookAdsetOrder,
+ adgroup: facebookAdsOrder,
+ ads: facebookAdsOrder,
+ audiences: facebookAudiencesOrder
+ },
+ twitter: {
+ adset: twitterAdsetOrder,
+ adgroup: twitterAdsOrder,
+ ads: twitterAdsOrder,
+ audiences: twitterAudiencesOrder
+ }
+ };
+ return columnOrder[this.getOption('platform')][this.getOption('level')];
+ },
+ /**
+ * Every column in here has a unique name property that is used exactly as is and never overridden. Any columns
+ * that share the same name attribute but it's other properties are different are defined in each platform and
+ * type method.
+ *
+ * TODO: This could be DRYed up. But would require some real work which I don't have time for at the moment.
+ * If you're inspired feel free to simplify this section.
+ */
+ uniqueColumns: [
+ {
+ name: 'lifetime_budget',
+ label: 'Budget',
+ cell: Backgrid.Extension.SCBudgetCell.extend({
+ isEditable: function() {
+ return false;
+ }
+ }),
+ headerCell: Backgrid.HeaderCell.extend({
+ className: 'lifetime_budget number-cell'
+ })
+ },
+ {
+ name: 'start_time',
+ label: 'Start',
+ cell: Backgrid.Extension.SCDateCell
+ },
+ {
+ name: 'end_time',
+ label: 'End',
+ cell: Backgrid.Extension.SCDateCell
+ },
+ {
+ name: 'campaign_status',
+ label: 'Status',
+ cell: Backgrid.Extension.SCStatusCell
+ },
+ {
+ name: 'kpi_primary_spend',
+ label: 'Spend',
+ cell: Backgrid.Extension.CurrencyCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'kpi_primary_spend number-cell' })
+ },
+ {
+ name: 'reach',
+ label: 'Reach',
+ cell: Backgrid.Extension.SCReachCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
+ },
+ {
+ name: 'status',
+ label: 'Status',
+ cell: Backgrid.Extension.SCStatusCell
+ },
+ {
+ name: 'frequency',
+ label: 'Frequency',
+ cell: Backgrid.Extension.SCFrequencyCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
+ },
+ {
+ name: 'bid_info',
+ label: 'Max Bid',
+ cell: Backgrid.Extension.SCBidCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'bid_info number-cell' })
+ },
+ {
+ name: 'daily_budget_amount_local_micro',
+ label: 'Budget',
+ cell: Backgrid.Extension.MicrodollarCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'budget' })
+ },
+ {
+ name: 'bid_amount_local_micro',
+ label: 'Bid',
+ cell: Backgrid.Extension.MicrodollarCell
+ },
+ {
+ name: 'campaign__name',
+ label: 'Ad Group',
+ cell: 'string'
+ },
+ {
+ name: 'placement_type',
+ label: 'Placement Type',
+ cell: 'string'
+ },
+ {
+ name: 'goal_settings',
+ label: 'Goal',
+ cell: 'string'
+ },
+ {
+ name: 'size',
+ label: 'Size',
+ cell: Backgrid.Extension.SCReachCell,
+ headerCell: Backgrid.HeaderCell.extend({ className: 'number-cell' })
+ }
+ ]
});
+ module.exports = Views;
---------- diff:
modules/campaign/card-views/details.js
- AdvisorApp.module('CampaignModule.Views', function(Views, App) {
- this.CloneCampaignView = Marionette.ItemView.extend({
+ /* global dependencies */
+ var _ = require('underscore');
+ var $ = require('jquery');
+ var moment = require('moment');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- template: Handlebars.templates.CloneCampaignForm,
- title: 'Duplicate Campaign',
+ /* module definition */
+ var Views = {};
- className: 'clone-campaign-form-sc',
- ui: {
- name: '#name',
- cancel: '#cancel'
+ this.CloneCampaignView = Marionette.ItemView.extend({
+ template: Handlebars.templates.CloneCampaignForm,
+ title: 'Duplicate Campaign',
+ className: 'clone-campaign-form-sc',
+ ui: {
+ name: '#name',
+ cancel: '#cancel'
+ },
+ triggers: {
+ 'click @ui.cancel': 'cancel',
+ 'submit form': 'save'
+ }
+ });
+ this.Details = Marionette.ItemView.extend({
+ template: Handlebars.templates.DetailsCard,
+ budgetAdjustments: (function() {
+ return App.module('Common.Language').en.budget_adjustment;
+ }()),
+ ui: {
+ editLink: '#edit-details',
+ cloneLink: '#clone-details',
+ moreDetailsLink: '#more-details',
+ cancelEditLink: '#cancel-edit-details',
+ saveChanges: '#save-changes',
+ name: '#name',
+ budget: '#budget',
+ kpiGoal: '#kpi-goal',
+ startDate: '#start_date',
+ endDate: '#end_date',
+ budgetAdjustment: '#budget_adjustment',
+ budgetAdjustmentSelect: '#budget_adjustment_select',
+ budgetAdjustmentOther: '#budget_adjustment_other',
+ budgetAdjustmentOtherText: '#budget_adjustment_other_text'
+ },
+ events: {
+ 'click @ui.editLink': function(e) {
+ e.preventDefault();
+ this.linkClick(true);
},
- triggers: {
- 'click @ui.cancel': 'cancel',
- 'submit form': 'save'
- }
- });
- this.Details = Marionette.ItemView.extend({
- template: Handlebars.templates.DetailsCard,
- budgetAdjustments: (function() {
- return App.module('Common.Language').en.budget_adjustment;
- }()),
- ui: {
- editLink: '#edit-details',
- cloneLink: '#clone-details',
- moreDetailsLink: '#more-details',
- cancelEditLink: '#cancel-edit-details',
- saveChanges: '#save-changes',
- name: '#name',
- budget: '#budget',
- kpiGoal: '#kpi-goal',
- startDate: '#start_date',
- endDate: '#end_date',
- budgetAdjustment: '#budget_adjustment',
- budgetAdjustmentSelect: '#budget_adjustment_select',
- budgetAdjustmentOther: '#budget_adjustment_other',
- budgetAdjustmentOtherText: '#budget_adjustment_other_text'
+ 'click @ui.moreDetailsLink': function(e) {
+ e.preventDefault();
+ AdvisorApp.module('CampaignModule').moduleChannel.vent.trigger('edit:show', this.model.get('initiative_id'), this.model.get('id'));
},
- events: {
- 'click @ui.editLink': function(e) {
- e.preventDefault();
- this.linkClick(true);
- },
- 'click @ui.moreDetailsLink': function(e) {
- e.preventDefault();
- AdvisorApp.module('CampaignModule').moduleChannel.vent.trigger('edit:show', this.model.get('initiative_id'), this.model.get('id'));
- },
- 'click @ui.cloneLink': function(e) {
- e.preventDefault();
- this.cloneLinkClick();
- },
- 'click @ui.cancelEditLink': function(e) {
- e.preventDefault();
- this.linkClick(false);
- },
- 'click @ui.saveChanges': function(e) {
- e.preventDefault();
- this.saveChanges();
- },
- 'change @ui.budget': 'handleBudgetUpdate',
- 'change @ui.budgetAdjustment': 'handleBudgetAdjustmentUpdate'
+ 'click @ui.cloneLink': function(e) {
+ e.preventDefault();
+ this.cloneLinkClick();
},
- handleBudgetUpdate: function() {
- this.ui.budgetAdjustment.show();
- this.ui.budgetAdjustment.trigger('change');
+ 'click @ui.cancelEditLink': function(e) {
+ e.preventDefault();
+ this.linkClick(false);
},
- handleBudgetAdjustmentUpdate: function() {
- var budgetReason = this.ui.budgetAdjustmentSelect.find('option:selected').text();
- this.ui.budgetAdjustmentOther.toggle(budgetReason === 'Other');
+ 'click @ui.saveChanges': function(e) {
+ e.preventDefault();
+ this.saveChanges();
},
- initialize: function() {
- this.model.set('initiative_media_budget', this.getOption('initiative').get('media_budget'));
- this.model.set('initiative_end_date', moment(this.getOption('initiative').get('end_date')).format('MM/DD/YYYY'));
- },
- onRender: function() {
- if (this.model.get('editing')) {
- this.stickit(this.model, this.bindings);
- this.model.startTracking();
+ 'change @ui.budget': 'handleBudgetUpdate',
+ 'change @ui.budgetAdjustment': 'handleBudgetAdjustmentUpdate'
+ },
+ handleBudgetUpdate: function() {
+ this.ui.budgetAdjustment.show();
+ this.ui.budgetAdjustment.trigger('change');
+ },
+ handleBudgetAdjustmentUpdate: function() {
+ var budgetReason = this.ui.budgetAdjustmentSelect.find('option:selected').text();
+ this.ui.budgetAdjustmentOther.toggle(budgetReason === 'Other');
+ },
+ initialize: function() {
+ this.model.set('initiative_media_budget', this.getOption('initiative').get('media_budget'));
+ this.model.set('initiative_end_date', moment(this.getOption('initiative').get('end_date')).format('MM/DD/YYYY'));
+ },
+ onRender: function() {
+ if (this.model.get('editing')) {
+ this.stickit(this.model, this.bindings);
+ this.model.startTracking();
+ }
+ },
+ templateHelpers:function() {
+ return {
+ followers: this.getOption('initiative').get('followers').toJSON()
+ };
+ },
+ getTemplate: function() {
+ if (this.model.get('editing')) {
+ return Handlebars.templates.DetailsEditCard;
+ } else {
+ return Handlebars.templates.DetailsCard;
+ }
+ },
+ onFollowers: function() {
+ this.render();
+ },
+ linkClick: function(editing) {
+ if (!editing) {
+ this.unstickit();
+ this.model.resetAttributes();
+ }
+ this.model.set('editing', editing);
+ this.render();
+ },
+ cloneLinkClick: function() {
+ var existingCampaignModel = this.model;
+ var myCloneCampaignView = new Views.CloneCampaignView();
+ myCloneCampaignView.on({
+ cancel: function() {
+ this.triggerMethod('modal:close');
+ },
+ save: function() {
+ $.ajax({
+ url: '/api/advisor/v1/campaign/' + existingCampaignModel.get('id') + '/clone/',
+ type: 'post',
+ data: JSON.stringify({
+ name: myCloneCampaignView.ui.name.val()
+ }),
+ contentType: 'application/json',
+ success: function(clonedCampaignId) {
+ App.module('CampaignModule').moduleChannel.vent.trigger('edit:show',
+ existingCampaignModel.get('initiative_id'), parseInt(clonedCampaignId));
+ }
+ // TODO "toast" failures via $.ajaxSetup(), etc.
+ });
+ this.triggerMethod('modal:close');
}
- },
- templateHelpers:function() {
- return {
- followers: this.getOption('initiative').get('followers').toJSON()
- };
- },
- getTemplate: function() {
- if (this.model.get('editing')) {
- return Handlebars.templates.DetailsEditCard;
- } else {
- return Handlebars.templates.DetailsCard;
+ });
+ App.modalRegion.show(myCloneCampaignView);
+ },
+ notOriginalDateValue: function(value, originalValue) {
+ return moment(value).format('MM/DD/YYYY') !== moment(originalValue).format('MM/DD/YYYY');
+ },
+ /**
+ * Handle the rare (?) cases where a user may select a new Start/End Date value, then change it
+ * back to the original value. Since we only want to send the edited values, we'll remove any
+ * value changed and then set back to it's original value. This is automatically handled by
+ * backbone-trackit for the non-date fields, but because the dates are formatted through moment,
+ * trackit cannot tell that a date has been set back to its original value.
+ */
+ checkDates: function(edits) {
+ if (!this.notOriginalDateValue(edits.start_date, this.model._originalAttrs.start_date)) {
+ edits = _(edits).omit('start_date');
+ }
+ if (edits.end_date) {
+ if (edits.end_date === moment(this.model._originalAttrs.end_date).format('YYYY-MM-DD')) {
+ edits = _(edits).omit('end_date');
}
- },
- onFollowers: function() {
- this.render();
- },
- linkClick: function(editing) {
- if (!editing) {
- this.unstickit();
- this.model.resetAttributes();
+ }
+ return edits;
+ },
+ saveChanges: function() {
+ var edits = this.checkDates(this.model.unsavedAttributes());
+ /** If there are no edits then simply revert back to read-only mode */
+ if (_(edits).isEmpty()) {
+ this.linkClick(false);
+ } else {
+ if (edits.hasOwnProperty('budget_adjustment_select')) {
+ var pre_adjustment_budget = this.model._originalAttrs.budget;
+ var amount = edits.budget - pre_adjustment_budget;
+ $.ajax({
+ url: '/api/advisor/v1/budgetadjustment/',
+ type: 'post',
+ data: JSON.stringify({
+ campaign: this.model._originalAttrs.resource_uri,
+ pre_adjustment_budget: pre_adjustment_budget, //Budget before the adjustment
+ amount: amount, // Amount by which budget is being increased or decreased
+ adjustment_type: edits.budget_adjustment_select,
+ notes: edits.budget_adjustment_other_text || ''
+ }),
+ contentType: 'application/json',
+ error: function(model, response) {
+ AdvisorApp.mainRegion.currentView.detailsView.triggerMethod('Error', response.responseJSON.error_message);
+ }
+ });
}
- this.model.set('editing', editing);
- this.render();
- },
- cloneLinkClick: function() {
- var existingCampaignModel = this.model;
- var myCloneCampaignView = new Views.CloneCampaignView();
- myCloneCampaignView.on({
- cancel: function() {
- this.triggerMethod('modal:close');
+ this.model.save(edits, {
+ patch: true,
+ success: function(model, response) {
+ model.unset('budget_adjustment_other_text');
+ model.unset('budget_adjustment_select');
+ var details = AdvisorApp.mainRegion.currentView.detailsView;
+ details.model.set('editing', false);
+ details.render();
},
- save: function() {
- $.ajax({
- url: '/api/advisor/v1/campaign/' + existingCampaignModel.get('id') + '/clone/',
- type: 'post',
- data: JSON.stringify({
- name: myCloneCampaignView.ui.name.val()
- }),
- contentType: 'application/json',
- success: function(clonedCampaignId) {
- App.module('CampaignModule').moduleChannel.vent.trigger('edit:show',
- existingCampaignModel.get('initiative_id'), parseInt(clonedCampaignId));
- }
- // TODO "toast" failures via $.ajaxSetup(), etc.
- });
- this.triggerMethod('modal:close');
+ error: function(model, response) {
+ AdvisorApp.mainRegion.currentView.detailsView.triggerMethod('Error', response.responseJSON.error_message);
}
});
- App.modalRegion.show(myCloneCampaignView);
+ }
+ },
+ bindings: {
+ '#name': {
+ observe: 'name'
},
- notOriginalDateValue: function(value, originalValue) {
- return moment(value).format('MM/DD/YYYY') !== moment(originalValue).format('MM/DD/YYYY');
- },
- /**
- * Handle the rare (?) cases where a user may select a new Start/End Date value, then change it
- * back to the original value. Since we only want to send the edited values, we'll remove any
- * value changed and then set back to it's original value. This is automatically handled by
- * backbone-trackit for the non-date fields, but because the dates are formatted through moment,
- * trackit cannot tell that a date has been set back to its original value.
- */
- checkDates: function(edits) {
- if (!this.notOriginalDateValue(edits.start_date, this.model._originalAttrs.start_date)) {
- edits = _(edits).omit('start_date');
- }
- if (edits.end_date) {
- if (edits.end_date === moment(this.model._originalAttrs.end_date).format('YYYY-MM-DD')) {
- edits = _(edits).omit('end_date');
+ '#budget': {
+ observe: 'budget',
+ onSet: function(value, options) {
+ if (value < this.model.get('spend_lifetime')) {
+ Helpers.invalidInput(this.ui.budget, this.ui.saveChanges, 'Budget cannot be less than Total Spend');
+ } else {
+ Helpers.validInput(this.ui.budget, this.ui.saveChanges);
}
+ return accounting.unformat(value);
}
- return edits;
},
- saveChanges: function() {
- var edits = this.checkDates(this.model.unsavedAttributes());
- /** If there are no edits then simply revert back to read-only mode */
- if (_(edits).isEmpty()) {
- this.linkClick(false);
- } else {
- if (edits.hasOwnProperty('budget_adjustment_select')) {
- var pre_adjustment_budget = this.model._originalAttrs.budget;
- var amount = edits.budget - pre_adjustment_budget;
- $.ajax({
- url: '/api/advisor/v1/budgetadjustment/',
- type: 'post',
- data: JSON.stringify({
- campaign: this.model._originalAttrs.resource_uri,
- pre_adjustment_budget: pre_adjustment_budget, //Budget before the adjustment
- amount: amount, // Amount by which budget is being increased or decreased
- adjustment_type: edits.budget_adjustment_select,
- notes: edits.budget_adjustment_other_text || ''
- }),
- contentType: 'application/json',
- error: function(model, response) {
- AdvisorApp.mainRegion.currentView.detailsView.triggerMethod('Error', response.responseJSON.error_message);
- }
- });
+ '#kpi-goal': {
+ observe: 'kpi_goal',
+ onSet: function(value, options) {
+ if (value < 0) {
+ Helpers.invalidInput(this.ui.kpiGoal, this.ui.saveChanges, 'KPI Goal must be a positive value');
+ } else {
+ Helpers.validInput(this.ui.kpiGoal, this.ui.saveChanges);
}
- this.model.save(edits, {
- patch: true,
- success: function(model, response) {
- model.unset('budget_adjustment_other_text');
- model.unset('budget_adjustment_select');
- var details = AdvisorApp.mainRegion.currentView.detailsView;
- details.model.set('editing', false);
- details.render();
- },
- error: function(model, response) {
- AdvisorApp.mainRegion.currentView.detailsView.triggerMethod('Error', response.responseJSON.error_message);
- }
- });
+ return accounting.unformat(value);
}
},
- bindings: {
- '#name': {
- observe: 'name'
+ '#start_date': {
+ observe: 'start_date',
+ onGet: function(value) {
+ return moment(value, 'YYYY-MM-DD').format('MM/DD/YYYY');
},
- '#budget': {
- observe: 'budget',
- onSet: function(value, options) {
- if (value < this.model.get('spend_lifetime')) {
- Helpers.invalidInput(this.ui.budget, this.ui.saveChanges, 'Budget cannot be less than Total Spend');
- } else {
- Helpers.validInput(this.ui.budget, this.ui.saveChanges);
- }
- return accounting.unformat(value);
+ onSet: function(value) {
+ var notOriginal = this.notOriginalDateValue(value, this.model._originalAttrs.start_date);
+ if ((moment(value) < moment().startOf('day')) && notOriginal) {
+ Helpers.invalidInput(this.ui.startDate, this.ui.saveChanges, 'Start Date cannot be earlier than current date');
+ } else {
+ Helpers.validInput(this.ui.startDate, this.ui.saveChanges);
}
+ return moment(value, 'MM/DD/YYYY').format('YYYY-MM-DD');
+ }
+ },
+ '#end_date': {
+ observe: 'end_date',
+ onGet: function(value) {
+ return moment(value, 'YYYY-MM-DD').format('MM/DD/YYYY');
},
- '#kpi-goal': {
- observe: 'kpi_goal',
- onSet: function(value, options) {
- if (value < 0) {
- Helpers.invalidInput(this.ui.kpiGoal, this.ui.saveChanges, 'KPI Goal must be a positive value');
- } else {
- Helpers.validInput(this.ui.kpiGoal, this.ui.saveChanges);
- }
- return accounting.unformat(value);
+ onSet: function(value) {
+ var notOriginal = this.notOriginalDateValue(value, this.model._originalAttrs.end_date);
+ if ((moment(value) < moment().startOf('day')) && notOriginal) {
+ Helpers.invalidInput(this.ui.endDate, this.ui.saveChanges, 'End Date cannot be earlier than current date');
+ } else {
+ Helpers.validInput(this.ui.endDate, this.ui.saveChanges);
}
- },
- '#start_date': {
- observe: 'start_date',
- onGet: function(value) {
- return moment(value, 'YYYY-MM-DD').format('MM/DD/YYYY');
- },
- onSet: function(value) {
- var notOriginal = this.notOriginalDateValue(value, this.model._originalAttrs.start_date);
- if ((moment(value) < moment().startOf('day')) && notOriginal) {
- Helpers.invalidInput(this.ui.startDate, this.ui.saveChanges, 'Start Date cannot be earlier than current date');
- } else {
- Helpers.validInput(this.ui.startDate, this.ui.saveChanges);
- }
- return moment(value, 'MM/DD/YYYY').format('YYYY-MM-DD');
+ return moment(value, 'MM/DD/YYYY').format('YYYY-MM-DD');
+ }
+ },
+ 'select#budget_adjustment_select': {
+ observe: 'budget_adjustment_select',
+ selectOptions: {
+ collection: 'this.budgetAdjustments',
+ defaultOption: {
+ label: 'Select Reason',
+ value: null
}
- },
- '#end_date': {
- observe: 'end_date',
- onGet: function(value) {
- return moment(value, 'YYYY-MM-DD').format('MM/DD/YYYY');
- },
- onSet: function(value) {
- var notOriginal = this.notOriginalDateValue(value, this.model._originalAttrs.end_date);
- if ((moment(value) < moment().startOf('day')) && notOriginal) {
- Helpers.invalidInput(this.ui.endDate, this.ui.saveChanges, 'End Date cannot be earlier than current date');
- } else {
- Helpers.validInput(this.ui.endDate, this.ui.saveChanges);
- }
- return moment(value, 'MM/DD/YYYY').format('YYYY-MM-DD');
- }
- },
- 'select#budget_adjustment_select': {
- observe: 'budget_adjustment_select',
- selectOptions: {
- collection: 'this.budgetAdjustments',
- defaultOption: {
- label: 'Select Reason',
- value: null
- }
- }
- },
- '#budget_adjustment_other_text': {
- observe: 'budget_adjustment_other_text'
}
},
- behaviors: {
- DateRangeValidate: {},
- DisplayError: {}
+ '#budget_adjustment_other_text': {
+ observe: 'budget_adjustment_other_text'
}
- });
+ },
+ behaviors: {
+ DateRangeValidate: {},
+ DisplayError: {}
+ }
});
+ module.exports = Views;
---------- diff:
modules/campaign/card-views/facebook-chart.js
+ /* global dependencies */
+ var moment = require('moment');
+ var AdvisorApp = require('../../../app');
/**
* NOTE! In the CampaignModule this view is nested inside of the Summary view.
*/
- AdvisorApp.module('CampaignModule.Views', function(Views) {
- this.FacebookChart = this.Chart.extend({
- /**
- * @returns {Object} of collectionFilters. These translate to query params in the request URL.
- */
- getCollectionFilters: function() {
- return {
- adgroup__adcampaign: this.model.get('platform_campaigns')[0].platform_campaign_id,
- group_by: 'date_start',
- metric: 'kpi',
- order_by: 'date_start',
- date_start__gte: moment(this.model.get('start_date')).startOf('day'),
- date_start__lte: moment(this.model.get('end_date')).isAfter(moment()) ? moment().endOf('day') : moment(this.model.get('end_date')).endOf('day')
- };
- }
- });
+ /* module definition */
+ var Views = {};
+ this.FacebookChart = this.Chart.extend({
+ /**
+ * @returns {Object} of collectionFilters. These translate to query params in the request URL.
+ */
+ getCollectionFilters: function() {
+ return {
+ adgroup__adcampaign: this.model.get('platform_campaigns')[0].platform_campaign_id,
+ group_by: 'date_start',
+ metric: 'kpi',
+ order_by: 'date_start',
+ date_start__gte: moment(this.model.get('start_date')).startOf('day'),
+ date_start__lte: moment(this.model.get('end_date')).isAfter(moment()) ? moment().endOf('day') : moment(this.model.get('end_date')).endOf('day')
+ };
+ }
});
+ module.exports = Views;
---------- diff:
modules/campaign/card-views/summary.js
- AdvisorApp.module('CampaignModule.Views', function(Views) {
- this.Summary = Marionette.LayoutView.extend({
- template: Handlebars.templates.SummaryCard,
+ /* global dependencies */
+ var moment = require('moment');
+ var _ = require('underscore');
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
- regions: {
- pacingRegion: '.pacing',
- chartRegion: '.chart'
- },
- initialize: function() {
- var platform = _.string.titleize(this.options.platform); // I.E. 'Facebook', 'Twitter', etc.
+ /* module definition */
+ var Views = {};
- this.pacingView = new (AdvisorApp.module('Common.Views')).Pacing({
- model: this.model
- });
- this.chartView = new Views[platform + 'Chart']({
- level: this.options.level,
- platform: this.options.platform,
- collection: this.collection,
- model: this.model
- });
- },
- onShow: function() {
- /**
- * @param {String} selector CSS selector for region.
- * @param {String} region String of the region key on this view.
- */
- var showRegion = function(selector, region) {
- var view = region.replace('Region', 'View');
- this[region].show(this[view]);
- };
- _(this.regions).each(showRegion, this);
- },
+ this.Summary = Marionette.LayoutView.extend({
+ template: Handlebars.templates.SummaryCard,
+ regions: {
+ pacingRegion: '.pacing',
+ chartRegion: '.chart'
+ },
+ initialize: function() {
+ var platform = _.string.titleize(this.options.platform); // I.E. 'Facebook', 'Twitter', etc.
+ this.pacingView = new (AdvisorApp.module('Common.Views')).Pacing({
+ model: this.model
+ });
+ this.chartView = new Views[platform + 'Chart']({
+ level: this.options.level,
+ platform: this.options.platform,
+ collection: this.collection,
+ model: this.model
+ });
+ },
+ onShow: function() {
/**
- * @returns {Object} of variables we want accessible in our Handlebar Template.
+ * @param {String} selector CSS selector for region.
+ * @param {String} region String of the region key on this view.
*/
- templateHelpers:function() {
- var endDate = this.model.has('end_time') ? moment(this.model.get('end_time')) : moment(this.model.get('end_date')).endOf('day');
+ var showRegion = function(selector, region) {
+ var view = region.replace('Region', 'View');
+ this[region].show(this[view]);
+ };
+ _(this.regions).each(showRegion, this);
+ },
+ /**
+ * @returns {Object} of variables we want accessible in our Handlebar Template.
+ */
+ templateHelpers:function() {
+ var endDate = this.model.has('end_time') ? moment(this.model.get('end_time')) : moment(this.model.get('end_date')).endOf('day');
+ return {
+ dateIsInFuture: moment().diff(endDate) < 0, // Dates in future will be a negative number.
+ timeToEndDate: endDate.fromNow(true), // Returns a lovely string of time from now.
+ endDate: endDate.format('MM/DD/YYYY'),
+ parentInitiativeName: this.getOption('initiative').get('name'),
+ parentInitiativeId: this.getOption('initiative').get('id')
+ };
+ }
+ });
- return {
- dateIsInFuture: moment().diff(endDate) < 0, // Dates in future will be a negative number.
- timeToEndDate: endDate.fromNow(true), // Returns a lovely string of time from now.
- endDate: endDate.format('MM/DD/YYYY'),
- parentInitiativeName: this.getOption('initiative').get('name'),
- parentInitiativeId: this.getOption('initiative').get('id')
- };
- }
- });
- });
+ module.exports = Views;
---------- diff:
modules/campaign/card-views/triggers.js
- AdvisorApp.module('CampaignModule.Views', function(Views) {
- this.Triggers = Marionette.ItemView.extend({
- template: Handlebars.templates.TriggersCard
- });
+ /* global dependencies */
+ var Marionette = require('marionette');
+ var AdvisorApp = require('../../../app');
+ /* module definition */
+ var Views = {};
+ this.Triggers = Marionette.ItemView.extend({
+ template: Handlebars.templates.TriggersCard
});
+ module.exports = Views;
---------- diff:
modules/campaign/card-views/twitter-chart.js
+ /* global dependencies */
+ var _ = require('underscore');
+ var moment = require('moment');
+ var AdvisorApp = require('../../../app');
/**
* NOTE! In the CampaignModule this view is nested inside of the Summary view.
*/
- AdvisorApp.module('CampaignModule.Views', function(Views) {
- this.TwitterChart = this.Chart.extend({
- /**
- * The new endpoint to get Twitter Analytics takes a comma separated list of ids when getting data for
- * Twitter Campaign level.
- * For Twitter Line Item (a.k.a. AdGroup) level charts, we can just send the 'id' of the Twitter Campaign.
- * @returns {Object} of collectionFilters. These translate to query params in the request URL.
- */
- getCollectionFilters: function(options) {
- return {
- campaign__in: this.getCampaignIn(),
- group_by: 'start',
- start__gte: moment(this.model.get('start_date')).startOf('day'),
- start__lte: moment(this.model.get('end_date')).isAfter(moment()) ? moment().endOf('day') : moment(this.model.get('end_date')).endOf('day')
- };
- },
+ /* module definition */
+ var Views = {};
- /** @returns {Array|Number} */
- getCampaignIn: function() {
- if (this.options.level === 'adset') {
- return _(this.model.get('platform_campaigns')).map(function(pc) {
- return pc.platform_campaign_id;
- }).join();
- }
- return this.model.get('id');
+ this.TwitterChart = this.Chart.extend({
+ /**
+ * The new endpoint to get Twitter Analytics takes a comma separated list of ids when getting data for
+ * Twitter Campaign level.
+ * For Twitter Line Item (a.k.a. AdGroup) level charts, we can just send the 'id' of the Twitter Campaign.
+ * @returns {Object} of collectionFilters. These translate to query params in the request URL.
+ */
+ getCollectionFilters: function(options) {
+ return {
+ campaign__in: this.getCampaignIn(),
+ group_by: 'start',
+ start__gte: moment(this.model.get('start_date')).startOf('day'),
+ start__lte: moment(this.model.get('end_date')).isAfter(moment()) ? moment().endOf('day') : moment(this.model.get('end_date')).endOf('day')
+ };
+ },
+ /** @returns {Array|Number} */
+ getCampaignIn: function() {
+ if (this.options.level === 'adset') {
+ return _(this.model.get('platform_campaigns')).map(function(pc) {
+ return pc.platform_campaign_id;
+ }).join();
}
- });
+ return this.model.get('id');
+ }
});
+ module.exports = Views;
---------- diff:
modules/campaign/edit/edit-controller.js
- AdvisorApp.module('CampaignModule.Edit', function(Edit, App) {
+ /* global dependencies */
+ var $ = require('jquery');
+ var _ = require('underscore');
+ var AdvisorApp = require('../../../app');
- var moduleChannel;
- /**
- * Configure cards to be shown on the edit page
- */
- var cardsInView = [
- {
- module: 'tracking:card',
- region: 'tracking'
- },
- {
- module: 'targeting:card',
- region: 'targeting'
- },
- {
- module: 'summary:card',
- region: 'sub-1'
- }
- ];
+ /* module definition */
+ var Edit = {};
- /**
- * Fetch campaign then load view
- * @param {Object} options
- */
- var init = function(options) {
- if (!options || !options.region || !options.campaignID) { return; }
- App.request('advisor:initiative:entity', options.initiativeID).done(function(initiative) {
- App.request('advisor:campaign:entity', options.campaignID).done(function(campaign) {
- loadEditView({
- initiative: initiative,
- campaign: campaign,
- region: options.region,
- cards: cardsInView
- });
+ var moduleChannel;
+ var cardsInView = [
+ {
+ module: 'tracking:card',
+ region: 'tracking'
+ },
+ {
+ module: 'targeting:card',
+ region: 'targeting'
+ },
+ {
+ module: 'summary:card',
+ region: 'sub-1'
+ }
+ ];
+ var init = function(options) {
+ if (!options || !options.region || !options.campaignID) { return; }
+ App.request('advisor:initiative:entity', options.initiativeID).done(function(initiative) {
+ App.request('advisor:campaign:entity', options.campaignID).done(function(campaign) {
+ loadEditView({
+ initiative: initiative,
+ campaign: campaign,
+ region: options.region,
+ cards: cardsInView
});
});
+ });
+ };
+ var loadEditView = function(options) {
+ var layoutView = new (moduleChannel.reqres.request('edit:layout:View'))(options);
+ layoutView.loadCards = loadCards;
+ layoutView.loadToolbar = loadToolbar;
+ layoutView.on(layoutEvents);
+ options.region.show(layoutView);
+ App.trigger('usage:pageevent', 'Campaign Detail', 'Edit');
+ };
+ var loadCards = function() {
+ var platformCards = {
+ facebook: ['tracking:card', 'targeting:card', 'summary:card'],
+ twitter: ['targeting:card', 'summary:card']
};
- /**
- * Load edit view
- * @param {Object} options
- */
- var loadEditView = function(options) {
- var layoutView = new (moduleChannel.reqres.request('edit:layout:View'))(options);
- layoutView.loadCards = loadCards;
- layoutView.loadToolbar = loadToolbar;
- layoutView.on(layoutEvents);
- options.region.show(layoutView);
- App.trigger('usage:pageevent', 'Campaign Detail', 'Edit');
- };
- /**
- * Load cards into layout view
- * Options.cards defines each card's module and view region
- * @return {Object} layout view
- */
- var loadCards = function() {
- var platformCards = {
- facebook: ['tracking:card', 'targeting:card', 'summary:card'],
- twitte
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment