Created
July 10, 2015 20:39
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
---------- 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: '«', | |
title: 'First' | |
}, | |
back: { | |
label: '‹', | |
title: 'Previous' | |
}, | |
forward: { | |
label: '›', | |
title: 'Next' | |
}, | |
fastForward: { | |
label: '»', | |
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('_', '_​'); | |
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