Skip to content

Instantly share code, notes, and snippets.

@lacostej
Created September 24, 2014 05:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lacostej/1945c375ca2d3ae9629f to your computer and use it in GitHub Desktop.
Save lacostej/1945c375ca2d3ae9629f to your computer and use it in GitHub Desktop.
Beautified GameAnalytics.com Angular JS client side (v 1411390919231). From https://go.gameanalytics.com/static/ga-app/scripts/1411390919231.app.js
This file has been truncated, but you can view the full file.
"use strict";
angular.module("ga.app", ["ngCookies", "ngRoute", "ngAnimate", "ngSanitize", "ngTouch", "swipe", "ui.router", "pasvaz.bindonce", "ga.app.routes.admin", "ga.app.routes.studio", "ga.app.routes.game", "ga.app.routes.user", "ga.app.routes.public", "ga.app.routes.error", "ga.app.partials.toolHeader", "ga.app.springboard", "ga.app.footer", "ga.services.user", "ga.services.tracking", "ga.services.pardot", "ga.services.announcement", "ga.pages.templates", "ga.pages.content", "ga.app.headerContent", "ga.components.rollbar", "ga.utils.tracking", "ga.cache-buster", "ga.api.statuspage", "ga.ui.tour", "ga.ui.notify", "ga.ui.modal", "ga.config", "ga.services.user"]).config(function($locationProvider, $stateProvider, $urlRouterProvider, $provide, $httpProvider) {
var SILENT_USER_RESOLVE = function($q, gaServicesUser) {
return gaServicesUser.resolveUser().catch(function() {
return $q.when(null)
})
};
$provide.decorator("$exceptionHandler", function($delegate, $injector, gaComponentsRollbar) {
return function(exception, cause) {
$delegate(exception, cause), gaComponentsRollbar.put(exception), $injector.invoke(["gaServicesTracking",
function(gaServicesTracking) {
gaServicesTracking.submitEvent("error", null, {
severity: "error",
message: exception.stack
})
}
])
}
}), window.location.host.match("guides.*?") ? $stateProvider.state("root", {
url: "/",
resolve: {
redirect: function($timeout, $state) {
return $timeout(function() {
$state.go("content.section", {
section: "sdk"
}, {
location: "replace"
})
}), !1
}
}
}) : $stateProvider.state("root", {
url: "/",
resolve: {
user: SILENT_USER_RESOLVE,
redirect: function(user, $timeout, $state) {
return user ? ($state.go("user.home", {}, {
location: "replace"
}), !1) : ($state.go("public.login", {}, {
location: "replace"
}), !1)
}
}
}), $stateProvider.state("templates", {
url: "/templates",
resolve: {
user: SILENT_USER_RESOLVE
},
views: {
main: {
templateUrl: "/static/ga-app/modules/pages/templates/templates.html",
controller: "gaPagesTemplatesController"
},
header: {
template: ""
},
footer: {
template: ""
}
}
}), $stateProvider.state("templates.show", {
url: "/:category/:item"
}), $stateProvider.state("content", {
url: "/content",
resolve: {
user: SILENT_USER_RESOLVE
},
views: {
main: {
templateUrl: "/static/ga-app/modules/pages/content/content.html",
controller: "gaPagesContentController"
},
header: {
templateUrl: "/static/ga-app/modules/app/partials/public/header-content.html",
controller: "gaAppHeaderContentController"
},
footer: {
template: ""
}
}
}), $stateProvider.state("content.section", {
url: "/:section?page&step"
}), $locationProvider.html5Mode(!0);
var interceptor = function($q, $injector, $window, gaConfig) {
var responseError = function(response) {
var redirect, isUserApi;
switch (response.status) {
case 401:
redirect = "login", response.config.url.match("v1/user/data") && (redirect = !1), isUserApi = !!response.config.url.match(new RegExp("(^/|" + gaConfig.userApi.baseUrl + ")")), isUserApi || (redirect = !1), redirect && $injector.get("$state").go("public.login", null, {
inherit: !1
});
break;
case 403:
redirect = !0, isUserApi = !!response.config.url.match(new RegExp("(^/|" + gaConfig.userApi.baseUrl + ")")), isUserApi || (redirect = !1), $window.location.href = "/home";
break;
case 500:
response.data = {
results: [],
errors: [{
msg: "Internal server error",
type: "internalServerError"
}]
};
break;
case 0:
response = {
data: {
error: "cancel"
},
status: 0
}
}
return $q.reject(response)
};
return {
responseError: responseError
}
};
$httpProvider.interceptors.push(interceptor)
}).run(function($rootScope, $window, $timeout, $state, gaUtilsTracking, gaApiStatuspage, gaServicesTracking, gaUiTour, gaUiNotify, gaServicesPardot, gaServicesUser, gaServicesAnnouncement, gaUiModal) {
$timeout(gaApiStatuspage.updateStatus.bind(gaApiStatuspage), 500), $rootScope.$on("$stateChangeStart", function() {
gaUiNotify.loading(!0)
}), $rootScope.$on("$stateChangeSuccess", function(event, to, toParams, from, fromParams) {
return gaServicesTracking.init(), gaUiTour.stop(), $rootScope.$broadcast("killTour"), gaUiModal.hideAll(), $state.current.data && $state.current.data.alias ? void $state.go($state.current.data.alias, {}, {
location: "replace"
}) : (toParams.alert && (gaUiNotify.show(toParams.alert, 5e3, "default"), $timeout($state.go.bind($state, $state.$current.self.name, {
alert: null
}, {
notify: !1
}))), gaUtilsTracking.trackPage(from, fromParams), gaServicesTracking.trackState(), $state.includes("public.login") || $state.includes("public.signup") ? gaServicesPardot.trackPage("loginSignup") : $state.includes("public.create-account") && null !== $state.params.invite && gaServicesPardot.trackPage("invited"), gaServicesUser.token && $state.includes("game") && gaServicesUser.game(parseInt($state.params.gameId, 10)) && gaServicesUser.game(parseInt($state.params.gameId, 10)).demo ? gaServicesAnnouncement.add({
id: "demo-game",
style: "yellow",
icon: "ga-icon-severity-error",
content: "<strong>DEMO GAME</strong> - Please be aware that the changes you make will not be saved and certain features have been disabled. Have fun!",
replace: !1,
close: !1
}) : gaServicesAnnouncement.hide("demo-game"), gaUiNotify.loading(!1), void(gaServicesUser.token && $state.params.gameId && gaServicesAnnouncement.add({
id: "release-2014-08-06",
style: "light-blue",
icon: "ga-icon-logo",
content: 'Brand new Funnels: Completely rebuilt with greater possibilities to slice your data the way you want it – <a href="/game/' + $state.params.gameId + '/funnel" class="hide-me">Learn more</a>.',
replace: !1,
close: !0,
cookie: !0
})))
})
}), angular.module("ga.config", []).value("gaConfig", {
getUrlInc: {},
getUrl: function(batch, realtime, funnels) {
var type = realtime ? "realtime" : "regular",
servers = "realtime" === type ? this.data.operationsBaseDomain : this.data.baseDomain;
"object" != typeof servers && (servers = [servers]), this.getUrlInc[type] = ++this.getUrlInc[type] || 0, this.getUrlInc[type] > servers.length - 1 && (this.getUrlInc[type] = 0);
var baseUrl = servers[this.getUrlInc[type]] + (batch ? this.data.batchPath : funnels ? this.data.funnelPath : this.data.basePath);
return baseUrl
},
data: {
baseDomain: ["https://query-0.gameanalytics.com", "https://query-1.gameanalytics.com", "https://query-2.gameanalytics.com", "https://query-3.gameanalytics.com"],
operationsBaseDomain: ["https://ops-query-0.gameanalytics.com", "https://ops-query-1.gameanalytics.com", "https://ops-query-2.gameanalytics.com", "https://ops-query-3.gameanalytics.com"],
basePath: "/v1/games/",
batchPath: "/v1/batch/",
funnelPath: "/v2/games/"
},
baseUrl: window.location.protocol + "//" + window.location.host + "/",
userApi: {
baseUrl: window.location.protocol + "//" + window.location.host + "/v1/"
},
images: {
baseUrl: "https://s3.amazonaws.com/images.gameanalytics.com/"
},
cache: {
type: "angular",
id: "ga",
expire: 3e5
},
cacheBuster: function() {
return "1411390919231"
},
demoGame: {
id: 6788,
token: "priv-eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJ0b2tlbiI6ICJleUpoYkdjaU9pQWlTRk15TlRZaUxDQWlkSGx3SWpvZ0lrcFhWQ0o5LmV5SmxlSEFpT2lBeE16azBOekEzTXpBM2ZRLk9VQnZnbmN4blB1NDAwWDBhcVpFT2RvREk2NWpFUmZVbXlRSVhESHNGNFkifQ.LMfTob9YZm71Z3B2PKBjtDJysxOUN4ddEuEt5E8mhVk"
},
gaPlatform: "production",
gaKey: "WXpnNU9ERmhOVEk1TlRCbE56STRPVEUwTldZNFlXSmhabUV3WVRoaU4yUT0=",
gaSecret: "TnpjMk56SmpOR05pWkRrM1pEbGpPRFl4TjJGaE9UazRNak14TW1SalpEbGtaVEF6WWpZeU5nPT0="
}), angular.module("ga.app.routes.studio", ["ui.router", "ga.app.mainMenu", "ga.services.user", "ga.pages.studio.settings.information", "ga.pages.studio.settings.users", "ga.pages.studio.settings.games", "ga.pages.user.subscriptions"]).config(function($stateProvider) {
var USER_RESOLVE = function($q, $state, gaServicesUser) {
return gaServicesUser.resolveUser().catch($state.go.bind($state, "public.login", null, {
inherit: !1,
replace: !0
}))
};
$stateProvider.state("studio", {
"abstract": !0,
url: "/studio/:studioId?alert",
resolve: {
userResolve: USER_RESOLVE
},
views: {
main: {
template: '<div style="margin-left: 80px;" ui-view></div>'
},
header: {
templateUrl: "/static/ga-app/modules/app/partials/menu/menu.html",
controller: "gaAppMainMenuController"
},
footer: {
template: ""
}
}
}), $stateProvider.state("studio.settings", {
url: "/settings",
templateUrl: "/static/ga-app/modules/pages/general/tabbed/tabbed.html",
controller: "gaPagesGeneralTabbedController",
data: {
tabs: [{
title: "Studio information",
state: "studio.settings.information"
}, {
title: "Users",
state: "studio.settings.users"
}, {
title: "Games",
state: "studio.settings.games"
}]
}
}), $stateProvider.state("studio.settings.information", {
url: "/information",
templateUrl: "/static/ga-app/modules/pages/studio/settings/information/information.html",
controller: "gaPagesStudioSettingsInformationController"
}), $stateProvider.state("studio.settings.users", {
url: "/users",
templateUrl: "/static/ga-app/modules/pages/studio/settings/users/users.html",
controller: "gaPagesStudioSettingsUsersController"
}), $stateProvider.state("studio.settings.games", {
url: "/games",
templateUrl: "/static/ga-app/modules/pages/studio/settings/games/games.html",
controller: "gaPagesStudioSettingsGamesController"
})
}), angular.module("ga.app.routes.game", ["ga.app.mainMenu", "ui.router", "ga.services.user", "ga.api.userDb.authenticated.heatmap", "ga.pages.dashboards", "ga.pages.explore2", "ga.pages.explore", "ga.pages.heatmap", "ga.pages.cohorts", "ga.pages.game.funnel.list", "ga.pages.game.funnel.view", "ga.pages.game.funnel.edit", "ga.pages.querybuilder", "ga.pages.game.initialize", "ga.pages.querytester", "ga.pages.game.settings.information", "ga.pages.game.settings.users", "ga.pages.game.settings.emailReports", "ga.pages.game.settings.services", "ga.pages.game.settings.exportData", "ga.pages.amt"]).config(function($stateProvider) {
var SILENT_USER_RESOLVE = function($q, gaServicesUser) {
return gaServicesUser.resolveUser().catch(function() {
return $q.when(null)
})
},
DASHBOARDS_RESOLVE = function(gameResolve, $q, $timeout, $stateParams, $state, gaServicesUser, gaApiUserDbAuthenticatedDashboards) {
return gaApiUserDbAuthenticatedDashboards.getDashboards($stateParams.gameId).catch(function() {
return $state.go("user.home", null, {
inherit: !1,
location: "replace"
}), $q.reject()
}).then(function() {
return gaApiUserDbAuthenticatedDashboards.resolve($stateParams).catch(function(newParams) {
return $state.go("game.dashboards.dashboard", newParams, {
location: "replace"
}), $q.reject()
})
})
},
GAME_RESOLVE = function(user, $q, $state, $stateParams, $window, gaServicesUser, gaUtilsCache) {
if (user) {
var game = gaServicesUser.game(parseInt($stateParams.gameId || 0, 10));
return game ? $q.when() : ($state.go("user.home"), $q.reject("rejected"))
}
var path = $window.location.href.replace($window.location.origin, "");
return gaUtilsCache.put("goToOnNextLogin", path, "localStorage", 3e5), $state.go("public.login", null, {
inherit: !1
}), $q.reject()
},
GAME_SETTINGS_RESOLVE = function($stateParams, $timeout, $q, $state, gameResolve, gaServicesUser) {
var gameId = parseInt($stateParams.gameId || 0, 10),
game = gaServicesUser.game(gameId);
if (game) {
var adminStatus = game.admin;
if (adminStatus === !1) return $timeout(function() {
$state.go("game.settingsview", {
gameId: gameId
})
}), $q.reject()
}
},
AUTH_HEATMAP = function(gameResolve, gaApiUserDbAuthenticatedHeatmap, $stateParams) {
return gaApiUserDbAuthenticatedHeatmap.authHeatmap($stateParams.gameId, $stateParams.firsttime)
},
GAME_STATUS = function(gameResolve, $stateParams, gaServicesGame) {
return gaServicesGame.getStatus($stateParams.gameId, !0)
};
$stateProvider.state("game", {
resolve: {
user: SILENT_USER_RESOLVE,
gameResolve: GAME_RESOLVE
},
url: "/game/:gameId?alert",
"abstract": !0,
views: {
main: {
template: '<div class="ga wrapper tool" ui-view></div>'
},
header: {
templateUrl: "/static/ga-app/modules/app/partials/menu/menu.html",
controller: "gaAppMainMenuController"
},
footer: {
template: ""
}
}
}), $stateProvider.state("game.content", {
"abstract": !0,
url: "/content",
templateUrl: "/static/ga-app/modules/pages/content/content.html",
controller: "gaPagesContentController"
}), $stateProvider.state("game.content.section", {
url: "/:section?page&step"
}), $stateProvider.state("game.settings", {
resolve: {
resolveGameSettings: GAME_SETTINGS_RESOLVE
},
url: "/settings",
templateUrl: "/static/ga-app/modules/pages/general/tabbed/tabbed.html",
controller: "gaPagesGeneralTabbedController",
data: {
tabs: [{
title: "Game information",
state: "game.settings.information"
}, {
title: "Users",
state: "game.settings.users"
}, {
title: "Email reports",
state: "game.settings.emailReports"
}, {
title: "Services",
state: "game.settings.services"
}, {
title: "Export data",
state: "game.settings.exportData"
}]
}
}), $stateProvider.state("game.settingsview", {
url: "/settings/view",
templateUrl: "/static/ga-app/modules/pages/general/tabbed/tabbed.html",
controller: "gaPagesGeneralTabbedController",
data: {
tabs: [{
title: "Game information",
state: "game.settingsview.information"
}]
}
}), $stateProvider.state("game.settings.emailReports", {
url: "/email-reports",
templateUrl: "/static/ga-app/modules/pages/game/settings/email-reports/email-reports.html",
controller: "gaPagesGameSettingsEmailReportsController"
}), $stateProvider.state("game.settings.exportData", {
url: "/export-data",
templateUrl: "/static/ga-app/modules/pages/game/settings/export-data/export-data.html",
controller: "gaPagesGameSettingsExportDataController"
}), $stateProvider.state("game.settings.information", {
url: "/information",
templateUrl: "/static/ga-app/modules/pages/game/settings/information/information.html",
controller: "gaPagesGameSettingsInformationController"
}), $stateProvider.state("game.settingsview.information", {
url: "/information",
templateUrl: "/static/ga-app/modules/pages/game/settings/information/information.html",
controller: "gaPagesGameSettingsInformationController",
data: {
showViewOnly: !0
}
}), $stateProvider.state("game.settings.users", {
url: "/users",
templateUrl: "/static/ga-app/modules/pages/game/settings/users/users.html",
controller: "gaPagesGameSettingsUsersController"
}), $stateProvider.state("game.settings.services", {
url: "/services?pop",
templateUrl: "/static/ga-app/modules/pages/game/settings/services/services.html",
controller: "gaPagesGameSettingsServicesController"
}), $stateProvider.state("game.initialize", {
url: "/initialize?skip",
resolve: {
resolveGameStatus: GAME_STATUS
},
templateUrl: "/static/ga-app/modules/pages/game/initialize/initialize.html",
controller: "gaPagesGameInitializeController"
}), $stateProvider.state("game.explore", {
url: "/explore",
templateUrl: "/static/ga-app/modules/pages/game/explore/explore.html",
controller: "gaPagesExploreController"
}), $stateProvider.state("game.heatmap", {
resolve: {
resolveHeatmap: AUTH_HEATMAP
},
url: "/heatmap?firsttime",
templateUrl: "/static/ga-app/modules/pages/game/heatmap/heatmap.html",
controller: "gaPagesHeatmapController"
}), $stateProvider.state("game.heatmap-firsttime", {
url: "/heatmap/firsttime",
templateUrl: "/static/ga-app/modules/pages/game/heatmap/heatmap-firsttime.html",
controller: "gaPagesHeatmapFirstTimeController"
}), $stateProvider.state("game.heatmap-nodata", {
url: "/heatmap/nodata",
templateUrl: "/static/ga-app/modules/pages/game/heatmap/heatmap-nodata.html",
controller: "gaPagesHeatmapNodataController"
}), $stateProvider.state("game.funnel", {
"abstract": !0,
template: "<ui-view />"
}), $stateProvider.state("game.funnel.list", {
url: "/funnel?reload",
templateUrl: "/static/ga-app/modules/pages/game/funnel/list/funnel-list.html",
controller: "gaPagesFunnelListController"
}), $stateProvider.state("game.funnel.list-safe", {
url: "/funnel/",
templateUrl: "/static/ga-app/modules/pages/game/funnel/list/funnel-list.html",
controller: "gaPagesFunnelListController"
}), $stateProvider.state("game.funnel.view-backend", {
url: "/funnel/:funnelId",
templateUrl: "/static/ga-app/modules/pages/game/funnel/view/funnel-view.html",
controller: "gaPagesFunnelViewController"
}), $stateProvider.state("game.funnel.edit", {
url: "/funnel/:funnelId/edit",
templateUrl: "/static/ga-app/modules/pages/game/funnel/edit/funnel-edit.html",
controller: "gaPagesFunnelEditController"
}), $stateProvider.state("game.funnel.view", {
url: "/funnel/:funnelId/:dateRangeId",
templateUrl: "/static/ga-app/modules/pages/game/funnel/view/funnel-view.html",
controller: "gaPagesFunnelViewController"
}), $stateProvider.state("game.cohort", {
url: "/cohort",
templateUrl: "/static/ga-app/modules/pages/game/cohorts/cohorts.html",
controller: "gaPagesCohortsController"
}), $stateProvider.state("game.querytester", {
url: "/querytester?state",
templateUrl: "/static/ga-app/modules/pages/game/querytester/querytester.html",
controller: "gaPagesQuerytesterController"
}), $stateProvider.state("game.querybuilder", {
url: "/querybuilder",
templateUrl: "/static/ga-app/modules/pages/game/querybuilder/querybuilder.html",
controller: "gaPagesQuerybuilderController"
}), $stateProvider.state("game.amt", {
url: "/amt",
templateUrl: "/static/ga-app/modules/pages/game/amt/amt.html",
controller: "gaPagesAmtController"
}), $stateProvider.state("game.mock", {
url: "/mock",
templateUrl: "/static/ga-app/modules/pages/game/mock/mock.html",
controller: "gaPagesMockController"
}), $stateProvider.state("game.dashboardsFix", {
url: "/dashboards/",
data: {
alias: "game.dashboards"
}
}), $stateProvider.state("game.dashboards", {
url: "/dashboards",
templateUrl: "/static/ga-app/modules/pages/game/dashboards/dashboards.html",
controller: "gaPagesDashboardsController"
}), $stateProvider.state("game.dashboards.dashboard", {
url: "/:action/:dashboardId?state",
resolve: {
dashboardsResolve: DASHBOARDS_RESOLVE
}
}), $stateProvider.state("game.explore2", {
url: "/explore2",
templateUrl: "/static/ga-app/modules/pages/game/explore2/explore2.html",
controller: "gaPagesExplore2Controller"
}), $stateProvider.state("game.dashboards2.dashboard", {
url: "/:action/:dashboardId?state",
resolve: {
dashboardsResolve: DASHBOARDS_RESOLVE
}
})
}), angular.module("ga.app.routes.user", ["ga.app.mainMenu", "ui.router", "ga.services.user", "ga.pages.general.tabbed", "ga.pages.user.home", "ga.pages.user.profile", "ga.pages.user.locale", "ga.pages.user.subscriptions"]).config(function($stateProvider) {
var USER_RESOLVE = function($q, $state, gaServicesUser) {
return gaServicesUser.resolveUser().catch($state.go.bind($state, "public.login", null, {
inherit: !1,
replace: !0
}))
},
USER_SUBSCRIPTIONS = function($q, gaApiUserDbAuthenticatedUser) {
return gaApiUserDbAuthenticatedUser.subscriptions()
};
$stateProvider.state("user", {
"abstract": !0,
url: "?alert",
resolve: {
userResolve: USER_RESOLVE
},
views: {
main: {
template: '<div style="margin-left: 80px;" ui-view></div>'
},
header: {
templateUrl: "/static/ga-app/modules/app/partials/menu/menu.html",
controller: "gaAppMainMenuController"
},
footer: {
template: ""
}
}
}), $stateProvider.state("user.home", {
url: "/home?archive",
templateUrl: "/static/ga-app/modules/pages/user/home/home.html",
controller: "gaPagesUserHomeController"
}), $stateProvider.state("user.settings", {
url: "/settings",
templateUrl: "/static/ga-app/modules/pages/general/tabbed/tabbed.html",
controller: "gaPagesGeneralTabbedController",
data: {
tabs: [{
title: "Profile",
state: "user.settings.profile"
}, {
title: "Email reports",
state: "user.settings.subscriptions"
}, {
title: "Locale settings",
state: "user.settings.locale"
}]
}
}), $stateProvider.state("user.settings.profile", {
url: "/profile?source&link_token",
templateUrl: "/static/ga-app/modules/pages/user/profile/profile.html",
controller: "gaPagesUserProfileController"
}), $stateProvider.state("user.settings.locale", {
url: "/locale",
templateUrl: "/static/ga-app/modules/pages/user/locale/locale.html",
controller: "gaPagesUserLocaleController"
}), $stateProvider.state("user.settings.subscriptions", {
url: "/subscriptions",
resolve: {
resolveSubscriptions: USER_SUBSCRIPTIONS
},
templateUrl: "/static/ga-app/modules/pages/user/subscriptions/subscriptions.html",
controller: "gaPagesUserSubscriptionsController"
})
}), angular.module("ga.app.routes.public", ["ui.router", "ga.services.user", "ga.pages.public.linkAccount", "ga.pages.public.createAccount", "ga.pages.public.activateAccount", "ga.pages.public.signup", "ga.pages.public.login", "ga.pages.public.invite", "ga.pages.public.passwordReset", "ga.pages.public.releaseNotes", "ga.utils.cache", "ga.ui.notify", "ngRoute", "ga.api.userDb.public.passwordReset", "ga.api.userDb.public.invite", "ga.api.userDb.public.unsubscribe", "ga.api.userDb.public.activate"]).config(function($stateProvider) {
var SILENT_USER_RESOLVE = function($q, $stateParams, $state, gaServicesUser, gaUiNotify) {
return null !== $stateParams.logout ? (gaServicesUser.logout(), gaUiNotify.show("You are now logged out", 4e3, "default"), $q.when(null)) : gaServicesUser.resolveUser().catch(function() {
return $q.when(null)
})
},
INVITE_RESOLVE = function(user, $q, $state, $stateParams, gaServicesUser, gaApiUserDbPublicInvite) {
return gaApiUserDbPublicInvite.getInviteInfo($stateParams.email, $stateParams.resource, $stateParams.token).then(function(response) {
if (user) {
if (response.inviteFound && response.userFound && gaServicesUser.details.email === $stateParams.email) return $state.go("user.home", {}, {
location: "replace",
inherit: !1
}), $q.reject()
} else {
var params;
if (response.inviteFound && !response.userFound) return params = {
token: response.createToken,
invite: "",
email: $stateParams.email,
emailmismatch: $stateParams.emailmismatch
}, $state.go("public.create-account", params, {
location: "replace",
inherit: !1
}), $q.reject();
if (response.inviteFound && response.userFound) return params = {
invite: "",
logout: "",
email: $stateParams.email
}, $state.go("public.login", params, {
location: "replace",
inherit: !1
}), $q.reject()
}
return $q.when(response)
})
},
ACTIVATE_RESOLVE = function(user, $q, $state, $stateParams, gaServicesUser, gaApiUserDbPublicActivate) {
return gaApiUserDbPublicActivate.checkActivation($stateParams.email).then(function() {
return user && (gaServicesUser.token = null), $q.when()
}).catch(function(errors) {
return $state.go("public.login", {
error: errors[0].msg
}), $q.reject()
})
},
HOME_RESOLVE = function(user, $timeout, $q, $state) {
return user ? ($timeout(function() {
$state.go("user.home", {}, {
location: "replace"
})
}), $q.reject()) : !0
},
LOGIN_RESOLVE = function(user, $window, $q, $timeout, $state, $stateParams, gaServicesUser, gaUtilsCache, $location) {
if (null !== $stateParams.logout) return $state.go("public.login", {
logout: null
}, {
inherit: !0,
replace: !0,
notify: !0
}), $q.reject("logged out");
if (user) {
if (null !== $stateParams.support) gaServicesUser.supportToken().then(function(token) {
var returnTo = $stateParams.return_to || "/home";
$window.location.href = "http://support.gameanalytics.com/access/jwt?jwt=" + token.support_token + "&return_to=" + returnTo
}).catch(function() {
$state.go("user.home", {}, {
location: "replace"
})
});
else {
var goToPath = $stateParams.goto;
goToPath ? $timeout(function() {
$location.url(goToPath), $location.replace()
}) : $state.go("user.home", {}, {
location: "replace"
})
}
return $q.reject()
}
return $stateParams.token && $stateParams.exp ? (gaServicesUser.token = {
token: $stateParams.token,
exp: $stateParams.exp
}, gaServicesUser.resolveUser().then(function() {
if (null !== $stateParams.support) gaServicesUser.supportToken().then(function(token) {
var returnTo = $stateParams.return_to || "/home";
$window.location.href = "http://support.gameanalytics.com/access/jwt?jwt=" + token.support_token + "&return_to=" + returnTo
}).catch(function() {
$state.go("user.home", {}, {
location: "replace"
})
});
else {
var goTo = $stateParams.goto || gaUtilsCache.get("goToOnNextLogin", "localStorage");
goTo ? $timeout(function() {
$location.url(goTo), $location.replace()
}) : $state.go("user.home", {}, {
location: "replace"
})
}
gaUtilsCache.remove("goToOnNextLogin", "localStorage")
}).catch(function() {
$state.go("public.login", {
token: null,
exp: null
}, {
location: "replace",
reload: !0
})
}), $q.reject()) : $q.when()
},
PASSWORD_RESET_TOKEN_RESOLVE = function(user, $q, $stateParams, gaApiUserDbPublicPasswordReset, gaServicesUser) {
return gaApiUserDbPublicPasswordReset.info($stateParams.token).then(function() {
return gaServicesUser.token = null, $q.when(!0)
}).catch(function() {
return $q.when(!1)
})
},
USER_SUBSCRIPTIONS = function($q, $stateParams, gaApiUserDbPublicUnsubscribe) {
return gaApiUserDbPublicUnsubscribe.getSubscriptions($stateParams.email, $stateParams.token).catch(function(errors) {
return $q.when({
errors: errors
})
})
};
$stateProvider.state("public", {
"abstract": !0,
resolve: {
user: SILENT_USER_RESOLVE
},
url: "?alert&logout",
views: {
main: {
template: "<ui-view />"
},
header: {
templateUrl: "/static/ga-app/modules/app/partials/public/header.html"
},
footer: {
template: ""
}
}
}), $stateProvider.state("public2", {
"abstract": !0,
resolve: {
user: SILENT_USER_RESOLVE
},
url: "?alert&logout",
views: {
main: {
template: "<ui-view />"
},
header: {
templateUrl: "/static/ga-app/modules/app/partials/public/header-content.html",
controller: "gaAppHeaderContentController"
},
footer: {
template: ""
}
}
}), $stateProvider.state("public2.release-notes", {
url: "/release-notes",
templateUrl: "/static/ga-app/modules/pages/public/release-notes/release-notes.html",
controller: "gaPagesPublicReleaseNotesController",
data: {
title: "Release Notes",
link: "/release-notes"
}
}), $stateProvider.state("public.login", {
resolve: {
token: LOGIN_RESOLVE
},
url: "/login?source&token&exp&email&invite&error&linkedother&notfound&passwordreset&notverified&support&return_to&goto&showreset",
templateUrl: "/static/ga-app/modules/pages/public/login/login.html",
controller: "gaPagesPublicLoginController"
}), $stateProvider.state("public.invite", {
resolve: {
inviteResolve: INVITE_RESOLVE
},
url: "/invite/:resource/:token/:email",
templateUrl: "/static/ga-app/modules/pages/public/invite/invite.html",
controller: "gaPagesPublicInviteController"
}), $stateProvider.state("public.link-account", {
url: "/link-account/:email/:token?source",
templateUrl: "/static/ga-app/modules/pages/public/link-account/link-account.html",
controller: "gaPagesPublicLinkAccountController"
}), $stateProvider.state("public.signup", {
resolve: {
token: HOME_RESOLVE
},
url: "/signup?email&linkeduserfound&ref&notverified&source&nr",
templateUrl: "/static/ga-app/modules/pages/public/signup/signup.html",
controller: "gaPagesPublicSignupController"
}), $stateProvider.state("public.create-account", {
url: "/create-account/:email/:token?source&invite&emailmismatch&notverified&firstname&lastname",
templateUrl: "/static/ga-app/modules/pages/public/create-account/create-account.html",
controller: "gaPagesPublicCreateAccountController"
}), $stateProvider.state("public.activate", {
url: "/activate/:email/:token",
resolve: {
resolveActivate: ACTIVATE_RESOLVE
},
templateUrl: "/static/ga-app/modules/pages/public/activate-account/activate-account.html",
controller: "gaPagesPublicActivateAccountController"
}), $stateProvider.state("public.password-reset", {
url: "/reset-password/:token",
resolve: {
resolveValidToken: PASSWORD_RESET_TOKEN_RESOLVE
},
templateUrl: "/static/ga-app/modules/pages/public/password-reset/password-reset.html",
controller: "gaPagesPublicPasswordResetController"
}), $stateProvider.state("public.wrapper", {
"abstract": !0,
template: '<div class="ga page box normal" ui-view></div>'
}), $stateProvider.state("public.wrapper.unsubscribe", {
url: "/reports/unsubscribe/:email/:token",
resolve: {
resolveSubscriptions: USER_SUBSCRIPTIONS
},
templateUrl: "/static/ga-app/modules/pages/user/subscriptions/subscriptions.html",
controller: "gaPagesUserSubscriptionsController"
})
}), angular.module("ga.app.routes.admin", ["ui.router", "ga.services.user", "ga.pages.admin.layout.header", "ga.pages.admin.home", "ga.pages.admin.search", "ga.pages.admin.logs", "ga.pages.admin.user", "ga.pages.admin.game", "ga.pages.admin.studio", "ga.pages.admin.haystack", "ga.pages.admin.export"]).config(function($stateProvider) {
var USER_RESOLVE = ["$state", "gaServicesUser",
function($state, gaServicesUser) {
return gaServicesUser.resolveUser().catch($state.go.bind($state, "public.login", null, {
inherit: !1
}))
}
],
ADMIN_RESOLVE = function(user, $q, $state, gaServicesUser) {
if (user) {
if (user.admin !== !0 || user.adminLoggedIn !== !0) return $state.go("user.home", null, {
inherit: !1
}), $q.reject("rejected");
var now_ts = Math.round((new Date).getTime() / 1e3);
return now_ts > user.adminExpires ? gaServicesUser.getUserData(!0, !0).then(function() {
return $state.go("user.home", null, {
inherit: !1
}), $q.reject()
}).catch(function() {
return $state.go("public.login", null, {
inherit: !1
}), $q.reject()
}) : $q.when()
}
return $state.go($state, "public.login", null, {
inherit: !1
}), $q.reject()
};
$stateProvider.state("admin", {
resolve: {
user: USER_RESOLVE,
admin: ADMIN_RESOLVE
},
url: "/admin",
"abstract": !0,
views: {
main: {
template: "<ui-view/>"
},
header: {
templateUrl: "/static/ga-app/modules/pages/admin/layout/header/header.html",
controller: "gaPagesAdminLayoutHeaderController"
}
}
}), $stateProvider.state("admin.home", {
url: "",
templateUrl: "/static/ga-app/modules/pages/admin/home/home.html",
controller: "gaPagesAdminHomeController"
}), $stateProvider.state("admin.haystack", {
url: "/haystack",
templateUrl: "/static/ga-app/modules/pages/admin/haystack/haystack.html",
controller: "gaPagesAdminHaystackController"
}), $stateProvider.state("admin.search", {
url: "/search",
templateUrl: "/static/ga-app/modules/pages/admin/search/search.html",
controller: "gaPagesAdminSearchController"
}), $stateProvider.state("admin.logs", {
url: "/logs",
templateUrl: "/static/ga-app/modules/pages/admin/logs/logs.html",
controller: "gaPagesAdminLogsController"
}), $stateProvider.state("admin.user", {
url: "/user/:userId",
templateUrl: "/static/ga-app/modules/pages/admin/user/user.html",
controller: "gaPagesAdminUserController"
}), $stateProvider.state("admin.game", {
url: "/game/:gameId",
templateUrl: "/static/ga-app/modules/pages/admin/game/game.html",
controller: "gaPagesAdminGameController"
}), $stateProvider.state("admin.studio", {
url: "/studio/:studioId",
templateUrl: "/static/ga-app/modules/pages/admin/studio/studio.html",
controller: "gaPagesAdminStudioController"
}), $stateProvider.state("admin.export", {
url: "/export",
templateUrl: "/static/ga-app/modules/pages/admin/export/export.html",
controller: "gaPagesAdminExportController"
})
}), angular.module("ga.app.routes.error", ["ngRoute", "ui.router"]).config(function($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise("/error/404"), $stateProvider.state("error", {
url: "/error",
"abstract": !0,
views: {
main: {
template: '<div class="ga-error-page"><div class="ga sections inside"><section class="ga" ui-view></section></div></div>'
},
header: {
template: ""
},
footer: {
template: ""
}
}
}), $stateProvider.state("error.404", {
url: "/404",
template: '<h1>Page not found</h1><p>The page you\'ve requested isn\'t here. <br />Contact <a class="ga color white" href="mailto:support@gameanalytics.com">support@gameanalytics.com</a> if you believe this is an error.</p><p><a href="/home" class="ga color orange">&larr; Go back home</a></p>'
}), $stateProvider.state("error.500", {
url: "/500",
template: '<h1>Internal server error</h1><p>An unexpected error occured.</p><p><a href="/home" class="ga color orange">&larr; Go back home</a></p>'
}), $stateProvider.state("error.maintenance", {
url: "/maintenance",
template: '<h1>We\'re temporarily unavailable</h1><p>We are currently performing maintenance and will be back shortly.<br/>Don\'t worry about your data - our servers are gathering it without interruptions.</p><p>We\'re sorry for the inconvenience.</p><p><a href="https://gameanalytics.statuspage.io/" target="_blank" class="ga color orange">Get updates on our statuspage</a></p>'
})
}), angular.module("ga.app.mainMenu", ["ui.router", "ga.services.user", "ga.ui.menu", "ga.ui.tooltip", "ga.utils.detect", "ga.ui.modal", "ga.ui.notify", "ga.services.dialogs", "ga.api.admin", "ga.pages.admin.controller"]).controller("gaAppMainMenuController", function($rootScope, $scope, $state, gaUiModal, gaUiNotify, gaServicesUser, gaDetect, gaPagesAdminController) {
var updateUserSettings = function() {
$scope.user = {
studios: gaServicesUser.studios,
admin: gaServicesUser.admin,
adminLoggedIn: gaServicesUser.adminLoggedIn,
impersonated: gaServicesUser.impersonated
}
};
$scope.logout = "", updateUserSettings(), $scope.$on("userStudiosChange", function() {
$scope.user.studios = gaServicesUser.studios, selectGame(parseInt($state.params.gameId, 10) || 0)
}), $scope.iframeFix = $state.includes("game.heatmap") && "windows" === gaDetect.OS(), $scope.$on("$stateChangeSuccess", function() {
$scope.iframeFix = $state.includes("game.heatmap") && "windows" === gaDetect.OS()
}), $scope.goTo = function(gameId) {
$state.includes("game.settings") ? $state.go("game.settings", {
gameId: gameId,
state: null
}) : $state.includes("game.heatmap") || $state.includes("game.heatmap-nodata") || $state.includes("game.heatmap-firsttime") ? $state.go("game.heatmap", {
gameId: gameId,
state: null
}) : $state.includes("game.explore") ? $state.go("game.explore", {
gameId: gameId,
state: null
}) : $state.includes("game.funnel") ? $state.go("game.funnel.list", {
gameId: gameId,
state: null
}) : $state.includes("game.cohort") ? $state.go("game.cohort", {
gameId: gameId,
state: null
}) : $state.includes("game.querybuilder") ? $state.go("game.querybuilder", {
gameId: gameId,
state: null
}) : isNaN(parseInt($state.params.dashboardId, 10)) ? $state.go("game.dashboards.dashboard", {
action: "show",
gameId: gameId,
dashboardId: $state.params.dashboardId,
state: null
}) : $state.go("game.dashboards.dashboard", {
action: "show",
gameId: gameId,
dashboardId: 0,
state: null
})
};
var selectGame = function(gameId) {
if ($scope.gameId = gameId, gameId) {
var game = gaServicesUser.game(gameId);
$scope.gameTitle = game.title, $scope.imagePath = game.imagePath, $scope.isDemo = game.demo
}
};
selectGame(parseInt($state.params.gameId, 10) || 0);
var _setMenu = function() {
if ($state.current && $state.current.name) {
var active = $state.current.name.split(".");
$scope.activeMenuItem = active.length > 1 ? active[1] : "", ("heatmap-nodata" === $scope.activeMenuItem || "heatmap-firsttime" === $scope.activeMenuItem) && ($scope.activeMenuItem = "heatmap")
} else $scope.activeMenuItem = ""
};
$rootScope.$on("$stateChangeSuccess", function() {
_setMenu()
}), $scope.adminLoginModal = function() {
gaPagesAdminController.loginModal().then(function() {
updateUserSettings(), gaUiNotify.show("admin successfully logged in!", 4e3, "default"), $state.go("admin.home")
})
}, $scope.adminLogout = function() {
gaPagesAdminController.logout().then(function() {
gaServicesUser.getUserData(!0, !0).then(function() {
updateUserSettings(), gaUiNotify.show("admin logged out!", 4e3, "default")
})
})
}, $scope.adminResetImpersonate = function() {
gaPagesAdminController.resetImpersonate().then(function() {
gaServicesUser.getUserData(!1, !0).then(function() {
updateUserSettings(), gaUiNotify.show("user impersonation stopped!", 4e3, "default"), $state.go("admin.search")
})
})
}, _setMenu()
}), angular.module("ga.app.springboard", ["ui.router", "ga.config", "ga.services.user", "ga.services.tracking", "ga.ui.popdown", "ga.api.statuspage"]).controller("gaAppSpringboardController", function($scope, $rootScope, $state, $route, gaConfig, gaServicesTracking, gaServicesUser) {
$scope.menuVisible = !1, $scope.gameId = parseInt($state.params.gameId, 10) || 0, $scope.userCanEditGame = (gaServicesUser.game($scope.gameId) || {}).admin, ($state.includes("game.initialize") || $state.includes("game.content")) && ($scope.homeOnly = !0), $scope.imageBaseUrl = gaConfig.images.baseUrl, $scope.gameId && ($scope.game = gaServicesUser.game($scope.gameId)), $scope.isAdmin = gaServicesUser.admin, $scope.isAdminLoggedIn = gaServicesUser.adminLoggedIn, $scope.isUserImpersonated = $scope.isAdminLoggedIn ? gaServicesUser.userImpersonated : !1, $scope.user = {
name: gaServicesUser.details.name,
studios: gaServicesUser.studios,
activated: gaServicesUser.activated,
admin: gaServicesUser.admin,
adminLoggedIn: gaServicesUser.adminLoggedIn,
impersonated: gaServicesUser.impersonated
}, $scope.$on("userStudiosChange", function() {
$scope.user.studios = gaServicesUser.studios
}), $scope.$on("userDetailsChange", function() {
$scope.user.name = gaServicesUser.details.name, $scope.user.studios = gaServicesUser.studios
}), $scope.$on("currentGameChange", function() {
$scope.user.studios = gaServicesUser.studios, $scope.game = gaServicesUser.game($scope.gameId)
}), $scope.$on("studioChange", function() {
$scope.user.studios = gaServicesUser.studios
}), $scope.logout = function() {
$state.go("public.login", {
logout: ""
}, {
inherit: !1
})
}, $scope.goToFunnel = function() {
gaServicesTracking.addEvent("design", "state.game.funnel", {
value: 1
}, !0).then(function() {
window.location.href = "/game/" + $scope.gameId + "/funnel"
}).catch(function() {
window.location.href = "/game/" + $scope.gameId + "/funnel"
})
}, $scope.goTo = function(gameId) {
$state.includes("game.heatmap") || $state.includes("game.heatmap-nodata") || $state.includes("game.heatmap-firsttime") ? $state.go("game.heatmap", {
gameId: gameId,
state: null
}) : $state.includes("game.explore") ? $state.go("game.explore", {
gameId: gameId,
state: null
}) : $state.includes("game.cohort") ? $state.go("game.cohort", {
gameId: gameId,
state: null
}) : $state.includes("game.querybuilder") ? $state.go("game.querybuilder", {
gameId: gameId,
state: null
}) : isNaN(parseInt($state.params.dashboardId, 10)) ? $state.go("game.dashboards.dashboard", {
action: "show",
gameId: gameId,
dashboardId: $state.params.dashboardId,
state: null
}) : $state.go("game.dashboards.dashboard", {
action: "show",
gameId: gameId,
dashboardId: 0,
state: null
}), $route.reload()
};
var _setMenu = function() {
if ($state.current && $state.current.name) {
var active = $state.current.name.split(".");
$scope.activeMenuItem = active.length > 1 ? active[1] : "", ("heatmap-nodata" === $scope.activeMenuItem || "heatmap-firsttime" === $scope.activeMenuItem) && ($scope.activeMenuItem = "heatmap")
} else $scope.activeMenuItem = ""
};
$rootScope.$on("$stateChangeSuccess", function() {
_setMenu()
}), _setMenu()
}), angular.module("ga.app.headerContent", ["ui.router", "ga.values.user"]).controller("gaAppHeaderContentController", function($scope, $state, gaValuesUser) {
$scope.userLoggedIn = !!gaValuesUser.token, $scope.guides = window.location.host.match("guides.*?"), $state.current.data && $state.current.data.title ? ($scope.title = $state.current.data.title, $scope.link = $state.current.data.link) : ($scope.title = "GameAnalytics guides", $scope.link = "/content/sdk")
}), angular.module("ga.app.footer", ["ui.router"]).controller("gaAppFooterController", function($scope, $rootScope, $timeout, $state) {
$scope.year = (new Date).getFullYear(), $scope.links = [{
title: "About",
href: "http://www.gameanalytics.com/aboutus.html",
target: "_blank"
}, {
title: "Blog",
href: "http://blog.gameanalytics.com/",
target: "_blank"
}, {
title: "Support",
href: "http://support.gameanalytics.com/",
target: "_blank"
}, {
title: "Guides",
href: "/content/sdk"
}], $scope.visible = !1, $rootScope.$on("$stateChangeStart", function() {
$scope.visible = !1
}), $rootScope.$on("$stateChangeSuccess", function() {
"edit" !== $state.params.action && $timeout(function() {
$scope.visible = !0
}, 500)
}), "edit" !== $state.params.action && $timeout(function() {
$scope.visible = !0
}, 500)
}), angular.module("ga.app.partials.toolHeader", ["ga.services.user"]).controller("gaAppPartialsToolHeader", function($scope, gaServicesUser) {
$scope.studios = gaServicesUser.studios, $scope.userDetails = gaServicesUser.details
}), angular.module("ga.api.admin", ["ga.api.userDb", "ga.config", "ga.utils.cache"]).factory("gaApiAdmin", function($rootScope, $location, $injector, $q, $cookieStore, $cookies, gaApiUserDb, gaUtilsCache) {
var adminLogin = function(authKey) {
var url = "admin/login/" + authKey;
return gaApiUserDb.post(url)
},
adminLogout = function() {
var url = "admin/logout";
return gaApiUserDb.post(url)
},
adminResetImpersonate = function() {
var url = "admin/reset_impersonating";
return gaApiUserDb.post(url)
},
impersonateUser = function(userId) {
var url = "admin/users/" + userId + "/impersonate";
return gaApiUserDb.put(url)
},
getUsersBySearch = function(filters, force) {
var url = "admin/users",
cacheString = "adminSearchUsers" + JSON.stringify(filters),
users = force ? null : gaUtilsCache.get(cacheString);
return users && $.when(users), gaApiUserDb.post(url, filters).then(function(results) {
return gaUtilsCache.put(cacheString, results), $.when(results || null)
})
},
getUser = function(userId) {
var url = "admin/users/" + userId;
return gaApiUserDb.get(url).then(function(results) {
return $.when(results[0] || null)
})
},
getStudiosBySearch = function(filters, force) {
var url = "admin/studios",
cacheString = "adminSearchGames" + JSON.stringify(filters),
studios = force ? null : gaUtilsCache.get(cacheString);
return studios && $.when(studios), gaApiUserDb.post(url, filters).then(function(results) {
return gaUtilsCache.put(cacheString, results), $.when(results || null)
})
},
getStudio = function(studioId) {
var url = "admin/studios/" + studioId;
return gaApiUserDb.get(url).then(function(results) {
return $.when(results[0] || null)
})
},
getGamesBySearch = function(filters, force) {
var url = "admin/games",
cacheString = "adminSearchGames" + JSON.stringify(filters),
games = force ? null : gaUtilsCache.get(cacheString);
return games && $.when(games), gaApiUserDb.post(url, filters).then(function(results) {
return gaUtilsCache.put(cacheString, results), $.when(results || null)
})
},
getGame = function(gameId) {
var url = "admin/games/" + gameId;
return gaApiUserDb.get(url).then(function(results) {
return $.when(results[0] || null)
})
},
getUserLogs = function(filters, force) {
var url = "admin/userlogs",
cacheString = "adminToolUserLogs" + JSON.stringify(filters),
usersLogs = force ? null : gaUtilsCache.get(cacheString);
return usersLogs ? $.when(usersLogs) : gaApiUserDb.post(url, filters).then(function(results) {
return gaUtilsCache.put(cacheString, results), $.when(results || null)
})
},
getHomeStats = function(force) {
var url = "admin/all",
cacheString = "adminToolHomeStats",
homeStatsCached = force ? null : gaUtilsCache.get(cacheString);
return homeStatsCached ? $.when(homeStatsCached) : gaApiUserDb.get(url).then(function(results) {
return gaUtilsCache.put(cacheString, results[0]), $.when(results[0] || null)
})
},
getRedshiftStatus = function() {
var url = "admin/redshift/status";
return gaApiUserDb.get(url).then(function(results) {
return $.when(results[0] || null)
})
},
getRedshiftIdentifiers = function(idType, rankCriterion, gameType, targetGameId, targetSimilarGames, countryType, targetCountries) {
var url = "admin/redshift/export?type=" + idType + "&rank=" + rankCriterion + "&gameType=" + gameType + "&targetGameId=" + targetGameId + "&targetSimilarGames=" + targetSimilarGames + "&countryType=" + countryType + "&targetCountries=" + targetCountries;
return gaApiUserDb.get(url).then(function(results) {
return $.when(results[0] || null)
})
},
getSimilarityScoresForGame = function(targetGameId) {
var url = "admin/redshift/similarity/" + targetGameId;
return gaApiUserDb.get(url).then(function(results) {
return $.when(results || null)
})
},
changeUserEmail = function(userId, email) {
var emailData = {
email: email
},
url = "admin/users/" + userId + "/email";
return gaApiUserDb.put(url, emailData)
},
changeStudioOwner = function(studioId, userId) {
var userIdData = {
user_id: userId
},
url = "admin/studios/" + studioId + "/owner";
return gaApiUserDb.put(url, userIdData)
};
return {
adminLogin: adminLogin,
adminLogout: adminLogout,
adminResetImpersonate: adminResetImpersonate,
impersonateUser: impersonateUser,
getUsersBySearch: getUsersBySearch,
getUser: getUser,
getStudiosBySearch: getStudiosBySearch,
getStudio: getStudio,
getGamesBySearch: getGamesBySearch,
getGame: getGame,
getUserLogs: getUserLogs,
getHomeStats: getHomeStats,
getRedshiftStatus: getRedshiftStatus,
getRedshiftIdentifiers: getRedshiftIdentifiers,
getSimilarityScoresForGame: getSimilarityScoresForGame,
changeUserEmail: changeUserEmail,
changeStudioOwner: changeStudioOwner
}
}), angular.module("ga.api.data", ["ga.config", "ga.utils.date", "ga.utils.cache", "ga.api.meta", "ga.values.user", "ga.utils.transform", "ga.utils.urlState", "ga.services.tracking"]).factory("gaApiData", function($window, $rootScope, $http, $q, $state, $timeout, gaUtilsCache, gaValuesUser, gaConfig, gaApiMeta, gaUtilsTransform, gaUtilsDate, gaServicesTracking) {
var requestPromises = {},
get = function(requestQuery, UID, sortArray) {
var deferred = $q.defer();
if ("daily" === requestQuery.rollup && delete requestQuery.rollup, !gaValuesUser.token) return $q.reject({
error: "no_user"
});
if (!requestQuery.gameId && !$state.params.gameId) return $q.reject({
error: "no_game"
});
if (!requestQuery.metric || angular.isArray(requestQuery.metric) && !requestQuery.metric.length) return $q.reject({
error: "no_metric"
});
if ("dimension" === requestQuery.group && !requestQuery.filter) return $q.reject({
error: "group_dimension_with_no_filter"
});
if (angular.isArray(requestQuery.metric) || (requestQuery.metric = [requestQuery.metric]), "Virtual" === requestQuery.metric[0].currency && !requestQuery.isVirtualCurrency) return _getVirtualCurrencies(requestQuery).then(function(virtualCurrencies) {
0 === virtualCurrencies.length && (virtualCurrencies = ["null"]);
var newMetrics = [];
return angular.forEach(virtualCurrencies, function(currency) {
var metric = angular.copy(requestQuery.metric[0]);
metric.currency = currency, newMetrics.push(metric)
}), requestQuery.isVirtualCurrency = !0, requestQuery.metric = newMetrics, get(requestQuery, UID, sortArray).then(function(response) {
return $q.when(response)
})
});
requestQuery.compareInterval === !0 && (requestQuery.compareInterval = gaUtilsDate.getPreviousPeriod(requestQuery.interval)), requestQuery.mergeCompare = requestQuery.mergeCompare !== !1, requestQuery.filter && angular.isString(requestQuery.filter.dimension) && requestQuery.filter.dimension.indexOf("cohort_") > -1 && (requestQuery.compareInterval = null, "dimension" === requestQuery.group && (requestQuery.group = "time", requestQuery.normalized = !0));
var request = createQueryObject(requestQuery),
response = function(fromCache) {
if (request.mainQuery.isVirtualCurrency && angular.forEach(request.queries, function(query, index) {
var newResponse = {},
type = request.response[index].timeseries ? "timeseries" : "aggregated";
newResponse[type] = {}, angular.forEach(request.response[index][type], function(data, event) {
newResponse[type][event + " - " + query.metric.currency] = data
}), request.response[index] = newResponse
}), request.mainQuery.noParse) deferred.resolve(request);
else {
var newParsedPromise = gaUtilsTransform.parse(request, null, sortArray);
newParsedPromise.then(function(parsed) {
fromCache === !0 && (parsed.fromCache = !0), request.mainQuery.returnAll ? (request.parsed = parsed, deferred.resolve(request)) : deferred.resolve(parsed)
})
}
};
if (request.response = request.mainQuery.noCache ? [] : gaUtilsCache.get(request.hash) || [], request.response.length) response(!0);
else {
UID && (removeUID(UID), requestPromises[UID] = $q.defer());
var get_config = {
headers: {
Authorization: gaValuesUser.gameToken(request.mainQuery.gameId)
},
timeout: requestPromises[UID] ? requestPromises[UID].promise : null
},
successCallback = function(collection) {
removeUID(UID);
var hasError = !1;
collection.data.forEach(function(data) {
request.response.push(data.body), 200 !== data.code && (hasError = !0)
}), hasError ? (request.mainQuery.error = !0, deferred.reject(request)) : (request.mainQuery.realtime || gaUtilsCache.put(request.hash, request.response), response(request.response))
},
errorCallback = function(response) {
removeUID(UID), deferred.reject(response.data)
};
if (1 === request.mainQuery.metric.length && "null" === request.mainQuery.metric[0].category) {
var event = request.mainQuery.metric[0].event,
responseData = [{
body: {
timeseries: {}
}
}, {
body: {
aggregated: {}
}
}];
responseData[0].body.timeseries[event] = [];
var interval = gaUtilsDate.getInterval(request.mainQuery.interval.start, request.mainQuery.interval.end, 864e5, 0);
angular.forEach(interval, function(timestamp) {
responseData[0].body.timeseries[event].push({
timestamp: timestamp / 1e3
})
}), responseData[1].body.aggregated[event] = {}, successCallback({
data: responseData
})
} else {
var baseUrl = gaConfig.getUrl(!0, request.mainQuery.realtime),
time = (new Date).getTime();
$http.post(baseUrl + request.mainQuery.gameId, request.urls, get_config).then(function(response) {
return time = (new Date).getTime() - time, "core" === request.mainQuery.metric[0].category || "operations" === request.mainQuery.metric[0].category ? gaServicesTracking.addEvent("design", "perf:data-api:batch:" + request.mainQuery.metric[0].category + ":load-time-ms", {
value: time
}) : gaServicesTracking.addEvent("design", "perf:data-api:batch:" + request.mainQuery.metric[0].category + ":load-time-ms", {
value: time
}), $q.when(response)
}).then(successCallback, errorCallback)
}
}
return deferred.promise
},
createQueryObject = function(requestQuery) {
var request = {
mainQuery: {},
queries: [],
urls: [],
response: []
};
return request.mainQuery = angular.copy(requestQuery), request.mainQuery.top = angular.isNumber(request.mainQuery.top) ? request.mainQuery.top : 15, request.mainQuery.aggregation = request.mainQuery.aggregation || "mean", request.mainQuery.aggregation_aggregated = request.mainQuery.aggregation || "mean", request.mainQuery.group = request.mainQuery.group || "time", request.mainQuery.gameId = request.mainQuery.gameId || $state.params.gameId, request.mainQuery.metric = angular.isArray(request.mainQuery.metric) ? request.mainQuery.metric : [request.mainQuery.metric], 1 === request.mainQuery.metric.length && "null" === request.mainQuery.metric[0].category && (request.mainQuery.compareInterval = null), angular.forEach(request.mainQuery.metric, function(metric) {
var query;
"retention" !== metric.event && "retention_full" !== metric.event || "core" !== metric.category ? "returning_users" !== metric.event && "returning_users_full" !== metric.event || "core" !== metric.category ? (query = angular.copy(request.mainQuery), query.metric = metric, request.queries.push(query)) : angular.forEach(["returning_users_1", "returning_users_2", "returning_users_3", "returning_users_4", "returning_users_5", "returning_users_6", "returning_users_7", "returning_users_14", "returning_users_28", , "returning_users_30", "returning_users_90"], function(event) {
query = angular.copy(request.mainQuery), query.metric = angular.copy(metric), query.metric.event = event, request.queries.push(query)
}) : angular.forEach(["retention_1", "retention_2", "retention_3", "retention_4", "retention_5", "retention_6", "retention_7", "retention_14", "retention_28", "retention_30", "retention_90"], function(event) {
query = angular.copy(request.mainQuery), query.metric = angular.copy(metric), query.metric.event = event, request.queries.push(query)
})
}), angular.forEach(request.queries, function(q) {
if (q.compareInterval) {
var compareQuery = angular.copy(q);
compareQuery.isCompare = !0, compareQuery.interval = compareQuery.compareInterval, delete compareQuery.compareInterval, request.queries.push(compareQuery), delete q.compareInterval
}
}), angular.forEach(request.queries, function(q) {
if ("time" === q.group && !q.noAggregation) {
delete q.includeAggregated;
var aggregatedQuery = angular.copy(q);
if (aggregatedQuery.group = "dimension", q.showSum && "mean" === aggregatedQuery.aggregation) {
var meta = gaApiMeta.getMetric(aggregatedQuery.metric.category, aggregatedQuery.metric.event);
meta.forceTotal && (meta.available_aggregations && meta.available_aggregations.indexOf("sum") > -1 ? (aggregatedQuery.aggregation = "sum", request.mainQuery.aggregation_aggregated = "sum") : meta.available_aggregations && meta.available_aggregations.indexOf("event_count") > -1 && (aggregatedQuery.aggregation = "event_count", request.mainQuery.aggregation_aggregated = "event_count"))
}
request.queries.push(aggregatedQuery)
}
}), request.urls = request.queries.map(function(q) {
return makeRequestURL(q, !1)
}), request.hash = btoa(request.urls.join(";")), request
},
makeRequestURL = function(query, includeDomain, onlyMetric) {
includeDomain = void 0 === includeDomain ? !0 : includeDomain;
var gameId = query.gameId || $state.params.gameId,
metric = angular.isArray(query.metric) ? query.metric[0] : query.metric,
meta = gaApiMeta.getMetric(metric),
requestURL = "";
requestURL = onlyMetric ? "/" + metric.category + "/" + $window.encodeURIComponent(meta.alias || metric.event) : includeDomain ? gaConfig.getUrl(!1, !1) + gameId + "/" + metric.category + "/" + $window.encodeURIComponent(meta.alias || metric.event) : gaConfig.data.basePath + gameId + "/" + metric.category + "/" + $window.encodeURIComponent(meta.alias || metric.event);
var queryParams = [];
return queryParams.push("start=" + query.interval.start / 1e3), queryParams.push("end=" + query.interval.end / 1e3), query.filter && queryParams.push(query.filter.dimension + "=" + query.filter.values.map($window.encodeURIComponent).join(",")), query.metric.currency && queryParams.push("currency=" + $window.encodeURIComponent(query.isVirtualCurrency ? "USD" : query.metric.currency)), query.aggregation && "time" !== query.group && queryParams.push("aggregation=" + $window.encodeURIComponent(query.aggregation)), query.normalized === !0 && "time" === query.group && queryParams.push("normalized=true"), query.aggregation && "time" !== query.group || meta.ignore_rollup || query.rollup && queryParams.push("rollup=" + $window.encodeURIComponent(query.rollup)), requestURL + "?" + queryParams.join("&")
},
removeUID = function(UID) {
requestPromises[UID] && (requestPromises[UID].resolve(), delete requestPromises[UID])
},
_getVirtualCurrencies = function(originalQuery) {
var query = angular.copy(originalQuery);
query.isVirtualCurrency = !0, query.group = "dimension";
var url = makeRequestURL(query, !1, !0);
return getValue(url).then(function(data) {
var currencies = [];
return angular.forEach(data.aggregated, function(data) {
angular.forEach(data.total, function(value, currency) {
"USD" !== currency && currencies.indexOf(currency) < 0 && currencies.push(currency)
})
}), $q.when(currencies)
})
},
getQuality = function(options, UID) {
UID && (removeUID(UID), requestPromises[UID] = $q.defer()), options.gameId = options.gameId || $state.params.gameId;
var get_config = {
headers: {
Authorization: gaValuesUser.gameToken(options.gameId)
},
timeout: requestPromises[UID] ? requestPromises[UID].promise : null
},
requestURLs = [],
category = options.realtime ? "operations" : "error";
angular.forEach(options.levels, function(level) {
requestURLs.push("/v1/games/" + options.gameId + "/" + category + "/stacktrace:" + level + "?" + (options.timeseries ? "" : "aggregation=sum&") + (options.filter && options.filter.dimension ? options.filter.dimension + "=" + options.filter.values.map(window.escape).join(",") + "&" : "") + "start=" + options.interval.start / 1e3 + "&end=" + options.interval.end / 1e3)
});
var baseUrl = gaConfig.getUrl(!0, options.realtime),
time = (new Date).getTime();
return $http.post(baseUrl + (options.gameId || $state.params.gameId), requestURLs, get_config).then(function(response) {
return time = (new Date).getTime() - time, gaServicesTracking.addEvent("design", "perf:data-api:batch:" + category + ":stacktrace:load-time-ms", {
value: time
}), $q.when(response)
}).then(function(response) {
return $q.when(qualityParse(response.data, options))
})
},
qualityParse = function(data, options) {
var parsed = [];
return angular.forEach(data, function(response, index) {
var level = options.levels[index],
dimData = {};
response.body.aggregated && response.body.aggregated["stacktrace:" + level] && (options.filter && options.filter.dimension ? response.body.aggregated["stacktrace:" + level].dimensions && response.body.aggregated["stacktrace:" + level].dimensions[options.filter.dimension] && (dimData = response.body.aggregated["stacktrace:" + level].dimensions[options.filter.dimension]) : dimData[0] = response.body.aggregated["stacktrace:" + level].total, angular.forEach(dimData, function(data, dimension) {
angular.forEach(data, function(occurence) {
var item = {
level: level,
key: occurence.key,
name: occurence.value,
dimension: options.filter && options.filter.dimension || null,
dimensionValue: dimension || null,
eventCount: occurence.count
};
parsed.push(item)
})
}))
}), options.eventPattern && (parsed = parsed.filter(function(a) {
return a.name.match(options.eventPattern)
})), parsed
},
getValue = function(url, gameId, noCache) {
gameId = gameId || $state.params.gameId;
var realtime = url.indexOf("operations/") > -1,
baseUrl = gaConfig.getUrl(!1, realtime),
requestURL = baseUrl + gameId + url,
get_config = {
headers: {
Authorization: gaValuesUser.gameToken(gameId)
}
},
cache = noCache ? null : gaUtilsCache.get(requestURL);
return cache ? $q.when(cache) : $http.get(requestURL, get_config).then(function(response) {
return !noCache && gaUtilsCache.put(requestURL, response.data), $q.when(response.data)
})
},
getDataDownloadLinks = function(gameId, interval) {
var requestURL = gaConfig.getUrl(!1, !1) + gameId + "/export",
queryString = "?" + ["start=" + interval.start / 1e3, "end=" + interval.end / 1e3].join("&");
requestURL += queryString;
var getConfig = {
headers: {
Authorization: gaValuesUser.gameToken(gameId)
}
};
return $http.get(requestURL, getConfig).then(function(response) {
return $q.when(response.data)
})
},
getFunnels = function(gameId) {
var requestURL = gaConfig.getUrl(!1, !1) + gameId + "/funnels",
getConfig = {
headers: {
Authorization: gaValuesUser.gameToken(gameId)
}
};
return $http.get(requestURL, getConfig).then(function(response) {
return $q.when(response.data)
})
};
return {
getQuality: getQuality,
getValue: getValue,
get: get,
removeUID: removeUID,
makeRequestURL: makeRequestURL,
getDataDownloadLinks: getDataDownloadLinks,
getFunnels: getFunnels
}
}), angular.module("ga.api.data.funnel", ["ga.config", "ga.values.user", "ga.utils.transform"]).service("gaApiDataFunnel", function($http, $q, $state, gaValuesUser, gaConfig, gaUtilsTransform) {
this.getList = function(gameId) {
var requestURL = gaConfig.getUrl(!1, !1, !0) + gameId + "/funnels";
return $http.get(requestURL, headerConfig(gameId)).then(function(response) {
return $q.when(response.data)
})
}, this.getFunnel = function(gameId, funnel, dateRangeId) {
if (!funnel.dateRanges || !funnel.dateRanges.length || !dateRangeId) return $q.reject();
var backendId = null;
if (funnel.dateRanges.some(function(dr) {
return parseInt(dateRangeId) === dr.id ? dr.status && "processed" === dr.status.id && !dr.status.hasData ? (backendId = "__empty__", !0) : (backendId = dr.backendId, !0) : void 0
}), !backendId || backendId && "__failed__" === backendId) return $q.reject();
if ("__empty__" === backendId) return $q.when(gaUtilsTransform.parseEmptyFunnelData(funnel));
var requestURL = gaConfig.getUrl(!1, !1, !0) + gameId + "/funnels/" + backendId;
return $http.get(requestURL, headerConfig(gameId)).then(function(response) {
return gaUtilsTransform.parseFunnelData(response.data, funnel)
})
}, this.processFunnel = function(gameId, email, options) {
var payload = {
start: options.start / 1e3,
end: options.end / 1e3,
events: options.events,
dimensions: options.dimensions,
email: email,
funnel_title: options.name
},
requestURL = gaConfig.getUrl(!1, !1, !0) + gameId + "/funnels";
return $http.post(requestURL, payload, headerConfig(gameId)).then(function(response) {
return $q.when(response.data)
})
};
var headerConfig = function(gameId) {
return {
headers: {
Authorization: gaValuesUser.gameToken(gameId)
}
}
}
}), angular.module("ga.api.meta", ["ga.filters.numberFormat", "ga.utils.date", "ga.components.moment", "ga.values.user"]).filter("formatUnitType", function($rootScope, $filter, gaApiMeta, gaUtilsDate, moment, gaValuesUser) {
var gaNumberFormat = $filter("gaNumberFormat");
return function(input, type, unit, defaultValue, noFix, noShorten) {
var isNumber = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n)
},
currency = gaValuesUser.settings.currencyDefault,
ignoreFixUnit = !1;
if (angular.isObject(type) && (noShorten = noFix, noFix = defaultValue, defaultValue = unit, unit = type.unit, currency = type.currency || gaValuesUser.settings.currencyDefault, type = type.type), void 0 === input) return "";
defaultValue = defaultValue || "", "money" === unit ? (currency = gaApiMeta.getCurrency(currency), unit = {
preUnit: currency.symbol
}) : unit = gaApiMeta.getUnit(unit || "_default"), unit.postFix && unit.postUnit && !noFix && (ignoreFixUnit = !0);
var filtered = defaultValue,
timezone = gaValuesUser.settings.timeZone;
if (null !== input && void 0 !== input) switch (type) {
case "percent":
isNaN(input) || (filtered = gaNumberFormat(100 * input));
break;
case "number":
filtered = noShorten ? gaNumberFormat(input, !0) : gaNumberFormat(input);
break;
case "second":
filtered = gaNumberFormat(input, !0);
break;
case "seconds":
input = Math.round(input);
var secs = 0,
mins = 0,
hours = 0,
days = 0;
60 > input ? filtered = input + "s." : 3600 > input ? (secs = input % 60, mins = Math.floor((input - secs) / 60), filtered = mins + "m. " + secs + "s.") : 172800 > input ? (hours = Math.floor(input / 3600), mins = Math.round((input - 60 * hours * 60) / 60), filtered = hours + "h. " + mins + "m.") : (days = Math.floor(input / 86400), hours = Math.round((input - 60 * days * 60 * 24) / 3600), filtered = days + "d. " + hours + "h.");
break;
case "money":
filtered = $filter("gaNumberFormat")(input);
break;
case "date":
filtered = isNumber(input) ? moment.utc(parseInt(input, 10)).format(gaUtilsDate.getCurrentDateFormatString()) : input;
break;
case "time":
filtered = isNumber(input) ? moment.utc(parseInt(input, 10)).tz(timezone).format(gaUtilsDate.getCurrentTimeFormat()) : input;
break;
case "datetime":
filtered = isNumber(input) ? moment.utc(parseInt(input, 10)).tz(timezone).format(gaUtilsDate.getCurrentDateFormatString() + " " + gaUtilsDate.getCurrentTimeFormat()) : input;
break;
case "timedate":
filtered = isNumber(input) ? moment.utc(parseInt(input, 10)).tz(timezone).format(gaUtilsDate.getCurrentTimeFormat() + " (" + gaUtilsDate.getCurrentDateFormatString() + ")") : input;
break;
case "unixdate":
filtered = isNumber(input) ? moment.utc(1e3 * parseInt(input, 10)).format(gaUtilsDate.getCurrentDateFormatString()) : input;
break;
case "unixdatetime":
filtered = isNumber(input) ? moment.utc(1e3 * parseInt(input, 10)).tz(timezone).format(gaUtilsDate.getCurrentDateFormatString() + " " + gaUtilsDate.getCurrentTimeFormat()) : input;
break;
case "rawdate":
filtered = isNumber(input) ? moment.utc(parseInt(input, 10)).format("YYYY-MM-DD") : input;
break;
case "rawdatetime":
filtered = isNumber(input) ? moment.utc(parseInt(input, 10)).format("YYYY-MM-DD HH:mm:ss") : input;
break;
case "session_length":
var tmp = input.split("-");
filtered = 2 === tmp.length ? tmp[1] ? parseInt(tmp[0], 10) / 60 + " - " + parseInt(tmp[1], 10) / 60 + " min" + ("60" === tmp[1] ? "." : "s.") : "> " + parseInt(tmp[0], 10) / 60 + " mins." : input;
break;
case "session_count":
filtered = input + " session" + ("1" === input || 1 === input ? "" : "s");
break;
case "dimension":
filtered = gaApiMeta.getDimension(input).title;
break;
case "country":
filtered = gaApiMeta.getCountry(input);
break;
case "cohort_week":
input = parseInt(input, 10);
var week = Math.floor(input / 7);
filtered = "Week " + week + " - day " + (input % 7 + 1);
break;
case "cohort_week_weekly":
input = parseInt(input, 10), filtered = "Week " + input;
break;
case "cohort_month":
input = parseInt(input, 10);
var month = Math.floor(input / 28);
filtered = "Month " + month + " - day " + (input % 28 + 1);
break;
case "cohort_month_monthly":
input = parseInt(input, 10), filtered = "Month " + input;
break;
default:
filtered = angular.isNumber(input) ? $filter("gaNumberFormat")(input) : input
}
return filtered === defaultValue ? filtered : (noFix ? "" : unit.preFix || "") + (unit.preUnit || "") + filtered + (ignoreFixUnit ? "" : unit.postUnit || "") + (noFix ? "" : unit.postFix || "")
}
}).filter("formatMetricNameSingle", function(gaApiMeta) {
return gaApiMeta.getMetricDisplay
}).factory("gaApiMeta", function() {
var dimensions = {
area: {
title: "Area"
},
build: {
title: "Build"
},
cohort: {
title: "Cohort",
children: !0
},
cohort_month: {
title: "Month",
parent: "cohort",
type: "unixdate"
},
cohort_week: {
title: "Week",
parent: "cohort",
type: "unixdate"
},
country: {
title: "Country",
type: "country"
},
install_ad: {
title: "Ad",
parent: "acquisition"
},
install_adgroup: {
title: "Ad Group",
parent: "acquisition"
},
install_campaign: {
title: "Campaign",
parent: "acquisition"
},
install_keyword: {
title: "Keyword",
parent: "acquisition"
},
install_publisher: {
title: "Publisher",
parent: "acquisition"
},
install_site: {
title: "Site",
parent: "acquisition"
},
is_paying: {
title: "Paying Users"
},
acquisition: {
title: "Acquisition",
children: !0
},
origin: {
title: "Source",
parent: "acquisition"
},
platform: {
title: "Platform"
},
device: {
title: "Device"
},
os_version: {
title: "OS Version"
},
_default: {
type: "dimension",
isDefault: !0
},
paying: {
title: "Paying"
},
not_paying: {
title: "Non-Paying"
},
organic: {
title: "Organic"
},
severity: {
title: "Severity",
hidden: !0,
values: {
critical: {
title: "Critical"
},
error: {
title: "Error"
},
warning: {
title: "Warning"
}
}
},
sdk_version: {
title: "SDK Version",
hidden: !0
},
category: {
title: "Category",
hidden: !0
}
},
getDimension = function(dimension, value) {
var meta = angular.copy(dimensions[dimension] || dimensions._default);
return meta.type = meta.type || "dimension", meta.name = dimension, meta.unit = dimension, meta.title = meta.title || dimension, meta.hidden = meta.hidden || !1, meta.parent && (meta.parent = getDimension(meta.parent)), value && (meta.title = meta.values[value] && meta.values[value].title || value), meta
},
units = {
user: {
title: "Users",
postUnit: "",
postFix: " Users"
},
session: {
title: "Sessions",
postUnit: "",
postFix: " Sessions"
},
install: {
title: "Installs",
postUnit: "",
postFix: " Installs"
},
event: {
title: "Events",
postUnit: "",
postFix: " Events"
},
money: {
title: "Money",
preUnit: "$"
},
percent: {
title: "Percent",
postUnit: "%",
postFix: "%"
},
second: {
title: "Seconds",
postUnit: "s",
postFix: " seconds"
},
minute: {
title: "Minutes",
postUnit: "m",
postFix: " minutes"
},
hour: {
title: "Hours",
postUnit: "h",
postFix: " hours"
},
transaction: {
postFix: " Transactions"
},
FPS: {
title: "FPS",
postFix: " FPS"
},
_default: {
title: "",
isDefault: !0
}
},
getUnit = function(unit) {
return units[unit] || units._default
},
meta = {
operations: {
returning_users: {
title: "Returning users",
description: "",
aggregation: ["mean", "sum"],
axisY: {
unit: "user",
type: "number"
}
},
installs: {
title: "New users",
description: "",
aggregation: ["mean", "sum"],
axisY: {
unit: "user",
type: "number"
}
},
revenue: {
title: "Revenue",
description: "",
aggregation: ["mean", "sum"],
currency: !0,
axisY: {
unit: "money",
type: "number"
}
},
converting_users: {
title: "Conversion to paying",
description: "",
aggregation: ["mean", "sum"],
axisY: {
unit: "user",
type: "number"
}
},
error_count: {
title: "Error Events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
type: "number"
}
},
concurrent_users: {
title: "Concurrent Users",
description: "",
aggregation: ["sum"],
axisY: {
unit: "user",
type: "number"
}
},
event_count: {
title: "Incoming Events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
type: "number"
}
},
rejected_events: {
title: "Rejected Events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
type: "number"
}
},
session_count: {
title: "Active Sessions",
description: "",
aggregation: ["sum"],
axisY: {
unit: "session",
type: "number"
}
},
_default: {
title: "",
description: "",
aggregation: ["mean", "sum"],
axisY: {
unit: "none",
type: "number"
}
}
},
core: {
ARPDAU: {
title: "ARPDAU",
description: "Average revenue per daily active user",
aggregation: ["mean"],
available_aggregations: ["mean"],
currency: !0,
axisY: {
type: "number",
unit: "money"
}
},
ARPPU: {
title: "ARPPU",
description: "Average revenue per paying user",
aggregation: ["mean"],
available_aggregations: ["mean"],
currency: !0,
axisY: {
type: "number",
unit: "money"
}
},
DAU: {
title: "DAU",
description: "Unique number of users in a day",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
MAU: {
title: "MAU",
description: "Unique number of monthly users",
aggregation: ["mean"],
available_aggregations: ["mean"],
ignore_rollup: !0,
axisY: {
unit: "user",
type: "number"
}
},
WAU: {
title: "WAU",
description: "Unique number of weekly users",
aggregation: ["mean"],
available_aggregations: ["mean"],
ignore_rollup: !0,
axisY: {
unit: "user",
type: "number"
}
},
churn_28: {
title: "Churn",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
converting_users: {
title: "Conversion to paying",
description: "First time purchase",
aggregation: ["mean", "sum"],
available_aggregations: ["mean", "sum"],
axisY: {
type: "number",
unit: "user"
}
},
conversion: {
title: "Conversion rate",
description: "First time purchase",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
type: "percent",
unit: "percent"
}
},
event_count: {
title: "Event count",
description: "",
aggregation: ["event_count"],
available_aggregations: ["event_count"],
axisY: {
unit: "event",
type: "number"
}
},
installs: {
title: "New users",
description: "",
aggregation: ["mean", "sum"],
available_aggregations: ["mean", "sum"],
axisY: {
unit: "user",
type: "number"
}
},
paying_users: {
title: "Paying Users",
description: "Number of paying users of DAU",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_week: {
title: "Returning users (first week)",
description: "",
aggregation: ["mean", "sum"],
available_aggregations: ["mean", "sum"],
groupValues: !0,
groupValuesUnit: "",
axisY: {
unit: "user",
type: "number"
}
},
returning_users: {
title: "Returning users",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
groupValues: !0,
groupValuesUnit: "",
axisY: {
unit: "user",
type: "number"
}
},
returning_users_1: {
parent: "returning_users",
title: "Day 1",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_2: {
parent: "returning_users",
title: "Day 2",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_3: {
parent: "returning_users",
title: "Day 3",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_4: {
parent: "returning_users",
title: "Day 4",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_5: {
parent: "returning_users",
title: "Day 5",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_6: {
parent: "returning_users",
title: "Day 6",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_7: {
parent: "returning_users",
title: "Day 7",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_14: {
parent: "returning_users",
title: "Day 14",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_28: {
parent: "returning_users",
title: "Day 28",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_30: {
parent: "returning_users",
title: "Day 30",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
returning_users_90: {
parent: "returning_users",
title: "Day 90",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
retention: {
title: "Retention",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
groupValues: !0,
groupValuesUnit: "",
axisY: {
unit: "percent",
type: "percent"
}
},
retention_1: {
parent: "retention",
title: "Day 1",
description: "",
aggregation: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_2: {
parent: "retention",
title: "Day 2",
description: "",
aggregation: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_3: {
parent: "retention",
title: "Day 3",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_4: {
parent: "retention",
title: "Day 4",
description: "",
aggregation: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_5: {
parent: "retention",
title: "Day 5",
description: "",
aggregation: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_6: {
parent: "retention",
title: "Day 6",
description: "",
aggregation: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_7: {
parent: "retention",
title: "Day 7",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_10: {
parent: "retention",
title: "Day 10",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_11: {
parent: "retention",
title: "Day 11",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_12: {
parent: "retention",
title: "Day 12",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_13: {
parent: "retention",
title: "Day 13",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_14: {
parent: "retention",
title: "Day 14",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_15: {
parent: "retention",
title: "Day 15",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_16: {
parent: "retention",
title: "Day 16",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_17: {
parent: "retention",
title: "Day 17",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_18: {
parent: "retention",
title: "Day 18",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_19: {
parent: "retention",
title: "Day 19",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_20: {
parent: "retention",
title: "Day 20",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_21: {
parent: "retention",
title: "Day 21",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_22: {
parent: "retention",
title: "Day 22",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_23: {
parent: "retention",
title: "Day 23",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_24: {
parent: "retention",
title: "Day 24",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_25: {
parent: "retention",
title: "Day 25",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_26: {
parent: "retention",
title: "Day 26",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_27: {
parent: "retention",
title: "Day 27",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_28: {
parent: "retention",
title: "Day 28",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_29: {
parent: "retention",
title: "Day 29",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_30: {
parent: "retention",
title: "Day 30",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_90: {
parent: "retention",
title: "Day 90",
description: "",
aggregation: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
retention_120: {
parent: "retention",
title: "Day 120",
description: "",
aggregation: ["mean"],
axisY: {
unit: "percent",
type: "percent"
}
},
returning: {
title: "Returning users",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "user",
type: "number"
}
},
transactions: {
title: "Transactions",
eventCountTitle: "Transactions",
alias: "revenue",
description: "",
aggregation: ["event_count"],
available_aggregations: ["event_count"],
currency: !0,
axisY: {
unit: "transaction",
type: "number"
},
eventCountUnit: {
unit: "transaction",
type: "number"
}
},
revenue: {
title: "Revenue per transaction",
description: "",
aggregation: ["mean", "sum"],
available_aggregations: ["mean", "sum"],
currency: !0,
axisY: {
unit: "money",
type: "number"
}
},
stickiness: {
title: "DAU/MAU Ratio",
available_aggregations: ["mean"],
description: "",
aggregation: [],
axisY: {
unit: "percent",
type: "percent"
}
},
session_length: {
title: "Session length",
description: "",
aggregation: ["histogram"],
available_aggregations: ["mean", "sum"],
groupValues: !0,
groupTime: !1,
histogram: !0,
noSort: !0,
keyUnit: "session_length",
axisY: {
unit: "session",
unitTitle: "Sessions",
type: "number"
}
},
session_length_mean: {
title: "Avg. session length",
description: "",
aggregation: ["mean"],
available_aggregations: ["mean"],
axisY: {
unit: "second",
unitTitle: "Seconds",
type: "second"
}
},
session_count: {
title: "Session count",
description: "",
aggregation: ["histogram"],
available_aggregations: ["mean", "sum"],
groupValues: !0,
groupTime: !1,
histogram: !0,
noSort: !0,
keyUnit: "session_count",
axisY: {
unit: "user",
unitTitle: "Users",
type: "number"
}
},
session_count_sum: {
hidden: !0,
title: "Session count SUM",
description: "",
aggregation: ["sum"],
axisY: {
unit: "session",
type: "number"
}
},
session_count_mean: {
title: "Session count mean",
description: "",
aggregation: ["mean"],
axisY: {
unit: "session",
type: "number"
}
},
_default: {
title: "",
description: "",
aggregation: [],
axisY: {
unit: "event",
type: "number"
},
isDefault: !0
}
},
design: {
_default: {
axisY: {
unit: "none",
type: "number"
},
aggregation: ["mean", "sum", "event_count"],
available_aggregations: ["mean", "sum", "event_count"]
},
"GA:AverageFPS": {
title: "Average FPS",
eventCountTitle: "Average FPS Events",
axisY: {
unit: "FPS",
unitTitle: "FPS",
type: "number"
},
aggregation: ["mean", "event_count"],
available_aggregations: ["mean", "event_count"]
},
"GA:CriticalFPS": {
title: "Critical FPS Events",
axisY: {
unit: "none",
type: "number"
},
aggregation: ["event_count"],
available_aggregations: ["event_count"]
}
},
business: {
_default: {
axisY: {
unit: "money",
type: "number"
},
aggregation: ["mean", "sum", "event_count"],
available_aggregations: ["mean", "sum", "event_count"],
currency: !0
}
},
quality: {
_default: {
axisY: {
unit: "none",
type: "number"
},
aggregation: ["mean", "sum", "event_count"]
}
},
error: {
_default: {
aggregation: ["mean", "sum", "event_count"],
axisY: {
unit: "none",
type: "number"
}
},
"users_affected_by:.*": {
title: "Users affected by Error events",
description: "",
aggregation: ["sum"],
axisY: {
unit: "user",
unitTitle: "Users",
type: "number"
}
},
"event_count_by:.*": {
title: "Error events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
unitTitle: "Events",
type: "number"
}
},
users_affected_by: {
title: "Users affected by Error events",
description: "",
aggregation: ["sum"],
axisY: {
unit: "user",
unitTitle: "Users",
type: "number"
}
},
event_count_by: {
title: "Error events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
unitTitle: "Events",
type: "number"
}
},
"users_affected_by:critical": {
title: "Users affected by critical",
description: "",
aggregation: ["sum"],
axisY: {
unit: "user",
unitTitle: "Users",
type: "number"
}
},
"users_affected_by:error": {
title: "Users affected by error",
description: "",
aggregation: ["sum"],
axisY: {
unit: "user",
unitTitle: "Users",
type: "number"
}
},
"users_affected_by:warning": {
title: "Users affected by warning",
description: "",
aggregation: ["sum"],
axisY: {
unit: "user",
unitTitle: "Users",
type: "number"
}
},
"event_count_by:critical": {
title: "Critical events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
unitTitle: "Events",
type: "number"
}
},
"event_count_by:error": {
title: "Error events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
unitTitle: "Events",
type: "number"
}
},
"event_count_by:warning": {
title: "Warning events",
description: "",
aggregation: ["event_count"],
axisY: {
unit: "event",
unitTitle: "Events",
type: "number"
}
}
},
_default: {
axisY: {
unit: "none",
type: "number"
}
}
},
parseMeta = function(fMeta, metric, dimension) {
return fMeta = angular.copy(fMeta), fMeta.title || (fMeta.title = metric.split(":").pop()), dimension && (fMeta.title = dimension), fMeta.displayTitle = fMeta.title.split(":").pop(), ".*" === fMeta.displayTitle && (fMeta.displayTitle = metric.split(":").slice(-2)[0] + ":" + fMeta.displayTitle), fMeta
},
getMetricArray = function(category, event, getAll) {
var selectionArray = [],
events = [];
if (event) events = event.split(":");
else {
var tmp = category.split(":");
category = tmp[0].toLowerCase(), events = tmp.slice(1)
}!getAll && events.pop(), selectionArray.push({
value: category.charAt(0).toUpperCase() + category.slice(1),
title: category.charAt(0).toUpperCase() + category.slice(1)
});
for (var i = 0; i < events.length; i++) selectionArray.push({
value: events[i],
title: getMetric(category, events.slice(0, i + 1).join(":")).title
});
return selectionArray
},
getMetricDisplay = function(category, event, singleEvent) {
var events = [];
if (category) {
if (angular.isObject(category) && (singleEvent = event, event = category.event, category = category.category), event) events = event.split(":");
else {
var tmp = category.split(":");
category = tmp[0].toLowerCase(), events = tmp.slice(1)
}
var eventsDisplay = angular.copy(events);
if (singleEvent) {
var dis = "";
return dis = ".*" === eventsDisplay[eventsDisplay.length - 1] ? getMetric(category, events.slice(0, events.length - 1).join(":")).title + " > All (.*)" : getMetric(category, events.slice(0, events.length).join(":")).title
}
for (var i = 0; i < events.length; i++) eventsDisplay[i] = ".*" === events[i] ? "All (.*)" : getMetric(category, events.slice(0, i + 1).join(":")).title;
var display = category.charAt(0).toUpperCase() + category.slice(1) + " > " + eventsDisplay.join(" > ");
return display
}
},
getMetric = function(category, metric, subEvent) {
var returnMeta;
if ("object" == typeof category && (subEvent = metric, metric = category.event, category = category.category), "error" === category && "stacktrace" === metric.substr(0, 10) && (metric = "Occurences"), returnMeta = angular.copy(meta[category] ? meta[category][metric] ? meta[category][metric] : meta[category]._default : meta._default), returnMeta.axisX = returnMeta.axisX || {
type: "date",
unit: "date",
title: "Date"
}, subEvent) {
var subMeta = getMetric(category, subEvent);
subEvent = subMeta.title
}
var tmp = parseMeta(returnMeta, metric, subEvent);
return tmp.category = category, tmp.event = metric, tmp.eventCountTitle = tmp.eventCountTitle || tmp.displayTitle, tmp.starEvent = ".*" === metric.split(":").slice(-1)[0], tmp.starEvent && (tmp.groupValues = !0), tmp
},
countries = {
AF: "Afghanistan",
AX: "Åland Islands",
AL: "Albania",
DZ: "Algeria",
AS: "American Samoa",
AD: "Andorra",
AO: "Angola",
AI: "Anguilla",
AQ: "Antarctica",
AG: "Antigua and Barbuda",
AR: "Argentina",
AM: "Armenia",
AW: "Aruba",
AU: "Australia",
AT: "Austria",
AZ: "Azerbaijan",
BS: "Bahamas",
BH: "Bahrain",
BD: "Bangladesh",
BB: "Barbados",
BY: "Belarus",
BE: "Belgium",
BZ: "Belize",
BJ: "Benin",
BM: "Bermuda",
BT: "Bhutan",
BO: "Bolivia, Plurinational State of",
BQ: "Bonaire, Sint Eustatius and Saba",
BA: "Bosnia and Herzegovina",
BW: "Botswana",
BV: "Bouvet Island",
BR: "Brazil",
IO: "British Indian Ocean Territory",
BN: "Brunei Darussalam",
BG: "Bulgaria",
BF: "Burkina Faso",
BI: "Burundi",
KH: "Cambodia",
CM: "Cameroon",
CA: "Canada",
CV: "Cape Verde",
KY: "Cayman Islands",
CF: "Central African Republic",
TD: "Chad",
CL: "Chile",
CN: "China",
CX: "Christmas Island",
CC: "Cocos (Keeling) Islands",
CO: "Colombia",
KM: "Comoros",
CG: "Congo",
CD: "Congo, the Democratic Republic of the",
CK: "Cook Islands",
CR: "Costa Rica",
CI: "Côte d´Ivoire",
HR: "Croatia",
CU: "Cuba",
CW: "Curaçao",
CY: "Cyprus",
CZ: "Czech Republic",
DK: "Denmark",
DJ: "Djibouti",
DM: "Dominica",
DO: "Dominican Republic",
EC: "Ecuador",
EG: "Egypt",
SV: "El Salvador",
GQ: "Equatorial Guinea",
ER: "Eritrea",
EE: "Estonia",
ET: "Ethiopia",
FK: "Falkland Islands (Malvinas)",
FO: "Faroe Islands",
FJ: "Fiji",
FI: "Finland",
FR: "France",
GF: "French Guiana",
PF: "French Polynesia",
TF: "French Southern Territories",
GA: "Gabon",
GM: "Gambia",
GE: "Georgia",
DE: "Germany",
GH: "Ghana",
GI: "Gibraltar",
GR: "Greece",
GL: "Greenland",
GD: "Grenada",
GP: "Guadeloupe",
GU: "Guam",
GT: "Guatemala",
GG: "Guernsey",
GN: "Guinea",
GW: "Guinea-Bissau",
GY: "Guyana",
HT: "Haiti",
HM: "Heard Island and McDonald Islands",
VA: "Holy See (Vatican City State)",
HN: "Honduras",
HK: "Hong Kong",
HU: "Hungary",
IS: "Iceland",
IN: "India",
ID: "Indonesia",
IR: "Iran, Islamic Republic of",
IQ: "Iraq",
IE: "Ireland",
IM: "Isle of Man",
IL: "Israel",
IT: "Italy",
JM: "Jamaica",
JP: "Japan",
JE: "Jersey",
JO: "Jordan",
KZ: "Kazakhstan",
KE: "Kenya",
KI: "Kiribati",
KP: "Korea, Democratic People´s Republic of",
KR: "Korea, Republic of",
KW: "Kuwait",
KG: "Kyrgyzstan",
LA: "Lao People´s Democratic Republic",
LV: "Latvia",
LB: "Lebanon",
LS: "Lesotho",
LR: "Liberia",
LY: "Libya",
LI: "Liechtenstein",
LT: "Lithuania",
LU: "Luxembourg",
MO: "Macao",
MK: "Macedonia, the former Yugoslav Republic of",
MG: "Madagascar",
MW: "Malawi",
MY: "Malaysia",
MV: "Maldives",
ML: "Mali",
MT: "Malta",
MH: "Marshall Islands",
MQ: "Martinique",
MR: "Mauritania",
MU: "Mauritius",
YT: "Mayotte",
MX: "Mexico",
FM: "Micronesia, Federated States of",
MD: "Moldova, Republic of",
MC: "Monaco",
MN: "Mongolia",
ME: "Montenegro",
MS: "Montserrat",
MA: "Morocco",
MZ: "Mozambique",
MM: "Myanmar",
NA: "Namibia",
NR: "Nauru",
NP: "Nepal",
NL: "Netherlands",
NC: "New Caledonia",
NZ: "New Zealand",
NI: "Nicaragua",
NE: "Niger",
NG: "Nigeria",
NU: "Niue",
NF: "Norfolk Island",
MP: "Northern Mariana Islands",
NO: "Norway",
OM: "Oman",
PK: "Pakistan",
PW: "Palau",
PS: "Palestinian Territory, Occupied",
PA: "Panama",
PG: "Papua New Guinea",
PY: "Paraguay",
PE: "Peru",
PH: "Philippines",
PN: "Pitcairn",
PL: "Poland",
PT: "Portugal",
PR: "Puerto Rico",
QA: "Qatar",
RE: "Réunion",
RO: "Romania",
RU: "Russia",
RW: "Rwanda",
BL: "Saint Barthélemy",
SH: "Saint Helena, Ascension and Tristan da Cunha",
KN: "Saint Kitts and Nevis",
LC: "Saint Lucia",
MF: "Saint Martin (French part)",
PM: "Saint Pierre and Miquelon",
VC: "Saint Vincent and the Grenadines",
WS: "Samoa",
SM: "San Marino",
ST: "Sao Tome and Principe",
SA: "Saudi Arabia",
SN: "Senegal",
RS: "Serbia",
SC: "Seychelles",
SL: "Sierra Leone",
SG: "Singapore",
SX: "Sint Maarten (Dutch part)",
SK: "Slovakia",
SI: "Slovenia",
SB: "Solomon Islands",
SO: "Somalia",
ZA: "South Africa",
GS: "South Georgia and the South Sandwich Islands",
SS: "South Sudan",
ES: "Spain",
LK: "Sri Lanka",
SD: "Sudan",
SR: "Suriname",
SJ: "Svalbard and Jan Mayen",
SZ: "Swaziland",
SE: "Sweden",
CH: "Switzerland",
SY: "Syrian Arab Republic",
TW: "Taiwan, Republic of China",
TJ: "Tajikistan",
TZ: "Tanzania, United Republic of",
TH: "Thailand",
TL: "Timor-Leste",
TG: "Togo",
TK: "Tokelau",
TO: "Tonga",
TT: "Trinidad and Tobago",
TN: "Tunisia",
TR: "Turkey",
TM: "Turkmenistan",
TC: "Turks and Caicos Islands",
TV: "Tuvalu",
UG: "Uganda",
UA: "Ukraine",
AE: "United Arab Emirates",
GB: "United Kingdom",
US: "United States",
UM: "United States Minor Outlying Islands",
UY: "Uruguay",
UZ: "Uzbekistan",
VU: "Vanuatu",
VE: "Venezuela, Bolivarian Republic of",
VN: "Vietnam",
VG: "Virgin Islands, British",
VI: "Virgin Islands, U.S.",
WF: "Wallis and Futuna",
EH: "Western Sahara",
YE: "Yemen",
ZM: "Zambia",
ZW: "Zimbabwe"
},
getCountry = function(shortCode) {
return countries[shortCode] || shortCode
},
currenciesList = ["AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTC", "BTN", "BWP", "BYR", "BZD", "CAD", "CDF", "CHF", "CLF", "CLP", "CNY", "COP", "CRC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EEK", "EGP", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LVL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MTL", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "STD", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VEF", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XCD", "XDR", "XOF", "XPF", "YER", "ZAR", "ZMK", "ZMW", "ZWL"],
currencies = {
USD: {
symbol: "$",
name: "US Dollar"
},
CAD: {
symbol: "CA$",
name: "Canadian Dollar"
},
EUR: {
symbol: "€",
name: "Euro"
},
AED: {
symbol: "AED",
name: "United Arab Emirates Dirham"
},
AFN: {
symbol: "Af",
name: "Afghan Afghani"
},
ALL: {
symbol: "ALL",
name: "Albanian Lek"
},
AMD: {
symbol: "AMD",
name: "Armenian Dram"
},
ARS: {
symbol: "AR$",
name: "Argentine Peso"
},
AUD: {
symbol: "AU$",
name: "Australian Dollar"
},
AZN: {
symbol: "man.",
name: "Azerbaijani Manat"
},
BAM: {
symbol: "KM",
name: "Bosnia-Herzegovina Convertible Mark"
},
BDT: {
symbol: "Tk",
name: "Bangladeshi Taka"
},
BGN: {
symbol: "BGN",
name: "Bulgarian Lev"
},
BHD: {
symbol: "BD",
name: "Bahraini Dinar"
},
BIF: {
symbol: "FBu",
name: "Burundian Franc"
},
BND: {
symbol: "BN$",
name: "Brunei Dollar"
},
BOB: {
symbol: "Bs",
name: "Bolivian Boliviano"
},
BRL: {
symbol: "R$",
name: "Brazilian Real"
},
BWP: {
symbol: "BWP",
name: "Botswanan Pula"
},
BYR: {
symbol: "BYR",
name: "Belarusian Ruble"
},
BZD: {
symbol: "BZ$",
name: "Belize Dollar"
},
CDF: {
symbol: "CDF",
name: "Congolese Franc"
},
CHF: {
symbol: "CHF",
name: "Swiss Franc"
},
CLP: {
symbol: "CL$",
name: "Chilean Peso"
},
CNY: {
symbol: "CN¥",
name: "Chinese Yuan"
},
COP: {
symbol: "CO$",
name: "Colombian Peso"
},
CRC: {
symbol: "₡",
name: "Costa Rican Colón"
},
CVE: {
symbol: "CV$",
name: "Cape Verdean Escudo"
},
CZK: {
symbol: "Kč",
name: "Czech Republic Koruna"
},
DJF: {
symbol: "Fdj",
name: "Djiboutian Franc"
},
DKK: {
symbol: "Dkr",
name: "Danish Krone"
},
DOP: {
symbol: "RD$",
name: "Dominican Peso"
},
DZD: {
symbol: "DA",
name: "Algerian Dinar"
},
EEK: {
symbol: "Ekr",
name: "Estonian Kroon"
},
EGP: {
symbol: "EGP",
name: "Egyptian Pound"
},
ERN: {
symbol: "Nfk",
name: "Eritrean Nakfa"
},
ETB: {
symbol: "Br",
name: "Ethiopian Birr"
},
GBP: {
symbol: "£",
name: "British Pound Sterling"
},
GEL: {
symbol: "GEL",
name: "Georgian Lari"
},
GHS: {
symbol: "GH₵",
name: "Ghanaian Cedi"
},
GNF: {
symbol: "FG",
name: "Guinean Franc"
},
GTQ: {
symbol: "GTQ",
name: "Guatemalan Quetzal"
},
HKD: {
symbol: "HK$",
name: "Hong Kong Dollar"
},
HNL: {
symbol: "HNL",
name: "Honduran Lempira"
},
HRK: {
symbol: "kn",
name: "Croatian Kuna"
},
HUF: {
symbol: "Ft",
name: "Hungarian Forint"
},
IDR: {
symbol: "Rp",
name: "Indonesian Rupiah"
},
ILS: {
symbol: "₪",
name: "Israeli New Sheqel"
},
INR: {
symbol: "Rs",
name: "Indian Rupee"
},
IQD: {
symbol: "IQD",
name: "Iraqi Dinar"
},
IRR: {
symbol: "IRR",
name: "Iranian Rial"
},
ISK: {
symbol: "Ikr",
name: "Icelandic Króna"
},
JMD: {
symbol: "J$",
name: "Jamaican Dollar"
},
JOD: {
symbol: "JD",
name: "Jordanian Dinar"
},
JPY: {
symbol: "¥",
name: "Japanese Yen"
},
KES: {
symbol: "Ksh",
name: "Kenyan Shilling"
},
KHR: {
symbol: "KHR",
name: "Cambodian Riel"
},
KMF: {
symbol: "CF",
name: "Comorian Franc"
},
KRW: {
symbol: "₩",
name: "South Korean Won"
},
KWD: {
symbol: "KD",
name: "Kuwaiti Dinar"
},
KZT: {
symbol: "KZT",
name: "Kazakhstani Tenge"
},
LBP: {
symbol: "LB£",
name: "Lebanese Pound"
},
LKR: {
symbol: "SLRs",
name: "Sri Lankan Rupee"
},
LTL: {
symbol: "Lt",
name: "Lithuanian Litas"
},
LVL: {
symbol: "Ls",
name: "Latvian Lats"
},
LYD: {
symbol: "LD",
name: "Libyan Dinar"
},
MAD: {
symbol: "MAD",
name: "Moroccan Dirham"
},
MDL: {
symbol: "MDL",
name: "Moldovan Leu"
},
MGA: {
symbol: "MGA",
name: "Malagasy Ariary"
},
MKD: {
symbol: "MKD",
name: "Macedonian Denar"
},
MMK: {
symbol: "MMK",
name: "Myanma Kyat"
},
MOP: {
symbol: "MOP$",
name: "Macanese Pataca"
},
MUR: {
symbol: "MURs",
name: "Mauritian Rupee"
},
MXN: {
symbol: "MX$",
name: "Mexican Peso"
},
MYR: {
symbol: "RM",
name: "Malaysian Ringgit"
},
MZN: {
symbol: "MTn",
name: "Mozambican Metical"
},
NAD: {
symbol: "N$",
name: "Namibian Dollar"
},
NGN: {
symbol: "₦",
name: "Nigerian Naira"
},
NIO: {
symbol: "C$",
name: "Nicaraguan Córdoba"
},
NOK: {
symbol: "Nkr",
name: "Norwegian Krone"
},
NPR: {
symbol: "NPRs",
name: "Nepalese Rupee"
},
NZD: {
symbol: "NZ$",
name: "New Zealand Dollar"
},
OMR: {
symbol: "OMR",
name: "Omani Rial"
},
PAB: {
symbol: "B/.",
name: "Panamanian Balboa"
},
PEN: {
symbol: "S/.",
name: "Peruvian Nuevo Sol"
},
PHP: {
symbol: "₱",
name: "Philippine Peso"
},
PKR: {
symbol: "PKRs",
name: "Pakistani Rupee"
},
PLN: {
symbol: "zł",
name: "Polish Zloty"
},
PYG: {
symbol: "₲",
name: "Paraguayan Guarani"
},
QAR: {
symbol: "QR",
name: "Qatari Rial"
},
RON: {
symbol: "RON",
name: "Romanian Leu"
},
RSD: {
symbol: "din.",
name: "Serbian Dinar"
},
RUB: {
symbol: "RUB",
name: "Russian Ruble"
},
RWF: {
symbol: "RWF",
name: "Rwandan Franc"
},
SAR: {
symbol: "SR",
name: "Saudi Riyal"
},
SDG: {
symbol: "SDG",
name: "Sudanese Pound"
},
SEK: {
symbol: "Skr",
name: "Swedish Krona"
},
SGD: {
symbol: "S$",
name: "Singapore Dollar"
},
SOS: {
symbol: "Ssh",
name: "Somali Shilling"
},
SYP: {
symbol: "SY£",
name: "Syrian Pound"
},
THB: {
symbol: "฿",
name: "Thai Baht"
},
TND: {
symbol: "DT",
name: "Tunisian Dinar"
},
TOP: {
symbol: "T$",
name: "Tongan Paʻanga"
},
TRY: {
symbol: "TL",
name: "Turkish Lira"
},
TTD: {
symbol: "TT$",
name: "Trinidad and Tobago Dollar"
},
TWD: {
symbol: "NT$",
name: "New Taiwan Dollar"
},
TZS: {
symbol: "TSh",
name: "Tanzanian Shilling"
},
UAH: {
symbol: "₴",
name: "Ukrainian Hryvnia"
},
UGX: {
symbol: "USh",
name: "Ugandan Shilling"
},
UYU: {
symbol: "$U",
name: "Uruguayan Peso"
},
UZS: {
symbol: "UZS",
name: "Uzbekistan Som"
},
VEF: {
symbol: "Bs.F.",
name: "Venezuelan Bolívar"
},
VND: {
symbol: "₫",
name: "Vietnamese Dong"
},
XAF: {
symbol: "FCFA",
name: "CFA Franc BEAC"
},
XOF: {
symbol: "CFA",
name: "CFA Franc BCEAO"
},
YER: {
symbol: "YR",
name: "Yemeni Rial"
},
ZAR: {
symbol: "R",
name: "South African Rand"
},
ZMK: {
symbol: "ZK",
name: "Zambian Kwacha"
},
ZWL: {
symbol: "Z$",
name: "Zimbabwean Dollar"
}
},
getCurrency = function(code) {
return currencies[code] || {
symbol: "",
name: code,
code: code
}
},
getCurrencies = function() {
return currenciesList
},
aggregationTooltips = {
design: {
sum: 'The sum of all numbers found in the "value" field of the design event.',
mean: 'The arithmetic mean of all numbers found in the "value" field.',
count: "The number of design events seen, including those events which did not have a value."
},
business: {
sum: 'The monetary sum of all business events converted into the currency of your choice. The "amount" field sent in the SDK uses cents, while the sum aggregation divides the sum by 100. Virtual currencies are ignored.',
mean: 'The arithmetic mean of all business events, converted into the currency of your choice. The "amount" field sent in the SDK uses cents, while the mean aggregation divides the sum by 100. Virtual currencies are ignored.',
count: "The number of business events seen."
}
},
getAggregationTooltip = function(category) {
return aggregationTooltips[category] || null
};
return {
getMetric: getMetric,
getMetricDisplay: getMetricDisplay,
getMetricArray: getMetricArray,
getDimension: getDimension,
getUnit: getUnit,
getCurrencies: getCurrencies,
getCurrency: getCurrency,
getCountry: getCountry,
getAggregationTooltip: getAggregationTooltip
}
}), angular.module("ga.api.metric", ["ui.router", "ga.config", "ga.api.data", "ga.api.meta", "ga.components.rollbar"]).factory("gaApiMetric", function($q, $rootScope, $timeout, $stateParams, gaConfig, gaApiMeta, gaApiData, gaComponentsRollbar) {
function zerofy(str) {
var match = str.match(/\d+$/);
if (match) {
for (var z = 10 - match[0].length, zstr = "", i = 1; z >= i; i++) zstr += "0";
return str.replace(/(\d+)$/, zstr + "$1")
}
return str
}
var getParsedMetricListPromise, metrics = [],
reImport = !1,
nbOfResults = 0,
nbOfResultsSliced = 0,
nbOfSearchResults = 0,
parsedMetrics = [],
currentSelection = [],
options = {
noCombine: !1,
filter: null,
searchQuery: ""
},
categories = {
core: {
label: "Core"
},
design: {
label: "Design"
},
quality: {
label: "Quality"
},
business: {
label: "Business"
},
error: {
label: "Error"
}
},
activeCategories = [],
showStarEvents = !0,
getParsedMetricList = function(sMetric, query) {
var selectedMetric = angular.copy(sMetric);
if (metrics.length && !reImport) getParsedMetricListPromise = $q.defer(), buildMetricList(selectedMetric, query).then(function(obj) {
getParsedMetricListPromise.resolve(obj), getParsedMetricListPromise = null
});
else {
if (getParsedMetricListPromise) return getParsedMetricListPromise.promise;
getParsedMetricListPromise = $q.defer(), _getList().then(function() {
buildMetricList(selectedMetric, query).then(function(obj) {
getParsedMetricListPromise.resolve(obj), getParsedMetricListPromise = null
})
}, function() {
getParsedMetricListPromise.reject(), getParsedMetricListPromise = null
}), reImport = !1
}
return getParsedMetricListPromise.promise
},
setOptions = function(opts) {
options.noCombine = void 0 !== opts.noCombine ? opts.noCombine : !1, void 0 !== opts.filter ? (options.filter = opts.filter, showStarEvents = void 0 !== options.filter.disableStarEvents ? !options.filter.disableStarEvents : !0, activeCategories = [], options.filter.categories ? activeCategories = options.filter.categories : angular.forEach(categories, function(cat) {
activeCategories.push(cat.label.toLowerCase())
})) : (options.filter = null, showStarEvents = !0, activeCategories = [], angular.forEach(categories, function(cat) {
activeCategories.push(cat.label.toLowerCase())
})), reImport = !0, currentSelection = []
},
getMetricHierarchy = function(metric) {
var deferred = $q.defer();
if (metrics.length) {
var metricInfo = _getMetricHierarchy(metric);
deferred.resolve(metricInfo)
} else getParsedMetricList().then(function() {
if (metrics.length) {
var metricInfo = _getMetricHierarchy(metric);
deferred.resolve(metricInfo)
} else deferred.reject()
});
return deferred.promise
},
getMetricInfo = function(metric) {
var deferred = $q.defer();
if (metrics.length) {
var childEvents = _hasChildEvents(metric);
deferred.resolve({
hasChildEvents: childEvents
})
} else getParsedMetricList().then(function() {
if (metrics.length) {
var childEvents = _hasChildEvents(metric);
deferred.resolve({
hasChildEvents: childEvents
})
} else deferred.reject()
});
return deferred.promise
},
_hasChildEvents = function(qMetric) {
var queryMetric = angular.copy(qMetric),
hasChildEvents = !1;
if (queryMetric.event.indexOf(".*") > -1) hasChildEvents = !0;
else {
var metricStr = queryMetric.category + ":" + queryMetric.event,
regExpr = new RegExp("^" + metricStr + ":([^:]*)", "i");
metrics.forEach(function(metric) {
var match = (metric.fullEvent.match(regExpr) || [])[1];
match && (hasChildEvents = !0)
})
}
return hasChildEvents
},
_getMetricHierarchy = function(qMetric) {
var queryMetric = angular.copy(qMetric),
returnArr = [],
metricStr = queryMetric.category + ":" + queryMetric.event,
arrMetric = metricStr.split(":"),
hasChildEvents = _hasChildEvents(qMetric);
queryMetric.event.indexOf(".*") > -1 && arrMetric.pop(), returnArr.push({
name: queryMetric.category.charAt(0).toUpperCase() + queryMetric.category.slice(1),
title: queryMetric.category.charAt(0).toUpperCase() + queryMetric.category.slice(1),
event: null
}), arrMetric.shift();
var prevEventStr = "";
return angular.forEach(arrMetric, function(evt, index) {
var eventStr = evt;
eventStr && (prevEventStr && (eventStr = prevEventStr + ":" + eventStr), prevEventStr = angular.copy(eventStr), index === arrMetric.length - 1 ? -1 === queryMetric.event.indexOf(".*") ? eventStr = null : hasChildEvents && (eventStr += ":.*") : eventStr += ":.*"), returnArr.push({
name: evt,
title: gaApiMeta.getMetric(queryMetric.category, prevEventStr).title,
event: eventStr
})
}), returnArr
},
buildMetricList = function(selectedMetric, query) {
var deferred = $q.defer();
if (metrics.length) {
var cEvent;
options.searchQuery = query || !1, angular.isArray(selectedMetric) ? currentSelection = selectedMetric : angular.isObject(selectedMetric) && (cEvent = options.filter && options.filter.customEventList ? selectedMetric.event : selectedMetric.category + ":" + selectedMetric.event, currentSelection = []), _parseMetrics(cEvent).then(function() {
deferred.resolve({
nbOfResults: nbOfResults,
nbOfResultsSliced: nbOfResultsSliced,
metrics: parsedMetrics,
selected: currentSelection,
nbOfSearchResults: nbOfSearchResults
})
})
} else deferred.reject();
return deferred.promise
},
_parseCategory = function(rawData, category, each) {
var data = rawData[category],
metrics = [];
return angular.forEach(data, function(event) {
var include = !0;
if ("function" == typeof each && (include = each(category, event)), include) {
var selectable = !0;
options && options.filter && options.filter.blacklisted && options.filter.blacklisted.indexOf(category + ":" + event) > -1 && (selectable = !1);
var meta = gaApiMeta.getMetric(category, event);
metrics.push({
category: category,
event: event,
selectable: selectable,
title: meta.title,
fullEvent: categories[category].label + ":" + event
})
}
}), metrics
},
_getList = function() {
var deferred = $q.defer();
return metrics = [], options.filter && options.filter.customEventList ? (_parseCustomData(options.filter.customEventList), deferred.resolve()) : gaApiData.getValue("/metrics", $stateParams.gameId).then(function(rawData) {
_parseRawData(rawData), deferred.resolve()
}, function(response) {
response.error && "no_game_api_key" === response.error ? deferred.reject() : deferred.reject("error")
}), deferred.promise
},
_parseCustomData = function(data) {
angular.forEach(data, function(event) {
var category = event.split(":")[0],
meta = gaApiMeta.getMetric(category, event);
metrics.push({
category: category,
event: event,
selectable: !0,
title: meta.title,
fullEvent: event
})
})
},
_parseRawData = function(rawData) {
rawData.core = rawData.core || [], rawData.core.push("transactions"), angular.forEach(rawData.core, function(event) {
var meta;
if (options.noCombine) {
if (meta = gaApiMeta.getMetric("core", event), meta.parent) {
var parentMeta = gaApiMeta.getMetric("core", meta.parent);
meta.title = parentMeta.title + " - " + meta.title
}
} else {
if ("returning_users_1" === event && (event = "returning_users"), event.indexOf("returning_users_") > -1 && event.indexOf("returning_users_week") < 0) return;
if ("retention_3" === event && (event = "retention"), event.indexOf("retention_") > -1) return;
meta = gaApiMeta.getMetric("core", event)
} if (!meta.hidden) {
if (!gaConfig.showNoMetaMetrics && meta.isDefault) return;
var selectable = !0;
if (options && options.filter && (options.filter.groupTime && meta.groupTime === !1 && (selectable = !1), options.filter.blacklisted && options.filter.blacklisted.indexOf(event) > -1)) return;
metrics.push({
category: "core",
event: event,
title: meta.title,
fullEvent: categories.core.label + ":" + event,
selectable: selectable
})
}
}), metrics = metrics.concat(_parseCategory(rawData, "design")), metrics = metrics.concat(_parseCategory(rawData, "quality", function() {
return gaConfig.showQualityEvents
})), metrics = metrics.concat(_parseCategory(rawData, "business")), metrics = metrics.concat(_parseCategory(rawData, "error", function(category, event) {
return "stacktrace" !== event.substr(0, 10)
})), metrics.sort(function(a, b) {
var x = zerofy(a.title || a.fullEvent),
y = zerofy(b.title || b.fullEvent);
return y > x ? -1 : x > y ? 1 : 0
})
},
_parseMetrics = function(fullEvent) {
var deferred = $q.defer();
fullEvent && (currentSelection = options.filter && options.filter.customEventList ? fullEvent.split(":").slice(0, -1).map(function(val) {
return {
title: val,
value: val
}
}) : gaApiMeta.getMetricArray(fullEvent));
var rootArray = [],
checkList = {},
list = [];
if (angular.forEach(currentSelection, function(value) {
rootArray.push(value.value)
}), !options.searchQuery) return getTree(rootArray.join(":")).then(function(parsed) {
if (rootArray.length && "core" !== rootArray[0].toLowerCase() && (angular.forEach(parsed, function(metric) {
metric.hasChildEvents && parsed.push({
label: metric.label,
hasChildEvents: !1,
event: metric.event,
selectable: metric.singleEventSelectable,
category: metric.category
})
}), parsed.sort(function(a, b) {
var a1 = a.hasChildEvents ? 0 : 1,
b1 = b.hasChildEvents ? 0 : 1,
a2 = zerofy(a.event.toLowerCase()),
b2 = zerofy(b.event.toLowerCase());
return b1 > a1 ? -1 : a1 > b1 ? 1 : b2 > a2 ? -1 : a2 > b2 ? 1 : 0
}), rootArray.length > 1 && showStarEvents)) {
var event = rootArray.slice(1).join(":");
parsed.unshift({
selectAll: !0,
label: '.* Select all "' + event + '"',
hasChildEvents: !1,
selectable: !0,
event: event ? event + ":.*" : ".*",
category: rootArray[0].toLowerCase()
})
}
nbOfResults = parsed.length;
var tmpMetrics = [];
parsed.some(function(evt) {
(!evt.category && activeCategories.indexOf(evt.event.toLowerCase()) > -1 || evt.category && activeCategories.indexOf(evt.category.toLowerCase()) > -1) && tmpMetrics.push(evt)
}), tmpMetrics && tmpMetrics.length > 2500 && gaComponentsRollbar.putCustom({
level: "critical",
msg: "More than 2500 metrics in same level",
point: {
error: "Too many metrics in same level",
data: ""
}
});
var parsedSliced = tmpMetrics.slice(0, 2500);
nbOfResultsSliced = parsedSliced.length, parsedMetrics = parsedSliced, deferred.resolve()
}), deferred.promise;
var searchQuery = !1;
options.searchQuery && (searchQuery = sanitizeRegExp(rootArray.join(":")), searchQuery += "(.*?)", searchQuery += sanitizeRegExp(options.searchQuery).replace(/\s/g, "[\\s|:]"), searchQuery = new RegExp(searchQuery, "i"));
var rootResultCount = 0;
if (rootArray.length && searchQuery) {
var query = new RegExp(sanitizeRegExp(options.searchQuery).replace(/\s/g, "[\\s|:]"), "i");
rootResultCount = metrics.filter(function(item) {
return query.test(item.event)
}).length
}
var regExpr = new RegExp(rootArray.length ? "^" + rootArray.join(":") + ":([^:]*)" : "([^:]*)", "i");
metrics.forEach(function(metric) {
var match = (metric.fullEvent.match(regExpr) || [])[1],
found = match && searchQuery ? searchQuery.test(metric.fullEvent) || searchQuery.test(metric.title) : !0;
if (found && match && !checkList[match]) {
checkList[match] = searchQuery ? !1 : !0;
var hasChildEvents = !1,
hasChildEventsPattern = new RegExp((rootArray.length ? sanitizeRegExp(rootArray.join(":")) + ":" : "") + match + ":");
hasChildEvents = searchQuery || ".*" === match ? !1 : metric.fullEvent.match(hasChildEventsPattern);
var label = searchQuery ? gaApiMeta.getMetricDisplay(metric.fullEvent) : match;
if (list.push({
event: hasChildEvents ? !1 : metric.event,
label: label,
labelNoHtml: label,
hasChildEvents: hasChildEvents,
selectable: metric.selectable,
category: metric.category
}), hasChildEvents && metric.event) {
var tmpEvent = metric.fullEvent.split(":");
tmpEvent.length > 1
}
}
}), rootResultCount -= list.length, rootResultCount = 0 > rootResultCount ? 0 : rootResultCount, nbOfSearchResults = rootResultCount;
var slicedList = list.slice(0, 2500);
return parsedMetrics = slicedList, deferred.resolve(), deferred.promise
},
getTree = function(match) {
var deferred = $q.defer(),
level = (match ? match.split(":").length : 0) + 1,
tmpMetrics = [];
if (match) {
var pattern = new RegExp("^" + sanitizeRegExp(match) + "[$|:.*]");
tmpMetrics = metrics.filter(function(metric) {
var include = metric.fullEvent.match(pattern) && metric.fullEvent.split(":").length === level;
return include
}), $timeout(function() {
tmpMetrics = tmpMetrics.map(function(metric) {
if (void 0 === metric.hasChildEvents) {
var hasChildEventsPattern = new RegExp("^" + sanitizeRegExp(metric.fullEvent) + "\\:"),
hasChildEvents = metrics.some(function(metric) {
return metric.fullEvent.match(hasChildEventsPattern)
});
metric.hasChildEvents = hasChildEvents
}
return {
label: metric.title || metric.event.split(":").pop(),
category: metric.category,
event: metric.event,
selectable: metric.hasChildEvents ? !0 : metric.selectable,
singleEventSelectable: metric.selectable,
hasChildEvents: metric.hasChildEvents
}
}), deferred.resolve(tmpMetrics)
}, tmpMetrics.length > 1e3 ? 100 : 0)
} else metrics.some(function(metric) {
return "core" === metric.category
}) && tmpMetrics.push({
event: "Core",
label: "Core",
hasChildEvents: !0,
selectable: !0
}), metrics.some(function(metric) {
return "design" === metric.category
}) && tmpMetrics.push({
event: "Design",
label: "Design",
hasChildEvents: !0,
selectable: !0
}), metrics.some(function(metric) {
return "quality" === metric.category
}) && tmpMetrics.push({
event: "Quality",
label: "Quality",
hasChildEvents: !0,
selectable: !0
}), metrics.some(function(metric) {
return "business" === metric.category
}) && tmpMetrics.push({
event: "Business",
label: "Business",
hasChildEvents: !0,
selectable: !0
}), metrics.some(function(metric) {
return "error" === metric.category
}) && tmpMetrics.push({
event: "Error",
label: "Error",
hasChildEvents: !0,
selectable: !0
}), options.filter && options.filter.customEventList && angular.forEach(metrics, function(metric) {
if (void 0 === metric.hasChildEvents) {
var hasChildEventsPattern = new RegExp("^" + sanitizeRegExp(metric.fullEvent) + "\\:"),
hasChildEvents = metrics.some(function(metric) {
return metric.fullEvent.match(hasChildEventsPattern)
});
metric.hasChildEvents = hasChildEvents
}(metric.hasChildEvents && 1 === metric.event.split(":").length || metric.category === metric.event) && tmpMetrics.push({
label: metric.title || metric.event.split(":").pop(),
category: metric.category,
event: metric.event,
selectable: metric.hasChildEvents ? !0 : metric.selectable,
singleEventSelectable: metric.selectable,
hasChildEvents: metric.hasChildEvents
})
}), deferred.resolve(tmpMetrics);
return deferred.promise
},
sanitizeRegExp = function(string) {
return string.replace(/[\?\(\)\[\]\$\^\:\.]/g, "\\$&")
},
getMetric = function(metricPath) {
var deferred = $q.defer();
if (metrics.length) {
var metric = _getMetric(metricPath);
deferred.resolve(metric ? metric : null)
} else getParsedMetricList().then(function() {
if (metrics.length) {
var metric = _getMetric(metricPath);
deferred.resolve(metric ? metric : null)
} else deferred.resolve(null)
});
return deferred.promise
},
_getMetric = function(metricPath) {
var found = null;
return metrics.some(function(metric) {
return metricPath === metric.event ? (found = metric, !0) : void 0
}), found
};
return {
getParsedMetricList: getParsedMetricList,
setOptions: setOptions,
getMetricHierarchy: getMetricHierarchy,
getMetricInfo: getMetricInfo,
getMetric: getMetric
}
}), angular.module("ga.api.userDb", ["ga.config", "ga.values.user", "ga.api.userDb.mock"]).factory("gaApiUserDb", function($http, $q, $timeout, gaConfig, gaValuesUser, gaApiUserDbMock) {
var getConfig = function() {
var config = {
headers: {}
};
return gaValuesUser.token && (config.headers["X-Authorization"] = gaValuesUser.token), config
},
_validateData = function(data, single) {
return "object" == typeof data && angular.isArray(data.errors) && angular.isArray(data.results) || (data = {
errors: [{
id: "invalidResponse",
msg: "Response is not valid",
data: data
}],
results: []
}), data.errors.length ? $q.reject(data.errors) : $q.when(single ? data.results[0] : data.results)
},
_request = function(type, url, data, config, raw) {
return config = config || getConfig(), gaApiUserDbMock.request.apply(gaApiUserDbMock, Array.prototype.slice.call(arguments)).then(function(response) {
return raw ? $q.when(response) : _validateData(response)
}).catch(function() {
url = gaConfig.userApi.baseUrl + url, data = data || null;
var args = ["get", "delete", "head"].indexOf(type) > -1 ? [url, config] : [url, data, config];
return $http[type].apply($http, args).catch(function(response) {
return raw ? $q.reject(response.data) : _validateData(response.data)
}).then(function(response) {
return raw ? $q.when(response.data) : _validateData(response.data)
})
})
},
upload = function(url, file, progressCallback) {
var config = getConfig(),
deferred = $q.defer(),
fd = new FormData;
fd.append("image", file), config && config.headers && config.headers["X-Authorization"] && fd.append("X-Authorization", config.headers["X-Authorization"]);
var xhr = new XMLHttpRequest;
return "function" == typeof progressCallback && xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
var percent = e.loaded / e.total || 0;
progressCallback(percent)
}
}, !1), xhr.addEventListener("load", function(e) {
var response = {};
try {
response = JSON.parse(e.target.responseText)
} catch (e) {}
deferred.resolve(_validateData(response, !0))
}, !1), xhr.addEventListener("error", function() {
deferred.reject("An error occured")
}, !1), xhr.addEventListener("abort", function() {
deferred.reject("Upload was cancelled")
}, !1), xhr.open("POST", url), xhr.setRequestHeader("Authorization", gaValuesUser.token), xhr.send(fd), deferred.promise
},
copyFile = function(fileUrl) {
var payload = {
fileUrl: fileUrl
};
return put("upload/image", payload)
},
get = _request.bind(this, "get"),
post = _request.bind(this, "post"),
put = _request.bind(this, "put"),
update = _request.bind(this, "put"),
remove = _request.bind(this, "delete");
return {
upload: upload,
copyFile: copyFile,
post: post,
get: get,
put: put,
update: update,
remove: remove,
getConfig: getConfig,
_validateData: _validateData
}
}), angular.module("ga.api.userDb.mock", ["ga.config", "ga.values.user", "ga.api.userDb.public.user"]).service("gaApiUserDbMock", function($q, $injector, gaConfig, gaValuesUser) {
var replyJson = function(results, errors) {
return {
results: results ? angular.isArray(results) ? results : [results] : [],
errors: errors ? angular.isArray(errors) ? errors : [errors] : []
}
},
getData = function(url, fallback) {
var store = {};
try {
store = JSON.parse(localStorage.getItem("gaUserLocalStore")) || {}
} catch (e) {}
return store[url] || fallback || null
},
putData = function(url, data) {
var store = {};
try {
store = JSON.parse(localStorage.getItem("gaUserLocalStore")) || {}
} catch (e) {}
store[url] = data, localStorage.setItem("gaUserLocalStore", JSON.stringify(store))
};
this.replyJson = replyJson, this.getData = getData, this.putData = putData, this.request = function(type, url, data) {
if (url.match(/^public/)) return $q.reject();
if ("user/data" === url && gaValuesUser.activated === !1) return $injector.get("gaApiUserDbPublicUser").getData(gaValuesUser.token).then(function(result) {
return result.onboarding = getData("user/onboarding", result.onboarding), result.settings = getData("user/settings", result.settings), $q.when(replyJson(result))
});
var gameId, dashboardId, useMock = !gaValuesUser.activated;
if ((gameId = url.match(/games\/([0-9]+)/)) ? (gameId = parseInt(gameId[1] || null), useMock = (gaValuesUser.game(gameId) || {}).demo) : (dashboardId = url.match(/([0-9]+-demo)/)) && (useMock = !0, dashboardId = dashboardId[1]), !useMock) return $q.reject();
if (mocks[type] && useMock) {
var route = null,
match = null;
if (mocks[type].some(function(r) {
return match = url.match(new RegExp(r.path)), match ? (route = r, !0) : void 0
}), route && match) {
var results = "function" == typeof route.results ? route.results.apply(this, [data].concat(match.slice(1))) : route.results;
return $q.when(route.raw ? results : replyJson(results))
}
}
return $q.reject()
};
var mocks = {
get: [{
path: "data_api/status/processing",
results: replyJson([])
}, {
path: "games/([0-9]+)/dashboards/sortorder",
results: function() {
var data = getData("user/dashboards/sortorder", []);
return {
results: data,
errors: []
}
},
raw: !0
}, {
path: "games/([0-9]+)/dashboards",
results: function() {
return getData("user/dashboards/dashboards", [])
}
}, {
path: "api/games/([0-9]+)/has/heatmap",
results: {
errors: [],
results: [],
heatmaps: !0
},
raw: !0
}, {
path: "games/([0-9]+)/heatmap/settings",
results: {
errors: [],
results: [{
gameId: 7165,
heatmap_skey: "3fc6d0597a73fe28c80962b3bfcfedeb93adddef",
heatmap_dkey: "afc969bda35dfa5bbba69922bcf73309324e89b6",
heatmap_key: "183609b5aeb121013eb395f559fd9199",
settings: {
selectedSet: 0,
sets: [{
selectedHeatmap: 0,
name: "Heatmap 1",
area: "Level 1",
heatmaps: [{
settingsExpanded: !0,
uid: "1395236473-10038",
selectedColor: "1",
dateRange: {
main: {
start: null,
end: null
}
},
selectedEvents: [{
category: "Level 1",
type: "add",
name: "Level 1:Kill"
}],
name: "Heatmap",
selectedBuild: null,
visible: 1,
range: {
max: 100,
min: 0
},
radius: 10,
selectedRender: "0"
}],
mesh: null,
id: 0
}]
},
shared: !1,
initial_load: !1,
createdDate: "2014-03-19T13:41:05",
userId: null,
id: 1,
editedDate: "2014-03-26T08:40:44"
}]
},
raw: !0
}, {
path: "games/([0-9]+)/dashboards",
results: function() {
return getData("user/dashboards/dashboards", [])
}
}, {
path: "dashboards/([0-9]+)-demo",
results: function(o, id) {
var dashboards = getData("user/dashboards/dashboards", []),
found = null;
return dashboards && dashboards.length && dashboards.some(function(d) {
d.id === id + "-demo" && (found = d)
}), found.locked = !1, found
}
}, {
path: "games/([0-9]+)/queries",
results: function() {
return [getData("user/explore/queries", [])]
}
}],
put: [{
path: "user/onboarding",
results: function(data) {
return putData("user/onboarding", data), []
}
}, {
path: "user/settings",
results: function(data) {
return putData("user/settings", data), []
}
}, {
path: "games/([0-9]+)/dashboards/sortorder",
results: function(data) {
return putData("user/dashboards/sortorder", data.sortorder), []
}
}, {
path: "games/([0-9]+)/dashboards",
results: function(data) {
if (0 === data.id) {
data.id = Math.floor(1e4 * Math.random()) + "-demo";
var cdashboards = getData("user/dashboards/dashboards", []);
if (data.widgets && data.widgets.length)
for (var i = 0, max = data.widgets.length; max > i; i++) data.widgets[i].id = i;
cdashboards.push(data), putData("user/dashboards/dashboards", cdashboards)
} else {
var dashboards = getData("user/dashboards/dashboards", []),
found = null;
dashboards && dashboards.length && (dashboards.some(function(d) {
return d.id === data.id ? (found = d, !0) : void 0
}), found && (found = data)), putData("user/dashboards/dashboards", dashboards)
}
return data.locked = !1, data
}
}, {
path: "games/([0-9]+)/heatmap/settings",
results: replyJson([])
}, {
path: "dashboards/([0-9]+)-demo/lock",
results: function(o, id) {
var dashboards = getData("user/dashboards/dashboards", []),
found = null;
return dashboards && dashboards.length && dashboards.some(function(d) {
return d.id === id + "-demo" ? (found = d, !0) : void 0
}), found.locked = !0, found
}
}, {
path: "dashboards/([0-9]+)-demo/unlock",
results: function(o, id) {
var dashboards = getData("user/dashboards/dashboards", []),
found = null;
return dashboards && dashboards.length && dashboards.some(function(d) {
return d.id === id + "-demo" ? (found = d, !0) : void 0
}), found.locked = !1, found
}
}, {
path: "dashboards/([0-9]+)-demo",
results: function(data) {
var dashboards = getData("user/dashboards/dashboards", []);
if (dashboards && dashboards.length) {
var cleanedDashboards = [];
dashboards.some(function(d) {
if (d.id === data.id) {
if (data.widgets && data.widgets.length)
for (var i = 0, max = data.widgets.length; max > i; i++) data.widgets[i].id = i;
d = data
}
cleanedDashboards.push(d)
}), putData("user/dashboards/dashboards", cleanedDashboards)
}
return data.locked = !1, data
}
}, {
path: "games/([0-9]+)/queries",
results: function(data) {
var queries = getData("user/explore/queries", []),
saveObj = {
id: Math.floor(1e4 * Math.random()) + "-demo",
name: data.name,
saveInterval: data.saveInterval,
query_data: {
global: data.global,
yAxis: data.yAxis
}
};
return queries.push(saveObj), putData("user/explore/queries", queries), saveObj
}
}],
post: [{
path: "",
results: []
}],
"delete": [{
path: "dashboards/([0-9]+)-demo",
results: function(o, id) {
var dashboards = getData("user/dashboards/dashboards", []);
if (dashboards && dashboards.length) {
var cleanedDashboards = [];
dashboards.some(function(d) {
d.id !== id + "-demo" && cleanedDashboards.push(d)
}), putData("user/dashboards/dashboards", cleanedDashboards)
}
return []
}
}, {
path: "queries/([0-9]+)-demo",
results: function(o, id) {
var queries = getData("user/explore/queries", []);
if (queries && queries.length) {
var cleanedQueries = [];
queries.some(function(d) {
d.id !== id + "-demo" && cleanedQueries.push(d)
}), putData("user/explore/queries", cleanedQueries)
}
return []
}
}],
head: [{
path: "",
results: []
}]
}
}), angular.module("ga.api.userDb.authenticated.dashboard", ["ui.router", "ga.api.userDb", "ga.utils.helpers", "ga.services.user", "ga.services.announcement", "ga.ui.notify"]).service("gaApiUserDbAuthenticatedDashboards", function($q, $http, $stateParams, gaApiUserDb, gaHelpers, gaServicesUser) {
var sortDashboards = function(customSortList, a, b) {
var sortOrderA = customSortList.indexOf(a.id),
sortOrderB = customSortList.indexOf(b.id);
return a.sortOrder = sortOrderA > -1 ? sortOrderA : a.sortOrder, b.sortOrder = sortOrderB > -1 ? sortOrderB : b.sortOrder, a.sortOrder === b.sortOrder ? 0 : a.sortOrder > b.sortOrder ? 1 : -1
};
this.sortDashboards = sortDashboards;
var staticDashboards = null;
this.dashboards = {}, this.getStaticDashboards = function() {
if (staticDashboards) return $q.when(staticDashboards);
var url = "/static/ga-app/modules/api/user-db/authenticated/dashboard.json";
return $http.get(url).then(function(response) {
return staticDashboards = gaHelpers.copy(response.data), $q.when(staticDashboards)
})
}, this.getGameDashboards = function(gameId) {
gameId = gameId || $stateParams.gameId;
var url = "games/" + gameId + "/dashboards";
return gaApiUserDb.get(url)
}, this.getGameDashboardsSortOrder = function(gameId) {
gameId = gameId || $stateParams.gameId;
var url = "games/" + gameId + "/dashboards/sortorder";
return gaApiUserDb.get(url)
}, this.getDashboards = function(gameId) {
return gameId = gameId || $stateParams.gameId, this.dashboards[gameId] ? $q.when(this.dashboards[gameId]) : $q.all({
"static": this.getStaticDashboards(),
game: this.getGameDashboards(gameId),
sort: this.getGameDashboardsSortOrder(gameId)
}).then(function(results) {
return this.dashboards[gameId] = results.static.concat(results.game).sort(sortDashboards.bind(this, results.sort)), $q.when(this.dashboards[gameId])
}.bind(this))
}, this.sortOrder = function(gameId) {
return gameId = gameId || $stateParams.gameId, (this.dashboards[gameId] || []).map(function(dashboard) {
return dashboard.id
})
}, this.lock = function(dashboardId, unlock) {
var url = "dashboards/" + dashboardId + "/" + (unlock ? "unlock" : "lock");
return gaApiUserDb.put(url).then(function(results) {
return $q.when(results[0] || null)
})
}, this._getDashboard = function(dashboardId) {
if (isNaN(parseInt(dashboardId, 10))) {
var found;
return staticDashboards.some(function(dashboard) {
return dashboard.id === dashboardId ? (found = dashboard, !0) : void 0
}), found ? $q.when(found) : $q.reject([{
msg: "Dashboard not found"
}])
}
var url = "dashboards/" + dashboardId;
return gaApiUserDb.get(url).then(function(results) {
return $q.when(results[0] || null)
})
}, this.getDashboard = function(dashboardId) {
return this._getDashboard(dashboardId).then(function(dashboard) {
return dashboard.widgets.forEach(function(widget) {
widget.statusObject = {
state: "init"
}, widget.trackerId = dashboardId + "-" + widget.id
}), $q.when(dashboard)
})
}, this.saveDashboardsOrder = function(sortOrderList, gameId) {
var url = "games/" + gameId + "/dashboards/sortorder",
payload = {
sortorder: sortOrderList
};
return gaApiUserDb.put(url, payload).then(function(results) {
return this.dashboards[gameId] = null, $q.when(results[0] || null)
}.bind(this))
}, this.saveDashboard = function(dashboard, gameId) {
var url;
return url = dashboard.id ? "dashboards/" + dashboard.id : "games/" + gameId + "/dashboards", gaApiUserDb.put(url, dashboard).then(function(results) {
return this.dashboards[gameId] = null, $q.when(results[0] || null)
}.bind(this))
}, this.deleteDashboard = function(dashboardId, gameId) {
var url = "dashboards/" + dashboardId;
return gaApiUserDb.remove(url).then(function(results) {
return this.dashboards[gameId] = null, $q.when(results[0] || null)
}.bind(this))
}, this.resolve = function(stateParams) {
var params = gaHelpers.copy(stateParams),
isStatic = !1;
params.dashboardId = parseInt(stateParams.dashboardId, 10), params.dashboardId !== params.dashboardId && (isStatic = !0, params.dashboardId = stateParams.dashboardId), (stateParams.dashboardId || "").toString().match(/-demo$/) && (params.dashboardId = stateParams.dashboardId);
var dashboard;
return this.dashboards[params.gameId].some(function(d) {
return params.dashboardId === d.id ? (dashboard = d, !0) : void 0
}), "edit" === params.action && 0 === params.dashboardId ? (dashboard = !0, $q.when()) : dashboard ? isStatic && "edit" === params.action ? (params.action = "show", $q.reject(params)) : isStatic || "intro" !== params.action ? isStatic && "show" === params.action && !gaServicesUser.onboarding.dashboard[params.dashboardId] ? (params.action = "intro", $q.reject(params)) : "edit" === params.action && params.dashboardId ? this.lock(params.dashboardId).catch(function() {
return params.action = "show", $q.reject(params)
}) : $q.when(dashboard) : (params.action = "show", $q.reject(params)) : (params.action = "show", params.dashboardId = "engagement", $q.reject(params))
}.bind(this)
}), angular.module("ga.api.userDb.authenticated.query", ["ga.api.userDb", "ui.router"]).factory("gaApiUserDbAuthenticatedQuery", function($q, $http, $state, gaApiUserDb) {
var getQueryList = function() {
var gameId = $state.params.gameId,
url = "games/" + gameId + "/queries";
return gaApiUserDb.get(url).then(function(results) {
return $q.when(results[0] || null)
})
},
saveQuery = function(queryObj) {
var gameId = $state.params.gameId,
url = "games/" + gameId + "/queries";
return gaApiUserDb.put(url, queryObj).then(function(results) {
return $q.when(results[0] || null)
})
},
deleteQuery = function(queryId) {
var url = "queries/" + queryId;
return gaApiUserDb.remove(url).then(function(results) {
return $q.when(results[0] || null)
})
},
getQuery = function(queryId) {
var url = "queries/" + queryId;
return gaApiUserDb.get(url).then(function(results) {
return $q.when(results[0] || null)
})
};
return {
getQueryList: getQueryList,
saveQuery: saveQuery,
deleteQuery: deleteQuery,
getQuery: getQuery
}
}), angular.module("ga.api.userDb.authenticated.heatmap", ["ui.router", "ga.api.userDb"]).factory("gaApiUserDbAuthenticatedHeatmap", function($q, $state, $http, gaApiUserDb) {
var getSettings = function(gameId) {
var url = "games/" + gameId + "/heatmap/settings";
return gaApiUserDb.get(url).then(function(results) {
return $q.when(results[0] || null)
})
},
putSettings = function(gameId, settings) {
var url = "games/" + gameId + "/heatmap/settings";
return gaApiUserDb.put(url, settings).then(function(results) {
return $q.when(results)
})
},
authHeatmap = function(gameId, firsttime) {
var deferred = $q.defer(),
rejectFunc = function(nodata) {
deferred.reject(nodata), nodata ? $state.go("game.heatmap-nodata", {
gameId: gameId
}) : $state.go("game.heatmap-firsttime", {
gameId: gameId
})
};
return gaApiUserDb.get("data_api/games/" + gameId + "/has/heatmap", null, null, !0).then(function(response) {
response.errors.length > 0 || !response.heatmaps ? rejectFunc(!0) : firsttime ? deferred.resolve() : gaApiUserDb.get("games/" + gameId + "/heatmap/settings").then(function(data) {
if (void 0 !== data[0] && data[0].initial_load) rejectFunc(!1);
else {
var settings = data[0].settings;
void 0 !== settings && void 0 !== settings.sets ? (settings.keys = {
gamekey: data[0].heatmap_key,
secretkey: data[0].heatmap_skey,
apikey: data[0].heatmap_dkey
}, settings.gameId = data[0].gameId, deferred.resolve(settings)) : rejectFunc(!1)
}
}, function() {
rejectFunc(!1)
})
}, rejectFunc), deferred.promise
};
return {
authHeatmap: authHeatmap,
getSettings: getSettings,
putSettings: putSettings
}
}), angular.module("ga.api.userDb.authenticated.invite", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedInvite", function($q, $timeout, gaApiUserDb) {
this.accept = function(invite) {
var service = invite.type + "_access/" + invite.id + "/accept_decline",
payload = {
action: "accept"
};
return gaApiUserDb.put(service, payload)
}, this.decline = function(invite) {
var service = invite.type + "_access/" + invite.id + "/accept_decline",
payload = {
action: "decline"
};
return gaApiUserDb.put(service, payload)
}, this.delete = function(invite) {
var service = invite.type + "_access/" + invite.id;
return gaApiUserDb.remove(service)
}, this.change = function(invite) {
var payload = {
role_id: invite.role
},
service = invite.type + "_access/" + invite.id;
return gaApiUserDb.put(service, payload)
}
}), angular.module("ga.api.userDb.authenticated.user", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedUser", function($q, gaApiUserDb) {
this.supportToken = function() {
return gaApiUserDb.get("user/support/token").then(function(results) {
return $q.when(results[0] || null)
})
}, this.getData = function() {
return gaApiUserDb.get("user/data").then(function(results) {
return $q.when(results[0] || null)
})
}, this.saveOnboarding = function(data) {
return data ? gaApiUserDb.put("user/onboarding", data).then(function(results) {
return $q.when(results[0] || null)
}) : void 0
}, this.save = function(data) {
return gaApiUserDb.put("user", data).then(function(results) {
return $q.when(results[0] || null)
})
}, this.unlink = function() {
return gaApiUserDb.put("user/unlink", {})
}, this.link = function(data) {
return gaApiUserDb.put("user/link", data)
}, this.passwordChange = function(data) {
return gaApiUserDb.put("user/password", data)
}, this.subscriptions = function() {
return gaApiUserDb.get("user/subscriptions")
}, this.unsubscribe = function(data) {
return gaApiUserDb.put("user/subscriptions", data)
}, this.createStudio = function(data) {
return gaApiUserDb.put("user/studios", data)
}, this.logout = function() {
return gaApiUserDb.get("user/logout")
}
}), angular.module("ga.api.userDb.authenticated.report", ["ga.api.userDb", "ui.router"]).service("gaApiUserDbAuthenticatedReport", function($q, $state, gaApiUserDb) {
this.getGameReports = function() {
var gameId = $state.params.gameId,
url = "games/" + gameId + "/reports";
return gaApiUserDb.get(url)
}, this.getReportSubscribers = function(reportId) {
var url = "reports/" + reportId + "/report_subscribers";
return gaApiUserDb.get(url)
}, this.updateReportSubscriber = function(subscriberId, subscriberData) {
var url = "report_subscribers/" + subscriberId;
return gaApiUserDb.put(url, subscriberData).then(function(results) {
return $q.when(results[0] || null)
})
}, this.createReportSubscriber = function(reportId, subscriberData) {
var url = "reports/" + reportId + "/report_subscribers";
return gaApiUserDb.put(url, subscriberData).then(function(results) {
return $q.when(results[0] || null)
})
}, this.deleteReportSubscriber = function(subscriberId) {
var url = "report_subscribers/" + subscriberId;
return gaApiUserDb.remove(url)
}
}), angular.module("ga.api.userDb.authenticated.status", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedStatus", function($q, $timeout, gaApiUserDb) {
this.getProcessing = function() {
var url = "data_api/status/processing";
return gaApiUserDb.get(url)
}
}), angular.module("ga.api.userDb.authenticated.game", ["ga.api.userDb", "ui.router"]).service("gaApiUserDbAuthenticatedGame", function($q, $state, gaApiUserDb) {
this.getGameData = function(gameId) {
return gaApiUserDb.get("games/" + gameId).then(function(results) {
return $q.when(results[0] || null)
})
}, this.getGameUsers = function() {
return gaApiUserDb.get("games/" + $state.params.gameId + "/users")
}, this.getAccesses = function() {
return gaApiUserDb.get("games/" + $state.params.gameId + "/accesses")
}, this.getStoreApps = function() {
return gaApiUserDb.get("games/" + $state.params.gameId + "/store_apps")
}, this.createStoreApps = function(payload) {
return gaApiUserDb.put("games/" + $state.params.gameId + "/store_apps", payload)
}, this.addUser = function(gameId, email, role, email_reports) {
var payload = {
role_id: role,
email: email,
add_email_reports: email_reports
},
service = "games/" + gameId + "/accesses";
return gaApiUserDb.put(service, payload)
}, this.saveGame = function(gameId, data) {
return gaApiUserDb.put("games/" + gameId, data).then(function(results) {
return $q.when(results[0] || null)
})
}, this.archiveGame = function(gameId, archiveStatus) {
var payload = {
archive: archiveStatus
},
service = "games/" + gameId + "/archive";
return gaApiUserDb.put(service, payload)
}, this.saveGameLinkNotification = function(gameId, hideNotification) {
var payload = {
hideNotification: hideNotification
},
service = "games/" + gameId + "/link_game_notification";
return gaApiUserDb.put(service, payload)
}, this.saveOldFunnelsImported = function(gameId) {
var payload = {
oldFunnelsImported: !0
},
service = "games/" + gameId + "/old_funnels_imported";
return gaApiUserDb.put(service, payload)
}, this.getFunnels = function(gameId) {
return gaApiUserDb.get("games/" + gameId + "/funnels")
}, this.getOldFunnels = function(gameId) {
return gaApiUserDb.get("games/" + gameId + "/funnels_old")
}, this.createFunnel = function(gameId, funnelName, settings, dateRanges) {
var tmpDateRanges = null;
dateRanges && (tmpDateRanges = dateRanges.map(function(dr) {
return {
id: dr.id,
start: dr.start / 1e3,
end: dr.end / 1e3
}
}));
var payload = {
funnel_name: funnelName,
settings: settings,
date_ranges: tmpDateRanges
},
service = "games/" + gameId + "/funnels";
return gaApiUserDb.put(service, payload).then(function(results) {
return $q.when(results[0] || null)
})
}
}), angular.module("ga.api.userDb.authenticated.studio", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedStudio", function($q, gaApiUserDb) {
this.createGame = function(studioId, data) {
return gaApiUserDb.put("studios/" + studioId + "/games", data)
}, this.saveStudio = function(studioId, data) {
return gaApiUserDb.put("studios/" + studioId, data).then(function(results) {
return $q.when(results[0] || null)
})
}, this.getStudioUsers = function(studioId) {
return gaApiUserDb.get("studios/" + studioId + "/accesses").then(function(results) {
return $q.when(results[0] || null)
})
}, this.addUser = function(studioId, email, role, email_reports) {
var payload = {
role_id: role,
email: email,
add_email_reports: email_reports
},
service = "studios/" + studioId + "/accesses";
return gaApiUserDb.put(service, payload)
}, this.archiveStudio = function(studioId, archiveStatus) {
var payload = {
archive: archiveStatus
},
service = "studios/" + studioId + "/archive";
return gaApiUserDb.put(service, payload)
}
}), angular.module("ga.api.userDb.authenticated.genre", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedGenre", function(gaApiUserDb) {
this.getGenres = function() {
return gaApiUserDb.get("game_genres")
}
}), angular.module("ga.api.userDb.authenticated.appfigures", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedAppfigures", function($q, $timeout, $window, gaApiUserDb) {
this.getAppMeta = function(productid) {
return gaApiUserDb.get("data_api/appfigures/meta?productid=" + productid).then(function(results) {
return $q.when(results[0] || null)
})
}, this.search = function(query) {
var q = encodeURIComponent(query.replace(/ \\/g, "+"));
return gaApiUserDb.get("data_api/appfigures/search?query=" + q).then(function(results) {
return $q.when(results[0] || null)
}).catch(function() {})
}
}), angular.module("ga.api.userDb.authenticated.store_app", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedStoreApp", function($q, $timeout, gaApiUserDb) {
this.update = function(store_app_id, payload) {
var url = "store_apps/" + store_app_id;
return gaApiUserDb.put(url, payload)
}, this.delete = function(store_app_id) {
var url = "store_apps/" + store_app_id;
return gaApiUserDb.remove(url)
}
}), angular.module("ga.api.userDb.authenticated.funnel", ["ga.api.userDb", "ui.router"]).service("gaApiUserDbAuthenticatedFunnel", function($q, $state, gaApiUserDb) {
this.updateFunnel = function(funnelId, funnelName, settings, dateRanges) {
var tmpDateRanges = null;
dateRanges && (tmpDateRanges = dateRanges.map(function(dr) {
return {
id: dr.id,
start: dr.start / 1e3,
end: dr.end / 1e3,
backendId: dr.backendId,
"delete": dr.delete || !1
}
}));
var payload = {
funnel_name: funnelName,
settings: settings,
date_ranges: tmpDateRanges
},
service = "funnels/" + funnelId;
return gaApiUserDb.put(service, payload)
}, this.deleteFunnel = function(funnelId) {
var service = "funnels/" + funnelId;
return gaApiUserDb.remove(service)
}
}), angular.module("ga.api.userDb.authenticated.haystack", ["ga.api.userDb"]).service("gaApiUserDbAuthenticatedHaystack", function($q, gaApiUserDb) {
this.haystackJson = function() {
return gaApiUserDb.get("admin/haystack.json", null, null, !0).then(function(response) {
return $q.when(response)
})
}
}), angular.module("ga.api.userDb.public.user", ["ga.api.userDb"]).service("gaApiUserDbPublicUser", function($q, gaApiUserDb) {
this.getData = function(token) {
return gaApiUserDb.get("public/user/" + token).then(function(results) {
return $q.when(results[0] || null)
})
}, this.saveOnboarding = function() {
return $q.when()
}, this.save = function() {
return $q.when()
}, this.subscriptions = function() {
return $q.when([])
}, this.unsubscribe = function() {
return $q.when()
}, this.createStudio = function() {
return $q.when()
}
}), angular.module("ga.api.userDb.public.activate", ["ga.api.userDb"]).service("gaApiUserDbPublicActivate", function(gaApiUserDb) {
this.sendActivationEmail = function(email) {
return gaApiUserDb.put("public/activate_account/" + email)
}, this.checkActivation = function(email) {
return gaApiUserDb.get("public/activate_account_check/" + email)
}
}), angular.module("ga.api.userDb.public.invite", ["ga.api.userDb", "ga.utils.cache"]).service("gaApiUserDbPublicInvite", function($state, $q, gaApiUserDb, gaUtilsCache) {
this.getInviteInfo = function(email, resource, token) {
var dataPayload = {
email: email,
resource: resource,
token: token
},
tryCache = gaUtilsCache.get(dataPayload.resource + dataPayload.token);
return tryCache && $q.when(tryCache) || gaApiUserDb.post("public/invite", dataPayload).then(function(results) {
return results[0] && gaUtilsCache.put(dataPayload.resource + dataPayload.token, results[0], null, 1e4), $q.when(results[0] || null)
})
}
}), angular.module("ga.api.userDb.public.signup", ["ga.api.userDb"]).service("gaApiUserDbPublicSignup", function($q, $http, $state, gaApiUserDb) {
this.signupUser = function(params) {
var url = "public/signup/basic";
return gaApiUserDb.put(url, params).then(function(results) {
return $q.when(results[0] || null)
})
}, this.activateAccount = function(params) {
var url = "public/activate_account";
return gaApiUserDb.put(url, params).then(function(results) {
return $q.when(results[0] || null)
})
}, this.createAccount = function(params) {
var url = "public/create_account";
return gaApiUserDb.put(url, params).then(function(results) {
return $q.when(results[0] || null)
})
}
}), angular.module("ga.api.userDb.public.unsubscribe", ["ga.api.userDb"]).service("gaApiUserDbPublicUnsubscribe", function(gaApiUserDb) {
this.getSubscriptions = function(email, token) {
var url = "public/reports/subscriptions/" + email + "/" + token;
return gaApiUserDb.get(url)
}, this.unsubscribe = function(email, token, data) {
var url = "public/reports/subscriptions/" + email + "/" + token;
return gaApiUserDb.put(url, data)
}
}), angular.module("ga.api.userDb.public.login", ["ga.api.userDb"]).service("gaApiUserDbPublicLogin", function($q, gaApiUserDb) {
this.loginBasic = function(email, password, remember) {
var dataPayload = {
email: email,
password: password,
remember: !!remember
};
return gaApiUserDb.post("public/login/basic", dataPayload).then(function(results) {
return $q.when(results[0] || null)
})
}
}), angular.module("ga.api.userDb.public.linkAccount", ["ga.api.userDb"]).service("gaApiUserDbPublicLinkAccount", function($q, gaApiUserDb) {
this.link = function(dataPayload) {
return gaApiUserDb.put("public/link_account", dataPayload).then(function(results) {
return $q.when(results[0] || null)
})
}
}), angular.module("ga.api.userDb.public.passwordReset", ["ga.api.userDb"]).service("gaApiUserDbPublicPasswordReset", function($q, gaApiUserDb) {
this.info = function(token) {
return gaApiUserDb.get("public/password_reset/" + token).then(function(results) {
return $q.when(results[0] || null)
})
}, this.reset = function(data, token) {
return gaApiUserDb.put("public/password_reset/" + token, data).then(function(results) {
return $q.when(results[0] || null)
})
}, this.request = function(data) {
return gaApiUserDb.put("public/password_reset", data).then(function(results) {
return $q.when(results[0] || null)
})
}
}), angular.module("ga.api.statuspage", ["ui.router", "ga.utils.cookie", "ga.values.user", "ga.services.announcement"]).factory("gaApiStatuspage", function($rootScope, $http, $timeout, $state, gaValuesUser, gaServicesAnnouncement) {
var $scope = $rootScope.$new(),
statusPageUrl = "https://gameanalytics.statuspage.io/index.json";
$scope.data = null;
var updateStatusTimer, updateStatus = function() {
$timeout.cancel(updateStatusTimer), $state.includes("game") || $state.includes("user.home") ? $http.get(statusPageUrl).then(function(response) {
$scope.data = response.data, updateStatusTimer = $timeout(updateStatus, 6e5)
}).catch(function() {
updateStatusTimer = $timeout(updateStatus, 6e5)
}) : updateStatusTimer = $timeout(updateStatus, 6e4)
};
return $scope.$watch("data.status", function(newVal, oldVal) {
if (newVal && !angular.equals(newVal, oldVal))
if ($rootScope.$broadcast("GaStatusChange", newVal), "none" !== newVal.indicator) {
var components = [];
if (angular.forEach($scope.data.components, function(component) {
"operational" !== component.status && components.push("<strong>" + component.name + "</strong>")
}), components.length > 1) {
var last = components.pop();
components[components.length - 1] = components[components.length - 1] + " and " + last
}
var content = "We are currently experiencing some issues with our " + components.join(", ");
content += ' - keep updated on our <a href="http://gameanalytics.statuspage.io/" target="_blank">status page</a>', gaServicesAnnouncement.add({
id: "status-page-io",
icon: "ga-icon-severity-warning",
style: "yellow",
content: content,
replace: !0
})
} else gaServicesAnnouncement.hide("status-page-io")
}), {
data: $scope.data,
updateStatus: updateStatus
}
}), angular.module("ga.api.s3", ["ga.utils.cache"]).factory("gaApiS3", function($http, $q, gaUtilsCache) {
var s3Url = "https://s3.amazonaws.com/",
downloadBucket = "public.gameanalytics.com/",
getSdkChangelogs = function(force) {
var sdkChangesFile = "sdk_status/change_logs.json",
requestURL = s3Url + downloadBucket + sdkChangesFile,
deferred = $q.defer(),
changelogs = force ? null : gaUtilsCache.get("S3SdkChangeLogs", "localStorage");
if (changelogs) deferred.resolve(changelogs);
else {
var Success = function(response) {
deferred.resolve(response.data);
var cacheTime = 18e5;
gaUtilsCache.put("S3SdkChangeLogs", response.data, "localStorage", cacheTime)
},
Error = function() {
deferred.reject()
};
$http.get(requestURL).then(Success, Error)
}
return deferred.promise
},
getSdkLatestVersions = function() {
var sdkVersionsFile = "sdk_status/current.json",
requestURL = s3Url + downloadBucket + sdkVersionsFile,
deferred = $q.defer();
return $http.get(requestURL).then(function(response) {
deferred.resolve(response.data)
}, function(response) {
deferred.reject(response)
}), deferred.promise
},
getSdkMeta = function() {
var sdkInfo = {
android: {
icon: "",
name: "Android",
repoUrl: "https://github.com/GameAnalytics/GA-Android-SDK"
},
ios: {
icon: "",
name: "iOS",
repoUrl: "https://github.com/GameAnalytics/GA-iOS-SDK"
},
corona: {
icon: "",
name: "Corona",
repoUrl: "https://github.com/GameAnalytics/GA-Corona-SDK"
},
unity: {
icon: "",
name: "Unity",
repoUrl: "https://github.com/GameAnalytics/GA-Unity-SDK"
},
flash: {
icon: "",
name: "Flash",
repoUrl: "https://github.com/GameAnalytics/GA-Flash-SDK"
}
};
return sdkInfo
};
return {
getSdkLatestVersions: getSdkLatestVersions,
getSdkChangelogs: getSdkChangelogs,
getSdkMeta: getSdkMeta
}
}), angular.module("ga.values.user", []).value("gaValuesUser", {
settings: {},
details: {},
token: null,
readonly: null,
activated: null,
gameToken: function() {
return null
},
game: function() {
return null
},
id: 0
}), angular.module("ga.services.user", ["ui.router", "ga.values.user", "ga.config", "ga.api.userDb.public.login", "ga.api.userDb.authenticated.user", "ga.api.userDb.authenticated.invite", "ga.utils.cookie", "ga.utils.helpers", "ga.services.game", "ga.ui.modal", "ga.pages.user.invites", "ga.pages.user.passwordChange", "ga.pages.public.passwordReset", "ga.services.pardot"]).service("gaServicesUser", function($window, $state, $rootScope, $q, $timeout, gaConfig, gaValuesUser, gaApiUserDbPublicLogin, gaApiUserDbAuthenticatedInvite, gaApiUserDbAuthenticatedUser, gaUtilsCookie, gaHelpers, gaServicesGame, gaUiModal, gaServicesPardot) {
var detectSleepTimer, detectSleepLast, detectSleep = function() {
var currentTime = (new Date).getTime();
currentTime > detectSleepLast + 1e4 + 2e3 && this.token && this.getUserData(), detectSleepLast = (new Date).getTime()
};
detectSleepTimer = setInterval(detectSleep.bind(this), 1e4);
var _userToken = {
token: null,
exp: 0
},
_userData = null,
_userFlags = {},
getUserDataInterval = 6e5,
getUserDataTimer = null,
getUserDataInProgress = !1,
populateValues = function() {
Object.defineProperties(gaValuesUser, {
token: {
enumerable: !0,
get: function() {
return this.token
}.bind(this)
},
gameToken: {
enumerable: !1,
value: this.gameToken.bind(this)
},
settings: {
enumerable: !0,
get: this.settings.serialize.bind(this)
},
details: {
enumerable: !0,
get: this.details.serialize.bind(this)
},
activated: {
enumerable: !0,
get: function() {
return this.activated
}.bind(this)
},
game: {
enumerable: !1,
value: function(gameId) {
return this.game(gameId)
}.bind(this)
},
id: {
enumerable: !0,
get: function() {
return this.id
}.bind(this)
}
})
}.bind(this),
updateUserData = function(newData, noBroadcast) {
var oldData = this.data;
gaHelpers.equals(newData.invites, oldData.invites) || (_userData.invites = newData.invites, !noBroadcast && $rootScope.$broadcast("userInvitesChange")), gaHelpers.equals(newData.studiosGames, oldData.studiosGames, ["dataApiToken"]) ? _userData.studiosGames.forEach(function(studio, studioIndex) {
studio.games.forEach(function(game, gameIndex) {
game.dataApiToken = newData.studiosGames[studioIndex].games[gameIndex].dataApiToken
})
}) : (_userData.studiosGames = newData.studiosGames, !noBroadcast && $rootScope.$broadcast("userStudiosChange"))
}.bind(this),
parseStudio = function(studio) {
var parsed = gaHelpers.copy(studio);
return parsed.games = (parsed.games || []).map(parseGame.bind(this, parsed)), parsed.imagePath = studio.demo ? "/static/ga-app/images/demo-studio-icon.png" : parsed.imageFile ? gaConfig.images.baseUrl + parsed.imageFile : "/static/ga-app/images/default-studio-icon.png", parsed.owner = "owner" === parsed.access.role, parsed.admin = parsed.owner || "admin" === parsed.access.role, parsed.viewer = parsed.admin || "viewer" === parsed.access.role || this.demo, parsed
}.bind(this),
parseGame = function(studio, game) {
var parsed = gaHelpers.copy(game);
return parsed.studioId = studio.id, parsed.owner = "owner" === parsed.access.role, parsed.admin = studio.admin || parsed.owner || "admin" === parsed.access.role, parsed.viewer = studio.viewer || parsed.admin || "viewer" === parsed.access.role, parsed.imagePath = studio.demo ? "/static/ga-app/images/demo-game-icon.png" : parsed.imageFile ? gaConfig.images.baseUrl + parsed.imageFile : "/static/ga-app/images/default-game-icon.png", Object.defineProperties(parsed, {
status: {
enumerable: !1,
value: gaServicesGame.getStatus.bind(gaServicesGame, game.id)
},
numbers: {
enumerable: !1,
value: gaServicesGame.getNumbers.bind(gaServicesGame, game.id)
},
token: {
enumerable: !1,
get: function() {
return game.dataApiToken.token
}
}
}), parsed
}.bind(this),
parseInvite = function(invite) {
return invite.type = invite.title ? "game" : "studio", invite.name = invite.name || invite.title, Object.defineProperties(invite, {
accept: {
enumerable: !1,
value: function() {
return gaApiUserDbAuthenticatedInvite.accept(invite).then(function() {
return this.getUserData()
}.bind(this))
}.bind(this)
},
decline: {
enumerable: !1,
value: function() {
return gaApiUserDbAuthenticatedInvite.decline(invite).then(function() {
return invite.remove(), $q.when()
})
}.bind(this)
},
remove: {
enumerable: !1,
value: function() {
if (_userData) {
var found = _userData.invites[invite.type].some(function(item, index) {
return item.id === invite.id ? (_userData.invites[invite.type].splice(index, 1), !0) : void 0
});
return found && $rootScope.$broadcast("userInvitesChange"), found
}
}.bind(this)
}
}), invite
}.bind(this);
this.settings = {}, this.invites = {}, this.details = {}, this.logout = function() {
gaApiUserDbAuthenticatedUser.logout(), this.token = null
}, this.init = function() {
this.token = gaHelpers.parse(gaUtilsCookie.get("gaUserToken"), {
token: null,
exp: 0
}), populateValues.bind(this)()
}, this.dialogInvites = function() {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/user/invites/invites.html",
controller: "gaPagesUserInvitesController",
parameters: {},
escape: !0,
width: 800
})
}, this.dialogPasswordChange = function() {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/user/password-change/password-change.html",
controller: "gaPagesUserPasswordChangeController",
parameters: {},
escape: !0,
width: 600
})
}, this.dialogPasswordForgot = function(email) {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/public/password-forgot/password-forgot.html",
controller: "gaPagesPublicPasswordForgotController",
parameters: {
data: {
email: email || this.details.email
}
},
escape: !0,
width: 600
})
}, this.login = function(email, password, remember) {
return gaApiUserDbPublicLogin.loginBasic(email, password, remember).then(function(result) {
return this.token = result, this.resolveUser()
}.bind(this))
}, this.getUserData = function(noBroadcast, forceRefresh) {
clearTimeout(getUserDataTimer);
var init = !_userData;
return this.token ? (getUserDataInProgress = !0, gaApiUserDbAuthenticatedUser.getData().then(function(result) {
return getUserDataInProgress = !1, this.token = result.userApiToken, result.pardotToken && gaServicesPardot.addProspect(result.details.email, result.pardotToken.token), delete result.pardotToken, init ? _userData = result : forceRefresh ? (_userData = result, !noBroadcast && $rootScope.$broadcast("forcedUserDataChange")) : updateUserData(result, noBroadcast), getUserDataTimer = setTimeout(this.getUserData.bind(this), getUserDataInterval), $q.when(result)
}.bind(this)).catch(function(errors) {
return getUserDataInProgress = !1, this.token = null, $q.reject(errors)
}.bind(this))) : $q.reject([{
msg: "No token"
}])
}, this.resolveUser = function(retryNr) {
return retryNr = retryNr || 0, this.token ? _userData ? $q.when(_userData) : this.getUserData() : $q.reject("No token present")
}, Object.defineProperties(this, {
supportToken: {
enumerable: !1,
value: function() {
return this.token ? gaApiUserDbAuthenticatedUser.supportToken() : $q.when(null)
}.bind(this)
},
instance: {
enumerable: !1,
get: function() {
var userInstance = angular.element.extend({
name: this.details.name,
id: this.id,
linked: this.linked
}, this.details.serialize(), this.settings.serialize());
return userInstance
}
},
activated: {
enumerable: !0,
get: function() {
return _userFlags && _userFlags.nonActivated ? !1 : !0
}
},
demoGameEnabled: {
enumerable: !0,
get: function() {
return _userData && _userData.demoGameEnabled || !1
}
},
admin: {
enumerable: !0,
get: function() {
return _userData && _userData.admin || !1
}
},
adminLoggedIn: {
enumerable: !0,
get: function() {
return _userData && _userData.adminLoggedIn || !1
}
},
impersonated: {
enumerable: !0,
get: function() {
return _userData && _userData.impersonated || !1
}
},
id: {
enumerable: !0,
get: function() {
return _userData ? _userData.id : 0
}
},
linked: {
enumerable: !0,
get: function() {
return _userData ? _userData.linked : null
},
set: function(newLinked) {
_userData && (_userData.linked = newLinked)
}
},
demo: {
enumerable: !0,
get: function() {
return _userData ? !!_userData.demo : !1
}
},
demoStudio: {
enumerable: !0,
get: function() {
return _userData ? !!_userData.demoStudio : !1
}
},
flags: {
enumerable: !1,
get: function() {
return _userFlags || {}
}
},
token: {
enumerable: !1,
get: function() {
return !_userToken || this.expires <= 0 ? null : _userToken.token
}.bind(this),
set: function(token) {
if (void 0 === token) return !1;
if (null === token || token === !1) _userToken = {
token: null,
exp: 0
}, gaUtilsCookie.remove("gaUserToken"), localStorage.clear(), _userData = null, _userFlags = {};
else if (token && void 0 !== token.token && void 0 !== token.exp) {
_userToken = {
token: token.token,
exp: token.exp
}, _userToken.exp = 1e3 * parseInt(_userToken.exp, 10), gaUtilsCookie.set("gaUserToken", JSON.stringify(token), 14);
for (var flag in token.flags) _userFlags[flag] = token.flags[flag]
} else gaUtilsCookie.remove("gaUserToken"), localStorage.clear()
}
},
expires: {
enumerable: !1,
get: function() {
return _userToken && _userToken.token ? 1 : 0
}
},
data: {
enumerable: !1,
get: function() {
return _userData ? gaHelpers.copy(_userData) : null
}
},
studios: {
enumerable: !0,
get: function() {
return (_userData && _userData.studiosGames ? _userData.studiosGames : []).map(parseStudio)
}
},
studio: {
enumerable: !1,
value: function(studioId) {
var found = null;
return (_userData && _userData.studiosGames ? _userData.studiosGames : []).some(function(studio) {
return studio.id === studioId ? (found = parseStudio(studio), !0) : void 0
}), found
}
},
game: {
enumerable: !1,
value: function(gameId) {
var found = null;
return (_userData ? _userData.studiosGames : []).some(function(studio) {
return studio.games.some(function(game) {
return game.id === gameId ? (found = parseGame(studio, game), !0) : void 0
})
}), found
}
},
gameToken: {
enumerable: !1,
value: function(gameId) {
var game = this.game(parseInt(gameId, 10));
return game ? game.token : null
}
},
onboarding: {
enumerable: !1,
get: function() {
var onboarding = gaHelpers.serializeObject(_userData && _userData.onboarding || {});
return onboarding.dashboard = onboarding.dashboard || {}, onboarding.dashboard.realtime = !0, Object.defineProperties(onboarding, {
set: {
enumerable: !1,
value: function(section, key, boolValue) {
_userData && (_userData.onboarding = _userData.onboarding || {}, _userData.onboarding[section] = _userData.onboarding[section] || {}, _userData.onboarding[section][key] = !!boolValue)
}
},
save: {
enumerable: !1,
value: gaApiUserDbAuthenticatedUser.saveOnboarding.bind(gaApiUserDbAuthenticatedUser, (_userData || {}).onboarding)
}
}), onboarding
}
},
invites: {
enumerable: !1,
get: function() {
if (!_userData || !_userData.invites) return [];
var allInvites = gaHelpers.copy(_userData.invites.studio.concat(_userData.invites.game)).map(parseInvite).sort(function(a, b) {
return a.invitedTs === b.invitedTs ? 0 : a.invitedTs < b.invitedTs ? 1 : -1
});
return allInvites
}
},
invite: {
enumerable: !1,
value: function(id, type) {
if ("studio" !== type && "game" !== type) return null;
var found = null;
return (_userData ? _userData.invites[type] : []).some(function(invite) {
invite.id === id && (found = parseInvite(gaHelpers.copy(invite)))
}), found
}
}
}), Object.defineProperties(this.settings, {
timeZone: {
enumerable: !0,
get: function() {
return _userData && _userData.settings.timeZone || "Europe/London"
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.settings.timeZone = value
}
},
numberFormat: {
enumerable: !0,
get: function() {
return _userData && _userData.settings.numberFormat || "1"
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.settings.numberFormat = value
}
},
startOfWeek: {
enumerable: !0,
get: function() {
return _userData && _userData.settings.startOfWeek || "Monday"
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.settings.startOfWeek = value
}
},
dateFormat: {
enumerable: !0,
get: function() {
return _userData && _userData.settings.dateFormat || "MDY"
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.settings.dateFormat = value
}
},
timeFormat: {
enumerable: !0,
get: function() {
return _userData && _userData.settings.timeFormat || "24hour"
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.settings.timeFormat = value
}
},
currencyDefault: {
enumerable: !0,
get: function() {
return _userData && _userData.settings.currencyDefault || "USD"
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.settings.currencyDefault = value
}
},
serialize: {
enumerable: !1,
value: gaHelpers.serializeObject.bind(this, this.settings)
},
save: {
enumerable: !1,
value: function(newValues) {
return gaApiUserDbAuthenticatedUser.save(newValues).then(function() {
var old = this.settings.serialize();
_userData.settings = angular.element.extend(_userData.settings, newValues), $rootScope.$broadcast("userSettingsChange", this.settings.serialize(), old)
}.bind(this))
}.bind(this)
}
}), Object.defineProperties(this.details, {
firstName: {
enumerable: !0,
get: function() {
return _userData ? _userData.details.firstName : ""
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.details.firstName = value
}
},
lastName: {
enumerable: !0,
get: function() {
return _userData ? _userData.details.lastName : ""
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.details.lastName = value
}
},
city: {
enumerable: !0,
get: function() {
return _userData ? _userData.details.city : ""
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.details.city = value
}
},
country: {
enumerable: !0,
get: function() {
return _userData ? _userData.details.country : ""
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.details.country = value
}
},
name: {
enumerable: !1,
get: function() {
return this.activated ? (this.details.firstName + " " + this.details.lastName).trim() : this.details.email
}.bind(this)
},
email: {
enumerable: !0,
get: function() {
return _userData ? _userData.details.email : ""
},
set: function(value) {
if (!_userData) throw new Error("No user is logged in");
_userData.details.email = value
}
},
serialize: {
enumerable: !1,
value: gaHelpers.serializeObject.bind(this, this.details)
},
save: {
enumerable: !1,
value: function(newValues) {
var details = gaHelpers.copy(newValues);
return delete details.email, gaApiUserDbAuthenticatedUser.save(details).then(function() {
return _userData.demoGameEnabled !== newValues.demoGameEnabled ? (_userData.demoGameEnabled = newValues.demoGameEnabled, this.getUserData().then(function() {
return $rootScope.$broadcast("userDemoGameChange"), $rootScope.$broadcast("userDetailsChange"), $q.when()
})) : (_userData.details = angular.element.extend(_userData.details, newValues), $rootScope.$broadcast("userDetailsChange"), $q.when())
}.bind(this))
}.bind(this)
}
});
var storageEventHandler = function(e) {
if ("gaUserToken" === e.key) {
var newValue = gaHelpers.parse(e.newValue),
oldValue = gaHelpers.parse(e.oldValue);
oldValue && oldValue.token ? oldValue && oldValue.token && (newValue && newValue.token || (this.token = null, $state.reload())) : newValue && newValue.token && (this.token = newValue, this.resolveUser().then(function() {
$state.reload()
}))
}
};
$window.addEventListener("storage", storageEventHandler.bind(this), !1), this.__mockUser = function(mockData) {
_userData = mockData
}, this.init()
}), angular.module("ga.services.user.mock", ["ga.services.user", "ga.values.user"]).service("gaServicesUserMock", function(gaServicesUser) {
this.mockUser = function() {
var expires = Math.round(((new Date).getTime() + 12e5) / 1e3),
game = {
access: {
flags: {
business: !0
},
role: "owner",
level: 99
},
imageName: "test.jpeg",
dataApiToken: {
token: '><(((">oOo<")><',
exp: expires
},
id: 1,
title: "Test Game"
},
mockData = {
onboarding: {
cohort: {
tour: !0
},
explore: {
tour: !0
},
dashboard: {
engagement: !0,
tour: !0,
realtimeTour: !0,
acquisition_notice: !0,
quality: !0,
acquisition: !0,
monetization: !0
}
},
studiosGames: [{
access: {
flags: {
business: !0
},
role: "owner",
level: 99
},
imageName: null,
games: [game],
id: 1,
name: "Test Studio"
}],
details: {
city: null,
firstName: "John",
country: null,
lastName: "Doe",
createdDate: "2013-11-19T11:37:14",
id: 3,
phone: null,
email: "john.doe@gameanalytics.com"
},
userApiToken: {
token: '><((">oOo<"))><',
id: 1,
exp: expires
},
settings: {
numberFormat: "1",
dateFormat: "YMD",
timeFormat: "24hour",
timeZone: "Europe/London",
currencyDefault: "USD",
startOfWeek: "Monday"
}
};
gaServicesUser.token = {
token: '><(("> <"))><',
exp: expires
}, gaServicesUser.__mockUser(mockData)
}, this.unmockUser = function() {
gaServicesUser.token = null
}
}).run(function(gaServicesUserMock) {
gaServicesUserMock.mockUser()
}), angular.module("ga.services.game", ["ga.config", "ga.values.user", "ga.api.data", "ga.utils.query", "ga.utils.cache", "ga.utils.date", "ga.api.userDb.authenticated.game"]).service("gaServicesGame", function($window, $q, $http, gaConfig, gaValuesUser, gaApiData, gaUtilsQuery, gaUtilsCache, gaUtilsDate, gaApiUserDbAuthenticatedGame) {
var statusCacheInterval = 15e3,
gameNumbersCacheInterval = 36e5,
validateBatchResponse = function(responses) {
var numbers = {},
error = responses.data.some(function(response) {
if (!response || 200 !== response.code || !response.body || !response.body.aggregated) return !0;
for (var event in response.body.aggregated) {
response.body.aggregated[event].total = response.body.aggregated[event].total || 0;
var dimensions = null;
if (response.body.aggregated[event].dimensions) {
dimensions = [];
for (var dimension in response.body.aggregated[event].dimensions)
for (var key in response.body.aggregated[event].dimensions[dimension]) dimensions.push({
key: key,
total: response.body.aggregated[event].dimensions[dimension][key] || 0
})
}
response.body.aggregated[event].dimensions = dimensions && dimensions.sort(function(a, b) {
return a.total === b.total ? 0 : a.total > b.total ? 1 : -1
}) || null, numbers[event] = response.body.aggregated[event]
}
});
return error ? $q.reject({
data: {
error: "invalid_response"
}
}) : $q.when(numbers)
},
getConfig = function(gameId) {
return {
headers: {
Authorization: gaValuesUser.gameToken(gameId)
}
}
};
this.getMetrics = function(gameId, metrics, interval) {
var realtime = metrics.some(function(metric) {
return "operations" === metric.category
});
realtime && (interval = "last24hours"), interval = "string" == typeof interval ? gaUtilsDate.getPeriodRange(interval) : interval;
var batchCalls = metrics.map(function(metric) {
return "/v1/games/" + gameId + "/" + metric.category + "/" + metric.event + "?start=" + interval.start / 1e3 + "&end=" + interval.end / 1e3 + "&aggregation=" + (metric.aggregation || "mean") + (metric.filter ? "&" + metric.filter.dimension + "=" + $window.escape(metric.filter.values.join(",")) : "")
}),
config = getConfig(gameId),
url = gaConfig.getUrl(!0, realtime) + gameId;
return $http.post(url, batchCalls, config).then(validateBatchResponse)
}, this.getDimensions = function(gameId) {
var config = getConfig(gameId),
url = gaConfig.getUrl(!1, !1) + gameId + "/dimensions";
return $http.get(url, config).then(function(response) {
return $q.when(response.data)
}).catch(function(response) {
return $q.reject(response.data)
})
}, this.getStatus = function(gameId, numbers) {
var result = gaUtilsCache.get("game_status" + (numbers ? "_numbers" : "") + "_" + gameId, "localStorage");
if (result) return $q.when(result);
var status = {
gameId: gameId,
eventCount: 0,
rejectedEvents: 0,
concurrentUsers: 0,
topSdk: null,
initialized: !1,
data: !1,
data24: !1,
error: !1
};
return this.getDimensions(gameId).then(function(response) {
status.initialized = !0;
var hasKeys;
for (hasKeys in response) break;
hasKeys && (status.data = !0);
var metrics = [{
category: "operations",
event: "event_count",
aggregation: "event_count"
}];
return numbers && (metrics = [{
category: "operations",
event: "event_count",
aggregation: "event_count",
filter: {
dimension: "sdk_version",
values: ["*"]
}
}, {
category: "operations",
event: "rejected_events",
aggregation: "event_count"
}, {
category: "operations",
event: "concurrent_users",
aggregation: "sum"
}]), this.getMetrics(gameId, metrics).then(function(numbers) {
return numbers.event_count.dimensions && numbers.event_count.dimensions[0] && null !== numbers.event_count.dimensions[0].key && (status.topSdk = numbers.event_count.dimensions[0].key, status.topSdk = "null" === status.topSdk ? "Unknown" : status.topSdk), status.eventCount = numbers.event_count.total || 0, status.rejectedEvents = numbers.rejected_events && numbers.rejected_events.total || 0, status.concurrentUsers = numbers.concurrent_users && numbers.concurrent_users.total || 0, status.data24 = !!numbers.event_count.total, status.eventCount - status.rejectedEvents > 0 && (status.data = !0), $q.when(status)
})
}.bind(this)).catch(function(response) {
return response && "no_game_api_key" === response.error || (status.error = !0), $q.when(status)
}).then(function(response) {
return gaUtilsCache.put("game_status" + (numbers ? "_numbers" : "") + "_" + gameId, response, "localStorage", statusCacheInterval), $q.when(response)
})
}, this.realtimeNumbers = function(gameId) {
var query = gaUtilsQuery.getQuery({
gameId: gameId,
compare: !1,
interval: "lastWeek",
metric: {
category: "operations",
event: "rejected_events"
},
filter: null,
group: "time",
aggregation: "event_count"
}),
query2 = angular.copy(query);
query2.metric = {
category: "operations",
event: "concurrent_users"
}, query2.aggregation = "sum";
var promises = {
events: gaApiData.get(query),
users: gaApiData.get(query2)
};
return $q.all(promises).then(function(response) {
var result = {
rejectedEvents: response.events.noData ? 0 : response.events.data.aggregated[0].total,
users: response.users.noData ? 0 : response.users.data.aggregated[0].total
};
return $q.when(result)
})
}, this.getNumbers = function(gameId) {
var result = gaUtilsCache.get("gameNumbers_" + gameId, "localStorage");
if (result) return $q.when(result);
var promises = [this.getMetricNumber(gameId, {
category: "core",
event: "DAU"
}), this.getMetricNumber(gameId, {
category: "core",
event: "installs"
}), this.getMetricNumber(gameId, {
category: "core",
event: "ARPDAU"
})];
return $q.all(promises).then(function(result) {
return gaUtilsCache.put("gameNumbers_" + gameId, result, "localStorage", gameNumbersCacheInterval, 1), $q.when(result)
})
}, this.getMetricNumber = function(gameId, metric, aggregation, interval) {
interval = interval || "yesterday";
var query = gaUtilsQuery.getQuery({
gameId: gameId,
compare: !0,
interval: interval,
metric: metric,
filter: null,
group: "time",
aggregation: aggregation || "mean"
});
return gaApiData.get(query).then(function(response) {
var result = {
title: response.data.aggregated[0].meta.title,
value: response.data.aggregated[0].total || 0,
cValue: response.data.aggregated[0].cTotal || 0
};
return result.diff = result.value - result.cValue, result.percent = 0 === result.diff ? 0 : result.diff / result.cValue, result.percentClass = result.percent ? result.percent > 0 ? "ga color green" : "ga color red" : "", result.percent > 10 || 1 / 0 === result.percent ? (result.percent = 10, result.percentClass += " more") : (result.percent < -10 || result.percent === -1 / 0) && (result.percent = -10, result.percentClass += " less"), $q.when(result)
}).catch(function() {
var result = {
title: "&nbsp;",
value: 0,
cValue: 0
};
return result.diff = 0, result.percent = 0, $q.when(result)
})
}, this.saveGameLinkNotification = function(gameId, hideNotification) {
return gaApiUserDbAuthenticatedGame.saveGameLinkNotification(gameId, hideNotification)
}
}), angular.module("ga.services.dataStatus", ["ga.api.data", "ga.utils.date", "ui.router"]).service("gaServicesDataStatus", function($state, gaApiData, gaUtilsDate) {
var lessThan = "is less than",
moreThan = "is more than";
this.lag = {
updateInterval: 6e4,
listeners: [],
timer: null,
last: null,
on: function(callback, id) {
"$id" in id && (id.$on("$destroy", this.lag.off.bind(this, id.$id)), id = id.$id), id = (id || callback).toString(), this.lag.listeners.push({
id: id,
callback: callback
}), 1 === this.lag.listeners.length && this.lag.update(), this.lag.last && callback(null, this.lag.last)
}.bind(this),
off: function(id) {
id = id.toString(), this.lag.listeners = this.lag.listeners.filter(function(listener) {
return listener.id !== id
}), this.lag.listeners.length || clearTimeout(this.lag.timer)
}.bind(this),
update: function() {
clearTimeout(this.lag.timer), gaApiData.getValue("/operations/lag", $state.params.gameId, !0).then(this.lag.updateSuccess.bind(this)).catch(this.lag.updateError.bind(this))
}.bind(this),
updateError: function() {
this.lag.listeners.forEach(function(listener) {
listener.callback("response error", null)
}), this.lag.listeners.length && (this.lag.timer = setTimeout(this.lag.update.bind(this), this.lag.updateInterval))
}.bind(this),
updateSuccess: function(response) {
var callbackParams = [];
if (response && response.lag && null !== response.lag.seconds) {
var serverNow = gaUtilsDate.moment(1e3 * response.timestamp).utc(),
processing = serverNow.clone().add(-response.lag.seconds, "seconds"),
lag = {};
lag.s = lag.raw = parseInt(response.lag.seconds, 10) + 120, lag.h = Math.floor(lag.s / 3600), lag.s = lag.s - 60 * lag.h * 60, lag.m = Math.floor(lag.s / 60), lag.s = lag.s - 60 * lag.m, lag.ts = serverNow.valueOf(), lag.tsProcessing = processing.valueOf(), lag.tsProcessingHour = processing.clone().startOf("hour").valueOf(), lag.offsetHours = serverNow.clone().startOf("hour").add(1, "hours").diff(processing.startOf("hour"), "hours"), lag.isCurrentHour = lag.raw < 2700 ? !0 : !1, lag.warning = lag.h >= 1 || lag.m >= 45 ? "critical" : lag.m >= 15 ? "warning" : null, lag.message = lag.h >= 1 ? [moreThan, lag.h > 5 ? 5 : lag.h, "hr" + (lag.h > 1 ? "s" : "") + "."].join(" ") : lag.m >= 45 ? [moreThan, 45, "mins."].join(" ") : lag.m >= 30 ? [lessThan, 45, "mins."].join(" ") : lag.m >= 20 ? [lessThan, 30, "mins."].join(" ") : lag.m >= 15 ? [lessThan, 20, "mins."].join(" ") : lag.m >= 10 ? [lessThan, 15, "mins."].join(" ") : lag.m >= 9 ? [lessThan, 10, "mins."].join(" ") : [lessThan, lag.m, "mins."].join(" "), this.lag.last = lag, callbackParams = [null, this.lag.last]
} else callbackParams = ["invalid response"];
this.lag.listeners.forEach(function(listener) {
listener.callback.apply(null, callbackParams)
}), this.lag.listeners.length && (this.lag.timer = setTimeout(this.lag.update.bind(this), this.lag.updateInterval))
}.bind(this)
}
}), angular.module("ga.services.tracking", ["ui.router", "ga.config", "ga.utils.cookie", "ga.utils.crypto", "ga.values.user"]).service("gaServicesTracking", function($q, $window, $state, gaConfig, gaUtilsCookie, gaUtilsCrypto, gaValuesUser) {
var isInitialized = !1,
build = "0.0.1",
sdk_version = "JS 0.0.1",
platform = $window.location.host;
platform = platform.indexOf("virtual.ga.com") > -1 ? "dev" : platform.indexOf("test1.gameanalytics.com") > -1 ? "test" : platform.indexOf("latest.gameanalytics.com") > -1 ? "latest" : platform.indexOf("go.gameanalytics.com") > -1 ? "production" : "unknown", this.getUserId = function() {
if (gaValuesUser.details.id) return platform + "_" + gaValuesUser.details.id;
var id = gaUtilsCookie.get("ga_user_id");
return id || (id = "ga_user_" + Math.random().toString().replace(".", ""), gaUtilsCookie.set("ga_user_id", id, 365)), id
}, this.getSessionId = function() {
var id = gaUtilsCookie.get("ga_session_id");
return id || (id = "ga_session_" + Math.random().toString().replace(".", ""), gaUtilsCookie.set("ga_session_id", id)), id
}, this.submitEvent = function(category, event, data) {
return this.addEvent(category, event, data)
};
var processQueueTimer, queue = {},
processQueueInterval = 2e3;
this.processQueue = function() {
clearTimeout(processQueueTimer);
for (var category in queue) this.submitData(category, queue[category]), queue[category] = [], delete queue[category];
processQueueTimer = setTimeout(this.processQueue.bind(this), processQueueInterval)
}, this.submitData = function(category, data) {
var deferred = $q.defer(),
json_data = JSON.stringify(data),
md5_auth = gaUtilsCrypto.md5(json_data + atob(atob(gaConfig.gaSecret))),
options = {
type: "POST",
url: "https://api.gameanalytics.com/1/" + atob(atob(gaConfig.gaKey)) + "/" + category + "/",
data: json_data,
headers: {
Authorization: md5_auth,
"Content-Type": "text/plain"
}
};
return angular.element.ajax(options).done(function(response) {
deferred.resolve(response)
}).fail(function(response) {
deferred.reject(response)
}), deferred.promise
}, this.addEvent = function(category, event, data, direct) {
return data.user_id = this.getUserId(), data.session_id = this.getSessionId(), data.build = build, data.event_id = event, direct ? this.submitData(category, data) : (queue[category] = queue[category] || [], queue[category].push(data), $q.when())
}, this.init = function() {
return isInitialized ? !1 : (isInitialized = !0, void this.addEvent("user", "init", {
sdk_version: sdk_version,
platform: platform
}, !0).then(this.processQueue.bind(this)))
}, this.trackState = function(state, params) {
state = state || $state.current.name, params = params || angular.copy($state.params);
var ignore = ["game.dashboards"];
if (!(ignore.indexOf(state) > -1)) {
switch (state) {
case "game.dashboards.dashboard":
var idInt = parseInt(params.dashboardId, 10);
if (idInt !== idInt) state = "game.dashboards-" + params.dashboardId;
else {
if (!idInt) return;
state = "game.dashboards-custom"
}
state = state + "." + params.action;
break;
case "game.heatmap":
state = "game.heatmap.hasdata";
break;
case "game.heatmap-nodata":
state = "game.heatmap.nodata";
break;
case "game.heatmap-firsttime":
state = "game.heatmap.firsttime"
}
state = "state." + state, state = state.split(".").join(":"), this.addEvent("design", state, {
value: 1
})
}
}
}), angular.module("ga.services.state", ["ga.utils.urlState"]).provider("gaState", function() {
var stateObject = {},
_states = {},
states = {},
parse = function(string) {
try {
return JSON.parse(string)
} catch (e) {
return null
}
};
this.initState = function(id, options) {
return _states[id] ? !0 : (options = angular.element.extend(!0, {
type: "none",
properties: {}
}, options), _states[id] = options, _states[id].values = {}, void(_states[id].changes = {}))
}, this.$get = function($rootScope, gaUtilsUrlState) {
var getPersist = function(id) {
var state;
switch (_states[id].type) {
case "url":
state = gaUtilsUrlState.getState();
break;
case "sessionStorage":
case "localStorage":
state = parse(window[_states[id].type].getItem("ga.state." + id))
}
_states[id].values = state || {}
},
setPersist = function(id) {
var state = states[id].serialize(!0);
switch (_states[id].type) {
case "url":
gaUtilsUrlState.setState(state);
break;
case "sessionStorage":
case "localStorage":
window[_states[id].type].setItem("ga.state." + id, JSON.stringify(state))
}
},
emitChanges = function(id, key) {
var _state = _states[id],
state = states[id];
clearTimeout(_state.timer), _state.changes[key] = _state.changes[key] || {
oldVal: state[key]
}, _state.timer = setTimeout(function() {
angular.forEach(_state.changes, function(change, k) {
change.newVal = state[k]
}), $rootScope.$broadcast(id + ".state", _state.changes), _state.changes = {}, setPersist(id)
}.bind(this))
},
get = function(id, key) {
var state = _states[id],
property = state.properties[key],
value = void 0 === state.values[key] ? property.init : state.values[key];
return value
},
set = function(id, key, value) {
var state = _states[id],
property = state.properties[key];
if ("function" == typeof property.valid && !property.valid(value)) throw new TypeError("invalid value");
if ("string" == typeof property.valid && typeof value !== property.valid) throw new TypeError("invalid value");
angular.equals(value, _states[id].values[key]) || (emitChanges(id, key), state.values[key] = value)
},
createState = function(state, id) {
getPersist(id), states[id] = {}, Object.defineProperty(stateObject, id, {
enumerable: !0,
get: function() {
return states[id]
}
}), Object.defineProperties(states[id], {
serialize: {
enumerable: !1,
value: function(excludeDefaults) {
var state = {};
return angular.forEach(_states[id].properties, function(property, key) {
var value = states[id][key];
excludeDefaults && value === property.init || (state[key] = value)
}), state
}
}
}), angular.forEach(_states[id].properties, function(property, key) {
Object.defineProperty(states[id], key, {
enumerable: !0,
get: get.bind(null, id, key),
set: set.bind(null, id, key)
})
})
};
return angular.forEach(_states, createState), stateObject
}
}), angular.module("ga.services.content", []).service("gaServicesContent", function($q, $http, $templateCache) {
var notFoundTemplate = '<h1>Page not found</h1><h2>The page you were looking for was not found</h2><section><a data-href="/content/sdk">Go back</a></section>',
_content = {},
parseElement = function(root, selector) {
try {
var element = root.find(selector).get(0);
return {
content: element.innerHTML || "&nbsp;",
empty: !element.innerHTML,
inherit: null !== element.getAttribute("data-inherit")
}
} catch (e) {
return {
content: "&nbsp;",
empty: !0
}
}
},
getUrl = function(section, page) {
return "static/ga-app/content/" + section + "/" + (page || "index") + ".html"
};
this.loadContent = function(section, page) {
return $http.get(getUrl(section, page)).then(function(response) {
var matches = response.data.match(/<ga-content-code>([\S\s]*?)<\/ga-content-code>/g) || [];
return matches.forEach(function(match) {
var tmp = match.match(/<ga-content-code>([\S\s]*?)<\/ga-content-code>/);
response.data = response.data.replace(match, "<ga-content-code>" + tmp[1].replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</ga-content-code>")
}), $q.when(this.putContent(section, page, response.data))
}.bind(this)).catch(function() {
return $q.when(this.putContent(section, page, notFoundTemplate))
}.bind(this))
}, this.putContent = function(section, page, content) {
var root = angular.element("<div>" + content + "</div>");
root.find("[data-href]").each(function() {
this.setAttribute("ng-href", "{{ gameId ? '/game/' + gameId : '' }}" + this.getAttribute("data-href")), this.removeAttribute("data-href")
});
var parsed = {
header: parseElement(root, ">header"),
h1: parseElement(root, ">h1"),
h2: parseElement(root, ">h2"),
aside: parseElement(root, ">aside"),
section: parseElement(root, ">section"),
footer: parseElement(root, ">footer"),
finish: parseElement(root, "[data-finish]")
};
page ? parsed.parent = _content[section] : parsed.children = [], angular.forEach(parsed, function(data, key) {
data.template = "content/" + section + "/" + (page || "index") + "/_el/" + key, $templateCache.put(data.template, data.content)
});
var steps = root.find("[data-steps] > li");
return steps.length && (parsed.steps = [], steps.each(function(index) {
var $this = angular.element(this),
step = {
title: this.getAttribute("data-title"),
nextStep: this.getAttribute("data-next-step") || "",
header: parseElement($this, ">header"),
h1: parseElement($this, ">h1"),
h2: parseElement($this, ">h2"),
aside: parseElement($this, ">aside"),
section: parseElement($this, ">section"),
footer: parseElement($this, ">footer")
};
angular.forEach(step, function(data, key) {
"object" == typeof data && (data.template = "content/" + section + "/" + (page || "index") + "/_el/" + index + "/" + key), $templateCache.put(data.template, data.content)
}), parsed.steps.push(step)
})), page ? _content[section][page] = parsed : _content[section] = parsed, parsed
}, this.get = function(section, page) {
return _content[section] ? page ? _content[section][page] ? $q.when(_content[section][page]) : this.loadContent(section, page) : $q.when(_content[section]) : this.loadContent(section).then(function() {
return page ? this.loadContent(section, page) : $q.when(_content[section])
}.bind(this))
}
}), angular.module("ga.services.pardot", []).service("gaServicesPardot", function($window, $q) {
function loadScripts(src) {
var deferred = $q.defer(),
script = document.createElement("script");
script.src = src, script.onload = script.onreadystatechange = function() {
script.onreadystatechange = script.onload = null, deferred.resolve()
};
var head = document.getElementsByTagName("head")[0];
return (head || document.body).appendChild(script), deferred.promise
}
var piAId = "21732",
piCIds = {
loginSignup: "2011",
invited: "2071"
},
pardotLoaded = ("https:" === window.location.protocol ? "https://go.pardot.com/" : "http://easy.gameanalytics.com/", !1),
pardot = function() {
if (pardotLoaded) return $q.when(!1);
var src = ("https:" === document.location.protocol ? "https://pi" : "http://cdn") + ".pardot.com/pd.js";
return loadScripts(src).then(function() {
return pardotLoaded = !0, $q.when(!0)
})
};
this.trackPage = function(campaign) {
piCIds[campaign] && ($window.piAId = piAId, $window.piCId = piCIds[campaign], pardot().then(function(loaded) {
loaded || setTimeout(function() {
piTracker()
})
}))
}, this.addProspect = function(email, token) {
var iframe = '<iframe height="1" width="1" frameBorder="0" src="/pardot/iframe/' + email + "/" + token + '"></iframe>';
return angular.element("body").append(iframe), $q.when(!0)
}
}), angular.module("ga.services.announcement", ["ga.utils.cookie", "ga.utils.detect"]).service("gaServicesAnnouncement", function(gaUtilsCookie, gaDetect) {
var $announcementContainer, template = function(options, contentOnly) {
var html = [];
return !contentOnly && html.push('<li class="' + (options.style || "") + '" data-id="' + options.id + '">'), html.push('<div class="' + (options.icon || "") + ' icon-padding">'), html.push(options.content || ""), options.close && html.push('<div class="ga-icon-delete"></div>'), html.push("</div>"), !contentOnly && html.push("</li>"), html.join("")
},
activeAnnouncements = {},
announcementsCount = 0;
$announcementContainer = "windows" === gaDetect.OS() ? angular.element('<ul class="ga announcements"><li class="overlay"><iframe frameborder="0"></iframe></li></ul>').appendTo("body") : angular.element('<ul class="ga announcements"></ul>').appendTo("body"), this.hide = function(id) {
var options = activeAnnouncements[id];
if (options) {
var element = $announcementContainer.find("li[data-id=" + options.id + "]");
if (!element.length) return void delete activeAnnouncements[id];
options.cookie && gaUtilsCookie.set("ga-announcement-" + options.id, 1, 365), element.removeClass("show"), setTimeout(function() {
element.remove()
}, 300), delete activeAnnouncements[id], announcementsCount--, angular.element("body").removeClass("a" + (announcementsCount + 1)), angular.element("body").addClass("a" + announcementsCount)
}
}, this.add = function(options) {
if (options = angular.element.extend({
id: Math.random().toString().replace(".", ""),
style: "",
icon: "",
content: "",
cookie: !1,
replace: !1,
close: !0
}, options || {}), options.cookie && gaUtilsCookie.get("ga-announcement-" + options.id)) return !1;
if (activeAnnouncements[options.id]) {
if (options.replace) {
var container = $announcementContainer.find("li[data-id=" + options.id + "]"),
stateOptions = activeAnnouncements[options.id];
return container.removeClass(stateOptions.style).addClass(options.style), stateOptions.content = options.content, stateOptions.close = options.close || stateOptions.close, stateOptions.icon = options.icon || stateOptions.icon, stateOptions.style = options.style || stateOptions.style, void container.html(template(stateOptions, !0))
}
} else {
announcementsCount++, activeAnnouncements[options.id] = options;
var element = angular.element(template(options));
$announcementContainer.prepend(element), setTimeout(function() {
element.addClass("show"), angular.element("body").removeClass("a" + (announcementsCount - 1)), angular.element("body").addClass("a" + announcementsCount)
})
}
}, $announcementContainer.on("click", ".ga-icon-delete, .hide-me", function(e) {
var id = angular.element(e.target).closest("li[data-id]").data("id");
this.hide(id)
}.bind(this))
}), angular.module("ga.services.dialogs", ["ga.ui.modal", "ga.dialogs.metricpicker", "ga.dialogs.access", "ga.dialogs.linkGame", "ga.dialogs.archive", "ga.dialogs.emailReportsPreview"]).service("gaDialogs", function($q, $rootScope, $timeout, gaUiModal) {
this.metricpickerNew = function(gameId, metric) {
return gaUiModal.page({
controller: "gaDialogsMetricpicker",
templateUrl: "/static/ga-app/modules/dialogs/metricpicker/metricpicker.html",
width: 700,
parameters: {
gameId: gameId,
metric: metric
}
})
}, this.access = function(options) {
return gaUiModal.page({
controller: "gaDialogsAccess",
templateUrl: "/static/ga-app/modules/dialogs/access/access.html",
width: 700,
parameters: {
options: options
}
})
}, this.linkGame = function(options) {
return gaUiModal.page({
controller: "gaDialogsLinkGame",
templateUrl: "/static/ga-app/modules/dialogs/link-game/link-game.html",
width: 700,
parameters: {
options: options
}
})
}, this.emailReportsPreview = function(options) {
return gaUiModal.page({
controller: "gaDialogsEmailReportsPreview",
templateUrl: "/static/ga-app/modules/dialogs/email-reports-preview/email-reports-preview.html",
width: 700,
parameters: {
options: options
}
})
}, this.archive = function(studiosAndGames) {
return gaUiModal.page({
controller: "gaDialogsArchive",
templateUrl: "/static/ga-app/modules/dialogs/archive/archive.html",
width: 700,
parameters: {
studiosAndGames: studiosAndGames
}
})
}, this.metricPicker = function(event, category, filter) {
var deferred = $q.defer(),
$tmpScope = $rootScope.$new(!0);
event && category && ($tmpScope.tmpMetric = {
event: event,
category: category
});
var tmpListener = $tmpScope.$watch("tmpMetric", function(newVal, oldVal) {
newVal !== oldVal && ($timeout(function() {
gaUiModal.hide("subModal")
}), deferred.resolve($tmpScope.tmpMetric))
});
return $tmpScope.filter = filter || null, gaUiModal.show({
header: "Select a metric",
scope: $tmpScope,
iframe: !0,
newScope: !1,
width: 720,
customClass: "secondary",
content: '<ga-ui-metricpicker metric="tmpMetric" filter="filter"></ga-ui-metricpicker>',
buttons: [{
title: "Cancel",
"class": "ga-btn-alt",
keyCode: 27
}],
always: function() {
tmpListener(), $tmpScope.$destroy(), deferred.reject()
}
}, "subModal"), deferred.promise
}
}), angular.module("ga.dialogs.metricpicker", ["ga.api.data"]).service("gaDialogsMetricpickerService", function($q, gaApiData) {
var metrics = {},
loadMetrics = function(gameId) {
return gaApiData.getValue("/metrics", gameId).then(function(rawData) {
return rawData
})
},
parseMetrics = function(rawMetrics) {
angular.forEach(rawMetrics, function(metrics, category) {
console.log(category, metrics.length, "metrics")
})
};
this.getMetrics = function(gameId) {
return metrics[gameId] ? $q.when(metrics[gameId]) : loadMetrics(gameId).then(function(rawMetrics) {
return parseMetrics(rawMetrics), $q.when(rawMetrics)
})
}
}).controller("gaDialogsMetricpicker", function($scope, $q, gaDialogsMetricpickerService) {
return $scope.gameId ? void gaDialogsMetricpickerService.getMetrics($scope.gameId) : void $scope._reject()
}), angular.module("ga.dialogs.access", ["ga.api.userDb.authenticated.invite", "ga.api.userDb.authenticated.game", "ga.api.userDb.authenticated.studio", "ga.ui.notify", "ga.ui.modal"]).controller("gaDialogsAccess", function($scope, gaApiUserDbAuthenticatedInvite, gaApiUserDbAuthenticatedGame, gaApiUserDbAuthenticatedStudio, gaUiNotify, gaUiModal) {
$scope.accessType = $scope.options.type;
var entityTitle = $scope.options.title;
$scope.user = {
email: ""
}, $scope.isInvite = $scope.options.userAccessId ? !1 : !0, $scope.requestActive = !1, $scope.isInvite ? ($scope.title = "Invite user to " + entityTitle, $scope.btnTitle = "Send invite", $scope.selectedRole = null, $scope.invite = !0, $scope.emailReports = {
daily: !1,
weekly: !0,
monthly: !1
}) : ($scope.username = entityTitle, $scope.title = "Edit role for " + entityTitle, $scope.btnTitle = "Save changes", $scope.selectedRole = 1 === parseInt($scope.options.role, 10) ? "admin" : "viewer", $scope.invite = !1), $scope.delete = function() {
if (!$scope.requestActive && !$scope.isInvite) {
$scope.requestActive = !0;
var userAccessId = parseInt($scope.options.userAccessId, 10);
userAccessId && gaUiModal.confirm("Please confirm that you want to revoke " + $scope.accessType + " access for " + entityTitle + ".", "Revoke access", !1, {
hideClose: !0
}).then(function() {
gaApiUserDbAuthenticatedInvite.delete({
type: $scope.accessType,
id: userAccessId
}).then(function() {
$scope.requestActive = !1, $scope._resolve({
deletion: !0
})
})
}).catch(function() {
$scope.requestActive = !1
})
}
}, $scope.save = function() {
if (!$scope.requestActive)
if ($scope.requestActive = !0, $scope.invite) {
var reports = [];
$scope.emailReports.daily && reports.push("daily"), $scope.emailReports.weekly && reports.push("weekly"), $scope.emailReports.monthly && reports.push("monthly"), "game" === $scope.options.type ? gaApiUserDbAuthenticatedGame.addUser($scope.options.gameId, $scope.user.email, "admin" === $scope.selectedRole ? 1 : 2, reports).then(function(result) {
result = result && result.length > 0 ? result[0] : {}, $scope.requestActive = !1, $scope._resolve(result)
}).catch(function(result) {
$scope.requestActive = !1, gaUiNotify.show(result[0].msg, 6e3, "warning")
}) : gaApiUserDbAuthenticatedStudio.addUser($scope.options.studioId, $scope.user.email, "admin" === $scope.selectedRole ? 1 : 2, reports).then(function() {
$scope.requestActive = !1, $scope._resolve()
}).catch(function(result) {
gaUiNotify.show(result[0].msg, 6e3, "warning"), $scope.requestActive = !1
})
} else gaApiUserDbAuthenticatedInvite.change({
type: $scope.options.type,
id: parseInt($scope.options.userAccessId, 10),
role: "admin" === $scope.selectedRole ? 1 : 2
}).then(function() {
$scope._resolve(), $scope.requestActive = !1
})
}
}), angular.module("ga.dialogs.linkGame", ["ga.ui.floatLabel", "ga.api.userDb.authenticated.appfigures"]).controller("gaDialogsLinkGame", function($scope, gaApiUserDbAuthenticatedAppfigures) {
$scope.game = angular.copy($scope.options.game), $scope.iconPath = "/static/ga-app/images/stores/", $scope.apps = [], $scope.state = null, $scope.search = function() {
$scope.state = "searching", $scope.apps = [], gaApiUserDbAuthenticatedAppfigures.search($scope.game.title).then(function(results) {
$scope.searching = !1, results && results.length ? ($scope.state = null, $scope.apps = results.map(function(app) {
return "google_play" === app.store ? app.storeIcon = "google-play.png" : "apple" === app.store && "Desktop" === app.devices[0] ? app.storeIcon = "mac.png" : "apple" === app.store ? app.storeIcon = "ios.png" : "amazon_appstore" === app.store && (app.storeIcon = "amazon.png"), app
})) : $scope.state = "no_results"
}).catch(function(error) {
console.log(error)
})
}, $scope.selectApp = function(app) {
$scope._resolve(app)
}
}), angular.module("ga.dialogs.archive", ["ga.api.userDb.authenticated.game", "ga.api.userDb.authenticated.studio", "ga.ui.notify", "ga.utils.helpers"]).controller("gaDialogsArchive", function($q, $scope, gaUiNotify, gaApiUserDbAuthenticatedGame, gaApiUserDbAuthenticatedStudio, gaHelpers) {
$scope.studiosAndGamesCopy = gaHelpers.copy($scope.studiosAndGames).filter(function(studio) {
return !studio.demo
}), angular.forEach($scope.studiosAndGamesCopy, function(studio) {
studio.archivedChange = studio.archived, angular.forEach(studio.games, function(game) {
game.archivedChange = game.archived, game.archivedGameVisual = studio.archived ? !0 : game.archived
})
}), $scope.title = "Archive", $scope.changed = !1, $scope.changedStudios = [], $scope.changedGames = [], $scope.requestActive = !1;
var gameArchiveRequest = function(gameId, archived) {
return gaApiUserDbAuthenticatedGame.archiveGame(gameId, archived)
},
studioArchiveRequest = function(studioId, archived) {
return gaApiUserDbAuthenticatedStudio.archiveStudio(studioId, archived)
};
$scope.save = function() {
$scope.requestActive = !0;
var promises = [];
angular.forEach($scope.changedStudios, function(changedStudio) {
promises.push(studioArchiveRequest(changedStudio.id, changedStudio.archived))
}), angular.forEach($scope.changedGames, function(changedGame) {
promises.push(gameArchiveRequest(changedGame.id, changedGame.archived))
}), promises.length && $q.all(promises).then(function() {
$scope.requestActive = !1, $scope._resolve()
}).catch(function(result) {
$scope.requestActive = !1, gaUiNotify.show(result[0].msg, 6e3, "warning")
})
};
var clickStudio = function(studio) {
var studioCheckboxState = !studio.archivedChange;
studio.archivedChange = studioCheckboxState, studioCheckboxState === !0 ? angular.forEach(studio.games, function(game) {
game.archivedGameVisual = !0
}) : angular.forEach(studio.games, function(game) {
game.archivedGameVisual = game.archivedChange
}), checkChanges()
},
clickGame = function(game, studio) {
var gameCheckboxState = !game.archivedGameVisual;
studio.archivedChange === !0 && (gameCheckboxState = !0), game.archivedChange = gameCheckboxState, game.archivedGameVisual = gameCheckboxState, studio.archivedChange === !0 && (angular.forEach(studio.games, function(studioGame) {
studioGame.archivedChange !== studioGame.archivedGameVisual && studioGame.id !== game.id && (studioGame.archivedGameVisual = studioGame.archivedChange)
}), studio.archivedChange = !1), checkChanges()
},
checkChanges = function() {
$scope.changedStudios = [], $scope.changedGames = [], angular.forEach($scope.studiosAndGamesCopy, function(studio) {
studio.archivedChange !== studio.archived && $scope.changedStudios.push({
id: studio.id,
archived: studio.archivedChange
}), angular.forEach(studio.games, function(game) {
game.archivedChange !== game.archived && $scope.changedGames.push({
id: game.id,
archived: game.archivedChange
})
})
}), $scope.changed = $scope.changedStudios.length || $scope.changedGames.length ? !0 : !1
};
checkChanges(), $scope.clickStudio = clickStudio, $scope.clickGame = clickGame
}), angular.module("ga.dialogs.emailReportsPreview", ["ga.utils.date"]).controller("gaDialogsEmailReportsPreview", function($scope, gaUtilsDate) {
$scope.previewData = {
daily: {},
weekly: {},
monthly: {},
title: "Daily report",
metrics: [1, 2]
}, $scope.reports = {
selected: "daily",
metrics: {
daily: [{
v1: "123,829",
v2: "109,729",
c: 11,
name: "DAU",
description: "The total number of unique active users."
}, {
v1: "12,909",
v2: "15,521",
c: -20,
name: "Installs",
description: "The number of unique users observed for the first time for your game by the GA servers."
}, {
v1: "104,829",
v2: "94,019",
c: 10,
name: "Conversion to paying",
description: "The number of players that made their first in-game purchase."
}, {
v1: "102",
v2: "77",
c: 24,
name: "Paying Users",
description: "The total number of unique paying users."
}, {
v1: "51%",
v2: "49%",
c: 2,
name: "Retention Day 1",
description: "The percent of players that first started the game 1 day before the date targeted by the report and then came back on the day targeted by the report."
}, {
v1: "29%",
v2: "28%",
c: 1,
name: "Retention Day 7",
description: "The percent of players that first started the game 7 days before the date targeted by the report and then came back on the day targeted by the report."
}, {
v1: "$400.544",
v2: "$380.544",
c: 2,
name: "Total Revenue",
description: "Total amount of revenue generated for the day. All valid currencies provided are converted to USD."
}, {
v1: "$0.04",
v2: "$0.04",
c: 0,
name: "ARPDAU",
description: "Average revenue per daily active user during the day. All valid currencies provided are converted to USD."
}, {
v1: "$18",
v2: "$17",
c: 11,
name: "ARPPU",
description: "Average revenue per paying user during the day targeted by the report. All valid currencies provided are converted to USD."
}],
weekly: [{
v1: "982,038",
v2: "923,711",
c: 6,
name: "WAU",
description: "The total number of unique active users over the last 7 days."
}, {
v1: "74,661",
v2: "75,942",
c: -2,
name: "Weekly Installs",
description: "The number of unique users observed for the first time for your game by the GA servers."
}, {
v1: "710,682",
v2: "702,777",
c: 1,
name: "Conversion to paying",
description: "The number of players that made their first in-game purchase."
}, {
v1: "634",
v2: "601",
c: 5,
name: "Paying Users (Avg. daily)",
description: "The average of the daily number of unique paying users over the last 7 days."
}, {
v1: "43%",
v2: "46%",
c: -3,
name: "Retention Day 1",
description: "For weekly Day 1 Retention, we define a different weekly period, which is the week targeted by the report minus 1 day. Thus, Day 1 Retention is the percent of players that first started the game some day within the weekly period defined above and then returned 1 day later."
}, {
v1: "22%",
v2: "21%",
c: 1,
name: "Retention Day 7",
description: "For weekly Day 7 Retention, we define a different weekly period, which is the week targeted by the report minus 7 days. Thus, Day 7 Retention is the percent of players that first started the game some day within the weekly period defined above and then returned 7 days later."
}, {
v1: "$400.544",
v2: "$380.544",
c: 2,
name: "Total Revenue",
description: "Total amount of revenue generated for the week. All valid currencies provided are converted to USD."
}, {
v1: "$0.04",
v2: "$0.04",
c: 0,
name: "ARPDAU",
description: "Average revenue per daily active user during the week. All valid currencies provided are converted to USD."
}, {
v1: "$18",
v2: "$17",
c: 11,
name: "ARPPU",
description: "Average revenue per daily paying user, over the days of the week targeted by the report. All valid currencies provided are converted to USD."
}],
monthly: [{
v1: "1,71M",
v2: "1,64M",
c: 4,
name: "MAU",
description: "The total number of unique active users over the last 30 days."
}, {
v1: "317,616",
v2: "278,011",
c: 12,
name: "Monthly Installs",
description: "The number of unique users observed for the first time for your game by the GA servers."
}, {
v1: "1,2M",
v2: "1,01M",
c: 25,
name: "Conversion to Paying",
description: "The number of players that made their first in-game purchase."
}, {
v1: "1210",
v2: "1592",
c: -24,
name: "Paying Users (Avg. daily)",
description: "The average of the daily number of unique paying users over the last month."
}, {
v1: "41%",
v2: "39%",
c: 2,
name: "Retention Day 1",
description: "For monthly Day 1 Retention, we define a different monthly period, which is the month targeted by the report minus 1 day. Thus, Day 1 Retention is the percent of players that first started the game some day within the monthly period defined above and then returned 1 day later."
}, {
v1: "18%",
v2: "14%",
c: 4,
name: "Retention Day 7",
description: "For monthly Day 7 Retention, we define a different monthly period, which is the month targeted by the report minus 7 days. Thus, Day 7 Retention is the percent of players that first started the game some day within the monthly period defined above and then returned 7 days later."
}, {
v1: "12%",
v2: "6%",
c: 6,
name: "Retention Day 30",
description: "For monthly Day 30 Retention, we define a different monthly period, which is the month targeted by the report minus 30 days. Thus, Day 30 Retention is the percent of players that first started the game some day within the monthly period defined above and then returned 30 days later."
}, {
v1: "$400.544",
v2: "$380.544",
c: 2,
name: "Total Revenue",
description: "Total amount of revenue generated for the month. All valid currencies provided are converted to USD."
}, {
v1: "$0.04",
v2: "$0.04",
c: 0,
name: "ARPDAU",
description: "Average revenue per daily active user during the month. All valid currencies provided are converted to USD."
}, {
v1: "$18",
v2: "$17",
c: 11,
name: "ARPPU",
description: "Average revenue per daily paying user, over the days the month targeted by the report. All valid currencies provided are converted to USD."
}]
}
}, $scope.previewData.daily = {
title: "Daily report",
date1: gaUtilsDate.moment().add("days", -1).valueOf(),
date2: null,
currentDate: gaUtilsDate.moment().valueOf(),
thisTitle: "Yesterday",
previousTitle: "A week ago",
metrics: []
}, angular.forEach($scope.reports.metrics.daily, function(metric) {
var percent = metric.c;
$scope.previewData.daily.metrics.push({
name: metric.name,
current: metric.v1,
last: metric.v2,
percent: parseInt(Math.abs(percent), 10).toString() + "%",
color: percent ? 0 > percent ? "#C30000" : "#00C300" : "#C3C3C3",
change: percent ? 0 > percent ? "∨" : "∧" : ""
})
}), $scope.previewData.weekly = {
title: "Weekly report",
date1: gaUtilsDate.moment().startOf("week").add("days", -6).valueOf(),
date2: gaUtilsDate.moment().startOf("week").add("days").valueOf(),
currentDate: gaUtilsDate.moment().startOf("week").add("days", 1).valueOf(),
thisTitle: "Last week",
previousTitle: "Previous period",
metrics: []
}, angular.forEach($scope.reports.metrics.weekly, function(metric) {
var percent = metric.c;
$scope.previewData.weekly.metrics.push({
name: metric.name,
current: metric.v1,
last: metric.v2,
percent: parseInt(Math.abs(percent), 10).toString() + "%",
color: percent ? 0 > percent ? "#C30000" : "#00C300" : "#C3C3C3",
change: percent ? 0 > percent ? "∨" : "∧" : ""
})
}), $scope.previewData.monthly = {
title: "Monthly report",
date1: gaUtilsDate.moment().add("months", -1).startOf("month").valueOf(),
date2: gaUtilsDate.moment().add("months", -1).endOf("month").valueOf(),
currentDate: gaUtilsDate.moment().add("months", -1).endOf("month").add("days", 1).valueOf(),
thisTitle: "Last month",
previousTitle: "Previous period",
metrics: []
}, angular.forEach($scope.reports.metrics.monthly, function(metric) {
var percent = metric.c;
$scope.previewData.monthly.metrics.push({
name: metric.name,
current: metric.v1,
last: metric.v2,
percent: parseInt(Math.abs(percent), 10).toString() + "%",
color: percent ? 0 > percent ? "#C30000" : "#00C300" : "#C3C3C3",
change: percent ? 0 > percent ? "∨" : "∧" : ""
})
})
}), angular.module("ga.pages.studio.settings.information", ["ga.services.user", "ga.api.userDb.authenticated.studio", "ga.config", "ga.ui.notify", "ga.ui.modal"]).controller("gaPagesStudioSettingsInformationController", function($scope, $rootScope, $state, gaServicesUser, gaConfig, gaUiNotify, gaApiUserDbAuthenticatedStudio, gaUiModal) {
var studioId = parseInt($state.params.studioId, 10),
studio = gaServicesUser.studio(studioId),
saving = !1;
$scope.errors = {}, $scope.validated = !0, $scope.changed = !1, $scope.name = studio.name, $scope.imageFile = studio.imageFile, $scope.defaultImage = "/static/ga-app/images/default-studio-icon.png", $scope.imageBaseUrl = gaConfig.images.baseUrl, $scope.uploadFile = null, $scope.validate = function() {
0 === $scope.name.length ? ($scope.errors.name = "Name can not be zero characters", $scope.validated = !1) : ($scope.errors.name = null, $scope.validated = !0), $scope.changed = !0
}, $scope.save = function(redirectObj) {
saving || (saving = !0, gaApiUserDbAuthenticatedStudio.saveStudio(studioId, {
name: $scope.name,
imageFile: $scope.imageFile
}).then(function() {
redirectObj ? gaServicesUser.getUserData().then(function() {
$rootScope.$broadcast("studioChange", null), $state.go(redirectObj.state, redirectObj.params)
}) : (gaUiNotify.show("Studio saved", 4e3, "default"), gaServicesUser.getUserData().then(function() {
$rootScope.$broadcast("studioChange", null), studio = gaServicesUser.studio(studioId), saving = !1
})), $scope.changed = !1
}).catch(function() {
gaUiNotify.show("Something went wrong while saving", 2e3, "warning"), saving = !1
}))
}, $scope.discard = function() {
$scope.name = studio.name, $scope.imageFile = studio.imageFile, $scope.changed = !1
}, $scope.$watch("imageFile", function(newVal, oldVal) {
newVal && newVal !== oldVal && ($scope.changed = !0)
});
var ignoreSaveState = !1;
$scope.$on("$stateChangeStart", function(event, toState, toParams) {
$scope.changed && !ignoreSaveState && (event.preventDefault(), gaUiNotify.loading(!1), gaUiModal.show({
scope: $scope,
width: 500,
header: "Unsaved changes",
content: "Do you want to save the changes you made or discard the changes and leave the page?",
buttons: [{
title: "Save",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "save"
}, {
title: "Discard changes",
"class": "ga-btn-text right",
keyCode: 27,
action: "go"
}],
actions: {
go: function() {
ignoreSaveState = !0, $state.go(toState, toParams)
},
save: function() {
ignoreSaveState = !0, $scope.save({
state: toState,
params: toParams
})
}
}
}))
})
}), angular.module("ga.pages.studio.settings.games", ["ga.services.user", "ga.ui.modal", "ga.ui.notify", "ga.utils.tracking"]).controller("gaPagesStudioSettingsGamesController", function($scope, $state, gaUiModal, gaUiNotify, gaServicesUser, gaUtilsTracking) {
var studioId = parseInt($state.params.studioId, 10),
studio = gaServicesUser.studio(studioId);
$scope.games = studio.games, $scope.gotosettings = function(game) {
$state.go("game.settings.information", {
gameId: game.id
})
};
var createGame = function() {
var promise = gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/user/game-create/game-create.html",
controller: "gaPagesUserGameCreateController",
parameters: {
studioId: studioId,
step: 2
}
});
gaUtilsTracking.trackPageRaw("/game/add"), promise.then(function(newGameId) {
gaUiNotify.show("Game successfully created", 4e3, "default"), $state.go("game.content.section", {
section: "sdk",
gameId: newGameId
})
})
};
$scope.createGame = createGame
}), angular.module("ga.pages.studio.settings.users", ["ga.api.userDb.authenticated.studio", "ga.api.userDb.authenticated.invite", "ga.services.user", "ga.config", "ga.ui.notify", "ga.services.dialogs", "ga.ui.modal"]).controller("gaPagesStudioSettingsUsersController", function($scope, $state, gaConfig, gaServicesUser, gaUiModal, gaUiNotify, gaDialogs, gaApiUserDbAuthenticatedStudio, gaApiUserDbAuthenticatedInvite) {
var studioId = parseInt($state.params.studioId, 10),
studio = gaServicesUser.studio(studioId),
defaultImage = "/static/ga-app/images/default-game-icon.png",
imageBaseUrl = gaConfig.images.baseUrl,
getUsers = function() {
gaApiUserDbAuthenticatedStudio.getStudioUsers(studioId).then(function(result) {
var users = [],
tmpOwner = {
email: result.owner.email,
invite: !1,
name: result.owner.first_name + " " + result.owner.last_name,
role: "Studio owner",
owner: !0,
self: !1
};
gaServicesUser.id === result.owner.id && (tmpOwner.self = !0), users.push(tmpOwner);
var games = [];
if (result.accesses && result.accesses.length) {
var invited = [],
members = [];
result.accesses.some(function(usr) {
if (usr.invite_pending) invited.push({
email: usr.invite_email,
invite: !0,
id: usr.id,
role: getStudioRole(usr.role_id)
});
else {
var tmpUsr = {
email: usr.email,
invite: !1,
id: usr.id,
userId: usr.user_id,
name: usr.first_name + " " + usr.last_name,
role: getStudioRole(usr.role_id),
roleId: usr.role_id,
self: !1
};
gaServicesUser.id === usr.user_id ? (tmpUsr.self = !0, users.unshift(tmpUsr)) : members.push(tmpUsr)
}
}), members = members.sort(function(a, b) {
return a.name.localeCompare(b.name)
}), users = users.concat(members), invited = invited.sort(function(a, b) {
return a.email.localeCompare(b.email)
}), users = users.concat(invited)
}
$scope.studioUsers = users, result.games && result.games.length && (result.games = result.games.sort(function(a, b) {
return a.title.localeCompare(b.title)
}), games = [], result.games.some(function(game) {
var tmpGame = {
gameTitle: game.title,
imagePath: game.image_file ? imageBaseUrl + game.image_file : defaultImage,
id: game.id,
users: []
};
if (game.accesses) {
var invites = [],
members = [];
game.accesses.some(function(user) {
user.invite_pending ? invites.push({
email: user.invite_email,
invite: !0,
name: null,
roleId: user.role_id,
role: getGameRole(user.role_id),
id: user.id
}) : members.push({
email: user.email,
invite: !1,
name: user.first_name + " " + user.last_name,
roleId: user.role_id,
role: getGameRole(user.role_id),
id: user.id
})
}), members = members.sort(function(a, b) {
return a.name > b.name
}), tmpGame.users = tmpGame.users.concat(members), invites = invites.sort(function(a, b) {
return a.email > b.email
}), tmpGame.users = tmpGame.users.concat(invites)
}
games.push(tmpGame)
})), $scope.studioGames = games
}).catch(function() {
console.log("error gettings users")
})
},
getStudioRole = function(roleid) {
return 1 === roleid ? "Studio admin" : "Studio viewer"
},
getGameRole = function(roleid) {
return 1 === roleid ? "Game admin" : "Game viewer"
};
$scope.gameSettings = function(id) {
$state.go("game.settings.information", {
gameId: id
})
}, $scope.deleteInvite = function(userAccessId, type) {
gaUiModal.confirm("Please confirm that you wan't to delete the invitation.", "Delete invitation").then(function() {
gaApiUserDbAuthenticatedInvite.delete({
type: type,
id: userAccessId
}).then(function() {
getUsers()
})
})
}, $scope.inviteStudioUser = function() {
gaDialogs.access({
type: "studio",
role: null,
userAccessId: null,
studioId: studio.id,
title: studio.name
}).then(function() {
getUsers(), gaUiNotify.show("Studio invite has been sent", 4e3, "default")
})
}, $scope.inviteGameUser = function(game) {
gaDialogs.access({
type: "game",
role: null,
userAccessId: null,
gameId: game.id,
title: game.gameTitle
}).then(function(result) {
result && result.access_created === !0 ? gaUiNotify.show("User access created", 4e3, "default") : gaUiNotify.show("Game invite has been sent", 4e3, "default"), getUsers()
})
}, $scope.editUser = function(userAccess, type, game) {
"studio" === type ? gaDialogs.access({
type: "studio",
studioName: studio.name,
studioId: studio.id,
userAccessId: userAccess.id,
role: userAccess.roleId,
title: userAccess.name
}).then(function(result) {
result && result.deletion === !0 ? gaUiNotify.show("The user was removed from the studio", 4e3, "default") : gaUiNotify.show("User saved", 4e3, "default"), getUsers()
}) : gaDialogs.access({
type: "game",
gameTitle: game.gameTitle,
studioId: studio.id,
role: userAccess.roleId,
userAccessId: userAccess.id,
gameId: game.id,
title: userAccess.name
}).then(function(result) {
result && result.deletion === !0 ? gaUiNotify.show("The user was removed from the game", 4e3, "default") : gaUiNotify.show("User saved", 4e3, "default"), getUsers()
})
}, $scope.goToUserSettings = function() {
$state.go("user.settings.profile")
}, getUsers()
}), angular.module("ga.pages.amt", ["ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.utils.date", "ga.utils.cache", "ui.router"]).filter("reverse", function() {
return function(items) {
return items.slice().reverse()
}
}).filter("filterTests", function() {
return function(items) {
return items.slice().reverse()
}
}).controller("gaPagesAmtController", function($rootScope, $scope, $q, $state, $timeout, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate) {
$scope.metrics = null, $scope.dimensions = null, $scope.daterange = gaUtilsDate.getPeriodRange("last30Days"), $scope.compare = !1, $scope.tests = [], $scope.testStatus = 0, $scope.currentTest = null, $scope.UID = "AMT", $scope.filter = {
OK: !0,
ERROR: !0,
LongResponse: !0,
NullTotals: !0,
NoData: !0
}, $scope.filterTableCompleted = function(item) {
return item.status > 1
}, $scope.filterTable = function(item) {
var show = !1;
return 0 === item.status ? !1 : 1 === item.status ? !0 : (!$scope.filter.OK || 1 !== item.results.status || item.results.notes && item.results.notes.length || (show = !0), $scope.filter.ERROR && 0 === item.results.status && (show = !0), $scope.filter.LongResponse && item.results.notes && item.results.notes.indexOf("LongResponse") > -1 && (show = !0), $scope.filter.NullTotals && item.results.notes && item.results.notes.indexOf("NullTotals") > -1 && (show = !0), $scope.filter.NoData && item.results.notes && item.results.notes.indexOf("NoData") > -1 && (show = !0), show)
};
var deferred, init = function() {
var metrics = $q.defer(),
dimensions = $q.defer(),
promises = {
metrics: metrics.promise,
dimensions: dimensions.promise
};
gaApiData.getValue("/metrics", $state.params.gameId).then(function(rawData) {
metrics.resolve(rawData.core)
}), gaApiData.getValue("/dimensions", $state.params.gameId).then(function(rawData) {
dimensions.resolve(rawData)
}), $q.all(promises).then(function(data) {
$scope.dimensions = data.dimensions, $scope.metrics = data.metrics, $scope.tests = createTests()
})
},
createTests = function(metrics) {
$scope.tests = [], metrics = metrics || angular.copy($scope.metrics);
var tests = [];
return angular.forEach(metrics, function(metric) {
var meta = gaApiMeta.getMetric("core", metric);
angular.forEach(meta.aggregation, function(aggregation) {
if ("histogram" !== aggregation) {
var query = createQuery(metric);
query.group = meta.groupTime === !1 ? "value" : "time", query.aggregation = aggregation, tests.push({
query: query,
status: 0,
results: {}
}), angular.forEach($scope.dimensions, function(values, dimension) {
var subQuerySome = angular.copy(query),
subQueryAll = angular.copy(query);
subQuerySome.filter = {
dimension: dimension,
values: values.slice(0, 3)
}, subQueryAll.filter = {
dimension: dimension,
values: ["*"]
}, tests.push({
query: subQuerySome,
status: 0,
results: {}
}), tests.push({
query: subQueryAll,
status: 0,
results: {}
})
})
}
})
}), tests
},
createQuery = function(metric) {
var meta = gaApiMeta.getMetric("core", metric),
query = {
returnAll: !0,
metric: {
category: "core",
event: metric
},
interval: angular.copy($scope.daterange)
};
return meta.currency && (query.metric.currency = "USD"), query
},
resetTests = function() {
$scope.testStatus = 0, $scope.results = [], angular.forEach($scope.tests, function(test) {
test.status = 0, test.results = {}
})
},
cancelTests = function() {
deferred.reject("cancelled"), gaApiData.removeUID($scope.UID), $scope.testStatus = 2
},
executeTests = function() {
return 0 !== $scope.testStatus ? !1 : ($scope.testStatus = 1, void _executeTests())
},
_executeTests = function() {
var metricTest = null;
$scope.tests.some(function(test) {
var uncompleted = 0 === test.status;
return uncompleted && (metricTest = test), uncompleted
}), null === metricTest ? ($scope.currentTest = null, $scope.testStatus = 2) : ($scope.currentTest = metricTest, $scope.currentTest.status = 1, loadData($scope.currentTest).then(function() {
$scope.currentTest.status = 2, $scope.currentTest.results.status = 1, $scope.currentTest.results.notes.length && ($scope.currentTest.results.status = 2), _executeTests()
}, function(status) {
"cancelled" === status ? $scope.currentTest = null : ($scope.currentTest.status = 2, $scope.currentTest.results.status = 0, _executeTests())
}))
},
loadData = function(testObject) {
var time = Date.now();
return deferred = $q.defer(), gaApiData.get(testObject.query, $scope.UID).then(function(responseObject) {
testObject.results.response = responseObject, testObject.results.time = Date.now() - time, testObject = testData(testObject), deferred.resolve(!0)
}, function() {
deferred.reject()
}), deferred.promise
},
testData = function(testObject) {
if (testObject.results.notes = [], testObject.results.response.parsed.noData)
if (angular.isArray(testObject.results.response.parsed.data.timeseries)) {
var hasTimeData = testObject.results.response.parsed.data.timeseries.some(function(data1) {
return data1.data.some(function(data2) {
return data2.data.some(function(item) {
return null !== item.total
})
})
});
testObject.results.notes.push(hasTimeData ? "NullTotals" : "NoData")
} else testObject.results.notes.push("NoData");
return testObject.results.time >= 1e3 && testObject.results.notes.push("LongResponse"), testObject
},
datepicker = function() {
var $tmpScope = $rootScope.$new(!0);
$tmpScope.range = {
main: angular.copy($scope.daterange),
compare: null
}, gaUiModal.show({
header: "Select a date interval",
scope: $tmpScope,
newScope: !1,
width: 775,
customClass: "secondary",
content: '<ga-ui-datepicker-directive data-range="range" data-no-buttons></ga-ui-datepicker-directive>',
buttons: [{
title: "Apply",
"class": "ga-btn-alt orange right",
action: "apply"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
apply: function() {
$scope.daterange = angular.copy($tmpScope.rangeLocal.main), $scope.tests = createTests()
}
},
always: function() {
$tmpScope.$destroy()
}
}, "subModal")
};
$scope.intervalTitle = function() {
return gaUtilsDate.getIntervalTitle($scope.daterange)
}, $scope.executeTests = executeTests, $scope.resetTests = resetTests, $scope.cancelTests = cancelTests, $scope.datepicker = datepicker, $timeout(init)
}), angular.module("ga.pages.querytester", ["ga.services.user", "ga.services.state", "ga.api.data", "ga.api.meta", "ga.utils.query", "ga.utils.transform.chart", "ga.ui.datepicker", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.ui.tooltip", "ga.ui.modal", "ga.ui.json", "ga.visualizations.chartCanvas"]).config(function(gaStateProvider) {
gaStateProvider.initState("querytester", {
type: "sessionStorage",
properties: {
interval: {
init: {
main: "last30Days",
compare: !1
},
valid: function(value) {
try {
var string = JSON.stringify(value),
pattern = new RegExp('^{"main":(.*?),"compare":(.*?)}{1}$'),
match = string.match(pattern),
main = typeof JSON.parse(match[1]),
compare = typeof JSON.parse(match[2]);
return "string" !== main && "object" !== main ? !1 : "boolean" !== compare && "object" !== compare ? !1 : !0
} catch (e) {
return !1
}
}
},
filter: {
init: null,
valid: function(value) {
return null === value ? !0 : "object" == typeof value && value.dimension && angular.isArray(value.values) ? !0 : !1
}
},
group: {
init: "time",
valid: "string"
},
aggregation: {
init: "mean",
valid: "string"
},
currency: {
init: null,
valid: "string"
},
metric: {
init: [{
category: "core",
event: "DAU"
}],
valid: function(value) {
return angular.isArray(value) || null === value
}
}
}
})
}).controller("gaPagesQuerytesterController", function($scope, $timeout, gaServicesUser, gaApiData, gaApiMeta, gaUtilsQuery, gaUtilsChartTransform, gaUiModal, gaState) {
$scope.run = function() {
$scope.query.returnAll = !0, $scope.running = !0, $scope.chart = {
settings: null
}, gaApiData.get($scope.query).then(function(result) {
$scope.running = !1, $scope.urls = result.urls.join("\n"), $scope.result = result.parsed;
var resultRaw = result.response;
$scope.resultRaw = resultRaw;
var chartData = {
left: JSON.parse(JSON.stringify(result.parsed))
};
chartData.left.type = "line";
var settings = {
height: 200,
width: 484,
padding: {
left: 40,
right: 10,
top: 20,
bottom: 20
},
xAxis: {
ticks: 3
},
yAxis: {
lines: 9,
ticks: 3,
zeroLine: !0,
zeroArea: !0
},
legends: 3,
tooltip: !0,
compareToggle: !0,
chartData: {},
inc: 0
},
canvasChartSettings = gaUtilsChartTransform.canvasChartSettings(chartData, settings, !1);
$scope.chart.settings = canvasChartSettings
}).catch(function(result) {
$scope.running = !1, $scope.result = "string" == typeof result ? {
error: result
} : {
error: !0
}, $scope.urls = (result && result.urls || []).join("\n");
var resultRaw = result && result.response;
$scope.resultRaw = resultRaw, $scope.chartSettings = null
})
}, $scope.buildQuery = function() {
var options = {
range: $scope.selection.interval.selected,
currency: $scope.selection.currency.selected
},
query = {
metric: $scope.selection.metric.selected,
filter: $scope.selection.filter.selected,
aggregation: $scope.selection.aggregation.selected,
group: $scope.selection.group.selected
};
try {
$scope.query = gaUtilsQuery.getQuery(query, options)
} catch (e) {
$scope.query = {}
}
return setState(), !0
}, $scope.metricPicker = function() {
var $tmpScope = $scope.$new(!0);
$tmpScope.tmpMetric = angular.copy($scope.selection.metric.selected && $scope.selection.metric.selected[0] || null), $tmpScope.$watch("tmpMetric", function(metric, oldMetric) {
return metric === oldMetric ? !1 : ($scope.selection.metric.selected = [metric], void gaUiModal.hide("main"))
}), gaUiModal.show({
header: "Select a metric",
scope: $tmpScope,
newScope: !1,
width: 720,
content: '<ga-ui-metricpicker metric="tmpMetric"></ga-ui-metricpicker>',
buttons: [{
title: "Cancel",
"class": "ga-btn-alt",
keyCode: 27
}],
always: function() {
$tmpScope.$destroy()
}
}, "main")
}, $scope.dimensionPicker = function() {
var $tmpScope = $scope.$new(!0);
$tmpScope.filter = angular.copy($scope.selection.filter.selected), $tmpScope.selected = angular.copy($scope.selection.filter.selected), gaUiModal.show({
header: "Select a dimension",
scope: $tmpScope,
newScope: !1,
width: 720,
content: '<ga-ui-dimensionpicker selected="selected" filter="filter"></ga-ui-dimensionpicker>',
buttons: [{
title: "Apply",
"class": "ga-btn orange right",
action: "apply"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
apply: function() {
$tmpScope.selected && $tmpScope.selected.dimension && $tmpScope.selected.values.length && ($scope.selection.filter.selected = $tmpScope.selected)
}
},
always: function() {
$tmpScope.$destroy()
}
}, "main")
}, $scope.meta = null;
var setStateTimer, testSelection = function() {
return $scope.selection.group.disabled = !$scope.meta, $scope.selection.aggregation.disabled = !$scope.meta, $scope.selection.currency.disabled = !$scope.meta || !$scope.meta.currency, $scope.selection.group.invalid = !1, $scope.selection.aggregation.invalid = !1, $scope.meta && ($scope.selection.group.selected && ("value" !== $scope.selection.group.selected || $scope.meta.groupValues) && ("dimension" !== $scope.selection.group.selected || $scope.selection.filter.selected) ? "time" === $scope.selection.group.selected && $scope.meta.groupTime === !1 && ($scope.selection.group.invalid = !0) : $scope.selection.group.invalid = !0, $scope.meta.aggregation.indexOf($scope.selection.aggregation.selected) < 0 && ($scope.selection.aggregation.invalid = !0)), !0
},
getState = function() {},
setState = function() {
clearTimeout(setStateTimer), setStateTimer = setTimeout(function() {
angular.forEach($scope.selection, function(value, key) {
gaState.querytester[key] = value.selected
})
}, 250)
};
$scope.selection = {}, $scope.selection.group = {
selected: gaState.querytester.group
}, $scope.$watch("selection.group.selected", function(val, oldVal) {
$scope.selection.group.title = val ? val.slice(0, 1).toUpperCase() + val.slice(1) : "", val !== oldVal && testSelection() && $scope.buildQuery()
}), $scope.selection.aggregation = {
selected: gaState.querytester.aggregation
}, $scope.$watch("selection.aggregation.selected", function(val, oldVal) {
var title = (val || "").replace("event_", "");
$scope.selection.aggregation.title = val ? title.slice(0, 1).toUpperCase() + title.slice(1) : "", val !== oldVal && testSelection() && $scope.buildQuery()
}), $scope.selection.filter = {
selected: gaState.querytester.filter
}, $scope.$watch("selection.filter.selected", function(val, oldVal) {
$scope.selection.filter.title = val ? val.values.map(function(a) {
return a
}).join(",") : "No dimension selected", val !== oldVal && testSelection() && $scope.buildQuery()
}), $scope.selection.currency = {
selected: gaState.querytester.currency || gaServicesUser.settings.currencyDefault
}, $scope.$watch("selection.currency.selected", function(val, oldVal) {
$scope.selection.currency.title = val ? val : "-", val !== oldVal && $scope.buildQuery()
}), $scope.selection.metric = {
selected: gaState.querytester.metric
}, $scope.$watch("selection.metric.selected", function(val, oldVal) {
val && val.length ? ($scope.meta = gaApiMeta.getMetric(val[0]), $scope.selection.metric.title = gaApiMeta.getMetricDisplay(val[0], !0)) : ($scope.meta = null, $scope.selection.metric.title = "No metric selected"), val !== oldVal && testSelection() && $scope.buildQuery()
}), $scope.selection.interval = {
selected: gaState.querytester.interval
}, $scope.$watch("selection.interval.selected", function(val, oldVal) {
switch (val.main) {
case "last30Days":
$scope.selection.interval.title = "Last 30 days";
break;
case "lastWeek":
$scope.selection.interval.title = "Last week";
break;
case "yesterday":
$scope.selection.interval.title = "Yesterday"
}
val !== oldVal && $scope.buildQuery()
}), $timeout(testSelection).then($scope.buildQuery).then(getState)
}), angular.module("ga.pages.querybuilder", ["ui.router", "ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.ui.json", "ga.utils.date", "ga.utils.cache"]).controller("gaPagesQuerybuilderController", function($rootScope, $scope, $timeout, $state, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate) {
$scope.currencies = {
available: gaApiMeta.getCurrencies(),
selectActive: !1,
query: ""
}, $scope.widget = {
id: 1,
name: "Test Widget",
type: "table",
grid: {},
query: {
interval: gaUtilsDate.getPeriodRange("last30Days"),
currency: "",
aggregation: "",
group: "time",
metric: null,
filter: null,
noParse: "true",
returnAll: !0
}
}, $scope.forceDisable = !1, $scope.$watch("widget.query.metric", function(newVal) {
if ($scope.widget.query.filter = null, newVal) {
if ($scope.meta = gaApiMeta.getMetric(newVal.category, newVal.event), $scope.meta.aggregation.indexOf($scope.widget.query.aggregation) < 0 && ($scope.widget.query.aggregation = ""), $scope.widget.query.currency = $scope.meta.currency ? "USD" : "", $scope.meta.groupTime === !1) {
var setAggregation = $scope.meta.aggregation.slice(0, 1);
setAggregation.length ? ($scope.widget.query.aggregation = setAggregation[0], $scope.forceDisable = !1) : ($scope.widget.query.aggregation = "", $scope.forceDisable = !0)
}
} else $scope.meta = null, $scope.widget.query.currency = ""
});
var request = function() {
var query = angular.copy($scope.widget.query);
if (query.top = 10, query.interval = query.interval || gaUtilsDate.getPeriodRange("last30Days"), query.interval.start = query.interval.start, query.interval.end = query.interval.end, query.metric.currency = query.currency, query.noParse = "true" === query.noParse ? !0 : !1, query.newParser = !0, query.returnAll = !0, query.gameId = $state.params.gameId, $scope.loading = !0, query.aggregation) query.group = "dimension";
else {
var meta = gaApiMeta.getMetric(query.metric.category, query.metric.event);
query.aggregation = meta.aggregation[0] || "mean"
}
var url = gaApiData.makeRequestURL(query, null, !0);
$scope.requestURL = gaApiData.makeRequestURL(query);
var promise = gaApiData.getValue(url, null, !0);
promise.then(function(data) {
$scope.loading = !1, $scope.RAW = data
}, function(data) {
$scope.loading = !1, $scope.RAW = data
})
},
metricpicker = function() {
var $tmpScope = $rootScope.$new(!0);
$tmpScope.tmpMetric = angular.copy($scope.widget.query.metric);
var tmpListener = $tmpScope.$watch("tmpMetric", function(newVal, oldVal) {
newVal !== oldVal && ($scope.widget.query.metric = angular.copy($tmpScope.tmpMetric), $timeout(function() {
gaUiModal.hide("subModal")
}))
});
gaUiModal.show({
header: "Select a metric",
scope: $tmpScope,
newScope: !1,
width: 720,
customClass: "secondary",
content: '<ga-ui-metricpicker metric="tmpMetric" data-no-combine></ga-ui-metricpicker>',
buttons: [{
title: "Cancel",
"class": "ga-btn-alt",
keyCode: 27
}],
always: function() {
tmpListener(), $tmpScope.$destroy()
}
}, "subModal")
},
dimensionpicker = function() {
var $tmpScope = $rootScope.$new(!0);
$tmpScope.tmpFilter = angular.copy($scope.widget.query.filter), $tmpScope.tmpSelected = null, $tmpScope.metric = angular.copy($scope.widget.query.metric), gaUiModal.show({
header: "Select a filter",
scope: $tmpScope,
newScope: !1,
width: 720,
customClass: "secondary",
content: '<ga-ui-dimensionpicker no-compare filter="tmpFilter" metric="metric" selected="tmpSelected"></ga-ui-dimensionpicker>',
buttons: [{
title: "Apply",
"class": "ga-btn-alt orange right",
action: "apply"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
apply: function() {
$scope.widget.query.filter = $tmpScope.tmpSelected.values && $tmpScope.tmpSelected.values.length ? angular.copy($tmpScope.tmpSelected) : null
}
},
always: function() {
$tmpScope.$destroy()
}
}, "subModal")
},
datepicker = function() {
var $tmpScope = $rootScope.$new(!0);
$tmpScope.range = {
main: angular.copy($scope.widget.query.interval),
compare: $scope.widget.query.compareInterval ? angular.copy($scope.widget.query.compareInterval) : null
}, $tmpScope.compareModeAvailable = !1, gaUiModal.show({
header: "Select a date interval",
scope: $tmpScope,
newScope: !1,
width: 775,
customClass: "secondary",
content: '<ga-ui-datepicker-directive data-range="range" data-no-buttons></ga-ui-datepicker-directive>',
buttons: [{
title: "Apply",
"class": "ga-btn-alt orange right",
action: "apply"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
apply: function() {
$scope.widget.query.interval = $tmpScope.rangeLocal.main, $scope.widget.query.compareInterval = $tmpScope.compareMode ? $tmpScope.rangeLocal.compare : null
}
},
always: function() {
$tmpScope.$destroy()
}
}, "subModal")
};
$scope.intervalTitle = function() {
return gaUtilsDate.getIntervalTitles($scope.widget.query.interval, $scope.widget.query.compareInterval)
}, $scope.request = request, $scope.metricpicker = metricpicker, $scope.dimensionpicker = dimensionpicker, $scope.datepicker = datepicker
}), angular.module("ga.pages.explore", ["ga.ui.explore"]).controller("gaPagesExploreController", function($scope) {
$scope.exploreSettingsInit = {
standalone: !0
}
}), angular.module("ga.pages.cohorts", ["ui.router", "ga.ui.datepicker", "ga.utils.date", "ga.utils.transform", "ga.utils.cache", "ga.api.meta", "ga.api.data", "ga.ui.modal", "ga.ui.tour", "ga.utils.tracking", "ga.services.user", "ga.visualizations.cohortTable", "ga.components.moment"]).controller("gaPagesCohortsController", function($scope, $rootScope, $timeout, $state, $q, $filter, $cookies, $stateParams, gaUtilsTracking, gaServicesUser, gaUiTour, gaUtilsCache, gaUtilsGridTransform, moment, gaApiData, gaApiMeta, gaUtilsDate, gaUiModal) {
var bgColors = ["E7EEF4", "D4E1ED", "C2D5E7", "AFC9E1", "9CBDDB", "89B0D4", "75A3CD", "6498C8", "508AC0", "3E7FBB"],
datepickerRanges = {
weeks: [{
id: "last4weeks",
name: "Last 4 weeks"
}, {
id: "last12weeks",
name: "Last 12 weeks"
}, {
id: "last24weeks",
name: "Last 24 weeks"
}],
months: [{
id: "last3months",
name: "Last 3 months"
}, {
id: "last6months",
name: "Last 6 months"
}, {
id: "last12months",
name: "Last 12 months"
}]
},
blacklisted = ["WAU", "MAU", "session_length", "session_count", "churn_28", "event_count", "installs"],
defaultInterval = gaUtilsDate.getPeriodRange("last30Days"),
defaultDatepickerData = {
interval: {
main: {
start: defaultInterval.start,
end: defaultInterval.end
},
compare: null
},
selection: "last30Days",
compare: !1,
title: "Date range",
disableCompare: !0,
customRange: datepickerRanges.weeks
};
$scope.globalOptions = {
datepicker: defaultDatepickerData,
granularity: "cohort_day",
showHeatmap: !0,
aggregation: "mean"
}, $scope.metricForPicker = null, $scope.metric = null, $scope.loading = !0, $scope.weekTooltipNotice = "", $scope.table = {
settings: {
maxRows: 10,
pager: !0,
colPager: !1,
sortable: !0,
more: !1,
totals: !0,
sumSwitcher: !1
},
tableView: "list",
datasets: null,
installCol: null
}, $scope.aggregations = {
mean: !1,
sum: !1,
event_count: !1
}, $scope.nodata = !1;
var cachedParsedData = null;
$scope.datepickerWatch = $scope.$watch("globalOptions.datepicker.interval", function(newVal, oldVal) {
newVal === oldVal || $scope.updatingRules || performRequest()
}), $scope.heatmapWatch = $scope.$watch("globalOptions.showHeatmap", function(newVal, oldVal) {
if (newVal !== oldVal) {
if ($scope.loading) return;
gaUtilsTracking.trackEvent({
category: "cohort",
action: "heatmap",
label: $scope.globalOptions.showHeatmap ? "show" : "hide"
}), $scope.table.showColors = $scope.globalOptions.showHeatmap, saveState()
}
}), $scope.$on("userSettingsChange", function() {
performRequest(), updateWeekNotice()
});
var init = function() {
var cached = gaUtilsCache.get("cohort-state-" + $stateParams.gameId, "localStorage");
cached ? loadState(cached) : changeMetric({
category: "core",
currency: "",
event: "retention"
}), updateWeekNotice()
},
updateWeekNotice = function() {
$scope.weekTooltipNotice = "Sunday" === gaServicesUser.settings.startOfWeek ? "Cohort weeks always start Monday" : ""
},
saveState = function() {
var saveObject = {
metric: $scope.metric,
options: $scope.globalOptions
};
delete saveObject.metric.meta, gaUtilsCache.put("cohort-state-" + $stateParams.gameId, saveObject, "localStorage", 6048e5)
},
loadState = function(cached) {
if (cached.options.datepicker && "custom" !== cached.options.datepicker.selection) {
var interval = gaUtilsDate.getPeriodRange(cached.options.datepicker.selection);
cached.options.datepicker.interval.main = interval
}
$scope.globalOptions = cached.options, changeMetric(cached.metric)
},
performRequest = function() {
$scope.metric && ($scope.loading = !0, "retention" === $scope.metric.event || "returning_users" === $scope.metric.event ? request() : getValidCohorts().then(function(cohorts) {
request(cohorts)
}))
},
request = function(cohorts) {
var intervalEnd, intervalStart = moment($scope.globalOptions.datepicker.interval.main.start).utc(),
rollup = "";
"cohort_week" === $scope.globalOptions.granularity ? (intervalStart = intervalStart.startOf("isoweek").unix(), intervalEnd = moment().utc().unix(), rollup = "weekly") : "cohort_month" === $scope.globalOptions.granularity ? (intervalStart = intervalStart.startOf("month").unix(), intervalEnd = moment().utc().unix(), rollup = "monthly") : (intervalStart = intervalStart.unix(), intervalEnd = moment($scope.globalOptions.datepicker.interval.main.end).utc().unix());
var interval = {
start: 1e3 * intervalStart,
end: 1e3 * intervalEnd
},
installQuery = {
interval: interval,
currency: gaServicesUser.settings.currencyDefault,
noAggregation: !0,
metric: [{
category: "core",
currency: "",
event: "installs"
}]
};
rollup && (installQuery.rollup = rollup);
var meta = gaApiMeta.getMetric($scope.metric.category, $scope.metric.event);
meta.currency && ($scope.metric.currency = gaServicesUser.settings.currencyDefault), $scope.metric.aggregation = $scope.globalOptions.aggregation || meta.aggregation[0] || "mean", $scope.table.settings.sumSwitcher = "mean" !== $scope.metric.aggregation ? !0 : !1;
var metric, noAggregation;
"retention" === $scope.metric.event ? (metric = {
category: "core",
currency: "",
event: "retention_full"
}, noAggregation = !1) : "returning_users" === $scope.metric.event ? (metric = {
category: "core",
currency: "",
event: "returning_users_full"
}, noAggregation = !1) : (metric = $scope.metric, noAggregation = !0);
var query = {
interval: interval,
aggregation: $scope.metric.aggregation,
noAggregation: noAggregation,
group: "time",
metric: [metric]
};
cohorts && (query.filter = {
dimension: $scope.globalOptions.granularity,
values: cohorts
}, query.group = "dimension", rollup && (query.rollup = rollup));
var promises = {
metric: gaApiData.get(query),
installs: gaApiData.get(installQuery)
};
if ("retention" === $scope.metric.event) {
var returningUserQuery = {
interval: interval,
aggregation: "mean",
noAggregation: !0,
group: "time",
metric: [{
category: "core",
currency: "",
event: "returning_users_full"
}]
};
promises.users = gaApiData.get(returningUserQuery)
} else if ("returning_users" === $scope.metric.event) {
var retentionUserQuery = {
interval: interval,
aggregation: "mean",
noAggregation: !0,
group: "time",
metric: [{
category: "core",
currency: "",
event: "retention_full"
}]
};
promises.users = gaApiData.get(retentionUserQuery)
} else {
if (-1 === ["DAU", "MAU", "WAU"].indexOf($scope.metric.event)) {
var userQuery = {
interval: interval,
noAggregation: !0,
group: "dimension",
metric: [{
category: "core",
currency: "",
event: "DAU"
}],
filter: {
dimension: $scope.globalOptions.granularity,
values: cohorts
}
};
rollup && (userQuery.rollup = rollup), promises.users = gaApiData.get(userQuery)
}
if (["ARPDAU", "ARPPU", "paying_users"].indexOf($scope.metric.event) > -1 || "business" === $scope.metric.category) {
var revenueQuery = {
interval: interval,
noAggregation: !0,
aggregation: "sum",
group: "dimension",
metric: [{
category: "core",
currency: gaServicesUser.settings.currencyDefault,
event: "revenue"
}],
filter: {
dimension: $scope.globalOptions.granularity,
values: cohorts
}
};
rollup && (revenueQuery.rollup = rollup), promises.revenue = gaApiData.get(revenueQuery)
}
if (["ARPDAU", "ARPPU", "revenue"].indexOf($scope.metric.event) > -1) {
var payingUsersQuery = {
interval: interval,
noAggregation: !0,
group: "dimension",
metric: [{
category: "core",
currency: gaServicesUser.settings.currencyDefault,
event: "paying_users"
}],
filter: {
dimension: $scope.globalOptions.granularity,
values: cohorts
}
};
rollup && (payingUsersQuery.rollup = rollup), promises.payingUsers = gaApiData.get(payingUsersQuery)
}
if ("conversion" === $scope.metric.event || "converting_users" === $scope.metric.event) {
var conversionQuery = {
interval: interval,
noAggregation: !0,
group: "dimension",
metric: [{
category: "core",
currency: "",
event: "conversion" === $scope.metric.event ? "converting_users" : "conversion"
}],
filter: {
dimension: $scope.globalOptions.granularity,
values: cohorts
}
};
rollup && (conversionQuery.rollup = rollup), promises.conversion = gaApiData.get(conversionQuery)
}
}
saveState(), $q.all(promises).then(function(data) {
var installs = null;
data.installs && (installs = parseInstalls(data.installs, cohorts)), data.metric && parseMetric(data.metric, installs, data.users, data.revenue, data.conversion, data.payingUsers)
})
},
parseMetric = function(data, installs, users, revenue, conversion, payingUsers) {
if (data.noData) return $scope.nodata = !0, $scope.table.settings.maxRows = 15, $scope.table.datasets = null, $scope.table.showColors = $scope.globalOptions.showHeatmap, $scope.table.dataChanged = {
dataChange: !0
}, void($scope.loading = !1);
$scope.nodata = !1, "retention" !== $scope.metric.event || gaServicesUser.onboarding.cohort && gaServicesUser.onboarding.cohort.tour || (gaServicesUser.onboarding.set("cohort", "tour", !0), gaServicesUser.onboarding.save(), runTour());
var parsed = gaUtilsGridTransform.parse(data),
parsedUsers = null,
parsedRevenue = null,
parsedConversion = null,
parsedPayingUsers = null;
"retention" === $scope.metric.event || "returning_users" === $scope.metric.event ? users && (parsedUsers = gaUtilsGridTransform.parse(users)) : (parsed = gaUtilsGridTransform.flipData(parsed), users && (parsedUsers = gaUtilsGridTransform.parse(users), parsedUsers = gaUtilsGridTransform.flipData(parsedUsers)), revenue && (parsedRevenue = gaUtilsGridTransform.parse(revenue), parsedRevenue = gaUtilsGridTransform.flipData(parsedRevenue)), conversion && (parsedConversion = gaUtilsGridTransform.parse(conversion), parsedConversion = gaUtilsGridTransform.flipData(parsedConversion)), payingUsers && (parsedPayingUsers = gaUtilsGridTransform.parse(payingUsers), parsedPayingUsers = gaUtilsGridTransform.flipData(parsedPayingUsers))), installs && (parsed = applyInstalls(parsed, installs)), parsed = applyCohortValues({
data: parsed,
users: parsedUsers,
revenue: parsedRevenue,
conversion: parsedConversion,
payingUsers: parsedPayingUsers
}), cachedParsedData = angular.copy(parsed);
var nbRows = parsed.rows.length;
$scope.table.settings.maxRows = nbRows > 15 && 30 >= nbRows ? nbRows : 15 >= nbRows ? 15 : 30, $scope.table.datasets = parsed, $scope.table.showColors = $scope.globalOptions.showHeatmap, $scope.table.dataChanged = {
dataChange: !0
}, $scope.loading = !1
},
applyInstalls = function(data, installs) {
return "retention" === $scope.metric.event || "returning_users" === $scope.metric.event ? angular.forEach(installs.installs, function(install, index) {
data.rows[index].axis.installs = install.sum
}) : angular.forEach(installs.installs, function(install) {
data.rows.some(function(row) {
return row.axis.dimensionValue === install.cohort ? (row.axis.installs = install.sum, !0) : void 0
})
}), data.footer.axis.installs = installs.sum, data
},
parseInstalls = function(data, cohorts) {
var installSum = [],
total = 0;
if ("retention" === $scope.metric.event || "returning_users" === $scope.metric.event) data.data && data.data.timeseries && data.data.timeseries.length && data.data.timeseries[0].data[0].data && angular.forEach(data.data.timeseries[0].data[0].data, function(install, index) {
installSum.push({
index: index,
sum: install.total
}), total += install.total
});
else if (data.data && data.data.timeseries && data.data.timeseries.length && data.data.timeseries[0].data[0].data) {
var installs = data.data.timeseries[0].data[0].data;
angular.forEach(cohorts, function(cohort) {
angular.forEach(installs, function(install, index) {
install.ts / 1e3 === parseInt(cohort) && (installSum.push({
cohort: cohort,
index: index,
sum: install.total
}), total += install.total)
})
})
}
return {
installs: installSum,
sum: total
}
},
getValidCohorts = function() {
var deferred = $q.defer(),
values = [],
interval = $scope.globalOptions.datepicker.interval.main,
intervalStart = 0,
intervalEnd = 0;
return "cohort_week" === $scope.globalOptions.granularity ? (intervalStart = moment(interval.start).utc().startOf("isoweek").unix(), intervalEnd = moment(interval.end).utc().endOf("isoweek").unix()) : "cohort_month" === $scope.globalOptions.granularity ? (intervalStart = moment(interval.start).utc().startOf("month").unix(), intervalEnd = moment(interval.end).utc().endOf("month").unix()) : (intervalStart = moment(interval.start).utc().unix(), intervalEnd = moment(interval.end).utc().unix()), gaApiData.getValue("/dimensions", $state.params.gameId).then(function(rawData) {
angular.forEach(rawData, function(dimensions, key) {
$scope.globalOptions.granularity === key && (angular.forEach(dimensions, function(value) {
value && value >= intervalStart && intervalEnd >= value && values.push(value)
}), deferred.resolve(values))
})
}), deferred.promise
},
changeMetric = function(metric) {
var meta = gaApiMeta.getMetric(metric.category, metric.event);
metric.meta = meta, $scope.metric = metric, $scope.metricForPicker = metric, $scope.metricForPicker.eventDisplay || ($scope.metricForPicker.eventDisplay = gaApiMeta.getMetricDisplay(metric.category, metric.event)), "retention" === metric.event || "returning_users" === metric.event ? changeGranularity("cohort_day", !0) : "cohort_day" === $scope.globalOptions.granularity && changeGranularity("cohort_week", !0), $scope.aggregations.mean = -1 === meta.aggregation.indexOf("mean") && meta.aggregation.length ? !1 : !0, $scope.aggregations.sum = -1 === meta.aggregation.indexOf("sum") ? !1 : !0, $scope.aggregations.event_count = -1 === meta.aggregation.indexOf("event_count") ? !1 : !0, $scope.showDisplayValues = !$scope.aggregations.mean && ($scope.aggregations.event_count || $scope.aggregations.sum) || $scope.aggregations.event_count || $scope.aggregations.sum ? !0 : !1, $scope.aggregations[$scope.globalOptions.aggregation] || ($scope.aggregations.mean ? $scope.globalOptions.aggregation = "mean" : $scope.aggregations.sum ? $scope.globalOptions.aggregation = "sum" : $scope.aggregations.event_count && ($scope.globalOptions.aggregation = "event_count")), gaUtilsTracking.trackEvent({
category: "cohort",
action: "metric",
label: metric.event
}), performRequest()
},
changeGranularity = function(granularity, noreq) {
$scope.globalOptions.granularity !== granularity && ($scope.globalOptions.granularity = granularity, $scope.globalOptions.datepicker.customRange = "cohort_week" === granularity ? datepickerRanges.weeks : "cohort_month" === granularity ? datepickerRanges.months : null, noreq || (gaUtilsTracking.trackEvent({
category: "cohort",
action: "granularity",
label: granularity
}), performRequest()))
},
changeAggregation = function(aggregation, noreq) {
$scope.globalOptions.aggregation = aggregation, noreq || performRequest()
},
useMetricpicker = function() {
var $tmpScope = $rootScope.$new(!0);
$tmpScope.tmpMetric = angular.copy($scope.metricForPicker);
var tmpListener = $tmpScope.$watch("tmpMetric", function(newVal, oldVal) {
newVal !== oldVal && ($timeout(function() {
gaUiModal.hide("subModal")
}), $scope.initializing = !1, changeMetric($tmpScope.tmpMetric))
});
$tmpScope.filter = {
blacklisted: blacklisted,
disableStarEvents: !0
}, gaUiModal.show({
header: "Select a metric",
scope: $tmpScope,
newScope: !1,
width: 720,
customClass: "secondary",
content: '<ga-ui-metricpicker metric="tmpMetric" filter="filter"></ga-ui-metricpicker>',
buttons: [{
title: "Cancel",
"class": "ga-btn-alt",
keyCode: 27
}],
always: function() {
tmpListener(), $tmpScope.$destroy()
}
}, "subModal")
},
applyCohortValues = function(options) {
if (!options.data) return null;
var data = options.data || null,
users = options.users || null,
revenue = options.revenue || null,
conversion = options.conversion || null,
payingUsers = options.payingUsers || null,
max = null,
min = null;
angular.forEach(data.rows, function(row) {
angular.forEach(row.values, function(val) {
val.value && (min || max ? (min > val.value && (min = val.value), max < val.value && (max = val.value)) : (min = val.value, max = val.value))
})
});
var delta = (max - min) / 10;
return angular.forEach(data.rows, function(row, index) {
angular.forEach(row.values, function(val, subIndex) {
if (val.value) {
var intervalIndex = Math.round((val.value - min) / delta);
intervalIndex || (intervalIndex = 1), val.color = bgColors[intervalIndex - 1], users && users.rows[index].values[subIndex] && (val.users = $filter("formatUnitType")(users.rows[index].values[subIndex].value, users.meta.cols[subIndex])), revenue && revenue.rows[index].values[subIndex] && (val.revenue = $filter("formatUnitType")(revenue.rows[index].values[subIndex].value, revenue.meta.cols[subIndex])), conversion && conversion.rows[index].values[subIndex] && (val.conversion = $filter("formatUnitType")(conversion.rows[index].values[subIndex].value, conversion.meta.cols[subIndex])), payingUsers && payingUsers.rows[index].values[subIndex] && (val.payingUsers = $filter("formatUnitType")(payingUsers.rows[index].values[subIndex].value, payingUsers.meta.cols[subIndex]))
}
})
}), data
},
exportToCsv = function() {
if (!$scope.loading) {
gaUtilsTracking.trackPageRaw("/game/export/cohort");
var data = getCsvData(),
name = $scope.metric.category + "_" + $scope.metric.event + "_" + $scope.globalOptions.granularity,
form = angular.element("#cohort-export-csv-form");
form.find("[name=X-Authorization]").val(gaServicesUser.token), form.find("[name=csv_name]").val(sanitizeCsvStr(name) + ".csv"), form.find("[name=csv_data]").val(data), form.submit()
}
},
getCsvData = function() {
var csv = [],
header = [cachedParsedData.header.axis.value],
footer = [cachedParsedData.footer.axis.value];
return $scope.table.settings.sumSwitcher && "Sum" === $scope.table.settings.sumSelected && (footer[0] = "Sum"), angular.forEach(cachedParsedData.header.values, function(value, index) {
0 === index && header.push("Users"), header.push(value.value + (value.subValue ? " (" + value.subValue + ")" : ""))
}), angular.forEach(cachedParsedData.footer.values, function(value, index) {
0 === index && footer.push(cachedParsedData.footer.axis.installs), footer.push($scope.table.settings.sumSwitcher && "Sum" === $scope.table.settings.sumSelected ? value.sum : value.value)
}), csv.push(header), angular.forEach(cachedParsedData.rows, function(row) {
var tmpRow = [];
tmpRow.push("date" === cachedParsedData.meta.axis.type ? $filter("formatUnitType")(row.axis.value, "rawdate") : row.axis.value), tmpRow.push(row.axis.installs), angular.forEach(row.values, function(value) {
tmpRow.push(value.value)
}), csv.push(tmpRow)
}), csv.push(footer), csv = csv.map(function(row) {
return row.join(";")
}), csv = csv.join("\n")
},
sanitizeCsvStr = function(string) {
return string.replace(/\*/g, "all").replace(/[^a-z0-9]/gi, "_").replace(/_{2,}/g, "_").toLowerCase()
},
destroyWatches = function() {
$scope.datepickerWatch && ($scope.datepickerWatch(), $scope.datepickerWatch = null), $scope.heatmapWatch && ($scope.heatmapWatch(), $scope.heatmapWatch = null)
},
runTour = function() {
gaUiTour.reset(), gaUiTour.addStep({
title: "Granulate",
text: "See your data broken down into days, weeks or months cohorts. Retention, specifically, is only available in Days granularity.",
element: ".granularityselect",
watcher: {},
arrow: "top-left",
anchor: "bottom-left-center",
zindex: 1200,
skippable: !0
}), gaUiTour.addStep({
title: "Metrics",
text: "Choose from a select group of metrics.",
element: ".metricsselect",
watcher: {},
arrow: "top-left",
anchor: "bottom-center",
zindex: 1200,
skippable: !0
}), gaUiTour.addStep({
title: "Toggle heatmap",
text: "Toggle heatmap colours in the table cells to enhance value distribution.",
element: ".toggleheatmap",
watcher: {},
arrow: "top-right",
anchor: "bottom-right-center",
zindex: 1200,
skippable: !0
}), gaUiTour.addStep({
title: "Details",
text: "Hover a specific value cell to see even more detailed information.",
element: 'td[data-position="1,1"]',
image: "",
watcher: {},
arrow: "top-left",
anchor: "bottom-left-center",
zindex: 1200,
skippable: !0
}), gaUiTour.setOptions({
navigation: !0
}), gaUiTour.start()
};
init(), $scope.$on("$destroy", function() {
destroyWatches()
}), $scope.useMetricpicker = useMetricpicker, $scope.changeGranularity = changeGranularity, $scope.changeAggregation = changeAggregation, $scope.exportToCsv = exportToCsv, $scope.runTour = runTour
}), angular.module("ga.pages.game.settings.information", ["ga.services.user", "ga.api.userDb.authenticated.game", "ga.config", "ga.ui.notify", "ga.ui.modal"]).controller("gaPagesGameSettingsInformationController", function($scope, $rootScope, $state, gaServicesUser, gaConfig, gaUiNotify, gaApiUserDbAuthenticatedGame, gaUiModal) {
$scope.showViewOnly = $state.$current.data.showViewOnly || !1;
var saving = !1,
gameId = parseInt($state.params.gameId, 10),
game = gaServicesUser.game(gameId);
$scope.errors = {}, $scope.validated = !0;
var defaultImage = "static/ga-app/images/default-game-icon.png",
imageBaseUrl = gaConfig.images.baseUrl;
$scope.uploadFile = null, $scope.gameInfo = {
title: game.title,
imageFilePath: game.imageFile ? imageBaseUrl + game.imageFile : defaultImage,
imageFile: game.imageFile ? game.imageFile : null,
changed: !1
};
var getkeys = function() {
gaApiUserDbAuthenticatedGame.getGameData(gameId).then(function(result) {
$scope.keys = {
key: result.key,
secret: result.secret_key,
api: result.data_api_key
}
}).catch(function() {
gaUiNotify.show("Something went wrong getting game keys", 2e3, "warning")
})
};
$scope.validate = function() {
0 === $scope.gameInfo.title.length ? ($scope.errors.name = "Title can not be zero characters", $scope.validated = !1) : ($scope.errors.name = null, $scope.validated = !0), $scope.gameInfo.changed = !0
}, $scope.save = function(redirectObj) {
saving || (saving = !0, gaApiUserDbAuthenticatedGame.saveGame(gameId, {
title: $scope.gameInfo.title,
imageFile: $scope.gameInfo.imageFile
}).then(function() {
redirectObj ? gaServicesUser.getUserData().then(function() {
$rootScope.$broadcast("currentGameChange", null), $state.go(redirectObj.state, redirectObj.params)
}) : ($scope.gameInfo.changed = !1, gaUiNotify.show("Game saved", 4e3, "default"), gaServicesUser.getUserData().then(function() {
$rootScope.$broadcast("currentGameChange", null), game = gaServicesUser.game(gameId), saving = !1
}))
}).catch(function() {
gaUiNotify.show("Something went wrong while saving", 2e3, "warning"), saving = !1
}))
}, $scope.discard = function() {
$scope.gameInfo = {
title: game.title,
imageFile: game.imageFile ? game.imageFile : null,
imageFilePath: game.imageFile ? imageBaseUrl + game.imageFile : defaultImage,
changed: !1
}
}, $scope.$watch("gameInfo.imageFile", function(newVal, oldVal) {
newVal && newVal !== oldVal && newVal !== game.imageFile && ($scope.gameInfo.imageFilePath = newVal ? imageBaseUrl + newVal : defaultImage, $scope.gameInfo.changed = !0)
}), getkeys();
var ignoreSaveState = !1;
$scope.$on("$stateChangeStart", function(event, toState, toParams) {
$scope.gameInfo.changed && !ignoreSaveState && (event.preventDefault(), gaUiNotify.loading(!1), gaUiModal.show({
scope: $scope,
width: 500,
header: "Unsaved changes",
content: "Do you want to save the changes you made or discard the changes and leave the page?",
buttons: [{
title: "Save",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "save"
}, {
title: "Discard changes",
"class": "ga-btn-text right",
keyCode: 27,
action: "go"
}],
actions: {
go: function() {
ignoreSaveState = !0, $state.go(toState, toParams)
},
save: function() {
ignoreSaveState = !0, $scope.save({
state: toState,
params: toParams
})
}
}
}))
})
}), angular.module("ga.pages.game.settings.exportData", ["ui.router", "ga.api.data", "ga.ui.datepicker", "ga.ui.modal", "ga.utils.date", "ga.pages.game.settings.exportData.modal", "ga.components.moment"]).controller("gaPagesGameSettingsExportDataController", function($scope, $q, $compile, $timeout, $http, $state, $window, gaApiData, gaUtilsDate, gaUiModal, moment) {
$scope.gameId = $state.params.gameId, $scope.options = {
dateRange: {
main: {
start: null,
end: null
},
restrictInterval: {
start: moment.utc({
year: 2014,
month: 3,
day: 1
}),
end: null
}
}
}, $scope.exportData = function() {
var tsStart = $scope.options.dateRange.main.start,
tsEnd = $scope.options.dateRange.main.end;
tsStart && tsEnd && ($scope.processing = !0, gaApiData.getDataDownloadLinks($scope.gameId, {
start: tsStart,
end: tsEnd
}).then(function(result) {
showDownloadModal(result), $scope.processing = !1
}).catch(function() {
$scope.processing = !1
}))
};
var showDownloadModal = function(links) {
var proxyLinks = links.map(function(link) {
var match = link.url.match(/\/(\d{4})\/(\d{2})\/(\d{2})\/(\d+)\/(.+?)\.json\.gz\?.+?Expires=(\d+?)&Signature=(.+)/);
return match && 8 === match.length ? {
url: window.location.protocol + "//" + window.location.host + "/export-data/" + match[4] + "-" + match[1] + "-" + match[2] + "-" + match[3] + "-" + match[5] + ".json.gz?e=" + match[6] + "&s=" + match[7]
} : {
url: "invalid"
}
}).filter(function(link) {
return link
}),
dateTitle = gaUtilsDate.getIntervalTitle({
start: gaUtilsDate.moment.utc($scope.options.dateRange.main.start),
end: gaUtilsDate.moment.utc($scope.options.dateRange.main.end)
});
gaUiModal.page({
controller: "gaPagesGameSettingsExportDataModalController",
templateUrl: "/static/ga-app/modules/pages/game/settings/export-data/export-modal.html",
width: 800,
parameters: {
links: proxyLinks,
title: dateTitle
}
})
}
}), angular.module("ga.pages.game.settings.exportData.modal", ["ga.utils.helpers.focusElement", "ga.utils.tracking"]).controller("gaPagesGameSettingsExportDataModalController", function($scope, gaUtilsTracking) {
$scope.dataExport = {
links: $scope.links,
amountOfLinks: $scope.links.length,
parsedLinks: null,
linkType: "RAW"
};
var parseLinks = function(type, links) {
$scope.dataExport.parsedLinks = "LINUX" === type ? 'wget -O "' + links.map(function(o) {
return getFileName(o.url) + '" "' + o.url
}).join('" & wget -O "') + '"' : links.map(function(o) {
return o.url
}).join("\n")
},
getFileName = function(url) {
return url = url.substring(0, -1 === url.indexOf("#") ? url.length : url.indexOf("#")), url = url.substring(0, -1 === url.indexOf("?") ? url.length : url.indexOf("?")), url.substring(url.lastIndexOf("/") + 1, url.length)
};
$scope.selectLinkType = function(linkType) {
$scope.dataExport.linkType = linkType, parseLinks($scope.dataExport.linkType, $scope.dataExport.links), setTimeout(focusLinks)
};
var focusLinks = function() {
angular.element.find("#data-export-links-text-area")[0].select()
};
parseLinks($scope.dataExport.linkType, $scope.dataExport.links), gaUtilsTracking.trackPageRaw("/game/export/data")
}), angular.module("ga.pages.game.settings.users", ["ga.services.user", "ga.api.userDb.authenticated.game", "ga.api.userDb.authenticated.invite", "ga.services.dialogs", "ga.ui.notify", "ga.ui.modal"]).controller("gaPagesGameSettingsUsersController", function($scope, $state, gaServicesUser, gaUiModal, gaApiUserDbAuthenticatedGame, gaDialogs, gaUiNotify, gaApiUserDbAuthenticatedInvite) {
var gameId = parseInt($state.params.gameId, 10),
game = gaServicesUser.game(gameId);
$scope.gameUsers = [];
var getUsers = function() {
gaApiUserDbAuthenticatedGame.getAccesses().then(function(result) {
if (result && result[0]) {
var users = [],
members = [],
invited = [];
if (result[0].studio && result[0].studio.owner) {
var tmpOwner = {
email: result[0].studio.owner.email,
invite: !1,
name: result[0].studio.owner.first_name + " " + result[0].studio.owner.last_name,
role: "Studio owner",
owner: !0,
self: !1,
studio: !0
};
gaServicesUser.id === result[0].studio.owner.id && (tmpOwner.self = !0), users.push(tmpOwner)
}
result[0].studio && result[0].studio.accesses && result[0].studio.accesses.some(function(userAccess) {
var tmpUsr = {
email: userAccess.email,
invite: !1,
id: userAccess.id,
userId: userAccess.user_id,
name: userAccess.first_name + " " + userAccess.last_name,
role: getStudioRole(userAccess.role_id),
roleId: userAccess.role_id,
self: !1,
studio: !0
};
users.push(tmpUsr)
}), result[0].accesses && (result[0].accesses.some(function(userAccess) {
if (userAccess.invite_pending) invited.push({
email: userAccess.invite_email,
invite: !0,
id: userAccess.id,
role: getGameRole(userAccess.role_id),
studio: !1
});
else {
var tmpUsr = {
email: userAccess.email,
invite: !1,
id: userAccess.id,
userId: userAccess.user_id,
name: userAccess.first_name + " " + userAccess.last_name,
role: getGameRole(userAccess.role_id),
roleId: userAccess.role_id,
self: !1,
studio: !1
};
gaServicesUser.id === userAccess.user_id ? (tmpUsr.self = !0, users.unshift(tmpUsr)) : members.push(tmpUsr)
}
}), members = members.sort(function(a, b) {
return a.name.localeCompare(b.name)
}), users = users.concat(members), invited = invited.sort(function(a, b) {
return a.email.localeCompare(b.email)
}), users = users.concat(invited)), $scope.gameUsers = users
}
})
};
$scope.deleteInvite = function(id) {
gaUiModal.confirm("Please confirm that you wan't to delete the invitation.", "Delete invitation").then(function() {
gaApiUserDbAuthenticatedInvite.delete({
type: "game",
id: id
}).then(function() {
getUsers()
})
})
}, $scope.inviteUser = function() {
gaDialogs.access({
type: "game",
role: null,
userid: null,
gameId: game.id,
title: game.title
}).then(function(result) {
result && result.access_created === !0 ? gaUiNotify.show("User access created", 4e3, "default") : gaUiNotify.show("Game invite has been sent", 4e3, "default"), getUsers()
})
}, $scope.editUser = function(userAccess) {
gaDialogs.access({
type: "game",
role: userAccess.roleId,
userAccessId: userAccess.id,
gameId: game.id,
gameTitle: game.title,
title: userAccess.name
}).then(function(result) {
result && result.deletion === !0 ? gaUiNotify.show("The user was removed from the game", 4e3, "default") : gaUiNotify.show("User saved", 4e3, "default"), getUsers()
})
}, $scope.goToUserSettings = function() {
$state.go("user.settings.profile")
};
var getGameRole = function(roleid) {
return 1 === roleid ? "Game admin" : "Game viewer"
},
getStudioRole = function(roleid) {
return 1 === roleid ? "Studio admin" : "Studio viewer"
};
getUsers()
}), angular.module("ga.pages.game.settings.emailReports", ["ui.router", "ga.api.userDb.authenticated.report", "ga.api.userDb.authenticated.game", "ga.ui.modal", "ga.services.user", "ga.pages.game.settings.emailReports.addSubscriber", "ga.services.dialogs", "ga.ui.notify"]).controller("gaPagesGameSettingsEmailReportsController", function($scope, $q, $compile, $timeout, $http, $state, gaApiUserDbAuthenticatedReport, gaApiUserDbAuthenticatedGame, gaUiModal, gaServicesUser, gaDialogs, gaUiNotify) {
$scope.gameId = $state.params.gameId;
var ignoreSaveState = !1;
$scope.newSubscriber = {
email: "",
disabled: !1,
error: "",
selected: {
daily: !1,
weekly: !0,
monthly: !1
}
}, $scope.reportId = 0, $scope.subscribers = [], $scope.orgSubscribers = [];
var getReport = function() {
var deferred = $q.defer();
return gaApiUserDbAuthenticatedReport.getGameReports().then(function(data) {
data.length ? ($scope.reportId = data[0].id, deferred.resolve($scope.reportId)) : (deferred.reject("No report found for this game"), console.log("No reports found for game"))
}), deferred.promise
},
getSubscribers = function() {
var deferred = $q.defer(),
tmpSubscribers = {
subscribers: {},
users: {}
};
return getGameUsers().then(function(data) {
tmpSubscribers.users = data, gaApiUserDbAuthenticatedReport.getReportSubscribers($scope.reportId).then(function(data) {
angular.forEach(data, function(subscriber) {
tmpSubscribers.users[subscriber.email] ? tmpSubscribers.users[subscriber.email] = angular.extend(tmpSubscribers.users[subscriber.email] || {}, subscriber) : tmpSubscribers.subscribers[subscriber.email] = subscriber
});
var subscribers = [],
users = [];
angular.forEach(tmpSubscribers.subscribers, function(subscriber) {
subscribers.push(subscriber)
}), subscribers = subscribers.sort(function(a, b) {
return a.email.localeCompare(b.email)
}), angular.forEach(tmpSubscribers.users, function(user) {
users.push(user)
}), users = users.sort(function(a, b) {
return a.email.localeCompare(b.email)
}).sort(function(a, b) {
return a.sortId > b.sortId
}), deferred.resolve({
subscribers: subscribers,
users: users
})
})
}), deferred.promise
},
getGameUsers = function() {
var deferred = $q.defer(),
gameUsers = {};
return gaApiUserDbAuthenticatedGame.getGameUsers().then(function(data) {
angular.forEach(data, function(user) {
gameUsers[user.email] = {
id: 0,
user_id: user.id,
report_id: $scope.reportId,
email: user.email,
gameUser: !0,
daily: !1,
weekly: !1,
monthly: !1,
sortId: user.studio_owner ? 0 : _getSortId(user.studio_user, user.role_id),
role: user.studio_owner ? "Studio owner" : getRoleTitle(user.studio_user, user.role_id),
self: gaServicesUser.details.email === user.email ? -1 : 0
}
}), deferred.resolve(gameUsers)
}), deferred.promise
},
_getSortId = function(studio, role_id) {
return role_id + (studio ? 0 : 2)
},
addSubscriberClick = function() {
gaUiModal.page({
controller: "gaPagesGameSettingsEmailReportsAddSubscriberController",
templateUrl: "/static/ga-app/modules/pages/game/settings/email-reports/dialogs/add-subscriber.html",
width: 700,
parameters: {
reportId: $scope.reportId
}
}).then(function() {
getSubscribers().then(function(users) {
$scope.orgSubscribers = angular.copy(users), $scope.subscribers = users
}), gaUiNotify.show("Subscriber added", 4e3)
})
},
save = function(redirectObj) {
var promises = [],
updateList = !1;
angular.forEach($scope.subscribers.users, function(subscriber, index) {
angular.equals(subscriber, $scope.orgSubscribers.users[index]) || (subscriber.id || (updateList = !0), promises.push(putSubscriber(subscriber)))
}), angular.forEach($scope.subscribers.subscribers, function(subscriber, index) {
angular.equals(subscriber, $scope.orgSubscribers.subscribers[index]) || (subscriber.id || (updateList = !0), promises.push(putSubscriber(subscriber)))
}), promises.length ? (checkStatus(), $q.all(promises).then(function() {
redirectObj ? $state.go(redirectObj.state, redirectObj.params) : (updateList ? getReport().then(function() {
getSubscribers().then(function(users) {
$scope.orgSubscribers = angular.copy(users), $scope.subscribers = users, checkStatus()
})
}) : ($scope.orgSubscribers = angular.copy($scope.subscribers), checkStatus()), gaUiNotify.show("Changes saved", 4e3))
}).catch(function() {
getReport().then(function() {
getSubscribers().then(function(users) {
$scope.orgSubscribers = angular.copy(users), $scope.subscribers = users, checkStatus()
})
}), gaUiNotify.show("Something went wrong while saving", 4e3, "warning")
})) : checkStatus()
},
putSubscriber = function(subscriber) {
return subscriber.id ? gaApiUserDbAuthenticatedReport.updateReportSubscriber(subscriber.id, subscriber) : gaApiUserDbAuthenticatedReport.createReportSubscriber($scope.reportId, subscriber)
},
deleteSubscriber = function(subscriber) {
var index = $scope.subscribers.subscribers.indexOf(subscriber);
gaUiModal.show({
scope: $scope,
width: 500,
header: "Remove subscriber",
content: "Are you sure you want to remove <strong>" + subscriber.email + "</strong> from the list of subscribers?",
buttons: [{
title: "Remove",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "doRemove"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
doRemove: function() {
gaApiUserDbAuthenticatedReport.deleteReportSubscriber(subscriber.id).then(function() {
$scope.subscribers.subscribers.splice(index, 1), gaUiNotify.show("Subscriber removed", 4e3)
})
}
}
})
},
checkStatus = function() {
$scope.changed = !angular.equals($scope.orgSubscribers, $scope.subscribers)
},
discard = function() {
$scope.subscribers = angular.copy($scope.orgSubscribers), $scope.changed = !1
},
showPreview = function() {
gaDialogs.emailReportsPreview()
},
getRoleTitle = function(studio, roleid) {
return (studio ? "Studio " : "Game ") + (1 === roleid ? "admin" : "viewer")
};
getReport().then(function() {
getSubscribers().then(function(users) {
$scope.orgSubscribers = angular.copy(users), $scope.subscribers = users
})
}), $scope.$on("$stateChangeStart", function(event, toState, toParams) {
$scope.changed && !ignoreSaveState && (event.preventDefault(), gaUiNotify.loading(!1), gaUiModal.show({
scope: $scope,
width: 500,
header: "Unsaved changes",
content: "Do you want to save the changes you made or discard the changes and leave the page?",
buttons: [{
title: "Save",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "save"
}, {
title: "Discard changes",
"class": "ga-btn-text right",
keyCode: 27,
action: "go"
}],
actions: {
go: function() {
ignoreSaveState = !0, $state.go(toState, toParams)
},
save: function() {
ignoreSaveState = !0, $scope.save({
state: toState,
params: toParams
})
}
}
}))
}), $scope.save = save, $scope.deleteSubscriber = deleteSubscriber, $scope.addSubscriberClick = addSubscriberClick, $scope.showPreview = showPreview, $scope.checkStatus = checkStatus, $scope.discard = discard
}), angular.module("ga.pages.game.settings.emailReports.addSubscriber", ["ga.api.userDb.authenticated.report", "ga.ui.notify"]).controller("gaPagesGameSettingsEmailReportsAddSubscriberController", function($scope, gaApiUserDbAuthenticatedReport, gaUiNotify) {
$scope.email = "", $scope.emailReports = {
daily: !1,
weekly: !0,
monthly: !1
}, $scope.errors = {
email: ""
}, $scope.disabled = !0;
var addSubscriber = function() {
if ($scope.email && ($scope.emailReports.daily || $scope.emailReports.weekly || $scope.emailReports.monthly)) {
var subscriber = {
id: 0,
user_id: 0,
report_id: $scope.reportId,
email: $scope.email,
daily: $scope.emailReports.daily,
weekly: $scope.emailReports.weekly,
monthly: $scope.emailReports.monthly
};
gaApiUserDbAuthenticatedReport.createReportSubscriber($scope.reportId, subscriber).then(function(data) {
$scope._resolve(data)
}).catch(function(result) {
result[0].type && ("serverError" === result[0].type ? gaUiNotify.show(result[0].msg, 6e3, "warning") : $scope.errors.email = result[0].msg)
})
}
},
validate = function() {
$scope.disabled = !$scope.email || !$scope.emailReports.daily && !$scope.emailReports.weekly && !$scope.emailReports.monthly
};
$scope.addSubscriber = addSubscriber, $scope.validate = validate
}), angular.module("ga.pages.game.settings.services", ["ga.services.dialogs", "ga.services.user", "ga.api.userDb.authenticated.game", "ga.api.userDb.authenticated.store_app", "ga.api.userDb.authenticated.appfigures", "ga.api.userDb", "ga.ui.notify", "ga.ui.modal", "ga.services.game"]).controller("gaPagesGameSettingsServicesController", function($scope, $state, $timeout, gaDialogs, gaServicesUser, gaApiUserDbAuthenticatedGame, gaApiUserDbAuthenticatedStoreApp, gaApiUserDbAuthenticatedAppfigures, gaUiNotify, gaUiModal, gaServicesGame, gaApiUserDb) {
var gameId = parseInt($state.params.gameId, 10),
game = gaServicesUser.game(gameId);
$scope.iconPath = "/static/ga-app/images/stores/", $scope.app = {}, $scope.state = "linking", $scope.copying = !1;
var loadStores = function() {
return gaApiUserDbAuthenticatedGame.getStoreApps().then(function(result) {
result && result.length ? (parseStore(result[0]), $scope.state = "loaded") : $scope.state = "no_app"
}).catch(function() {
gaUiNotify.show("Error loading connected app store", 4e3, "warning"), $scope.state = "no_app"
})
};
$scope.addStore = function() {
gaDialogs.linkGame({
game: game
}).then(function(app) {
return $scope.state = "linking", gaApiUserDbAuthenticatedAppfigures.getAppMeta(app.id)
}).then(function(meta) {
return meta && meta.meta && delete meta.meta, gaApiUserDbAuthenticatedGame.createStoreApps({
app_title: meta.name || "",
store_name: "apple" === meta.store ? meta.devices.indexOf("Desktop") > -1 ? "apple_mac" : "apple_ios" : meta.store,
appfigures_id: meta.id,
developer: meta.developer || "",
store_url: meta.view_url || "",
handheld: meta.devices.indexOf("Handheld") > -1,
tablet: meta.devices.indexOf("Tablet") > -1,
desktop: meta.devices.indexOf("Desktop") > -1,
image_url: meta.icon || "",
game_type: "0.00" === meta.price.price ? "free" : "paid",
categories: meta.categories.map(function(cat) {
return {
name: cat.name || "",
appfigures_id: cat.id,
appfigures_parent_id: cat.parent_id,
device: cat.device || ""
}
}),
raw_data: meta
})
}).then(function(result) {
1 === result.length && (parseStore(result[0]), $scope.state = "loaded"), gaServicesGame.saveGameLinkNotification(gameId, !0)
}).catch(function() {
game && !game.link_game_notification && (gaServicesGame.saveGameLinkNotification(gameId, !0), $timeout(gaServicesUser.getUserData.bind(gaServicesUser), 50))
})
}, $scope.updateApp = function(app) {
gaApiUserDbAuthenticatedAppfigures.getAppMeta(app.appfigures_id).then(function(meta) {
return gaApiUserDbAuthenticatedStoreApp.update(app.id, {
app_title: meta.name || "",
developer: meta.developer || "",
store_url: meta.view_url || "",
handheld: meta.devices.indexOf("Handheld") > -1,
tablet: meta.devices.indexOf("Tablet") > -1,
desktop: meta.devices.indexOf("Desktop") > -1,
image_url: meta.icon || "",
game_type: "0.00" === meta.price.price ? "free" : "paid",
categories: meta.categories.map(function(cat) {
return {
name: cat.name,
appfigures_id: cat.id,
appfigures_parent_id: cat.parent_id,
device: cat.device
}
}),
raw_data: meta
})
}).then(function() {
loadStores()
}).catch(function() {
gaUiNotify.show("Error updating!", 4e3, "warning")
})
}, $scope.unlinkApp = function() {
$scope.app && $scope.app.id && gaUiModal.confirm("Are you sure you want to disconnect the game from the app store?", "Disconnect").then(function() {
gaApiUserDbAuthenticatedStoreApp.delete($scope.app.id).then(function(res) {
res[0] && ($scope.app = {}, gaUiNotify.show("App store disconnected", 4e3, "default"), $scope.state = "no_app")
}).catch(function() {
gaUiNotify.show("Something went wrong while disconnecting", 4e3, "warning")
})
})
}, $scope.copyGameIcon = function() {
$scope.copying = !0, gaApiUserDb.copyFile($scope.app.image_url).then(function(result) {
var imgPath = result[0].image_path;
return gaApiUserDbAuthenticatedGame.saveGame(gameId, {
title: game.title,
imageFile: imgPath
})
}).then(function() {
gaUiNotify.show("Game icon replaced", 4e3, "default"), $scope.copying = !1, $timeout(gaServicesUser.getUserData.bind(gaServicesUser), 50)
}).catch(function() {
gaUiNotify.show("Something went wrong while replacing the game icon", 4e3, "warning"), $scope.copying = !1
})
};
var parseStore = function(storeData) {
$scope.app = storeData, $scope.app.storeIcon = getStoreIcon($scope.app.store_name), $scope.app.storeName = getStoreName($scope.app.store_name), $scope.app.catList = $scope.app.categories.map(function(app) {
return app.name
}).join(", "), $scope.app.handheldCat = $scope.app.categories.filter(function(a) {
return "handheld" === a.device
}).map(function(a) {
return a.name
}).join(", "), $scope.app.tabletCat = $scope.app.categories.filter(function(a) {
return "tablet" === a.device
}).map(function(a) {
return a.name
}).join(", "), $scope.app.desktopCat = $scope.app.categories.filter(function(a) {
return "desktop" === a.device
}).map(function(a) {
return a.name
}).join(", ")
},
getStoreIcon = function(name) {
var icon = "";
return "google_play" === name ? icon = "google-play.png" : "apple_mac" === name ? icon = "mac.png" : "apple_ios" === name ? icon = "ios.png" : "amazon_appstore" === name && (icon = "amazon.png"), icon
},
getStoreName = function(name) {
var storeName = "";
return "google_play" === name ? storeName = "Google Play" : "apple_mac" === name ? storeName = "Mac App Store" : "apple_ios" === name ? storeName = "iOS App Store" : "amazon_appstore" === name && (storeName = "Amazon Appstore"), storeName
};
loadStores().then(function() {
$state.params.pop && "no_app" === $scope.state && $scope.addStore()
})
}), angular.module("ga.pages.explore2", ["ga.api.meta", "ga.ui.menu", "ga.utils.query"]).controller("gaPagesExplore2Controller", function($scope, gaApiMeta, gaUtilsQuery) {
$scope.explore = {};
var i, sides = ["left", "right"];
for (i in sides) {
var side = sides[i];
$scope.explore[side] = {
disabled: !1,
metric: {
category: "core",
event: "DAU"
},
meta: gaApiMeta.getMetric({
category: "core",
event: "DAU"
}),
title: gaApiMeta.getMetricDisplay({
category: "core",
event: "DAU"
}),
types: {
selected: "line",
options: [{
title: "ga-icon-linechart",
value: "line",
disabled: !1
}, {
title: "ga-icon-barchart",
value: "bar",
disabled: !1
}, {
title: "ga-icon-area",
value: "area",
disabled: !1
}]
},
aggregations: {
selected: "mean",
options: [{
title: "Mean",
value: "mean",
disabled: !1
}, {
title: "Sum",
value: "sum",
disabled: !1
}, {
title: "Count",
value: "event_count",
disabled: !1
}, {
title: "Histogram",
value: "histogram",
disabled: !1
}]
}
}
}
$scope.explore.range = {
main: "last30Days",
compare: !0
}, $scope.explore.currency = {
selected: "USD",
options: [{
title: "USD",
value: "USD",
disabled: !1
}, {
title: "EUR",
value: "EUR",
disabled: !1
}]
}, $scope.explore.filter = null, $scope.explore.grouping = {
selected: "time",
options: [{
title: "Time",
value: "time",
disabled: !1
}, {
title: "Dimension",
value: "dimension",
disabled: !1
}, {
title: "Value",
value: "value",
disabled: !0
}]
};
var toggleCompare = function($event) {
$scope.explore.range.compare = !$scope.explore.range.compare, $event.stopPropagation(), $event.preventDefault()
};
$scope.toggleCompare = toggleCompare
}), angular.module("ga.pages.dashboards", ["ga.utils.urlState", "ga.ui.pardot", "ga.ui.widgetChart", "ga.ui.widgetPiechart", "ga.ui.widgetTable", "ga.ui.widgetMap", "ga.ui.widgetQuality", "ga.ui.popdown", "ga.ui.gridster", "ga.ui.dimensionpicker", "ga.ui.metricpicker", "ga.ui.modal", "ga.ui.tour", "ga.ui.sortable", "ga.ui.explore.overlay", "ga.filters.slice", "ga.ui.datepicker", "ga.ui.realtimeSdkStatus", "ga.utils.helpers.clickElement", "ga.utils.tracking", "ga.utils.cache", "ui.router", "ga.api.userDb.authenticated.dashboard", "ga.api.userDb.authenticated.status", "ga.api.meta", "ga.api.data", "ga.api.userDb", "ga.pages.dashboard.dialog.widgetEdit", "ga.pages.dashboard.dialog.widgetAdd", "ga.services.tracking", "ga.services.user", "ga.services.game", "ga.ui.notify", "ga.ui.menu", "ga.api.userDb.authenticated.game"]).controller("gaPagesDashboardsController", function($window, $document, $filter, $timeout, $rootScope, $scope, $q, $state, $cookieStore, gaServicesTracking, gaUtilsTracking, gaApiMeta, gaApiData, gaUiModal, gaUtilsUrlState, gaUiTour, gaUiTourNew, gaUtilsDate, gaApiUserDbAuthenticatedDashboards, orderByFilter, gaUiExploreOverlay, gaApiUserDb, gaServicesUser, gaApiUserDbAuthenticatedStatus, gaUiNotify, gaUtilsCache, gaApiUserDbAuthenticatedGame, gaServicesGame) {
var formatUnitType = $filter("formatUnitType");
$scope.widgetStatus = [], $scope.state = gaUtilsUrlState.getState(), $scope.gameId = parseInt($state.params.gameId, 10) || 0, $scope.user = {
activated: gaServicesUser.activated,
id: gaServicesUser.id
}, $scope.game = {
demo: (gaServicesUser.game($scope.gameId) || {}).demo
}, $scope.action = "show", $scope.dashboards = [], $scope.dashboard = null, $scope.widget = null, $scope.showNoDataNotice = !1, $scope.processingNoticeData = {}, $scope.showProcessingNotice = !1;
var setRealtimeDateTimer, setRealtimeDate = function() {
if ($timeout.cancel(setRealtimeDateTimer), $scope.dashboard && "realtime" === $scope.dashboard.id) {
var baseDatetime = gaUtilsDate.moment().utc().startOf("hour");
$scope.realtimeInterval = {
start: formatUnitType(baseDatetime.clone().add("days", -1).valueOf(), "datetime"),
end: formatUnitType(baseDatetime.valueOf(), "datetime"),
cStart: formatUnitType(baseDatetime.clone().add("days", -8).valueOf(), "datetime"),
cEnd: formatUnitType(baseDatetime.clone().add("days", -7).valueOf(), "datetime")
}, setRealtimeDateTimer = $timeout(setRealtimeDate, 1e3)
}
},
stateCheck = function() {
if ("game.dashboards" === $state.current.name) {
var tmpGame = gaServicesUser.game(parseInt($state.params.gameId, 10));
return tmpGame && tmpGame.demo && (gaUtilsTracking.trackPageRaw("/game/dashboards/demo"), gaUtilsTracking.trackEvent({
category: "home",
action: "game",
label: "demo"
})), $timeout($state.go.bind($state, "game.dashboards.dashboard", {
action: "show",
dashboardId: "engagement",
gameId: $state.params.gameId
}, {
location: "replace"
})), !1
}
var ignoreIntroPages = [],
dashboardId = ($state.params.dashboardId || "").match(/^\d+$/) ? parseInt($state.params.dashboardId, 10) : $state.params.dashboardId,
action = $state.params.action || "show";
if ($scope.gameId = $state.params.gameId, $scope.dashboards = gaApiUserDbAuthenticatedDashboards.dashboards[$scope.gameId], "show" === action && !dashboardId && !$scope.dashboards.some(function(dashboard) {
return dashboard.id === dashboardId
})) return void $state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: $scope.dashboards[0].id
}, {
location: "replace"
});
if (dashboardId && dashboardId !== ($scope.dashboard || {}).id) return gaUiNotify.loading(!0), void gaApiUserDbAuthenticatedDashboards.getDashboard(dashboardId).then(function(dashboard) {
gaUiNotify.loading(!1);
var doStateCheck = !0;
if (!dashboard) return $state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: 0
}, {
location: "replace"
}), void($scope.gameId = null);
"realtime" === dashboard.id && $scope.widgetGlobalOptions.filter && $scope.widgetGlobalOptions.filter.values.length && ($scope.widgetGlobalOptions.filter.values = []), dashboard.widgets.sort(function(a, b) {
return a.grid.row === b.grid.row ? a.grid.col - b.grid.col : a.grid.row - b.grid.row
}), $scope.widgetStatus = [];
var lastWidgetGrid = {
row: 0,
sizey: 0
};
return angular.forEach(dashboard.widgets, function(widget) {
widget.grid.row >= lastWidgetGrid.row && (lastWidgetGrid = widget.grid);
var widgetStatusObject = {
id: widget.id,
state: "init",
status: ""
};
widget.statusObject = widgetStatusObject, $scope.widgetStatus.push(widget.statusObject)
}), $scope.containerRow = lastWidgetGrid.row + lastWidgetGrid.sizey, "show" !== action || !angular.isString(dashboard.id) || dashboard.id.toString().match(/([0-9]+-demo)/) || gaServicesUser.onboarding.dashboard[dashboard.id] ? ("show" === action ? (doStateCheck = !1, $scope.dashboard = null, $timeout(function() {
$scope.dashboard = dashboard;
var did = angular.isNumber($scope.dashboard.id) || $scope.dashboard.id.match(/^\d+$/) ? "custom" : $scope.dashboard.id;
gaServicesTracking.submitEvent("design", "pages:game:dashboard:view:" + did, {
value: 1
}), setRealtimeDate(), stateCheck(), "realtime" === $scope.dashboard.id && $rootScope.$broadcast("closeExploreAnnouncement")
})) : ($scope.dashboard = dashboard, setRealtimeDate()), angular.element("body,html").scrollTop(0), "intro" === action ? void(ignoreIntroPages.indexOf($scope.dashboard.id) >= 0 ? $scope.action = "show" : angular.isString($scope.dashboard.id) ? ($scope.action = action, $scope.introUrl = "/static/ga-app/modules/pages/game/dashboards/dialogs/dashboard-intro-page-" + $scope.dashboard.id + ".html", gaServicesTracking.submitEvent("design", "pages:game:dashboard:intro:" + $scope.dashboard.id, {
value: 1,
amount: 1
})) : ($state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: 0
}, {
location: "replace"
}), $scope.gameId = null)) : void(doStateCheck && stateCheck())) : ($scope.dashboard = dashboard, $state.go("game.dashboards.dashboard", {
action: "intro",
gameId: $scope.gameId,
dashboardId: $scope.dashboard.id
}, {
location: "replace"
}), void setRealtimeDate())
});
if ("intro" === action) {
if (ignoreIntroPages.indexOf($scope.dashboard.id) >= 0) return;
$scope.introUrl = "/static/ga-app/modules/pages/game/dashboards/dialogs/dashboard-intro-page-" + $scope.dashboard.id + ".html"
}
return "show" !== action || !angular.isString($scope.dashboard.id) || $scope.dashboard.id.toString().match(/([0-9]+-demo)/) || gaServicesUser.onboarding.dashboard[$scope.dashboard.id] ? (action !== $scope.action && "edit" === action ? dashboardId ? ($scope.action = action, $scope.dashboardState = angular.copy($scope.dashboard), $scope.execEditWidget ? editWidget() : $scope.execAddWidgets && addWidgets(), $scope.execEditWidget = !1, $scope.execAddWidgets = !1) : ($scope.previousDashboardId = $scope.dashboard && $scope.dashboard.id, $scope.dashboard = null, createDashboard(), $scope.action = action) : ($scope.action = action, gaUiTour.isRunning() || (!gaServicesUser.onboarding.dashboard.tour && "intro" !== action && $scope.dashboard && "realtime" !== $scope.dashboard.id ? (runTour(), gaServicesUser.onboarding.set("dashboard", "tour", !0), gaServicesUser.onboarding.save("dashboard", "tour", !0)) : !gaServicesUser.onboarding.dashboard.realtimeTour && "intro" !== action && $scope.dashboard && "realtime" === $scope.dashboard.id ? (runRealtimeTour(), gaServicesUser.onboarding.set("dashboard", "realtimeTour", !0), gaServicesUser.onboarding.save("dashboard", "realtimeTour", !0)) : stopTour())), "show" !== $scope.action && stopTour(!0), "show" === $scope.action ? (checkForDataProcessing(), checkForGameLink()) : $scope.showProcessingNotice = !1, void("show" === $scope.action && "acquisition" === $scope.dashboard.id ? checkForZeroState() : $scope.showNoDataNotice = !1)) : void $state.go("game.dashboards.dashboard", {
action: "intro",
gameId: $scope.gameId,
dashboardId: $scope.dashboard.id
}, {
location: "replace"
})
};
$scope.$on("$stateChangeSuccess", stateCheck);
var createDashboard = function() {
var dashboard = {
id: 0,
name: "Custom dashboard",
widgets: [],
init: !1
};
dashboardSettingsDialog(dashboard).then(function(dashboard) {
$scope.dashboard = dashboard
}, function() {
$state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: $scope.previousDashboardId || 0
}, {
location: "replace"
}), $scope.previousDashboardId = 0
})
},
dashboardSettings = function() {
dashboardSettingsDialog($scope.dashboard).then(function(dashboard) {
dashboard && ($scope.dashboard.name = dashboard.name)
})
},
dashboardSettingsDialog = function(dashboard) {
var deferred = $q.defer(),
$newScope = $rootScope.$new();
$newScope.dashboard = angular.copy(dashboard);
var dashboardSettingsTemplate = '<div class=ga-form dashboard-settings"><label for="name">Dashboard name</label><input class="ga-input widget-name-input" name="name" ng-model="dashboard.name" /></div>',
buttons = [{
title: "Confirm",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "confirm",
disabled: "!dashboard.name"
}];
return dashboard.id && !dashboard.unsaved && buttons.push({
title: "Delete dashboard",
"class": "ga-btn-text light red ga-icon-trash vertical-none left",
action: "delete"
}), buttons.push({
title: "Cancel",
"class": "ga-btn-text right",
action: "cancel",
keyCode: 27
}), gaUiModal.show({
scope: $newScope,
header: dashboard.id ? "Dashboard Settings" : "Create Dashboard",
content: dashboardSettingsTemplate,
width: 500,
actions: {
"delete": function() {
confirmDashboardDeletion($newScope.dashboard).then(function() {
deferred.resolve(null)
}, function() {
deferred.reject()
})
},
cancel: function() {
deferred.reject($newScope.dashboard)
},
confirm: function() {
deferred.resolve($newScope.dashboard)
}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
}), deferred.promise
},
confirmDashboardDeletion = function(dashboard) {
var deferred = $q.defer(),
doDelete = function(dashboard) {
gaApiUserDbAuthenticatedDashboards.deleteDashboard(dashboard.id, $scope.gameId).then(function() {
$state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: 0
}, {
location: "replace"
}), $scope.dashboard = null, $scope.gameId = null, gaUtilsTracking.trackEvent({
category: "dashboards",
action: "custom",
label: "delete"
}), gaServicesTracking.submitEvent("design", "pages:game:dashboard:delete:custom", {
value: 1
}), deferred.resolve()
})
};
return dashboard.id && gaUiModal.show({
scope: $scope,
width: 500,
header: "Delete dashboard",
content: 'Are you sure you want to delete the dashboard <strong ng-bind="dashboard.name"></strong> and <strong>all</strong> widgets contained within?',
buttons: [{
title: "Delete",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "doDelete"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
doDelete: function() {
doDelete($scope.dashboard)
}
}
}, "confirmDashboardDelete"), deferred.promise
},
cancelDashboardChanges = function() {
var doCancel = function() {
$scope.dashboard.id ? gaApiUserDbAuthenticatedDashboards.lock($scope.dashboard.id, !0).then(function() {
$state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: $scope.dashboard.id
}, {
location: "replace"
}), $scope.dashboard = null
}) : ($state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: $scope.previousDashboardId || 0
}, {
location: "replace"
}), $scope.previousDashboardId = null, $scope.dashboard = null)
};
$scope.dashboard.id && "0" !== $scope.dashboard.id && angular.equals($scope.dashboardState, $scope.dashboard) ? doCancel() : gaUiModal.show({
scope: $scope,
width: 500,
header: $scope.dashboard.id ? "Cancel dashboard changes" : "Cancel dashboard creation",
content: $scope.dashboard.id ? 'Are you sure you want to cancel dashboard <strong ng-bind="dashboard.name"></strong> changes?' : 'Are you sure you want to cancel dashboard <strong ng-bind="dashboard.name"></strong> creation?',
buttons: [{
title: "Confirm",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "confirm"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
confirm: doCancel
}
})
};
$scope.isSavingDashboard = !1;
var saveDashboardChanges = function() {
return $scope.dashboard ? void($scope.isSavingDashboard || ($scope.isSavingDashboard = !0, gaApiUserDbAuthenticatedDashboards.saveDashboard($scope.dashboard, $scope.gameId).then(function(dashboard) {
$state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: dashboard.id
}, {
location: "replace"
}), 0 === $scope.dashboard.id ? (gaUtilsTracking.trackEvent({
category: "dashboards",
action: "custom",
label: "create"
}), gaServicesTracking.submitEvent("design", "pages:game:dashboard:create:custom", {
value: 1
})) : (gaUtilsTracking.trackEvent({
category: "dashboards",
action: "custom",
label: "edit"
}), gaServicesTracking.submitEvent("design", "pages:game:dashboard:save:custom", {
value: 1
})), $scope.dashboard = null, $scope.gameId = null, $scope.isSavingDashboard = !1
}).catch(function() {
$scope.isSavingDashboard = !1
}))) : void $state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: 0
}, {
location: "replace"
})
},
widgetMenu = function() {
gaUiModal.show({
scope: $scope,
newScope: !1,
header: "Add Widget",
content: '<div class="ga-dashboard-add"><ul class="ga-btn-group square huge box-shadow"><li class="ga-btn square huge ga-icon-dashboards" ng-click="addWidgets(); x__modal.actions.x_hide();">Add existing widgets</li><li class="ga-btn square huge ga-icon-add" ng-click="editWidget(); x__modal.actions.x_hide();">Create new widget</li></ul></div>',
buttons: [{
title: "Cancel",
"class": "ga-btn-alt",
keyCode: 27
}]
})
},
orderDashboards = function() {
var $newScope = $rootScope.$new();
$newScope.dashboards = orderByFilter($scope.dashboards, "sortOrder");
var updateSortOrder = function() {
var sortOrderList = [];
angular.forEach($newScope.dashboards, function(dashboard, index) {
dashboard.sortOrder = index, sortOrderList.push(dashboard.id)
}), gaApiUserDbAuthenticatedDashboards.saveDashboardsOrder(sortOrderList, $scope.gameId).then(function() {}), $scope.dashboards = $newScope.dashboards, gaUtilsTracking.trackPageRaw("/game/dashboards/sortdashboard")
};
gaUiModal.show({
scope: $newScope,
header: "Re-order dashboards",
content: '<div class="sort-dashboards-container"><ul ga-ui-sortable sortable-options="{}" ng-model=\'dashboards\' class="ga-default-list border boxes"><li ng-repeat="dashboardItem in dashboards" class="ga-icon-dashboard box draggable" ng-class="{\'sort-dashboards-lower\': $index>4}"> {{dashboardItem.name}}</li></ul></div>',
buttons: [{
title: "Confirm",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "confirm"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
confirm: updateSortOrder
},
always: function() {
$newScope.$destroy()
}
})
},
createWidget = function() {
return {
id: 0,
name: "",
grid: {
col: 1,
row: 1,
sizex: 2,
sizey: 1
},
type: "line",
query: {
metric: null,
aggregation: "mean",
group: "time",
filter: null
}
}
},
editWidget = function(widget, $index) {
if ("edit" !== $scope.action) return $scope.execEditWidget = !0, void $state.go("game.dashboards.dashboard", {
action: "edit",
gameId: $scope.gameId,
dashboardId: $scope.dashboard.id
}, {
location: "replace"
});
if (!widget) {
var canCreateMore = !0;
if (angular.forEach($scope.dashboard.widgets, function(widget) {
widget.grid.row >= 24 ? canCreateMore = !1 : 2 === widget.grid.sizey && widget.grid.row >= 23 && (canCreateMore = !1)
}), !canCreateMore) return !1
}
editWidgetDialog(widget, $scope.dashboard).then(function(widget) {
void 0 !== $index ? (widget.update = !0, $scope.dashboard.widgets[$index] = widget) : $scope.dashboard.widgets.push(widget), $scope.$broadcast("reloadGridster")
}), widget && gaUtilsTracking.trackEvent({
category: "dashboards",
action: "widget",
label: "edit"
})
},
editWidgetDialog = function(widget, dashboard) {
var deferred = $q.defer(),
$newScope = $rootScope.$new();
$newScope.widget = angular.copy(widget || createWidget()), $newScope.dashboard = angular.copy(dashboard);
var buttons = [{
title: widget ? "Save changes" : "Add widget to dashboard",
"class": "ga-btn-alt orange right",
action: "confirm",
disabled: "isInvalid"
}];
return buttons.push(dashboard && dashboard.init ? {
title: "Skip",
"class": "ga-btn-alt right",
action: "cancel"
} : {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27,
action: "cancel"
}), gaUiModal.show({
scope: $newScope,
newScope: !1,
destroyScope: !0,
header: widget ? "Edit Widget" : "Create New Widget",
width: 850,
content: "<div ga-widget-edit></div>",
actions: {
confirm: function() {
deferred.resolve($newScope.widget), widget || gaUtilsTracking.trackEvent({
category: "dashboards",
action: "widget",
label: "create"
})
},
cancel: function() {
deferred.reject()
}
},
buttons: buttons,
always: function() {
$newScope.$destroy()
}
}), deferred.promise
},
addWidgets = function() {
return "edit" !== $scope.action ? ($scope.execAddWidgets = !0, void $state.go("game.dashboards.dashboard", {
action: "edit",
gameId: $scope.gameId,
dashboardId: $scope.dashboard.id
}, {
location: "replace"
})) : void addWidgetsDialog($scope.dashboard).then(function(widgets) {
$scope.dashboard.widgets = $scope.dashboard.widgets.concat(widgets), $scope.$broadcast("reloadGridster")
})
},
addWidgetsDialog = function(dashboard) {
var deferred = $q.defer(),
$newScope = $rootScope.$new();
$newScope.dashboards = angular.copy($scope.dashboards), $newScope.dashboard = angular.copy(dashboard);
var buttons = [{
title: "Add widgets to dashboard",
"class": "ga-btn-alt orange right",
action: "confirm",
disabled: "selected.widgets.length?false:true"
}];
return buttons.push(dashboard && dashboard.init ? {
title: "Skip",
"class": "ga-btn-alt right",
action: "cancel"
} : {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27,
action: "cancel"
}), gaUiModal.show({
scope: $newScope,
newScope: !1,
header: "Add Existing Widgets to Dashboard",
width: 750,
absolute: !0,
content: "<div ga-widget-add></div>",
actions: {
confirm: function() {
$newScope.selected.widgets = $newScope.selected.widgets.reverse().map(function(widget) {
return widget.id = 0, widget.grid.row = 1, widget.grid.col = 1, "spark" === widget.type ? (widget.grid.sizex = 1, widget.grid.sizey = 1) : "quality" === widget.type ? (widget.grid.sizex = 4, widget.grid.sizey = 2) : (widget.grid.sizex = 2, widget.grid.sizey = 1), widget
}), deferred.resolve($newScope.selected.widgets), gaUtilsTracking.trackEvent({
category: "dashboards",
action: "widget",
label: "copy",
value: $newScope.selected.widgets.length
})
},
cancel: function() {
deferred.reject()
}
},
buttons: buttons,
always: function() {
$newScope.$destroy()
}
}), deferred.promise
},
resizeWidget = function(widget, $index, size) {
if (-1 === size && 1 === widget.grid.sizex) return !1;
if (1 === size && 4 === widget.grid.sizex) return !1;
var newSize = [2, 1];
1 === widget.grid.sizex && 1 === size ? newSize = [2, 1] : 2 === widget.grid.sizex && 1 === size ? newSize = [4, 2] : 2 === widget.grid.sizex && -1 === size ? newSize = [1, 1] : 4 === widget.grid.sizex && -1 === size && (newSize = [2, 1]), $scope.$broadcast("resizeWidgetGridster", $index, newSize)
},
removeWidget = function(widget, $index) {
gaUiModal.show({
scope: $scope,
width: 500,
header: "Delete widget",
content: "Are you sure you want to delete <strong>" + widget.name + "</strong> widget?",
buttons: [{
title: "Delete",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "confirm"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
confirm: function() {
$scope.$broadcast("removeWidgetGridster", $index, function($index) {
widget.id && ($scope.dashboard.deletedWidgets = $scope.dashboard.deletedWidgets || [], $scope.dashboard.deletedWidgets.push(widget.id)), $scope.dashboard.widgets.splice($index, 1), $scope.$broadcast("reloadGridster")
}), gaUtilsTracking.trackEvent({
category: "dashboards",
action: "widget",
label: "delete"
})
}
}
})
},
showExplore = function(widget, $event) {
var clickElement = angular.element($event.target),
overlayElement = $document.find("body"),
datepicker = angular.copy($scope.datepickerOptions),
globalFilter = $scope.widgetGlobalOptions.filter,
globalCurrency = $scope.widgetGlobalOptions.currency,
widgetTitle = widget.name ? widget.name : null,
widgetGroupBy = widget.query.group ? widget.query.group : null,
widgetVisualization = widget.type,
tmpMetric = angular.copy(angular.isArray(widget.query.metric) ? widget.query.metric[0] : widget.query.metric);
"retention_1" === tmpMetric.event && (tmpMetric.event = "retention");
var widgetMetric = {
aggregation: widget.query.aggregation,
category: tmpMetric.category,
event: tmpMetric.event
},
filter = null;
filter = globalFilter ? globalFilter : widget.query.hasOwnProperty("filter") && widget.query.filter ? widget.query.filter : null;
var currency = null;
currency = globalCurrency ? globalCurrency : widget.query.metric.hasOwnProperty("currency") && widget.query.metric.currency ? widget.query.metric.currency : "USD", widgetMetric.currency = currency;
var exploreSettingsInit = {
datepicker: datepicker,
filter: filter,
groupBy: widgetGroupBy,
currency: widgetMetric.currency,
title: widgetTitle,
visualization: widgetVisualization,
metric: widgetMetric,
rollup: $scope.widgetGlobalOptions.rollup || null
},
widgetCompareToggleOff = clickElement.closest(".widget-inner").data("compareToogleOff");
"boolean" == typeof widgetCompareToggleOff && (exploreSettingsInit.datepicker.compare = !widgetCompareToggleOff), gaUiExploreOverlay.show(overlayElement, exploreSettingsInit);
var id = $scope.dashboard.id,
intId = parseInt(id, 10);
id = intId !== intId ? id : "custom", gaServicesTracking.addEvent("design", "popup:explore:dashboards-" + id, {
value: 1
}), gaUtilsTracking.trackEvent({
category: "dashboards",
action: "explore",
label: widget.name
})
},
activeWidgetIndex = function() {
var activeIndex = -1;
return $scope.widget ? ($scope.dashboard.widgets.some(function(widget, index) {
return widget.id === $scope.widget.id ? (activeIndex = index, !0) : !1
}), activeIndex) : activeIndex
},
getNextPrevWidget = function(direction) {
var index = activeWidgetIndex();
return void 0 !== $scope.dashboard.widgets[index + direction] ? $scope.dashboard.widgets[index + direction] : void 0
},
gotoDashboard = function(dashboardId, action) {
action = action || "show", $state.go("game.dashboards.dashboard", {
action: action,
gameId: $scope.gameId,
dashboardId: dashboardId
})
},
selectAction = function(action) {
$state.go("game.dashboards.dashboard", {
action: action
}, {
location: "replace"
})
},
dimensionTitle = function(value) {
return gaApiMeta.getDimension(value).title
};
$scope.$on("$destroy", function() {
$timeout.cancel(setRealtimeDateTimer)
}), $scope.dimensionTitle = dimensionTitle, $scope.gotoDashboard = gotoDashboard, $scope.selectAction = selectAction, $scope.dashboardSettings = dashboardSettings, $scope.saveDashboardChanges = saveDashboardChanges, $scope.cancelDashboardChanges = cancelDashboardChanges, $scope.orderDashboards = orderDashboards, $scope.widgetMenu = widgetMenu, $scope.editWidget = editWidget, $scope.addWidgets = addWidgets, $scope.resizeWidget = resizeWidget, $scope.removeWidget = removeWidget, $scope.showExplore = showExplore, $scope.getNextPrevWidget = getNextPrevWidget, $scope.activeWidgetIndex = activeWidgetIndex;
var showMeTour = function() {
$scope.dashboard && ("realtime" === $scope.dashboard.id ? runRealtimeTour() : runTour())
};
$scope.showMeTour = showMeTour;
var runRealtimeTour = function() {
gaUiTourNew.stop(), gaUiTourNew.addStep({
title: "Introducing the Real-time Dashboard",
text: "The Real-time Dashboard is a vital addition to our Dashboards collection. Use it to monitor activities when running new acquisition or marketing campaigns. Let's take you through the features.",
nextText: "Take the tour"
}), gaUiTourNew.addStep({
title: "Status keeps you updated",
text: "With Status you get an immediate response on your integrated SDK's along with the activity in your game only minutes delayed. Status can also let you know if any events are rejected from our collectors.",
element: ".integration-static-widget .title",
pos: ["bottom"],
zIndex: 900
}), gaUiTourNew.addStep({
title: "Timely updates",
text: "All metrics, except for the Status bar, are updated on an hourly basis, to get fast feedback when running new activities.",
element: ".ga-ui-gridster > li:first .ga-ui-progress",
pos: ["bottom", "left"],
zIndex: 900
}), gaUiTourNew.addStep({
title: "No margin for error",
text: "We made it easy for you to filter your Error report by different levels of severity. Making troubleshooting quick and easy.",
element: ".ga-ui-gridster > li:last .title",
pos: ["bottom"],
zIndex: 900
}), gaUiTourNew.addStep({
title: "Enhanced personalization",
text: "Set your timezone in User settings -> Locale settings, and choose your preferred date, time and number formats.",
element: ".ga.main-menu .popdown.ga-icon-user",
pos: ["right", "top"],
fixed: !0
}), gaUiTourNew.start({
navigation: !0
})
},
runTour = function() {
gaUiTourNew.stop(), gaUiTourNew.addStep({
title: "Date picker",
text: "You can choose a custom period or select one from our presets. Additionally, you can turn the time comparison period on or off.",
element: ".ga.bar.page.sub .left .datepicker",
pos: ["bottom", "right"],
fixed: !0,
zIndex: 900
}), gaUiTourNew.addStep({
title: "Filter your data",
text: "You can create a filter on your data by choosing certain parameters like country, build and more.",
element: ".ga.bar.page.sub .left .dimensionpicker",
pos: ["bottom", "right"],
fixed: !0,
zIndex: 900
}), gaUiTourNew.addStep({
title: "Get all the details",
text: "This is an extended graph and table view of the widget data. You can choose to group that data differently and export it to CSV.",
element: ".ga-ui-gridster>li:first-child .title.more",
pos: ["bottom"],
fixed: !1,
zIndex: 900
}), gaUiTourNew.addStep({
title: "Custom dashboards",
text: "Create your own dashboards with custom widgets or choose to use existing widgets. Create a custom dashboard.",
element: ".ga.bar.page .right .createdashboard",
pos: ["bottom", "left"],
fixed: !0
}), gaUiTourNew.addStep({
title: "Enhanced personalization",
text: "To set your date, time and number formats go to User settings -> Locale settings",
element: ".ga.main-menu .popdown.ga-icon-user",
pos: ["right", "top"],
fixed: !0
}), gaUiTourNew.start({
navigation: !0
})
},
stopTour = function() {
$rootScope.$broadcast("killTour")
};
$scope.dismissIntro = function() {
gaServicesUser.onboarding.set("dashboard", $scope.dashboard.id, !0), gaServicesUser.onboarding.save(), $state.go("game.dashboards.dashboard", {
action: "show",
gameId: $scope.gameId,
dashboardId: $scope.dashboard.id
}, {
location: "replace"
})
};
var checkForZeroState = function() {
angular.isString($scope.dashboard.id) && !gaServicesUser.onboarding.dashboard.acquisition_notice && "acquisition" === $scope.dashboard.id && ($scope.showNoDataNotice = !1, gaApiData.getValue("/dimensions", $state.params.gameId).then(function(data) {
!angular.isObject(data) || data.install_publisher || data.install_campaign || ($scope.showNoDataNotice = !0)
}))
},
checkForDataProcessing = function() {
var game_id = $state.params.gameId;
if ($rootScope.gaProcessed && $rootScope.gaProcessed[game_id]) return void($scope.showProcessingNotice = !1);
var todayDate = gaUtilsDate.moment().utc().format("YYYY-MM-DD");
gaApiUserDbAuthenticatedStatus.getProcessing().then(function(result) {
try {
result && result.results && result.results.length && todayDate !== result.results[0].processing && ($scope.processingNoticeData[game_id] = {
show: !0,
date: gaUtilsDate.moment(result.results[0].processing).utc().format("MMM. D. YYYY")
}, $scope.showProcessingNotice = !0)
} catch (e) {}
})
},
gamelinkShown = !1,
checkForGameLink = function() {
var game = gaServicesUser.game(parseInt($state.params.gameId, 10));
game && !game.link_game_notification && game.admin && !gamelinkShown && (gamelinkShown = !0, gaServicesGame.getNumbers(game.id).then(function(numbers) {
return numbers && numbers[0] && numbers[0].value > 10 ? !0 : !1
}).then(function(goOn) {
return goOn ? gaApiUserDbAuthenticatedGame.getStoreApps() : !1
}).then(function(result) {
result && 0 === result.length && gaUiModal.show({
scope: $scope,
width: 650,
header: "Introducing App Store Connect",
content: '<div class="app_store_connect_modal"><h2>Connect your game</h2>You can now connect your game to the Apple, Google or Amazon app stores.<br><strong>Link your game now and you are ready</strong> for new features to come such as benchmarking and segmenting.<div class="ga color grey">*Please note that you can only connect to one app store per game.</div></div>',
buttons: [{
title: "Search for my game",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "gotoSettings"
}, {
title: "Cancel",
"class": "ga-btn-text right",
action: "dismiss"
}],
actions: {
gotoSettings: function() {
$state.go("game.settings.services", {
pop: !0
})
},
dismiss: function() {
gaServicesGame.saveGameLinkNotification(game.id, !0), $timeout(gaServicesUser.getUserData.bind(gaServicesUser), 50)
}
}
})
}))
};
$scope.hideNoticeBar = function(name) {
$scope.showNoDataNotice = !1, gaServicesUser.onboarding.set("dashboard", name + "_notice", !0), gaServicesUser.onboarding.save("dashboard", name + "_notice", !0)
}, $scope.hideProcessedNoticeBar = function() {
$scope.showProcessingNotice = !1;
var game_id = $state.params.gameId;
$scope.processingNoticeData[game_id].show = !1, $rootScope.gaProcessed = $rootScope.gaProcessed || {}, $rootScope.gaProcessed[game_id] = !0
}, $scope.changeWidgetTotalValue = function(widget, totalValue, event) {
event.stopPropagation()
}, $scope.datepickerOptions = {
interval: {},
selection: "last30Days",
compare: !0,
title: "Date range"
}, $scope.state.d ? angular.isObject($scope.state.d) ? ($scope.datepickerOptions.selection = "custom", $scope.datepickerOptions.interval = angular.copy($scope.state.d), $scope.datepickerOptions.compare = !!$scope.datepickerOptions.interval.compare) : ($scope.datepickerOptions.selection = $scope.state.d, $scope.datepickerOptions.compare = 0 === $scope.state.dc ? !1 : !0) : $scope.datepickerOptions.compare = 0 === $scope.state.dc ? !1 : !0;
var datepickerCompareStatus = gaUtilsCache.get("dashboards-settings-compare", "localStorage");
"boolean" == typeof datepickerCompareStatus && ($scope.datepickerOptions.compare = datepickerCompareStatus), "custom" !== $scope.datepickerOptions.selection && ($scope.datepickerOptions.interval.main = gaUtilsDate.getPeriodRange($scope.datepickerOptions.selection), $scope.datepickerOptions.interval.compare = $scope.datepickerOptions.compare ? gaUtilsDate.getPreviousPeriod($scope.datepickerOptions.interval.main) : null), $scope.$watch("datepickerOptions.interval", function(newVal, oldVal) {
if (newVal !== oldVal) {
$scope.datepickerOptions.compare ? delete $scope.state.dc : $scope.state.dc = 0, "last30Days" === $scope.datepickerOptions.selection ? delete $scope.state.d : "custom" === $scope.datepickerOptions.selection ? ($scope.state.d = $scope.datepickerOptions.interval, delete $scope.state.dc) : $scope.state.d = $scope.datepickerOptions.selection, $scope.widgetGlobalOptions.range = angular.copy($scope.datepickerOptions.interval);
var compareState = $scope.widgetGlobalOptions.range.compare ? !0 : !1;
gaUtilsCache.put("dashboards-settings-compare", compareState, "localStorage")
}
}), $scope.widgetGlobalOptions = {
range: angular.copy($scope.datepickerOptions.interval),
filter: $scope.state.f || null,
rollup: $scope.state.r || "daily"
}, $scope.filterMeta = $scope.widgetGlobalOptions.filter && $scope.widgetGlobalOptions.filter.values.length ? gaApiMeta.getDimension($scope.widgetGlobalOptions.filter.dimension) : null;
var removeFilter = function(index) {
$scope.widgetGlobalOptions.filter.values.splice(index, 1), $scope.widgetGlobalOptions.filter = $scope.widgetGlobalOptions.filter.values.length ? {
dimension: $scope.widgetGlobalOptions.filter.dimension,
values: $scope.widgetGlobalOptions.filter.values
} : null
};
$scope.removeFilter = removeFilter, $scope.$watch("widgetGlobalOptions.filter", function(newVal, oldVal) {
null !== $scope.widgetGlobalOptions.filter && newVal !== oldVal && (newVal && newVal.values.length && newVal.dimension ? ($scope.filterMeta = gaApiMeta.getDimension(newVal.dimension), $scope.state.f = newVal) : ($scope.filterMeta = null, $scope.widgetGlobalOptions.filter = null, delete $scope.state.f))
}, !0), $scope.$watch("widgetGlobalOptions.rollup", function(newVal, oldVal) {
null !== $scope.widgetGlobalOptions.rollup && newVal !== oldVal && (newVal ? $scope.state.r = newVal : ($scope.widgetGlobalOptions.rollup = null, delete $scope.state.r))
}, !0), $scope.$watch("state", function(newState, oldState) {
gaUtilsUrlState.setState($scope.state), newState !== oldState && null === newState.currency && delete newState.currency;
var newStateCheck = angular.copy(newState),
ignoreDateChange = !1;
if (newStateCheck.daterange || (newStateCheck.daterange = "last30Days", oldState.daterange || (ignoreDateChange = !0)), newStateCheck.filter && newStateCheck.filter.dimension && newStateCheck.filter.values.length && (!oldState.filter || newStateCheck.filter.dimension !== oldState.filter.dimension) && gaUtilsTracking.trackEvent({
category: "dashboards",
action: "filter",
label: newStateCheck.filter.dimension
}), newStateCheck.daterangeCompare !== oldState.daterangeCompare) {
var compareLabel = "";
compareLabel = void 0 === newStateCheck.daterangeCompare || newStateCheck.daterangeCompare ? angular.isString(newStateCheck.daterange) ? newStateCheck.daterange : newStateCheck.daterange.compare ? gaUtilsDate.getIntervalTitle(newStateCheck.daterange.compare) : "soff" : "off", gaUtilsTracking.trackEvent({
category: "dashboards",
action: "daterangecompare",
label: compareLabel
})
}
if (!ignoreDateChange && newStateCheck.daterange && !angular.equals(newStateCheck.daterange, oldState.daterange)) {
var label = "";
angular.isString(newStateCheck.daterange) ? label = newStateCheck.daterange : oldState.daterange && !angular.equals(newStateCheck.daterange.main, oldState.daterange.main) && (label = gaUtilsDate.getIntervalTitle(newStateCheck.daterange.main)), label && gaUtilsTracking.trackEvent({
category: "dashboards",
action: "daterange",
label: label
})
}
}, !0), $scope.popdown = ""
}), angular.module("ga.pages.dashboard.dialog.widgetEdit", ["ga.api.meta", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.ui.tooltip", "ga.ui.modal", "ga.utils.helpers.focusElement", "ga.utils.date"]).controller("gaWidgetEditController", function($scope, $rootScope, $timeout, $filter, gaUiModal, gaApiMeta, gaUtilsDate) {
var formatUnitType = $filter("formatUnitType");
$scope.currencies = {
available: gaApiMeta.getCurrencies(),
selectActive: !1,
query: "",
lastSelected: null
}, $scope.aggregationTooltip = {}, $scope.widget && $scope.widget.query && $scope.widget.query.metric && $scope.widget.query.metric.currency && ($scope.currencies.lastSelected = $scope.widget.query.metric.currency);
var metricpicker = function() {
if ($scope.metric.disabled) return !1;
var $tmpScope = $rootScope.$new(!0);
$tmpScope.tmpMetric = angular.copy($scope.widget.query.metric);
var tmpListener = $tmpScope.$watch("tmpMetric", function(newVal, oldVal) {
newVal !== oldVal && (newVal && ("business" === newVal.category || "design" === newVal.category) && newVal.event && newVal.event.indexOf(":.*") > -1 && ($scope.widget.query.group = "value"), $scope.widget.query.metric = angular.copy($tmpScope.tmpMetric), $timeout(function() {
gaUiModal.hide("subModal")
}))
});
gaUiModal.show({
header: "Select a metric",
scope: $tmpScope,
newScope: !1,
width: 720,
customClass: "secondary",
content: '<ga-ui-metricpicker metric="tmpMetric"></ga-ui-metricpicker>',
buttons: [{
title: "Cancel",
"class": "ga-btn-alt",
keyCode: 27
}],
always: function() {
tmpListener(), $tmpScope.$destroy()
}
}, "subModal")
},
dimensionpicker = function() {
if ($scope.dimension.disabled) return !1;
var $tmpScope = $rootScope.$new(!0);
$tmpScope.tmpFilter = angular.copy($scope.widget.query.filter), $tmpScope.tmpSelected = null, $tmpScope.metric = angular.copy($scope.widget.query.metric), gaUiModal.show({
header: "Select a filter",
scope: $tmpScope,
newScope: !1,
width: 720,
customClass: "secondary",
content: '<ga-ui-dimensionpicker filter="tmpFilter" metric="metric" selected="tmpSelected"></ga-ui-dimensionpicker>',
buttons: [{
title: "Apply",
"class": "ga-btn-alt orange right",
action: "apply"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
apply: function() {
$tmpScope.tmpSelected && $tmpScope.tmpSelected.values && $tmpScope.tmpSelected.values.length ? ($tmpScope.tmpFilter || ($scope.widget.query.group = "dimension"), $scope.widget.query.filter = angular.copy($tmpScope.tmpSelected), dimensionsProcess()) : ($scope.widget.query.filter = null, dimensionsProcess())
}
},
always: function() {
$tmpScope.$destroy()
}
}, "subModal")
},
dimensionsProcess = function() {
$scope.widget.query.filter ? ($scope.filterMeta = gaApiMeta.getDimension($scope.widget.query.filter.dimension), $scope.filterValues = [], "*" === $scope.widget.query.filter.values[0] ? $scope.filterValues.push(($scope.filterMeta.parent ? $scope.filterMeta.parent.title + " > " + $scope.filterMeta.title : $scope.filterMeta.title) + " > All") : angular.forEach($scope.widget.query.filter.values, function(value) {
$scope.filterValues.push(formatUnitType(value, $scope.filterMeta.type))
})) : ($scope.filterValues = [], $scope.filterMeta = null)
},
validateWidget = function() {
return !widgetCheckRequirements()
};
$scope.isValid = !0, $scope.isInvalid = !0, $scope.name = {
required: !1,
disabled: !1,
readonly: !1
}, $scope.metric = {
required: !1,
disabled: !1,
readonly: !1
}, $scope.dimension = {
required: !1,
disabled: !1,
readonly: !1
}, $scope.group = {
time: !1,
dimension: !1,
value: !1
}, $scope.aggregation = {
mean: !1,
sum: !1,
event_count: !1,
histogram: !1
}, $scope.currency = {
disabled: !0,
eventCountDisabled: !0
}, $scope.type = {
spark: !1
}, $scope.$watch("widget.name", function(newVal, oldVal) {
newVal !== oldVal && widgetCheckRequirements()
}), $scope.$watch("widget.type", function(newVal, oldVal) {
newVal !== oldVal && widgetCheckType(newVal, oldVal)
}), $scope.$watch("widget.query.metric.currency", function(newVal, oldVal) {
newVal !== oldVal && ($scope.currencies.lastSelected = newVal)
}), $scope.$watch("widget.query.metric", function(newVal, oldVal) {
$scope.metricDisplay = newVal ? gaApiMeta.getMetricDisplay(newVal.category, newVal.event) : null, newVal !== oldVal && widgetCheckMetric(newVal, oldVal)
}), $scope.$watch("widget.query.filter", function(newVal, oldVal) {
newVal !== oldVal && widgetCheckFilter(newVal, oldVal), dimensionsProcess()
}), $scope.$watch("widget.query.group", function(newVal, oldVal) {
newVal !== oldVal && widgetCheckRequirements()
}), $scope.showHidePreview = function(show) {
$scope.isInvalid || show === !1 || ["pie", "table", "map", "quality"].indexOf($scope.widget.type) > -1 ? $scope.widgetPreview = null : ($scope.widgetPreviewOptions = {
range: {
main: gaUtilsDate.getPeriodRange("lastWeek"),
compare: !0
}
}, $scope.widgetPreview = angular.copy($scope.widget))
};
var widgetCheckRequirementsTimer, widgetCheckRequirements = function() {
$timeout.cancel(widgetCheckRequirementsTimer), widgetCheckRequirementsTimer = $timeout(_widgetCheckRequirements, 250)
},
_widgetCheckRequirements = function() {
var isValid = !0;
return $scope.dimension.readonly = !1, $scope.metric.disabled = !1, $scope.currency.eventCountDisabled = !0, $scope.widget.name ? "quality" === $scope.widget.type ? ($scope.name.required = !1, isValid = !0) : $scope.widget.query.metric ? ($scope.name.required = !1, $scope.metric.required = !1, "pie" !== $scope.widget.type || "value" === $scope.widget.query.group || $scope.widget.query.filter ? $scope.dimension.required = !1 : ($scope.dimension.required = "You need a dimension for this visualization", isValid = !1)) : ($scope.name.required = !1, $scope.metric.required = "Select a core metric or one or more events", $scope.dimension.required = !1, isValid = !1) : ($scope.name.required = "Please give it a meaningful name", $scope.metric.required = !1, $scope.dimension.required = !1, isValid = !1), $scope.meta ? ($scope.type.spark = $scope.type.map = $scope.meta.histogram ? "This visualization is not compatible with the selected metric" : "", $scope.aggregation = {
mean: $scope.meta.aggregation.indexOf("mean") > -1 || !$scope.meta.aggregation.length,
sum: $scope.meta.aggregation.indexOf("sum") > -1,
event_count: $scope.meta.aggregation.indexOf("event_count") > -1,
histogram: $scope.meta.aggregation.indexOf("histogram") > -1
}, $scope.dimension.disabled = !1, $scope.group.time = "pie" !== $scope.widget.type && $scope.meta.groupTime !== !1, $scope.group.value = $scope.meta.groupValues || $scope.meta.starEvent, $scope.group.dimension = $scope.widget.query.filter ? $scope.meta.groupTime !== !1 : !1, $scope.currency.typeSelect = $scope.meta.currency && "core" !== $scope.meta.category ? $scope.meta.currency : null) : ($scope.group = {
time: !1,
dimension: !1,
value: !1
}, $scope.aggregation = {
mean: !1,
sum: !1,
event_count: !1,
histogram: !1
}, $scope.dimension.disabled = !0, $scope.currency.disabled = !0, "map" !== $scope.widget.type && "quality" !== $scope.widget.type && ($scope.widget.query.filter = null), $scope.type.spark = $scope.type.map = ""), "pie" === $scope.widget.type && "value" === $scope.widget.query.group ? $scope.dimension.disabled = !0 : "map" === $scope.widget.type ? ($scope.dimension.disabled = !1, $scope.dimension.readonly = !0, $scope.group.value = !1, $scope.group.time = !1) : "spark" === $scope.widget.type ? ($scope.group.dimension = !1, $scope.group.value = !1) : "quality" === $scope.widget.type && ($scope.metric.required = !1, $scope.metric.disabled = !0, $scope.dimension.disabled = !1), autoSelect(), $scope.widget.query.group || "quality" === $scope.widget.type || (isValid = !1), $scope.widget.query.aggregation || "quality" === $scope.widget.type || (isValid = !1), $scope.isInvalid = !isValid, $scope.aggregationTooltip = $scope.widget.query && $scope.widget.query.metric && $scope.widget.query.metric.category ? gaApiMeta.getAggregationTooltip($scope.widget.query.metric.category) : {}, isValid
},
autoSelect = function() {
var index = 0,
aggregations = ["mean", "sum", "event_count", "histogram"];
for (index = 0; index < aggregations.length && !$scope.aggregation[$scope.widget.query.aggregation];) $scope.aggregation[aggregations[index]] && ($scope.widget.query.aggregation = aggregations[index]), index++;
$scope.aggregation[$scope.widget.query.aggregation] || ($scope.widget.query.aggregation = "");
var groups = ["time", "dimension", "value"];
for (index = 0; index < groups.length && !$scope.group[$scope.widget.query.group];) $scope.group[groups[index]] && ($scope.widget.query.group = groups[index]), index++;
$scope.group[$scope.widget.query.group] || ($scope.widget.query.group = ""), $scope.dimension.disabled && ($scope.widget.query.filter = null), $scope.metric.disabled && ($scope.widget.query.metric = null), $scope.widget && $scope.widget.query && $scope.widget.query.metric && $scope.meta && $scope.meta.currency && !$scope.widget.query.metric.currency && ($scope.widget.query.metric.currency = "business" !== $scope.widget.query.metric.category && "Virtual" === $scope.currencies.lastSelected ? "USD" : $scope.currencies.lastSelected || "USD"), $scope.type.spark && "spark" === $scope.widget.type ? $scope.widget.type = "line" : $scope.type.map && "map" === $scope.widget.type && ($scope.widget.type = "line")
},
widgetCheckMetric = function() {
$scope.widget.query.metric ? ($scope.meta = gaApiMeta.getMetric($scope.widget.query.metric.category, $scope.widget.query.metric.event), widgetCheckRequirements()) : ($scope.meta = null, widgetCheckRequirements())
},
widgetCheckFilter = function() {
widgetCheckRequirements()
},
widgetCheckType = function(newVal, oldVal) {
"map" === newVal ? $scope.widget.query.filter = {
dimension: "country",
values: ["*"]
} : "map" === oldVal ? $scope.widget.query.filter = null : widgetCheckRequirements(), "quality" === newVal ? ($scope.widget.grid.sizex = 4, $scope.widget.grid.sizey = 2) : "spark" === newVal ? ($scope.widget.grid.sizex = 1, $scope.widget.grid.sizey = 1) : 1 === sisexState || "pie" === newVal ? ($scope.widget.grid.sizex = 2, $scope.widget.grid.sizey = 1) : ($scope.widget.grid.sizex = sisexState, $scope.widget.grid.sizey = siseyState)
};
$scope.meta = $scope.widget && $scope.widget.query.metric ? gaApiMeta.getMetric($scope.widget.query.metric.category, $scope.widget.query.metric.event) : null, $scope.widget = $scope.widget || {
id: Math.random().toString().replace(".", ""),
name: "",
grid: {
col: 1,
row: 1,
sizex: 2,
sizey: 1
},
type: "line",
query: {
metric: null,
aggregation: "mean",
group: "time",
filter: null
}
};
var sisexState = $scope.widget.grid.sizex,
siseyState = $scope.widget.grid.sizey;
return dimensionsProcess(), widgetCheckRequirements(), {
metricpicker: metricpicker,
dimensionpicker: dimensionpicker,
validateWidget: validateWidget,
dimensionsProcess: dimensionsProcess
}
}).directive("gaWidgetEdit", function() {
var linkingFunction = function($scope, $element, $attrs, $controller) {
$scope.metricpicker = $controller.metricpicker, $scope.dimensionpicker = $controller.dimensionpicker, $scope.validateWidget = $controller.validateWidget, $scope.dimensionsProcess = $controller.dimensionsProcess
};
return {
restrict: "A",
replace: !1,
link: linkingFunction,
controller: "gaWidgetEditController",
templateUrl: "/static/ga-app/modules/pages/game/dashboards/dialogs/widget-edit.html",
scope: !1
}
}), angular.module("ga.pages.dashboard.dialog.widgetAdd", []).controller("gaWidgetAddController", function($scope) {
$scope.selected = {
dashboard: null,
widgets: []
}, $scope.notHidden = function(dashboard) {
return dashboard.hidden !== !0
};
var addWidget = function(widget) {
$scope.selected.widgets.unshift(angular.copy(widget))
},
removeWidget = function($index) {
$scope.selected.widgets.splice($index, 1)
};
return {
addWidget: addWidget,
removeWidget: removeWidget
}
}).directive("gaWidgetAdd", function() {
var linkingFunction = function($scope, $element, $attrs, $controller) {
$scope.addWidget = $controller.addWidget, $scope.removeWidget = $controller.removeWidget
};
return {
restrict: "A",
replace: !1,
link: linkingFunction,
controller: "gaWidgetAddController",
templateUrl: "/static/ga-app/modules/pages/game/dashboards/dialogs/widget-add.html",
scope: !1
}
}), angular.module("ga.pages.game.funnel.controller", ["ga.api.meta", "ga.api.data", "ga.api.data.funnel", "ga.api.metric", "ga.api.userDb.authenticated.game", "ga.api.userDb.authenticated.funnel", "ga.ui.modal", "ga.ui.sortable", "ga.services.dialogs", "ga.utils.date", "ga.utils.tracking", "ga.services.user", "ga.components.moment", "ga.pages.game.funnel.view", "ga.pages.game.funnel.dialogs.list-date-ranges", "ga.pages.game.funnel.dialogs.process", "ga.pages.game.funnel.dialogs.edit-prompt"]).service("gaFunnelController", function($rootScope, $state, $q, $filter, $timeout, moment, gaUtilsDate, gaUiModal, gaApiMetric, gaApiMeta, gaDialogs, gaApiDataFunnel, gaApiUserDbAuthenticatedGame, gaApiUserDbAuthenticatedFunnel, gaApiData, gaServicesUser, gaUtilsTracking) {
var _loaded = !1,
currentGameId = null,
_tmpCreatedFunnel = null,
_isDemo = !1,
_data = {
backendFunnels: [],
rawFunnels: [],
parsedFunnels: [],
filteredFunnelList: []
},
_defaultSort = {
name: "lastEdited",
desc: !0
},
_notification = null,
_listFilters = null,
_imported = !1,
_sortStatusIndex = ["processed", "processing", "queued", "failed", "notQueued"],
_lastLoadedTime = 0,
_loadFunnels = function(gameId) {
return _isDemo ? (_data.parsedFunnels = _getDemoFunnels(), $q.when(!0)) : gaApiUserDbAuthenticatedGame.getFunnels(gameId).then(function(funnels) {
return funnels ? (_data.rawFunnels = funnels, gaApiDataFunnel.getList(gameId)) : $q.reject()
}).then(function(backendList) {
backendList && backendList.length && (_data.backendFunnels = backendList), _parseFunnels(), _loaded = !0, _lastLoadedTime = moment()
})
},
_setDefaultFilters = function() {
_listFilters = {
status: {
processed: !0,
processing: !0,
queued: !0,
failed: !0,
notQueued: !0,
empty: !0
},
onlyOwned: !1,
sort: angular.copy(_defaultSort)
}
},
_getStatus = function(name) {
var returnObj = {};
switch (name) {
case "RUNNING":
case "QUEUED":
returnObj = {
id: "processing",
title: "Processing...",
css: "yellow ga-icon-refresh"
};
break;
case "EMPTY":
returnObj = {
id: "processed",
title: "Processed",
css: "green ga-icon-check",
hasData: !1
};
break;
case "COMPLETED":
returnObj = {
id: "processed",
title: "Processed",
css: "green ga-icon-check",
hasData: !0
};
break;
case "FAILED":
returnObj = {
id: "failed",
title: "Failed",
css: "red ga-icon-severity-error"
};
break;
default:
returnObj = {
id: "notQueued",
title: "Not processed",
css: "grey"
}
}
return returnObj
},
_parseFunnels = function() {
_data.parsedFunnels = _data.rawFunnels.map(function(funnel) {
return _parseFunnel(funnel)
})
},
_parseFunnel = function(funnel) {
var interval = {
start: 0,
end: 0
},
dateRanges = [],
defaultDateRange = 0,
dateRangeListFormatted = "",
status = _getStatus(),
statusStates = {};
funnel.date_ranges && funnel.date_ranges.length && funnel.date_ranges.forEach(function(drange) {
var tmpDateRange = {
id: drange.id,
backendId: drange.backend_id,
start: 1e3 * moment.utc(drange.from_date).unix(),
end: 1e3 * moment.utc(drange.to_date).unix(),
owner: gaServicesUser.id === drange.created_by_id,
createdBy: drange.created_by_name,
status: _getStatus()
};
tmpDateRange.dateRangeFormatted = gaUtilsDate.getIntervalTitle(tmpDateRange), drange.backend_id && ("__failed__" === drange.backend_id ? (tmpDateRange.status = _getStatus("FAILED"), statusStates[tmpDateRange.status.id] || (statusStates[tmpDateRange.status.id] = []), statusStates[tmpDateRange.status.id].push(tmpDateRange)) : "__empty__" === drange.backend_id ? (tmpDateRange.status = _getStatus("EMPTY"), statusStates[tmpDateRange.status.id] || (statusStates[tmpDateRange.status.id] = []), statusStates[tmpDateRange.status.id].push(tmpDateRange)) : _data.backendFunnels.some(function(b) {
return b.funnel_id === drange.backend_id ? (tmpDateRange.status = _getStatus(b.status), tmpDateRange.interval = {
start: 1e3 * b.start,
end: 1e3 * b.end
}, statusStates[tmpDateRange.status.id] || (statusStates[tmpDateRange.status.id] = []), statusStates[tmpDateRange.status.id].push(tmpDateRange), funnel.settings.dimensions && angular.forEach(funnel.settings.dimensions, function(val, dim) {
val.values && 1 === val.values.length && "*" === val.values[0] && b.dimensions[dim] && !b.dimensions[dim].negation && (val.values = b.dimensions[dim].values)
}), funnel.settings.excludedDimensions && angular.forEach(funnel.settings.excludedDimensions, function(val, dim) {
val.values && 1 === val.values.length && "*" === val.values[0] && b.dimensions[dim] && b.dimensions[dim].negation && (val.values = b.dimensions[dim].values)
}), !0) : void 0
})), dateRanges.push(tmpDateRange)
});
var statusStatesFormatted = {};
if (dateRanges.length) {
if (dateRanges.sort(function(a, b) {
return b.end > a.end ? 1 : a.end === b.end ? 0 : -1
}), statusStates.processed && statusStates.processed.length) {
var tmpStatus = "COMPLETED";
statusStates.processed.length === statusStates.processed.filter(function(dr) {
return dr.status && !dr.status.hasData
}).length ? (tmpStatus = "EMPTY", dateRanges.some(function(dr) {
return "processed" === dr.status.id ? (interval = {
start: dr.start,
end: dr.end
}, defaultDateRange = dr.id, !0) : void 0
})) : dateRanges.some(function(dr) {
return "processed" === dr.status.id && dr.status.hasData ? (interval = {
start: dr.start,
end: dr.end
}, defaultDateRange = dr.id, !0) : void 0
}), status = _getStatus(tmpStatus)
} else statusStates.processing && statusStates.processing.length ? (status = _getStatus("RUNNING"), dateRanges.some(function(dr) {
return "processing" === dr.status.id ? (interval = {
start: dr.start,
end: dr.end
}, defaultDateRange = dr.id, !0) : void 0
})) : statusStates.failed && statusStates.failed.length ? (status = _getStatus("FAILED"), dateRanges.some(function(dr) {
return "failed" === dr.status.id ? (interval = {
start: dr.start,
end: dr.end
}, defaultDateRange = dr.id, !0) : void 0
})) : (interval = {
start: dateRanges[0].start,
end: dateRanges[0].end
}, defaultDateRange = dateRanges[0].id);
dateRanges.length > 1 && (dateRangeListFormatted = dateRanges.filter(function(dr) {
return dr.id !== defaultDateRange
}).map(function(dr) {
return dr.dateRangeFormatted + (dr.status && "processed" === dr.status.id && !dr.status.hasData ? " (No data)" : "")
}).join("<br />"));
var diffStatus = 0;
angular.forEach(statusStates, function(state, key) {
var title = key;
title = "<strong>" + title.charAt(0).toUpperCase() + title.slice(1) + "</strong>", statusStatesFormatted[key] = {
count: state.length,
ranges: title + "<br />" + state.map(function(dr) {
return dr.dateRangeFormatted
}).join("<br />")
}, diffStatus++
}), 2 > diffStatus && (statusStatesFormatted = {})
}
var metrics = [];
funnel.settings.events && funnel.settings.events.length && (metrics = funnel.settings.events.map(function(e) {
var arrEvent = e.split(":"),
arrFEvent = e.replace(/ /gi, "&nbsp;").split(":");
arrFEvent.splice(-1, 1);
var fpath = arrFEvent.join("&nbsp;&gt; ") + "&nbsp;&gt;";
return fpath = fpath.charAt(0).toUpperCase() + fpath.slice(1), {
category: arrEvent[0],
shortName: getMetricShortName(e),
formatted: getMetricFormatted(e),
event: arrEvent.splice(1).join(":")
}
}));
var filters = [];
funnel.settings.dimensions && angular.forEach(funnel.settings.dimensions, function(val, dim) {
var meta = gaApiMeta.getDimension(dim) || null;
filters.push({
meta: meta,
dimension: dim,
values: val.values,
formattedValues: val.values.map(function(value) {
return "*" === value ? "All" : $filter("formatUnitType")(gaApiMeta.getDimension(value).title, meta.type)
})
})
});
var excludes = [];
return funnel.settings.excludedDimensions && angular.forEach(funnel.settings.excludedDimensions, function(val, dim) {
var meta = gaApiMeta.getDimension(dim) || null;
excludes.push({
meta: meta,
dimension: dim,
values: val.values,
formattedValues: val.values.map(function(value) {
return "*" === value ? "All" : $filter("formatUnitType")(gaApiMeta.getDimension(value).title, meta.type)
})
})
}), {
id: funnel.id,
name: funnel.funnel_name,
createdBy: funnel.created_by_name,
owner: gaServicesUser.id === funnel.created_by_id,
dateRangeFormatted: gaUtilsDate.getIntervalTitle(interval),
dateRangeListFormatted: dateRangeListFormatted,
dateRange: interval.end,
dateRanges: dateRanges,
defaultDateRange: defaultDateRange,
statusStates: statusStatesFormatted,
lastEdited: moment.utc(funnel.edited_date).unix(),
status: status,
metrics: metrics,
filters: filters,
excludes: excludes,
range: {
main: {
start: interval.start,
end: interval.end
}
}
}
},
_nbOfStatusFilters = function() {
var nb = 0;
return angular.forEach(_listFilters.status, function(stat) {
nb += stat ? 1 : 0
}), nb
},
_filterFunnelList = function(category, value) {
if (_data.parsedFunnels) {
category && ("status" === category ? _listFilters.status[value] = _listFilters.status[value] && _nbOfStatusFilters() > 1 ? !1 : !0 : "onlyOwned" === category && (_listFilters.onlyOwned = !_listFilters.onlyOwned));
var list = [];
return _data.parsedFunnels.some(function(f) {
var valid = !0;
if (_listFilters.onlyOwned && !f.owner && (valid = !1), f.dateRanges && f.dateRanges.length) {
var drvalid = !1;
f.dateRanges.some(function(dr) {
return _listFilters.status[dr.status.id] ? (drvalid = !0, !0) : void 0
}), drvalid || (valid = !1)
} else _listFilters.status[f.status.id] || (valid = !1);
valid && list.push(f)
}), list = _sortFunnelList(list), _data.filteredFunnelList = list, $q.all(list)
}
return $q.all([])
},
_sortFunnelList = function(value) {
return _listFilters.sort || (_listFilters.sort = angular.copy(_defaultSort)), angular.isArray(value) ? value.sort(_applyFunnelSort) : (_listFilters.sort.name === value ? _listFilters.sort.desc ? _listFilters.sort = angular.copy(_defaultSort) : _listFilters.sort.desc = !0 : (_listFilters.sort.name = value, _listFilters.sort.desc = !1), void _data.filteredFunnelList.sort(_applyFunnelSort))
},
_applyFunnelSort = function(a, b) {
if ("status" === _listFilters.sort.name || ["lastEdited", "dateRange"].indexOf(_listFilters.sort.name) > -1) {
var sortA = a[_listFilters.sort.name],
sortB = b[_listFilters.sort.name];
return "status" === _listFilters.sort.name && (sortA = _sortStatusIndex.indexOf(sortA.id), sortB = _sortStatusIndex.indexOf(sortB.id)), _listFilters.sort.desc ? sortB > sortA ? 1 : sortA === sortB ? 0 : -1 : sortA > sortB ? 1 : sortA === sortB ? 0 : -1
}
return _listFilters.sort.desc ? b[_listFilters.sort.name].localeCompare(a[_listFilters.sort.name]) : a[_listFilters.sort.name].localeCompare(b[_listFilters.sort.name])
},
_nameDialog = function(name, duplicate) {
var deferred = $q.defer(),
$newScope = $rootScope.$new(),
title = "Create funnel";
$newScope.values = {
name: "Untitled funnel"
}, name && ($newScope.values = {
name: name
}, title = duplicate ? "Duplicate funnel" : "Rename funnel");
var createTemplate = '<div class="ga"><label for="name">Name</label><input class="ga-input full-width" name="name" ng-model="values.name" /></div>',
buttons = [{
title: "Save",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "confirm",
disabled: "!values.name"
}, {
title: "Cancel",
"class": "ga-btn-text right",
action: "cancel",
keyCode: 27
}];
return gaUiModal.show({
scope: $newScope,
header: title,
content: createTemplate,
width: 500,
actions: {
cancel: function() {
deferred.reject()
},
confirm: function() {
deferred.resolve($newScope.values.name)
}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
}), deferred.promise
},
getEmptyFunnel = function(name) {
return {
id: 0,
name: name || "Untitled funnel",
metrics: [],
filters: [],
excludes: [],
dateRanges: [],
range: {
main: gaUtilsDate.getPeriodRange("last30Days")
}
}
},
getFunnelAndDateRangeFromBackendId = function(backendId) {
return getFunnelList($state.params.gameId).then(function(funnelList) {
if (funnelList && funnelList.length) {
var foundDateRangeId = !1,
returnObj = null;
return funnelList.some(function(funnel) {
return funnel.dateRanges && funnel.dateRanges.some(function(dr) {
dr.backendId === backendId && (foundDateRangeId = dr.id)
}), foundDateRangeId ? (returnObj = {
funnelId: funnel.id,
dateRangeId: foundDateRangeId
}, !0) : void 0
}), returnObj ? returnObj : $q.reject()
}
return $q.reject()
})
},
getFunnelList = function(gameId, filters, reload) {
filters && (_listFilters = filters);
var expired = moment().diff(_lastLoadedTime, "seconds") > 60;
return reload || !_loaded || currentGameId !== gameId || expired ? (currentGameId === gameId && _listFilters || (currentGameId = gameId, _isDemo = gaServicesUser.game(parseInt(currentGameId, 10)).demo, _setDefaultFilters()), _loadFunnels(gameId).then(function() {
return _filterFunnelList()
})) : _filterFunnelList()
},
getFunnel = function(gameId, funnelId, reload) {
var promises = !0;
return (reload || !_loaded || currentGameId !== gameId) && (currentGameId = gameId, _isDemo = gaServicesUser.game(parseInt(currentGameId, 10)).demo, _setDefaultFilters(), promises = _loadFunnels(gameId)), $q.all(promises).then(function() {
funnelId = isNaN(funnelId) ? funnelId : parseInt(funnelId, 10);
var tmpFunnel = null;
return _data.parsedFunnels.some(function(funnel) {
return isNaN(funnelId) && funnel.backendId === funnelId ? (tmpFunnel = funnel, !0) : isNaN(funnelId) || funnel.id !== funnelId ? void 0 : (tmpFunnel = funnel, !0)
}), tmpFunnel ? angular.copy(tmpFunnel) : $q.reject()
})
},
getFunnelWithData = function(gameId, funnelId, dateRangeId, reload) {
return getFunnel(gameId, funnelId, reload).then(function(tmpFunnel) {
return _isDemo ? _getDemoFunnelDateRange(tmpFunnel, dateRangeId) : (tmpFunnel.dateRangeFormatted = gaUtilsDate.getIntervalTitle(tmpFunnel.range.main), tmpFunnel && tmpFunnel.status && "processed" === tmpFunnel.status.id && !tmpFunnel.status.hasData ? _setDimensionValues(tmpFunnel).then(function(newFunnel) {
return _getFunnelWithData(newFunnel, dateRangeId)
}) : _getFunnelWithData(tmpFunnel, dateRangeId))
})
},
_getFunnelWithData = function(funnel, dateRangeId) {
return gaApiDataFunnel.getFunnel($state.params.gameId, funnel, dateRangeId).then(function(data) {
return funnel.query = {
left: data.query
}, funnel.overview = data.overview, funnel
}).catch(function() {
return funnel
})
},
_setDimensionValues = function(funnel) {
var lookup = !1;
return funnel && funnel.filters && funnel.filters.some(function(filter) {
return filter.values && "*" === filter.values[0] ? (lookup = !0, !0) : void 0
}), lookup ? gaApiData.getValue("/dimensions", $state.params.gameId).then(function(dimensions) {
return dimensions && funnel.filters.forEach(function(filter) {
filter.values && "*" === filter.values[0] && dimensions[filter.dimension] && (filter.values = dimensions[filter.dimension])
}), funnel
}) : $q.when(funnel)
},
saveFunnel = function(funnel) {
if (_isDemo) return $q.reject();
var dimensions = {};
funnel.filters && funnel.filters.length && funnel.filters.some(function(filter) {
dimensions[filter.dimension] = {
values: filter.values
}
});
var excludedDimensions = {};
funnel.excludes && funnel.excludes.length && funnel.excludes.some(function(filter) {
excludedDimensions[filter.dimension] = {
values: filter.values
}
});
var tmpEvents = [];
funnel.metrics && funnel.metrics.length && (tmpEvents = funnel.metrics.map(function(metric) {
return metric.category + ":" + metric.event
}));
var options = {
events: tmpEvents,
dimensions: dimensions,
excludedDimensions: excludedDimensions
};
return _saveFunnelToDB(funnel, options)
},
_saveFunnelToDB = function(funnel, options) {
return funnel.id ? gaApiUserDbAuthenticatedFunnel.updateFunnel(funnel.id, funnel.name, options, funnel.dateRanges).then(function(result) {
if (result && result[0]) {
var parsedFunnel = _parseFunnel(result[0]);
return _data.parsedFunnels = _data.parsedFunnels.map(function(f) {
return f.id === funnel.id && (f = parsedFunnel), f
}), parsedFunnel
}
}) : gaApiUserDbAuthenticatedGame.createFunnel($state.params.gameId, funnel.name, options, funnel.dateRanges).then(function(f) {
return _loadFunnels($state.params.gameId).then(function() {
return getFunnel($state.params.gameId, f.id)
})
})
},
reProcessFunnel = function(funnel) {
return processFunnel(funnel, !0)
},
_processDateRange = function(options) {
var processOptions = angular.copy(options),
query = {
metric: {
category: processOptions.events[0].category,
event: processOptions.events[0].event
},
filter: null,
group: "aggregated",
aggregation: "event_count",
interval: {
start: processOptions.start,
end: processOptions.end
},
compareInterval: !1,
returnAll: !1,
merged: !1
};
return "business" === query.metric.category && (query.metric.currency = "USD"), gaApiData.get(query).then(function(result) {
var hasEvents = !1;
return result && result.data && result.data.aggregated && result.data.aggregated[0].total && result.data.aggregated[0].total > 0 && (hasEvents = !0), hasEvents ? (processOptions.events = processOptions.events.map(function(event) {
return event.category + ":" + event.event + ":.*"
}), gaApiDataFunnel.processFunnel($state.params.gameId, gaServicesUser.details.email, processOptions).catch(function() {
return null
})) : {
funnel_id: "__empty__",
status: "empty"
}
})
},
processFunnel = function(funnel, reprocess) {
return _isDemo ? $q.reject() : _validateFunnel(funnel).then(function() {
return _processFunnel(funnel, reprocess)
}).catch(function() {
return $q.reject({
error: "Can't process the funnel"
})
})
},
_processFunnel = function(funnel, reprocess) {
var promises = {},
dimensions = {};
funnel.filters && funnel.filters.length && funnel.filters.some(function(filter) {
dimensions[filter.dimension] = {
values: filter.values
}
}), funnel.excludes && funnel.excludes.length && funnel.excludes.some(function(filter) {
dimensions[filter.dimension] = {
values: filter.values,
negation: !0
}
});
var options = {
events: funnel.metrics.map(function(metric) {
return {
category: metric.category,
event: metric.event
}
}),
dimensions: dimensions,
start: 0,
end: 0,
name: funnel.name
};
return funnel.dateRanges.some(function(dr) {
var notQueued = dr.status && "notQueued" === dr.status.id;
return reprocess && dr.backendId && !notQueued ? !1 : (options.start = dr.start, options.end = dr.end, void(promises[dr.id] = _processDateRange(options)))
}), $q.all(promises).then(function(results) {
if (results) {
var status = "processing",
emptyDateRange = null,
failed = 0,
count = 0;
return angular.forEach(results, function(data, key) {
data ? data.funnel_id && data.status && funnel.dateRanges.some(function(dr) {
dr.id === parseInt(key) && (dr.backendId = data.funnel_id, "__empty__" === data.funnel_id && (emptyDateRange || (emptyDateRange = dr, status = "empty"))), dr.status = {
id: "added"
}
}) : (failed++, funnel.dateRanges.some(function(dr) {
dr.id === parseInt(key) && (dr.backendId = "__failed__")
})), count++
}), failed === count && (status = "failed"), saveFunnel(funnel).then(function(savedFunnel) {
return getFunnel($state.params.gameId, savedFunnel.id, !0)
}).then(function(funnel) {
var trackingLabel = "";
return trackingLabel = "failed" === status ? "failed" : reprocess && "empty" === status ? "empty-existing" : "empty" === status ? "empty-new" : reprocess ? "queued-existing" : "queued", gaUtilsTracking.trackEvent({
category: "funnels",
action: "process",
label: trackingLabel
}), {
funnel: funnel,
status: status,
dateRange: emptyDateRange
}
})
}
})
},
_validateFunnel = function(funnel) {
var valid = gaServicesUser.details.email && funnel.metrics.length > 0 && funnel.dateRanges && funnel.dateRanges.length > 0;
return valid ? $q.when(!0) : $q.reject()
},
importFunnels = function(gameId) {
if (_imported && currentGameId === gameId && !_isDemo) return $q.when(!0);
_imported = !0;
var importStatus = {
existingFunnels: !1,
imported: !0,
eventErrors: !1,
funnelErrors: !1
};
return gaApiUserDbAuthenticatedGame.saveOldFunnelsImported($state.params.gameId).then(function() {
return $timeout(gaServicesUser.getUserData.bind(gaServicesUser), 50), gaApiUserDbAuthenticatedGame.getOldFunnels(gameId).then(function(result) {
return result && result.length ? (importStatus.existingFunnels = !0, _parseOldFunnels(result).then(function(funnels) {
var promises = [];
return funnels && funnels.length ? (angular.forEach(funnels, function(funnel) {
funnel ? (funnel.importError && (importStatus.eventErrors = !0), promises.push(saveFunnel(funnel))) : importStatus.funnelErrors = !0
}), 0 === promises.length && funnels.length > 0 && (importStatus.imported = !1), $q.all(promises).then(function() {
return importStatus
})) : importStatus
})) : importStatus
})
})
},
importedDialog = function(status) {
var deferred = $q.defer(),
$newScope = $rootScope.$new(),
headline = "",
text = "",
btnText = "",
title = "Importing existing funnels";
status.imported ? status.eventErrors ? (headline = "Funnels imported with events missing", text = "All existing funnels have been imported, however certain events could not be re-created. ", text += "You can change the order of events and remember that all funnels will need to be processed with a date range.", btnText = "View funnels") : status.funnelErrors ? (headline = "Most funnels are imported", text = "We managed to import most of your existing funnels, however certain funnels could not be re-created. ", text += "You can change the order of events and remember that all funnels will need to be processed with a date range.", btnText = "View funnels") : (headline = "Funnels imported succesfully", text = "All existing funnels have been imported and are now available. ", text += "You can change the order of events and start choosing dimensions you wish to include or exclude. ", text += "All funnels will need to be processed with a date range.", btnText = "View funnels") : (headline = "No funnels imported", text = "Unfortunately we could not re-create any of the existing funnels for this game. <br />", text += "You can create new funnels from design and business events tracked in the game and segment by different dimensions.", btnText = "Start using funnels");
var createTemplate = '<div class="ga"><h3>' + headline + "</h3><p>" + text + "</p><p class=\"modal-notice\">Please note that the 'Started Game' event has been removed as part of the update</p></div>",
buttons = [{
title: btnText,
"class": "ga-btn-alt orange",
keyCode: 13,
action: "confirm"
}];
return gaUiModal.show({
scope: $newScope,
header: title,
content: createTemplate,
customClass: "funnels-imported-modal",
width: 650,
actions: {
cancel: function() {
deferred.reject()
},
confirm: function() {
deferred.reject()
}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
}), deferred.promise
},
_parseOldFunnels = function(oldFunnels) {
var promises = [];
return angular.forEach(oldFunnels, function(funnel) {
promises.push(_parseOldFunnel(funnel))
}), $q.all(promises)
},
_parseOldFunnel = function(funnel) {
if (funnel.event_ids && funnel.event_ids.length) {
var promises = [];
return angular.forEach(funnel.event_ids, function(event) {
promises.push(gaApiMetric.getMetric(event))
}), $q.all(promises).then(function(eventList) {
var tmpFunnel = getEmptyFunnel(funnel.funnel_name);
return eventList && eventList.length ? (angular.forEach(eventList, function(event) {
event ? tmpFunnel.metrics.push(event) : tmpFunnel.importError = !0
}), tmpFunnel.metrics && tmpFunnel.metrics.length ? tmpFunnel : null) : null
}).catch(function() {
return null
})
}
return []
},
saveAndProcessFunnel = function(funnel) {
return saveFunnel(funnel).then(function(savedFunnel) {
var tmpSavedFunnel = savedFunnel;
return processFunnel(savedFunnel).catch(function() {
console.log(tmpSavedFunnel)
})
})
},
getCreatedFunnel = function() {
return _tmpCreatedFunnel || (_tmpCreatedFunnel = getEmptyFunnel("Untitled funnel")), _tmpCreatedFunnel
},
createFunnelDialog = function() {
return _nameDialog().then(function(name) {
gaUtilsTracking.trackEvent({
category: "funnels",
action: "create",
label: "new"
});
var newFunnel = getEmptyFunnel(name);
return saveFunnel(newFunnel)
})
},
renameFunnelDialog = function(funnel) {
return _nameDialog(funnel.name).then(function(name) {
var tmpFunnel = angular.copy(funnel);
return tmpFunnel.name = name, saveFunnel(tmpFunnel).then(function() {
return tmpFunnel
})
})
},
duplicateFunnelDialog = function(funnel) {
return _nameDialog(funnel.name, !0).then(function(name) {
var tmpFunnel = angular.copy(funnel);
return tmpFunnel.name = name, tmpFunnel.id = null, tmpFunnel.dateRanges && (tmpFunnel.dateRanges = tmpFunnel.dateRanges.map(function(dr) {
return {
start: dr.start,
end: dr.end,
id: 0
}
})), gaUtilsTracking.trackEvent({
category: "funnels",
action: "create",
label: "duplicate"
}), saveFunnel(tmpFunnel).then(function(f) {
return f
})
})
},
deleteFunnelDialog = function(funnel) {
return gaUiModal.confirm("Are you sure you want to delete this funnel? All processed data will be lost!", "Delete funnel").then(function() {
return gaApiUserDbAuthenticatedFunnel.deleteFunnel(funnel.id).then(function() {
var trackingLabel = "not-processed";
return funnel.status && "processed" === funnel.status.id && (trackingLabel = "processed"), gaUtilsTracking.trackEvent({
category: "funnels",
action: "delete",
label: trackingLabel
}), _loadFunnels($state.params.gameId)
})
}).catch(function() {
return $q.reject({
action: "closed"
})
})
},
dateRangesDialog = function(funnel, process, failed) {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/game/funnel/dialogs/list-date-ranges.html",
controller: "gaPagesFunnelListDateRangesController",
width: 800,
parameters: {
funnel: funnel,
failedOnly: failed || !1,
process: process
}
})
},
editPromptDialog = function(funnel) {
gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/game/funnel/dialogs/edit-prompt.html",
controller: "gaPagesFunnelEditPromptController",
width: 800,
parameters: {
funnel: funnel
}
}).then(function(result) {
result && ("duplicate" === result.action ? duplicateFunnelDialog(funnel).then(function(newFunnel) {
newFunnel && newFunnel.id && (setNotification("Funnel duplicated!"), $state.go("game.funnel.edit", {
funnelId: newFunnel.id,
gameId: $state.params.gameId
}))
}) : "edit" === result.action && (gaUtilsTracking.trackEvent({
category: "funnels",
action: "create",
label: "edit-existing"
}), editFunnel(funnel.id)))
})
},
editFunnel = function(funnelId) {
getFunnel($state.params.gameId, funnelId).then(function(funnel) {
funnel.dateRanges && (funnel.dateRanges = funnel.dateRanges.map(function(dr) {
return dr.backendId = "", dr
})), saveFunnel(funnel).then(function() {
$state.go("game.funnel.edit", {
funnelId: $state.params.funnelId,
gameId: $state.params.gameId,
dateRangeId: $state.params.dateRangeId
})
})
})
},
getMetricShortName = function(event) {
if (event) {
var tmp = event.split(":").slice(-1)[0];
return ".*" === tmp && (tmp = event.split(":").slice(-2)[0] + tmp), tmp.charAt(0).toUpperCase() + tmp.slice(1)
}
},
getMetricFormatted = function(event, category) {
var name = getMetricShortName(event),
arrEvent = event.replace(/ /gi, "&nbsp;").split(":");
".*" === arrEvent[arrEvent.length - 1] ? arrEvent.splice(-2, 2) : arrEvent.splice(-1, 1);
var fpath = (category ? category + "&nbsp;&gt; " : "") + (arrEvent.length ? arrEvent.join("&nbsp;&gt; ") + "&nbsp;&gt; " : "");
fpath = fpath.charAt(0).toUpperCase() + fpath.slice(1);
var fpathclean = fpath.replace(/&nbsp;/g, " ").replace(/&gt;/g, ">");
return {
name: name,
path: fpath,
cleanPath: fpathclean + name
}
},
getMetricFormattedName = function(metric) {
return {
path: metric.event,
name: metric.event.split(":").slice(-1)[0]
}
},
getNewDateRange = function(range, dateRanges) {
if (range.start && range.end) {
if (!dateRanges) return !1;
var existing = dateRanges.filter(function(r) {
return r.start === range.start && r.end === range.end
});
return 1 === existing.length && existing[0].delete ? (delete existing[0].delete, existing[0]) : {
id: 0,
createdBy: gaServicesUser.details.name,
status: {
title: "Just added - not processed",
css: "grey"
},
start: range.start,
end: range.end,
dateRangeFormatted: gaUtilsDate.getIntervalTitle({
start: range.start,
end: range.end
}),
"delete": !1
}
}
return !1
},
summaryDialog = function(funnel) {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/game/funnel/dialogs/process-funnel.html",
controller: "gaPagesFunnelProcessController",
width: 800,
parameters: {
funnel: funnel,
readOnly: !0
}
})
},
hasFunnels = function() {
return !!_data.parsedFunnels.length
},
getNotification = function() {
var tn = angular.copy(_notification);
return _notification = null, tn || null
},
setNotification = function(message, type) {
_notification = {
message: message || "",
type: type || "default"
}
},
_getDemoFunnelDateRange = function(funnel, dateRangeId) {
return funnel.overview = 1 === parseInt(dateRangeId, 10) ? {
avgTime: 65.21,
conversionRate: .596807172,
conversions: 15175,
dropoffs: 10254,
total: 25432
} : {
avgTime: 64.71,
conversionRate: .6027,
conversions: 15175,
dropoffs: 9954,
total: 24932
}, funnel
},
_getDemoFunnels = function() {
var demoFunnels = [{
id: 1,
createdBy: "GameAnalytics",
dateRange: 14041728e5,
dateRangeFormatted: "1. Jun - 30. Jun 2014",
dateRangeListFormatted: "1. May - 31. May 2014",
dateRanges: [{
backendId: "backendDemo1",
createdBy: "GameAnalytics",
dateRangeFormatted: "1. Jun - 30. Jun 2014",
end: 14041728e5,
id: 1,
owner: !0,
start: 14015808e5,
status: {
css: "green ga-icon-check",
id: "processed",
title: "Processed",
hasData: !0
}
}, {
backendId: "backendDemo2",
createdBy: "GameAnalytics",
dateRangeFormatted: "1. May - 31. May 2014",
end: 14041728e5,
id: 2,
owner: !0,
start: 14015808e5,
status: {
css: "green ga-icon-check",
id: "processed",
title: "Processed",
hasData: !0
}
}],
defaultDateRange: 1,
excludes: [{
dimension: "Devices",
meta: {
hidden: !1,
name: "device",
title: "Devices",
type: "dimension",
unit: "device"
},
values: ["i1", "i2"],
formattedValues: ["iPhone 4", "iPhone 4S"]
}],
filters: [{
dimension: "build",
meta: {
hidden: !1,
name: "build",
title: "Build",
type: "dimension",
unit: "build"
},
values: ["1.0", "1.1", "1.2"],
formattedValues: ["1.0", "1.1", "1.2"]
}, {
dimension: "country",
meta: {
hidden: !1,
name: "country",
title: "Country",
type: "dimension",
unit: "country"
},
values: ["US", "DK", "CA"],
formattedValues: ["United States", "Denmark", "Canada"]
}],
lastEdited: 1405338586,
metrics: [{
category: "design",
event: "Level 1:EnterArea1",
shortName: "EnterArea1",
formatted: {
cleanPath: "Design > Level 1 > EnterArea1",
name: "EnterArea1",
path: "Design&nbsp;&gt; Level&nbsp;1&nbsp;&gt;EnterArea1&nbsp;&gt;"
}
}, {
category: "design",
event: "Level 1:CompleteArea1",
shortName: "CompleteArea1",
formatted: {
cleanPath: "Design > Level 1 > CompleteArea1",
name: "CompleteArea1",
path: "Design&nbsp;&gt; Level&nbsp;1&nbsp;&gt;CompleteArea1&nbsp;&gt;"
}
}, {
category: "design",
event: "Level 1:Levelup1",
shortName: "Levelup1",
formatted: {
cleanPath: "Design > Level 1 > Levelup1",
name: "Levelup1",
path: "Design&nbsp;&gt; Level&nbsp;1&nbsp;&gt;Levelup1&nbsp;&gt;"
}
}, {
category: "design",
event: "Level 2:EnterArea2",
shortName: "EnterArea2",
formatted: {
cleanPath: "Design > Level 2 > EnterArea2",
name: "EnterArea2",
path: "Design&nbsp;&gt; Level&nbsp;2&nbsp;&gt;EnterArea2&nbsp;&gt;"
}
}, {
category: "design",
event: "Level 2:CompleteArea2",
shortName: "CompleteArea2",
formatted: {
cleanPath: "Design > Level 2 > CompleteArea2",
name: "CompleteArea2",
path: "Design&nbsp;&gt; Level&nbsp;2&nbsp;&gt;CompleteArea2&nbsp;&gt;"
}
}, {
category: "design",
event: "Level 2:Levelup2",
shortName: "Levelup2",
formatted: {
cleanPath: "Design > Level 2 > Levelup2",
name: "Levelup2",
path: "Design&nbsp;&gt; Level&nbsp;2&nbsp;&gt;Levelup2&nbsp;&gt;"
}
}],
name: "Player progressions",
owner: !0,
range: {
main: {
start: 14015808e5,
end: 14015808e5
}
},
status: {
css: "green ga-icon-check",
id: "processed",
title: "Processed",
hasData: !0
},
overview: {
avgTime: 65.21,
conversionRate: .596807172,
conversions: 15175,
dropoffs: 10254,
total: 25432
},
query: {
left: {
activeDimensionIndex: 0,
aggregation: "sum",
aggregation_aggregated: "sum",
compare: !1,
data: {
timeseries: [{
data: [{
dimension: "1.0",
data: [{
value: .1,
extraValues: {
count: 2543
}
}, {
value: .091675841,
extraValues: {
count: 2331,
avgTime: 15,
dropoff: 212
}
}, {
value: .084543095,
extraValues: {
count: 2150,
avgTime: 28,
dropoff: 181
}
}, {
value: .076757628,
extraValues: {
count: 1952,
avgTime: 10,
dropoff: 198
}
}, {
value: .068854199,
extraValues: {
count: 1751,
avgTime: 85,
dropoff: 201
}
}, {
value: .059680717,
extraValues: {
count: 1518,
avgTime: 72,
dropoff: 233
}
}]
}, {
dimension: "1.1",
data: [{
value: .4,
extraValues: {
count: 10173
}
}, {
value: .366703366,
extraValues: {
count: 9326,
avgTime: 13,
dropoff: 847
}
}, {
value: .338172381,
extraValues: {
count: 8600,
avgTime: 28,
dropoff: 726
}
}, {
value: .307030513,
extraValues: {
count: 7808,
avgTime: 57,
dropoff: 792
}
}, {
value: .275416798,
extraValues: {
count: 7004,
avgTime: 74,
dropoff: 804
}
}, {
value: .238722869,
extraValues: {
count: 6071,
avgTime: 69,
dropoff: 93
}
}]
}, {
dimension: "1.2",
data: [{
value: .5,
extraValues: {
count: 12716
}
}, {
value: .458379207,
extraValues: {
count: 11658,
avgTime: 12,
dropoff: 1059
}
}, {
value: .422715477,
extraValues: {
count: 10751,
avgTime: 78,
dropoff: 907
}
}, {
value: .383788141,
extraValues: {
count: 9761,
avgTime: 54,
dropoff: 990
}
}, {
value: .344270997,
extraValues: {
count: 8756,
avgTime: 24,
dropoff: 1005
}
}, {
value: .298403586,
extraValues: {
count: 7589,
avgTime: 41,
dropoff: 1167
}
}]
}],
meta: {
hidden: !1,
name: "build",
title: "Build",
type: "dimension",
unit: "build"
},
total: 0
}, {
data: [{
dimension: "CA",
data: [{
value: .05,
extraValues: {
count: 1271
}
}, {
value: .045837921,
extraValues: {
count: 1166,
avgTime: 52,
dropoff: 105
}
}, {
value: .042271548,
extraValues: {
count: 1075,
avgTime: 42,
dropoff: 91
}
}, {
value: .038378814,
extraValues: {
count: 976,
avgTime: 94,
dropoff: 99
}
}, {
value: .0344271,
extraValues: {
count: 876,
avgTime: 85,
dropoff: 100
}
}, {
value: .029840359,
extraValues: {
count: 758,
avgTime: 19,
dropoff: 118
}
}]
}, {
dimension: "DK",
data: [{
value: .35,
extraValues: {
count: 8901
}
}, {
value: .320865445,
extraValues: {
count: 8160,
avgTime: 57,
dropoff: 741
}
}, {
value: .295900834,
extraValues: {
count: 7525,
avgTime: 67,
dropoff: 635
}
}, {
value: .268651699,
extraValues: {
count: 6832,
avgTime: 25,
dropoff: 693
}
}, {
value: .240989698,
extraValues: {
count: 6129,
avgTime: 11,
dropoff: 703
}
}, {
value: .20888251,
extraValues: {
count: 5312,
avgTime: 55,
dropoff: 817
}
}]
}, {
dimension: "US",
data: [{
value: .6,
extraValues: {
count: 12716
}
}, {
value: .550055049,
extraValues: {
count: 13989,
avgTime: 24,
dropoff: 1270
}
}, {
value: .507258572,
extraValues: {
count: 12901,
avgTime: 41,
dropoff: 1088
}
}, {
value: .460545769,
extraValues: {
count: 11713,
avgTime: 95,
dropoff: 1188
}
}, {
value: .413125197,
extraValues: {
count: 10507,
avgTime: 35,
dropoff: 1206
}
}, {
value: .358084303,
extraValues: {
count: 9107,
avgTime: 25,
dropoff: 1400
}
}]
}],
meta: {
hidden: !1,
name: "country",
title: "Country",
type: "dimension",
unit: "country"
},
total: 0
}]
},
dimensionMeta: [{
hidden: !1,
name: "build",
title: "Build",
type: "dimension",
unit: "build"
}, {
hidden: !1,
name: "country",
title: "Country",
type: "dimension",
unit: "country"
}],
dimensions: [{
dimension: "build",
formattedValues: ["1.0", "1.1", "1.2"],
meta: {
hidden: !1,
name: "build",
title: "Build",
type: "dimension",
unit: "build"
},
values: ["1.0", "1.1", "1.2"]
}, {
dimension: "country",
formattedValues: ["United States", "Denmark", "Canada"],
meta: {
hidden: !1,
name: "country",
title: "Country",
type: "dimension",
unit: "country"
},
values: ["US", "DK", "CA"]
}],
filter: [{
dimension: "build",
values: ["1.0", "1.1", "1.2"]
}, {
dimension: "country",
values: ["US", "DK", "CA"]
}],
group: "metric",
interval: {
start: 14015808e5,
end: 14015808e5
},
intervalDisplay: "1. Jun - 30. Jun 2014",
meta: {
subTitle: "",
title: "Player progressions",
type: "string",
unit: "none"
},
type: "bar",
xValues: [{
title: "EnterArea1",
meta: {
category: "design",
event: "Level 1:EnterArea1",
inactive: !1,
keyUnit: void 0,
subEvent: void 0,
title: "EnterArea1",
type: "number",
unit: "none"
},
values: {
diff: 1,
sum: 25432
}
}, {
title: "CompleteArea1",
meta: {
category: "design",
event: "Level 1:CompleteArea1",
inactive: !1,
keyUnit: void 0,
subEvent: void 0,
title: "CompleteArea1",
type: "number",
unit: "none"
},
values: {
diff: .916758415,
sum: 23315
}
}, {
title: "Levelup1",
meta: {
category: "design",
event: "Level 1:Levelup1",
inactive: !1,
keyUnit: void 0,
subEvent: void 0,
title: "Levelup1",
type: "number",
unit: "none"
},
values: {
diff: .845430953,
sum: 21501
}
}, {
title: "EnterArea2",
meta: {
category: "design",
event: "Level 2:EnterArea2",
inactive: !1,
keyUnit: void 0,
subEvent: void 0,
title: "EnterArea2",
type: "number",
unit: "none"
},
values: {
diff: .767576282,
sum: 19521
}
}, {
title: "CompleteArea2",
meta: {
category: "design",
event: "Level 2:CompleteArea2",
inactive: !1,
keyUnit: void 0,
subEvent: void 0,
title: "CompleteArea2",
type: "number",
unit: "none"
},
values: {
diff: .688541994,
sum: 17511
}
}, {
title: " Levelup2",
meta: {
category: "design",
event: "Level 2: Levelup2",
inactive: !1,
keyUnit: void 0,
subEvent: void 0,
title: " Levelup2",
type: "number",
unit: "none"
},
values: {
diff: .596807172,
sum: 15178
}
}],
yMeta: {
type: "percent",
unit: "percent"
}
}
},
statusStates: {}
}];
return demoFunnels
};
return {
getFunnelList: getFunnelList,
getFunnelWithData: getFunnelWithData,
getFunnel: getFunnel,
getCreatedFunnel: getCreatedFunnel,
createFunnelDialog: createFunnelDialog,
renameFunnelDialog: renameFunnelDialog,
duplicateFunnelDialog: duplicateFunnelDialog,
dateRangesDialog: dateRangesDialog,
editPromptDialog: editPromptDialog,
editFunnel: editFunnel,
deleteFunnelDialog: deleteFunnelDialog,
getMetricShortName: getMetricShortName,
getMetricFormattedName: getMetricFormattedName,
getMetricFormatted: getMetricFormatted,
getFunnelAndDateRangeFromBackendId: getFunnelAndDateRangeFromBackendId,
getNewDateRange: getNewDateRange,
hasFunnels: hasFunnels,
saveFunnel: saveFunnel,
processFunnel: processFunnel,
reProcessFunnel: reProcessFunnel,
saveAndProcessFunnel: saveAndProcessFunnel,
getNotification: getNotification,
setNotification: setNotification,
importFunnels: importFunnels,
importedDialog: importedDialog,
summaryDialog: summaryDialog
}
}), angular.module("ga.pages.game.funnel.list", ["ga.pages.game.funnel.controller", "ga.api.meta", "ga.api.data.funnel", "ga.api.userDb.authenticated.game", "ga.ui.modal", "ga.ui.sortable", "ga.services.dialogs", "ga.utils.date", "ga.utils.tracking", "ga.services.user", "ga.components.moment", "ga.pages.game.funnel.view", "ga.ui.notify"]).controller("gaPagesFunnelListController", function($scope, $rootScope, $state, $q, $filter, $interval, gaUiNotify, moment, gaUtilsDate, gaUiModal, gaApiMeta, gaDialogs, gaApiDataFunnel, gaApiUserDbAuthenticatedGame, gaServicesUser, gaFunnelController, gaUtilsTracking) {
$scope.loading = !1, $scope.error = !1, $scope.funnels = [], $scope.hasFunnels = !1, $scope.listFilters = {
status: {
processed: !0,
processing: !0,
failed: !0,
notQueued: !0
},
onlyOwned: !1,
sort: null
};
var game = gaServicesUser.game(parseInt($state.params.gameId, 10));
$scope.isDemo = game.demo, $scope.importing = !1;
var _loadFunnelList = function(reload) {
if ($scope.loading = !0, reload = reload || !1, game && void 0 !== game.old_funnels_imported && !game.old_funnels_imported) {
$scope.importing = !0;
var importStatus = {};
gaFunnelController.importFunnels($state.params.gameId).then(function(status) {
importStatus = status, gaFunnelController.getFunnelList($state.params.gameId, $scope.listFilters, !0).then(function(list) {
$scope.funnels = list, $scope.loading = !1, $scope.importing = !1, $scope.hasFunnels = gaFunnelController.hasFunnels(), importStatus.existingFunnels && gaFunnelController.importedDialog(importStatus), $scope.hasFunnels && _startAutoUpdater()
})
}).catch(function() {
gaFunnelController.getFunnelList($state.params.gameId, $scope.listFilters, reload).then(function(list) {
$scope.funnels = list, $scope.loading = !1, $scope.hasFunnels = gaFunnelController.hasFunnels(), $scope.hasFunnels && _startAutoUpdater()
})
})
} else gaFunnelController.getFunnelList($state.params.gameId, $scope.listFilters, reload).then(function(list) {
$scope.funnels = list, $scope.loading = !1, $scope.hasFunnels = gaFunnelController.hasFunnels(), $scope.hasFunnels && _startAutoUpdater()
}).catch(function() {
$scope.error = !0
})
},
_nbOfStatusFilters = function() {
var nb = 0;
return angular.forEach($scope.listFilters.status, function(stat) {
nb += stat ? 1 : 0
}), nb
};
$scope.filterFunnelList = function(category, value) {
category && ("status" === category ? $scope.listFilters.status[value] = $scope.listFilters.status[value] && _nbOfStatusFilters() > 1 ? !1 : !0 : "onlyOwned" === category && ($scope.listFilters.onlyOwned = !$scope.listFilters.onlyOwned)), _loadFunnelList()
}, $scope.sortFunnelList = function(value) {
$scope.listFilters.sort || ($scope.listFilters.sort = {
name: value,
desc: !1
}), angular.isArray(value) || ($scope.listFilters.sort.name === value ? $scope.listFilters.sort.desc ? $scope.listFilters.sort = null : $scope.listFilters.sort.desc = !0 : ($scope.listFilters.sort.name = value, $scope.listFilters.sort.desc = !1)), _loadFunnelList()
}, $scope.createFunnel = function() {
gaFunnelController.createFunnelDialog().then(function(funnel) {
gaFunnelController.setNotification("Funnel created!"), $scope.editFunnel(funnel.id)
})
}, $scope.editFunnel = function(funnelId) {
$state.go("game.funnel.edit", {
funnelId: funnelId,
gameId: $state.params.gameId
})
}, $scope.showFunnel = function(funnel) {
"processed" === funnel.status.id ? (gaUtilsTracking.trackEvent({
category: "funnels",
action: "view",
label: "processed"
}), $state.go("game.funnel.view", {
funnelId: funnel.id,
dateRangeId: funnel.defaultDateRange,
gameId: $state.params.gameId
})) : "processing" !== funnel.status.id ? (gaUtilsTracking.trackEvent({
category: "funnels",
action: "view",
label: "not-processed"
}), $state.go("game.funnel.edit", {
funnelId: funnel.id,
gameId: $state.params.gameId
})) : (gaUtilsTracking.trackEvent({
category: "funnels",
action: "view",
label: "queued"
}), gaFunnelController.summaryDialog(funnel))
};
var _autoLoader, _startAutoUpdater = function() {
angular.isDefined(_autoLoader) || (_autoLoader = $interval(function() {
_loadFunnelList(!0)
}, 6e4))
},
_stopAutoUpdater = function() {
angular.isDefined(_autoLoader) && ($interval.cancel(_autoLoader), _autoLoader = void 0)
};
_loadFunnelList($state.params.reload);
var noti = gaFunnelController.getNotification();
noti && gaUiNotify.show(noti.message, 4e3, noti.type), $scope.$on("$destroy", function() {
_stopAutoUpdater()
})
}), angular.module("ga.pages.game.funnel.view", ["ga.ui.json", "ga.visualizations.chartCanvas", "ga.visualizations.expandableTable", "ga.utils.transform.chart", "ga.utils.transform.grid", "ga.utils.tracking", "ga.pages.game.funnel.controller", "ga.ui.notify", "ga.ui.tour", "ga.services.user"]).controller("gaPagesFunnelViewController", function($scope, $rootScope, $state, $q, $filter, $timeout, $cookies, gaUiTour, gaServicesUser, gaUtilsTracking, gaUiNotify, gaFunnelController, gaUtilsChartTransform, gaUtilsGridTransform) {
$scope.loading = !1, $scope.error = !1, $scope.backendError = !1, $scope.noData = !1, $scope.activeFunnel = null, $scope.activeDateRange = null, $scope.settings = {
activeDimension: 0,
tableGrouping: "metric"
}, $scope.processStates = {
errors: null,
processing: null
}, $scope.showFilters = !1, $scope.chart = {
settings: {}
}, $scope.table = {
settings: {
maxRows: 15,
maxCols: 50,
pager: !0,
colPager: !1,
sortable: !0,
more: !1,
totals: !1,
compare: !1,
compareHidden: !0,
tooltipEnabled: !0,
tooltipType: "metric"
},
tableView: "list",
datasets: null
}, $scope.isDemo = gaServicesUser.game(parseInt($state.params.gameId, 10)).demo;
var _loadFunnel = function(funnelId, dateRangeId) {
$scope.loading = !0, isNaN(funnelId) ? gaFunnelController.getFunnelAndDateRangeFromBackendId(funnelId).then(function(result) {
result && result.funnelId && result.dateRangeId ? $state.go("game.funnel.view", {
funnelId: result.funnelId,
dateRangeId: result.rangeId,
gameId: $state.params.gameId
}) : ($scope.loading = !1, $scope.backendError = !0, $scope.error = !0)
}).catch(function() {
$scope.loading = !1, $scope.backendError = !0, $scope.error = !0
}) : dateRangeId ? gaFunnelController.getFunnelWithData($state.params.gameId, funnelId, dateRangeId).then(function(funnel) {
dateRangeId = parseInt(dateRangeId, 10), funnel.dateRanges && funnel.dateRanges.length && funnel.dateRanges.forEach(function(dr) {
dr.id === dateRangeId && ($scope.activeDateRange = dr)
}), $scope.activeFunnel = funnel, _processStatus(), funnel.query ? ($scope.chart.settings = gaUtilsChartTransform.canvasChartSettings(funnel.query, "funnel", !0), $scope.switchTableDimension(0), $scope.error = !1, $timeout(function() {
angular.element(".axis.x-axis span b").each(function(index, element) {
element.offsetWidth < element.scrollWidth && angular.element(element).addClass("pre-text-overflow")
})
}, 100), $scope.isDemo || $timeout(function() {
gaServicesUser.onboarding.funnels && gaServicesUser.onboarding.funnels.view_tour || (gaServicesUser.onboarding.set("funnels", "view_tour", !0), gaServicesUser.onboarding.save(), runTour())
}, 10)) : ($scope.error = !0, $scope.activeFunnel.status && "processed" === $scope.activeFunnel.status.id && !$scope.activeFunnel.status.hasData && ($scope.noData = !0)), $scope.loading = !1
}).catch(function(errFunnel) {
errFunnel && ($scope.activeFunnel = errFunnel), $scope.loading = !1, $scope.error = !0
}) : gaFunnelController.getFunnel($state.params.gameId, funnelId).then(function(funnel) {
var rangeId = null;
return funnel.dateRanges && funnel.dateRanges.some(function(dr) {
return dr.status && "processed" === dr.status.id ? (rangeId = dr.id, !0) : void 0
}), rangeId ? void $state.go("game.funnel.view", {
funnelId: funnelId,
dateRangeId: rangeId,
gameId: $state.params.gameId
}) : !1
}).catch(function(errFunnel) {
errFunnel && ($scope.activeFunnel = errFunnel), $scope.loading = !1, $scope.error = !0
})
},
_init = function() {
$state.params.funnelId && _loadFunnel($state.params.funnelId, $state.params.dateRangeId)
},
_processStatus = function() {
var errors = [],
processing = [],
processed = [];
$scope.activeFunnel.dateRanges ? ($scope.activeFunnel.dateRanges.forEach(function(dr) {
dr.status && "failed" === dr.status.id && errors.push(dr.dateRangeFormatted), dr.status && "processing" === dr.status.id && processing.push(dr.dateRangeFormatted), dr.status && "processed" === dr.status.id && processed.push(dr.dateRangeFormatted)
}), $scope.processStates.errors = errors.join(", "), $scope.processStates.processing = processing.join(", "), $scope.processStates.processed = processed.join(", ")) : ($scope.processStates.errors = null, $scope.processStates.processing = null, $scope.processStates.processed = null)
},
_reloadTable = function() {
$scope.table.datasets = gaUtilsGridTransform.parse($scope.activeFunnel.query.left), $scope.table.dataChanged = {
dataChange: !0
}
},
_sanitizeCsvStr = function(string) {
return string.replace(/\*/g, "all").replace(/[^a-z0-9]/gi, "_").replace(/_{2,}/g, "_").toLowerCase()
},
_getCsvDataForFilter = function(filterIndex, byDimension) {
byDimension = byDimension || !1;
var dataQuery = angular.copy($scope.activeFunnel.query.left);
dataQuery.activeDimensionIndex = filterIndex;
var totals = !1;
byDimension ? (dataQuery.group = "metric-dimension", totals = !0) : dataQuery.group = "metric";
var dataset = gaUtilsGridTransform.parse(dataQuery),
csv = _getCsvData(dataset, totals);
return csv
},
_getCsvData = function(dataset, totals) {
var csv = [],
header = [dataset.header.axis.value];
if (angular.forEach(dataset.header.values, function(value) {
header.push(value.value)
}), csv.push(header), angular.forEach(dataset.rows, function(row) {
var tmpRow = [];
tmpRow.push("date" === dataset.meta.axis.type ? $filter("formatUnitType")(row.axis.value, "rawdate") : row.axis.fullValue ? row.axis.fullValue.replace(/,/g, "") : row.axis.value), angular.forEach(row.values, function(value) {
tmpRow.push(value.value)
}), csv.push(tmpRow)
}), totals) {
var footer = [dataset.footer.axis.value];
angular.forEach(dataset.footer.values, function(value) {
footer.push(value.value)
}), csv.push(footer)
}
return csv = csv.map(function(row) {
return row.join(";")
}), csv = csv.join("\n")
};
_init(), $scope.switchTableGrouping = function(newView) {
newView !== $scope.settings.tableGrouping && ($scope.settings.tableGrouping = newView, "dimension" === $scope.settings.tableGrouping ? ($scope.activeFunnel.query.left.group = "metric-dimension", $scope.table.settings.totals = !0) : ($scope.activeFunnel.query.left.group = "metric", $scope.table.settings.totals = !1), _reloadTable())
}, $scope.switchTableDimension = function(dimensionIndex) {
$scope.settings.activeDimension = dimensionIndex, $scope.activeFunnel.query.left.activeDimensionIndex = dimensionIndex, _reloadTable()
}, $scope.showFunnelList = function() {
$state.go("game.funnel.list", {
gameId: $state.params.gameId
})
}, $scope.selectDateRange = function(dateRange) {
dateRange.status && "processed" === dateRange.status.id && $state.go("game.funnel.view", {
funnelId: $state.params.funnelId,
dateRangeId: dateRange.id,
gameId: $state.params.gameId
})
}, $scope.createFunnel = function() {
$scope.isDemo || gaFunnelController.createFunnelDialog().then(function(funnel) {
gaFunnelController.setNotification("Funnel created!"), $state.go("game.funnel.edit", {
funnelId: funnel.id,
gameId: $state.params.gameId
})
})
}, $scope.editErrorFunnel = function() {
$scope.isDemo || gaFunnelController.editFunnel($state.params.funnelId)
}, $scope.editFunnel = function() {
$scope.isDemo || gaFunnelController.editPromptDialog($scope.activeFunnel)
}, $scope.duplicate = function() {
$scope.isDemo || gaFunnelController.duplicateFunnelDialog($scope.activeFunnel).then(function(funnel) {
funnel && funnel.id && (gaFunnelController.setNotification("Funnel duplicated!"), $state.go("game.funnel.edit", {
funnelId: funnel.id,
gameId: $state.params.gameId
}))
})
}, $scope.rename = function() {
$scope.isDemo || gaFunnelController.renameFunnelDialog($scope.activeFunnel).then(function(funnel) {
$scope.activeFunnel.name = funnel.name, gaUiNotify.show("Funnel renamed", 4e3, "default")
})
}, $scope.delete = function() {
$scope.isDemo || gaFunnelController.deleteFunnelDialog($scope.activeFunnel).then(function() {
gaFunnelController.setNotification("Funnel deleted!"), $scope.showFunnelList()
}).catch(function(error) {
error && "closed" === error.action || gaUiNotify.show("Error deleting funnel", 4e3, "warning")
})
}, $scope.export = function() {
if (!$scope.loading && !$scope.isDemo) {
var data_json = {},
name = "funnel_" + $scope.activeFunnel.name + "_" + $scope.activeDateRange.dateRangeFormatted;
$scope.activeFunnel.filters && $scope.activeFunnel.filters.length ? $scope.activeFunnel.filters.forEach(function(filter, index) {
data_json[_sanitizeCsvStr(filter.meta.title) + "_by_step.csv"] = _getCsvDataForFilter(index), data_json[_sanitizeCsvStr(filter.meta.title) + "_by_dimension.csv"] = _getCsvDataForFilter(index, !0)
}) : data_json[_sanitizeCsvStr(name) + ".csv"] = _getCsvData($scope.table.datasets), name = _sanitizeCsvStr(name) + ".zip";
var csv_data_json = JSON.stringify(data_json),
form = angular.element("#funnel-export-csv-form");
form.find("[name=X-Authorization]").val(gaServicesUser.token), form.find("[name=zip_name]").val(name), form.find("[name=csv_data_json]").val(csv_data_json), form.submit(), gaUtilsTracking.trackPageRaw("/game/export/funnel")
}
}, $scope.editDateRanges = function() {
$scope.isDemo || gaFunnelController.dateRangesDialog($scope.activeFunnel, !0).then(function(result) {
var funnel = result.funnel;
if (funnel)
if (funnel.dateRanges && funnel.dateRanges.length > 0) {
var found = !1;
if (funnel.dateRanges.some(function(dr) {
return dr.id === parseInt($state.params.dateRangeId, 10) ? (found = !0, !0) : void 0
}), $scope.activeFunnel.dateRanges = funnel.dateRanges, _processStatus(), found) "empty" === result.status && result.dateRange && result.dateRange.id !== parseInt($state.params.dateRangeId, 10) && (gaFunnelController.setNotification("Your funnel was processed!"), $state.go("game.funnel.view", {
funnelId: $state.params.funnelId,
gameId: $state.params.gameId,
dateRangeId: result.dateRange.id
}));
else {
var newDateRange = null;
if (funnel.dateRanges.some(function(dr) {
if (dr.status && "processed" === dr.status.id) {
if (dr.status.hasData) return newDateRange = dr, !0;
newDateRange || (newDateRange = dr)
}
}), newDateRange) $state.go("game.funnel.view", {
funnelId: $state.params.funnelId,
gameId: $state.params.gameId,
dateRangeId: newDateRange.id
});
else {
var processing = funnel.dateRanges.filter(function(dr) {
return dr.status && "processing" === dr.status.id
}).length;
processing === funnel.dateRanges.length && $scope.showFunnelList();
var failed = funnel.dateRanges.filter(function(dr) {
return dr.status && "failed" === dr.status.id
}).length;
failed === funnel.dateRanges.length && $scope.editErrorFunnel()
}
}
} else $scope.editErrorFunnel()
})
}, $scope.editFailedDateRanges = function() {
$scope.isDemo || gaFunnelController.dateRangesDialog($scope.activeFunnel, !0, !0).then(function(result) {
result.funnel && ($scope.activeFunnel.dateRanges = result.funnel.dateRanges, _processStatus())
})
}, $scope.toggleFilters = function() {
$scope.showFilters = !$scope.showFilters
};
var runTour = function() {
gaUiTour.reset(), gaUiTour.addStep({
title: "Add new date ranges",
text: "You can add new date ranges to your funnel and manage existing ones.",
element: ".popdown.ga-icon-calendar",
watcher: {},
arrow: "top-left",
anchor: "bottom-center",
position: "fixed",
zindex: 1200,
skippable: !0
}), gaUiTour.addStep({
title: "Conversion",
text: "This is a summary of the players that converted through all steps of your funnel.",
element: ".overview-container",
watcher: {},
arrow: "top-center",
anchor: "top-center",
zindex: 1200,
skippable: !0
}), gaUiTour.addStep({
title: "Want to make changes?",
text: "You can at any time make changes to the steps or filters, however be aware that existing date ranges will need to be re-processed.",
element: ".ga-btn.blue.small",
watcher: {},
arrow: "top-right",
anchor: "bottom-right-center",
zindex: 1200,
position: "fixed",
skippable: !0
}), gaUiTour.setOptions({
navigation: !1
}), gaUiTour.start()
},
noti = gaFunnelController.getNotification();
noti && gaUiNotify.show(noti.message, 4e3, noti.type)
}), angular.module("ga.pages.game.funnel.edit", ["ga.api.meta", "ga.pages.game.funnel.controller", "ga.pages.game.funnel.dialogs.process", "ga.pages.game.funnel.view", "ga.ui.modal", "ga.ui.sortable", "ga.ui.notify", "ga.ui.tour", "ga.services.dialogs", "ga.components.moment", "ga.services.user"]).controller("gaPagesFunnelEditController", function($scope, $rootScope, $state, $q, $filter, $timeout, moment, gaServicesUser, gaUiNotify, gaUiTour, gaUiModal, gaApiMeta, gaDialogs, gaFunnelController) {
$scope.loading = !1, $scope.creation = !1, $scope.activeFunnel = null;
var _processingRequest = !1;
$scope.uiValues = {
filterBlacklist: [],
addLastMetricStr: ""
};
var ignoreSaveState = !1,
orgFunnel = null;
$scope.addMetric = function() {
var blacklisted = $scope.activeFunnel.metrics.map(function(m) {
return m.category + ":" + m.event
});
gaDialogs.metricPicker(null, null, {
categories: ["design", "business"],
disableStarEvents: !0,
blacklisted: blacklisted
}).then(function(metric) {
metric && (metric.formatted = gaFunnelController.getMetricFormatted(metric.event, metric.category), metric.shortName = metric.formatted.name, metric.added = !0, $scope.activeFunnel.metrics.push(metric), _checkMetricStatus(), $timeout(function() {
angular.element(".steps").scrollLeft(1e7)
}, 1))
}).catch(function() {})
}, $scope.editMetric = function(index) {
var currentMetric = $scope.activeFunnel.metrics[index],
blacklisted = $scope.activeFunnel.metrics.filter(function(m, mindex) {
return mindex !== index
}).map(function(m) {
return m.category + ":" + m.event
});
gaDialogs.metricPicker(currentMetric.event, currentMetric.category, {
categories: ["design", "business"],
disableStarEvents: !0,
blacklisted: blacklisted
}).then(function(metric) {
metric && (metric.formatted = gaFunnelController.getMetricFormatted(metric.event, metric.category), metric.shortName = metric.formatted.name, $scope.activeFunnel.metrics[index] = metric)
}).catch(function() {})
}, $scope.removeMetric = function(index) {
$scope.activeFunnel.metrics.forEach(function(m) {
void 0 !== m.added && delete m.added
}), $timeout(function() {
$scope.activeFunnel.metrics.splice(index, 1), _checkMetricStatus()
}, 1)
}, $scope.removeFilter = function(filter) {
var foundIndex = _getFilterIndex(filter);
foundIndex > -1 && ($scope.activeFunnel.filters.splice(foundIndex, 1), _updateFilterBlacklist(), _setUiValues())
}, $scope.removeExclude = function(exclude) {
var foundIndex = _getExcludeIndex(exclude);
foundIndex > -1 && ($scope.activeFunnel.excludes.splice(foundIndex, 1), _updateFilterBlacklist(), _setUiValues())
}, $scope.removeFilterValue = function(filter, valueIndex) {
var filterIndex = _getFilterIndex(filter);
$scope.activeFunnel.filters[filterIndex] && ($scope.activeFunnel.filters[filterIndex].values.splice(valueIndex, 1), $scope.activeFunnel.filters[filterIndex].formattedValues.splice(valueIndex, 1), $scope.activeFunnel.filters[filterIndex].values.length || $scope.activeFunnel.filters.splice(filterIndex, 1)), _updateFilterBlacklist(), _setUiValues()
}, $scope.removeExcludeValue = function(exclude, valueIndex) {
var excludeIndex = _getExcludeIndex(exclude);
$scope.activeFunnel.excludes[excludeIndex] && ($scope.activeFunnel.excludes[excludeIndex].values.splice(valueIndex, 1), $scope.activeFunnel.excludes[excludeIndex].formattedValues.splice(valueIndex, 1), $scope.activeFunnel.excludes[excludeIndex].values.length || $scope.activeFunnel.excludes.splice(excludeIndex, 1)), _updateFilterBlacklist(), _setUiValues()
}, $scope.createFunnel = function() {
gaFunnelController.createFunnelDialog().then(function(funnel) {
gaFunnelController.setNotification("Funnel created!"), $scope.editFunnel(funnel.id)
})
}, $scope.editFunnel = function(funnelId) {
$state.go("game.funnel.edit", {
funnelId: funnelId,
gameId: $state.params.gameId
})
};
var _editFunnel = function(funnelId) {
$scope.loading = !0, 0 === funnelId && ($scope.creation = !0);
var prom = 0 === funnelId ? gaFunnelController.getCreatedFunnel() : gaFunnelController.getFunnel($state.params.gameId, funnelId);
$q.all(prom).then(function(funnel) {
funnel.status && "processed" === funnel.status.id ? $scope.showFunnelList() : (orgFunnel = angular.copy(funnel), $scope.activeFunnel = funnel, $scope.loading = !1, _updateFilterBlacklist(), _checkMetricStatus(), _setUiValues(), $timeout(function() {
gaServicesUser.onboarding.funnels && gaServicesUser.onboarding.funnels.edit_tour || (gaServicesUser.onboarding.set("funnels", "edit_tour", !0), gaServicesUser.onboarding.save(), runTour())
}, 10))
}).catch(function() {
$scope.showFunnelList()
})
},
_updateFilterBlacklist = function() {
var blacklist = [];
$scope.activeFunnel.filters && (blacklist = $scope.activeFunnel.filters.map(function(filter) {
return filter.dimension
})), $scope.uiValues.excludeBlacklist = blacklist;
var eblacklist = [];
$scope.activeFunnel.excludes && $scope.activeFunnel.excludes.some(function(filter) {
-1 === eblacklist.indexOf(filter.dimension) && eblacklist.push(filter.dimension)
}), $scope.uiValues.filterBlacklist = eblacklist
},
_setUiValues = function() {
$scope.uiValues.filter = $scope.activeFunnel.filters ? $scope.activeFunnel.filters.map(function(filter) {
return {
dimension: filter.dimension,
values: filter.values
}
}) : [], $scope.uiValues.exclude = $scope.activeFunnel.excludes ? $scope.activeFunnel.excludes.map(function(filter) {
return {
dimension: filter.dimension,
values: filter.values
}
}) : []
};
$scope.saveFunnel = function() {
0 !== $scope.activeFunnel.metrics.length && _saveDialog().then(function(result) {
result && result.processed ? (ignoreSaveState = !0, result.status && "failed" === result.status ? (gaUiNotify.show("Error saving and processing funnel", 4e3, "warning"), orgFunnel = angular.copy(result.funnel), $scope.activeFunnel = result.funnel) : result.status && "empty" === result.status && result.dateRange ? (gaFunnelController.setNotification("Your funnel was processed!"), $state.go("game.funnel.view", {
funnelId: result.funnel.id,
dateRangeId: result.funnel.dateRange.id,
gameId: $state.params.gameId
})) : (gaFunnelController.setNotification("Your funnel is processing! We'll notify you by email when it's done."), $scope.showFunnelList(!0))) : result ? 0 === orgFunnel.id && 0 !== result.funnel.id ? (ignoreSaveState = !0, gaFunnelController.setNotification("Funnel saved!"), $state.go("game.funnel.edit", {
funnelId: result.funnel.id,
gameId: $state.params.gameId
})) : (orgFunnel = angular.copy(result.funnel), $scope.activeFunnel = result.funnel, gaUiNotify.show("Funnel saved!", 4e3, "default")) : gaUiNotify.show("Error saving funnel", 4e3, "warning")
}).catch(function(error) {
error && gaUiNotify.show("Error saving funnel", 4e3, "warning")
})
};
var _saveDialog = function() {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/game/funnel/dialogs/process-funnel.html",
controller: "gaPagesFunnelProcessController",
width: 800,
parameters: {
funnel: $scope.activeFunnel
}
})
};
$scope.showFunnelList = function(reload) {
reload ? $state.go("game.funnel.list", {
gameId: $state.params.gameId
}) : $state.go("game.funnel.list", {
gameId: $state.params.gameId
})
}, $scope.save = function(state) {
_processingRequest || (_processingRequest = !0, gaFunnelController.saveFunnel($scope.activeFunnel).then(function(funnel) {
$scope.creation ? (ignoreSaveState = !0, gaUiNotify.show("Funnel saved", 4e3, "default"), $state.go("game.funnel.edit", {
funnelId: funnel.id,
gameId: $state.params.gameId
})) : (orgFunnel = angular.copy(funnel), $scope.activeFunnel = funnel, gaUiNotify.show("Funnel saved", 4e3, "default"), state && $state.go(state.state, state.params)), _processingRequest = !1
}).catch(function() {
gaUiNotify.show("Error saving funnel", 4e3, "warning")
}))
}, $scope.duplicate = function() {
_processingRequest || (_processingRequest = !0, gaFunnelController.duplicateFunnelDialog($scope.activeFunnel).then(function(funnel) {
funnel && funnel.id ? (gaFunnelController.setNotification("Funnel duplicated!"), $state.go("game.funnel.edit", {
funnelId: funnel.id,
gameId: $state.params.gameId
})) : _processingRequest = !1
}).catch(function() {
_processingRequest = !1
}))
}, $scope.rename = function() {
_processingRequest || (_processingRequest = !0, gaFunnelController.renameFunnelDialog($scope.activeFunnel).then(function(funnel) {
orgFunnel = angular.copy(funnel), $scope.activeFunnel = funnel, gaUiNotify.show("Funnel renamed", 4e3, "default"), _processingRequest = !1
}).catch(function() {
_processingRequest = !1
}))
}, $scope.delete = function() {
_processingRequest || gaFunnelController.deleteFunnelDialog($scope.activeFunnel).then(function() {
gaFunnelController.setNotification("Funnel deleted!"), $scope.showFunnelList()
}).catch(function(error) {
error && "closed" === error.action || gaUiNotify.show("Error deleting funnel", 4e3, "warning"), _processingRequest = !1
})
}, $scope.editDateRanges = function() {
_processingRequest || gaFunnelController.dateRangesDialog($scope.activeFunnel, !1).then(function(result) {
result.funnel && (orgFunnel = angular.copy(result.funnel), $scope.activeFunnel = result.funnel), _processingRequest = !1
})
};
var _getFilterIndex = function(filter) {
var foundIndex = null;
return $scope.activeFunnel.filters.some(function(f, index) {
return f.dimension === filter.dimension ? (foundIndex = index, !0) : void 0
}), foundIndex
},
_getExcludeIndex = function(exclude) {
var foundIndex = null;
return $scope.activeFunnel.excludes.some(function(f, index) {
return f.dimension === exclude.dimension ? (foundIndex = index, !0) : void 0
}), foundIndex
},
_init = function() {
_editFunnel(parseInt($state.params.funnelId, 10));
var noti = gaFunnelController.getNotification();
noti && gaUiNotify.show(noti.message, 4e3, noti.type)
},
_checkMetricStatus = function() {
$scope.uiValues.addLastMetricStr = 1 === $scope.activeFunnel.metrics.length ? "You need min. 2 steps to start processing the funnel" : ""
};
$scope.$on("$stateChangeStart", function(event, toState, toParams) {
angular.equals(orgFunnel, $scope.activeFunnel) || ignoreSaveState || (event.preventDefault(), gaUiNotify.loading(!1), gaUiModal.show({
scope: $scope,
width: 500,
header: "Unsaved changes",
content: "Do you want to save the changes you made or discard the changes and leave the page?",
buttons: [{
title: "Save",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "save"
}, {
title: "Discard changes",
"class": "ga-btn-text right",
keyCode: 27,
action: "go"
}],
actions: {
go: function() {
ignoreSaveState = !0, $state.go(toState, toParams)
},
save: function() {
ignoreSaveState = !0, $scope.save({
state: toState,
params: toParams
})
}
}
}))
});
var runTour = function() {
gaUiTour.reset(), $scope.activeFunnel.metrics && 0 !== $scope.activeFunnel.metrics.length || gaUiTour.addStep({
title: "Start building",
text: "Funnels can be built using only your custom design or business events. Please choose first event to start building.",
element: "#first-step-creation",
watcher: {},
arrow: "right-top",
anchor: "left-center",
zindex: 1200,
skippable: !0,
doneFn: function() {
var deferred = $q.defer();
return this.watcher = $scope.$watch("activeFunnel.metrics", function(newVal, oldVal) {
newVal !== oldVal && deferred.resolve()
}, !0), deferred.promise
},
hideFn: function() {
var deferred = $q.defer();
return angular.element(this.element).on("mousedown.tour", function() {
deferred.resolve()
}), deferred.promise
},
destroyFn: function() {
angular.element(this.element).off("mousedown.tour"), this.watcher()
}
}), gaUiTour.addStep({
title: "Create segments",
text: "You can choose to process your funnel based on players from certain dimensions by either including or excluding these values.",
element: ".popdown.ga-icon-filter ",
clickElement: ".popdown.ga-icon-filter > span",
watcher: {},
arrow: "top-left",
anchor: "bottom-center",
position: "fixed",
zindex: 1200,
skippable: !0,
doneDefered: null,
doneFn: function() {
return this.doneDefered = $q.defer(), this.watcher = $scope.$watch("activeFunnel.filters", function(newVal, oldVal) {
newVal !== oldVal && this.doneDefered.resolve()
}.bind(this), !0), this.doneDefered.promise
},
hideDoneFn: function() {
angular.element(this.clickElement).on("mousedown.tour", function() {
this.doneDefered.resolve()
}.bind(this))
},
hideFn: function() {
var deferred = $q.defer();
return angular.element(this.clickElement).on("mousedown.tour", function() {
deferred.resolve(), angular.element(this.clickElement).off("mousedown.tour"), this.hideDoneFn()
}.bind(this)), deferred.promise
},
destroyFn: function() {
angular.element(this.clickElement).off("mousedown.tour"), this.watcher()
}
}), gaUiTour.addStep({
title: "Processing",
text: "To get results from your funnel, you need to select a date range to process from. As soon as it's done , we'll notify you by email.",
element: "#start-processing",
watcher: {},
arrow: "top-right",
anchor: "bottom-right-center",
zindex: 1200,
position: "fixed",
skippable: !0,
hideFn: function() {
var deferred = $q.defer();
return angular.element(this.element).on("mousedown.tour", function() {
deferred.resolve()
}), deferred.promise
},
destroyFn: function() {
angular.element(this.element).off("mousedown.tour")
}
}), gaUiTour.setOptions({
navigation: !1
}), gaUiTour.start()
},
game = gaServicesUser.game(parseInt($state.params.gameId, 10));
game.demo ? $scope.showFunnelList() : _init(), $scope.$watch("uiValues.filter", function(newVal) {
newVal && newVal.length && ($scope.activeFunnel.filters = [], angular.forEach(newVal, function(filter) {
var tmpFilter = angular.copy(filter);
tmpFilter.meta = gaApiMeta.getDimension(filter.dimension) || {
type: ""
}, tmpFilter.formattedValues = tmpFilter.values.map(function(value) {
return "*" === value ? "All" : $filter("formatUnitType")(gaApiMeta.getDimension(value).title, tmpFilter.meta.type)
}), $scope.activeFunnel.filters.push(tmpFilter)
}), _updateFilterBlacklist())
}), $scope.$watch("uiValues.exclude", function(newVal) {
newVal && newVal.length && ($scope.activeFunnel.excludes = [], angular.forEach(newVal, function(filter) {
var tmpFilter = angular.copy(filter);
tmpFilter.meta = gaApiMeta.getDimension(filter.dimension) || {
type: ""
}, tmpFilter.formattedValues = tmpFilter.values.map(function(value) {
return "*" === value ? "All" : $filter("formatUnitType")(gaApiMeta.getDimension(value).title, tmpFilter.meta.type)
}), $scope.activeFunnel.excludes.push(tmpFilter)
}), _updateFilterBlacklist())
})
}), angular.module("ga.pages.game.funnel.dialogs.process", ["ga.pages.game.funnel.controller", "ga.ui.notify", "ga.ui.datepicker", "ga.utils.date"]).controller("gaPagesFunnelProcessController", function($scope, $state, $timeout, gaUtilsDate, gaFunnelController, gaUiNotify) {
$scope.tmpFunnel = angular.copy($scope.funnel), $scope.saving = !1;
var _restrictDate = {
start: 13963104e5,
end: 1e3 * gaUtilsDate.moment.utc().add(-48, "hours").unix()
};
$scope.uiValues = {
dateRange: {
main: {},
restrictInterval: _restrictDate
},
dateRanges: angular.copy($scope.tmpFunnel.dateRanges),
filters: [],
excludes: [],
metrics: [],
metricsStr: [],
processAllowed: !1
}, $scope.uiValues.metrics = $scope.tmpFunnel.metrics.map(function(metric) {
return $scope.uiValues.metricsStr.push(metric.shortName), {
shortName: metric.shortName,
cleanPath: metric.formatted ? metric.formatted.cleanPath : ""
}
}), $scope.uiValues.metricStr = $scope.uiValues.metricsStr.join(" > "), $scope.tmpFunnel.filters && ($scope.uiValues.filters = $scope.tmpFunnel.filters.map(function(filter) {
return {
name: filter.meta.parent ? filter.meta.parent.title + " > " + filter.meta.title : filter.meta.title,
strValue: filter.formattedValues.splice(0, 6).join(", "),
strValueAll: filter.formattedValues.join(", "),
count: filter.values.length - 6
}
})), $scope.tmpFunnel.excludes && ($scope.uiValues.excludes = $scope.tmpFunnel.excludes.map(function(filter) {
return {
name: filter.meta.parent ? filter.meta.parent.title + " > " + filter.meta.title : filter.meta.title,
strValue: filter.formattedValues.splice(0, 6).join(", "),
strValueAll: filter.formattedValues.join(", "),
count: filter.values.length - 6
}
})), $scope.creation = 0 === $scope.tmpFunnel.id, $scope.saveBtnTitle = "Save funnel", $scope.saveAndQueueBtnTitle = "Start processing";
var _addRange = function() {
if ($scope.uiValues.dateRange.main && $scope.uiValues.dateRange.main.start && $scope.uiValues.dateRange.main.end) {
var tmpRanges = [];
$scope.uiValues.dateRanges && $scope.uiValues.dateRanges.length && $scope.uiValues.dateRanges.forEach(function(dr) {
tmpRanges.push(dr)
});
var tmpRange = gaFunnelController.getNewDateRange($scope.uiValues.dateRange.main, tmpRanges);
tmpRange && 0 === tmpRange.id && $scope.uiValues.dateRanges.unshift(tmpRange), $scope.uiValues.dateRange = {
main: {},
restrictInterval: _restrictDate
}
}
_checkProcessingState()
};
$scope.removeRange = function(index) {
$scope.uiValues.dateRanges[index] && $scope.uiValues.dateRanges[index].id > 0 ? $scope.uiValues.dateRanges[index].delete = !0 : $scope.uiValues.dateRanges.splice(index, 1), _checkProcessingState()
}, $scope.save = function(queueIt) {
if (!$scope.saving) {
$scope.saving = !0;
var saveTmpFunnel = angular.copy($scope.tmpFunnel);
saveTmpFunnel.dateRanges = [], $scope.uiValues.dateRanges && $scope.uiValues.dateRanges.forEach(function(dr) {
saveTmpFunnel.dateRanges.push(dr)
}), $timeout(function() {
_doSave(saveTmpFunnel, queueIt)
}, 1)
}
};
var _doSave = function(saveTmpFunnel, queueIt) {
return $scope.readOnly ? void 0 : queueIt ? gaFunnelController.saveAndProcessFunnel(saveTmpFunnel).then(function(results) {
$scope._resolve({
funnel: results.funnel,
processed: !0,
status: results.status,
dateRange: results.dateRange
})
}).catch(function(error) {
error ? (gaUiNotify.show("Error saving and processing funnel", 4e3, "warning"), $scope.saving = !1) : $scope._reject()
}) : gaFunnelController.saveFunnel(saveTmpFunnel).then(function(results) {
$scope._resolve({
funnel: results,
processed: !1
})
}).catch(function(error) {
error ? (gaUiNotify.show("Error saving funnel", 4e3, "warning"), $scope.saving = !1) : $scope._reject()
})
},
_checkProcessingState = function() {
$scope.uiValues.processAllowed = !1, $scope.uiValues.dateRanges && $scope.uiValues.dateRanges.length && $scope.uiValues.dateRanges.some(function(dr) {
return dr.delete ? void 0 : void($scope.uiValues.processAllowed = !0)
})
};
_checkProcessingState(), $scope.$watch("uiValues.dateRange", function(newVal, oldVal) {
newVal && newVal !== oldVal && !angular.equals({}, newVal) && newVal.main && newVal.main.start && _addRange()
})
}), angular.module("ga.pages.game.funnel.dialogs.list-date-ranges", ["ga.ui.datepicker", "ga.services.user", "ga.utils.date", "ga.pages.game.funnel.controller"]).controller("gaPagesFunnelListDateRangesController", function($scope, $state, $timeout, gaServicesUser, gaUtilsDate, gaFunnelController) {
var _tmpFunnel = angular.copy($scope.funnel);
$scope.saving = !1, $scope.tmpDateRanges = [], _tmpFunnel.dateRanges && _tmpFunnel.dateRanges.length && ($scope.tmpDateRanges = $scope.failedOnly ? _tmpFunnel.dateRanges.filter(function(dr) {
return dr.status && "failed" === dr.status.id
}).map(function(dr) {
return dr.delete = !1, dr
}) : _tmpFunnel.dateRanges.map(function(dr) {
return dr.delete = !1, dr
}));
var _restrictDate = {
start: 13963104e5,
end: 1e3 * gaUtilsDate.moment.utc().add(-48, "hours").unix()
};
$scope.uiValues = {
dateRange: {
main: {},
restrictInterval: _restrictDate
},
dateRanges: [],
changes: !1
}, $scope.saveBtnTitle = "Save funnel", $scope.saveAndQueueBtnTitle = "Save changes";
var _addRange = function() {
if ($scope.tmpDateRanges && $scope.uiValues.dateRange.main && $scope.uiValues.dateRange.main.start && $scope.uiValues.dateRange.main.end) {
var tmpRange = gaFunnelController.getNewDateRange($scope.uiValues.dateRange.main, $scope.tmpDateRanges);
tmpRange && $scope.tmpDateRanges.unshift(tmpRange), $scope.uiValues.dateRange = {
main: {},
restrictInterval: _restrictDate
}, _checkState()
}
},
_checkState = function() {
var added = $scope.tmpDateRanges.filter(function(f) {
return 0 === f.id || f.status && "notQueued" === f.status.id
}).length,
deleted = $scope.tmpDateRanges.filter(function(f) {
return f.delete
}).length,
failed = $scope.tmpDateRanges.filter(function(f) {
return !f.delete && f.status && "failed" === f.status.id
}).length;
$scope.process && !$scope.failedOnly ? added && deleted ? ($scope.uiValues.changes = !0, $scope.saveAndQueueBtnTitle = "Start processing") : added || failed ? ($scope.uiValues.changes = !0, $scope.saveAndQueueBtnTitle = "Start processing") : deleted ? ($scope.uiValues.changes = !0, $scope.saveAndQueueBtnTitle = "Save changes") : ($scope.saveAndQueueBtnTitle = "Save changes", $scope.uiValues.changes = !1) : $scope.failedOnly ? deleted === $scope.tmpDateRanges.length ? ($scope.uiValues.changes = !0, $scope.saveAndQueueBtnTitle = "Save changes") : ($scope.uiValues.changes = !0, $scope.saveAndQueueBtnTitle = "Start processing") : added || deleted ? ($scope.uiValues.changes = !0, $scope.saveAndQueueBtnTitle = "Save changes") : ($scope.uiValues.changes = !1, $scope.saveAndQueueBtnTitle = "Save changes")
};
$scope.removeRange = function(index) {
0 === $scope.tmpDateRanges[index].id ? $scope.tmpDateRanges.splice(index, 1) : $scope.tmpDateRanges[index].delete = !0, _checkState()
}, $scope.save = function() {
$scope.saving || ($scope.saving = !0, $timeout(function() {
_doSave()
}, 1))
};
var _doSave = function() {
_tmpFunnel.dateRanges = $scope.tmpDateRanges.filter(function(dr) {
return dr.delete || 0 === dr.id || $scope.process && dr.status && "failed" === dr.status.id
}).map(function(dr) {
return {
id: dr.id,
"delete": dr.delete,
backendId: "",
start: dr.start,
end: dr.end
}
}), gaFunnelController.saveFunnel(_tmpFunnel, !1).then(function(savedFunnel) {
$scope.process && savedFunnel.dateRanges && savedFunnel.dateRanges.length > 0 ? gaFunnelController.reProcessFunnel(savedFunnel).then(function(processResults) {
$scope._resolve(processResults)
}) : $scope._resolve({
funnel: savedFunnel,
status: "not_processed"
})
}).catch(function() {
$scope._reject()
})
};
$scope.$watch("uiValues.dateRange", function(newVal, oldVal) {
newVal && newVal !== oldVal && !angular.equals({}, newVal) && _addRange()
}), _checkState()
}), angular.module("ga.pages.game.funnel.dialogs.edit-prompt", ["ga.pages.game.funnel.controller"]).controller("gaPagesFunnelEditPromptController", function($scope) {
$scope.edit = function() {
$scope._resolve({
action: "edit"
})
}, $scope.duplicate = function() {
$scope._resolve({
action: "duplicate"
})
}
}), angular.module("ga.pages.heatmap", ["ga.api.data", "ga.ui.tour", "ga.ui.popdown", "ga.ui.select", "ga.utils.mailman", "ga.ui.unityplayer", "ga.api.userDb.authenticated.heatmap", "ga.ui.tooltip", "ga.ui.modal", "ga.components.rollbar", "ga.visualizations.histogram", "ga.ui.slider"]).filter("getByUid", function() {
return function(input, id) {
for (var i = 0, len = input.length; len > i; i++)
if (input[i].uid === id) return input[i];
return null
}
}).filter("getIndexByUid", function() {
return function(input, id) {
for (var i = 0, len = input.length; len > i; i++)
if (input[i].uid === id) return i;
return null
}
}).filter("getById", function() {
return function(input, id) {
for (var i = 0, len = input.length; len > i; i++)
if (input[i].id === id) return input[i];
return null
}
}).controller("gaPagesHeatmapNodataController", function() {}).controller("gaPagesHeatmapFirstTimeController", function($scope, $state) {
$scope.gotoHeatmap = function() {
$state.go("game.heatmap", {
gameId: $state.params.gameId
})
}
}).controller("gaPagesHeatmapController", function(resolveHeatmap, $rootScope, $scope, $filter, $q, $timeout, gaApiData, gaUtilsMailman, moment, gaUiTour, gaApiUserDbAuthenticatedHeatmap, gaUiModal, gaComponentsRollbar) {
$scope.state = "loading", $scope.settings = null, $scope.selectOptions = {}, $scope.selectOptions.colors = [{
start: "F6C300",
end: "DC0000",
name: '<span class="pokemon"></span>Red/Yellow',
id: "1",
classname: "redyellow"
}, {
start: "68B4FF",
end: "0F4A85",
name: '<span class="pokemon bluenavy"></span>Blue/Navy',
id: "2",
classname: "bluenavy"
}, {
start: "00B122",
end: "FD9927",
name: '<span class="pokemon greenorange"></span>Green/Orange',
id: "3",
classname: "greenorange"
}, {
start: "43B8A2",
end: "AA49C0",
name: '<span class="pokemon tealpurple"></span>Teal/Purple',
id: "4",
classname: "tealpurple"
}, {
start: "ABAC3E",
end: "A83A47",
name: '<span class="pokemon yellowbrown"></span>Yellow/Brown',
id: "5",
classname: "yellowbrown"
}], $scope.selectOptions.renders = [{
name: "Transparent",
id: "0"
}, {
name: "Transparent overlay",
id: "1"
}], $scope.selectOptions.eventTypes = [{
value: "add",
name: "ADD"
}, {
value: "sub",
name: "SUB"
}], $scope.areas = [], $scope.meshes = [], $scope.loadPlayer = !1, $scope.showPlayer = !1, $scope.selectedValues = {
set: null
}, $scope.currentHeatmap = {}, $scope.showSettings = !1, $scope.addingHeatmap = !1;
var runTour = !1,
firstRun = !1;
$scope.initializing = !0;
var loadSettings = function() {
void 0 !== resolveHeatmap && void 0 !== resolveHeatmap.sets && resolveHeatmap.sets.length > 0 ? parseSettings(resolveHeatmap) : (runTour = !0, parseSettings(resolveHeatmap, !0))
},
parseSettings = function(rsettings, emptySettings) {
emptySettings ? ($scope.settings = {
sets: [{
id: 0,
name: "Untitled heatmap set",
selectedHeatmap: 0,
area: null,
mesh: null,
heatmaps: []
}],
selectedSet: 0
}, $scope.settings.gameId = rsettings.gameId, $scope.settings.keys = rsettings.keys, $scope.selectedValues.set = $scope.settings.sets[0], firstRun = !0, $scope.loadPlayer = !0) : ($scope.settings = rsettings, $scope.settings.sets.length > 0 && (angular.forEach($scope.settings.sets, function(obj, key) {
if (obj.heatmaps.length > 0) {
var cFound, rFound;
angular.forEach(obj.heatmaps, function(heatmap) {
cFound = $filter("getById")($scope.selectOptions.colors, heatmap.selectedColor.id || heatmap.selectedColor), heatmap.selectedColor = cFound || $scope.selectOptions.colors[0], rFound = $filter("getById")($scope.selectOptions.renders, heatmap.selectedRender.id || heatmap.selectedRender), heatmap.selectedRender = rFound || $scope.selectOptions.renders[0], heatmap.selectedEvents && angular.forEach(heatmap.selectedEvents, function(event) {
event.type = event && event.type && "sub" === event.type ? $scope.selectOptions.eventTypes[1] : $scope.selectOptions.eventTypes[0]
})
})
}
key === $scope.settings.selectedSet && ($scope.selectedValues.set = obj)
}), $scope.loadPlayer = !0, $scope.showPlayer = !0, $scope.showSettings = !0), switchTab($scope.selectedValues.set.selectedHeatmap, !0))
},
saveSettings = function(saveRemote) {
if ($scope.settings) {
var cleanedSettings = angular.copy($scope.settings);
cleanedSettings.gameId && delete cleanedSettings.gameId, cleanedSettings.keys && delete cleanedSettings.keys, angular.forEach(cleanedSettings.sets, function(obj) {
void 0 !== obj.changed && delete obj.changed, void 0 !== obj.className && delete obj.className, void 0 === obj.mesh && (obj.mesh = null), angular.forEach(obj.heatmaps, function(heatmap) {
void 0 !== heatmap.builds && delete heatmap.builds, void 0 !== heatmap.events && delete heatmap.events, void 0 !== heatmap.histogramData && delete heatmap.histogramData, void 0 !== heatmap.className && delete heatmap.className, void 0 !== heatmap.tooltip && delete heatmap.tooltip, heatmap.selectedColor && heatmap.selectedColor.id && (heatmap.selectedColor = heatmap.selectedColor.id), heatmap.selectedRender && heatmap.selectedRender.id && (heatmap.selectedRender = heatmap.selectedRender.id), heatmap.dateRange && void 0 !== heatmap.dateRange.compare && delete heatmap.dateRange.compare, heatmap.dateRange && heatmap.dateRange.cal1 && (heatmap.dateRange.main = heatmap.dateRange.cal1, delete heatmap.dateRange.cal1), heatmap.dateRange && heatmap.dateRange.cal2 && delete heatmap.dateRange.cal2, heatmap.selectedEvents && angular.forEach(heatmap.selectedEvents, function(evt) {
evt.fullName && delete evt.fullName, evt.category || (evt.category = ""), evt.type = evt.type && evt.type.value && "sub" === evt.type.value ? "sub" : "add"
})
})
}), saveRemote && putSettings(cleanedSettings)
}
},
putSettings = function(csettings) {
gaApiUserDbAuthenticatedHeatmap.putSettings($scope.settings.gameId, csettings).then(function() {}, function() {
gaComponentsRollbar.putCustom({
level: "critical",
msg: "Could not save heatmap settings.",
point: {
error: "Unknown server error",
data: JSON.stringify(csettings)
}
})
})
},
toggleSettings = function() {
$scope.showSettings ? ($scope.showSettings = !1, $scope.currentHeatmap.settingsExpanded = !1) : $scope.currentHeatmap.selectedEvents.length > 0 && "" !== $scope.currentHeatmap.selectedEvents[0].name && ($scope.showSettings = !0, $scope.currentHeatmap.settingsExpanded = !0)
},
addEvent = function() {
$scope.currentHeatmap.selectedEvents[$scope.currentHeatmap.selectedEvents.length - 1].name && $scope.currentHeatmap.selectedEvents.push({
name: null,
category: null,
type: $scope.selectOptions.eventTypes[0]
})
},
deleteEvent = function(id) {
$scope.currentHeatmap.selectedEvents[id] && $scope.currentHeatmap.selectedEvents.splice(id, 1)
},
updateHeatmap = function(newData) {
var redrawOptions = {};
if (newData) {
var builds;
builds = null === $scope.currentHeatmap.selectedBuild ? "" : $scope.currentHeatmap.selectedBuild, redrawOptions = {
uid: $scope.currentHeatmap.uid,
events: [],
build: builds
};
for (var index in $scope.currentHeatmap.selectedEvents) void 0 !== $scope.currentHeatmap.selectedEvents[index].type && "" !== $scope.currentHeatmap.selectedEvents[index].type && void 0 !== $scope.currentHeatmap.selectedEvents[index].name && "" !== $scope.currentHeatmap.selectedEvents[index].name && redrawOptions.events.push({
name: $scope.currentHeatmap.selectedEvents[index].name,
type: $scope.currentHeatmap.selectedEvents[index].type.value
});
var dateRange = timestampToTs($scope.currentHeatmap.dateRange);
null !== dateRange && (redrawOptions.dateRange = dateRange), gaUtilsMailman.publish("global:unity:UpdateHeatmapData", redrawOptions), updateMetaData()
} else redrawOptions = {
uid: $scope.currentHeatmap.uid,
radius: $scope.currentHeatmap.radius,
rangeMin: $scope.currentHeatmap.range.min,
rangeMax: $scope.currentHeatmap.range.max,
renderModel: $scope.currentHeatmap.selectedRender.id,
showHeatmap: $scope.currentHeatmap.visible,
colour: $scope.currentHeatmap.selectedColor,
showValues: $scope.currentHeatmap.showValues ? 1 : 0
}, gaUtilsMailman.publish("global:unity:UpdateHeatmapView", redrawOptions)
},
resetHeatmaps = function() {
if (void 0 !== $scope.selectedValues.set && void 0 !== $scope.selectedValues.set.heatmaps && $scope.selectedValues.set.heatmaps.length > 0)
for (var i = 0, ln = $scope.selectedValues.set.heatmaps.length; ln > i; i++) gaUtilsMailman.publish("global:unity:DeleteHeatmap", $scope.selectedValues.set.heatmaps[i].uid);
$scope.addingHeatmap = !1
},
loadSetup = function() {
var area = $scope.selectedValues.set.area;
area && $scope.areas && $scope.areas.length && -1 === $scope.areas.indexOf(area) && (area = $scope.areas[0], $scope.updateSilentArea = !0, $scope.selectedValues.set.area = area);
var hm, events, dateRange, tmpObj, build, tooltip, options = {
mesh: $scope.selectedValues.set.mesh || null,
area: area || null,
heatmaps: []
};
options.mesh && ($scope.meshLoading = !0, $scope.meshLoadProgress = 0);
for (var index in $scope.selectedValues.set.heatmaps) {
hm = $scope.selectedValues.set.heatmaps[index], events = [], tooltip = "";
for (var eIndex in hm.selectedEvents) null !== hm.selectedEvents[eIndex].name && events.push({
name: hm.selectedEvents[eIndex].name,
type: hm.selectedEvents[eIndex].type.value
});
build = null === hm.selectedBuild ? "" : hm.selectedBuild, tmpObj = {
uid: hm.uid,
events: events,
build: build,
radius: hm.radius,
rangeMin: hm.range.min,
rangeMax: hm.range.max,
renderModel: hm.selectedRender.id,
showHeatmap: hm.visible,
colour: hm.selectedColor,
showValues: hm.showValues ? 1 : 0
}, dateRange = timestampToTs(hm.dateRange), null !== dateRange && (tmpObj.dateRange = dateRange), options.heatmaps.push(tmpObj)
}
updateMetaData(), gaUtilsMailman.publish("global:unity:LoadSetup", options), runTour && setTimeout(function() {
runTour = !1, setupTour()
}, 100), $scope.state = "", $timeout(function() {
$scope.initializing = !1
})
},
addHeatmapSet = function() {
$scope.settings.sets.push({
id: $scope.settings.sets.length,
name: "Untitled heatmap set",
selectedHeatmap: 0,
area: null,
mesh: null,
heatmaps: []
}), $scope.showSettings = !1, $scope.initializing = !0, resetHeatmaps(), $scope.selectedValues.set = $scope.settings.sets[$scope.settings.sets.length - 1], $scope.settings.selectedSet = $scope.settings.sets.length - 1, $scope.currentHeatmap = null, $scope.showPlayer = !1, $scope.firstRun = !0, setupSetTour(), saveSettings(!0), gaUtilsMailman.publish("global:unity:LoadSetup", {}), loadSetup()
},
addHeatmap = function() {
if (!$scope.addingHeatmap)
if ($scope.selectedValues.set.heatmaps.length > 8) {
var buttons = [{
title: "Ok",
"class": "ga-btn orange right",
action: "confirm",
keyCode: 27
}],
$newScope = $rootScope.$new();
gaUiModal.show({
scope: $newScope,
iframe: !0,
header: "Max number of heatmaps",
content: "You are only allowed to add 10 heatmap pr. set",
width: 500,
actions: {
confirm: function() {}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
})
} else $scope.addingHeatmap = !0, gaUtilsMailman.publish("global:unity:AddHeatmap")
},
switchTab = function(id, ignoreSave) {
void 0 !== $scope.selectedValues.set.heatmaps[id] && ($scope.initializing = !0, $scope.selectedValues.set.selectedHeatmap = id, $scope.currentHeatmap = $scope.selectedValues.set.heatmaps[id], $scope.showSettings = 0 === $scope.currentHeatmap.selectedEvents.length || "" === $scope.currentHeatmap.selectedEvents[0].name ? !1 : $scope.currentHeatmap.settingsExpanded, ignoreSave || saveSettings(!0), $timeout(function() {
$scope.initializing = !1
}))
},
toggleHeatmap = function(id) {
void 0 !== $scope.selectedValues.set.heatmaps[id] && $scope.selectedValues.set.selectedHeatmap === id && ($scope.selectedValues.set.heatmaps[id].visible = $scope.selectedValues.set.heatmaps[id].visible ? 0 : 1, updateHeatmap(!1), saveSettings(!0))
},
deleteHeatmap = function(id) {
if (void 0 !== $scope.selectedValues.set.heatmaps[id] && $scope.selectedValues.set.heatmaps.length > 1) {
var uid = $scope.selectedValues.set.heatmaps[id].uid;
$scope.selectedValues.set.selectedHeatmap === id && (angular.element(".heatmap-content .tabs li:eq(" + id + ")").hide(), $scope.selectedValues.set.heatmaps.splice(id, 1), gaUtilsMailman.publish("global:unity:DeleteHeatmap", uid), $scope.selectedValues.set.selectedHeatmap = 0, $scope.switchTab($scope.selectedValues.set.selectedHeatmap), saveSettings(!0))
}
},
updateMetaData = function() {
angular.forEach($scope.selectedValues.set.heatmaps, function(obj) {
var events = obj.selectedEvents.map(function(d) {
return d.name ? d.name.split(":").join(" > ") : ""
});
if (obj.tooltip = events.join("<br />"), "" === obj.tooltip) obj.tooltip = "No events selected";
else if (void 0 !== obj.dateRange && obj.dateRange.main && null !== obj.dateRange.main.start && null !== obj.dateRange.main.end) {
var startDate = moment(obj.dateRange.main.start).format("D. MMM YYYY"),
endDate = moment(obj.dateRange.main.end).format("D. MMM YYYY");
obj.tooltip += "<br />" + startDate + " - " + endDate
}
})
},
showHeatmapSettings = function(index) {
$scope.selectedValues.set.heatmaps[index] === $scope.currentHeatmap && showHeatmapSettingsModal(index).then(function(heatmap) {
heatmap && heatmap.name !== $scope.currentHeatmap.name && ($scope.currentHeatmap.name = heatmap.name, saveSettings(!0))
})
},
showHeatmapSettingsModal = function(index) {
var deferred = $q.defer(),
$newScope = $rootScope.$new();
$newScope.heatmap = angular.copy($scope.currentHeatmap);
var settingsTemplate = '<div class=ga-form heatmap-settings"><label for="name">Name</label><input class="ga-input widget-name-input" name="name" ng-model="heatmap.name" /></div>',
buttons = [{
title: "Save",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "confirm",
disabled: "!heatmap.name"
}];
return $scope.selectedValues.set.heatmaps.length > 1 && buttons.push({
title: "Delete heatmap",
"class": "ga-btn-text light red ga-icon-trash vertical-none left",
action: "delete"
}), buttons.push({
title: "Cancel",
"class": "ga-btn-text right",
action: "cancel",
keyCode: 27
}), gaUiModal.show({
scope: $newScope,
header: "Heatmap settings",
content: settingsTemplate,
width: 500,
iframe: !0,
actions: {
"delete": function() {
confirmHeatmapDeletion(index).then(function() {
deferred.resolve(null)
}, function() {
deferred.reject()
})
},
cancel: function() {
deferred.reject($newScope.heatmap)
},
confirm: function() {
deferred.resolve($newScope.heatmap)
}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
}), deferred.promise
},
confirmHeatmapDeletion = function(index) {
var deferred = $q.defer(),
$newScope = $rootScope.$new();
$newScope.name = $scope.currentHeatmap.name;
var doDelete = function() {
deleteHeatmap(index), deferred.resolve()
};
return gaUiModal.show({
scope: $newScope,
width: 500,
iframe: !0,
header: "Delete heatmap",
content: 'Are you sure you want to delete the heatmap <strong ng-bind="name"></strong>?',
buttons: [{
title: "Delete",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "doDelete"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
doDelete: function() {
doDelete()
}
}
}, "confirmHeatmapDelete"), deferred.promise
},
changeSet = function(id) {
void 0 !== $scope.settings.sets[id] && ($scope.initializing = !0, $scope.selectedValues.set = $scope.settings.sets[id], void 0 !== $scope.selectedValues.set.selectedHeatmap && null !== $scope.selectedValues.set.selectedHeatmap && $scope.selectedValues.set.heatmaps && $scope.selectedValues.set.heatmaps[$scope.selectedValues.set.selectedHeatmap] ? $scope.switchTab($scope.selectedValues.set.selectedHeatmap) : $scope.currentHeatmap = null, $scope.showSettings = !0, $scope.showPlayer = !0, $scope.settings.selectedSet = id, saveSettings(!0), resetHeatmaps(), gaUtilsMailman.publish("global:unity:LoadSetup", {}), loadSetup())
},
doDeleteSet = function(id) {
if (void 0 !== $scope.settings.sets[id] && $scope.settings.sets.length > 1) {
var delViewed = !1;
$scope.selectedValues.set.id === id && (delViewed = !0, resetHeatmaps($scope.settings.sets[id]), gaUtilsMailman.publish("global:unity:LoadSetup", {})), $scope.settings.sets.splice(id, 1), angular.forEach($scope.settings.sets, function(obj, key) {
obj.id = key
}), delViewed ? ($scope.selectedValues.set = $scope.settings.sets[0], $scope.settings.selectedSet = $scope.selectedValues.set.id) : $scope.settings.selectedSet = $scope.selectedValues.set.id, saveSettings(!0)
}
},
showSetSettings = function(index) {
$scope.settings.sets[index] && showSetSettingsModal($scope.settings.sets[index]).then(function(set) {
set && set.name !== $scope.settings.sets[index].name && ($scope.settings.sets[index].name = set.name, saveSettings(!0))
})
},
showSetSettingsModal = function(set) {
var deferred = $q.defer(),
$newScope = $rootScope.$new();
$newScope.set = angular.copy(set);
var settingsTemplate = '<div class=ga-form heatmap-settings"><label for="name">Name</label><input class="ga-input widget-name-input" name="name" ng-model="set.name" /></div>',
buttons = [{
title: "Save",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "confirm",
disabled: "!set.name"
}];
return $scope.settings.sets.length > 1 && buttons.push({
title: "Delete heatmap set",
"class": "ga-btn-text light red ga-icon-trash vertical-none left",
action: "delete"
}), buttons.push({
title: "Cancel",
"class": "ga-btn-text right",
action: "cancel",
keyCode: 27
}), gaUiModal.show({
scope: $newScope,
header: "Heatmap set settings",
iframe: !0,
content: settingsTemplate,
width: 500,
actions: {
"delete": function() {
confirmHeatmapSetDeletion($newScope.set).then(function() {
deferred.resolve(null)
}, function() {
deferred.reject()
})
},
cancel: function() {
deferred.reject($newScope.set)
},
confirm: function() {
deferred.resolve($newScope.set)
}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
}), deferred.promise
},
confirmHeatmapSetDeletion = function(set) {
var deferred = $q.defer(),
$newScope = $rootScope.$new();
$newScope.name = set.name;
var doDelete = function() {
doDeleteSet(set.id), deferred.resolve()
};
return gaUiModal.show({
scope: $newScope,
iframe: !0,
width: 500,
header: "Delete heatmap set",
content: 'Are you sure you want to delete the heatmap set <strong ng-bind="name"></strong> and <strong>all</strong> heatmaps contained within?',
buttons: [{
title: "Delete",
"class": "ga-btn-alt orange right",
keyCode: 13,
action: "doDelete"
}, {
title: "Cancel",
"class": "ga-btn-text right",
keyCode: 27
}],
actions: {
doDelete: function() {
doDelete($scope.set)
}
}
}, "confirmHeatmapDelete"), deferred.promise
},
changeMetric = function(id, metric) {
$scope.currentHeatmap.selectedEvents[id] && ($scope.currentHeatmap.selectedEvents[id].name = metric.event, $scope.currentHeatmap.selectedEvents[id].category = metric.category)
},
useMetricPicker = function(id) {
if ($scope.currentHeatmap.selectedEvents[id]) {
var $tmpScope = $rootScope.$new(!0);
$scope.currentHeatmap.selectedEvents[id].category && $scope.currentHeatmap.selectedEvents[id].name && ($tmpScope.tmpMetric = {
event: $scope.currentHeatmap.selectedEvents[id].name,
category: $scope.currentHeatmap.selectedEvents[id].category
});
var tmpListener = $tmpScope.$watch("tmpMetric", function(newVal, oldVal) {
newVal !== oldVal && ($timeout(function() {
gaUiModal.hide("subModal")
}), $scope.initializing = !1, changeMetric(id, $tmpScope.tmpMetric))
});
$tmpScope.filter = {
disableStarEvents: !0,
customEventList: $scope.currentHeatmap.events
}, gaUiModal.show({
header: "Select a metric",
scope: $tmpScope,
iframe: !0,
newScope: !1,
width: 720,
customClass: "secondary",
content: '<ga-ui-metricpicker metric="tmpMetric" filter="filter"></ga-ui-metricpicker>',
buttons: [{
title: "Cancel",
"class": "ga-btn-alt",
keyCode: 27
}],
always: function() {
tmpListener(), $tmpScope.$destroy()
}
}, "subModal")
}
},
resetBuilds = function() {
$scope.currentHeatmap.selectedBuild = null
},
timestampToTs = function(dateRange) {
var startDate, endDate, range;
return void 0 !== dateRange && dateRange.main && null !== dateRange.main.start && null !== dateRange.main.end ? (startDate = moment(dateRange.main.start).format("YYYY:MM:DD"), endDate = moment(dateRange.main.end).format("YYYY:MM:DD"), range = {
from: startDate,
to: endDate
}) : range = null, range
};
gaUtilsMailman.subscribe("global:unity:update", function(e, data) {
$scope.$apply(function() {
angular.isArray(data.areas) && ($scope.areas = data.areas), angular.isArray(data.meshes) && ($scope.meshes = data.meshes), loadSetup()
})
}, "heatmap"), gaUtilsMailman.subscribe("global:unity:addedHeatmap", function(e, data) {
$scope.$apply(function() {
$scope.addingHeatmap = !1;
var found = $filter("getByUid")($scope.selectedValues.set.heatmaps, data.uid);
if (null === found) {
var color = $scope.selectOptions.colors[$scope.selectedValues.set.heatmaps.length];
void 0 === color && (color = $scope.selectOptions.colors[0]);
var render = $scope.selectOptions.renders[0];
$scope.selectedValues.set.heatmaps.push({
uid: data.uid,
name: "Heatmap",
radius: 10,
range: {
min: 0,
max: 100
},
visible: 1,
builds: data.builds,
selectedBuild: null,
events: data.events,
selectedEvents: [{
type: $scope.selectOptions.eventTypes[0],
name: "",
category: ""
}],
selectedColor: color,
selectedRender: render,
settingsExpanded: !0,
dateRange: {
main: {
start: null,
end: null
}
}
}), $scope.switchTab($scope.selectedValues.set.heatmaps.length - 1), $scope.state = ""
} else found.builds = data.builds, found.events = data.events
})
}, "heatmap"), gaUtilsMailman.subscribe("global:unity:updatedHistogramData", function(e, data) {
$scope.$apply(function() {
var foundIndex = $filter("getIndexByUid")($scope.selectedValues.set.heatmaps, data.uid);
null !== foundIndex && ($scope.selectedValues.set.heatmaps[foundIndex].histogramData = {
data: data.rows,
min: data.min,
max: data.max,
count: data.count
})
})
}, "heatmap"), gaUtilsMailman.subscribe("global:unity:Initialized", function() {
gaUtilsMailman.publish("global:unity:SetKeys", $scope.settings.keys)
}, "heatmap"), gaUtilsMailman.subscribe("global:unity:playerBroken", function() {
var buttons = [{
title: "Ok",
"class": "ga-btn blue right",
action: "confirm",
keyCode: 27
}],
$newScope = $rootScope.$new();
gaUiModal.show({
scope: $newScope,
iframe: !0,
header: "Unity Web Player",
content: 'Your Unity Web Player seems broken. Try and reinstall it by clicking <a href="http://webplayer.unity3d.com/download_webplayer_beta/webplayer-i386.dmg">here</a> ',
width: 500,
actions: {
confirm: function() {}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
})
}, "heatmap"), gaUtilsMailman.subscribe("global:unity:playerMissing", function() {
var buttons = [{
title: "Install",
"class": "ga-btn orange right",
action: "confirm",
keyCode: 13
}, {
title: "Cancel",
"class": "ga-btn-text right",
action: "cancel",
keyCode: 27
}],
$newScope = $rootScope.$new();
gaUiModal.show({
scope: $newScope,
iframe: !0,
header: "Unity Web Player",
content: "You are missing the Unity Web Player. You need it in order to use the Heatmap feature.",
width: 500,
actions: {
confirm: function() {
window.open("http://unity3d.com/webplayer", "_blank")
},
cancel: function() {}
},
always: function() {
$newScope.$destroy()
},
buttons: buttons
})
}, "heatmap"), gaUtilsMailman.subscribe("global:unity:meshLoaded", function() {
$scope.$apply(function() {
$scope.meshLoading = !1, $scope.meshLoadProgress = 0
})
}, "heatmap"), gaUtilsMailman.subscribe("global:unity:meshLoadProgress", function(e, data) {
$scope.$apply(function() {
$scope.meshLoadProgress = Math.round(100 * data.progress)
})
}, "heatmap"), $scope.$watch("selectedValues.set.area", function(newVal, oldVal) {
return $scope.updateSilentArea ? void($scope.updateSilentArea = !1) : void(oldVal !== newVal && newVal && !$scope.initializing && (saveSettings(!0), oldVal ? ($scope.currentHeatmap.histogramData = {}, $scope.currentHeatmap.range = {
min: 0,
max: 100
}, changeSet($scope.selectedValues.set.id)) : (loadSetup(), addHeatmap(), $scope.state = "loading")))
}), $scope.$watch("selectedValues.set.mesh", function(newVal, oldVal) {
oldVal !== newVal && newVal && !$scope.initializing && (gaUtilsMailman.publish("global:unity:UpdateMesh", $scope.selectedValues.set.mesh), saveSettings(!0), $scope.meshLoading = !0, $scope.meshLoadProgress = 0)
}), $scope.$watch("currentHeatmap.selectedEvents", function(newEvents, oldEvents) {
if (newEvents !== oldEvents && newEvents && newEvents.length && "" !== newEvents[0].name && !$scope.initializing) {
if (newEvents.length > oldEvents.length && !newEvents[newEvents.length - 1].name) return;
$scope.showPlayer || ($scope.showPlayer = !0), $scope.showSettings = !0, $scope.currentHeatmap.settingsExpanded = !0, $scope.currentHeatmap.histogramData = {}, $scope.currentHeatmap.range = {
min: 0,
max: 100
}, updateHeatmap(!0), saveSettings(!0)
} else(newEvents !== oldEvents && newEvents && (0 === newEvents.length || "" === newEvents[0].name) || !newEvents) && ($scope.showSettings = !1, saveSettings(!0))
}, !0), $scope.$watch("currentHeatmap.selectedBuild", function(newBuild, oldBuild) {
newBuild !== oldBuild && (newBuild || !newBuild && oldBuild) && !$scope.initializing && (updateHeatmap(!0), saveSettings(!0))
}, !0), $scope.$watch("currentHeatmap.selectedRender", function(newRender, oldRender) {
newRender !== oldRender && newRender && !$scope.initializing && (updateHeatmap(!0), saveSettings(!0))
}, !0), $scope.$watch("currentHeatmap.selectedColor", function(newColor, oldColor) {
newColor !== oldColor && newColor && !$scope.initializing && (updateHeatmap(!1), saveSettings(!0))
}, !0), $scope.$watch("currentHeatmap.radius", function(newRadius, oldRadius) {
newRadius !== oldRadius && newRadius && !$scope.initializing && (updateHeatmap(!1), saveSettings(!1))
}, !0), $scope.$watch("currentHeatmap.range", function(newRange, oldRange) {
newRange !== oldRange && newRange && !$scope.initializing && (updateHeatmap(!1), saveSettings(!1))
}, !0), $scope.$watch("currentHeatmap.dateRange", function(newRange, oldRange) {
newRange !== oldRange && newRange && !$scope.initializing && (updateHeatmap(!0), saveSettings(!0))
}, !0);
var setupTour = window.setupTour = function() {
gaUiTour.addStep({
title: "Choose area",
text: "Choose from which area of your game you want to load data from and show in your heatmap",
element: ".areaselect",
watcher: {},
arrow: "top-left",
anchor: "bottom-left-center",
position: "fixed",
skippable: !1,
doneFn: function() {
var deferred = $q.defer();
return this.watcher = $scope.$watch("selectedValues.set.area", function(newVal, oldVal) {
newVal !== oldVal && deferred.resolve()
}, !0), deferred.promise
},
hideFn: function() {
var deferred = $q.defer();
return angular.element(this.element).on("mousedown.tour", "button", function() {
deferred.resolve()
}), deferred.promise
},
destroyFn: function() {
angular.element(this.element).off("mousedown.tour"), this.watcher()
}
}), gaUiTour.addStep({
title: "Upload Mesh",
text: 'To use a game mesh, you need to upload your mesh through our Unity SDK plug-in. Choose "Export Mesh AssetBundle" in the GameAnalytics menu.',
btnText: "OK, I’m ready",
element: ".meshselect",
arrow: "top-left",
anchor: "bottom-left-center",
position: "fixed",
skippable: !0,
watcher: function() {},
doneFn: function() {
var deferred = $q.defer();
return this.watcher = $scope.$watch("selectedValues.set.mesh", function(newVal, oldVal) {
newVal !== oldVal && deferred.resolve()
}, !0), deferred.promise
},
hideFn: function() {
var deferred = $q.defer();
return angular.element(this.element).on("mousedown.tour", "button", function() {
deferred.resolve()
}), deferred.promise
},
destroyFn: function() {
angular.element(this.element).off("mousedown.tour"), this.watcher()
}
}), gaUiTour.addStep({
title: "Choose event",
text: "Use the event picker to choose the event you want to see visualized in your heatmap.",
element: ".eventselect:first",
arrow: "top-left",
anchor: "bottom-left-center",
skippable: !0,
watcher: function() {},
showWatcher: function() {},
showFn: function() {
var deferred = $q.defer();
return this.showWatcher = $scope.$watch("selectedValues.set.heatmaps", function(newVal) {
newVal.length > 0 && (angular.element(".eventselect").length > 0 ? deferred.resolve() : $timeout(function() {
angular.element(".eventselect").length > 0 && deferred.resolve()
}, 1e3))
}, !0), deferred.promise
},
hideFn: function() {
var deferred = $q.defer();
return angular.element(this.element).on("mousedown.tour", "button", function() {
deferred.resolve()
}), deferred.promise
},
doneFn: function() {
var deferred = $q.defer();
return this.watcher = $scope.$watch("selectedValues.set.heatmaps", function(newVal) {
"" !== newVal[0].selectedEvents[0].name && deferred.resolve()
}, !0), deferred.promise
},
destroyFn: function() {
angular.element(this.element).off("mousedown.tour"), this.watcher(), this.showWatcher()
}
}), gaUiTour.addStep({
title: "Thats it!",
text: 'You’re now certified to begin heatmapping your data. If you have any further questions, you can find us in <a href="/Support">Support</a>.',
element: ".ga.bar.page.sub",
arrow: "hidden",
anchor: "top-center",
center: !0,
skippable: !0,
position: "fixed",
hideFn: function() {
var deferred = $q.defer();
return angular.element(this.element).on("mousedown.tour", "button", function() {
deferred.resolve()
}), deferred.promise
},
destroyFn: function() {
angular.element(this.element).off("mousedown.tour")
}
}), gaUiTour.start()
},
closeSelects = function(event) {
$rootScope.$broadcast("closeOtherSelects", "all"), event.stopPropagation()
};
$scope.$on("$destroy", function() {
gaUtilsMailman.unsubscribeAll("heatmap")
}), $scope.toggleHeatmap = toggleHeatmap, $scope.changeSet = changeSet, $scope.switchTab = switchTab, $scope.addHeatmap = addHeatmap, $scope.showHeatmapSettings = showHeatmapSettings, $scope.addHeatmapSet = addHeatmapSet, $scope.addEvent = addEvent, $scope.deleteEvent = deleteEvent, $scope.toggleSettings = toggleSettings, $scope.showSetSettings = showSetSettings, $scope.useMetricPicker = useMetricPicker, $scope.resetBuilds = resetBuilds, $scope.closeSelects = closeSelects, $timeout(function() {
loadSettings()
})
}), angular.module("ga.pages.game.initialize", ["ui.router", "ga.api.meta", "ga.api.data", "ga.services.game", "ga.ui.progress", "ga.utils.date", "ga.utils.tracking", "ga.utils.cookie"]).controller("gaPagesGameInitializeController", function(resolveGameStatus, $scope, $timeout, $state, gaServicesGame, gaApiData, gaUtilsDate, gaUtilsTracking, gaUtilsCookie) {
var UID = Math.random().toString().replace(".", "");
$scope.gameId = parseInt($state.params.gameId, 10), gaUtilsCookie.set("ga-game-guide-" + $scope.gameId, 1, 7), $scope.update = {
timer: null,
interval: 30,
update: 0
};
var _updateStatus = function(status) {
$scope.gameStatus = status, $scope.gameStatus.data ? ($scope.step = 5, gaUtilsTracking.trackEvent({
category: "home",
action: "game",
label: "hasdata"
})) : $scope.step = $scope.gameStatus.initialized ? 4 : 4, $scope.update.update++, $scope.update.timer = $timeout(updateStatus, 1e3 * $scope.update.interval), updateRejectedEvents()
},
updateStatus = function() {
$timeout.cancel($scope.update.timer), gaServicesGame.getStatus($scope.gameId, !0).then(_updateStatus)
},
updateRejectedEvents = function() {
var options = {
realtime: !0,
levels: ["info"],
filter: "Event rejected: ",
interval: gaUtilsDate.getPeriodRange("last24hours")
};
gaApiData.getQuality(options, UID).then(function(rejectedEvents) {
$scope.rejectedEvents = rejectedEvents.map(function(event) {
return event.name = event.name.replace("Event rejected: ", ""), event
}).sort(function(a, b) {
return a.eventCount === b.eventCount ? 0 : a.eventCount > b.eventCount ? -1 : 1
})
}).catch(function() {
$scope.rejectedEvents = []
})
};
_updateStatus(resolveGameStatus), $scope.$on("$destroy", function() {
gaApiData.removeUID(UID), $timeout.cancel($scope.update.timer)
})
}), angular.module("ga.pages.general.tabbed", ["ui.router"]).controller("gaPagesGeneralTabbedController", function($scope, $state) {
$scope.tabs = $state.$current.data.tabs || [], $scope.tabActive = null;
var setActiveTab = function() {
$scope.tabs.some(function(tab, index) {
return $state.current.name === tab.state ? ($scope.tabActive = index, !0) : void 0
})
};
$scope.$on("$stateChangeSuccess", function() {
$state.current.name.split(".").length < 3 && selectTab(0, !0), setActiveTab()
});
var selectTab = function(index, ignoreReplace) {
var loc = ignoreReplace ? {
location: "replace"
} : {};
$state.go($scope.tabs[index].state, null, loc)
};
$scope.selectTab = selectTab, setActiveTab()
}), angular.module("ga.pages.user.subscriptions", ["ui.router", "ga.config", "ga.api.userDb.authenticated.user", "ga.api.userDb.public.unsubscribe", "ga.ui.modal", "ga.ui.notify", "ga.ui.errors"]).controller("gaPagesUserSubscriptionsController", function(resolveSubscriptions, $timeout, $scope, $state, gaConfig, gaApiUserDbAuthenticatedUser, gaApiUserDbPublicUnsubscribe, gaUiModal, gaUiNotify) {
$scope.errors = [], $scope.noneSelected = !0;
var isPublic = $state.includes("public"),
updateSubscriptions = function() {
isPublic ? gaApiUserDbPublicUnsubscribe.getSubscriptions($state.params.email, $state.params.token).then(_updateSubscriptions).catch(function(errors) {
_updateSubscriptions({
errors: errors
})
}) : gaApiUserDbAuthenticatedUser.subscriptions().then(_updateSubscriptions).catch(function(errors) {
_updateSubscriptions({
errors: errors
})
})
},
_updateSubscriptions = function(results) {
return results.errors ? ($scope.subscriptions = [], void($scope.errors = results.errors)) : (results.forEach(function(studio) {
studio.games.forEach(function(game) {
game.imagePath = game.imageFile ? gaConfig.images.baseUrl + game.imageFile : "/static/ga-app/images/default-game-icon.png", game.subscriptions = {
daily: !1,
weekly: !1,
monthly: !1
}
})
}), void($scope.subscriptions = results))
};
_updateSubscriptions(resolveSubscriptions);
var selectAll = function() {
$scope.subscriptions.forEach(function(studio) {
studio.games.forEach(function(game) {
game.subscriptions.daily = game.daily ? !0 : !1, game.subscriptions.weekly = game.weekly ? !0 : !1, game.subscriptions.monthly = game.monthly ? !0 : !1
})
}), $scope.noneSelected = !1
},
change = function() {
$timeout(function() {
$scope.noneSelected = !$scope.subscriptions.some(function(studio) {
return studio.games.some(function(game) {
var tmp = null;
return game.subscriptions.daily && (tmp = tmp || {
id: game.id
}) && (tmp.daily = !1), game.subscriptions.weekly && (tmp = tmp || {
id: game.id
}) && (tmp.weekly = !1), game.subscriptions.monthly && (tmp = tmp || {
id: game.id
}) && (tmp.monthly = !1), tmp ? !0 : void 0
})
})
})
},
save = function() {
gaUiModal.confirm("Are you sure you want to unsubscribe from the selected email reports?", "Unsubscribe").then(function() {
var data = {
reports_unsubscribe: []
};
$scope.processing = !0, $scope.subscriptions.forEach(function(studio) {
studio.games.forEach(function(game) {
var tmp = null;
game.subscriptions.daily && (tmp = tmp || {
id: game.id
}) && (tmp.daily = !1), game.subscriptions.weekly && (tmp = tmp || {
id: game.id
}) && (tmp.weekly = !1), game.subscriptions.monthly && (tmp = tmp || {
id: game.id
}) && (tmp.monthly = !1), tmp && data.reports_unsubscribe.push(tmp)
})
}), isPublic ? gaApiUserDbPublicUnsubscribe.unsubscribe($state.params.email, $state.params.token, data).then(function() {
gaUiNotify.show("Successfully unsubscribed", 4e3, "default"), updateSubscriptions()
}).finally(function() {
$scope.processing = !1
}) : gaApiUserDbAuthenticatedUser.unsubscribe(data).then(function() {
gaUiNotify.show("Successfully unsubscribed", 4e3, "default"), updateSubscriptions()
}).finally(function() {
$scope.processing = !1
})
})
};
$scope.change = change, $scope.selectAll = selectAll, $scope.save = save
}), angular.module("ga.pages.user.profile", ["ui.router", "ga.services.user", "ga.api.userDb.authenticated.user", "ga.ui.modal", "ga.ui.notify"]).controller("gaPagesUserProfileController", function($scope, $stateParams, $state, $timeout, $window, gaServicesUser, gaApiUserDbAuthenticatedUser, gaUiModal, gaUiNotify) {
$scope.errors = [], $scope.validationErrors = {
firstName: null,
lastName: null
}, $stateParams.link_token && gaApiUserDbAuthenticatedUser.link({
token: $stateParams.link_token
}).then(function() {
setLink($stateParams.source), gaUiNotify.show("Account is now linked to " + $scope.linkedName, 4e3, "default"), gaServicesUser.linked = $scope.linked, $state.go("user.settings.profile", {}, {
inherit: !1,
notify: !1
})
}).catch(function(errors) {
$scope.linkError = errors[0].msg, $state.go("user.settings.profile", {}, {
inherit: !1,
notify: !1
})
});
var setLink = function(linked) {
switch ($scope.linked = linked, $scope.linked) {
case "google":
$scope.linkedName = "Google";
break;
case "github":
$scope.linkedName = "GitHub"
}
},
setUserData = function(reload) {
reload ? gaServicesUser.getUserData().then(function() {
$scope.user = gaServicesUser.details.serialize(), $scope.user.demoGameEnabled = gaServicesUser.demoGameEnabled, setLink(gaServicesUser.linked)
}) : ($scope.user = gaServicesUser.details.serialize(), $scope.user.demoGameEnabled = gaServicesUser.demoGameEnabled, setLink(gaServicesUser.linked))
};
setUserData(), $scope.save = function() {
return $scope.errors = [], $scope.processing = !0, $scope.validationErrors.firstName = $scope.user.hasOwnProperty("firstName") && !$scope.user.firstName ? "First name cannot be empty." : null, $scope.validationErrors.lastName = $scope.user.hasOwnProperty("lastName") && !$scope.user.lastName ? "Last name cannot be empty." : null, $scope.validationErrors.firstName || $scope.validationErrors.lastName ? void($scope.processing = !1) : void gaServicesUser.details.save($scope.user).then(function() {
gaUiNotify.show("User profile saved", 4e3, "default")
}).catch(function(errors) {
$scope.errors = errors
}).finally(function() {
$scope.processing = !1
})
}, $scope.link = function(linkType) {
$window.location.href = "/oauth2/" + linkType + "?action=link"
}, $scope.unlink = function() {
gaUiModal.confirm("Are you sure you want to unlink your account? You can still use your email and password to login.", "Unlink account").then(function() {
gaApiUserDbAuthenticatedUser.unlink().then(function() {
setLink(null), gaUiNotify.show("Account is now unlinked", 4e3, "default"), gaServicesUser.linked = null
})
})
}, $scope.changePassword = function() {
gaServicesUser.dialogPasswordChange()
}, $scope.countries = [{
title: "Afghanistan",
value: "Afghanistan"
}, {
title: "Albania",
value: "Albania"
}, {
title: "Algeria",
value: "Algeria"
}, {
title: "Andorra",
value: "Andorra"
}, {
title: "Angola",
value: "Angola"
}, {
title: "Antarctica",
value: "Antarctica"
}, {
title: "Antigua and Barbuda",
value: "Antigua and Barbuda"
}, {
title: "Argentina",
value: "Argentina"
}, {
title: "Armenia",
value: "Armenia"
}, {
title: "Australia",
value: "Australia"
}, {
title: "Austria",
value: "Austria"
}, {
title: "Azerbaijan",
value: "Azerbaijan"
}, {
title: "Bahamas",
value: "Bahamas"
}, {
title: "Bahrain",
value: "Bahrain"
}, {
title: "Bangladesh",
value: "Bangladesh"
}, {
title: "Barbados",
value: "Barbados"
}, {
title: "Belarus",
value: "Belarus"
}, {
title: "Belgium",
value: "Belgium"
}, {
title: "Belize",
value: "Belize"
}, {
title: "Benin",
value: "Benin"
}, {
title: "Bermuda",
value: "Bermuda"
}, {
title: "Bhutan",
value: "Bhutan"
}, {
title: "Bolivia",
value: "Bolivia"
}, {
title: "Bosnia and Herzegovina",
value: "Bosnia and Herzegovina"
}, {
title: "Botswana",
value: "Botswana"
}, {
title: "Brazil",
value: "Brazil"
}, {
title: "Brunei",
value: "Brunei"
}, {
title: "Bulgaria",
value: "Bulgaria"
}, {
title: "Burkina Faso",
value: "Burkina Faso"
}, {
title: "Burma",
value: "Burma"
}, {
title: "Burundi",
value: "Burundi"
}, {
title: "Cambodia",
value: "Cambodia"
}, {
title: "Cameroon",
value: "Cameroon"
}, {
title: "Canada",
value: "Canada"
}, {
title: "Cape Verde",
value: "Cape Verde"
}, {
title: "Central African Republic",
value: "Central African Republic"
}, {
title: "Chad",
value: "Chad"
}, {
title: "Chile",
value: "Chile"
}, {
title: "China",
value: "China"
}, {
title: "Colombia",
value: "Colombia"
}, {
title: "Comoros",
value: "Comoros"
}, {
title: "Congo, Democratic Republic",
value: "Congo, Democratic Republic"
}, {
title: "Congo, Republic of the",
value: "Congo, Republic of the"
}, {
title: "Costa Rica",
value: "Costa Rica"
}, {
title: "Cote d'Ivoire",
value: "Cote d'Ivoire"
}, {
title: "Croatia",
value: "Croatia"
}, {
title: "Cuba",
value: "Cuba"
}, {
title: "Cyprus",
value: "Cyprus"
}, {
title: "Czech Republic",
value: "Czech Republic"
}, {
title: "Denmark",
value: "Denmark"
}, {
title: "Djibouti",
value: "Djibouti"
}, {
title: "Dominica",
value: "Dominica"
}, {
title: "Dominican Republic",
value: "Dominican Republic"
}, {
title: "East Timor",
value: "East Timor"
}, {
title: "Ecuador",
value: "Ecuador"
}, {
title: "Egypt",
value: "Egypt"
}, {
title: "El Salvador",
value: "El Salvador"
}, {
title: "Equatorial Guinea",
value: "Equatorial Guinea"
}, {
title: "Eritrea",
value: "Eritrea"
}, {
title: "Estonia",
value: "Estonia"
}, {
title: "Ethiopia",
value: "Ethiopia"
}, {
title: "Faroe Islands",
value: "Faroe Islands"
}, {
title: "Fiji",
value: "Fiji"
}, {
title: "Finland",
value: "Finland"
}, {
title: "France",
value: "France"
}, {
title: "Gabon",
value: "Gabon"
}, {
title: "Gambia",
value: "Gambia"
}, {
title: "Georgia",
value: "Georgia"
}, {
title: "Germany",
value: "Germany"
}, {
title: "Ghana",
value: "Ghana"
}, {
title: "Greece",
value: "Greece"
}, {
title: "Greenland",
value: "Greenland"
}, {
title: "Grenada",
value: "Grenada"
}, {
title: "Guatemala",
value: "Guatemala"
}, {
title: "Guinea",
value: "Guinea"
}, {
title: "Guinea-Bissau",
value: "Guinea-Bissau"
}, {
title: "Guyana",
value: "Guyana"
}, {
title: "Haiti",
value: "Haiti"
}, {
title: "Honduras",
value: "Honduras"
}, {
title: "Hong Kong",
value: "Hong Kong"
}, {
title: "Hungary",
value: "Hungary"
}, {
title: "Iceland",
value: "Iceland"
}, {
title: "India",
value: "India"
}, {
title: "Indonesia",
value: "Indonesia"
}, {
title: "Iran",
value: "Iran"
}, {
title: "Iraq",
value: "Iraq"
}, {
title: "Ireland",
value: "Ireland"
}, {
title: "Israel",
value: "Israel"
}, {
title: "Italy",
value: "Italy"
}, {
title: "Jamaica",
value: "Jamaica"
}, {
title: "Japan",
value: "Japan"
}, {
title: "Jordan",
value: "Jordan"
}, {
title: "Kazakhstan",
value: "Kazakhstan"
}, {
title: "Kenya",
value: "Kenya"
}, {
title: "Kiribati",
value: "Kiribati"
}, {
title: "Korea, North",
value: "Korea, North"
}, {
title: "Korea, South",
value: "Korea, South"
}, {
title: "Kuwait",
value: "Kuwait"
}, {
title: "Kyrgyzstan",
value: "Kyrgyzstan"
}, {
title: "Laos",
value: "Laos"
}, {
title: "Latvia",
value: "Latvia"
}, {
title: "Lebanon",
value: "Lebanon"
}, {
title: "Lesotho",
value: "Lesotho"
}, {
title: "Liberia",
value: "Liberia"
}, {
title: "Libya",
value: "Libya"
}, {
title: "Liechtenstein",
value: "Liechtenstein"
}, {
title: "Lithuania",
value: "Lithuania"
}, {
title: "Luxembourg",
value: "Luxembourg"
}, {
title: "Macedonia",
value: "Macedonia"
}, {
title: "Madagascar",
value: "Madagascar"
}, {
title: "Malawi",
value: "Malawi"
}, {
title: "Malaysia",
value: "Malaysia"
}, {
title: "Maldives",
value: "Maldives"
}, {
title: "Mali",
value: "Mali"
}, {
title: "Malta",
value: "Malta"
}, {
title: "Marshall Islands",
value: "Marshall Islands"
}, {
title: "Mauritania",
value: "Mauritania"
}, {
title: "Mauritius",
value: "Mauritius"
}, {
title: "Mexico",
value: "Mexico"
}, {
title: "Micronesia",
value: "Micronesia"
}, {
title: "Moldova",
value: "Moldova"
}, {
title: "Mongolia",
value: "Mongolia"
}, {
title: "Morocco",
value: "Morocco"
}, {
title: "Monaco",
value: "Monaco"
}, {
title: "Mozambique",
value: "Mozambique"
}, {
title: "Namibia",
value: "Namibia"
}, {
title: "Nauru",
value: "Nauru"
}, {
title: "Nepal",
value: "Nepal"
}, {
title: "Netherlands",
value: "Netherlands"
}, {
title: "New Zealand",
value: "New Zealand"
}, {
title: "Nicaragua",
value: "Nicaragua"
}, {
title: "Niger",
value: "Niger"
}, {
title: "Nigeria",
value: "Nigeria"
}, {
title: "Norway",
value: "Norway"
}, {
title: "Oman",
value: "Oman"
}, {
title: "Pakistan",
value: "Pakistan"
}, {
title: "Panama",
value: "Panama"
}, {
title: "Papua New Guinea",
value: "Papua New Guinea"
}, {
title: "Paraguay",
value: "Paraguay"
}, {
title: "Peru",
value: "Peru"
}, {
title: "Philippines",
value: "Philippines"
}, {
title: "Poland",
value: "Poland"
}, {
title: "Portugal",
value: "Portugal"
}, {
title: "Qatar",
value: "Qatar"
}, {
title: "Romania",
value: "Romania"
}, {
title: "Russia",
value: "Russia"
}, {
title: "Rwanda",
value: "Rwanda"
}, {
title: "Samoa",
value: "Samoa"
}, {
title: "San Marino",
value: "San Marino"
}, {
title: "Sao Tome",
value: "Sao Tome"
}, {
title: "Saudi Arabia",
value: "Saudi Arabia"
}, {
title: "Senegal",
value: "Senegal"
}, {
title: "Serbia and Montenegro",
value: "Serbia and Montenegro"
}, {
title: "Seychelles",
value: "Seychelles"
}, {
title: "Sierra Leone",
value: "Sierra Leone"
}, {
title: "Singapore",
value: "Singapore"
}, {
title: "Slovakia",
value: "Slovakia"
}, {
title: "Slovenia",
value: "Slovenia"
}, {
title: "Solomon Islands",
value: "Solomon Islands"
}, {
title: "Somalia",
value: "Somalia"
}, {
title: "South Africa",
value: "South Africa"
}, {
title: "Spain",
value: "Spain"
}, {
title: "Sri Lanka",
value: "Sri Lanka"
}, {
title: "Sudan",
value: "Sudan"
}, {
title: "Suriname",
value: "Suriname"
}, {
title: "Swaziland",
value: "Swaziland"
}, {
title: "Sweden",
value: "Sweden"
}, {
title: "Switzerland",
value: "Switzerland"
}, {
title: "Syria",
value: "Syria"
}, {
title: "Taiwan",
value: "Taiwan"
}, {
title: "Tajikistan",
value: "Tajikistan"
}, {
title: "Tanzania",
value: "Tanzania"
}, {
title: "Thailand",
value: "Thailand"
}, {
title: "Togo",
value: "Togo"
}, {
title: "Tonga",
value: "Tonga"
}, {
title: "Trinidad and Tobago",
value: "Trinidad and Tobago"
}, {
title: "Tunisia",
value: "Tunisia"
}, {
title: "Turkey",
value: "Turkey"
}, {
title: "Turkmenistan",
value: "Turkmenistan"
}, {
title: "Uganda",
value: "Uganda"
}, {
title: "Ukraine",
value: "Ukraine"
}, {
title: "United Arab Emirates",
value: "United Arab Emirates"
}, {
title: "United Kingdom",
value: "United Kingdom"
}, {
title: "United States",
value: "United States"
}, {
title: "Uruguay",
value: "Uruguay"
}, {
title: "Uzbekistan",
value: "Uzbekistan"
}, {
title: "Vanuatu",
value: "Vanuatu"
}, {
title: "Venezuela",
value: "Venezuela"
}, {
title: "Vietnam",
value: "Vietnam"
}, {
title: "Yemen",
value: "Yemen"
}, {
title: "Zambia",
value: "Zambia"
}, {
title: "Zimbabwe",
value: "Zimbabwe"
}]
}), angular.module("ga.pages.user.locale", ["ui.router", "pasvaz.bindonce", "ga.api.meta", "ga.services.user", "ga.utils.date", "ga.ui.notify", "ga.ui.select"]).controller("gaPagesUserLocaleController", function($scope, $timeout, gaApiMeta, gaServicesUser, gaUtilsDate, gaNumberFormatFilter, gaUiNotify) {
$scope.errors = [], $scope.settings = gaServicesUser.settings.serialize();
var userTimeZoneData = gaUtilsDate.getTimeZoneData(gaServicesUser.settings.timeZone),
exampleNumber = 1234.56;
$scope.options = {
startOfWeek: ["Monday", "Sunday"],
timeFormat: [{
value: "24hour",
title: gaUtilsDate.getCurrentTimeString("24hour", userTimeZoneData.displayName)
}, {
value: "12hour",
title: gaUtilsDate.getCurrentTimeString("12hour", userTimeZoneData.displayName)
}],
dateFormat: [{
value: "MDY",
title: gaUtilsDate.getCurrentDateString("MDY", userTimeZoneData.displayName)
}, {
value: "DMY",
title: gaUtilsDate.getCurrentDateString("DMY", userTimeZoneData.displayName)
}, {
value: "YMD",
title: gaUtilsDate.getCurrentDateString("YMD", userTimeZoneData.displayName)
}],
numberFormat: [{
value: "1",
title: gaNumberFormatFilter(exampleNumber, null, 1)
}, {
value: "2",
title: gaNumberFormatFilter(exampleNumber, null, 2)
}, {
value: "3",
title: gaNumberFormatFilter(exampleNumber, null, 3)
}, {
value: "4",
title: gaNumberFormatFilter(exampleNumber, null, 4)
}],
currencyDefault: gaApiMeta.getCurrencies(),
timeZone: gaUtilsDate.getTimeZonesAvailable().map(function(tz) {
return {
value: tz.displayName,
title: tz.displayNameOffset
}
})
}, $scope.save = function() {
$scope.errors = [], $scope.processing = !0, gaServicesUser.settings.save($scope.settings).then(function() {
gaUiNotify.show("Locale settings saved", 4e3, "default")
}).catch(function(errors) {
$scope.errors = errors
}).finally(function() {
$scope.processing = !1
})
}
}), angular.module("ga.pages.user.home", ["ngSanitize", "ui.router", "ga.config", "ga.ui.modal", "ga.ui.widgetChart", "ga.ui.floatLabel", "ga.ui.upload", "ga.ui.notify", "ga.ui.tooltip", "ga.utils.helpers.focusElement", "ga.services.user", "ga.services.game", "ga.services.pardot", "ga.services.tracking", "ga.services.dialogs", "ga.pages.user.gameCreate", "ga.pages.user.studioCreate", "ga.api.userDb.authenticated.user", "ga.api.userDb.public.activate", "ga.utils.cookie", "ga.utils.date", "ga.utils.tracking", "ga.filters.titleCase"]).controller("gaPagesUserHomeController", function($rootScope, $scope, $timeout, $templateCache, $window, $state, gaConfig, gaServicesUser, gaServicesGame, gaServicesPardot, gaUiModal, gaApiUserDbAuthenticatedUser, gaUtilsCookie, gaUiNotify, gaUtilsDate, gaServicesTracking, gaUtilsTracking, gaApiUserDbPublicActivate, gaDialogs) {
$scope.homeState = {
state: null
}, $scope.archive = null !== $state.params.archive, $scope.previousPeriod = gaUtilsDate.moment.utc().startOf("day").add("days", -7).format("MMM Do"), $scope.imageBaseUrl = gaConfig.images.baseUrl;
var setState = function() {
$scope.inviteCount = gaServicesUser.invites.length, $scope.latestInvite = $scope.inviteCount ? gaServicesUser.invites[0] : null, $scope.homeState = {
state: null
};
var studioList = gaServicesUser.studios.filter(function(studio) {
return !studio.demo
});
if ($scope.inviteCount) $scope.homeState.state = "invites", gaServicesUser.onboarding.home.welcome || (gaServicesUser.onboarding.set("home", "welcome", !0), gaServicesUser.onboarding.save());
else if (gaServicesUser.activated)
if (studioList.length) {
if (!gaServicesUser.onboarding.home.welcome && 1 === studioList.length && studioList[0].games.length < 2) {
var game = $scope.user.studios[0] && $scope.user.studios[0].games && $scope.user.studios[0].games[0] || null;
game ? gaServicesGame.getStatus(game.id).then(function(status) {
return status.eventCount - status.rejectedEvents > 0 ? (gaServicesUser.onboarding.set("home", "welcome", !0), void gaServicesUser.onboarding.save()) : void($scope.homeState = {
state: "welcome",
onboarding: {
category: "home",
key: "welcome"
},
step: 2
})
}) : $scope.homeState = {
state: "welcome",
onboarding: {
category: "home",
key: "welcome"
},
step: 1
}
} else if (gaUtilsCookie.get("ga_share_the_love")) return
} else $scope.homeState = {
state: "empty"
};
else $scope.homeState = {
state: "welcomeNonActive"
}
},
dismissState = function() {
$scope.homeState.cookieName && gaUtilsCookie.set($scope.homeState.cookieName, "1", 28), $scope.homeState.onboarding && (gaServicesUser.onboarding.set($scope.homeState.onboarding.category, $scope.homeState.onboarding.key, !0), gaServicesUser.onboarding.save()), gaUtilsTracking.trackEvent({
category: "home",
action: "hero",
label: "dismiss"
}), $scope.homeState = {
state: null
}
};
$scope.archiveFilterStudio = function(studio) {
return $scope.archive && !studio.archived ? studio.games.some(function(game) {
return game.archived
}) : studio.archived === $scope.archive
}, $scope.archiveFilterGame = function(game) {
return $scope.archive && !game.archived ? gaServicesUser.studio(game.studioId).archived : game.archived === $scope.archive
};
var populateData = function() {
$scope.user = {
studios: gaServicesUser.studios,
activated: gaServicesUser.activated,
firstName: gaServicesUser.details.firstName
}, $scope.showArchiveButton = gaServicesUser.studios.some(function(studio) {
return studio.admin || studio.games.some(function(game) {
return game.admin
})
}), $scope.archivedCount = gaServicesUser.studios.reduce(function(count, studio) {
return studio.archived ? count += studio.games.length : studio.games.reduce(function(count, game) {
return count += game.archived ? 1 : 0
}, count)
}, 0), $scope.user.studios.forEach(function(studio) {
studio.hide = !studio.viewer && !studio.games.some(function(game) {
return !game.archived
}), studio.games.forEach(function(game) {
game.loading = 2, game.link = "/game/" + game.id + "/dashboards", gaServicesGame.getNumbers(game.id).then(function(response) {
game.gameNumbers = response, game.loading--
}), gaServicesGame.getStatus(game.id).then(function(status) {
game.gameStatus = status, status.error ? game.state = "error" : status.data24 ? game.state = "ok" : status.data ? game.state = "no-data-last24" : (game.state = "no-data", game.link = !game.admin || gaUtilsCookie.get("ga-game-guide-" + game.id) ? "/game/" + game.id + "/initialize" : "/game/" + game.id + "/content/sdk"), game.eventCount = status.eventCount, game.loading--
})
})
}), setState()
},
showInvites = function() {
gaServicesUser.dialogInvites()
},
acceptInvite = function(invite) {
invite && invite.accept().then(function() {
gaUiNotify.show("Invite accepted", 4e3)
})
},
declineInvite = function(invite) {
invite && gaUiModal.confirm("Are you sure you want to decline this invitation?", "Decline invitation").then(function() {
invite.decline().then(function() {
gaUiNotify.show("Invite declined", 4e3)
})
})
},
createStudio = function() {
var promise = gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/user/studio-create/studio-create.html",
controller: "gaPagesUserStudioCreateController"
});
gaUtilsTracking.trackEvent({
category: "home",
action: "studio",
label: "popupadd"
}), gaUtilsTracking.trackPageRaw("/studio/create"), promise.then(function() {
gaUiNotify.show("Studio successfully created", 4e3, "default"), gaUtilsTracking.trackEvent({
category: "home",
action: "studio",
label: "created"
})
})
},
createGame = function(studio) {
var promise = gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/user/game-create/game-create.html",
controller: "gaPagesUserGameCreateController",
parameters: {
studioId: studio.id,
step: 2
}
});
gaUtilsTracking.trackEvent({
category: "home",
action: "game",
label: "popupadd"
}), gaUtilsTracking.trackPageRaw("/game/add"), promise.then(function(newGameId) {
gaUiNotify.show("Game successfully created", 4e3, "default"), $state.go("game.content.section", {
section: "sdk",
gameId: newGameId
}), gaUtilsTracking.trackEvent({
category: "home",
action: "game",
label: "added"
})
})
},
resendActivationEmail = function() {
$scope.resending = !0, gaApiUserDbPublicActivate.sendActivationEmail(gaServicesUser.details.email).then(function() {
gaUiNotify.show("Activation email resent", 4e3), $scope.activationResent = !0, $scope.resending = !1
}).catch(function() {
$scope.resending = !1
})
};
$scope.$on("userStudiosChange", function() {
populateData()
}), $scope.$on("userInvitesChange", function() {
$scope.inviteCount = gaServicesUser.invites.length, $scope.latestInvite = $scope.inviteCount ? gaServicesUser.invites[0] : null, setState()
}), $scope.$watch("newStudio.active", function(newVal, oldVal) {
newVal !== oldVal && newVal ? setTimeout(function() {
angular.element("#addStudioBox").on("click.addStudio", function(e) {
return "file" === angular.element(e.target).attr("type") ? !0 : !1
}), angular.element("html").on("click.addStudio", $scope.$apply.bind($scope, function() {
$scope.newStudio.name || $scope.newStudio.imageFile || ($scope.newStudio.active = !1)
}))
}) : (angular.element("#addStudioBox").off(".addStudio"), angular.element("html").off(".addStudio"))
}), $scope.disableDemoGame = function() {
gaUtilsTracking.trackEvent({
category: "home",
action: "studio",
label: "demohide"
}), gaUiNotify.show("You can enable the Demo Game from the user settings.", 4e3), gaServicesUser.details.save({
demoGameEnabled: !1
})
}, $scope.archiving = function() {
gaDialogs.archive($scope.user.studios).then(function() {
gaUiNotify.show("Archive updated", 4e3, "default"), gaServicesUser.getUserData()
})
}, $scope.$on("forcedUserDataChange", function() {
populateData()
}), $scope.dismissState = dismissState, $scope.showInvites = showInvites, $scope.acceptInvite = acceptInvite, $scope.declineInvite = declineInvite, $scope.createGame = createGame, $scope.createStudio = createStudio, $scope.resendActivationEmail = resendActivationEmail, populateData(), setState()
}), angular.module("ga.pages.user.gameCreate", ["ui.router", "ga.config", "ga.ui.tagInput", "ga.ui.upload", "ga.ui.errors", "ga.api.userDb.authenticated.studio", "ga.api.userDb.authenticated.genre", "ga.services.user", "ga.utils.helpers.focusElement"]).controller("gaPagesUserGameCreateController", function($scope, $timeout, $state, gaConfig, gaApiUserDbAuthenticatedStudio, gaApiUserDbAuthenticatedGenre, gaServicesUser) {
$scope.errors = [], $scope.studioName = (gaServicesUser.studio($scope.studioId) || {}).name || "", $scope.genres = [], gaApiUserDbAuthenticatedGenre.getGenres().then(function(results) {
$scope.genres = results.map(function(genre) {
return {
value: genre.id,
title: genre.name
}
})
}), $scope.step = $scope.step || 2, $scope.defaultImage = "/static/ga-app/images/default-game-icon.png", $scope.imageBaseUrl = gaConfig.images.baseUrl, $scope.uploadFile = null, $scope.game = {
name: "",
tags: [],
storeLinks: [""]
}, $scope.checkStoreLinks = function(removeIndex) {
var newStoreLinks = angular.copy($scope.game.storeLinks);
removeIndex = void 0 === removeIndex ? !1 : removeIndex, removeIndex === !1 && newStoreLinks.some(function(link, index) {
return !link && index < newStoreLinks.length - 1 ? (removeIndex = index, !0) : void 0
}), removeIndex !== !1 && newStoreLinks.splice(removeIndex, 1), (!newStoreLinks.length || newStoreLinks[newStoreLinks.length - 1]) && newStoreLinks.push(""), $scope.game.storeLinks = newStoreLinks
}, $scope.checkStoreLinks(), $scope.fetchGameInfo = function() {
$scope.fetchingGameInfo = !0, $timeout(function() {
$scope.fetchingGameInfo = !1, $scope.fetchError = "We could not find any game info"
}, 2e3)
}, $scope.prevStep = function() {
$scope.step--
}, $scope.nextStep = function() {
$scope.step++
}, $scope.save = function() {
var payload = {
gameTitle: $scope.game.gameTitle || "",
imageFile: $scope.game.imageFile || "",
genres: $scope.game.tags.map(function(genre) {
return genre.value
})
};
$scope.processing = !0, gaApiUserDbAuthenticatedStudio.createGame($scope.studioId, payload).then(function(results) {
results[0] && gaServicesUser.getUserData(!0).then(function() {
$scope._resolve && $scope._resolve(results[0].id)
})
}).catch(function(errors) {
$scope.processing = !1, $scope.errors = errors
})
}, $scope.cancel = function() {
$scope._reject && $scope._reject("cancel")
}
}), angular.module("ga.pages.user.studioCreate", ["ui.router", "ga.config", "ga.ui.upload", "ga.ui.errors", "ga.api.userDb.authenticated.user", "ga.services.user", "ga.utils.helpers.focusElement"]).controller("gaPagesUserStudioCreateController", function($scope, $timeout, $state, gaConfig, gaApiUserDbAuthenticatedUser, gaServicesUser) {
$scope.errors = [], $scope.defaultImage = "/static/ga-app/images/default-studio-icon.png", $scope.imageBaseUrl = gaConfig.images.baseUrl, $scope.uploadFile = null, $scope.studio = {
name: ""
}, $scope.save = function() {
var payload = {
studioName: $scope.studio.name || "",
imageFile: $scope.studio.imageFile || ""
};
$scope.processing = !0, gaApiUserDbAuthenticatedUser.createStudio(payload).then(function() {
gaServicesUser.getUserData().then(function() {
$scope._resolve && $scope._resolve()
})
}).catch(function(errors) {
$scope.processing = !1, $scope.errors = errors
})
}, $scope.cancel = function() {
$scope._reject && $scope._reject("cancel")
}
}), angular.module("ga.pages.user.passwordChange", ["ga.services.user", "ga.api.userDb.authenticated.user", "ga.ui.errors", "ga.ui.floatLabel", "ga.ui.notify"]).controller("gaPagesUserPasswordChangeController", function($scope, $timeout, gaServicesUser, gaApiUserDbAuthenticatedUser, gaUiNotify) {
$scope.api = {
errors: [],
processing: !1,
success: !1
}, $scope.passwordChangeSuccess = !1, $scope.data = {
passwordCurrent: "",
password: "",
passwordConfirm: ""
}, $scope.cancel = function() {
$scope._reject && $scope._reject()
}, $scope.passwordChange = function() {
$scope.api.processing = !0, gaApiUserDbAuthenticatedUser.passwordChange($scope.data).then(function() {
gaUiNotify.show("Password was changed", 4e3, "default"), $scope._resolve && $scope._resolve()
}).catch(function(errors) {
$scope.api.errors = errors, $scope.api.processing = !1
})
}, $scope.passwordForgot = function() {
$scope._reject && $scope._reject(), $timeout(function() {
gaServicesUser.dialogPasswordForgot()
})
}
}), angular.module("ga.pages.user.invites", ["ga.config", "ga.services.user", "ga.ui.modal", "ga.ui.notify"]).controller("gaPagesUserInvitesController", function($scope, gaConfig, gaServicesUser, gaUiModal, gaUiNotify) {
$scope.imageBaseUrl = gaConfig.images.baseUrl, $scope.studioInvites = gaServicesUser.invites.filter(function(invite) {
return "studio" === invite.type
}), $scope.gameInvites = gaServicesUser.invites.filter(function(invite) {
return "game" === invite.type
}), $scope.$on("userInvitesChange", function() {
$scope.studioInvites = gaServicesUser.invites.filter(function(invite) {
return "studio" === invite.type
}), $scope.gameInvites = gaServicesUser.invites.filter(function(invite) {
return "game" === invite.type
})
}), $scope.close = function() {
$scope._resolve && $scope._resolve()
}, $scope.declineInvite = function(invite) {
invite && gaUiModal.confirm("Are you sure you want to decline this invitation?", "Decline invitation").then(function() {
invite.decline().then(function() {
gaUiNotify.show("Invite declined", 4e3)
})
})
}, $scope.acceptInvite = function(invite) {
invite && invite.accept().then(function() {
gaUiNotify.show("Invite accepted", 4e3)
})
}
}), angular.module("ga.pages.mock", ["ga.services.user", "ga.api.data", "ga.utils.date", "ga.ui.modal", "ga.ui.datepicker", "ga.ui.select"]).controller("gaPagesMockController", function() {}), angular.module("ga.pages.templates", ["ngSanitize"]).controller("gaPagesTemplatesController", function($scope, $http, $templateCache, $compile, $state, $sce) {
$scope.view = {
category: $state.params.category,
item: $state.params.item,
html: angular.element(),
parsed: []
};
var select = function(category, item) {
console.log(category, item), $state.go("templates.show", {
category: category,
item: item
}), $scope.view.category = category, $scope.view.item = item, loadTemplate()
},
loadTemplate = function() {
$scope.view.parsed = {};
var template = "/static/ga-app/modules/pages/templates/templates/" + $scope.view.category + "/" + $scope.view.item + ".html",
templateContent = $templateCache.get(template);
if (templateContent) return void parseTemplate(templateContent);
var promise = $http.get(template);
promise.then(function(response) {
$templateCache.put(template, response.data), parseTemplate(response.data)
}, function() {
$scope.view.parsed = {
title: "Template not found",
subTitle: template,
items: []
}
})
},
parseTemplate = function(template) {
var parsed = {
items: []
};
$scope.view.parsed = [];
var $el = angular.element(template);
parsed.title = $el.find("h1").html(), parsed.subTitle = $el.find("h2").html(), parsed.description = $el.find(">p").html() || "", $el.find("dt").each(function(index, $item) {
$item = angular.element($item);
var item = {};
item.title = $item.find("h3").html() || "", item.description = $item.find("p").html() || "", item.html = $item.next().html().trim().replace(/&amp;/g, "&").replace(/\n\s{12}/g, "\n"), parsed.items.push(item)
}), $scope.view.parsed = parsed
};
$scope.select = select, $scope.unsafeHTML = function(item) {
return $sce.trustAsHtml(item)
}, $scope.view.category && $scope.view.item && loadTemplate(), $scope.availableTemplates = [{
name: "form",
title: "Form Element",
items: [{
name: "input",
title: "Input"
}, {
name: "button",
title: "Button"
}, {
name: "select",
title: "Select"
}, {
name: "radiocheck",
title: "Radio / Checkbox"
}, {
name: "label",
title: "Label"
}]
}, {
name: "style",
title: "Style",
items: [{
name: "box",
title: "Box"
}, {
name: "typography",
title: "Typography"
}, {
name: "list",
title: "List"
}, {
name: "diverse",
title: "Diverse"
}]
}, {
name: "layout",
title: "Layout",
items: [{
name: "wrapper",
title: "Wrapper",
category: "layout"
}, {
name: "grid",
title: "Grid",
category: "layout"
}]
}]
}), angular.module("ga.pages.public.footer", []).directive("gaPagesPublicFooter", function() {
return {
restrict: "E",
replace: !0,
template: '<section class="ga fill light font-small text-center"><div class="ga grid"><a href="http://support.gameanalytics.com" target="_blank" class="grey">Support</a><a href="http://www.gameanalytics.com/terms.html" target="_blank" class="grey">Terms of service</a><a href="http://support.gameanalytics.com/hc/en-us/categories/200079956-For-Game-Developers" target="_blank" class="grey">Documentation</a></div></section>'
}
}), angular.module("ga.pages.public.login", ["ngRoute", "ga.pages.public.footer", "ga.services.user", "ga.ui.modal", "ga.ui.floatLabel", "ga.ui.errors", "ga.pages.public.passwordForgot", "ga.utils.cache", "ga.api.userDb.public.activate"]).controller("gaPagesPublicLoginController", function($window, $scope, $location, $state, $stateParams, gaServicesUser, gaUiModal, gaUtilsCache, gaApiUserDbPublicActivate) {
if ($scope.errors = [], $scope.support = !1, null !== $stateParams.support && ($scope.support = !0), null !== $stateParams.passwordreset && ($scope.passwordreset = !0, $state.go("public.login", {}, {
inherit: !1,
notify: !1
})), null !== $stateParams.notverified && ($stateParams.source ? "github" === $stateParams.source && $scope.errors.push({
id: "notverified",
msg: "Your GitHub account email address is not verified",
type: "queryString"
}) : $scope.notActivated = !0), null !== $stateParams.linkedother && $scope.errors.push({
id: "linkedother",
msg: "Account linked to another provider",
type: "queryString"
}), null !== $stateParams.notfound && $stateParams.source) {
var sourceName = "github" === $stateParams.source ? "GitHub" : "Google";
$scope.errors.push({
id: "notfound",
title: "User account doesn't exist",
msg: "We couldn’t match your " + sourceName + " account to an existing GameAnalytics account. Please try again.",
type: "queryString"
})
}
$scope.invite = null !== $stateParams.invite, $scope.login = {
email: "",
password: "",
remember: !1
}, $stateParams.email && $stateParams.email.length > 0 && ($scope.login.email = $stateParams.email);
var doLogin = function() {
return $scope.login.email = angular.element("input[name=email]").val(), $scope.login.password = angular.element("input[name=password]").val(), $scope.processing ? !1 : ($scope.processing = !0, void gaServicesUser.login($scope.login.email, $scope.login.password, $scope.login.remember).then(function() {
if ($scope.errors = [], $scope.support) return void gaServicesUser.supportToken().then(function(token) {
var returnTo = $stateParams.return_to || "/home";
$window.location.href = "http://support.gameanalytics.com/access/jwt?jwt=" + token.support_token + "&return_to=" + returnTo
});
var goTo = $stateParams.goto || gaUtilsCache.get("goToOnNextLogin", "localStorage");
goTo ? (gaUtilsCache.remove("goToOnNextLogin", "localStorage"), $location.url(goTo), $location.replace()) : $state.go("user.home")
}).catch(function(errors) {
$scope.processing = !1;
var notActivated = errors.some(function(error) {
return "user_account_not_activated" === error.id
}),
passwordFail = errors.some(function(error) {
return "password" === error.field
});
notActivated ? ($scope.notActivated = !0, $scope.notActivatedEmail = $scope.login.email) : $scope.errors = errors, passwordFail && angular.element("input[name=password]").select()
}))
},
loginDemoUser = function() {
gaServicesUser.token = {
token: "GameAnalytics",
exp: 1e13,
mock: $scope.login.email || "demo@gameanalytics.com",
readonly: !0
}, $state.go("user.home", {
alert: "You are now browsing the tool as a demo user"
}, {
inherit: !1
})
},
doLoginGoogle = function() {
if ($scope.support) {
var returnTo = $stateParams.return_to || "/home";
return void($window.location.href = "/oauth2/google?action=login_support&return_to=" + returnTo)
}
$window.location.href = "/oauth2/google?action=login"
},
doLoginGithub = function() {
if ($scope.support) {
var returnTo = $stateParams.return_to || "/home";
return void($window.location.href = "/oauth2/github?action=login_support&return_to=" + returnTo)
}
$window.location.href = "/oauth2/github?action=login"
},
resetPasswordDialog = function() {
gaServicesUser.dialogPasswordForgot($scope.login.email)
};
null !== $stateParams.showreset && ($state.go("public.login", {
email: $stateParams.email
}, {
inherit: !1,
notify: !1
}), resetPasswordDialog());
var resendActivationEmail = function() {
return $scope.resending ? !1 : ($scope.resending = !0, void gaApiUserDbPublicActivate.sendActivationEmail($scope.notActivatedEmail).then(function() {
$scope.activationResent = !0, $scope.resending = !1
}).catch(function() {
$scope.resending = !1
}))
};
return $scope.loginDemoUser = loginDemoUser, $scope.resetPasswordDialog = resetPasswordDialog, $scope.doLogin = doLogin, $scope.doLoginGoogle = doLoginGoogle, $scope.doLoginGithub = doLoginGithub, $scope.resendActivationEmail = resendActivationEmail, {
doLogin: doLogin,
doLoginGoogle: doLoginGoogle,
doLoginGithub: doLoginGithub
}
}), angular.module("ga.pages.public.signup", ["ga.pages.public.signup.refs", "ga.pages.public.footer", "ga.api.userDb.public.signup", "ga.api.userDb.public.activate", "ga.ui.errors", "ga.ui.floatLabel", "ga.services.user", "ga.utils.tracking", "ga.services.tracking", "ga.utils.cache", "ga.ui.notify"]).controller("gaPagesPublicSignupController", function($scope, $state, $stateParams, $window, gaServicesUser, gaApiUserDbPublicActivate, gaApiUserDbPublicSignup, gaServicesTracking, gaUtilsTracking, gaUtilsCache, gaUiNotify) {
$scope.errors = [], null !== $stateParams.linkeduserfound ? $scope.errors.push({
id: "linkeduserfound",
msg: "An account already exists with this email",
type: "queryString"
}) : "github" === $stateParams.source && null !== $stateParams.notverified && $scope.errors.push({
id: "notverified",
msg: "Your GitHub account email address is not verified",
type: "queryString"
}), $scope.refsActive = !0, $scope.signup = {
email: "",
ref: null
}, $scope.signup.ref = $stateParams.ref && $stateParams.ref.match(/(unity|default)/) ? $stateParams.ref : gaUtilsCache.get("ga_signup_ref", "localStorage") || "default", gaUtilsCache.put("ga_signup_ref", $scope.signup.ref, "localStorage", 18e5), $stateParams.email && $stateParams.email.length > 0 && ($scope.signup.email = $stateParams.email);
var doSignupBasic = function() {
if ($scope.processing) return !1;
$scope.processing = !0;
var payload = {
email: $scope.signup.email
};
["unity", "corona"].indexOf($scope.signup.ref) > -1 && (payload.ref = $scope.signup.ref), gaApiUserDbPublicSignup.signupUser($scope.signup).then(function(result) {
return $scope.errors = [], gaUtilsTracking.trackPageRaw("/public/signup/email"), result.token ? (gaServicesUser.token = result, void($scope.signup.success = !0)) : void($scope.processing = !1)
}).catch(function(errors) {
$scope.errors = errors, $scope.processing = !1
})
},
doSignupGoogle = function() {
gaUtilsTracking.trackPageRaw("/public/signup/google"), $window.location.href = "/oauth2/google?action=signup"
},
doSignupGithub = function() {
gaUtilsTracking.trackPageRaw("/public/signup/github"), $window.location.href = "/oauth2/github?action=signup"
},
startDemo = function() {
gaServicesUser.resolveUser().then(function() {
$state.go("user.home", {}, {
inherit: !1,
replace: !0
})
}).catch(function() {
$scope.signup.success = !1, $scope.processing = !1
})
},
resendActivationEmail = function() {
return $scope.resending ? !1 : ($scope.resending = !0, void gaApiUserDbPublicActivate.sendActivationEmail($scope.signup.email).then(function() {
$scope.resending = !1, gaUiNotify.show("Verification email sent", 5e3, "default")
}).catch(function() {
$scope.resending = !1
}))
};
return $scope.startDemo = startDemo, $scope.doSignupBasic = doSignupBasic, $scope.doSignupGoogle = doSignupGoogle, $scope.doSignupGithub = doSignupGithub, $scope.resendActivationEmail = resendActivationEmail, {
doSignupBasic: doSignupBasic,
doSignupGoogle: doSignupGoogle,
doSignupGithub: doSignupGithub
}
}), angular.module("ga.pages.public.signup.refs", ["ui.router", "ga.utils.helpers"]).directive("gaPagesPublicSignupRefs", function($templateCache, gaHelpers, $stateParams) {
var nr = parseInt($stateParams.nr, 10) || !1,
template = '<aside class="ga slim dark ng-hide text-shadow" ng-show="ref" style="{{ templateStyle }}; width:393px"><section class="ga double" ng-include="templateUrl"></section></aside>',
linkingFunction = function($scope) {
if ($scope.ref.match(/(unity|default)/)) {
var templates = $templateCache.get("/static/ga-app/modules/pages/public/signup/refs/" + $scope.ref + ".html");
if (templates) {
templates = angular.element(templates).find("> li");
var number = nr === !1 ? gaHelpers.randomNumber(0, templates.length - 1) : nr - 1,
template = $templateCache.get("/static/ga-app/modules/pages/public/signup/refs/" + $scope.ref + "-" + number + ".html");
template || (template = templates[number].innerHTML, $templateCache.put("/static/ga-app/modules/pages/public/signup/refs/" + $scope.ref + "-" + number + ".html", template));
var templateStyle = templates[number].getAttribute("style");
$scope.templateUrl = "/static/ga-app/modules/pages/public/signup/refs/" + $scope.ref + "-" + number + ".html", $scope.templateStyle = templateStyle
}
} else $scope.ref = ""
};
return {
restrict: "E",
replace: !0,
template: template,
link: linkingFunction,
scope: {
ref: "=?ref"
}
}
}), angular.module("ga.pages.public.invite", ["ga.pages.public.footer", "ga.services.user", "ga.api.userDb.public.invite"]).controller("gaPagesPublicInviteController", function(inviteResolve, $scope, $timeout, $state, $window, $stateParams, gaApiUserDbPublicInvite, gaServicesUser) {
$scope.userLoggedInEmail = gaServicesUser.details.email, $scope.email = $stateParams.email, $scope.init = !0, $scope.inviteResolve = inviteResolve, $scope.login = function() {
gaServicesUser.token = null, $state.go("public.login", {
invite: "",
email: $scope.email
}, {
inherit: !1,
reload: !0
})
}, $scope.create = function() {
gaServicesUser.token = null, $state.go("public.create-account", {
invite: "",
email: $scope.email,
token: $scope.inviteResolve.createToken
}, {
inherit: !1,
reload: !0
})
}, gaApiUserDbPublicInvite.getInviteInfo($stateParams.email, $stateParams.resource, $stateParams.token).then(function(response) {
$scope.inviteExists = response.inviteFound, $scope.inviteEmailExists = response.userFound, $scope.init = !1
})
}), angular.module("ga.pages.public.activateAccount", ["ga.pages.public.footer", "ga.services.user", "ga.api.userDb.public.signup", "ga.ui.errors", "ga.ui.floatLabel", "ga.utils.tracking", "ga.services.tracking"]).controller("gaPagesPublicActivateAccountController", function($scope, $state, $window, $stateParams, gaServicesUser, gaApiUserDbPublicSignup, gaServicesTracking, gaUtilsTracking) {
$scope.emailOptIn = !0, $scope.data = {
email: $stateParams.email,
token: $stateParams.token,
firstName: "",
lastName: "",
studioName: "",
password: "",
passwordConfirm: "",
emailOptOut: 0
}, $scope.errors = [], $scope.processing = !1;
var activateAccount = function() {
return $scope.processing ? !1 : ($scope.processing = !0, $scope.data.emailOptOut = $scope.emailOptIn ? 0 : 1, void gaApiUserDbPublicSignup.activateAccount($scope.data).then(function(result) {
gaUtilsTracking.trackPageRaw("/public/activated"), gaUtilsTracking.trackEvent({
category: "signup",
action: "activated",
label: "email"
}), gaServicesUser.token = {
token: result.token,
exp: result.exp
}, $state.go("user.home", {
alert: "Account is now activated."
}, {
inherit: !1
})
}).catch(function(errors) {
$scope.errors = errors, $scope.processing = !1
}))
};
$scope.activateAccount = activateAccount
}), angular.module("ga.pages.public.createAccount", ["ga.pages.public.footer", "ga.services.user", "ga.api.userDb.public.signup", "ga.ui.errors", "ga.ui.floatLabel", "ga.utils.tracking", "ga.services.tracking"]).controller("gaPagesPublicCreateAccountController", function($scope, $state, $window, $stateParams, gaServicesUser, gaApiUserDbPublicSignup, gaServicesTracking, gaUtilsTracking) {
switch ($scope.emailOptIn = !0, $scope.data = {
email: $stateParams.email || "",
createToken: $stateParams.token || "",
firstName: $stateParams.firstname || "",
lastName: $stateParams.lastname || "",
password: "",
passwordConfirm: "",
emailOptOut: 0
}, $scope.invite = null !== $stateParams.invite, $scope.source = $stateParams.source, $scope.sourceActive = $stateParams.notverified || $stateParams.emailmismatch || !$scope.source ? !1 : !0, $scope.invite || ($scope.data.studioName = ""), $scope.source) {
case "google":
$scope.sourceName = "Google";
break;
case "github":
$scope.sourceName = "GitHub";
break;
default:
$scope.sourceName = ""
}
$scope.errors = [], "true" === $stateParams.notverified && $scope.errors.push({
id: "notverified",
title: $scope.sourceName + " account is not verified!",
msg: "Your " + $scope.sourceName + " email address has not yet been verified, which means we cannot create your account at this time. Please verify your " + $scope.sourceName + " account and try again.",
type: "queryString"
}), "true" === $stateParams.emailmismatch && $scope.errors.push({
id: "emailmismatch",
title: $scope.sourceName + " account email mismatch!",
msg: "Your " + $scope.sourceName + " email address does not match the email on the invite.",
type: "queryString"
}), ($stateParams.notverified || $stateParams.emailmismatch) && $state.go("public.create-account", {
notverified: null,
emailmismatch: null,
source: null
}, {
notify: !1,
replace: !0
}), "true" === $stateParams.notfound && $scope.errors.push({
id: "notfound",
msg: "Account not found",
type: "queryString"
});
var src = $scope.source ? $scope.source : "invite";
gaUtilsTracking.trackPageRaw("/public/create-account/" + src), $scope.processing = !1;
var createAccount = function() {
return $scope.processing ? !1 : ($scope.processing = !0, $scope.data.emailOptOut = $scope.emailOptIn ? 0 : 1, void gaApiUserDbPublicSignup.createAccount($scope.data).then(function(result) {
gaUtilsTracking.trackEvent({
category: "signup",
action: "create",
label: $scope.source
}), gaServicesUser.token = {
token: result.token,
exp: result.exp
};
var src = $scope.source ? $scope.source : "invite";
gaUtilsTracking.trackPageRaw("/public/create-account/" + src + "/finished"), $state.go("user.home", {
alert: "You have successfully created an account."
}, {
inherit: !1
})
}).catch(function(errors) {
$scope.errors = errors, $scope.processing = !1
}))
},
linkedSignup = function(type) {
var action = "signup";
switch ($scope.invite && (action = "signup_invite"), type) {
case "google":
$window.location = "/oauth2/google?action=" + action + "&token=" + $scope.data.createToken;
break;
case "github":
$window.location = "/oauth2/github?action=" + action + "&token=" + $scope.data.createToken
}
},
cancelLink = function() {
$scope.source = !1, $scope.sourceActive = !1, $scope.sourceName = "", $state.go("public.create-account", {
source: null
}, {
notify: !1,
replace: !0
})
};
$scope.linkedSignup = linkedSignup, $scope.cancelLink = cancelLink, $scope.createAccount = createAccount
}), angular.module("ga.pages.public.linkAccount", ["ga.pages.public.footer", "ui.router", "ga.api.userDb.public.linkAccount", "ga.services.user", "ga.ui.errors", "ga.ui.floatLabel"]).controller("gaPagesPublicLinkAccountController", function($scope, $stateParams, $state, gaApiUserDbPublicLinkAccount, gaServicesUser) {
$scope.params = $stateParams, $scope.source = $stateParams.source, $scope.sourceName = "github" === $scope.source ? "GitHub" : "Google", $scope.errors = [], $scope.data = {
email: $stateParams.email,
token: $stateParams.token,
password: ""
}, $scope.resetPasswordDialog = function() {
gaServicesUser.dialogPasswordForgot($stateParams.email)
}, $scope.linkAccount = function() {
return $scope.processing ? !1 : ($scope.processing = !0, void gaApiUserDbPublicLinkAccount.link($scope.data).then(function(result) {
gaServicesUser.token = {
token: result.token,
exp: result.exp
};
var linkedName = "Google";
"github" === $scope.source && (linkedName = "GitHub"), $state.go("user.home", {
alert: "Account is now linked to " + linkedName + " account"
}, {
inherit: !1
})
}).catch(function(errors) {
$scope.processing = !1, $scope.errors = errors
}))
}
}), angular.module("ga.pages.public.passwordReset", ["ga.pages.public.footer", "ui.router", "ga.ui.errors", "ga.ui.floatLabel", "ga.api.userDb.public.passwordReset"]).controller("gaPagesPublicPasswordResetController", function(resolveValidToken, $scope, $stateParams, $state, gaApiUserDbPublicPasswordReset) {
$scope.validToken = resolveValidToken, $scope.errors = [], $scope.token = $stateParams.token, $scope.data = {
password: "",
passwordConfirm: ""
}, $scope.processing = !1;
var resetPassword = function() {
return $scope.processing ? !1 : ($scope.processing = !0, void gaApiUserDbPublicPasswordReset.reset($scope.data, $scope.token).then(function(data) {
var replyQueryStrings = {
passwordreset: ""
};
data && data.email && (replyQueryStrings.email = data.email), $state.go("public.login", replyQueryStrings, {
inherit: !1,
reload: !0
})
}).catch(function(errors) {
$scope.errors = errors, $scope.processing = !1
}))
};
$scope.resetPassword = resetPassword
}), angular.module("ga.pages.public.passwordForgot", ["ga.ui.errors", "ga.ui.floatLabel", "ga.api.userDb.public.passwordReset"]).controller("gaPagesPublicPasswordForgotController", function($scope, gaApiUserDbPublicPasswordReset) {
$scope.errors = [], $scope.data = $scope.data || {
email: ""
}, $scope.processing = !1;
var requestResetEmail = function() {
return $scope.processing ? !1 : ($scope.processing = !0, void gaApiUserDbPublicPasswordReset.request($scope.data).then(function() {
$scope.emailSent = !0
}).catch(function(errors) {
$scope.errors = errors, $scope.processing = !1
}))
};
$scope.requestResetEmail = requestResetEmail
}), angular.module("ga.pages.public.releaseNotes", []).controller("gaPagesPublicReleaseNotesController", function() {}), angular.module("ga.pages.content", ["ui.router", "ga.services.user", "ga.services.content"]).controller("gaPagesContentController", function($scope, $injector, $timeout, $stateParams, $state, gaServicesUser, gaServicesContent, gaApiUserDbAuthenticatedGame) {
$stateParams.gameId && ($scope.gameId = $stateParams.gameId, gaApiUserDbAuthenticatedGame.getGameData($stateParams.gameId).then(function(result) {
$scope.gameKey = result.key, $scope.secretKey = result.secret_key
})), $scope.$on("$stateChangeSuccess", function() {
$scope.section = $state.params.section, $scope.page = $state.params.page, $scope.currentStepIndex = parseInt($state.params.step || 1, 10) - 1, gaServicesContent.get($scope.section, $scope.page).then(function(response) {
$scope.content = response, $scope.currentStep = $scope.content.steps ? $scope.content.steps[$scope.currentStepIndex] : null, angular.element("body,html").scrollTop(0), $timeout(function() {
$scope.asideContainerEmpty = !angular.element(".ga-page-content aside .container").text().trim(), $scope.asideEmpty = !angular.element(".ga-page-content aside").text().trim()
})
})
}), $scope.goToStep = function(step) {
var params = angular.copy($state.params);
params.step = step, $scope.gameId ? $state.go("game.content.section", params) : $state.go("content.section", params)
}, $scope.getTemplate = function(key) {
return $scope.currentStep && $scope.currentStep[key] && !$scope.currentStep[key].empty ? $scope.currentStep[key].empty ? null : $scope.currentStep[key].template : $scope.content[key] ? $scope.content[key].empty ? null : $scope.content[key].template : null
}
}).directive("gaContentImage", function() {
var compile = function(element, attrs) {
element.replaceWith('<img class="ga-content-widget" src="/static/ga-app/content/_images/' + attrs.src + '" />')
};
return {
restrict: "E",
compile: compile
}
}).directive("gaContentNote", function() {
var compile = function(element) {
var content = element.get(0).innerHTML;
element.replaceWith('<div class="ga-content-widget note">' + content + "</ul>")
};
return {
restrict: "E",
compile: compile
}
}).directive("gaContentIcon", function() {
var compile = function(element, attrs) {
var addPadding = !!element.get(0).innerHTML;
element.addClass("ga-icon-" + attrs.gaContentIcon), attrs.size && element.addClass(attrs.size), addPadding && element.addClass("icon-padding")
};
return {
restrict: "A",
replace: !1,
compile: compile
}
}).directive("gaContentCode", function() {
var compile = function(element, attrs) {
var example = void 0 !== attrs.example,
trimSpace = null,
lines = element.get(0).innerHTML.split("\n").filter(function(line) {
return line.trim()
}).map(function(line) {
return line = line.replace(/</g, "&lt;").replace(/>/g, "&gt;"), line = line.replace(/\t/g, " "), null === trimSpace && (trimSpace = (line.match(/^\s+/) || [""])[0].length), line = line.replace(new RegExp("^\\s{" + trimSpace + "}"), ""), line = line.replace(/\s/g, "&nbsp;"), "<li>" + line + "</li>"
});
element.replaceWith('<ul class="ga-content-widget ga-content-code' + (example ? " example" : "") + '">' + lines.join("") + "</ul>")
};
return {
restrict: "E",
compile: compile
}
}).directive("gaContentGameKeys", function() {
var template = '<div class="ga-content-widget" ng-if="gameId" ng-show="gameKey"><h3>Unique game keys</h3><label>Game key</label><input auto-select class="ga-input small regular" value="{{ gameKey }}" readonly="readonly" /><label>Secret key</label><input auto-select class="ga-input small regular" value="{{ secretKey }}" readonly="readonly" /></div>';
return {
restrict: "E",
template: template,
replace: !0
}
}).directive("gaContentGameProgress", function() {
var template = '<span ng-if="gameId"><div class="ga menu-filler small" style="height: 30px;"></div><div class="ga bar page white small"><ul class="ga stack progress-list"><li class="done">Create game</li><li class="inprogress">Setup SDK</li><li>Waiting for data</li><li>Done</li></ul></div></span>';
return {
restrict: "E",
template: template,
replace: !0
}
}).directive("autoSelect", function() {
return {
link: function($scope, $element) {
$element.on("mouseup", function() {
this.select()
})
}
}
}), angular.module("ga.pages.admin.controller", ["ga.ui.modal", "ga.api.admin", "ga.pages.admin.ui.login", "ga.pages.admin.ui.changeUserEmail", "ga.pages.admin.ui.changeStudioOwner"]).service("gaPagesAdminController", function($rootScope, $state, $q, $filter, $timeout, gaUiModal, gaApiAdmin) {
var loginModal = function() {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/admin/ui/login/login.html",
controller: "gaPagesAdminUiLoginController"
})
},
changeUserEmailModal = function(userId) {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/admin/ui/change-user-email/change-user-email.html",
controller: "gaPagesAdminUiChangeUserEmailController",
parameters: {
userId: userId
}
})
},
adminChangeStudioOwnerModal = function(studioId) {
return gaUiModal.page({
templateUrl: "/static/ga-app/modules/pages/admin/ui/change-studio-owner/change-studio-owner.html",
controller: "gaPagesAdminUiChangeStudioOwnerController",
width: 800,
parameters: {
studioId: studioId
}
})
},
resetImpersonate = function() {
return gaApiAdmin.adminResetImpersonate()
},
logout = function() {
return gaApiAdmin.adminLogout()
};
return {
loginModal: loginModal,
changeUserEmailModal: changeUserEmailModal,
adminChangeStudioOwnerModal: adminChangeStudioOwnerModal,
resetImpersonate: resetImpersonate,
logout: logout
}
}), angular.module("ga.pages.admin.layout.header", ["ga.ui.popdown", "ui.router", "ga.api.admin", "ga.services.user", "ga.ui.notify"]).controller("gaPagesAdminLayoutHeaderController", function($scope, $rootScope, $location, $state, $route, gaApiAdmin, gaServicesUser, gaUiNotify) {
$scope.menuVisible = !1, $scope.dashboardsActive = $state.includes("game.dashboards.show"), $scope.menu = function(show) {
show = "boolean" == typeof show ? show : !$scope.menuVisible, $scope.menuVisible = show, show && $rootScope.$broadcast("popdown:open", {
id: "mainMenu"
})
}, $scope.goTo = function() {
$state.go("game.dashboards.dashboard", {
action: "show",
dashboardId: 1
}), $route.reload()
}, $scope.adminLogout = function() {
gaApiAdmin.adminLogout().then(function() {
gaServicesUser.getUserData(!0, !0).then(function() {
gaUiNotify.show("admin logged out!", 4e3, "default"), $state.go("user.home")
})
})
}
}), angular.module("ga.pages.admin.ui.searchStudios", ["ga.config", "ga.api.data", "ga.api.meta", "ui.router", "ga.api.admin", "ga.ui.popdown"]).controller("gaPagesAdminUiSearchStudiosController", function($scope, $http, gaApiData, gaApiMeta, $timeout, $window, $state, $filter, gaApiAdmin) {
var labels = {
order_by: "Order By",
order: "Order",
name: "Name",
created_date: "Created Date",
asc: "Ascending",
desc: "Descending",
id: "Id"
};
$scope.gaStudios = [], $scope.searchUi = {
search_string: null,
order_by: "name",
order: "asc",
pagination: 0
};
var tempSearchSettings = {
search_string: null,
order_by: "name",
order: "asc",
pagination: 0
};
$scope.initSearchSettings && ($scope.initSearchSettings.search_string && (tempSearchSettings.search_string = $scope.initSearchSettings.search_string), $scope.initSearchSettings.order_by && (tempSearchSettings.order_by = $scope.initSearchSettings.order_by), $scope.initSearchSettings.order && (tempSearchSettings.order = $scope.initSearchSettings.order), $scope.focusOnInit = $scope.initSearchSettings.focusOnInit ? !0 : !1), $scope.searchSettings = tempSearchSettings, $scope.isLoading = !1, $scope.renderLocalList = !1, $scope.studiosFound || ($scope.renderLocalList = !0);
var init = function() {
loadSearchQuery()
},
loadSearchQuery = function() {
$scope.isLoading === !1 && ($scope.isLoading = !0, gaApiAdmin.getStudiosBySearch($scope.searchSettings).then(function(result) {
$scope.isLoading = !1, !$scope.renderLocalList && $scope.studiosFound ? $scope.studiosFound = result : $scope.gaStudios = result, applySearchUiValues()
}).catch(function() {
$scope.isLoading = !1
}))
};
$scope.searchKeyUp = function(e) {
e.keyCode && 13 === e.keyCode && applySearchUiValues()
};
var timerPromise;
$scope.$watch("searchUi.search_string", function(newVal, oldVal) {
newVal !== oldVal && ($timeout.cancel(timerPromise), timerPromise = $timeout(function() {
applySearchUiValues()
}, 700))
}), $scope.selectStudio = function(studio) {
$scope.studioSelected = studio
};
var applySearchUiValues = function() {
angular.equals($scope.searchSettings, $scope.searchUi) || $scope.isLoading === !0 || ($scope.searchSettings = angular.copy($scope.searchUi), loadSearchQuery())
};
return $scope.changeOrderBy = function(order_by_new_value) {
$scope.searchUi.order_by = order_by_new_value, applySearchUiValues()
}, $scope.changeOrder = function(order_new_value) {
$scope.searchUi.order = order_new_value, applySearchUiValues()
}, $scope.getLabel = function(labelId) {
return labels && labels[labelId] ? labels[labelId] : "label not found"
}, $scope.getClassForOrder = function() {
return $scope.searchUi && $scope.searchUi.order && "asc" === $scope.searchUi.order ? "ga-icon-barchart" : "ga-icon-barchart-inverse"
}, $scope.updateFocusState = function(state) {
if ($scope.searchInFocus) {
var tempFocusDict = {
focus: !1
};
state && (tempFocusDict.focus = !0), $scope.searchInFocus = tempFocusDict
}
}, {
init: init
}
}).directive("gaPagesAdminUiSearchStudios", function() {
var linkingFunction = function($scope, $element, $attrs, $controller) {
$controller.init()
};
return {
restrict: "E",
replace: !0,
scope: {
studioSelected: "=?",
studiosFound: "=?",
initSearchSettings: "=?",
searchInFocus: "=?"
},
controller: "gaPagesAdminUiSearchStudiosController",
link: linkingFunction,
templateUrl: "/static/ga-app/modules/pages/admin/ui/search-studios/search-studios.html"
}
}), angular.module("ga.pages.admin.ui.searchGames", ["ga.config", "ga.api.data", "ga.api.meta", "ui.router", "ga.api.admin", "ga.ui.popdown"]).controller("gaPagesAdminUiSearchGamesController", function($scope, $http, gaApiData, gaApiMeta, $timeout, $window, $state, $filter, gaApiAdmin) {
var labels = {
order_by: "Order By",
order: "Order",
title: "Title",
created_date: "Created Date",
asc: "Ascending",
desc: "Descending",
id: "Id"
};
$scope.gaGames = [];
var tempSearchSettings = {
search_string: null,
order_by: "title",
order: "asc",
pagination: 0,
only_with_store_apps_defined: !1
};
$scope.initSearchSettings && ($scope.initSearchSettings.search_string && (tempSearchSettings.search_string = $scope.initSearchSettings.search_string), $scope.initSearchSettings.order_by && (tempSearchSettings.order_by = $scope.initSearchSettings.order_by), $scope.initSearchSettings.order && (tempSearchSettings.order = $scope.initSearchSettings.order), $scope.initSearchSettings.only_with_store_apps_defined && (tempSearchSettings.only_with_store_apps_defined = $scope.initSearchSettings.only_with_store_apps_defined), $scope.focusOnInit = $scope.initSearchSettings.focusOnInit ? !0 : !1), $scope.searchUi = angular.copy(tempSearchSettings), $scope.searchSettings = tempSearchSettings, $scope.isLoading = !1, $scope.renderLocalList = !1, $scope.gamesFound || ($scope.renderLocalList = !0);
var init = function() {
loadSearchQuery()
},
loadSearchQuery = function() {
$scope.isLoading === !1 && ($scope.isLoading = !0, gaApiAdmin.getGamesBySearch($scope.searchSettings).then(function(result) {
$scope.isLoading = !1, !$scope.renderLocalList && $scope.gamesFound ? $scope.gamesFound = result : $scope.gaGames = result, applySearchUiValues()
}).catch(function() {
$scope.isLoading = !1
}))
};
$scope.searchKeyUp = function(e) {
e.keyCode && 13 === e.keyCode && applySearchUiValues()
};
var timerPromise;
$scope.$watch("searchUi.search_string", function(newVal, oldVal) {
newVal !== oldVal && ($timeout.cancel(timerPromise), timerPromise = $timeout(function() {
applySearchUiValues()
}, 700))
}), $scope.selectGame = function(game) {
$scope.gameSelected && ($scope.gameSelected = game)
};
var applySearchUiValues = function() {
angular.equals($scope.searchSettings, $scope.searchUi) || $scope.isLoading === !0 || ($scope.searchSettings = angular.copy($scope.searchUi), loadSearchQuery())
};
return $scope.changeOrderBy = function(order_by_new_value) {
$scope.searchUi.order_by = order_by_new_value, applySearchUiValues()
}, $scope.changeOrder = function(order_new_value) {
$scope.searchUi.order = order_new_value, applySearchUiValues()
}, $scope.getLabel = function(labelId) {
return labels && labels[labelId] ? labels[labelId] : "label not found"
}, $scope.getClassForOrder = function() {
return $scope.searchUi && $scope.searchUi.order && "asc" === $scope.searchUi.order ? "ga-icon-barchart" : "ga-icon-barchart-inverse"
}, $scope.updateFocusState = function(state) {
if ($scope.searchInFocus) {
var tempFocusDict = {
focus: !1
};
state && (tempFocusDict.focus = !0), $scope.searchInFocus = tempFocusDict
}
}, {
init: init
}
}).directive("gaPagesAdminUiSearchGames", function() {
var linkingFunction = function($scope, $element, $attrs, $controller) {
$controller.init()
};
return {
restrict: "E",
replace: !0,
scope: {
gameSelected: "=?",
gamesFound: "=?",
initSearchSettings: "=?",
searchInFocus: "=?"
},
controller: "gaPagesAdminUiSearchGamesController",
link: linkingFunction,
templateUrl: "/static/ga-app/modules/pages/admin/ui/search-games/search-games.html"
}
}), angular.module("ga.pages.admin.ui.searchUsers", ["ga.config", "ga.api.data", "ga.api.meta", "ui.router", "ga.api.admin", "ga.ui.popdown"]).controller("gaPagesAdminUiSearchUsersController", function($scope, $http, gaApiData, gaApiMeta, $timeout, $window, $state, $filter, gaApiAdmin) {
var labels = {
order_by: "Order By",
order: "Order",
full_name: "Fullname",
email: "Email",
created_date: "Created Date",
last_login_date: "Last Login Date",
asc: "Ascending",
desc: "Descending",
id: "Id"
};
$scope.gaUsers = [], $scope.searchUi = {
search_string: null,
order_by: "last_login_date",
order: "desc",
pagination: 0
};
var tempSearchSettings = {
search_string: null,
order_by: "last_login_date",
order: "desc",
pagination: 0
};
$scope.initSearchSettings && ($scope.initSearchSettings.search_string && (tempSearchSettings.search_string = $scope.initSearchSettings.search_string), $scope.initSearchSettings.order_by && (tempSearchSettings.order_by = $scope.initSearchSettings.order_by), $scope.initSearchSettings.order && (tempSearchSettings.order = $scope.initSearchSettings.order), $scope.focusOnInit = $scope.initSearchSettings.focusOnInit ? !0 : !1), $scope.searchSettings = tempSearchSettings, $scope.isLoading = !1, $scope.renderLocalList = !1, $scope.usersFound || ($scope.renderLocalList = !0);
var init = function() {
loadSearchQuery()
},
loadSearchQuery = function() {
$scope.isLoading === !1 && ($scope.isLoading = !0, gaApiAdmin.getUsersBySearch($scope.searchSettings).then(function(result) {
$scope.isLoading = !1, !$scope.renderLocalList && $scope.usersFound ? $scope.usersFound = result : $scope.gaUsers = result, applySearchUiValues()
}).catch(function() {
$scope.isLoading = !1
}))
};
$scope.searchKeyUp = function(e) {
e.keyCode && 13 === e.keyCode && applySearchUiValues()
};
var timerPromise;
$scope.$watch("searchUi.search_string", function(newVal, oldVal) {
newVal !== oldVal && ($timeout.cancel(timerPromise), timerPromise = $timeout(function() {
applySearchUiValues()
}, 700))
}), $scope.selectUser = function(user) {
$scope.userSelected = user
};
var applySearchUiValues = function() {
angular.equals($scope.searchSettings, $scope.searchUi) || $scope.isLoading === !0 || ($scope.searchSettings = angular.copy($scope.searchUi), loadSearchQuery())
};
return $scope.changeOrderBy = function(order_by_new_value) {
$scope.searchUi.order_by = order_by_new_value, applySearchUiValues()
}, $scope.changeOrder = function(order_new_value) {
$scope.searchUi.order = order_new_value, applySearchUiValues()
}, $scope.getLabel = function(labelId) {
return labels && labels[labelId] ? labels[labelId] : "label not found"
}, $scope.getClassForOrder = function() {
return $scope.searchUi && $scope.searchUi.order && "asc" === $scope.searchUi.order ? "ga-icon-barchart" : "ga-icon-barchart-inverse"
}, $scope.updateFocusState = function(state) {
if ($scope.searchInFocus) {
var tempFocusDict = {
focus: !1
};
state && (tempFocusDict.focus = !0), $scope.searchInFocus = tempFocusDict
}
}, {
init: init
}
}).directive("gaPagesAdminUiSearchUsers", function() {
var linkingFunction = function($scope, $element, $attrs, $controller) {
$controller.init()
};
return {
restrict: "E",
replace: !0,
scope: {
userSelected: "=?",
usersFound: "=?",
initSearchSettings: "=?",
searchInFocus: "=?"
},
controller: "gaPagesAdminUiSearchUsersController",
link: linkingFunction,
templateUrl: "/static/ga-app/modules/pages/admin/ui/search-users/search-users.html"
}
}), angular.module("ga.pages.admin.ui.login", ["ui.router", "ga.config", "ga.ui.tagInput", "ga.ui.upload", "ga.ui.errors", "ga.api.admin", "ga.services.user", "ga.utils.helpers.focusElement", "ga.services.user"]).controller("gaPagesAdminUiLoginController", function($scope, $state, gaServicesUser, gaApiAdmin) {
(!gaServicesUser.admin || gaServicesUser.adminLoggedIn) && $scope._reject && $scope._reject("cancel"), $scope.data = {
loginError: "",
authKey: null,
processing: !1
}, $scope.cancel = function() {
$scope._reject && $scope._reject("cancel")
}, $scope.authcodeKeyUp = function(e) {
e.keyCode && 13 === e.keyCode && $scope.data.authKey && $scope.adminLogin()
}, $scope.adminLogin = function() {
$scope.data.processing = !0, gaApiAdmin.adminLogin($scope.data.authKey).then(function() {
gaServicesUser.getUserData(!0, !0).then(function() {
$scope.data.loginError = "", $scope._resolve && $scope._resolve()
}).catch(function() {
$scope.data.processing = !1, $scope._reject && $scope._reject("cancel")
})
}).catch(function(errors) {
$scope.data.loginError = errors[0] && errors[0].msg ? errors[0].msg : "", $scope.data.processing = !1
})
}
}), angular.module("ga.pages.admin.ui.changeUserEmail", ["ui.router", "ga.config", "ga.ui.tagInput", "ga.ui.upload", "ga.ui.errors", "ga.api.admin", "ga.services.user", "ga.utils.helpers.focusElement", "ga.services.user"]).controller("gaPagesAdminUiChangeUserEmailController", function($scope, $state, gaServicesUser, gaApiAdmin) {
gaServicesUser.admin && gaServicesUser.adminLoggedIn && $scope.userId || $scope._reject && $scope._reject("cancel"), $scope.data = {
requestError: "",
email: null,
processing: !1
}, $scope.cancel = function() {
$scope._reject && $scope._reject("cancel")
}, $scope.modalKeyUp = function(e) {
e.keyCode && 13 === e.keyCode && $scope.data.authKey && $scope.changeEmailRequest()
}, $scope.changeEmail = function() {
$scope.data.processing = !0, gaApiAdmin.changeUserEmail($scope.userId, $scope.data.email).then(function() {
$scope.data.processing = !1, $scope.data.requestError = "", $scope._resolve && $scope._resolve()
}).catch(function(errors) {
$scope.data.requestError = errors[0] && errors[0].msg ? errors[0].msg : "", $scope.data.processing = !1
})
}
}), angular.module("ga.pages.admin.ui.changeStudioOwner", ["ui.router", "ga.config", "ga.ui.tagInput", "ga.ui.upload", "ga.ui.errors", "ga.api.admin", "ga.services.user", "ga.utils.helpers.focusElement", "ga.services.user"]).controller("gaPagesAdminUiChangeStudioOwnerController", function($scope, $state, gaServicesUser, gaApiAdmin) {
gaServicesUser.admin && gaServicesUser.adminLoggedIn && $scope.studioId || $scope._reject && $scope._reject("cancel"), $scope.data = {
requestError: "",
processing: !1
}, $scope.initUserSearchSettings = {
focusOnInit: !0
}, $scope.userSelected = {}, $scope.cancelUser = function() {
$scope.userSelected = {}
}, $scope.cancel = function() {
$scope._reject && $scope._reject("cancel")
}, $scope.changeOwner = function() {
$scope.userSelected.id && ($scope.data.processing = !0, gaApiAdmin.changeStudioOwner($scope.studioId, $scope.userSelected.id).then(function() {
$scope.data.processing = !1, $scope.data.requestError = "", $scope._resolve && $scope._resolve()
}).catch(function(errors) {
$scope.data.requestError = errors[0] && errors[0].msg ? errors[0].msg : "", $scope.data.processing = !1
}))
}
}), angular.module("ga.pages.admin.home", ["ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.api.admin", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.utils.date", "ga.utils.cache"]).controller("gaPagesAdminHomeController", function($rootScope, $scope, $timeout, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate, gaUtilsCache, gaApiAdmin) {
var forceRefresh = function() {
gaApiAdmin.getHomeStats(!0).then(function(result) {
$scope.gaHomeStats = result
})
};
forceRefresh(), $scope.forceRefresh = forceRefresh
}), angular.module("ga.pages.admin.logs", ["ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.api.admin", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.utils.date", "ga.utils.cache"]).controller("gaPagesAdminLogsController", function($rootScope, $scope, $timeout, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate, gaUtilsCache, gaApiAdmin) {
$scope.gaLogFilters = {
userId: null,
action: [],
resourceId: null,
resourceType: [],
pagination: 0,
order: null,
orderBy: null
};
var logsRefresh = function(force) {
gaApiAdmin.getUserLogs($scope.gaLogFilters, force).then(function(result) {
$scope.gaLogs = result
})
},
paginationPrev = function() {
$scope.gaLogFilters.pagination <= 0 ? $scope.gaLogFilters.pagination = 0 : ($scope.gaLogFilters.pagination -= 1, logsRefresh())
},
paginationNext = function() {
$scope.gaLogFilters.pagination += 1, logsRefresh()
};
logsRefresh(!0), $scope.logsRefresh = logsRefresh, $scope.paginationPrev = paginationPrev, $scope.paginationNext = paginationNext
}), angular.module("ga.pages.admin.search", ["ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.api.admin", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.utils.date", "ga.utils.cache", "ga.ui.notify", "ga.services.user", "ga.pages.admin.ui.searchUsers", "ga.pages.admin.ui.searchGames", "ga.pages.admin.ui.searchStudios"]).controller("gaPagesAdminSearchController", function($window, $rootScope, $scope, $timeout, $state, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate, gaUtilsCache, gaApiAdmin, gaUiNotify, gaServicesUser) {
$scope.activeSearch = "user", $scope.adminSearch = {
usersFound: [],
gamesFound: [],
studiosFound: [],
userSearchInFocus: {
focus: !1
},
gameSearchInFocus: {
focus: !1
},
studioSearchInFocus: {
focus: !1
}
}, $scope.initUserSearchSettings = {
focusOnInit: !0
}, $scope.$watch("adminSearch.userSearchInFocus", function(newVal, oldVal) {
newVal !== oldVal && newVal && newVal.focus && newVal.focus === !0 && ($scope.activeSearch = "user")
}), $scope.$watch("adminSearch.gameSearchInFocus", function(newVal, oldVal) {
newVal !== oldVal && newVal && newVal.focus && newVal.focus === !0 && ($scope.activeSearch = "game")
}), $scope.$watch("adminSearch.studioSearchInFocus", function(newVal, oldVal) {
newVal !== oldVal && newVal && newVal.focus && newVal.focus === !0 && ($scope.activeSearch = "studio")
}), $scope.impersonateUser = function(user_id, $event) {
$event.stopPropagation(), $event.preventDefault(), gaApiAdmin.impersonateUser(user_id).then(function() {
gaServicesUser.getUserData(!1, !0).then(function() {
gaUiNotify.show("user impersonation started!", 4e3, "default"), $state.go("user.home")
})
}).catch(function(errors) {
errors && errors.length && errors[0].msg && gaUiNotify.show(errors[0].msg, 4e3, "warning")
})
}, $scope.selectUser = function(user_id) {
$state.go("admin.user", {
userId: user_id
})
}, $scope.selectGame = function(game_id) {
$state.go("admin.game", {
gameId: game_id
})
}, $scope.selectStudio = function(studio_id) {
$state.go("admin.studio", {
studioId: studio_id
})
}
}), angular.module("ga.pages.admin.user", ["ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.api.admin", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.utils.date", "ga.utils.cache", "ga.services.user", "ga.ui.notify", "ga.pages.admin.controller"]).controller("gaPagesAdminUserController", function($window, $rootScope, $scope, $timeout, $state, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate, gaUtilsCache, gaApiAdmin, gaServicesUser, gaUiNotify, gaPagesAdminController) {
var user_id = $state.params.userId;
gaApiAdmin.getUser(user_id).then(function(data) {
$scope.gaUser = data
}), $scope.gaUserLogFilters = {
userId: parseInt(user_id, 10),
action: [],
resourceId: null,
resourceType: [],
pagination: 0,
order: null,
orderBy: null
};
var impersonateUser = function(user_id) {
gaApiAdmin.impersonateUser(user_id).then(function() {
gaServicesUser.getUserData(!1, !0).then(function() {
gaUiNotify.show("user impersonation started!", 4e3, "default"), $state.go("user.home")
})
}).catch(function(errors) {
errors && errors.length && errors[0].msg && gaUiNotify.show(errors[0].msg, 4e3, "warning")
})
},
logsRefresh = function(force) {
gaApiAdmin.getUserLogs($scope.gaUserLogFilters, force).then(function(result) {
$scope.gaUserLogs = result
})
},
showStudio = function(studioId) {
$state.go("admin.studio", {
studioId: studioId,
state: null
})
},
showGame = function(gameId) {
$state.go("admin.game", {
gameId: gameId,
state: null
})
},
paginationPrev = function() {
$scope.gaUserLogFilters.pagination <= 0 ? $scope.gaUserLogFilters.pagination = 0 : ($scope.gaUserLogFilters.pagination -= 1, logsRefresh())
},
paginationNext = function() {
$scope.gaUserLogFilters.pagination += 1, logsRefresh()
},
adminChangeUserEmailModal = function(userId) {
gaPagesAdminController.changeUserEmailModal(userId).then(function() {
gaUiNotify.show("email successfully changed!", 4e3, "default"), $state.go("admin.user", {
userId: userId
}, {
reload: !0
})
})
};
logsRefresh(!0), $scope.logsRefresh = logsRefresh, $scope.impersonateUser = impersonateUser, $scope.paginationPrev = paginationPrev, $scope.paginationNext = paginationNext, $scope.showGame = showGame, $scope.showStudio = showStudio, $scope.adminChangeUserEmailModal = adminChangeUserEmailModal
}), angular.module("ga.pages.admin.game", ["ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.api.admin", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.utils.date", "ga.utils.cache"]).controller("gaPagesAdminGameController", function($rootScope, $scope, $timeout, $state, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate, gaUtilsCache, gaApiAdmin) {
var game_id = $state.params.gameId;
gaApiAdmin.getGame(game_id).then(function(data) {
$scope.gaGame = data
}), $scope.gaGameLogFilters = {
userId: null,
action: [],
resourceId: parseInt(game_id, 10),
resourceType: ["game"],
pagination: 0,
order: null,
orderBy: null
}, $scope.gaGameLogs = gaApiAdmin.getUserLogs($scope.gaGameLogFilters, !0);
var logsRefresh = function(force) {
gaApiAdmin.getUserLogs($scope.gaGameLogFilters, force).then(function(result) {
$scope.gaGameLogs = result
})
},
paginationPrev = function() {
$scope.gaGameLogFilters.pagination <= 0 ? $scope.gaGameLogFilters.pagination = 0 : ($scope.gaGameLogFilters.pagination -= 1, logsRefresh())
},
showStudio = function(studioId) {
$state.go("admin.studio", {
studioId: studioId,
state: null
})
},
showUser = function(userId) {
$state.go("admin.user", {
userId: userId,
state: null
})
},
paginationNext = function() {
$scope.gaGameLogFilters.pagination += 1, logsRefresh()
};
logsRefresh(!0), $scope.logsRefresh = logsRefresh, $scope.paginationPrev = paginationPrev, $scope.paginationNext = paginationNext, $scope.showStudio = showStudio, $scope.showUser = showUser
}), angular.module("ga.pages.admin.studio", ["ga.api.data", "ga.api.meta", "ga.ui.modal", "ga.api.admin", "ga.ui.metricpicker", "ga.ui.dimensionpicker", "ga.utils.date", "ga.utils.cache", "ga.ui.notify", "ga.pages.admin.controller"]).controller("gaPagesAdminStudioController", function($rootScope, $scope, $timeout, $state, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate, gaUtilsCache, gaApiAdmin, gaUiNotify, gaPagesAdminController) {
var studio_id = $state.params.studioId;
gaApiAdmin.getStudio(studio_id).then(function(data) {
$scope.gaStudio = data
}), $scope.gaStudioLogFilters = {
userId: null,
action: [],
resourceId: parseInt(studio_id, 10),
resourceType: ["studio"],
pagination: 0,
order: null,
orderBy: null
};
var logsRefresh = function(force) {
gaApiAdmin.getUserLogs($scope.gaStudioLogFilters, force).then(function(result) {
$scope.gaStudioLogs = result
})
},
showUser = function(userId) {
$state.go("admin.user", {
userId: userId,
state: null
})
},
showGame = function(gameId) {
$state.go("admin.game", {
gameId: gameId,
state: null
})
},
paginationPrev = function() {
$scope.gaStudioLogFilters.pagination <= 0 ? $scope.gaStudioLogFilters.pagination = 0 : ($scope.gaStudioLogFilters.pagination -= 1, logsRefresh())
},
paginationNext = function() {
$scope.gaStudioLogFilters.pagination += 1, logsRefresh()
},
adminChangeStudioOwnerModal = function(studioId) {
gaPagesAdminController.adminChangeStudioOwnerModal(studioId).then(function() {
gaUiNotify.show("ownership successfully changed!", 4e3, "default"), $state.go("admin.studio", {
studioId: studioId
}, {
reload: !0
})
})
};
logsRefresh(!0), $scope.logsRefresh = logsRefresh, $scope.paginationPrev = paginationPrev, $scope.paginationNext = paginationNext, $scope.showUser = showUser, $scope.showGame = showGame, $scope.adminChangeStudioOwnerModal = adminChangeStudioOwnerModal
}), angular.module("ga.pages.admin.haystack", ["uiSlider", "ga.api.meta", "ga.utils.date", "ga.ui.pager", "ga.api.userDb.authenticated.haystack"]).service("haystackService", function($q, $http, gaUtilsDate, gaApiUserDbAuthenticatedHaystack) {
var data;
this.getData = function() {
return data ? $q.when(data) : gaApiUserDbAuthenticatedHaystack.haystackJson().then(function(data) {
return data = data, $q.when(data)
})
}, this.getPeriod = function() {
return this.getData().then(function(data) {
var period = {
start: 0,
end: 0
};
for (var key in data) {
period.start = gaUtilsDate.moment(data[key].snapshot_date).add(-7, "day").format("ddd DD MMM YYYY"), period.end = gaUtilsDate.moment(data[key].snapshot_date).format("ddd DD MMM YYYY");
break
}
return $q.when(period)
})
}
}).directive("haystackSlider", function() {
var template = '<span><label class="ga-label small">{{ meta.XXX.title }} (YYY)</label>\n<label class="ga-label inline bold small darker" style="font-size:.7em; width:10px; margin-right: 5px; text-align:right;">0</label>\n<slider class="slider small" floor="0" ceiling="{{ meta.XXX.niceMax }}" step="{{ meta.XXX.step }}" precision="0"\n ng-model-low="filters.XXX.min" ng-model-high="filters.XXX.max" style="width:125px;"></slider>\n<label class="ga-label inline bold small darker" style="font-size:.7em; width:30px; text-align:left;">{{ meta.XXX.type===\'percent\' ? \'100%\' : (meta.XXX.niceMax | formatUnitType:meta.XXX.type) }}</label>\n<input class="ga-input small" type="text" ng-model="filters.XXX.min" style="width:75px; padding: 0 5px;" />\n<input class="ga-input small" type="text" ng-model="filters.XXX.max" style="width:75px; padding: 0 5px;" /></span>\n',
compile = function(element, attrs) {
return element.html(element.get(0).innerHTML.replace(/XXX/g, attrs.meta).replace(/YYY/g, attrs.aggregation || "MEAN")),
function() {}
};
return {
restrict: "E",
replace: !0,
scope: !1,
template: template,
compile: compile
}
}).controller("gaPagesAdminHaystackController", function(haystackService, $scope, $rootScope, $http, $q, $timeout) {
$rootScope.logo = "/static/ga-app/images/haystack-logo.png", $scope.$on("$destroy", function() {
$rootScope.logo = null
}), $scope.sortBy = "dau", $scope.sortReverse = !0, $scope.page = 1, $scope.perPage = 15, $scope.pageCount = 1, $scope.games = [], $scope.gamesRender = [], $scope.gamesUrl = "/v1/admin/haystack.json", $scope.filters = {}, $scope.filtersInvalid = {}, $scope.paging = {
pages: 1,
page: 1
}, $scope.meta = {
game_id: {
title: "Game ID",
type: "number"
},
title: {
title: "Game",
type: "string"
},
genres: {
title: "Genres",
type: "string"
},
dau: {
title: "DAU",
type: "number"
},
mau: {
title: "MAU",
type: "number"
},
day_one_retention: {
title: "Ret. 1",
type: "percent"
},
day_seven_retention: {
title: "Ret. 7",
type: "percent"
},
average_session_length: {
title: "Avg. Session Length",
type: "number"
},
new_users: {
title: "New Users",
type: "number",
aggregation: "SUM"
},
total_revenue: {
title: "Total Revenue",
type: "number",
aggregation: "SUM"
},
paying_users: {
title: "Paying Users",
type: "number"
},
darppu: {
title: "DARPPU",
type: "number"
},
darpu: {
title: "DARPU",
type: "number"
},
transactions: {
title: "Transactions",
type: "number",
aggregation: "SUM"
},
active_users: {
title: "Active Users",
type: "number"
},
logins: {
title: "Logins",
type: "number"
},
email_report_subscribers: {
title: "Email Subscribers",
type: "number"
},
event_count: {
title: "Event Count",
type: "number",
aggregation: "SUM"
}
};
var setSort = function(key) {
$scope.page = 1, $scope.sortBy === key ? ($scope.sortReverse = !$scope.sortReverse, render()) : ($scope.sortBy = key, $scope.sortReverse = "string" === $scope.meta[key].type ? !1 : !0, render())
};
$scope.$watch("paging.page", function(newVal, oldVal) {
newVal !== oldVal && render()
});
var setPage = function(page) {
page && (1 > page && (page = 1), page > $scope.pageCount && (page = $scope.pageCount), $scope.page = page, render())
};
$scope.dataStatus = "init";
var loadData = function() {
$scope.dataStatus = "loading", haystackService.getData().then(function(data) {
haystackService.getPeriod().then(function(period) {
$scope.period = period, parseData(data)
})
})
};
$scope.dateStart = "", $scope.dateEnd = "";
var filterChangeTimer, parseData = function(data) {
data.forEach(function(game) {
for (var key in game) $scope.meta[key] && ($scope.meta[key].aggregation = $scope.meta[key].aggregation || "MEAN", $scope.meta[key].max = $scope.meta[key].max || 100, "number" === $scope.meta[key].type && ($scope.meta[key].max = $scope.meta[key].max || 100, $scope.meta[key].max = $scope.meta[key].max < game[key] ? game[key] : $scope.meta[key].max))
}), angular.forEach($scope.meta, function(meta, key) {
if ("percent" === meta.type) return meta.niceMax = 100, meta.step = 1, void($scope.filters[key] = {
min: 0,
max: meta.niceMax
});
if ("number" === meta.type) {
var i, iMax, temp = Math.ceil(meta.max);
for (iMax = temp.toString().length - 1, i = 1; iMax >= i; i++) temp /= 10;
for (temp = Math.ceil(temp), i = 1; iMax >= i; i++) temp = 10 * temp;
meta.niceMax = temp, meta.step = temp / 100, ("number" === meta.type || "percent" === meta.type) && ($scope.filters[key] = {
min: 0,
max: meta.niceMax
})
}
}), $scope.games = data, render(), $scope.dataStatus = ""
},
sort = function(a, b) {
var sortA = a[$scope.sortBy],
sortB = b[$scope.sortBy];
return "string" === $scope.meta[$scope.sortBy].type && (sortA = sortA.toString().toLowerCase(), sortB = sortB.toString().toLowerCase()), a[$scope.sortBy] < b[$scope.sortBy] ? $scope.sortReverse ? 1 : -1 : a[$scope.sortBy] === b[$scope.sortBy] ? 0 : $scope.sortReverse ? -1 : 1
},
sanitizeRegExp = function(string) {
return string.replace(/[\?\(\)\[\]\$\^\:\.]/g, "\\$&")
},
filter = function(game) {
var truthy = !0;
return angular.forEach($scope.filters, function(value, key) {
if (truthy) {
var meta = $scope.meta[key];
switch (meta.type) {
case "number":
isNumeric(value.min) && isNumeric(value.max) && (truthy = !(game[key] > value.max || game[key] < value.min));
break;
case "percent":
isNumeric(value.min) && isNumeric(value.max) && (truthy = !(100 * game[key] > value.max || 100 * game[key] < value.min));
break;
default:
var pattern = new RegExp(sanitizeRegExp($scope.filters[key]), "i");
truthy = (game[key] || "").toString().match(pattern)
}
}
}), truthy
},
render = function() {
var temp = $scope.games.filter(filter).sort(sort);
$scope.paging.pageCount = Math.ceil(temp.length / $scope.perPage) || 1, $scope.paging.page < 1 && ($scope.paging.page = 1), $scope.paging.page > $scope.paging.pageCount && ($scope.paging.page = $scope.paging.pageCount);
var sliceStart = ($scope.paging.page - 1) * $scope.perPage,
sliceEnd = $scope.paging.page * $scope.perPage;
temp = temp.slice(sliceStart, sliceEnd), $scope.gamesRender = temp
},
isNumeric = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n)
};
$scope.$watch("filters", function(newFilters, oldFilters) {
angular.equals(newFilters, oldFilters) || ($timeout.cancel(filterChangeTimer), filterChangeTimer = $timeout(render, 500))
}, !0), $scope.setSort = setSort, $scope.setPage = setPage, loadData()
}),
function() {
var MODULE_NAME, SLIDER_TAG, angularize, bindHtml, gap, halfWidth, hide, inputEvents, module, offset, offsetLeft, pixelize, qualifiedDirectiveDefinition, roundStep, show, sliderDirective, width;
MODULE_NAME = "uiSlider", SLIDER_TAG = "slider", angularize = function(element) {
return angular.element(element)
}, pixelize = function(position) {
return "" + position + "px"
}, hide = function(element) {
return element.css({
opacity: 0
})
}, show = function(element) {
return element.css({
opacity: 1
})
}, offset = function(element, position) {
return element.css({
left: position
})
}, halfWidth = function(element) {
return element[0].offsetWidth / 2
}, offsetLeft = function(element) {
return element[0].offsetLeft
}, width = function(element) {
return element[0].offsetWidth
}, gap = function(element1, element2) {
return offsetLeft(element2) - offsetLeft(element1) - width(element1)
}, bindHtml = function(element, html) {
return element.attr("ng-bind-html-unsafe", html)
}, roundStep = function(value, precision, step, floor) {
var decimals, remainder, roundedValue, steppedValue;
return null == floor && (floor = 0), null == step && (step = 1 / Math.pow(10, precision)), remainder = (value - floor) % step, steppedValue = remainder > step / 2 ? value + step - remainder : value - remainder, decimals = Math.pow(10, precision), roundedValue = steppedValue * decimals / decimals, roundedValue.toFixed(precision)
}, inputEvents = {
mouse: {
start: "mousedown",
move: "mousemove",
end: "mouseup"
},
touch: {
start: "touchstart",
move: "touchmove",
end: "touchend"
}
}, sliderDirective = function($timeout) {
return {
restrict: "EA",
scope: {
floor: "@",
ceiling: "@",
step: "@",
precision: "@",
ngModel: "=?",
ngModelLow: "=?",
ngModelHigh: "=?",
translate: "&"
},
template: '<span class="bar"></span><span class="bar selection"></span><span class="pointer"></span><span class="pointer"></span><span class="bubble selection"></span><span ng-bind-html-unsafe="translate({value: floor})" class="bubble limit"></span><span ng-bind-html-unsafe="translate({value: ceiling})" class="bubble limit"></span><span class="bubble"></span><span class="bubble"></span><span class="bubble"></span>',
compile: function(element, attributes) {
var ceilBub, cmbBub, e, flrBub, fullBar, highBub, lowBub, maxPtr, minPtr, range, refHigh, refLow, selBar, selBub, watchables, _i, _len, _ref, _ref1;
if (attributes.translate && attributes.$set("translate", "" + attributes.translate + "(value)"), range = null == attributes.ngModel && null != attributes.ngModelLow && null != attributes.ngModelHigh, _ref = function() {
var _i, _len, _ref, _results;
for (_ref = element.children(), _results = [], _i = 0, _len = _ref.length; _len > _i; _i++) e = _ref[_i], _results.push(angularize(e));
return _results
}(), fullBar = _ref[0], selBar = _ref[1], minPtr = _ref[2], maxPtr = _ref[3], selBub = _ref[4], flrBub = _ref[5], ceilBub = _ref[6], lowBub = _ref[7], highBub = _ref[8], cmbBub = _ref[9], refLow = range ? "ngModelLow" : "ngModel", refHigh = "ngModelHigh", bindHtml(selBub, "'Range: ' + translate({value: diff})"), bindHtml(lowBub, "translate({value: " + refLow + "})"), bindHtml(highBub, "translate({value: " + refHigh + "})"), bindHtml(cmbBub, "translate({value: " + refLow + "}) + ' - ' + translate({value: " + refHigh + "})"), !range)
for (_ref1 = [selBar, maxPtr, selBub, highBub, cmbBub], _i = 0, _len = _ref1.length; _len > _i; _i++) element = _ref1[_i], element.remove();
return watchables = [refLow, "floor", "ceiling"], range && watchables.push(refHigh), {
post: function(scope, element, attributes) {
var barWidth, boundToInputs, dimensions, maxOffset, maxValue, minOffset, minValue, ngDocument, offsetRange, pointerHalfWidth, updateDOM, valueRange, w, _j, _len1;
for (boundToInputs = !1, ngDocument = angularize(document), attributes.translate || (scope.translate = function(value) {
return value.value || 0
}), pointerHalfWidth = barWidth = minOffset = maxOffset = minValue = maxValue = valueRange = offsetRange = void 0, dimensions = function() {
var value, _j, _len1, _ref2, _ref3;
for (null == (_ref2 = scope.precision) && (scope.precision = 0), null == (_ref3 = scope.step) && (scope.step = 1), _j = 0, _len1 = watchables.length; _len1 > _j; _j++) value = watchables[_j], scope[value] = parseFloat(scope[value]), scope[value] = isNaN(scope[value]) ? "" : scope[value], "" !== scope[value] && scope[value] > maxValue ? scope[value] = maxValue : "" !== scope[value] && scope[value] < minValue && (scope[value] = minValue);
return scope.diff = roundStep(scope[refHigh] - scope[refLow], parseInt(scope.precision), parseFloat(scope.step), parseFloat(scope.floor)), pointerHalfWidth = halfWidth(minPtr), barWidth = width(fullBar), minOffset = 0, maxOffset = barWidth - width(minPtr), minValue = parseFloat(attributes.floor), maxValue = parseFloat(attributes.ceiling), valueRange = maxValue - minValue, offsetRange = maxOffset - minOffset
}, updateDOM = function() {
var adjustBubbles, bindToInputEvents, fitToBar, percentOffset, percentToOffset, percentValue, setBindings, setPointers;
return dimensions(), percentOffset = function(offset) {
return (offset - minOffset) / offsetRange * 100
}, percentValue = function(value) {
return (value - minValue) / valueRange * 100
}, percentToOffset = function(percent) {
return pixelize(percent * offsetRange / 100)
}, fitToBar = function(element) {
return offset(element, pixelize(Math.min(Math.max(0, offsetLeft(element)), barWidth - width(element))))
}, setPointers = function() {
var newHighValue, newLowValue;
return offset(ceilBub, pixelize(barWidth - width(ceilBub))), newLowValue = percentValue(scope[refLow]), offset(minPtr, percentToOffset(newLowValue)), offset(lowBub, pixelize(offsetLeft(minPtr) - halfWidth(lowBub) + pointerHalfWidth)), range ? (newHighValue = percentValue(scope[refHigh]), offset(maxPtr, percentToOffset(newHighValue)), offset(highBub, pixelize(offsetLeft(maxPtr) - halfWidth(highBub) + pointerHalfWidth)), offset(selBar, pixelize(offsetLeft(minPtr) + pointerHalfWidth)), selBar.css({
width: percentToOffset(newHighValue - newLowValue)
}), offset(selBub, pixelize(offsetLeft(selBar) + halfWidth(selBar) - halfWidth(selBub))), offset(cmbBub, pixelize(offsetLeft(selBar) + halfWidth(selBar) - halfWidth(cmbBub)))) : void 0
}, adjustBubbles = function() {
var bubToAdjust;
return fitToBar(lowBub), bubToAdjust = highBub, range && (fitToBar(highBub), fitToBar(selBub), gap(lowBub, highBub) < 10 ? (hide(lowBub), hide(highBub), fitToBar(cmbBub), show(cmbBub), bubToAdjust = cmbBub) : (show(lowBub), show(highBub), hide(cmbBub), bubToAdjust = highBub)), gap(flrBub, lowBub) < 5 ? hide(flrBub) : range && gap(flrBub, bubToAdjust) < 5 ? hide(flrBub) : show(flrBub), gap(lowBub, ceilBub) < 5 ? hide(ceilBub) : range && gap(bubToAdjust, ceilBub) < 5 ? hide(ceilBub) : show(ceilBub)
}, bindToInputEvents = function(pointer, ref, events) {
var onEnd, onMove, onStart;
return onEnd = function() {
return pointer.removeClass("active"), ngDocument.unbind(events.move), ngDocument.unbind(events.end)
}, onMove = function(event) {
var eventX, newOffset, newPercent, newValue;
return eventX = event.clientX || event.touches[0].clientX, newOffset = eventX - element[0].getBoundingClientRect().left - pointerHalfWidth, newOffset = Math.max(Math.min(newOffset, maxOffset), minOffset), newPercent = percentOffset(newOffset), newValue = minValue + valueRange * newPercent / 100, range && (ref === refLow ? newValue > scope[refHigh] && (ref = refHigh, minPtr.removeClass("active"), maxPtr.addClass("active")) : newValue < scope[refLow] && (ref = refLow, maxPtr.removeClass("active"), minPtr.addClass("active"))), newValue = roundStep(newValue, parseInt(scope.precision), parseFloat(scope.step), parseFloat(scope.floor)), scope[ref] = newValue, scope.$apply()
}, onStart = function(event) {
return pointer.addClass("active"), dimensions(), event.stopPropagation(), event.preventDefault(), ngDocument.bind(events.move, onMove), ngDocument.bind(events.end, onEnd)
}, pointer.bind(events.start, onStart)
}, setBindings = function() {
var bind, inputMethod, _j, _len1, _ref2, _results;
for (boundToInputs = !0, bind = function(method) {
return bindToInputEvents(minPtr, refLow, inputEvents[method]), bindToInputEvents(maxPtr, refHigh, inputEvents[method])
}, _ref2 = ["touch", "mouse"], _results = [], _j = 0, _len1 = _ref2.length; _len1 > _j; _j++) inputMethod = _ref2[_j], _results.push(bind(inputMethod));
return _results
}, setPointers(), adjustBubbles(), boundToInputs ? void 0 : setBindings()
}, $timeout(updateDOM), _j = 0, _len1 = watchables.length; _len1 > _j; _j++) w = watchables[_j], scope.$watch(w, updateDOM)
}
}
}
}
}, qualifiedDirectiveDefinition = ["$timeout", sliderDirective], (module = function(window, angular) {
return angular.module(MODULE_NAME, []).directive(SLIDER_TAG, qualifiedDirectiveDefinition)
})(window, window.angular)
}.call(this), angular.module("ga.pages.admin.export", ["ga.api.admin", "ga.ui.notify", "ga.pages.admin.ui.searchGames", "ga.components.moment"]).controller("gaPagesAdminExportController", function($window, $rootScope, $scope, $timeout, $http, $location, gaApiData, gaApiMeta, gaUiModal, gaUtilsDate, gaUtilsCache, gaApiAdmin, gaUiNotify, moment) {
$scope.inclusionGameSelected = {}, $scope.similarityGameSelected = {}, $scope.gameWithAppStoreSearchInitSettings = {
only_with_store_apps_defined: !0
}, $scope.$watch("similarityGameSelected", function(newVal, oldVal) {
newVal !== oldVal && newVal && "similarity" === $scope.gameType && newVal.id && getSimilarityScoresForGame(newVal.id)
}), $scope.gaCountries = null;
var pollRedshift = function() {
gaApiAdmin.getRedshiftStatus().then(function(result) {
if (result.createTime) try {
result.createTime = moment.utc(result.createTime, "X").format("MMMM Do YYYY, HH:mm")
} catch (e) {
result.createTime = "non valid date returned.."
}
$scope.redshiftStatus = result
}).catch(function() {
gaUiNotify.show("An error occured getting redshift status!", 4e3, "warning")
})
},
startExport = function() {
if (!$scope.loading) {
$scope.loading = !0;
var currentGameIdTarget = "none";
"similarity" === $scope.gameType && $scope.similarityGameSelected && $scope.similarityGameSelected.id ? currentGameIdTarget = $scope.similarityGameSelected.id : "inclusion" === $scope.gameType && $scope.inclusionGameSelected && $scope.inclusionGameSelected.id && (currentGameIdTarget = $scope.inclusionGameSelected.id);
var tempGameIds = [];
angular.forEach($scope.targetSimilarGames, function(value) {
tempGameIds.push(value.id)
});
var similiarGamesIdList = tempGameIds.join(",");
$scope.rankCriterion = $scope.rankCriterion1 + $scope.rankCriterion2 + $scope.rankCriterion3 + $scope.rankCriterion4 === "" ? "none" : $scope.rankCriterion1 + $scope.rankCriterion2 + $scope.rankCriterion3 + $scope.rankCriterion4, gaApiAdmin.getRedshiftIdentifiers($scope.idType, $scope.rankCriterion, $scope.gameType, currentGameIdTarget, similiarGamesIdList, $scope.countryType, $scope.targetCountries).then(function(result) {
$scope.exportData = $scope.exportData.concat(result.urls), $scope.loading = !1
}).catch(function(errors) {
$scope.loading = !1, errors && errors.length && errors[0].msg && gaUiNotify.show(errors[0].msg, 4e3, "warning")
})
}
},
getSimilarityScoresForGame = function(gameId) {
gaApiAdmin.getSimilarityScoresForGame(gameId).then(function(result) {
$scope.similarityScores = result
}).catch(function() {
gaUiNotify.show("An error occured getting similarity scores!", 4e3, "warning"), $scope.similarityScores = []
})
},
searchGameFilter = function(game) {
var gameSearchString = (game.id + " " + game.title).toLowerCase();
return $scope.gameListFilter && 0 !== $scope.gameListFilter.length && -1 === gameSearchString.indexOf($scope.gameListFilter) ? !1 : !0
},
searchCountryFilter = function(country) {
var countryCodeSearchString = country.code,
countrySearchString = country.name;
return $scope.countryListFilter && 0 !== $scope.countryListFilter.length && -1 === countryCodeSearchString.indexOf($scope.countryListFilter) && -1 === countrySearchString.indexOf(capitaliseFirstLetter($scope.countryListFilter)) ? !1 : !0
},
capitaliseFirstLetter = function(string) {
return string.charAt(0).toUpperCase() + string.slice(1)
},
refresh = function() {
$scope.idType = "ios", $scope.gameType = "all", $scope.countryType = "all", $scope.targetCountries = "none", $scope.targetSimilarGames = "none", $scope.threshold = .5, $scope.exportData = [], $scope.loading = !1, $scope.rankCriterion1 = "", $scope.rankCriterion2 = "", $scope.rankCriterion3 = "", $scope.rankCriterion4 = "", $scope.gaCountries || $http.get("/static/json/country-codes.json").then(function(result) {
$scope.gaCountries = result.data
}), pollRedshift()
},
assignCountries = function(countries) {
$scope.targetCountries = countries
},
greaterThanThreshold = function(game) {
return game.score >= $scope.threshold / 100
};
refresh(), $scope.refresh = refresh, $scope.startExport = startExport, $scope.pollRedshift = pollRedshift, $scope.searchGameFilter = searchGameFilter, $scope.searchCountryFilter = searchCountryFilter, $scope.assignCountries = assignCountries, $scope.greaterThanThreshold = greaterThanThreshold
}), angular.module("ga.filters.numberFormat", ["ga.values.user"]).filter("gaNumberFormat", function($rootScope, gaValuesUser) {
var numberFormatSettings = {
1: {
thousandSeperator: ",",
comma: "."
},
2: {
thousandSeperator: ".",
comma: ","
},
3: {
thousandSeperator: "'",
comma: "."
},
4: {
thousandSeperator: " ",
comma: ","
}
},
getValue = function(input, noShorten, forcedPostfix, forcedFormatType) {
var userNumberFormat = 1;
forcedFormatType && forcedFormatType > 0 ? userNumberFormat = forcedFormatType : gaValuesUser.settings.numberFormat && gaValuesUser.settings.numberFormat > 0 && (userNumberFormat = gaValuesUser.settings.numberFormat);
var currentFormatSettings = numberFormatSettings[userNumberFormat];
if (!angular.isNumber(input)) return input;
var postfix = "",
isNegative = 0 > input;
input = Math.abs(input);
var commaDecimals = 2;
if (1 > input && input > 0)
for (var tmp = input.toFixed(5).split(".")[1], i = 0; 5 > i; i++)
if ("0" !== tmp.substr(i, 1)) {
commaDecimals = i + 1;
break
}
if (2 > commaDecimals && (commaDecimals = 2), !noShorten) {
var pf = getPostfix(input, forcedPostfix);
input = pf.input, postfix = pf.postfix
}
var formatted = input.toFixed(commaDecimals);
return formatted = formatted.split("."), formatted = formatted[0].replace(/\B(?=(\d{3})+(?!\d))/g, currentFormatSettings.thousandSeperator) + currentFormatSettings.comma + formatted[1], (isNegative ? "-" : "") + formatted + postfix
},
getValues = function(input, noShorten, forcedFormatType) {
var highest = Math.abs(input[input.length - 1]),
postfix = getPostfix(highest).postfix,
returnObj = {};
return angular.forEach(input, function(value) {
returnObj[value] = getValue(value, noShorten, postfix, forcedFormatType)
}), returnObj
},
getPostfix = function(input, forcedPostfix) {
var postfix = "";
return input > 0 && (input >= 1e12 || "T" === forcedPostfix ? (postfix = "T", input /= 1e12) : input >= 1e9 || "B" === forcedPostfix ? (postfix = "B", input /= 1e9) : input >= 1e6 || "M" === forcedPostfix ? (postfix = "M", input /= 1e6) : (input >= 1e4 || "K" === forcedPostfix) && (postfix = "K", input /= 1e3)), {
postfix: postfix,
input: input
}
};
return function(input, noShorten, forcedFormatType) {
return angular.isArray(input) ? getValues(input, noShorten, forcedFormatType) : getValue(input, noShorten, null, forcedFormatType)
}
}), angular.module("ga.filters.dateFormat", ["ga.utils.date"]).filter("gaFiltersDateFormat", function(gaUtilsDate) {
return function(interval) {
return gaUtilsDate.getIntervalTitle(interval)
}
}), angular.module("ga.filters.titleCase", []).filter("gaFiltersTitleCase", function() {
return function(input) {
var words = angular.isString(input) ? input.split(" ") : [];
return words = words.map(function(word) {
return word && (word = word.charAt(0).toUpperCase() + word.toLowerCase().slice(1)), word
}), words.join(" ")
}
}), angular.module("ga.filters.range", []).filter("gaFiltersRange", function() {
return function(input, total) {
total = parseInt(total, 10);
for (var i = 0; total > i; i++) input.push(i);
return input
}
}), angular.module("ga.filters.slice", []).filter("gaFiltersSlice", ["$parse",
function() {
return function(input, start, end) {
return start = parseInt(start, 10), end = end ? parseInt(end, 10) : end, input ? input.slice(start, end) : []
}
}
]), angular.module("ga.utils.helpers", []).factory("gaHelpers", function() {
var isScope = function(obj) {
return obj && obj.$evalAsync && obj.$watch
},
isWindow = function(obj) {
return obj && obj.document && obj.location && obj.alert && obj.setInterval
},
isRegExp = function(value) {
return "[object RegExp]" === {}.toString.call(value)
},
equals = function(o1, o2, ignore) {
if (o1 === o2) return !0;
if (null === o1 || null === o2) return !1;
if (o1 !== o1 && o2 !== o2) return !0;
var length, key, keySet, t1 = typeof o1,
t2 = typeof o2;
if (t1 === t2 && "object" === t1) {
if (!angular.isArray(o1)) {
if (angular.isDate(o1)) return angular.isDate(o2) && o1.getTime() === o2.getTime();
if (isRegExp(o1) && isRegExp(o2)) return o1.toString() === o2.toString();
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || angular.isArray(o2)) return !1;
keySet = {};
for (key in o1)
if (!(ignore && ignore.indexOf(key) > -1 || "$" === key.charAt(0) || angular.isFunction(o1[key]))) {
if (!equals(o1[key], o2[key], ignore)) return !1;
keySet[key] = !0
}
for (key in o2)
if (!(ignore && ignore.indexOf(key) > -1 || keySet.hasOwnProperty(key) || "$" === key.charAt(0) || void 0 === o2[key] || angular.isFunction(o2[key]))) return !1;
return !0
}
if (!angular.isArray(o2)) return !1;
if ((length = o1.length) === o2.length) {
for (key = 0; length > key; key++)
if (!equals(o1[key], o2[key], ignore)) return !1;
return !0
}
}
return !1
},
copy = function(object) {
try {
return JSON.parse(JSON.stringify(object))
} catch (e) {
return angular.copy(object)
}
},
parse = function(string, fallback) {
var object;
try {
object = JSON.parse(string)
} catch (e) {
object = fallback
} finally {
object = object || fallback || {}
}
return object
},
serializeObject = function(object) {
var values = {};
for (var key in object) values[key] = object[key];
return values
},
customSort = function(name, type) {
return function(o, p) {
var a, b;
return o && p && "object" == typeof o && "object" == typeof p ? (a = o[name], b = p[name], a === b ? "function" == typeof type ? type(o, p) : o : typeof a == typeof b ? a > b ? -1 : 1 : typeof b > typeof a ? -1 : 1) : void 0
}
},
scrollTop = function() {
return "undefined" != typeof window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ? document.body.scrollTop : 0
},
randomNumber = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
};
return {
isScope: isScope,
isWindow: isWindow,
isRegExp: isRegExp,
equals: equals,
copy: copy,
parse: parse,
serializeObject: serializeObject,
customSort: customSort,
randomNumber: randomNumber,
scrollTop: scrollTop
}
}), angular.module("ga.utils.helpers.clickElement", []).directive("gaUtilsHelpersClickElement", function($parse) {
var linkingFunction = function($scope, $element, $attrs) {
var segments = $attrs.gaUtilsHelpersClickElement.split(":"),
event = "click",
el = segments[0],
fn = $parse(segments[1]),
eventFn = function(e) {
$scope.$apply(function() {
fn($scope, {
$event: e
})
})
};
$element.on(event, el, eventFn), $scope.$on("$destroy", function() {
$element.off(event, el, eventFn)
})
};
return {
restrict: "AC",
link: linkingFunction,
scope: !1
}
}), angular.module("ga.utils.helpers.focusElement", []).directive("gaUtilsHelpersFocusElement", function() {
return function($scope, $element, $attr) {
$scope.$watch($attr.ngFocusElement, function(value) {
value && ($element.select(), $element.focus())
})
}
}).directive("focusElement", function($timeout) {
return function($scope, $element, $attr) {
$scope.$watch($attr.focusElement, function(value) {
value && $timeout(function() {
$element.select(), $element.focus()
}, 0)
})
}
}).filter("capitalize", function() {
return function(input) {
return angular.isString(input) ? input.charAt(0).toUpperCase() + input.slice(1) : input
}
}), angular.module("ga.utils.helpers.on", []).directive("gaUtilsHelpersOn", function($parse) {
var linkingFunction = function($scope, $element, $attrs) {
var segments = $attrs.gaUtilsHelpersOn.split(":"),
event = segments.length > 1 ? segments[0] : "click",
fn = $parse(segments.length > 1 ? segments[1] : segments[0]),
eventFn = function(e) {
$scope.$apply(function() {
fn($scope, {
$event: e
})
})
};
$element.on(event, eventFn), $scope.$on("$destroy", function() {
$element.off(event, eventFn)
})
};
return {
restrict: "AC",
link: linkingFunction,
scope: !1
}
}), angular.module("ga.utils.date", ["ga.components.moment", "ga.values.user"]).factory("gaUtilsDate", function($rootScope, gaValuesUser, moment) {
var cachedTimeZoneList = null,
getInterval = function(start, end, resolution, offset) {
start = moment(start).utc().startOf("day"), end = moment(end).utc().startOf("day"), offset = offset || 0;
for (var interval = [], ts = start; end > ts; ts += resolution) interval.push(ts + offset);
return interval
},
generateCalendar = function(year, month, startOfWeek) {
for (var date = moment.utc(month + "-" + year, "M-YYYY"), shiftDay = (date.day() || 7) - ("Sunday" === startOfWeek ? 0 : 1), firstDay = date.clone().add("days", -shiftDay), calendarArray = [], y = 0; 5 >= y; y++) {
calendarArray[y] = [];
for (var x = 0; 6 >= x; x++) {
var tmpDate = moment(firstDay + 864e5 * (7 * y + x)).utc();
calendarArray[y][x] = {
timestamp: tmpDate.valueOf(),
date: tmpDate.date(),
month: tmpDate.month(),
year: tmpDate.year(),
offMonth: tmpDate.month() !== date.month()
}
}
}
return calendarArray
},
validateDate = function(str, options) {
if (!str) return !1;
var mDate;
return options = angular.extend({
dateMin: null,
dateMax: null,
formats: null,
test: null
}, options), options.dateMin = options.dateMin || moment.utc([2e3, 0, 1]).valueOf(), mDate = moment.utc(str, options.formats), mDate.isValid() ? options.dateMin && mDate < options.dateMin ? !1 : options.dateMax && mDate > options.dateMax ? !1 : mDate : !1
},
getPeriodRange = function(period) {
var range = {
start: 0,
end: 0
};
switch (period) {
case "last24hours":
var start = moment().utc().startOf("hour");
range.start = start.clone().add("days", -1).valueOf(), range.end = start.valueOf();
break;
case "yesterday":
range.start = moment.utc().startOf("day").add("days", -1).valueOf(), range.end = moment.utc().startOf("day").add("days", -1).valueOf();
break;
case "lastWeek":
range.start = moment.utc().startOf("week").add("days", -6).valueOf(), range.end = moment.utc(range.start).add("days", 6).valueOf();
break;
case "lastMonth":
range.start = moment.utc().startOf("month").add("month", -1).valueOf(), range.end = moment.utc().startOf("month").add("days", -1).valueOf();
break;
case "last30Days":
range.start = moment.utc().startOf("day").add("days", -31).valueOf(), range.end = moment.utc(range.start).add("days", 30).valueOf();
break;
default:
var pattern = /last([0-9]+)(weeks|months|days)/gi,
values = pattern.exec(period);
if (values && 3 === values.length) {
var ln = values[1],
type = values[2];
"weeks" === type ? (range.start = moment.utc().startOf("week").add("days", -7 * parseInt(ln - 1, 10) - 6).valueOf(), range.end = moment.utc(range.start).add("days", 6 + 7 * parseInt(ln - 1, 10)).valueOf()) : "months" === type && (range.start = moment.utc().startOf("month").add("month", -1 * parseInt(ln, 10)).valueOf(), range.end = moment.utc().startOf("month").add("days", -1).valueOf())
}
}
return range
},
getPeriodRangeName = function(period) {
var name = "";
switch (period) {
case "yesterday":
name = "Yesterday";
break;
case "lastWeek":
name = "Last week";
break;
case "lastMonth":
name = "Last month";
break;
default:
var pattern = /last([0-9]+)(weeks|months|Days)/gi,
values = pattern.exec(period);
if (3 === values.length) {
var ln = values[1],
type = values[2];
"weeks" === type ? name = "Last " + parseInt(ln, 10) + " weeks" : "months" === type ? name = "Last " + parseInt(ln, 10) + " months" : "Days" === type && (name = "Last " + parseInt(ln, 10) + " days")
}
}
return name
},
getPreviousPeriod = function(period) {
var start = moment.utc(period.start),
days = Math.abs(start.diff(period.end, "days")) + 1,
weeks = Math.ceil(days / 7);
start.add("weeks", -weeks);
var end = start.clone().add("days", days - 1).valueOf();
return start = start.valueOf(), {
start: start,
end: end
}
},
validateRange = function(range) {
return null === range || void 0 === range ? !1 : range.start && range.end && moment(range.start).isValid() && moment(range.end).isValid() ? range.start > range.end ? !1 : !0 : !1
},
getIntervalTitle = function(interval) {
var formatString = getCurrentDateFormatString(),
formatStringNoYear = formatString.replace("YYYY", "").trim(),
title = "";
if (interval && interval.start && interval.end) {
var dateStart = moment.utc(interval.start),
dateEnd = moment.utc(interval.end);
dateStart.isValid() && dateEnd.isValid() && (interval.start === interval.end ? title = dateStart.format(formatString) : (title = dateStart.format(dateStart.year() !== dateEnd.year() ? formatString : formatStringNoYear), title = dateStart.format(dateStart.year() !== dateEnd.year() ? formatString : formatStringNoYear), title += " - " + dateEnd.format(formatString)))
}
return title
},
getIntervalTitles = function(interval, compareInterval, html) {
var title = "";
return interval || compareInterval ? title = compareInterval ? html ? "<div>" + getIntervalTitle(interval) + "</div><em>" + getIntervalTitle(compareInterval) + "</em>" : getIntervalTitle(interval) + " - " + getIntervalTitle(compareInterval) : getIntervalTitle(interval) : title
},
getTimeZoneData = function(timeZoneId, offsetMinutes) {
var offset = moment().tz(timeZoneId).format("Z");
return {
displayName: timeZoneId,
offsetMinutesUTC: offsetMinutes,
displayNameOffset: "(UTC " + offset + ") " + timeZoneId
}
},
getTimeZonesAvailable = function(force) {
if (null !== cachedTimeZoneList && !force) return cachedTimeZoneList;
var _compareFunction = function(a, b) {
return a.offsetMinutesUTC < b.offsetMinutesUTC ? -1 : a.offsetMinutesUTC > b.offsetMinutesUTC ? 1 : 0
},
timeZones = moment.tz.zones(),
parsedTimeZones = [];
try {
for (var i = 0; i < timeZones.length; i += 1) {
var offsetTotalMinutes = parseInt(timeZones[i].zones[timeZones[i].zones.length - 1].offset, 10),
timeZoneEntry = getTimeZoneData(timeZones[i].displayName, offsetTotalMinutes);
parsedTimeZones.push(timeZoneEntry)
}
} catch (e) {
parsedTimeZones = []
}
return parsedTimeZones.sort(_compareFunction), cachedTimeZoneList = parsedTimeZones, parsedTimeZones
},
getCurrentTimeString = function(timeFormat, timeZoneId) {
timeZoneId = timeZoneId || gaValuesUser.settings.timeZone, timeFormat = timeFormat || gaValuesUser.settings.timeFormat;
var result = null;
return result = moment().tz(timeZoneId).format("24hour" === timeFormat ? "HH:mm" : "12hour" === timeFormat ? "hh:mm A" : "HH:mm")
},
getCurrentDateString = function(dateFormat, timeZoneId) {
timeZoneId = timeZoneId || gaValuesUser.settings.timeZone, dateFormat = dateFormat || gaValuesUser.settings.dateFormat;
var result = null;
return result = moment().tz(timeZoneId).format("MDY" === dateFormat ? "MMM D. YYYY" : "DMY" === dateFormat ? "D. MMM YYYY" : "YMD" === dateFormat ? "YYYY MMM D" : "MMM D. YYYY")
},
getCurrentTimeFormat = function() {
switch (gaValuesUser.settings.timeFormat) {
case "12hour":
return "h:mm A";
default:
return "HH:mm"
}
},
getCurrentDateFormat = function() {
switch (gaValuesUser.settings.dateFormat) {
case "DMY":
return "DD.MM.YYYY";
case "MDY":
return "MM.DD.YYYY";
default:
return "YYYY.MM.DD"
}
},
getCurrentDateFormatString = function() {
switch (gaValuesUser.settings.dateFormat) {
case "DMY":
return "D. MMM YYYY";
case "MDY":
return "MMM D. YYYY";
default:
return "YYYY MMM D"
}
};
return {
moment: moment,
generateCalendar: generateCalendar,
validateDate: validateDate,
getInterval: getInterval,
getPeriodRange: getPeriodRange,
getPreviousPeriod: getPreviousPeriod,
validateRange: validateRange,
getIntervalTitle: getIntervalTitle,
getIntervalTitles: getIntervalTitles,
getTimeZonesAvailable: getTimeZonesAvailable,
getTimeZoneData: getTimeZoneData,
getCurrentTimeString: getCurrentTimeString,
getCurrentDateString: getCurrentDateString,
getCurrentTimeFormat: getCurrentTimeFormat,
getCurrentDateFormat: getCurrentDateFormat,
getCurrentDateFormatString: getCurrentDateFormatString,
getPeriodRangeName: getPeriodRangeName
}
}), angular.module("ga.utils.detect", []).factory("gaDetect", function($window) {
var _browser, OS = function(overrideNavigator) {
var nav = overrideNavigator || $window.navigator.appVersion;
return -1 !== nav.indexOf("Win") ? "windows" : -1 !== nav.indexOf("Mac") ? "mac" : -1 !== nav.indexOf("X11") ? "unix" : -1 !== nav.indexOf("Linux") ? "linux" : "unknown"
},
browser = function(overrideNavigator) {
var nav = overrideNavigator || $window.navigator;
return void 0 === _browser && (nav.appVersion && -1 !== nav.appVersion.indexOf("Chrome") ? _browser = "chrome" : nav.appVersion && -1 !== nav.appVersion.indexOf("Firefox") ? _browser = "firefox" : nav.appVersion && -1 !== nav.appVersion.indexOf("Apple") ? _browser = "safari" : (_browser = "unknown", "Microsoft Internet Explorer" === nav.appName ? _browser = "ie" : "Netscape" === nav.appName && nav.appVersion && nav.appVersion.indexOf("Trident") > -1 && (_browser = "ie"))), _browser
},
cookiesEnabled = function() {
var cookieEnabled = navigator.cookieEnabled ? !0 : !1;
return "undefined" != typeof navigator.cookieEnabled || cookieEnabled || (document.cookie = "testcookie", cookieEnabled = -1 !== document.cookie.indexOf("testcookie") ? !0 : !1), cookieEnabled
};
return {
OS: OS,
browser: browser,
cookiesEnabled: cookiesEnabled
}
}), angular.module("ga.utils.mailman", []).factory("gaUtilsMailman", function() {
function parseEventName(event) {
var evt = event.split(":");
return 3 === evt.length ? {
namespace: evt[0],
id: evt[1],
event: evt[2]
} : null
}
function publish(event, data) {
var eventObj = parseEventName(event);
if (null !== eventObj)
for (var i = 0, max = subscribers.length; max > i; i++) !("*" !== eventObj.namespace && "*" !== subscribers[i].namespace && subscribers[i].namespace !== eventObj.namespace || "*" !== eventObj.id && "*" !== subscribers[i].id && subscribers[i].id !== eventObj.id || "*" !== eventObj.event && "*" !== subscribers[i].event && subscribers[i].event !== eventObj.event || !angular.isFunction(subscribers[i].callback) || !subscribers[i].callback({
sender: eventObj.id,
event: eventObj.event
}, data))
}
function request(event, callback) {
var eventObj = parseEventName(event);
if (null !== eventObj)
for (var i = 0, max = responders.length; max > i; i++)("*" === eventObj.namespace || "*" === responders[i].namespace || responders[i].namespace === eventObj.namespace) && ("*" === eventObj.id || "*" === responders[i].id || responders[i].id === eventObj.id) && ("*" === eventObj.event || "*" === responders[i].event || responders[i].event === eventObj.event) && angular.isFunction(responders[i].callback) && angular.isFunction(callback) && callback({
sender: responders[i].id,
event: responders[i].event
}, responders[i].callback({
sender: eventObj.id,
event: eventObj.event
}))
}
function subscribe(event, callback, clientid) {
var eventObj = parseEventName(event);
null !== eventObj && subscribers.push({
namespace: eventObj.namespace,
event: eventObj.event,
id: eventObj.id,
callback: callback,
clientid: clientid
})
}
function unsubscribeAll(clientid) {
var tmpArray = [];
angular.forEach(subscribers, function(sub) {
sub.clientid !== clientid && tmpArray.push(sub)
}), subscribers = tmpArray
}
function respond(event, callback) {
var eventObj = parseEventName(event);
null !== eventObj && responders.push({
namespace: eventObj.namespace,
event: eventObj.event,
id: eventObj.id,
callback: callback
})
}
function outOfOffice(id) {
suspended.push(id)
}
function back(id) {
var index = suspended.indexOf(id);
index > -1 && (suspended[index] = null)
}
function destroy() {}
var subscribers = [],
responders = [],
suspended = [];
return {
request: request,
respond: respond,
subscribe: subscribe,
unsubscribeAll: unsubscribeAll,
publish: publish,
destroy: destroy,
outOfOffice: outOfOffice,
back: back
}
}), angular.module("ga.utils.throttle", []).factory("gaUtilsThrottle", function() {
var debounce = function(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments,
later = function() {
timeout = null, immediate || func.apply(context, args)
};
immediate && !timeout && func.apply(context, args), clearTimeout(timeout), timeout = setTimeout(later, wait)
}
},
throttle = function(func, wait) {
var context, args, timeout, throttling, more, result, whenDone = debounce(function() {
more = throttling = !1
}, wait);
return function() {
context = this, args = arguments;
var later = function() {
timeout = null, more && func.apply(context, args), whenDone()
};
return timeout || (timeout = setTimeout(later, wait)), throttling ? more = !0 : result = func.apply(context, args), whenDone(), throttling = !0, result
}
};
return {
throttle: throttle,
debounce: debounce
}
}), angular.module("ga.utils.transform", ["ga.api.meta", "ga.utils.date"]).factory("gaUtilsTransform", function($injector, $q, $filter, gaUtilsDate, gaApiMeta) {
var parse = function(requestObject, dimensions, sortArray) {
var deferred = $q.defer();
if (requestObject.mainQuery.filter && "*" === requestObject.mainQuery.filter.values[0])
if (requestObject.mainQuery.unknownDimension === !0) {
var tmpDimensions = _getDimensionsFromData(requestObject),
tmp = _parse(requestObject, tmpDimensions);
deferred.resolve(_parse(requestObject, tmp.sortArray))
} else $injector.invoke(function(gaApiData) {
gaApiData.getValue("/dimensions", requestObject.mainQuery.gameId).then(function(dimensions) {
deferred.resolve(_parse(requestObject, dimensions[requestObject.mainQuery.filter.dimension], sortArray))
})
});
else deferred.resolve(requestObject.mainQuery.filter ? _parse(requestObject, requestObject.mainQuery.filter.values) : _parse(requestObject));
return deferred.promise
},
_getDimensionsFromData = function(requestObject) {
var dimensions = [],
dimensionType = requestObject.mainQuery.filter.dimension;
return angular.forEach(requestObject.queries, function(query, queryIndex) {
var queryMetric = query.metric.event,
typeData = requestObject.response[queryIndex].timeseries ? "timeseries" : "aggregated";
angular.forEach(requestObject.response[queryIndex][typeData][queryMetric], function(dataEntry) {
dataEntry.dimensions && dataEntry.dimensions[dimensionType] && angular.forEach(dataEntry.dimensions[dimensionType], function(dimensionData, dimensionIndexName) {
dimensions.indexOf(dimensionIndexName) < 0 && dimensions.push(dimensionIndexName)
})
})
}), 0 === dimensions.length ? [] : dimensions
},
_parse = function(requestObject, dimensions, sortArray) {
var doSort = !0;
requestObject = preprocessResponse(requestObject);
var parsed = {
aggregation: requestObject.mainQuery.aggregation,
aggregation_aggregated: requestObject.mainQuery.aggregation_aggregated,
group: requestObject.mainQuery.group,
normalized: requestObject.mainQuery.normalized,
filter: requestObject.mainQuery.filter,
interval: requestObject.mainQuery.interval,
intervalDisplay: gaUtilsDate.getIntervalTitle(requestObject.mainQuery.interval),
meta: {
type: "date",
unit: "date",
title: "Date"
},
sortArray: [],
data: {}
};
if (requestObject.mainQuery.realtime && (parsed.meta = {
type: "time",
unit: "time",
title: "Time",
tooltipType: "datetime"
}), requestObject.mainQuery.filter && (parsed.dimensionMeta = gaApiMeta.getDimension(requestObject.mainQuery.filter.dimension), parsed.dimensionMeta.parent && (parsed.dimensionMeta.title = parsed.dimensionMeta.parent.title + " " + parsed.dimensionMeta.title), parsed.dimensionMeta.type = parsed.dimensionMeta.type || "dimension", requestObject.mainQuery.rollup && parsed.dimensionMeta.name.indexOf("cohort_") > -1 && (parsed.dimensionMeta.name = parsed.dimensionMeta.name + "_" + requestObject.mainQuery.rollup)), requestObject.mainQuery.normalized) parsed.meta = {
type: requestObject.mainQuery.filter.dimension,
unit: "none",
title: "Week"
}, requestObject.mainQuery.rollup && requestObject.mainQuery.filter.dimension.indexOf("cohort_") > -1 && (parsed.meta.type = requestObject.mainQuery.filter.dimension + "_" + requestObject.mainQuery.rollup);
else if ("dimension" === requestObject.mainQuery.group) parsed.meta = gaApiMeta.getDimension(requestObject.mainQuery.filter.dimension), parsed.meta.parent && (parsed.meta.title = parsed.meta.parent.title + " " + parsed.meta.title);
else if ("value" === requestObject.mainQuery.group) {
var tmpMeta = gaApiMeta.getMetric(requestObject.mainQuery.metric[0].category, requestObject.mainQuery.metric[0].event),
title = gaApiMeta.getMetricDisplay(tmpMeta.category, tmpMeta.event, !0);
doSort = !tmpMeta.noSort, parsed.meta = {
type: tmpMeta.keyUnit || "string",
unit: "none",
title: title,
subTitle: tmpMeta.groupValuesUnit || ""
}
}
if (requestObject.mainQuery.compareInterval && (parsed.compare = !0, parsed.compareInterval = requestObject.mainQuery.compareInterval, parsed.compareIntervalDisplay = gaUtilsDate.getIntervalTitle(requestObject.mainQuery.compareInterval)), angular.forEach(requestObject.queries, function(query, queryIndex) {
if (requestObject.queries.length !== requestObject.response.length) throw "Queries length does not match response length";
var dataType = "time" === query.group ? "timeseries" : "aggregated",
queryResponse = requestObject.response[queryIndex][dataType],
newQueryResponse = {};
newQueryResponse = "histogram" === query.aggregation ? splitHistogram(query, queryResponse, dataType, dimensions) : queryResponse;
var eventNameArray = [];
if (angular.forEach(newQueryResponse, function(eventData, eventName) {
eventNameArray.push("business" === query.metric.category || "design" === query.metric.category ? {
event: eventName.split(":").slice(-1)[0],
path: eventName
} : eventName)
}), "business" === query.metric.category || "design" === query.metric.category) {
var isNotNumber = eventNameArray.filter(function(data) {
return isNaN(data.event)
}).length;
eventNameArray = eventNameArray.sort(isNotNumber ? function(a, b) {
return a.event && b.event ? a.event.localeCompare(b.event) : 0
} : function(a, b) {
return (parseFloat(a.event) || 0) - (parseFloat(b.event) || 0)
}), eventNameArray = eventNameArray.map(function(e) {
return e.path
})
} else "core" !== query.metric.category && eventNameArray.sort();
angular.forEach(eventNameArray, function(eventName) {
var eventData = newQueryResponse[eventName];
switch (dataType) {
case "timeseries":
parsed.data["timeseries" + (query.isCompare ? "Compare" : "")] = parsed.data["timeseries" + (query.isCompare ? "Compare" : "")] || [], parsed.data["timeseries" + (query.isCompare ? "Compare" : "")].push(parseTimeseries(query, eventData, eventName, dimensions));
break;
case "aggregated":
parsed.data["aggregated" + (query.isCompare ? "Compare" : "")] = parsed.data["aggregated" + (query.isCompare ? "Compare" : "")] || [], parsed.data["aggregated" + (query.isCompare ? "Compare" : "")].push(parseAggregated(query, eventData, eventName, dimensions));
break;
default:
throw "unknown dataType: " + dataType
}
})
}), requestObject.mainQuery.filter && !requestObject.mainQuery.merged && parsed.data.aggregated && parsed.data.aggregated[0]) {
var tmp = sortAndSlice(requestObject.mainQuery, requestObject.mainQuery.top, parsed.data, sortArray);
parsed.data = tmp.data, parsed.sortArray = tmp.sortArray
}
if (requestObject.mainQuery.compareInterval && requestObject.mainQuery.mergeCompare && (parsed.data = mergeCompare(parsed.data)), "value" === requestObject.mainQuery.group) {
var groupedEvents = [],
dimensionsList = parsed.data.aggregated && parsed.data.aggregated[0].data.map(function(item) {
return item.dimension
}) || [];
angular.forEach(parsed.data.aggregated, function(data, eventIndex) {
angular.forEach(dimensionsList || [null], function(dimension, dimensionIndex) {
groupedEvents[dimensionIndex] = groupedEvents[dimensionIndex] || {
total: null,
cTotal: null,
data: [],
meta: {
title: dimension || "Value",
subTitle: data.meta.unitTitle ? dimension || "" : ""
}
};
var dimensionData = data.data.filter(function(item) {
return dimension ? item.dimension === dimension : !item.dimension
})[0] || {};
groupedEvents[dimensionIndex].data[eventIndex] = {
cValue: angular.isNumber(dimensionData.cValue) ? dimensionData.cValue : null,
value: angular.isNumber(dimensionData.value) ? dimensionData.value : null,
dimension: data.meta.title,
index: eventIndex,
meta: data.meta,
vDimension: dimension
}
})
}), parsed.data.aggregated = groupedEvents
}
if ("value" === requestObject.mainQuery.group && requestObject.mainQuery.valuesTop && parsed.data.aggregated.map(function(data) {
doSort && (data.data = data.data.sort(function(a, b) {
return (b.value || 0) - (a.value || 0)
})), data.data = data.data.slice(0, requestObject.mainQuery.valuesTop)
}), requestObject.mainQuery.noAggregation) {
var hasTimeData = parsed.data.timeseries.some(function(item) {
var hasSubData = item.data.some(function(subItem) {
var hasSubValueData = subItem.data.some(function(subItemValue) {
return null !== subItemValue.total
});
return hasSubValueData
});
return hasSubData
});
hasTimeData || (parsed.noData = !0)
} else if (parsed.data.aggregated && parsed.data.aggregated.length) {
var hasData = parsed.data.aggregated.some(function(item) {
var hasSubData = item.data.some(function(subItem) {
return void 0 !== subItem.cValue ? null !== subItem.value || null !== subItem.cValue : null !== subItem.value
});
return hasSubData
});
hasData || (parsed.noData = !0)
} else parsed.noData = !0; if (requestObject.mainQuery.rollup && "time" === requestObject.mainQuery.group) {
var rollupMeta = gaApiMeta.getMetric(requestObject.mainQuery.metric[0].category, requestObject.mainQuery.metric[0].event);
rollupMeta.ignore_rollup || (parsed.rollup = requestObject.mainQuery.rollup, parsed.rollupStart = {
main: requestObject.mainQuery.interval.start,
compare: requestObject.mainQuery.compareInterval ? requestObject.mainQuery.compareInterval.start : null
}, parsed.rollupEnd = {
main: requestObject.mainQuery.interval.end,
compare: requestObject.mainQuery.compareInterval ? requestObject.mainQuery.compareInterval.end : null
})
}
return parsed
},
preprocessResponse = function(requestObject, dimensions) {
var dimension = requestObject.mainQuery.filter && requestObject.mainQuery.filter.dimension;
angular.forEach(requestObject.response, function(data, index) {
var query = requestObject.queries[index];
"time" === query.group ? angular.forEach(data.timeseries, function(values) {
values = (values || []).map(function(item) {
item.total = void 0 !== item.total ? item.total : "histogram" === query.aggregation ? [] : null, dimension && (item.dimensions = item.dimensions || {}, item.dimensions.total = void 0 !== item.dimensions.total ? item.dimensions.total : "histogram" === query.aggregation ? [] : null, item.dimensions[dimension] = item.dimensions[dimension] || {}, angular.forEach(dimensions, function(dimensionValue) {
item.dimensions[dimension][dimensionValue] = void 0 !== item.dimensions[dimension][dimensionValue] ? item.dimensions[dimension][dimensionValue] : "histogram" === query.aggregation ? [] : null
}))
})
}) : angular.forEach(data.aggregated, function(values) {
values.total = void 0 !== values.total ? values.total : "histogram" === query.aggregation ? [] : null, dimension && (values.dimensions = values.dimensions || {}, values.dimensions.total = void 0 !== values.dimensions.total ? values.dimensions.total : "histogram" === query.aggregation ? [] : null, values.dimensions[dimension] = values.dimensions[dimension] || {}, angular.forEach(dimensions, function(dimensionValue) {
values.dimensions[dimension][dimensionValue] = void 0 !== values.dimensions[dimension][dimensionValue] ? values.dimensions[dimension][dimensionValue] : "histogram" === query.aggregation ? [] : null
}))
})
});
var tmpMeta = gaApiMeta.getMetric(requestObject.mainQuery.metric[0].category, requestObject.mainQuery.metric[0].event);
if ("histogram" === requestObject.mainQuery.aggregation) {
var keyCount = 0,
maxIndex = 0,
keyList = [];
requestObject.mainQuery.filter ? (angular.forEach(requestObject.response, function(data, index) {
data.aggregated[tmpMeta.event].dimensions.total.length > keyCount && (keyCount = data.aggregated[tmpMeta.event].dimensions.total.length, maxIndex = index)
}), keyList = requestObject.response[maxIndex].aggregated[tmpMeta.event].dimensions.total.map(function(item) {
return item.key
}), angular.forEach(requestObject.response, function(data) {
for (var i = data.aggregated[tmpMeta.event].dimensions.total.length; keyCount > i; i++) data.aggregated[tmpMeta.event].dimensions.total.push({
key: keyList[i],
value: null
})
})) : (angular.forEach(requestObject.response, function(data, index) {
data.aggregated[tmpMeta.event].total.length > keyCount && (keyCount = data.aggregated[tmpMeta.event].total.length, maxIndex = index)
}), keyList = requestObject.response[maxIndex].aggregated[tmpMeta.event].total.map(function(item) {
return item.key
}), angular.forEach(requestObject.response, function(data) {
for (var i = data.aggregated[tmpMeta.event].total.length; keyCount > i; i++) data.aggregated[tmpMeta.event].total.push({
key: keyList[i],
value: null
})
}))
}
return requestObject.mainQuery.filter && "*" === requestObject.mainQuery.filter.values[0] && angular.forEach(requestObject.response, function(data) {
data.aggregated && angular.forEach(data.aggregated, function(eventData) {
eventData.dimensions.total = void 0 === eventData.dimensions.total ? eventData.total : eventData.dimensions.total
})
}), requestObject
},
mergeCompare = function(transformData) {
return angular.forEach(transformData, function(typeData, type) {
type.indexOf("Compare") > -1 && (angular.forEach(typeData, function(data, event) {
var parent = transformData[type.replace("Compare", "")][event];
parent && (type.indexOf("aggregated") > -1 && (parent.cTotal = data.total), parent.data = mergeEvent(parent.data, data.data))
}), delete transformData[type])
}), transformData
},
mergeEvent = function(parent, child) {
return angular.forEach(parent, function(data, event) {
var childData = child[event];
data.data ? angular.forEach(data.data, function(d, i) {
var c = childData.data[i];
c && (d.cTs = c.ts, d.cTotal = c.total)
}) : data.cValue = angular.copy(childData.value)
}), parent
},
sortAndSlice = function(query, top, data, dimensions) {
if (!dimensions) {
var sortByDimensionName = query.filter && ("cohort_month" === query.filter.dimension || "cohort_week" === query.filter.dimension);
data.aggregated[0].data = data.aggregated[0].data.sort(function(a, b) {
return sortByDimensionName ? parseInt(a.dimension, 10) - parseInt(b.dimension, 10) : (b.value || 0) - (a.value || 0)
}), top && (data.aggregated[0].data = data.aggregated[0].data.slice(0, top)), dimensions = data.aggregated[0].data.map(function(item) {
return item.dimension
})
}
return angular.forEach(data, function(typeData) {
angular.forEach(typeData, function(dataItem) {
dataItem.data = dataItem.data.filter(function(item) {
return dimensions.indexOf(item.dimension) > -1
}).sort(function(a, b) {
return dimensions.indexOf(a.dimension) - dimensions.indexOf(b.dimension)
})
})
}), {
data: data,
sortArray: dimensions
}
},
splitHistogram = function(query, queryResponse, dataType, dimensions) {
var newQueryResponse = {};
return angular.forEach(queryResponse, function(data) {
angular.forEach(dimensions || [null], function(dimension) {
var totalGroup = dimension ? data.dimensions.total : data.total;
angular.forEach(totalGroup, function(totalItem) {
if (newQueryResponse[totalItem.key] = newQueryResponse[totalItem.key] || {
total: angular.isNumber(totalItem.value) ? totalItem.value : null
}, dimensions) {
var keyData = (data.dimensions[query.filter.dimension][dimension] || []).filter(function(item) {
return item.key === totalItem.key
})[0] || {};
newQueryResponse[totalItem.key].dimensions = newQueryResponse[totalItem.key].dimensions || {}, newQueryResponse[totalItem.key].dimensions[query.filter.dimension] = newQueryResponse[totalItem.key].dimensions[query.filter.dimension] || {}, newQueryResponse[totalItem.key].dimensions[query.filter.dimension][dimension] = angular.isNumber(keyData.value) ? keyData.value : null
}
})
})
}), newQueryResponse
},
parseVal = function(query, value) {
var returnValue = null;
return angular.isObject(value) ? returnValue = query.metric.currency && value[query.metric.currency] ? angular.isObject(value[query.metric.currency]) ? value[query.metric.currency][query.aggregation] : value[query.metric.currency] : value[query.aggregation] : angular.isNumber(value) && (returnValue = value), angular.isNumber(returnValue) ? returnValue : null
},
parseMeta = function(query, subEvent) {
var meta = gaApiMeta.getMetric(query.metric.category, query.metric.event, subEvent),
parsedMeta = meta.axisY;
return parsedMeta.inactive = query.metric.inactive || !1, parsedMeta.category = query.metric.category, parsedMeta.event = query.metric.event, subEvent !== parsedMeta.event && (parsedMeta.subEvent = subEvent), parsedMeta.title = "event_count" === query.aggregation ? meta.eventCountTitle : meta.displayTitle, parsedMeta.keyUnit = meta.keyUnit, subEvent !== parsedMeta.event && (parsedMeta.subEvent = subEvent), "event_count" === query.aggregation ? (parsedMeta.unit = meta.eventCountUnit && meta.eventCountUnit.unit || "event", parsedMeta.type = meta.eventCountUnit && meta.eventCountUnit.type || "number") : meta.currency && (parsedMeta.currency = query.metric.currency || "USD"), parsedMeta
},
parseTimeseries = function(query, dataSerie, subEvent, dimensions) {
var parsed = {
data: [],
meta: parseMeta(query, subEvent)
};
return dimensions = dimensions && !query.merged ? dimensions : [null], angular.forEach(dimensions, function(dimension, index) {
parsed.data[index] = parsed.data[index] || {
data: []
}, dimension && (parsed.data[index].dimension = dimension), angular.forEach(dataSerie, function(data) {
var temp;
if (null === dimension) temp = {
ts: data.timestamp * (query.normalized ? 1 : 1e3) + (query.normalized ? "" : 0),
total: parseVal(query, data.total)
};
else {
var total = data.dimensions && data.dimensions[query.filter.dimension] && data.dimensions[query.filter.dimension][dimension];
query.useZeroWhenNoDimensionData && void 0 === total && null !== data.total && (total = 0), temp = {
ts: data.timestamp * (query.normalized ? 1 : 1e3) + (query.normalized ? "" : 0),
total: parseVal(query, total)
}
}
query.normalized && (temp.index = parseInt(temp.ts, 10)), parsed.data[index].data.push(temp)
})
}), parsed
},
parseAggregated = function(query, data, subEvent, dimensions) {
var parsed = {
data: [],
meta: parseMeta(query, subEvent),
total: null
};
return dimensions = dimensions ? dimensions : [null], query.filter ? (parsed.total = parseVal(query, data.dimensions && data.dimensions.total ? data.dimensions.total : null), angular.forEach(dimensions, function(dimension) {
var dimensionData = data.dimensions && data.dimensions[query.filter.dimension] && data.dimensions[query.filter.dimension][dimension] ? data.dimensions[query.filter.dimension][dimension] : null;
parsed.data.push({
dimension: dimension,
value: parseVal(query, dimensionData)
})
})) : (parsed.total = parseVal(query, data.total), parsed.data.push({
value: parsed.total
})), parsed
},
parseFunnelData = function(data, funnel) {
if (!data || !data.result || !Object.keys(data.result).length) return $q.reject(funnel);
var filters = [],
dimensions = [],
dimensionMetas = [],
timeseriesData = [],
xValues = funnel.metrics.map(function(metric, index) {
return {
title: metric.event,
meta: parseMeta({
metric: metric
}),
values: {
sum: data.result.counts.total[index],
diff: data.result.counts.total[index] / data.result.counts.total[index - 1]
}
}
});
if (funnel.filters.length) {
funnel.filters = funnel.filters.sort(function(a, b) {
return a.values.length < b.values.length
});
var sortedFilter = {};
angular.forEach(data.result.counts, function(filters, key) {
if ("total" !== key) {
var tmpDimCount = [];
angular.forEach(filters, function(vals, name) {
var count = vals.reduce(function(prev, dataItem) {
return prev + (dataItem || 0)
}, 0);
tmpDimCount.push({
name: name,
value: count
})
}), tmpDimCount.sort(function(a, b) {
return a.value > b.value ? -1 : 1
}), sortedFilter[key] = tmpDimCount
}
}), funnel.filters.forEach(function(filter) {
var sortedValues = sortedFilter[filter.dimension].map(function(o) {
return o.name
});
filter.values.forEach(function(f) {
-1 === sortedValues.indexOf(f) && sortedValues.push(f)
}), filter.values = sortedValues
}), dimensions = funnel.filters.map(function(filter) {
return filter
}), angular.forEach(funnel.filters, function(filter, filterIndex) {
filters.push({
dimension: filter.dimension,
values: filter.values
});
var arrAggrData = [];
dimensionMetas.push(gaApiMeta.getDimension(filter.dimension) || null);
var calcFilterData = {},
countFilterData = {},
diffFilterData = {};
angular.forEach(data.result.counts[filter.dimension], function(values, key) {
values.forEach(function(value, valueIndex) {
calcFilterData[key] || (calcFilterData[key] = []), countFilterData[key] || (countFilterData[key] = []), diffFilterData[key] || (diffFilterData[key] = []), diffFilterData[key].push(valueIndex > 0 ? value / values[valueIndex - 1] * 100 / 100 : null), calcFilterData[key].push(value / data.result.counts.total[0] * 100 / 100), countFilterData[key].push(value)
})
}), filter.values.forEach(function(filterValue) {
var aggrData = {
meta: {
subTitle: "",
title: filterValue
},
total: null,
data: []
};
aggrData = [], angular.forEach(funnel.metrics, function(metric, index) {
var metricMeta = parseMeta({
metric: metric
});
metricMeta.type = "percent", metricMeta.unit = "percent";
var extraValues = null;
if (index > 0) try {
var extras = data.result["dwell-times"][filter.dimension][filterValue][index - 1];
extraValues = {
count: extras.observations
}, countFilterData[filterValue][index - 1] > 0 && (countFilterData[filterValue][index] || (countFilterData[filterValue][index] = 0), extraValues.avgTime = "nan" !== extras.mean ? extras.mean : 0, extraValues.dropoff = countFilterData[filterValue][index - 1] ? countFilterData[filterValue][index - 1] - countFilterData[filterValue][index] : 0, extraValues.diff = diffFilterData[filterValue][index] ? diffFilterData[filterValue][index] : null)
} catch (e) {
extraValues = {
count: 0
}
} else try {
extraValues = {
count: countFilterData[filterValue][index]
}
} catch (e) {
extraValues = {
count: 0
}
}
aggrData.push({
value: calcFilterData[filterValue] ? calcFilterData[filterValue][index] : 0,
extraValues: extraValues
})
}), arrAggrData.push({
data: aggrData,
dimension: filterValue
})
}), timeseriesData.push({
data: arrAggrData,
meta: dimensions[filterIndex].meta,
total: 0
})
})
} else {
var tmpTimeseriesData = {
total: 1,
meta: {
title: "Value",
subTitle: ""
},
data: []
};
angular.forEach(funnel.metrics, function(metric, index) {
var metricMeta = parseMeta({
metric: metric
});
metricMeta.type = "percent", metricMeta.unit = "percent";
var extraValues = null;
if (index > 0) try {
var extras = data.result["dwell-times"].total[index - 1];
extraValues = {
count: extras.observations,
avgTime: extras.mean && "nan" !== extras.mean ? extras.mean : null,
dropoff: data.result.counts.total[index - 1] - data.result.counts.total[index],
diff: data.result.counts.total[index] / data.result.counts.total[index - 1]
}
} catch (e) {} else extraValues = {
count: data.result.counts.total[index]
};
tmpTimeseriesData.data.push({
meta: metricMeta,
index: index,
value: data.result.counts.total[index] / data.result.counts.total[0] * 100 / 100,
values: {
sum: data.result.counts.total[index],
diff: data.result.counts.total[index] / data.result.counts.total[index - 1]
},
extraValues: extraValues
})
}), timeseriesData.push(tmpTimeseriesData)
}
var query = {
aggregation: "sum",
aggregation_aggregated: "sum",
compare: !1,
data: {
timeseries: timeseriesData
},
xValues: xValues || null,
yMeta: {
type: "percent",
unit: "percent"
},
dimensions: dimensions || null,
dimensionMeta: dimensionMetas || null,
filter: filters || null,
group: "metric",
interval: {
start: funnel.range.main.start,
end: funnel.range.main.end
},
intervalDisplay: gaUtilsDate.getIntervalTitle(funnel.range.main),
meta: {
subTitle: "",
title: funnel.name,
type: "string",
unit: "none"
},
type: "bar"
},
overview = {};
try {
var total = data.result.counts.total[0],
conversions = data.result.counts.total[data.result.counts.total.length - 1];
overview = {
dropoffs: total - conversions,
conversions: conversions,
conversionRate: conversions / total,
avgTime: "nan" === data.result["total-dwell-times"].total.mean ? null : data.result["total-dwell-times"].total.mean,
total: total
}
} catch (e) {}
return {
query: query,
overview: overview
}
},
parseEmptyFunnelData = function(funnel) {
var data = {
result: {
"total-dwell-times": {
total: {
observations: 0,
mean: null
}
},
"dwell-times": {
total: [{
observations: 0,
mean: null
}]
},
counts: {
total: []
}
}
},
steps = funnel.metrics.length;
return funnel.metrics.forEach(function() {
data.result.counts.total.push(0), data.result["dwell-times"].total.push({
observations: 0,
mean: null
})
}), funnel.filters && funnel.filters.forEach(function(filter) {
data.result["total-dwell-times"][filter.dimension] || (data.result["total-dwell-times"][filter.dimension] = {}), data.result["dwell-times"][filter.dimension] || (data.result["dwell-times"][filter.dimension] = {}), data.result.counts[filter.dimension] || (data.result.counts[filter.dimension] = {}), filter.values && filter.values.forEach(function(value) {
data.result["total-dwell-times"][filter.dimension][value] = {
observations: 0,
mean: null
}, data.result["dwell-times"][filter.dimension][value] = [], data.result.counts[filter.dimension][value] = [];
for (var i = 1; steps > i; i++) data.result["dwell-times"][filter.dimension][value].push({
observations: 0,
mean: null
}), data.result.counts[filter.dimension][value].push(0)
})
}), parseFunnelData(data, funnel)
};
return {
parse: parse,
_parse: _parse,
parseFunnelData: parseFunnelData,
parseEmptyFunnelData: parseEmptyFunnelData
}
}), angular.module("ga.utils.transform.chart", ["ga.api.meta", "ga.utils.helpers"]).factory("gaUtilsChartTransform", function($q, $filter, gaApiMeta, gaHelpers) {
var formatUnitType = $filter("formatUnitType"),
canvasParseSeries = function(queryData, xValues) {
var datasets = [];
return queryData.data.timeseries.forEach(function(serie) {
var dataset = {
title: serie.meta.title,
items: []
};
serie.data.forEach(function(serieItem) {
var datasetItem = {
data: [],
hidden: serie.meta.inactive || !1
};
serieItem.dimension && (datasetItem.dimension = queryData.dimensionMeta ? formatUnitType(serieItem.dimension, queryData.dimensionMeta) : serieItem.dimension), serieItem.data.forEach(function(value, i) {
var tmp = {};
tmp.y = value.total, tmp.x = xValues[i].sort, queryData.compare && (tmp.compareY = value.cTotal), datasetItem.data.push(tmp)
}), dataset.items.push(datasetItem)
}), datasets.push(dataset)
}), datasets
},
canvasParseAggregated = function(queryData, xValues) {
var datasets = [];
return queryData.data.aggregated.forEach(function(series) {
datasets.push(_canvasParseAggregatedSeries(series, queryData, xValues))
}), datasets
},
canvasParseMetricSeries = function(queryData, xValues) {
var datasets = [];
if (!queryData.dimensions.length) {
var dataset = {
title: null,
items: []
},
datesetItems = {
title: null,
data: [],
hidden: !1,
index: 0
};
return queryData.data.timeseries[0].data.forEach(function(series) {
datesetItems.data.push({
x: series.index,
y: series.value,
extraValues: series.extraValues || null
})
}), dataset.items.push(datesetItems), [dataset]
}
return queryData.data.timeseries.forEach(function(series, seriesIndex) {
series.data.forEach(function(seriesItem, seriesItemindex) {
datasets[seriesItemindex] || (datasets[seriesItemindex] = {
title: null,
items: []
});
var datasetItem = {
data: [],
hidden: series.meta.inactive || !1
};
seriesItem.dimension ? (datasetItem.dimension = queryData.dimensions ? formatUnitType(seriesItem.dimension, queryData.dimensions[seriesIndex].meta) : seriesItem.dimension, seriesItem.data.forEach(function(value, i) {
var tmp = {};
tmp.y = value.value, tmp.x = xValues[i].sort, tmp.extraValues = value.extraValues || null, datasetItem.data.push(tmp)
})) : datasetItem.data.push({
x: seriesItem.index,
y: seriesItem.value
}), datasets[seriesItemindex].items.push(datasetItem)
})
}), datasets
},
_canvasParseAggregatedSeries = function(series, queryData, xValues) {
var dataset = {
title: queryData.dimensionMeta ? formatUnitType(series.meta.title, queryData.dimensionMeta) : series.meta.title,
items: []
},
datasetItem = {
data: []
};
return series.data.forEach(function(serieItem, i) {
var tmp = {};
tmp.y = serieItem.value, tmp.x = xValues[i].sort, queryData.compare && (tmp.compareY = serieItem.cValue), datasetItem.data.push(tmp)
}), dataset.items.push(datasetItem), dataset
},
canvasParse = function(queriesData, stack) {
var parsed = {};
if (!queriesData || !queriesData.left && !queriesData.right) return null;
angular.forEach(queriesData, function(data, position) {
var parsedPosition = {};
data.noData && (parsedPosition.noData = !0), parsedPosition.realtime = data.realtime, parsedPosition.group = data.group, parsedPosition.type = data.type, parsedPosition.intervalTitle = data.intervalDisplay, parsedPosition.xMeta = data.meta, parsedPosition.position = position, parsedPosition.dimensions = data.dimensions || null, data.compare && (parsedPosition.compare = !0, parsedPosition.compareIntervalTitle = data.compareIntervalDisplay), queriesData.left && queriesData.right && data.data.aggregated && data.data.aggregated.length && data.data.aggregated.forEach(function(dataset) {
dataset.data && dataset.data.length && dataset.data.sort(gaHelpers.customSort("dimension"))
}), "time" === data.group ? (data.dimensionMeta && (parsedPosition.dimensionMeta = data.dimensionMeta), data.data.timeseries && data.data.timeseries.length && data.data.timeseries[0].data.length && (parsedPosition.xValues = data.data.timeseries[0].data[0].data.map(function(item) {
var tmp = {
x: item.ts,
sort: isNaN(parseInt(item.ts, 10)) ? item.ts : parseInt(item.ts, 10)
};
return data.compare && (tmp.compareX = item.cTs), tmp
}), parsedPosition.yMeta = gaApiMeta.getUnit(data.data.timeseries[0].meta.unit), parsedPosition.yMeta.currency = data.data.timeseries[0].meta.currency, parsedPosition.yMeta.currency && (parsedPosition.yMeta.postFix = gaApiMeta.getCurrency(parsedPosition.yMeta.currency).symbol), parsedPosition.yMeta.type = data.data.timeseries[0].meta.type, parsedPosition.yMeta.unit = data.data.timeseries[0].meta.unit, parsedPosition.yValues = canvasParseSeries(data, parsedPosition.xValues))) : "dimension" === data.group ? data.data.aggregated && data.data.aggregated.length && data.data.aggregated[0].data.length && (parsedPosition.xValues = data.data.aggregated[0].data.map(function(item) {
var tmp = {
x: item.dimension,
sort: formatUnitType(item.dimension, data.meta)
};
return data.compare && (tmp.compareX = item.dimension), tmp
}), parsedPosition.yMeta = gaApiMeta.getUnit(data.data.aggregated[0].meta.unit), parsedPosition.yMeta.type = data.data.aggregated[0].meta.type, parsedPosition.yMeta.unit = data.data.aggregated[0].meta.unit, parsedPosition.yValues = canvasParseAggregated(data, parsedPosition.xValues)) : "metric" === data.group ? (data.dimensions && (parsedPosition.dimensions = data.dimensions), parsedPosition.xValues = data.xValues.map(function(val, index) {
return {
x: val.title,
meta: val.meta,
values: val.values,
sort: index
}
}), parsedPosition.yMeta = gaApiMeta.getUnit(data.yMeta.unit), parsedPosition.yMeta.type = data.yMeta.type, parsedPosition.yMeta.unit = data.yMeta.unit, parsedPosition.yValues = canvasParseMetricSeries(data, parsedPosition.xValues)) : "value" === data.group && (data.dimensionMeta && (parsedPosition.dimensionMeta = data.dimensionMeta), data.data.aggregated && data.data.aggregated.length && data.data.aggregated[0].data.length && (parsedPosition.xValues = data.data.aggregated[0].data.map(function(item, index) {
var tmp = {
x: item.dimension,
sort: index
};
return item.values && (tmp.values = item.values), data.compare && (tmp.compareX = item.dimension), tmp
}), parsedPosition.yMeta = gaApiMeta.getUnit(data.data.aggregated[0].data[0].meta.unit), parsedPosition.yMeta.type = data.data.aggregated[0].data[0].meta.type, parsedPosition.yMeta.unit = data.data.aggregated[0].data[0].meta.unit, parsedPosition.yValues = canvasParseAggregated(data, parsedPosition.xValues))), parsedPosition.xValues = parsedPosition.xValues || [], parsedPosition.yMeta = parsedPosition.yMeta || null, parsedPosition.yValues = parsedPosition.yValues || [], parsed[position] = stack ? stackEvents(parsedPosition) : parsedPosition
}), parsed.left && parsed.right;
var tmpParsed = parsed.left || parsed.right,
leftCount = (parsed.left ? parsed.left.xValues : []).length,
rightCount = (parsed.right ? parsed.right.xValues : []).length,
combined = {
realtime: tmpParsed.realtime,
compare: tmpParsed.compare,
intervalTitle: tmpParsed.intervalTitle,
xMeta: tmpParsed.xMeta,
xValues: leftCount >= rightCount ? parsed.left.xValues : parsed.right.xValues,
group: tmpParsed.group,
data: {}
};
tmpParsed.dimensionMeta && (combined.dimensionMeta = tmpParsed.dimensionMeta), combined.compare && (combined.compareIntervalTitle = tmpParsed.compareIntervalTitle), parsed.left && (combined.data.left = {
datasets: parsed.left.yValues,
meta: parsed.left.yMeta,
type: parsed.left.type
}), parsed.left && parsed.left.noData && (combined.data.left.noData = !0), parsed.right && (combined.data.right = {
datasets: parsed.right.yValues,
meta: parsed.right.yMeta,
type: parsed.right.type
}), parsed.right && parsed.right.noData && (combined.data.right.noData = !0);
var cohort_filter = null;
if (queriesData.left ? cohort_filter = (queriesData.left || queriesData.left.rollup).filter && ((queriesData.left || queriesData.left.rollup).filter.dimension || "").match(/cohort/) : queriesData.right && (cohort_filter = (queriesData.right || queriesData.right.rollup).filter && ((queriesData.right || queriesData.right.rollup).filter.dimension || "").match(/cohort/)), !cohort_filter && queriesData.left && queriesData.left.rollup || queriesData.right && queriesData.right.rollup) {
var rollupStart = queriesData.left ? queriesData.left.rollupStart : queriesData.right.rollupStart,
rollupEnd = queriesData.left ? queriesData.left.rollupEnd : queriesData.right.rollupEnd,
first = combined.xValues[0],
last = combined.xValues[combined.xValues.length - 1];
last.x = rollupEnd.main, rollupEnd.compare && (last.compareX = rollupEnd.compare), first.x = rollupStart.main, rollupEnd.compare && (first.compareX = rollupStart.compare)
}
return combined
},
canvasChartSettings = function(data, size, stack) {
size = size || "default";
var group = (data.left || data.right).group,
settings = {
height: 200,
width: 400,
sort: "dimension" === group ? ["y", "desc"] : ["x", "asc"],
sortEnabled: !1,
sortDatasets: "desc",
padding: {
left: 40,
right: 40,
top: 20,
bottom: 20
},
xAxis: {
ticks: 3
},
yAxis: {
lines: 9,
ticks: 3,
zeroLine: !0,
zeroArea: !0
},
legends: 3,
tooltip: !0,
compareToggle: !0,
chartData: {},
inc: 0
};
switch ((data.left && data.left.rollup || data.right && data.right.rollup) && (settings.rollup = "weekly" === (data.left && data.left.rollup || data.right && data.right.rollup) ? "isoweek" : "month", settings.xAxis.showAll = !0), "time" !== (data.left && data.left.group || data.right && data.right.group) && (settings.xAxis.showAll = !0), size) {
case "home":
settings.height = 75, settings.width = 285, settings.padding = {
left: 0,
right: 0,
top: 0,
bottom: 0
}, settings.xAxis = null, settings.yAxis = null, settings.legends = null, settings.tooltip = !1;
break;
case "small":
settings.height = 90, settings.width = 272, settings.padding = {
left: 10,
right: 10,
top: 0,
bottom: 0
}, settings.xAxis = null, settings.yAxis = null, settings.legends = null;
break;
case "medium":
settings.height = 157, settings.width = 564, settings.xAxis.ticks = settings.xAxis.showAll ? 10 : 5, settings.xAxis.small = !0, settings.padding = {
left: 45,
right: 30,
top: 30,
bottom: 30
};
break;
case "mediumHigh":
settings.height = 238, settings.width = 564, settings.xAxis.ticks = settings.xAxis.showAll ? 10 : 5, settings.xAxis.small = !0, settings.padding = {
left: 40,
right: 30,
top: 30,
bottom: 27
};
break;
case "largeHigh":
settings.height = 534, settings.width = 1148, settings.xAxis.ticks = settings.xAxis.showAll ? 10 : 5, settings.legends = 5, settings.padding = {
left: 50,
right: 50,
top: 30,
bottom: 50
};
break;
case "large":
settings.height = 450, settings.width = 1148, settings.xAxis.ticks = settings.xAxis.showAll ? 10 : 5, settings.legends = 5, settings.padding = {
left: 50,
right: 50,
top: 30,
bottom: 50
};
break;
case "explore":
settings.height = 460, settings.width = 1150, settings.minWidth = 1150, settings.xAxis.ticks = settings.xAxis.showAll ? 10 : 5, settings.compareToggle = !1, settings.legends = 5, settings.sortEnabled = !0, settings.padding = {
left: 50,
right: 50,
top: 30,
bottom: 50
};
break;
case "quality":
settings.width = 1105, settings.height = 430, settings.xAxis.ticks = settings.xAxis.showAll ? 10 : 5, settings.legends = 5, settings.padding = {
left: 50,
right: 20,
top: 30,
bottom: 20
};
break;
case "mini":
settings.width = 130, settings.height = 65, settings.xAxis = !1, settings.yAxis = !1, settings.legends = 0, settings.compareToggle = !1, settings.tooltip = !1, settings.noProgress = !0, settings.padding = {
left: 0,
right: 0,
top: 0,
bottom: 0
};
break;
case "funnel":
settings.height = 460, settings.width = 1150, settings.minWidth = 1150, settings.xAxis.ticks = settings.xAxis.showAll ? 10 : 5, settings.compareToggle = !1, settings.legends = 5, settings.disableLegendClick = !0, settings.sortEnabled = !0, settings.bars = {
width: 90,
padding: 80,
diffContainer: !0
}, settings.yAxis = {
min: 0,
max: 1
}, settings.padding = {
left: 50,
right: 50,
top: 50,
bottom: 50
}, settings.innerPadding = {
left: 20
};
break;
default:
angular.isObject(size) && (settings = size)
}
return settings.chartData = canvasParse(data, stack), settings.chartData.data.right && (settings.compareToggle = !1), settings.xAxis && settings.xAxis.ticks && settings.chartData.xValues && settings.chartData.xValues.length <= 7 && (settings.xAxis.ticks = 7), settings.sortEnabled && ("time" === group ? (settings.sort = ["x", "asc"], settings.sortEnabled = !1) : settings.chartData.data.left && settings.chartData.data.right ? (settings.sort = ["x", "asc"], settings.sortEnabled = "x") : (settings.chartData.data.left || settings.chartData.data.right).datasets.length > 1 && (settings.sort = ["x", "asc"], settings.sortEnabled = "x")), settings
},
setSort = function(settings, sort) {
return settings.sortEnabled === !1 ? (settings.sort = ["x", "asc"], settings) : "x" === settings.sortEnabled && "y" === sort[0] ? (settings.sort = ["x", "asc"], settings) : (settings.sort = sort, settings)
},
stackEvents = function(data) {
var newDatasets = [];
return data.yValues.forEach(function(dataset) {
dataset.items.forEach(function(datasetItem, index) {
newDatasets[index] = newDatasets[index] || {
position: data.position,
title: data.dimensionMeta ? formatUnitType(datasetItem.dimension, data.dimensionMeta) : datasetItem.dimension,
type: data.type,
items: []
}, datasetItem.index = dataset.index || 0, "metric" === data.group ? (datasetItem.title = datasetItem.dimension, data.dimensions && data.dimensions[index] && (newDatasets[index].meta = data.dimensions[index].meta, newDatasets[index].title = data.dimensions[index].meta.title)) : datasetItem.dimension ? datasetItem.dimension = dataset.title : (delete datasetItem.dimension, datasetItem.title = dataset.title), newDatasets[index].items.push(datasetItem)
})
}), delete data.dimensionMeta, data.yValues = newDatasets, data
};
return {
canvasChartSettings: canvasChartSettings,
canvasParse: canvasParse,
setSort: setSort
}
}), angular.module("ga.utils.transform.grid", ["ga.api.meta", "ga.utils.date"]).factory("gaUtilsGridTransform", function(gaApiMeta, $filter, gaUtilsDate) {
var formatUnitType = $filter("formatUnitType"),
parse = function(queryResponse, nosplit) {
var parsed = {
header: {
values: []
},
footer: {
values: []
},
rows: [],
meta: {
axis: {},
cols: [],
dimension: null
}
};
queryResponse.dimensionMeta && (parsed.meta.dimension = queryResponse.dimensionMeta);
var aggregationName = function() {
var aggr = queryResponse.aggregation_aggregated || queryResponse.aggregation;
switch (aggr) {
case "sum":
return "SUM";
case "mean":
return "MEAN";
case "event_count":
return "Count"
}
};
return parsed.header.axis = {
value: queryResponse.meta.title,
subValue: queryResponse.meta.subTitle
}, parsed.footer.axis = {
value: aggregationName()
}, ".*" === parsed.header.axis.value && (parsed.header.axis.value = "All (.*)"), parsed.meta.axis = queryResponse.meta, parsed = "time" === queryResponse.group ? _parseTime(queryResponse, parsed, nosplit) : "metric-dimension" === queryResponse.group ? _parseMetricDimension(queryResponse, parsed) : "metric" === queryResponse.group ? _parseMetric(queryResponse, parsed) : _parseDefault(queryResponse, parsed, nosplit), parsed.header.values.length || (parsed.header.values.push({
col: 1,
value: " "
}), parsed.footer.values.push({
col: 1,
value: " "
}), parsed.rows.push({
axis: {
value: null
},
values: [{
value: null
}]
})), queryResponse.rollup && (parsed.rollup = !0), parsed
},
_parseTime = function(queryResponse, parsed, nosplit) {
!nosplit && queryResponse.data && queryResponse.data.aggregated && queryResponse.data.timeseries && 1 === queryResponse.data.timeseries.length && 1 === queryResponse.data.timeseries[0].data.length && (queryResponse.compare && (queryResponse.data.timeseries[1] = angular.copy(queryResponse.data.timeseries[0]), queryResponse.data.timeseries[1].meta.title = "Comparison", queryResponse.data.timeseries[1].meta.compare = !0, queryResponse.data.timeseries[0].data[0].data.map(function(item) {
item.cTs = null, item.cTotal = null
}), queryResponse.data.timeseries[1].data[0].data.map(function(item) {
item.ts = item.cTs, item.total = item.cTotal, item.cTs = null, item.cTotal = null
}), queryResponse.data.aggregated[1] = angular.copy(queryResponse.data.aggregated[0]), queryResponse.data.aggregated[1].data[0].value = queryResponse.data.aggregated[1].data[0].cValue, queryResponse.data.aggregated[1].data[0].cValue = null), queryResponse.data.aggregated[0].data[0].cValue = null, queryResponse.compare = !1);
var colIndex = 0;
return angular.forEach(queryResponse.data.aggregated, function(event) {
angular.forEach(event.data, function(data) {
parsed.footer.values.push({
value: angular.isNumber(data.value) ? data.value : null,
cValue: angular.isNumber(data.cValue) ? data.cValue : null
}), colIndex++
})
}), colIndex = 0, angular.forEach(queryResponse.data.timeseries, function(event) {
angular.forEach(event.data, function(data) {
parsed.meta.cols.push(event.meta), data.dimension && parsed.meta.dimension && "dimension" !== parsed.meta.dimension.type && (data.dimensionTitle = formatUnitType(data.dimension, parsed.meta.dimension)), queryResponse.dimensionMeta && !data.dimensionTitle && data.dimension && (data.dimensionTitle = formatUnitType(data.dimension, queryResponse.dimensionMeta)), parsed.header.values.push({
col: colIndex + 1,
value: event.meta.title,
subValue: data.dimensionTitle || data.dimension || null,
dimensionValue: data.dimension || null
}), angular.forEach(data.data, function(dataItem, rowIndex) {
var display, cDisplay, cohort_filter = queryResponse.filter && queryResponse.filter.dimension.match(/cohort/);
if (!cohort_filter && queryResponse.rollup && "time" === queryResponse.group && "normalized" !== queryResponse.group) {
0 === rowIndex && (dataItem.ts = queryResponse.interval.start, queryResponse.compare && (dataItem.cTs = queryResponse.compareInterval.start));
var rollup = "weekly" === queryResponse.rollup ? "isoweek" : "month";
cDisplay = null, data.data.length === rowIndex + 1 ? (dataItem.ts = queryResponse.interval.end, queryResponse.compare && (dataItem.cTs = queryResponse.compareInterval.end), display = gaUtilsDate.getIntervalTitle({
start: gaUtilsDate.moment.utc(dataItem.ts).startOf(rollup).valueOf(),
end: dataItem.ts
})) : display = gaUtilsDate.getIntervalTitle({
start: dataItem.ts,
end: gaUtilsDate.moment.utc(dataItem.ts).endOf(rollup).valueOf()
}), parsed.rows[rowIndex] = parsed.rows[rowIndex] || {
axis: {
value: dataItem.ts,
cValue: dataItem.cTs || null,
index: dataItem.index,
display: display
},
values: []
}
} else parsed.rows[rowIndex] = parsed.rows[rowIndex] || {
axis: {
value: dataItem.ts,
cValue: dataItem.cTs || null,
index: dataItem.index
},
values: []
};
var share = null,
cShare = null;
if ("percent" !== event.meta.type && parsed.footer.values[colIndex]) {
if (angular.isNumber(dataItem.total) && 0 !== dataItem.total)
if ("number" === event.meta.type && "mean" === queryResponse.aggregation_aggregated) {
var diff = dataItem.total - parsed.footer.values[colIndex].value,
diffTotal = diff / parsed.footer.values[colIndex].value;
share = Math.abs(diffTotal) > 10 ? 10 : diffTotal
} else "number" === event.meta.type && (share = dataItem.total / parsed.footer.values[colIndex].value);
if (angular.isNumber(dataItem.cTotal) && 0 !== dataItem.cTotal)
if ("number" === event.meta.type && "mean" === queryResponse.aggregation_aggregated) {
var cDiff = dataItem.cTotal - parsed.footer.values[colIndex].cValue,
cDiffTotal = cDiff / parsed.footer.values[colIndex].cValue;
cShare = Math.abs(cDiffTotal) > 10 ? 10 : cDiffTotal
} else "number" === event.meta.type && (cShare = dataItem.cTotal / parsed.footer.values[colIndex].cValue)
}
parsed.rows[rowIndex].values[colIndex] = {
value: angular.isNumber(dataItem.total) ? dataItem.total : null,
cValue: angular.isNumber(dataItem.cTotal) ? dataItem.cTotal : null,
share: share,
cShare: cShare,
shareMeta: {
type: "percent",
unit: "percent"
}
}
}), colIndex++
})
}), parsed
},
_parseDefault = function(queryResponse, parsed, nosplit) {
return !nosplit && queryResponse.data && queryResponse.data.aggregated && 1 === queryResponse.data.aggregated.length && (queryResponse.compare && (queryResponse.data.aggregated[1] = angular.copy(queryResponse.data.aggregated[0]), queryResponse.data.aggregated[1].meta.title = "Comparison", queryResponse.data.aggregated[1].meta.compare = !0, queryResponse.data.aggregated[1].total = queryResponse.data.aggregated[1].cTotal, queryResponse.data.aggregated[1].cTotal = null, queryResponse.data.aggregated[1].data.map(function(item) {
item.value = item.cValue, item.cValue = null
})), queryResponse.data.aggregated[0].cTotal = null, queryResponse.data.aggregated[0].data.map(function(item) {
item.cValue = null
}), queryResponse.compare = !1), angular.forEach(queryResponse.data.aggregated, function(colData, colIndex) {
parsed.meta.cols.push(colData.meta);
var title = colData.meta.title;
"value" === queryResponse.group && queryResponse.dimensionMeta && (title = $filter("formatUnitType")(colData.meta.title, queryResponse.dimensionMeta)), parsed.header.values.push({
col: colIndex + 1,
value: title,
subValue: colData.meta.subTitle
}), parsed.footer.values.push({
value: colData.total,
cValue: colData.cTotal
}), angular.forEach(colData.data, function(rowData, rowIndex) {
var dimensionMeta = gaApiMeta.getDimension(rowData.dimension);
parsed.rows[rowIndex] = parsed.rows[rowIndex] || {
axis: {
value: dimensionMeta.title,
index: rowData.index,
event: rowData.meta ? rowData.meta.subEvent : null
},
values: []
};
var share = null,
cShare = null;
if (parsed.footer.values[colIndex]) {
if (angular.isNumber(rowData.value) && 0 !== rowData.value)
if ("percent" === colData.meta.type || "number" === colData.meta.type && "mean" === queryResponse.aggregation_aggregated) {
var diff = rowData.value - parsed.footer.values[colIndex].value,
diffTotal = diff / parsed.footer.values[colIndex].value;
share = Math.abs(diffTotal) > 10 ? 10 : diffTotal
} else "number" === colData.meta.type && (share = rowData.value / parsed.footer.values[colIndex].value);
if (angular.isNumber(rowData.cValue) && 0 !== rowData.cValue)
if ("percent" === colData.meta.type || "number" === colData.meta.type && "mean" === queryResponse.aggregation_aggregated) {
var cDiff = rowData.cValue - parsed.footer.values[colIndex].cValue,
cDiffTotal = cDiff / parsed.footer.values[colIndex].cValue;
cShare = Math.abs(cDiffTotal) > 10 ? 10 : cDiffTotal
} else "number" === colData.meta.type && (cShare = rowData.cValue / parsed.footer.values[colIndex].cValue)
}
parsed.rows[rowIndex].values[colIndex] = {
value: rowData.value,
cValue: rowData.cValue,
meta: rowData.meta,
share: share,
cShare: cShare,
shareMeta: {
type: "percent",
unit: "percent"
}
}
})
}), parsed
},
_parseMetric = function(queryResponse, parsed) {
return parsed.header.axis = {
value: "Steps",
subValue: null
}, parsed = queryResponse.dimensions && queryResponse.dimensions.length ? _parseMetricWithDimensions(queryResponse, parsed) : _parseMetricWithoutDimensions(queryResponse, parsed)
},
_parseMetricWithDimensions = function(queryResponse, parsed) {
parsed.header.values = [{
col: 1,
value: "Total",
subValue: null,
dimensionValue: null
}];
var activeDimensionIndex = queryResponse.activeDimensionIndex || 0;
return queryResponse.dimensions[activeDimensionIndex].values.some(function(headerVal, headerIndex) {
parsed.header.values.push({
col: headerIndex + 2,
value: $filter("formatUnitType")(headerVal, queryResponse.dimensions[activeDimensionIndex].meta),
subValue: null,
dimensionValue: null
})
}), parsed.rows = [], angular.forEach(queryResponse.data.timeseries[activeDimensionIndex].data, function(row, colIndex) {
row.data.some(function(objData, rowIndex) {
parsed.rows[rowIndex] || (parsed.rows[rowIndex] = {
axis: {
index: colIndex,
value: queryResponse.xValues[rowIndex].meta.title,
fullValue: _parseMetricFullPath(queryResponse.xValues[rowIndex].meta)
},
values: [{
value: queryResponse.xValues[rowIndex].values.sum,
share: queryResponse.xValues[rowIndex].values.diff,
shareMeta: {
type: "percent",
unit: "percent"
}
}]
}), parsed.rows[rowIndex].values[colIndex + 1] || (parsed.rows[rowIndex].values[colIndex + 1] = {}), parsed.rows[rowIndex].values[colIndex + 1] = {
value: objData.extraValues.count,
share: objData.extraValues.diff,
shareMeta: {
type: "percent",
unit: "percent"
},
extraValues: objData.extraValues
}
})
}), parsed
},
_parseMetricWithoutDimensions = function(queryResponse, parsed) {
return parsed.header.values = [{
col: 1,
value: "Converting users",
subValue: null,
dimensionValue: null
}, {
col: 2,
value: "Dropoff",
subValue: null,
dimensionValue: null
}, {
col: 3,
value: "Avg. conversion time",
subValue: null,
dimensionValue: null
}], parsed.meta.cols = [{
type: "number",
unit: "user"
}, {
type: "number",
unit: "user"
}, {
type: "seconds",
unit: "seconds"
}], parsed.rows = [], angular.forEach(queryResponse.data.timeseries[0].data, function(row, rowIndex) {
parsed.rows.push({
axis: {
index: rowIndex,
value: row.meta ? row.meta.title : "",
fullValue: _parseMetricFullPath(row.meta)
},
values: [{
value: row.extraValues.count,
share: row.extraValues.diff,
shareMeta: {
type: "percent",
unit: "percent"
},
extraValues: row.extraValues
}, {
value: row.extraValues.dropoff || null,
extraValues: row.extraValues
}, {
value: row.extraValues.avgTime || null,
extraValues: row.extraValues
}]
})
}), parsed
},
_parseMetricDimension = function(queryResponse, parsed) {
var activeDimensionIndex = queryResponse.activeDimensionIndex || 0,
dimensionObj = queryResponse.dimensions[activeDimensionIndex];
return parsed.meta.axis = dimensionObj.meta, parsed.header.axis = {
value: dimensionObj.meta.title,
subValue: null
}, parsed.footer = {
axis: {
value: "Total"
},
values: []
}, queryResponse.xValues.some(function(xVal, xValIndex) {
parsed.footer.values.push({
value: xVal.values.sum,
share: xVal.values.diff,
shareMeta: {
type: "percent",
unit: "percent"
}
}), parsed.header.values.push({
col: xValIndex + 1,
value: xVal.meta ? xVal.meta.title : "",
subValue: null,
dimensionValue: null
})
}), angular.forEach(queryResponse.data.timeseries[activeDimensionIndex].data, function(row, rowIndex) {
var tmpRow = {
axis: {
index: rowIndex,
value: row.dimension
},
values: row.data.map(function(objData) {
return {
value: objData.extraValues.count,
share: objData.extraValues.diff,
shareMeta: {
type: "percent",
unit: "percent"
},
extraValues: objData.extraValues
}
})
};
parsed.rows.push(tmpRow)
}), parsed
},
_parseMetricFullPath = function(meta) {
var str = "";
return meta.category && (str = meta.category.charAt(0).toUpperCase() + meta.category.slice(1) + " > "), str += meta.event.replace(/\:/gi, " > ")
},
flipData = function(data) {
var meta = {
axis: {
title: data.meta.dimension.title,
type: data.meta.dimension.type,
unit: data.meta.dimension.unit
},
cols: [],
dimension: data.meta.dimension
},
header = {
axis: {
value: data.meta.dimension.title,
subValue: ""
},
values: []
},
footer = {
axis: {
value: "MEAN"
},
values: []
},
rows = [];
return angular.forEach(data.header.values, function(row, key) {
rows.push({
axis: {
index: key,
value: row.subValue,
dimensionValue: row.dimensionValue
},
values: []
})
}), angular.forEach(data.rows, function(row, key) {
header.values.push({
col: key + 1,
value: $filter("formatUnitType")(row.axis.value, data.meta.axis)
}), angular.forEach(rows, function(_row, index) {
_row.values.push(row.values[index])
});
var sum = 0,
count = 0;
angular.forEach(row.values, function(r) {
null !== r.value && void 0 !== r.value && (sum += r.value, count++)
}), footer.values.push({
value: sum / count || null,
sum: sum || null
}), meta.cols.push(data.meta.cols[0])
}), data.header = header, data.footer = footer, data.rows = rows, data.meta = meta, data
};
return {
parse: parse,
flipData: flipData
}
}), angular.module("ga.utils.urlState", ["ui.router"]).factory("gaUtilsUrlState", function($window, $state) {
var setState = function(obj) {
obj = angular.copy(obj);
var params = angular.copy($state.params),
stringified = JSON.stringify(obj);
params.state = "{}" === stringified ? null : btoa(JSON.stringify(obj)), $state.go($state.current.name, params, {
notify: !1
})
},
getState = function() {
var state = $state.params.state;
try {
state = JSON.parse(atob(state))
} catch (e) {}
return state || {}
};
return {
setState: setState,
getState: getState
}
}), angular.module("ga.cache-buster", ["ga.config", "ga.utils.detect"]).config(function($provide, $httpProvider) {
$provide.factory("cacheBuster", function($templateCache, gaConfig, gaDetect) {
function getBuster() {
return gaConfig.cacheBuster()
}
return {
request: function(config) {
if ($templateCache.get(config.url)) return config;
var prefix = "?";
return -1 !== config.url.search("\\?") && (prefix = "&"), config.url += prefix += "_=" + getBuster(), "ie" === gaDetect.browser() && (config.url += "&ie=" + Math.random().toString().replace("0.", "")), config
}
}
}), $httpProvider.interceptors.push("cacheBuster")
}), angular.module("ga.utils.cache", ["ga.config"]).factory("gaUtilsCache", function($window, $cacheFactory, gaConfig) {
function _byteLength(str) {
var escapedStr = encodeURI(str),
count = 0;
if (-1 !== escapedStr.indexOf("%")) {
count = escapedStr.split("%").length - 1, 0 === count && count++;
var tmp = escapedStr.length - 3 * count;
count += tmp
} else count = escapedStr.length;
return count
}
var cache, cacheId = gaConfig.cache.id,
cacheExpire = gaConfig.cache.expire,
_defaultGet = function() {
return null
},
_defaultPut = function() {
return null
},
_defaultRemove = function() {
return null
},
_defaultRemoveAll = function() {
return null
};
switch (gaConfig.cache.type) {
case "localStorage":
cache = $window.localStorage, _defaultGet = function(key) {
return cache.getItem(key)
}, _defaultPut = function(key, value) {
cache.setItem(key, value)
}, _defaultRemove = function(key) {
cache.removeItem(key)
}, _defaultRemoveAll = function() {
cache.clear()
};
break;
case "angular":
cache = $cacheFactory(cacheId), _defaultGet = cache.get, _defaultPut = cache.put, _defaultRemove = cache.remove, _defaultRemoveAll = cache.removeAll;
break;
default:
cache = null
}
var get = function(key, cacheType) {
var localGet = function() {
return null
};
localGet = cacheType ? getFn(cacheType) : _defaultGet, key = cacheId + "." + key;
var value = localGet(key);
try {
value = JSON.parse(value)
} catch (e) {
value = null
}
return value && "object" == typeof value && value.expire && value.expire > Date.now() ? value.data : null
}, getFn = function(cacheType) {
var fn = function() {
return null
},
c = null;
switch (cacheType) {
case "localStorage":
c = $window.localStorage, fn = function(key) {
return c.getItem(key)
};
break;
case "angular":
c = $cacheFactory(cacheId), fn = cache.get
}
return fn
}, put = function(key, value, cacheType, expire) {
var localPut = function() {
return null
},
localExpire = expire || cacheExpire;
localPut = cacheType ? putFn(cacheType) : _defaultPut, key = cacheId + "." + key, value = {
expire: Date.now() + localExpire,
data: value
}, localPut(key, JSON.stringify(value))
}, putFn = function(cacheType) {
var fn = function() {
return null
},
c = null;
switch (cacheType) {
case "localStorage":
c = $window.localStorage, fn = function(key, value) {
c.setItem(key, value)
};
break;
case "angular":
c = $cacheFactory(cacheId), fn = cache.put
}
return fn
}, remove = function(key, cacheType) {
key = cacheId + "." + key, "localStorage" === cacheType ? localStorage.removeItem(key) : _defaultRemove(key)
}, removeAll = function() {
var ls = $window.localStorage;
if (ls.clear(), "angular" !== gaConfig.cache.type) {
var ang = $cacheFactory(cacheId);
ang.removeAll()
} else cache.removeAll()
}, info = function() {
if (!cache) return {
id: cacheId,
size: 0,
type: "none"
};
if ("function" == typeof cache.info) {
var tmp = cache.info();
return tmp.type = "angular", tmp
}
var size = 0;
for (var key in cache) key.substr(0, cacheId.length + 1) === cacheId + "." && (size += _byteLength(cache[key] + key));
return {
id: cacheId,
size: size,
type: "localStorage"
}
};
return {
get: get,
put: put,
remove: remove,
removeAll: removeAll,
info: info
}
}), angular.module("ga.utils.tracking", ["ga.utils.date"]).factory("gaUtilsT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment