Created
September 24, 2014 05:02
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "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¬found&passwordreset¬verified&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¬verified&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¬verified&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">← 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">← 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: " ", | |
| 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 || " ", | |
| empty: !element.innerHTML, | |
| inherit: null !== element.getAttribute("data-inherit") | |
| } | |
| } catch (e) { | |
| return { | |
| content: " ", | |
| 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, "<").replace(/>/g, ">") + "</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, " ").split(":"); | |
| arrFEvent.splice(-1, 1); | |
| var fpath = arrFEvent.join(" > ") + " >"; | |
| 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, " ").split(":"); | |
| ".*" === arrEvent[arrEvent.length - 1] ? arrEvent.splice(-2, 2) : arrEvent.splice(-1, 1); | |
| var fpath = (category ? category + " > " : "") + (arrEvent.length ? arrEvent.join(" > ") + " > " : ""); | |
| fpath = fpath.charAt(0).toUpperCase() + fpath.slice(1); | |
| var fpathclean = fpath.replace(/ /g, " ").replace(/>/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 > Level 1 >EnterArea1 >" | |
| } | |
| }, { | |
| category: "design", | |
| event: "Level 1:CompleteArea1", | |
| shortName: "CompleteArea1", | |
| formatted: { | |
| cleanPath: "Design > Level 1 > CompleteArea1", | |
| name: "CompleteArea1", | |
| path: "Design > Level 1 >CompleteArea1 >" | |
| } | |
| }, { | |
| category: "design", | |
| event: "Level 1:Levelup1", | |
| shortName: "Levelup1", | |
| formatted: { | |
| cleanPath: "Design > Level 1 > Levelup1", | |
| name: "Levelup1", | |
| path: "Design > Level 1 >Levelup1 >" | |
| } | |
| }, { | |
| category: "design", | |
| event: "Level 2:EnterArea2", | |
| shortName: "EnterArea2", | |
| formatted: { | |
| cleanPath: "Design > Level 2 > EnterArea2", | |
| name: "EnterArea2", | |
| path: "Design > Level 2 >EnterArea2 >" | |
| } | |
| }, { | |
| category: "design", | |
| event: "Level 2:CompleteArea2", | |
| shortName: "CompleteArea2", | |
| formatted: { | |
| cleanPath: "Design > Level 2 > CompleteArea2", | |
| name: "CompleteArea2", | |
| path: "Design > Level 2 >CompleteArea2 >" | |
| } | |
| }, { | |
| category: "design", | |
| event: "Level 2:Levelup2", | |
| shortName: "Levelup2", | |
| formatted: { | |
| cleanPath: "Design > Level 2 > Levelup2", | |
| name: "Levelup2", | |
| path: "Design > Level 2 >Levelup2 >" | |
| } | |
| }], | |
| 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(/&/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, "<").replace(/>/g, ">"), 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, " "), "<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