├── .github
└── dependabot.yml
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── app
└── index.html
├── dist
├── GameAnalytics.construct.js
├── GameAnalytics.d.ts
├── GameAnalytics.debug.js
├── GameAnalytics.js
├── GameAnalytics.jspre
├── GameAnalytics.min.js
└── GameAnalytics.node.js
├── gulpfile.js
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── Enums.ts
├── GameAnalytics.ts
├── PublicEnums.ts
├── device
│ └── GADevice.ts
├── events
│ └── GAEvents.ts
├── http
│ └── GAHTTPApi.ts
├── logging
│ └── GALogger.ts
├── state
│ └── GAState.ts
├── store
│ └── GAStore.ts
├── tasks
│ └── SdkErrorTask.ts
├── threading
│ ├── GAThreading.ts
│ ├── PriorityQueue.ts
│ └── TimedBlock.ts
├── utilities
│ └── GAUtilities.ts
└── validators
│ └── GAValidator.ts
├── test
├── testEvents.js
├── testGAState.js
├── testUtilities.js
└── testValidator.js
├── tsconfig.json
└── vendor
├── bundle.min.js
├── cryptojs.d.ts
├── enc-base64-min.js
├── enc-base64.js
├── hmac-sha256-min.js
└── hmac-sha256.js
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "04:00"
8 | open-pull-requests-limit: 10
9 | ignore:
10 | - dependency-name: gulp-replace
11 | versions:
12 | - 1.1.0
13 | - dependency-name: karma
14 | versions:
15 | - 6.0.4
16 | - 6.1.0
17 | - 6.1.1
18 | - 6.1.2
19 | - 6.2.0
20 | - 6.3.0
21 | - dependency-name: typescript
22 | versions:
23 | - 4.1.4
24 | - 4.1.5
25 | - 4.2.2
26 | - dependency-name: ws
27 | versions:
28 | - 7.4.3
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.mem
3 | vendor/bundle.js
4 | vendor/bundle.min.js
5 | dist/*.map
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | app
3 | test
4 | vendor
5 | gulpfile.js
6 | karma.conf.js
7 | tsconfig.json
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "12"
4 |
5 | notifications:
6 | slack:
7 | rooms:
8 | - gameanalytics:hDYD7nkOTe3tkrvb68WEpyqw#ask-sdk
9 | on_success: change
10 | on_failure: change
11 | email:
12 | - sdk@gameanalytics.com
13 |
14 | before_install: npm install -g gulp
15 | install: npm install
16 | before_script: gulp debug
17 |
18 | branches:
19 | only:
20 | - develop
21 | - master
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Game Analytics
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/gameanalytics)
2 | [](https://www.npmjs.com/package/gameanalytics)
3 | [](http://opensource.org/licenses/MIT)
4 |
5 | GA-SDK-JAVASCRIPT
6 | =================
7 |
8 | Official repository for GameAnalytics JavaScript SDK. Written in Typescript.
9 |
10 | Documentation can be found [here](https://gameanalytics.com/docs/javascript-sdk).
11 |
12 | Changelog
13 | ---------
14 |
15 | **4.4.6**
16 | * fixed timestamps for daylight saving time
17 |
18 | **4.4.5**
19 | * fixed enums in typescript decleration file
20 |
21 | **4.4.4**
22 | * changed event uuid field name
23 |
24 | **4.4.3**
25 | * added event uuid to events sent
26 |
27 | **4.4.2**
28 | * added error events to be sent for invalid custom event fields used
29 | * added optional mergeFields argument to event methods to merge with global custom fields instead of overwrite them
30 |
31 | **4.4.1**
32 | * fixed missing custom event fields for when trying to fix missing session end events
33 |
34 | **4.4.0**
35 | * added global custom event fields function to allow to add custom fields to events sent automatically by the SDK
36 |
37 | **4.3.1**
38 | * added functionality to force a new user in a/b testing without having to uninstall app first, simply use custom user id function to set a new user id which hasn't been used yet
39 |
40 | **4.3.0**
41 | * added custom event fields feature
42 |
43 | **4.2.3**
44 | * added flutter to sdk version validator
45 |
46 | **4.2.2**
47 | * fixed addProgressionEvent score bug
48 |
49 | **4.2.1**
50 | * fixed ad event bug
51 |
52 | **4.2.0**
53 | * added before unload listener functions
54 |
55 | **4.1.6**
56 | * removed unused logs
57 |
58 | **4.1.5**
59 | * updated client ts validator
60 |
61 | **4.1.4**
62 | * corrected ad event annotation
63 |
64 | **4.1.3**
65 | * added godot to version validator
66 |
67 | **4.1.2**
68 | * small correction for support for KaiOS
69 |
70 | **4.1.1**
71 | * added support for KaiOS
72 |
73 | **4.1.0**
74 | * added ad event
75 |
76 | **4.0.10**
77 | * fixed bug to not send stored events from previous sessions (offline events or session end events not sent yet) by games on the same domain
78 | * this bug fix can potentially affect metrics slightly so be aware of this as old stored events (offline events and session end events not sent yet) in games will not be sent with this new fix because internal keys for storing events in localstorage have changed now
79 |
80 | **4.0.9**
81 | * added better internal error reporting
82 |
83 | **4.0.8**
84 | * fixed cryptojs bug
85 |
86 | **4.0.7**
87 | * added session_num to init request
88 |
89 | **4.0.6**
90 | * removed gender, facebook and birthyear methods
91 |
92 | **4.0.5**
93 | * A/B testing fixes
94 |
95 | **4.0.4**
96 | * remote configs fixes
97 |
98 | **4.0.3**
99 | * small remote configs fix
100 |
101 | **4.0.2**
102 | * fixed events bug
103 |
104 | **4.0.1**
105 | * small bug fix for http requests
106 |
107 | **4.0.0**
108 | * Remote Config calls have been updated and the old calls have deprecated. Please see GA documentation for the new SDK calls and migration guide
109 | * A/B testing support added
110 |
111 | **3.1.2**
112 | * declaration file fix
113 |
114 | **3.1.1**
115 | * typescript definition file fixed
116 |
117 | **3.1.0**
118 | * aded enable/disable event submission function
119 |
120 | **3.0.3**
121 | * fixed business event validation
122 |
123 | **3.0.2**
124 | * removed manual session handling check for startsession and endsession
125 |
126 | **3.0.1**
127 | * added missing function mappings
128 |
129 | **3.0.0**
130 | * added command center functionality
131 |
132 | **2.1.5**
133 | * fix to getbrowserversion for webviews on ios
134 |
135 | **2.1.4**
136 | * added custom dimensions to design and error events
137 |
138 | **2.1.3**
139 | * fixed not allowing to add events when session is not started
140 | * fixed session length bug
141 |
142 | **2.1.2**
143 | * fixed browser version fetch to support facebook
144 |
145 | **2.1.1**
146 | * fixed null error on property 'running'
147 |
148 | **2.1.0**
149 | * fixed sending events request when no events to send
150 | * added possiblity to change event process interval
151 |
152 | **2.0.1**
153 | * scoped javascript sdk namespace
154 |
155 | **2.0.0**
156 | * changed root namespace from 'ga' to 'gameanalytics'
157 | * it is now possible to async load library on websites to avoid any delays when loading the website (just like it is possible with Google Analytics)
158 |
159 | **1.1.11**
160 | * added 'construct' to version validator
161 |
162 | **1.0.10**
163 | * bug fix for end session when using manual session handling
164 |
165 | **1.0.9**
166 | * bug fix for sending events straight after initializing sdk
167 |
168 | **1.0.8**
169 | * removed debug log messages for release distribution versions
170 |
171 | **1.0.7**
172 | * version validator updated with gamemaker
173 |
174 | **1.0.6**
175 | * small bug fix in validator
176 |
177 | **1.0.5**
178 | * added os version
179 |
180 | **1.0.4**
181 | * bug fix for GAEvents.fixMissingSessionEndEvents
182 |
183 | **1.0.3**
184 | * minor dependency fixes
185 |
186 | **1.0.2**
187 | * enabled to use sdk via npm
188 |
189 | **1.0.1**
190 | * fixed debug log messages to use console.log when console.debug is not available
191 |
192 | **1.0.0**
193 | * bumped to v1.0.0
194 |
195 | **0.1.0**
196 | * initial release
197 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GameAnalytics JavaScript SDK Test
5 |
6 |
13 |
22 |
23 |
24 |
25 |
26 |
61 | GA JavaScript SDK test app
62 |
63 |
64 |
--------------------------------------------------------------------------------
/dist/GameAnalytics.d.ts:
--------------------------------------------------------------------------------
1 | declare module gameanalytics {
2 | enum EGAErrorSeverity {
3 | Undefined = 0,
4 | Debug = 1,
5 | Info = 2,
6 | Warning = 3,
7 | Error = 4,
8 | Critical = 5
9 | }
10 | enum EGAProgressionStatus {
11 | Undefined = 0,
12 | Start = 1,
13 | Complete = 2,
14 | Fail = 3
15 | }
16 | enum EGAResourceFlowType {
17 | Undefined = 0,
18 | Source = 1,
19 | Sink = 2
20 | }
21 | enum EGAAdAction {
22 | Undefined = 0,
23 | Clicked = 1,
24 | Show = 2,
25 | FailedShow = 3,
26 | RewardReceived = 4
27 | }
28 | enum EGAAdError {
29 | Undefined = 0,
30 | Unknown = 1,
31 | Offline = 2,
32 | NoFill = 3,
33 | InternalError = 4,
34 | InvalidRequest = 5,
35 | UnableToPrecache = 6
36 | }
37 | enum EGAAdType {
38 | Undefined = 0,
39 | Video = 1,
40 | RewardedVideo = 2,
41 | Playable = 3,
42 | Interstitial = 4,
43 | OfferWall = 5,
44 | Banner = 6
45 | }
46 | module http {
47 | enum EGAHTTPApiResponse {
48 | NoResponse = 0,
49 | BadResponse = 1,
50 | RequestTimeout = 2,
51 | JsonEncodeFailed = 3,
52 | JsonDecodeFailed = 4,
53 | InternalServerError = 5,
54 | BadRequest = 6,
55 | Unauthorized = 7,
56 | UnknownResponseCode = 8,
57 | Ok = 9,
58 | Created = 10
59 | }
60 | }
61 | module events {
62 | enum EGASdkErrorCategory {
63 | Undefined = 0,
64 | EventValidation = 1,
65 | Database = 2,
66 | Init = 3,
67 | Http = 4,
68 | Json = 5
69 | }
70 | enum EGASdkErrorArea {
71 | Undefined = 0,
72 | BusinessEvent = 1,
73 | ResourceEvent = 2,
74 | ProgressionEvent = 3,
75 | DesignEvent = 4,
76 | ErrorEvent = 5,
77 | InitHttp = 9,
78 | EventsHttp = 10,
79 | ProcessEvents = 11,
80 | AddEventsToStore = 12,
81 | AdEvent = 20
82 | }
83 | enum EGASdkErrorAction {
84 | Undefined = 0,
85 | InvalidCurrency = 1,
86 | InvalidShortString = 2,
87 | InvalidEventPartLength = 3,
88 | InvalidEventPartCharacters = 4,
89 | InvalidStore = 5,
90 | InvalidFlowType = 6,
91 | StringEmptyOrNull = 7,
92 | NotFoundInAvailableCurrencies = 8,
93 | InvalidAmount = 9,
94 | NotFoundInAvailableItemTypes = 10,
95 | WrongProgressionOrder = 11,
96 | InvalidEventIdLength = 12,
97 | InvalidEventIdCharacters = 13,
98 | InvalidProgressionStatus = 15,
99 | InvalidSeverity = 16,
100 | InvalidLongString = 17,
101 | DatabaseTooLarge = 18,
102 | DatabaseOpenOrCreate = 19,
103 | JsonError = 25,
104 | FailHttpJsonDecode = 29,
105 | FailHttpJsonEncode = 30,
106 | InvalidAdAction = 31,
107 | InvalidAdType = 32,
108 | InvalidString = 33
109 | }
110 | enum EGASdkErrorParameter {
111 | Undefined = 0,
112 | Currency = 1,
113 | CartType = 2,
114 | ItemType = 3,
115 | ItemId = 4,
116 | Store = 5,
117 | FlowType = 6,
118 | Amount = 7,
119 | Progression01 = 8,
120 | Progression02 = 9,
121 | Progression03 = 10,
122 | EventId = 11,
123 | ProgressionStatus = 12,
124 | Severity = 13,
125 | Message = 14,
126 | AdAction = 15,
127 | AdType = 16,
128 | AdSdkName = 17,
129 | AdPlacement = 18
130 | }
131 | }
132 | }
133 | export module gameanalytics {
134 | enum EGAErrorSeverity {
135 | Undefined = 0,
136 | Debug = 1,
137 | Info = 2,
138 | Warning = 3,
139 | Error = 4,
140 | Critical = 5
141 | }
142 | enum EGAProgressionStatus {
143 | Undefined = 0,
144 | Start = 1,
145 | Complete = 2,
146 | Fail = 3
147 | }
148 | enum EGAResourceFlowType {
149 | Undefined = 0,
150 | Source = 1,
151 | Sink = 2
152 | }
153 | enum EGAAdAction {
154 | Undefined = 0,
155 | Clicked = 1,
156 | Show = 2,
157 | FailedShow = 3,
158 | RewardReceived = 4
159 | }
160 | enum EGAAdError {
161 | Undefined = 0,
162 | Unknown = 1,
163 | Offline = 2,
164 | NoFill = 3,
165 | InternalError = 4,
166 | InvalidRequest = 5,
167 | UnableToPrecache = 6
168 | }
169 | enum EGAAdType {
170 | Undefined = 0,
171 | Video = 1,
172 | RewardedVideo = 2,
173 | Playable = 3,
174 | Interstitial = 4,
175 | OfferWall = 5,
176 | Banner = 6
177 | }
178 | }
179 | declare module gameanalytics {
180 | module logging {
181 | class GALogger {
182 | private static readonly instance;
183 | private infoLogEnabled;
184 | private infoLogVerboseEnabled;
185 | private static debugEnabled;
186 | private static readonly Tag;
187 | private constructor();
188 | static setInfoLog(value: boolean): void;
189 | static setVerboseLog(value: boolean): void;
190 | static i(format: string): void;
191 | static w(format: string): void;
192 | static e(format: string): void;
193 | static ii(format: string): void;
194 | static d(format: string): void;
195 | private sendNotificationMessage;
196 | }
197 | }
198 | }
199 | declare module gameanalytics {
200 | module utilities {
201 | class GAUtilities {
202 | static getHmac(key: string, data: string): string;
203 | static stringMatch(s: string, pattern: RegExp): boolean;
204 | static joinStringArray(v: Array, delimiter: string): string;
205 | static stringArrayContainsString(array: Array, search: string): boolean;
206 | private static readonly keyStr;
207 | static encode64(input: string): string;
208 | static decode64(input: string): string;
209 | static timeIntervalSince1970(): number;
210 | static createGuid(): string;
211 | }
212 | }
213 | }
214 | declare module gameanalytics {
215 | module validators {
216 | import EGASdkErrorCategory = gameanalytics.events.EGASdkErrorCategory;
217 | import EGASdkErrorArea = gameanalytics.events.EGASdkErrorArea;
218 | import EGASdkErrorAction = gameanalytics.events.EGASdkErrorAction;
219 | import EGASdkErrorParameter = gameanalytics.events.EGASdkErrorParameter;
220 | class ValidationResult {
221 | category: EGASdkErrorCategory;
222 | area: EGASdkErrorArea;
223 | action: EGASdkErrorAction;
224 | parameter: EGASdkErrorParameter;
225 | reason: string;
226 | constructor(category: EGASdkErrorCategory, area: EGASdkErrorArea, action: EGASdkErrorAction, parameter: EGASdkErrorParameter, reason: string);
227 | }
228 | class GAValidator {
229 | static validateBusinessEvent(currency: string, amount: number, cartType: string, itemType: string, itemId: string): ValidationResult;
230 | static validateResourceEvent(flowType: EGAResourceFlowType, currency: string, amount: number, itemType: string, itemId: string, availableCurrencies: Array, availableItemTypes: Array): ValidationResult;
231 | static validateProgressionEvent(progressionStatus: EGAProgressionStatus, progression01: string, progression02: string, progression03: string): ValidationResult;
232 | static validateDesignEvent(eventId: string): ValidationResult;
233 | static validateErrorEvent(severity: EGAErrorSeverity, message: string): ValidationResult;
234 | static validateAdEvent(adAction: EGAAdAction, adType: EGAAdType, adSdkName: string, adPlacement: string): ValidationResult;
235 | static validateSdkErrorEvent(gameKey: string, gameSecret: string, category: EGASdkErrorCategory, area: EGASdkErrorArea, action: EGASdkErrorAction): boolean;
236 | static validateKeys(gameKey: string, gameSecret: string): boolean;
237 | static validateCurrency(currency: string): boolean;
238 | static validateEventPartLength(eventPart: string, allowNull: boolean): boolean;
239 | static validateEventPartCharacters(eventPart: string): boolean;
240 | static validateEventIdLength(eventId: string): boolean;
241 | static validateEventIdCharacters(eventId: string): boolean;
242 | static validateAndCleanInitRequestResponse(initResponse: {
243 | [key: string]: any;
244 | }, configsCreated: boolean): {
245 | [key: string]: any;
246 | };
247 | static validateBuild(build: string): boolean;
248 | static validateSdkWrapperVersion(wrapperVersion: string): boolean;
249 | static validateEngineVersion(engineVersion: string): boolean;
250 | static validateUserId(uId: string): boolean;
251 | static validateShortString(shortString: string, canBeEmpty: boolean): boolean;
252 | static validateString(s: string, canBeEmpty: boolean): boolean;
253 | static validateLongString(longString: string, canBeEmpty: boolean): boolean;
254 | static validateConnectionType(connectionType: string): boolean;
255 | static validateCustomDimensions(customDimensions: Array): boolean;
256 | static validateResourceCurrencies(resourceCurrencies: Array): boolean;
257 | static validateResourceItemTypes(resourceItemTypes: Array): boolean;
258 | static validateDimension01(dimension01: string, availableDimensions: Array): boolean;
259 | static validateDimension02(dimension02: string, availableDimensions: Array): boolean;
260 | static validateDimension03(dimension03: string, availableDimensions: Array): boolean;
261 | static validateArrayOfStrings(maxCount: number, maxStringLength: number, allowNoValues: boolean, logTag: string, arrayOfStrings: Array): boolean;
262 | static validateClientTs(clientTs: number): boolean;
263 | }
264 | }
265 | }
266 | declare module gameanalytics {
267 | module device {
268 | class NameValueVersion {
269 | name: string;
270 | value: string;
271 | version: string;
272 | constructor(name: string, value: string, version: string);
273 | }
274 | class NameVersion {
275 | name: string;
276 | version: string;
277 | constructor(name: string, version: string);
278 | }
279 | class GADevice {
280 | private static readonly sdkWrapperVersion;
281 | private static readonly osVersionPair;
282 | static readonly buildPlatform: string;
283 | static readonly deviceModel: string;
284 | static readonly deviceManufacturer: string;
285 | static readonly osVersion: string;
286 | static readonly browserVersion: string;
287 | static sdkGameEngineVersion: string;
288 | static gameEngineVersion: string;
289 | private static connectionType;
290 | static touch(): void;
291 | static getRelevantSdkVersion(): string;
292 | static getConnectionType(): string;
293 | static updateConnectionType(): void;
294 | private static getOSVersionString;
295 | private static runtimePlatformToString;
296 | private static getBrowserVersionString;
297 | private static getDeviceModel;
298 | private static getDeviceManufacturer;
299 | private static matchItem;
300 | }
301 | }
302 | }
303 | declare module gameanalytics {
304 | module threading {
305 | class TimedBlock {
306 | readonly deadline: Date;
307 | block: () => void;
308 | readonly id: number;
309 | ignore: boolean;
310 | async: boolean;
311 | running: boolean;
312 | private static idCounter;
313 | constructor(deadline: Date);
314 | }
315 | }
316 | }
317 | declare module gameanalytics {
318 | module threading {
319 | interface IComparer {
320 | compare(x: T, y: T): number;
321 | }
322 | class PriorityQueue {
323 | _subQueues: {
324 | [key: number]: Array;
325 | };
326 | _sortedKeys: Array;
327 | private comparer;
328 | constructor(priorityComparer: IComparer);
329 | enqueue(priority: number, item: TItem): void;
330 | private addQueueOfPriority;
331 | peek(): TItem;
332 | hasItems(): boolean;
333 | dequeue(): TItem;
334 | private dequeueFromHighPriorityQueue;
335 | }
336 | }
337 | }
338 | declare module gameanalytics {
339 | module store {
340 | enum EGAStoreArgsOperator {
341 | Equal = 0,
342 | LessOrEqual = 1,
343 | NotEqual = 2
344 | }
345 | enum EGAStore {
346 | Events = 0,
347 | Sessions = 1,
348 | Progression = 2
349 | }
350 | class GAStore {
351 | private static readonly instance;
352 | private static storageAvailable;
353 | private static readonly MaxNumberOfEntries;
354 | private eventsStore;
355 | private sessionsStore;
356 | private progressionStore;
357 | private storeItems;
358 | private static readonly StringFormat;
359 | private static readonly KeyFormat;
360 | private static readonly EventsStoreKey;
361 | private static readonly SessionsStoreKey;
362 | private static readonly ProgressionStoreKey;
363 | private static readonly ItemsStoreKey;
364 | private constructor();
365 | static isStorageAvailable(): boolean;
366 | static isStoreTooLargeForEvents(): boolean;
367 | static select(store: EGAStore, args?: Array<[string, EGAStoreArgsOperator, any]>, sort?: boolean, maxCount?: number): Array<{
368 | [key: string]: any;
369 | }>;
370 | static update(store: EGAStore, setArgs: Array<[string, any]>, whereArgs?: Array<[string, EGAStoreArgsOperator, any]>): boolean;
371 | static delete(store: EGAStore, args: Array<[string, EGAStoreArgsOperator, any]>): void;
372 | static insert(store: EGAStore, newEntry: {
373 | [key: string]: any;
374 | }, replace?: boolean, replaceKey?: string): void;
375 | static save(gameKey: string): void;
376 | static load(gameKey: string): void;
377 | static setItem(gameKey: string, key: string, value: string): void;
378 | static getItem(gameKey: string, key: string): string;
379 | private static getStore;
380 | }
381 | }
382 | }
383 | declare module gameanalytics {
384 | module state {
385 | class GAState {
386 | private static readonly CategorySdkError;
387 | private static readonly MAX_CUSTOM_FIELDS_COUNT;
388 | private static readonly MAX_CUSTOM_FIELDS_KEY_LENGTH;
389 | private static readonly MAX_CUSTOM_FIELDS_VALUE_STRING_LENGTH;
390 | static readonly instance: GAState;
391 | private constructor();
392 | private userId;
393 | static setUserId(userId: string): void;
394 | private identifier;
395 | static getIdentifier(): string;
396 | private initialized;
397 | static isInitialized(): boolean;
398 | static setInitialized(value: boolean): void;
399 | sessionStart: number;
400 | static getSessionStart(): number;
401 | private sessionNum;
402 | static getSessionNum(): number;
403 | isUnloading: boolean;
404 | private transactionNum;
405 | static getTransactionNum(): number;
406 | sessionId: string;
407 | static getSessionId(): string;
408 | private currentCustomDimension01;
409 | static getCurrentCustomDimension01(): string;
410 | private currentCustomDimension02;
411 | static getCurrentCustomDimension02(): string;
412 | private currentCustomDimension03;
413 | static getCurrentCustomDimension03(): string;
414 | private gameKey;
415 | static getGameKey(): string;
416 | private gameSecret;
417 | static getGameSecret(): string;
418 | private availableCustomDimensions01;
419 | static getAvailableCustomDimensions01(): Array;
420 | static setAvailableCustomDimensions01(value: Array): void;
421 | private availableCustomDimensions02;
422 | static getAvailableCustomDimensions02(): Array;
423 | static setAvailableCustomDimensions02(value: Array): void;
424 | private availableCustomDimensions03;
425 | static getAvailableCustomDimensions03(): Array;
426 | static setAvailableCustomDimensions03(value: Array): void;
427 | currentGlobalCustomEventFields: {
428 | [key: string]: any;
429 | };
430 | private availableResourceCurrencies;
431 | static getAvailableResourceCurrencies(): Array;
432 | static setAvailableResourceCurrencies(value: Array): void;
433 | private availableResourceItemTypes;
434 | static getAvailableResourceItemTypes(): Array;
435 | static setAvailableResourceItemTypes(value: Array): void;
436 | private build;
437 | static getBuild(): string;
438 | static setBuild(value: string): void;
439 | private useManualSessionHandling;
440 | static getUseManualSessionHandling(): boolean;
441 | private _isEventSubmissionEnabled;
442 | static isEventSubmissionEnabled(): boolean;
443 | sdkConfigCached: {
444 | [key: string]: any;
445 | };
446 | private configurations;
447 | private remoteConfigsIsReady;
448 | private remoteConfigsListeners;
449 | private beforeUnloadListeners;
450 | initAuthorized: boolean;
451 | clientServerTimeOffset: number;
452 | configsHash: string;
453 | abId: string;
454 | static getABTestingId(): string;
455 | abVariantId: string;
456 | static getABTestingVariantId(): string;
457 | private defaultUserId;
458 | private setDefaultId;
459 | static getDefaultId(): string;
460 | sdkConfigDefault: {
461 | [key: string]: string;
462 | };
463 | sdkConfig: {
464 | [key: string]: any;
465 | };
466 | static getSdkConfig(): {
467 | [key: string]: any;
468 | };
469 | private progressionTries;
470 | static readonly DefaultUserIdKey: string;
471 | static readonly SessionNumKey: string;
472 | static readonly TransactionNumKey: string;
473 | private static readonly Dimension01Key;
474 | private static readonly Dimension02Key;
475 | private static readonly Dimension03Key;
476 | static readonly SdkConfigCachedKey: string;
477 | static readonly LastUsedIdentifierKey: string;
478 | static isEnabled(): boolean;
479 | static setCustomDimension01(dimension: string): void;
480 | static setCustomDimension02(dimension: string): void;
481 | static setCustomDimension03(dimension: string): void;
482 | static incrementSessionNum(): void;
483 | static incrementTransactionNum(): void;
484 | static incrementProgressionTries(progression: string): void;
485 | static getProgressionTries(progression: string): number;
486 | static clearProgressionTries(progression: string): void;
487 | static setKeys(gameKey: string, gameSecret: string): void;
488 | static setManualSessionHandling(flag: boolean): void;
489 | static setEnabledEventSubmission(flag: boolean): void;
490 | static getEventAnnotations(): {
491 | [key: string]: any;
492 | };
493 | static getSdkErrorEventAnnotations(): {
494 | [key: string]: any;
495 | };
496 | static getInitAnnotations(): {
497 | [key: string]: any;
498 | };
499 | static getClientTsAdjusted(): number;
500 | static sessionIsStarted(): boolean;
501 | private static cacheIdentifier;
502 | static ensurePersistedStates(): void;
503 | static calculateServerTimeOffset(serverTs: number): number;
504 | private static formatString;
505 | static validateAndCleanCustomFields(fields: {
506 | [id: string]: any;
507 | }, errorCallback?: (baseMessage: string, message: string) => void): {
508 | [id: string]: any;
509 | };
510 | static validateAndFixCurrentDimensions(): void;
511 | static getConfigurationStringValue(key: string, defaultValue: string): string;
512 | static isRemoteConfigsReady(): boolean;
513 | static addRemoteConfigsListener(listener: {
514 | onRemoteConfigsUpdated: () => void;
515 | }): void;
516 | static removeRemoteConfigsListener(listener: {
517 | onRemoteConfigsUpdated: () => void;
518 | }): void;
519 | static getRemoteConfigsContentAsString(): string;
520 | static populateConfigurations(sdkConfig: {
521 | [key: string]: any;
522 | }): void;
523 | static addOnBeforeUnloadListener(listener: {
524 | onBeforeUnload: () => void;
525 | }): void;
526 | static removeOnBeforeUnloadListener(listener: {
527 | onBeforeUnload: () => void;
528 | }): void;
529 | static notifyBeforeUnloadListeners(): void;
530 | }
531 | }
532 | }
533 | declare module gameanalytics {
534 | module tasks {
535 | class SdkErrorTask {
536 | private static readonly MaxCount;
537 | private static readonly countMap;
538 | private static readonly timestampMap;
539 | static execute(url: string, type: string, payloadData: string, secretKey: string): void;
540 | }
541 | }
542 | }
543 | declare module gameanalytics {
544 | module http {
545 | import EGASdkErrorCategory = gameanalytics.events.EGASdkErrorCategory;
546 | import EGASdkErrorArea = gameanalytics.events.EGASdkErrorArea;
547 | import EGASdkErrorAction = gameanalytics.events.EGASdkErrorAction;
548 | import EGASdkErrorParameter = gameanalytics.events.EGASdkErrorParameter;
549 | class GAHTTPApi {
550 | static readonly instance: GAHTTPApi;
551 | private protocol;
552 | private hostName;
553 | private version;
554 | private remoteConfigsVersion;
555 | private baseUrl;
556 | private remoteConfigsBaseUrl;
557 | private initializeUrlPath;
558 | private eventsUrlPath;
559 | private useGzip;
560 | private static readonly MAX_ERROR_MESSAGE_LENGTH;
561 | private constructor();
562 | requestInit(configsHash: string, callback: (response: EGAHTTPApiResponse, json: {
563 | [key: string]: any;
564 | }) => void): void;
565 | sendEventsInArray(eventArray: Array<{
566 | [key: string]: any;
567 | }>, requestId: string, callback: (response: EGAHTTPApiResponse, json: {
568 | [key: string]: any;
569 | }, requestId: string, eventCount: number) => void): void;
570 | sendSdkErrorEvent(category: EGASdkErrorCategory, area: EGASdkErrorArea, action: EGASdkErrorAction, parameter: EGASdkErrorParameter, reason: string, gameKey: string, secretKey: string): void;
571 | private static sendEventInArrayRequestCallback;
572 | private static sendRequest;
573 | private static initRequestCallback;
574 | private createPayloadData;
575 | private processRequestResponse;
576 | private static sdkErrorCategoryString;
577 | private static sdkErrorAreaString;
578 | private static sdkErrorActionString;
579 | private static sdkErrorParameterString;
580 | }
581 | }
582 | }
583 | declare module gameanalytics {
584 | module events {
585 | class GAEvents {
586 | private static readonly CategorySessionStart;
587 | private static readonly CategorySessionEnd;
588 | private static readonly CategoryDesign;
589 | private static readonly CategoryBusiness;
590 | private static readonly CategoryProgression;
591 | private static readonly CategoryResource;
592 | private static readonly CategoryError;
593 | private static readonly CategoryAds;
594 | private static readonly MaxEventCount;
595 | private static readonly MAX_ERROR_COUNT;
596 | private static readonly countMap;
597 | private static readonly timestampMap;
598 | private constructor();
599 | private static customEventFieldsErrorCallback;
600 | static addSessionStartEvent(): void;
601 | static addSessionEndEvent(): void;
602 | static addBusinessEvent(currency: string, amount: number, itemType: string, itemId: string, cartType: string, fields: {
603 | [id: string]: any;
604 | }, mergeFields: boolean): void;
605 | static addResourceEvent(flowType: EGAResourceFlowType, currency: string, amount: number, itemType: string, itemId: string, fields: {
606 | [id: string]: any;
607 | }, mergeFields: boolean): void;
608 | static addProgressionEvent(progressionStatus: EGAProgressionStatus, progression01: string, progression02: string, progression03: string, score: number, sendScore: boolean, fields: {
609 | [id: string]: any;
610 | }, mergeFields: boolean): void;
611 | static addDesignEvent(eventId: string, value: number, sendValue: boolean, fields: {
612 | [id: string]: any;
613 | }, mergeFields: boolean): void;
614 | static addErrorEvent(severity: EGAErrorSeverity, message: string, fields: {
615 | [id: string]: any;
616 | }, mergeFields: boolean, skipAddingFields?: boolean): void;
617 | static addAdEvent(adAction: EGAAdAction, adType: EGAAdType, adSdkName: string, adPlacement: string, noAdReason: EGAAdError, duration: number, sendDuration: boolean, fields: {
618 | [id: string]: any;
619 | }, mergeFields: boolean): void;
620 | static processEvents(category: string, performCleanUp: boolean): void;
621 | private static processEventsCallback;
622 | private static cleanupEvents;
623 | private static fixMissingSessionEndEvents;
624 | private static addEventToStore;
625 | private static updateSessionStore;
626 | private static addDimensionsToEvent;
627 | private static addCustomFieldsToEvent;
628 | private static resourceFlowTypeToString;
629 | private static progressionStatusToString;
630 | private static errorSeverityToString;
631 | private static adActionToString;
632 | private static adErrorToString;
633 | private static adTypeToString;
634 | }
635 | }
636 | }
637 | declare module gameanalytics {
638 | module threading {
639 | class GAThreading {
640 | private static readonly instance;
641 | readonly blocks: PriorityQueue;
642 | private readonly id2TimedBlockMap;
643 | private static runTimeoutId;
644 | private static readonly ThreadWaitTimeInMs;
645 | private static ProcessEventsIntervalInSeconds;
646 | private keepRunning;
647 | private isRunning;
648 | private constructor();
649 | static createTimedBlock(delayInSeconds?: number): TimedBlock;
650 | static performTaskOnGAThread(taskBlock: () => void, delayInSeconds?: number): void;
651 | static performTimedBlockOnGAThread(timedBlock: TimedBlock): void;
652 | static scheduleTimer(interval: number, callback: () => void): number;
653 | static getTimedBlockById(blockIdentifier: number): TimedBlock;
654 | static ensureEventQueueIsRunning(): void;
655 | static endSessionAndStopQueue(): void;
656 | static stopEventQueue(): void;
657 | static ignoreTimer(blockIdentifier: number): void;
658 | static setEventProcessInterval(interval: number): void;
659 | private addTimedBlock;
660 | private static run;
661 | private static startThread;
662 | private static getNextBlock;
663 | private static processEventQueue;
664 | }
665 | }
666 | }
667 | declare module gameanalytics {
668 | class GameAnalytics {
669 | private static initTimedBlockId;
670 | static methodMap: {
671 | [id: string]: (...args: any[]) => void;
672 | };
673 | private static getGlobalObject;
674 | static init(): void;
675 | static gaCommand(...args: any[]): void;
676 | static configureAvailableCustomDimensions01(customDimensions?: Array): void;
677 | static configureAvailableCustomDimensions02(customDimensions?: Array): void;
678 | static configureAvailableCustomDimensions03(customDimensions?: Array): void;
679 | static configureAvailableResourceCurrencies(resourceCurrencies?: Array): void;
680 | static configureAvailableResourceItemTypes(resourceItemTypes?: Array): void;
681 | static configureBuild(build?: string): void;
682 | static configureSdkGameEngineVersion(sdkGameEngineVersion?: string): void;
683 | static configureGameEngineVersion(gameEngineVersion?: string): void;
684 | static configureUserId(uId?: string): void;
685 | static initialize(gameKey?: string, gameSecret?: string): void;
686 | static addBusinessEvent(currency?: string, amount?: number, itemType?: string, itemId?: string, cartType?: string, customFields?: {
687 | [id: string]: any;
688 | }, mergeFields?: boolean): void;
689 | static addResourceEvent(flowType?: EGAResourceFlowType, currency?: string, amount?: number, itemType?: string, itemId?: string, customFields?: {
690 | [id: string]: any;
691 | }, mergeFields?: boolean): void;
692 | static addProgressionEvent(progressionStatus?: EGAProgressionStatus, progression01?: string, progression02?: string, progression03?: string, score?: number, customFields?: {
693 | [id: string]: any;
694 | }, mergeFields?: boolean): void;
695 | static addDesignEvent(eventId: string, value?: number, customFields?: {
696 | [id: string]: any;
697 | }, mergeFields?: boolean): void;
698 | static addErrorEvent(severity?: EGAErrorSeverity, message?: string, customFields?: {
699 | [id: string]: any;
700 | }, mergeFields?: boolean): void;
701 | static addAdEventWithNoAdReason(adAction?: EGAAdAction, adType?: EGAAdType, adSdkName?: string, adPlacement?: string, noAdReason?: EGAAdError, customFields?: {
702 | [id: string]: any;
703 | }, mergeFields?: boolean): void;
704 | static addAdEventWithDuration(adAction?: EGAAdAction, adType?: EGAAdType, adSdkName?: string, adPlacement?: string, duration?: number, customFields?: {
705 | [id: string]: any;
706 | }, mergeFields?: boolean): void;
707 | static addAdEvent(adAction?: EGAAdAction, adType?: EGAAdType, adSdkName?: string, adPlacement?: string, customFields?: {
708 | [id: string]: any;
709 | }, mergeFields?: boolean): void;
710 | static setEnabledInfoLog(flag?: boolean): void;
711 | static setEnabledVerboseLog(flag?: boolean): void;
712 | static setEnabledManualSessionHandling(flag?: boolean): void;
713 | static setEnabledEventSubmission(flag?: boolean): void;
714 | static setCustomDimension01(dimension?: string): void;
715 | static setCustomDimension02(dimension?: string): void;
716 | static setCustomDimension03(dimension?: string): void;
717 | static setGlobalCustomEventFields(customFields?: {
718 | [id: string]: any;
719 | }): void;
720 | static setEventProcessInterval(intervalInSeconds: number): void;
721 | static startSession(): void;
722 | static endSession(): void;
723 | static onStop(): void;
724 | static onResume(): void;
725 | static getRemoteConfigsValueAsString(key: string, defaultValue?: string): string;
726 | static isRemoteConfigsReady(): boolean;
727 | static addRemoteConfigsListener(listener: {
728 | onRemoteConfigsUpdated: () => void;
729 | }): void;
730 | static removeRemoteConfigsListener(listener: {
731 | onRemoteConfigsUpdated: () => void;
732 | }): void;
733 | static getRemoteConfigsContentAsString(): string;
734 | static getABTestingId(): string;
735 | static getABTestingVariantId(): string;
736 | static addOnBeforeUnloadListener(listener: {
737 | onBeforeUnload: () => void;
738 | }): void;
739 | static removeOnBeforeUnloadListener(listener: {
740 | onBeforeUnload: () => void;
741 | }): void;
742 | private static internalInitialize;
743 | private static newSession;
744 | private static startNewSessionCallback;
745 | private static resumeSessionAndStartQueue;
746 | private static isSdkReady;
747 | }
748 | }
749 | declare var GameAnalyticsCommand: typeof gameanalytics.GameAnalytics.gaCommand;
750 | export declare var GameAnalytics: typeof gameanalytics.GameAnalytics;
751 | export default GameAnalytics;
752 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var ts = require('gulp-typescript');
3 | var sourcemaps = require('gulp-sourcemaps');
4 | var uglify = require('gulp-uglify');
5 | var Server = require('karma').Server;
6 | var argv = require('yargs').argv;
7 | var gulpif = require('gulp-if');
8 | var tsProject = ts.createProject('tsconfig.json');
9 | var tsProjectMini = ts.createProject('tsconfig.json', { outFile: "./dist/GameAnalytics.min.js" });
10 | var tsProjectDebug = ts.createProject('tsconfig.json', { outFile: "./dist/GameAnalytics.debug.js" });
11 | var tsDeclaration = ts.createProject('tsconfig.json');
12 | var replace = require('gulp-replace');
13 | var concat = require('gulp-concat');
14 | var insert = require('gulp-insert');
15 |
16 | gulp.task('build_debug', function() {
17 | var tsResult = tsProjectDebug.src()
18 | .pipe(sourcemaps.init())
19 | .pipe(tsProjectDebug());
20 |
21 | return tsResult.js
22 | .pipe(sourcemaps.write())
23 | .pipe(gulp.dest('.'));
24 | });
25 |
26 | gulp.task('declaration', function() {
27 | var tsResult = tsDeclaration.src()
28 | .pipe(tsDeclaration());
29 |
30 | return tsResult.dts
31 | .pipe(replace('declare module public_enums', 'export module gameanalytics'))
32 | .pipe(replace('declare var GameAnalytics', 'declare var GameAnalyticsCommand'))
33 | .pipe(insert.wrap("", "export declare var GameAnalytics: typeof gameanalytics.GameAnalytics;\n"))
34 | .pipe(insert.wrap("", "export default GameAnalytics;\n"))
35 | .pipe(gulp.dest('.'));
36 | });
37 |
38 | gulp.task('bundle_min_js', function() {
39 | return gulp.src(['./vendor/hmac-sha256-min.js', './vendor/enc-base64-min.js'])
40 | .pipe(concat('bundle.min.js'))
41 | .pipe(gulp.dest('./vendor'));
42 | });
43 |
44 | gulp.task('bundle_js', function() {
45 | return gulp.src(['./vendor/hmac-sha256.js', './vendor/enc-base64.js'])
46 | .pipe(concat('bundle.js'))
47 | .pipe(gulp.dest('./vendor'));
48 | });
49 |
50 | gulp.task('test', function (done) {
51 | new Server({
52 | configFile: __dirname + '/karma.conf.js',
53 | singleRun: true
54 | }, done).start();
55 | });
56 |
57 | gulp.task('build_mini', function() {
58 | var tsResult = tsProjectMini.src()
59 | .pipe(replace('GALogger.debugEnabled = true', 'GALogger.debugEnabled = false'))
60 | .pipe(replace('GALogger.d(', '//GALogger.d('))
61 | .pipe(gulpif(argv.nologging, replace('GALogger.', '//GALogger.')))
62 | .pipe(gulpif(argv.nologging, replace('//GALOGGER_START', '/*GALOGGER_START')))
63 | .pipe(gulpif(argv.nologging, replace('//GALOGGER_END', '//GALOGGER_END*/')))
64 | .pipe(gulpif(argv.nologging, replace('import GALogger = gameanalytics.logging.GALogger', '//import GALogger = gameanalytics.logging.GALogger')))
65 | .pipe(tsProjectMini());
66 |
67 | return tsResult.js
68 | .pipe(gulp.dest('.'));
69 | });
70 |
71 | gulp.task('build_normal', function() {
72 | var tsResult = tsProject.src()
73 | .pipe(replace('GALogger.debugEnabled = true', 'GALogger.debugEnabled = false'))
74 | .pipe(replace('GALogger.d(', '//GALogger.d('))
75 | .pipe(tsProject());
76 |
77 | return tsResult.js
78 | .pipe(gulp.dest('.'));
79 | });
80 |
81 | var mini = function() {
82 | return gulp.src(['./vendor/bundle.min.js', './dist/GameAnalytics.min.js'])
83 | .pipe(concat('GameAnalytics.min.js'))
84 | .pipe(uglify())
85 | .pipe(insert.wrap("(function(scope){\n", "\nscope.gameanalytics=gameanalytics;\nscope.GameAnalytics=GameAnalytics;\n})(this);\n"))
86 | .pipe(gulp.dest('./dist'));
87 | };
88 | gulp.task('mini', gulp.series(gulp.parallel('bundle_min_js', 'build_mini'), mini));
89 |
90 | var normal = function() {
91 | return gulp.src(['./vendor/bundle.min.js', './dist/GameAnalytics.js'])
92 | .pipe(concat('GameAnalytics.js'))
93 | .pipe(uglify())
94 | .pipe(insert.wrap("(function(scope){\n", "\nscope.gameanalytics=gameanalytics;\nscope.GameAnalytics=GameAnalytics;\n})(this);\n"))
95 | .pipe(gulp.dest('./dist'));
96 | };
97 | gulp.task('normal', gulp.series(gulp.parallel('bundle_min_js', 'build_normal'), normal));
98 |
99 | var unity = function() {
100 | return gulp.src(['./vendor/bundle.js', './dist/GameAnalytics.js'])
101 | .pipe(concat('GameAnalytics.jspre'))
102 | .pipe(gulp.dest('./dist'));
103 | };
104 | gulp.task('unity', gulp.series(gulp.parallel('bundle_js', 'build_normal'), unity));
105 |
106 | var ga_node = function() {
107 | return gulp.src(['./vendor/bundle.js', './dist/GameAnalytics.js'])
108 | .pipe(concat('GameAnalytics.node.js'))
109 | .pipe(insert.wrap("'use strict';\n", "module.exports = gameanalytics;"))
110 | .pipe(gulp.dest('./dist'));
111 | };
112 | gulp.task('ga_node', gulp.series(gulp.parallel('bundle_js', 'build_normal'), ga_node));
113 |
114 | var construct = function () {
115 | return gulp.src(['./vendor/bundle.js', './dist/GameAnalytics.js'])
116 | .pipe(concat('GameAnalytics.construct.js'))
117 | .pipe(insert.wrap("'use strict';\n", "globalThis.gameanalytics = gameanalytics;"))
118 | .pipe(gulp.dest('./dist'));
119 | };
120 | gulp.task('construct', gulp.series(gulp.parallel('bundle_js', 'build_normal'), construct));
121 |
122 | var debug = function() {
123 | return gulp.src(['./vendor/bundle.min.js', './dist/GameAnalytics.debug.js'])
124 | .pipe(concat('GameAnalytics.debug.js'))
125 | .pipe(insert.wrap("(function(scope){\n", "\nscope.gameanalytics=gameanalytics;\nscope.GameAnalytics=GameAnalytics;\n})(this);\n"))
126 | .pipe(gulp.dest('./dist'));
127 | };
128 | gulp.task('debug', gulp.series(gulp.parallel('bundle_min_js', 'build_debug'), debug));
129 |
130 | gulp.task('default', gulp.series('debug', 'mini', 'unity', 'ga_node', 'construct', 'normal', 'declaration'));
131 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | browsers: ['ChromeHeadless'],
4 | frameworks: ['jasmine'],
5 | files: [
6 | 'dist/*.js',
7 | 'test/*.js'
8 | ],
9 | exclude: [
10 | 'dist/*.min.js',
11 | 'dist/GameAnalytics.js',
12 | 'dist/GameAnalytics.node.js'
13 | ],
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gameanalytics",
3 | "author": "GameAnalytics",
4 | "version": "4.4.6",
5 | "description": "Official JavaScript SDK for GameAnalytics. GameAnalytics is a free analytics platform that helps game developers understand their players' behaviour by delivering relevant insights.",
6 | "keywords": [
7 | "GameAnalytics",
8 | "JavaScript"
9 | ],
10 | "main": "./dist/GameAnalytics.node.js",
11 | "types": "./dist/GameAnalytics.d.ts",
12 | "contributors": [
13 | {
14 | "name": "Martin Treacy-Schwartz",
15 | "email": "mts@gameanalytics.com"
16 | }
17 | ],
18 | "license": "MIT",
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/GameAnalytics/GA-SDK-JAVASCRIPT"
22 | },
23 | "homepage": "http://www.gameanalytics.com/",
24 | "devDependencies": {
25 | "ansi-regex": "^6.0.1",
26 | "copy-props": "^3.0.1",
27 | "engine.io": "^6.1.3",
28 | "follow-redirects": "^1.14.9",
29 | "glob-parent": "^6.0.2",
30 | "gulp": "^4.0.2",
31 | "gulp-concat": "^2.6.1",
32 | "gulp-if": "^3.0.0",
33 | "gulp-insert": "^0.5.0",
34 | "gulp-replace": "^1.0.0",
35 | "gulp-sourcemaps": "^3.0.0",
36 | "gulp-typescript": "^5.0.1",
37 | "gulp-uglify": "^3.0.2",
38 | "ini": "^3.0.0",
39 | "jasmine": "^4.0.2",
40 | "karma": "^6.3.17",
41 | "karma-chrome-launcher": "^3.1.0",
42 | "karma-jasmine": "^5.0.1",
43 | "kind-of": "^6.0.3",
44 | "lodash": "^4.17.21",
45 | "lodash.template": "^4.5.0",
46 | "log4js": "^6.4.2",
47 | "minimatch": "^5.0.1",
48 | "minimist": "^1.2.3",
49 | "mixin-deep": "^2.0.1",
50 | "nanoid": "^4.0.0",
51 | "path-parse": "^1.0.7",
52 | "postcss": "^8.4.7",
53 | "set-value": "^4.1.0",
54 | "tar": "^6.1.11",
55 | "typescript": "^4.6.2",
56 | "ws": "^8.5.0",
57 | "yargs": "^17.3.0",
58 | "yargs-parser": "^21.0.1"
59 | },
60 | "scripts": {
61 | "test": "gulp test"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Enums.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export enum EGAErrorSeverity {
4 | Undefined = 0,
5 | Debug = 1,
6 | Info = 2,
7 | Warning = 3,
8 | Error = 4,
9 | Critical = 5
10 | }
11 |
12 | export enum EGAProgressionStatus {
13 | Undefined = 0,
14 | Start = 1,
15 | Complete = 2,
16 | Fail = 3
17 | }
18 |
19 | export enum EGAResourceFlowType {
20 | Undefined = 0,
21 | Source = 1,
22 | Sink = 2
23 | }
24 |
25 | export enum EGAAdAction {
26 | Undefined = 0,
27 | Clicked = 1,
28 | Show = 2,
29 | FailedShow = 3,
30 | RewardReceived = 4
31 | }
32 |
33 | export enum EGAAdError {
34 | Undefined = 0,
35 | Unknown = 1,
36 | Offline = 2,
37 | NoFill = 3,
38 | InternalError = 4,
39 | InvalidRequest = 5,
40 | UnableToPrecache = 6
41 | }
42 |
43 | export enum EGAAdType {
44 | Undefined = 0,
45 | Video = 1,
46 | RewardedVideo = 2,
47 | Playable = 3,
48 | Interstitial = 4,
49 | OfferWall = 5,
50 | Banner = 6
51 | }
52 |
53 | export module http
54 | {
55 | export enum EGAHTTPApiResponse
56 | {
57 | // client
58 | NoResponse,
59 | BadResponse,
60 | RequestTimeout, // 408
61 | JsonEncodeFailed,
62 | JsonDecodeFailed,
63 | // server
64 | InternalServerError,
65 | BadRequest, // 400
66 | Unauthorized, // 401
67 | UnknownResponseCode,
68 | Ok,
69 | Created
70 | }
71 | }
72 |
73 | export module events
74 | {
75 | export enum EGASdkErrorCategory
76 | {
77 | Undefined = 0,
78 | EventValidation = 1,
79 | Database = 2,
80 | Init = 3,
81 | Http = 4,
82 | Json = 5
83 | }
84 |
85 | export enum EGASdkErrorArea
86 | {
87 | Undefined = 0,
88 | BusinessEvent = 1,
89 | ResourceEvent = 2,
90 | ProgressionEvent = 3,
91 | DesignEvent = 4,
92 | ErrorEvent = 5,
93 | InitHttp = 9,
94 | EventsHttp = 10,
95 | ProcessEvents = 11,
96 | AddEventsToStore = 12,
97 | AdEvent = 20
98 | }
99 |
100 | export enum EGASdkErrorAction
101 | {
102 | Undefined = 0,
103 | InvalidCurrency = 1,
104 | InvalidShortString = 2,
105 | InvalidEventPartLength = 3,
106 | InvalidEventPartCharacters = 4,
107 | InvalidStore = 5,
108 | InvalidFlowType = 6,
109 | StringEmptyOrNull = 7,
110 | NotFoundInAvailableCurrencies = 8,
111 | InvalidAmount = 9,
112 | NotFoundInAvailableItemTypes = 10,
113 | WrongProgressionOrder = 11,
114 | InvalidEventIdLength = 12,
115 | InvalidEventIdCharacters = 13,
116 | InvalidProgressionStatus = 15,
117 | InvalidSeverity = 16,
118 | InvalidLongString = 17,
119 | DatabaseTooLarge = 18,
120 | DatabaseOpenOrCreate = 19,
121 | JsonError = 25,
122 | FailHttpJsonDecode = 29,
123 | FailHttpJsonEncode = 30,
124 | InvalidAdAction = 31,
125 | InvalidAdType = 32,
126 | InvalidString = 33
127 | }
128 |
129 | export enum EGASdkErrorParameter
130 | {
131 | Undefined = 0,
132 | Currency = 1,
133 | CartType = 2,
134 | ItemType = 3,
135 | ItemId = 4,
136 | Store = 5,
137 | FlowType = 6,
138 | Amount = 7,
139 | Progression01 = 8,
140 | Progression02 = 9,
141 | Progression03 = 10,
142 | EventId = 11,
143 | ProgressionStatus = 12,
144 | Severity = 13,
145 | Message = 14,
146 | AdAction = 15,
147 | AdType = 16,
148 | AdSdkName = 17,
149 | AdPlacement = 18
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/PublicEnums.ts:
--------------------------------------------------------------------------------
1 | module public_enums
2 | {
3 | export enum EGAErrorSeverity
4 | {
5 | Undefined = 0,
6 | Debug = 1,
7 | Info = 2,
8 | Warning = 3,
9 | Error = 4,
10 | Critical = 5
11 | }
12 |
13 | export enum EGAProgressionStatus
14 | {
15 | Undefined = 0,
16 | Start = 1,
17 | Complete = 2,
18 | Fail = 3
19 | }
20 |
21 | export enum EGAResourceFlowType
22 | {
23 | Undefined = 0,
24 | Source = 1,
25 | Sink = 2
26 | }
27 |
28 | export enum EGAAdAction
29 | {
30 | Undefined = 0,
31 | Clicked = 1,
32 | Show = 2,
33 | FailedShow = 3,
34 | RewardReceived = 4
35 | }
36 |
37 | export enum EGAAdError
38 | {
39 | Undefined = 0,
40 | Unknown = 1,
41 | Offline = 2,
42 | NoFill = 3,
43 | InternalError = 4,
44 | InvalidRequest = 5,
45 | UnableToPrecache = 6
46 | }
47 |
48 | export enum EGAAdType
49 | {
50 | Undefined = 0,
51 | Video = 1,
52 | RewardedVideo = 2,
53 | Playable = 3,
54 | Interstitial = 4,
55 | OfferWall = 5,
56 | Banner = 6
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/device/GADevice.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module device
4 | {
5 | export class NameValueVersion
6 | {
7 | public name:string;
8 | public value:string;
9 | public version:string;
10 |
11 | public constructor(name:string, value:string, version:string)
12 | {
13 | this.name = name;
14 | this.value = value;
15 | this.version = version;
16 | }
17 | }
18 |
19 | export class NameVersion
20 | {
21 | public name:string;
22 | public version:string;
23 |
24 | public constructor(name:string, version:string)
25 | {
26 | this.name = name;
27 | this.version = version;
28 | }
29 | }
30 |
31 | export class GADevice
32 | {
33 | private static readonly sdkWrapperVersion:string = "javascript 4.4.6";
34 | private static readonly osVersionPair:NameVersion = GADevice.matchItem([
35 | navigator.platform,
36 | navigator.userAgent,
37 | navigator.appVersion,
38 | navigator.vendor
39 | ].join(' '), [
40 | new NameValueVersion("windows_phone", "Windows Phone", "OS"),
41 | new NameValueVersion("windows", "Win", "NT"),
42 | new NameValueVersion("ios", "iPhone", "OS"),
43 | new NameValueVersion("ios", "iPad", "OS"),
44 | new NameValueVersion("ios", "iPod", "OS"),
45 | new NameValueVersion("android", "Android", "Android"),
46 | new NameValueVersion("blackBerry", "BlackBerry", "/"),
47 | new NameValueVersion("mac_osx", "Mac", "OS X"),
48 | new NameValueVersion("tizen", "Tizen", "Tizen"),
49 | new NameValueVersion("linux", "Linux", "rv"),
50 | new NameValueVersion("kai_os", "KAIOS", "KAIOS")
51 | ]);
52 |
53 | public static readonly buildPlatform:string = GADevice.runtimePlatformToString();
54 | public static readonly deviceModel:string = GADevice.getDeviceModel();
55 | public static readonly deviceManufacturer:string = GADevice.getDeviceManufacturer();
56 | public static readonly osVersion:string = GADevice.getOSVersionString();
57 | public static readonly browserVersion:string = GADevice.getBrowserVersionString();
58 |
59 | public static sdkGameEngineVersion:string;
60 | public static gameEngineVersion:string;
61 | private static connectionType:string;
62 | public static touch(): void
63 | {
64 | }
65 |
66 | public static getRelevantSdkVersion(): string
67 | {
68 | if(GADevice.sdkGameEngineVersion)
69 | {
70 | return GADevice.sdkGameEngineVersion;
71 | }
72 | return GADevice.sdkWrapperVersion;
73 | }
74 |
75 | public static getConnectionType(): string
76 | {
77 | return GADevice.connectionType;
78 | }
79 |
80 | public static updateConnectionType(): void
81 | {
82 | if(navigator.onLine)
83 | {
84 | if(GADevice.buildPlatform === "ios" || GADevice.buildPlatform === "android")
85 | {
86 | GADevice.connectionType = "wwan";
87 | }
88 | else
89 | {
90 | GADevice.connectionType = "lan";
91 | }
92 | // TODO: Detect wifi usage
93 | }
94 | else
95 | {
96 | GADevice.connectionType = "offline";
97 | }
98 | }
99 |
100 | private static getOSVersionString(): string
101 | {
102 | return GADevice.buildPlatform + " " + GADevice.osVersionPair.version;
103 | }
104 |
105 | private static runtimePlatformToString(): string
106 | {
107 | return GADevice.osVersionPair.name;
108 | }
109 |
110 | private static getBrowserVersionString(): string
111 | {
112 | var ua:string = navigator.userAgent;
113 | var tem:RegExpMatchArray;
114 | var M:RegExpMatchArray = ua.match(/(opera|chrome|safari|firefox|ubrowser|msie|trident|fbav(?=\/))\/?\s*(\d+)/i) || [];
115 |
116 | if(M.length == 0)
117 | {
118 | if(GADevice.buildPlatform === "ios")
119 | {
120 | return "webkit_" + GADevice.osVersion;
121 | }
122 | }
123 |
124 | if(/trident/i.test(M[1]))
125 | {
126 | tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
127 | return 'IE ' + (tem[1] || '');
128 | }
129 |
130 | if(M[1] === 'Chrome')
131 | {
132 | tem = ua.match(/\b(OPR|Edge|UBrowser)\/(\d+)/);
133 | if(tem!= null)
134 | {
135 | return tem.slice(1).join(' ').replace('OPR', 'Opera').replace('UBrowser', 'UC').toLowerCase();
136 | }
137 | }
138 |
139 | if(M[1] && M[1].toLowerCase() === 'fbav')
140 | {
141 | M[1] = "facebook";
142 |
143 | if(M[2])
144 | {
145 | return "facebook " + M[2];
146 | }
147 | }
148 |
149 | var MString:string[] = M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
150 |
151 | if((tem = ua.match(/version\/(\d+)/i)) != null)
152 | {
153 | MString.splice(1, 1, tem[1]);
154 | }
155 |
156 | return MString.join(' ').toLowerCase();
157 | }
158 |
159 | private static getDeviceModel():string
160 | {
161 | var result:string = "unknown";
162 |
163 | return result;
164 | }
165 |
166 | private static getDeviceManufacturer():string
167 | {
168 | var result:string = "unknown";
169 |
170 | return result;
171 | }
172 |
173 | private static matchItem(agent:string, data:Array):NameVersion
174 | {
175 | var result:NameVersion = new NameVersion("unknown", "0.0.0");
176 |
177 | var i:number = 0;
178 | var j:number = 0;
179 | var regex:RegExp;
180 | var regexv:RegExp;
181 | var match:boolean;
182 | var matches:RegExpMatchArray;
183 | var mathcesResult:string;
184 | var version:string;
185 |
186 | for (i = 0; i < data.length; i += 1)
187 | {
188 | regex = new RegExp(data[i].value, 'i');
189 | match = regex.test(agent);
190 | if (match)
191 | {
192 | regexv = new RegExp(data[i].version + '[- /:;]([\\d._]+)', 'i');
193 | matches = agent.match(regexv);
194 | version = '';
195 | if (matches)
196 | {
197 | if (matches[1])
198 | {
199 | mathcesResult = matches[1];
200 | }
201 | }
202 | if (mathcesResult)
203 | {
204 | var matchesArray:string[] = mathcesResult.split(/[._]+/);
205 | for (j = 0; j < Math.min(matchesArray.length, 3); j += 1)
206 | {
207 | version += matchesArray[j] + (j < Math.min(matchesArray.length, 3) - 1 ? '.' : '');
208 | }
209 | }
210 | else
211 | {
212 | version = '0.0.0';
213 | }
214 |
215 | result.name = data[i].name;
216 | result.version = version;
217 |
218 | return result;
219 | }
220 | }
221 |
222 | return result;
223 | }
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/http/GAHTTPApi.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module http
4 | {
5 | import GAState = gameanalytics.state.GAState;
6 | import GALogger = gameanalytics.logging.GALogger;
7 | import GAUtilities = gameanalytics.utilities.GAUtilities;
8 | import GAValidator = gameanalytics.validators.GAValidator;
9 | import SdkErrorTask = gameanalytics.tasks.SdkErrorTask;
10 | import EGASdkErrorCategory = gameanalytics.events.EGASdkErrorCategory;
11 | import EGASdkErrorArea = gameanalytics.events.EGASdkErrorArea;
12 | import EGASdkErrorAction = gameanalytics.events.EGASdkErrorAction;
13 | import EGASdkErrorParameter = gameanalytics.events.EGASdkErrorParameter;
14 |
15 | export class GAHTTPApi
16 | {
17 | public static readonly instance:GAHTTPApi = new GAHTTPApi();
18 | private protocol:string;
19 | private hostName:string;
20 | private version:string;
21 | private remoteConfigsVersion:string;
22 | private baseUrl:string;
23 | private remoteConfigsBaseUrl:string;
24 | private initializeUrlPath:string;
25 | private eventsUrlPath:string;
26 | private useGzip:boolean;
27 | private static readonly MAX_ERROR_MESSAGE_LENGTH:number = 256;
28 |
29 | private constructor()
30 | {
31 | // base url settings
32 | this.protocol = "https";
33 | this.hostName = "api.gameanalytics.com";
34 | this.version = "v2";
35 | this.remoteConfigsVersion = "v1";
36 |
37 | // create base url
38 | this.baseUrl = this.protocol + "://" + this.hostName + "/" + this.version;
39 | this.remoteConfigsBaseUrl = this.protocol + "://" + this.hostName + "/remote_configs/" + this.remoteConfigsVersion;
40 |
41 | this.initializeUrlPath = "init";
42 | this.eventsUrlPath = "events";
43 |
44 | this.useGzip = false;
45 | }
46 |
47 | public requestInit(configsHash:string, callback:(response:EGAHTTPApiResponse, json:{[key:string]: any}) => void): void
48 | {
49 | var gameKey:string = GAState.getGameKey();
50 |
51 | // Generate URL
52 | var url:string = this.remoteConfigsBaseUrl + "/" + this.initializeUrlPath + "?game_key=" + gameKey + "&interval_seconds=0&configs_hash=" + configsHash;
53 | GALogger.d("Sending 'init' URL: " + url);
54 |
55 | var initAnnotations:{[key:string]: any} = GAState.getInitAnnotations();
56 |
57 | // make JSON string from data
58 | var JSONstring:string = JSON.stringify(initAnnotations);
59 |
60 | if(!JSONstring)
61 | {
62 | callback(EGAHTTPApiResponse.JsonEncodeFailed, null);
63 | return;
64 | }
65 |
66 | var payloadData:string = this.createPayloadData(JSONstring, this.useGzip);
67 | var extraArgs:Array = [];
68 | extraArgs.push(JSONstring);
69 | GAHTTPApi.sendRequest(url, payloadData, extraArgs, this.useGzip, GAHTTPApi.initRequestCallback, callback);
70 | }
71 |
72 | public sendEventsInArray(eventArray:Array<{[key:string]: any}>, requestId:string, callback:(response:EGAHTTPApiResponse, json:{[key:string]: any}, requestId:string, eventCount:number) => void): void
73 | {
74 | if(eventArray.length == 0)
75 | {
76 | GALogger.d("sendEventsInArray called with missing eventArray");
77 | return;
78 | }
79 |
80 | var gameKey:string = GAState.getGameKey();
81 |
82 | // Generate URL
83 | var url:string = this.baseUrl + "/" + gameKey + "/" + this.eventsUrlPath;
84 | GALogger.d("Sending 'events' URL: " + url);
85 |
86 | // make JSON string from data
87 | var JSONstring:string = JSON.stringify(eventArray);
88 |
89 | if(!JSONstring)
90 | {
91 | GALogger.d("sendEventsInArray JSON encoding failed of eventArray");
92 | callback(EGAHTTPApiResponse.JsonEncodeFailed, null, requestId, eventArray.length);
93 | return;
94 | }
95 |
96 | var payloadData = this.createPayloadData(JSONstring, this.useGzip);
97 | var extraArgs:Array = [];
98 | extraArgs.push(JSONstring);
99 | extraArgs.push(requestId);
100 | extraArgs.push(eventArray.length.toString());
101 | GAHTTPApi.sendRequest(url, payloadData, extraArgs, this.useGzip, GAHTTPApi.sendEventInArrayRequestCallback, callback);
102 | }
103 |
104 | public sendSdkErrorEvent(category:EGASdkErrorCategory, area:EGASdkErrorArea, action:EGASdkErrorAction, parameter:EGASdkErrorParameter, reason:string, gameKey:string, secretKey:string): void
105 | {
106 | if(!GAState.isEventSubmissionEnabled())
107 | {
108 | return;
109 | }
110 |
111 | // Validate
112 | if (!GAValidator.validateSdkErrorEvent(gameKey, secretKey, category, area, action))
113 | {
114 | return;
115 | }
116 |
117 | // Generate URL
118 | var url:string = this.baseUrl + "/" + gameKey + "/" + this.eventsUrlPath;
119 | GALogger.d("Sending 'events' URL: " + url);
120 |
121 | var payloadJSONString:string = "";
122 | var errorType:string = ""
123 |
124 | var json:{[key:string]: any} = GAState.getSdkErrorEventAnnotations();
125 |
126 | var categoryString:string = GAHTTPApi.sdkErrorCategoryString(category);
127 | json["error_category"] = categoryString;
128 | errorType += categoryString;
129 |
130 | var areaString:string = GAHTTPApi.sdkErrorAreaString(area);
131 | json["error_area"] = areaString;
132 | errorType += ":" + areaString;
133 |
134 | var actionString:string = GAHTTPApi.sdkErrorActionString(action);
135 | json["error_action"] = actionString;
136 |
137 | var parameterString:string = GAHTTPApi.sdkErrorParameterString(parameter);
138 | if(parameterString.length > 0)
139 | {
140 | json["error_parameter"] = parameterString;
141 | }
142 |
143 | if(reason.length > 0)
144 | {
145 | var reasonTrimmed = reason;
146 | if(reason.length > GAHTTPApi.MAX_ERROR_MESSAGE_LENGTH)
147 | {
148 | var reasonTrimmed = reason.substring(0, GAHTTPApi.MAX_ERROR_MESSAGE_LENGTH);
149 | }
150 | json["reason"] = reasonTrimmed;
151 | }
152 |
153 | var eventArray:Array<{[key:string]: any}> = [];
154 | eventArray.push(json);
155 | payloadJSONString = JSON.stringify(eventArray);
156 |
157 | if(!payloadJSONString)
158 | {
159 | GALogger.w("sendSdkErrorEvent: JSON encoding failed.");
160 | return;
161 | }
162 |
163 | GALogger.d("sendSdkErrorEvent json: " + payloadJSONString);
164 | SdkErrorTask.execute(url, errorType, payloadJSONString, secretKey);
165 | }
166 |
167 | private static sendEventInArrayRequestCallback(request:XMLHttpRequest, url:string, callback:(response:EGAHTTPApiResponse, json:{[key:string]: any}, requestId:string, eventCount:number) => void, extra:Array = null): void
168 | {
169 | var authorization:string = extra[0];
170 | var JSONstring:string = extra[1];
171 | var requestId:string = extra[2];
172 | var eventCount:number = parseInt(extra[3]);
173 | var body:string = "";
174 | var responseCode:number = 0;
175 |
176 | body = request.responseText;
177 | responseCode = request.status;
178 |
179 | GALogger.d("events request content: " + body);
180 |
181 | var requestResponseEnum:EGAHTTPApiResponse = GAHTTPApi.instance.processRequestResponse(responseCode, request.statusText, body, "Events");
182 |
183 | // if not 200 result
184 | if(requestResponseEnum != EGAHTTPApiResponse.Ok && requestResponseEnum != EGAHTTPApiResponse.Created && requestResponseEnum != EGAHTTPApiResponse.BadRequest)
185 | {
186 | GALogger.d("Failed events Call. URL: " + url + ", Authorization: " + authorization + ", JSONString: " + JSONstring);
187 | callback(requestResponseEnum, null, requestId, eventCount);
188 | return;
189 | }
190 |
191 | // decode JSON
192 | var requestJsonDict:{[key:string]: any} = body ? JSON.parse(body) : {};
193 |
194 | if(requestJsonDict == null)
195 | {
196 | callback(EGAHTTPApiResponse.JsonDecodeFailed, null, requestId, eventCount);
197 | GAHTTPApi.instance.sendSdkErrorEvent(EGASdkErrorCategory.Http, EGASdkErrorArea.EventsHttp, EGASdkErrorAction.FailHttpJsonDecode, EGASdkErrorParameter.Undefined, body, GAState.getGameKey(), GAState.getGameSecret());
198 | return;
199 | }
200 |
201 | // print reason if bad request
202 | if(requestResponseEnum == EGAHTTPApiResponse.BadRequest)
203 | {
204 | GALogger.d("Failed Events Call. Bad request. Response: " + JSON.stringify(requestJsonDict));
205 | }
206 |
207 | // return response
208 | callback(requestResponseEnum, requestJsonDict, requestId, eventCount);
209 | }
210 |
211 | private static sendRequest(url:string, payloadData:string, extraArgs:Array, gzip:boolean, callback:(request:XMLHttpRequest, url:string, callback:(response:EGAHTTPApiResponse, json:{[key:string]: any}, requestId:string, eventCount:number) => void, extra:Array) => void, callback2:(response:EGAHTTPApiResponse, json:{[key:string]: any}, requestId:string, eventCount:number) => void): void
212 | {
213 | var request:XMLHttpRequest = new XMLHttpRequest();
214 |
215 | // create authorization hash
216 | var key:string = GAState.getGameSecret();
217 | var authorization:string = GAUtilities.getHmac(key, payloadData);
218 |
219 | var args:Array = [];
220 | args.push(authorization);
221 |
222 | for(let s in extraArgs)
223 | {
224 | args.push(extraArgs[s]);
225 | }
226 |
227 | request.onreadystatechange = () => {
228 | if(request.readyState === 4)
229 | {
230 | callback(request, url, callback2, args);
231 | }
232 | };
233 |
234 | request.open("POST", url, true);
235 | request.setRequestHeader("Content-Type", "application/json");
236 |
237 | request.setRequestHeader("Authorization", authorization);
238 |
239 | if(gzip)
240 | {
241 | throw new Error("gzip not supported");
242 | //request.setRequestHeader("Content-Encoding", "gzip");
243 | }
244 |
245 | try
246 | {
247 | request.send(payloadData);
248 | }
249 | catch(e)
250 | {
251 | console.error(e.stack);
252 | }
253 | }
254 |
255 | private static initRequestCallback(request:XMLHttpRequest, url:string, callback:(response:EGAHTTPApiResponse, json:{[key:string]: any}, requestId:string, eventCount:number) => void, extra:Array = null): void
256 | {
257 | var authorization:string = extra[0];
258 | var JSONstring:string = extra[1];
259 | var body:string = "";
260 | var responseCode:number = 0;
261 |
262 | body = request.responseText;
263 | responseCode = request.status;
264 |
265 | // process the response
266 | GALogger.d("init request content : " + body + ", JSONstring: " + JSONstring);
267 |
268 | var requestJsonDict:{[key:string]: any} = body ? JSON.parse(body) : {};
269 | var requestResponseEnum:EGAHTTPApiResponse = GAHTTPApi.instance.processRequestResponse(responseCode, request.statusText, body, "Init");
270 |
271 | // if not 200 result
272 | if(requestResponseEnum != EGAHTTPApiResponse.Ok && requestResponseEnum != EGAHTTPApiResponse.Created && requestResponseEnum != EGAHTTPApiResponse.BadRequest)
273 | {
274 | GALogger.d("Failed Init Call. URL: " + url + ", Authorization: " + authorization + ", JSONString: " + JSONstring);
275 | callback(requestResponseEnum, null, "", 0);
276 | return;
277 | }
278 |
279 | if(requestJsonDict == null)
280 | {
281 | GALogger.d("Failed Init Call. Json decoding failed");
282 | callback(EGAHTTPApiResponse.JsonDecodeFailed, null, "", 0);
283 | GAHTTPApi.instance.sendSdkErrorEvent(EGASdkErrorCategory.Http, EGASdkErrorArea.InitHttp, EGASdkErrorAction.FailHttpJsonDecode, EGASdkErrorParameter.Undefined, body, GAState.getGameKey(), GAState.getGameSecret());
284 | return;
285 | }
286 |
287 | // print reason if bad request
288 | if(requestResponseEnum === EGAHTTPApiResponse.BadRequest)
289 | {
290 | GALogger.d("Failed Init Call. Bad request. Response: " + JSON.stringify(requestJsonDict));
291 | // return bad request result
292 | callback(requestResponseEnum, null, "", 0);
293 | return;
294 | }
295 |
296 | // validate Init call values
297 | var validatedInitValues:{[key:string]: any} = GAValidator.validateAndCleanInitRequestResponse(requestJsonDict, requestResponseEnum === EGAHTTPApiResponse.Created);
298 |
299 | if(!validatedInitValues)
300 | {
301 | callback(EGAHTTPApiResponse.BadResponse, null, "", 0);
302 | return;
303 | }
304 |
305 | // all ok
306 | callback(requestResponseEnum, validatedInitValues, "", 0);
307 | }
308 |
309 | private createPayloadData(payload:string, gzip:boolean): string
310 | {
311 | var payloadData:string;
312 |
313 | if(gzip)
314 | {
315 | // payloadData = GAUtilities.GzipCompress(payload);
316 | // GALogger.D("Gzip stats. Size: " + Encoding.UTF8.GetBytes(payload).Length + ", Compressed: " + payloadData.Length + ", Content: " + payload);
317 | throw new Error("gzip not supported");
318 | }
319 | else
320 | {
321 | payloadData = payload;
322 | }
323 |
324 | return payloadData;
325 | }
326 |
327 | private processRequestResponse(responseCode:number, responseMessage:string, body:string, requestId:string): EGAHTTPApiResponse
328 | {
329 | // if no result - often no connection
330 | if(!body)
331 | {
332 | GALogger.d(requestId + " request. failed. Might be no connection. Description: " + responseMessage + ", Status code: " + responseCode);
333 | return EGAHTTPApiResponse.NoResponse;
334 | }
335 |
336 | // ok
337 | if (responseCode === 200)
338 | {
339 | return EGAHTTPApiResponse.Ok;
340 | }
341 | // created
342 | if (responseCode === 201)
343 | {
344 | return EGAHTTPApiResponse.Created;
345 | }
346 |
347 | // 401 can return 0 status
348 | if (responseCode === 0 || responseCode === 401)
349 | {
350 | GALogger.d(requestId + " request. 401 - Unauthorized.");
351 | return EGAHTTPApiResponse.Unauthorized;
352 | }
353 |
354 | if (responseCode === 400)
355 | {
356 | GALogger.d(requestId + " request. 400 - Bad Request.");
357 | return EGAHTTPApiResponse.BadRequest;
358 | }
359 |
360 | if (responseCode === 500)
361 | {
362 | GALogger.d(requestId + " request. 500 - Internal Server Error.");
363 | return EGAHTTPApiResponse.InternalServerError;
364 | }
365 |
366 | return EGAHTTPApiResponse.UnknownResponseCode;
367 | }
368 |
369 | private static sdkErrorCategoryString(value:EGASdkErrorCategory): string
370 | {
371 | switch (value)
372 | {
373 | case EGASdkErrorCategory.EventValidation:
374 | return "event_validation";
375 | case EGASdkErrorCategory.Database:
376 | return "db";
377 | case EGASdkErrorCategory.Init:
378 | return "init";
379 | case EGASdkErrorCategory.Http:
380 | return "http";
381 | case EGASdkErrorCategory.Json:
382 | return "json";
383 | default:
384 | break;
385 | }
386 | return "";
387 | }
388 |
389 | private static sdkErrorAreaString(value:EGASdkErrorArea): string
390 | {
391 | switch (value)
392 | {
393 | case EGASdkErrorArea.BusinessEvent:
394 | return "business";
395 | case EGASdkErrorArea.ResourceEvent:
396 | return "resource";
397 | case EGASdkErrorArea.ProgressionEvent:
398 | return "progression";
399 | case EGASdkErrorArea.DesignEvent:
400 | return "design";
401 | case EGASdkErrorArea.ErrorEvent:
402 | return "error";
403 | case EGASdkErrorArea.InitHttp:
404 | return "init_http";
405 | case EGASdkErrorArea.EventsHttp:
406 | return "events_http";
407 | case EGASdkErrorArea.ProcessEvents:
408 | return "process_events";
409 | case EGASdkErrorArea.AddEventsToStore:
410 | return "add_events_to_store";
411 | default:
412 | break;
413 | }
414 | return "";
415 | }
416 |
417 | private static sdkErrorActionString(value:EGASdkErrorAction): string
418 | {
419 | switch (value)
420 | {
421 | case EGASdkErrorAction.InvalidCurrency:
422 | return "invalid_currency";
423 | case EGASdkErrorAction.InvalidShortString:
424 | return "invalid_short_string";
425 | case EGASdkErrorAction.InvalidEventPartLength:
426 | return "invalid_event_part_length";
427 | case EGASdkErrorAction.InvalidEventPartCharacters:
428 | return "invalid_event_part_characters";
429 | case EGASdkErrorAction.InvalidStore:
430 | return "invalid_store";
431 | case EGASdkErrorAction.InvalidFlowType:
432 | return "invalid_flow_type";
433 | case EGASdkErrorAction.StringEmptyOrNull:
434 | return "string_empty_or_null";
435 | case EGASdkErrorAction.NotFoundInAvailableCurrencies:
436 | return "not_found_in_available_currencies";
437 | case EGASdkErrorAction.InvalidAmount:
438 | return "invalid_amount";
439 | case EGASdkErrorAction.NotFoundInAvailableItemTypes:
440 | return "not_found_in_available_item_types";
441 | case EGASdkErrorAction.WrongProgressionOrder:
442 | return "wrong_progression_order";
443 | case EGASdkErrorAction.InvalidEventIdLength:
444 | return "invalid_event_id_length";
445 | case EGASdkErrorAction.InvalidEventIdCharacters:
446 | return "invalid_event_id_characters";
447 | case EGASdkErrorAction.InvalidProgressionStatus:
448 | return "invalid_progression_status";
449 | case EGASdkErrorAction.InvalidSeverity:
450 | return "invalid_severity";
451 | case EGASdkErrorAction.InvalidLongString:
452 | return "invalid_long_string";
453 | case EGASdkErrorAction.DatabaseTooLarge:
454 | return "db_too_large";
455 | case EGASdkErrorAction.DatabaseOpenOrCreate:
456 | return "db_open_or_create";
457 | case EGASdkErrorAction.JsonError:
458 | return "json_error";
459 | case EGASdkErrorAction.FailHttpJsonDecode:
460 | return "fail_http_json_decode";
461 | case EGASdkErrorAction.FailHttpJsonEncode:
462 | return "fail_http_json_encode";
463 | default:
464 | break;
465 | }
466 | return "";
467 | }
468 |
469 | private static sdkErrorParameterString(value:EGASdkErrorParameter): string
470 | {
471 | switch (value)
472 | {
473 | case EGASdkErrorParameter.Currency:
474 | return "currency";
475 | case EGASdkErrorParameter.CartType:
476 | return "cart_type";
477 | case EGASdkErrorParameter.ItemType:
478 | return "item_type";
479 | case EGASdkErrorParameter.ItemId:
480 | return "item_id";
481 | case EGASdkErrorParameter.Store:
482 | return "store";
483 | case EGASdkErrorParameter.FlowType:
484 | return "flow_type";
485 | case EGASdkErrorParameter.Amount:
486 | return "amount";
487 | case EGASdkErrorParameter.Progression01:
488 | return "progression01";
489 | case EGASdkErrorParameter.Progression02:
490 | return "progression02";
491 | case EGASdkErrorParameter.Progression03:
492 | return "progression03";
493 | case EGASdkErrorParameter.EventId:
494 | return "event_id";
495 | case EGASdkErrorParameter.ProgressionStatus:
496 | return "progression_status";
497 | case EGASdkErrorParameter.Severity:
498 | return "severity";
499 | case EGASdkErrorParameter.Message:
500 | return "message";
501 | default:
502 | break;
503 | }
504 | return "";
505 | }
506 | }
507 | }
508 | }
509 |
--------------------------------------------------------------------------------
/src/logging/GALogger.ts:
--------------------------------------------------------------------------------
1 | //GALOGGER_START
2 | module gameanalytics
3 | {
4 | export module logging
5 | {
6 | enum EGALoggerMessageType
7 | {
8 | Error = 0,
9 | Warning = 1,
10 | Info = 2,
11 | Debug = 3
12 | }
13 |
14 | export class GALogger
15 | {
16 | // Fields and properties: START
17 |
18 | private static readonly instance:GALogger = new GALogger();
19 | private infoLogEnabled:boolean;
20 | private infoLogVerboseEnabled:boolean;
21 | private static debugEnabled:boolean;
22 | private static readonly Tag:string = "GameAnalytics";
23 |
24 | // Fields and properties: END
25 |
26 | private constructor()
27 | {
28 | GALogger.debugEnabled = true;
29 | }
30 |
31 | // Methods: START
32 |
33 | public static setInfoLog(value:boolean): void
34 | {
35 | GALogger.instance.infoLogEnabled = value;
36 | }
37 |
38 | public static setVerboseLog(value:boolean): void
39 | {
40 | GALogger.instance.infoLogVerboseEnabled = value;
41 | }
42 |
43 | public static i(format:string): void
44 | {
45 | if(!GALogger.instance.infoLogEnabled)
46 | {
47 | return;
48 | }
49 |
50 | var message:string = "Info/" + GALogger.Tag + ": " + format;
51 | GALogger.instance.sendNotificationMessage(message, EGALoggerMessageType.Info);
52 | }
53 |
54 | public static w(format:string): void
55 | {
56 | var message:string = "Warning/" + GALogger.Tag + ": " + format;
57 | GALogger.instance.sendNotificationMessage(message, EGALoggerMessageType.Warning);
58 | }
59 |
60 | public static e(format:string): void
61 | {
62 | var message:string = "Error/" + GALogger.Tag + ": " + format;
63 | GALogger.instance.sendNotificationMessage(message, EGALoggerMessageType.Error);
64 | }
65 |
66 | public static ii(format:string): void
67 | {
68 | if(!GALogger.instance.infoLogVerboseEnabled)
69 | {
70 | return;
71 | }
72 |
73 | var message:string = "Verbose/" + GALogger.Tag + ": " + format;
74 | GALogger.instance.sendNotificationMessage(message, EGALoggerMessageType.Info);
75 | }
76 |
77 | public static d(format:string): void
78 | {
79 | if(!GALogger.debugEnabled)
80 | {
81 | return;
82 | }
83 |
84 | var message:string = "Debug/" + GALogger.Tag + ": " + format;
85 | GALogger.instance.sendNotificationMessage(message, EGALoggerMessageType.Debug);
86 | }
87 |
88 | private sendNotificationMessage(message:string, type:EGALoggerMessageType): void
89 | {
90 | switch(type)
91 | {
92 | case EGALoggerMessageType.Error:
93 | {
94 | console.error(message);
95 | }
96 | break;
97 |
98 | case EGALoggerMessageType.Warning:
99 | {
100 | console.warn(message);
101 | }
102 | break;
103 |
104 | case EGALoggerMessageType.Debug:
105 | {
106 | if(typeof console.debug === "function")
107 | {
108 | console.debug(message);
109 | }
110 | else
111 | {
112 | console.log(message);
113 | }
114 | }
115 | break;
116 |
117 | case EGALoggerMessageType.Info:
118 | {
119 | console.log(message);
120 | }
121 | break;
122 | }
123 | }
124 |
125 | // Methods: END
126 | }
127 | }
128 | }
129 | //GALOGGER_END
130 |
--------------------------------------------------------------------------------
/src/store/GAStore.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module store
4 | {
5 | import GALogger = gameanalytics.logging.GALogger;
6 |
7 | export enum EGAStoreArgsOperator
8 | {
9 | Equal,
10 | LessOrEqual,
11 | NotEqual
12 | }
13 |
14 | export enum EGAStore
15 | {
16 | Events = 0,
17 | Sessions = 1,
18 | Progression = 2
19 | }
20 |
21 | export class GAStore
22 | {
23 | private static readonly instance:GAStore = new GAStore();
24 | private static storageAvailable:boolean;
25 | private static readonly MaxNumberOfEntries:number = 2000;
26 | private eventsStore:Array<{[key:string]: any}> = [];
27 | private sessionsStore:Array<{[key:string]: any}> = [];
28 | private progressionStore:Array<{[key:string]: any}> = [];
29 | private storeItems:{[key:string]: any} = {};
30 | private static readonly StringFormat = (str:string, ...args:string[]) => str.replace(/{(\d+)}/g, (_, index:number) => args[index] || '');
31 | private static readonly KeyFormat:string = "GA::{0}::{1}";
32 | private static readonly EventsStoreKey:string = "ga_event";
33 | private static readonly SessionsStoreKey:string = "ga_session";
34 | private static readonly ProgressionStoreKey:string = "ga_progression";
35 | private static readonly ItemsStoreKey:string = "ga_items";
36 |
37 | private constructor()
38 | {
39 | try
40 | {
41 | if (typeof localStorage === 'object')
42 | {
43 | localStorage.setItem('testingLocalStorage', 'yes');
44 | localStorage.removeItem('testingLocalStorage');
45 | GAStore.storageAvailable = true;
46 | }
47 | else
48 | {
49 | GAStore.storageAvailable = false;
50 | }
51 | }
52 | catch (e)
53 | {
54 | }
55 |
56 | GALogger.d("Storage is available?: " + GAStore.storageAvailable);
57 | }
58 |
59 | public static isStorageAvailable():boolean
60 | {
61 | return GAStore.storageAvailable;
62 | }
63 |
64 | public static isStoreTooLargeForEvents(): boolean
65 | {
66 | return GAStore.instance.eventsStore.length + GAStore.instance.sessionsStore.length > GAStore.MaxNumberOfEntries;
67 | }
68 |
69 | public static select(store:EGAStore, args:Array<[string, EGAStoreArgsOperator, any]> = [], sort:boolean = false, maxCount:number = 0): Array<{[key:string]: any}>
70 | {
71 | var currentStore:Array<{[key:string]: any}> = GAStore.getStore(store);
72 |
73 | if(!currentStore)
74 | {
75 | return null;
76 | }
77 |
78 | var result:Array<{[key:string]: any}> = [];
79 |
80 | for(let i = 0; i < currentStore.length; ++i)
81 | {
82 | var entry:{[key:string]: any} = currentStore[i];
83 |
84 | var add:boolean = true;
85 | for(let j = 0; j < args.length; ++j)
86 | {
87 | var argsEntry:[string, EGAStoreArgsOperator, any] = args[j];
88 |
89 | if(entry[argsEntry[0]])
90 | {
91 | switch(argsEntry[1])
92 | {
93 | case EGAStoreArgsOperator.Equal:
94 | {
95 | add = entry[argsEntry[0]] == argsEntry[2];
96 | }
97 | break;
98 |
99 | case EGAStoreArgsOperator.LessOrEqual:
100 | {
101 | add = entry[argsEntry[0]] <= argsEntry[2];
102 | }
103 | break;
104 |
105 | case EGAStoreArgsOperator.NotEqual:
106 | {
107 | add = entry[argsEntry[0]] != argsEntry[2];
108 | }
109 | break;
110 |
111 | default:
112 | {
113 | add = false;
114 | }
115 | break;
116 | }
117 | }
118 | else
119 | {
120 | add = false;
121 | }
122 |
123 | if(!add)
124 | {
125 | break;
126 | }
127 | }
128 |
129 | if(add)
130 | {
131 | result.push(entry);
132 | }
133 | }
134 |
135 | if(sort)
136 | {
137 | result.sort((a:{[key:string]: any}, b:{[key:string]: any}) => {
138 | return (a["client_ts"] as number) - (b["client_ts"] as number)
139 | });
140 | }
141 |
142 | if(maxCount > 0 && result.length > maxCount)
143 | {
144 | result = result.slice(0, maxCount + 1)
145 | }
146 |
147 | return result;
148 | }
149 |
150 | public static update(store:EGAStore, setArgs:Array<[string, any]>, whereArgs:Array<[string, EGAStoreArgsOperator, any]> = []): boolean
151 | {
152 | var currentStore:Array<{[key:string]: any}> = GAStore.getStore(store);
153 |
154 | if(!currentStore)
155 | {
156 | return false;
157 | }
158 |
159 | for(let i = 0; i < currentStore.length; ++i)
160 | {
161 | var entry:{[key:string]: any} = currentStore[i];
162 |
163 | var update:boolean = true;
164 | for(let j = 0; j < whereArgs.length; ++j)
165 | {
166 | var argsEntry:[string, EGAStoreArgsOperator, any] = whereArgs[j];
167 |
168 | if(entry[argsEntry[0]])
169 | {
170 | switch(argsEntry[1])
171 | {
172 | case EGAStoreArgsOperator.Equal:
173 | {
174 | update = entry[argsEntry[0]] == argsEntry[2];
175 | }
176 | break;
177 |
178 | case EGAStoreArgsOperator.LessOrEqual:
179 | {
180 | update = entry[argsEntry[0]] <= argsEntry[2];
181 | }
182 | break;
183 |
184 | case EGAStoreArgsOperator.NotEqual:
185 | {
186 | update = entry[argsEntry[0]] != argsEntry[2];
187 | }
188 | break;
189 |
190 | default:
191 | {
192 | update = false;
193 | }
194 | break;
195 | }
196 | }
197 | else
198 | {
199 | update = false;
200 | }
201 |
202 | if(!update)
203 | {
204 | break;
205 | }
206 | }
207 |
208 | if(update)
209 | {
210 | for(let j = 0; j < setArgs.length; ++j)
211 | {
212 | var setArgsEntry:[string, any] = setArgs[j];
213 | entry[setArgsEntry[0]] = setArgsEntry[1];
214 | }
215 | }
216 | }
217 |
218 | return true;
219 | }
220 |
221 | public static delete(store:EGAStore, args:Array<[string, EGAStoreArgsOperator, any]>): void
222 | {
223 | var currentStore:Array<{[key:string]: any}> = GAStore.getStore(store);
224 |
225 | if(!currentStore)
226 | {
227 | return;
228 | }
229 |
230 | for(let i = 0; i < currentStore.length; ++i)
231 | {
232 | var entry:{[key:string]: any} = currentStore[i];
233 |
234 | var del:boolean = true;
235 | for(let j = 0; j < args.length; ++j)
236 | {
237 | var argsEntry:[string, EGAStoreArgsOperator, any] = args[j];
238 |
239 | if(entry[argsEntry[0]])
240 | {
241 | switch(argsEntry[1])
242 | {
243 | case EGAStoreArgsOperator.Equal:
244 | {
245 | del = entry[argsEntry[0]] == argsEntry[2];
246 | }
247 | break;
248 |
249 | case EGAStoreArgsOperator.LessOrEqual:
250 | {
251 | del = entry[argsEntry[0]] <= argsEntry[2];
252 | }
253 | break;
254 |
255 | case EGAStoreArgsOperator.NotEqual:
256 | {
257 | del = entry[argsEntry[0]] != argsEntry[2];
258 | }
259 | break;
260 |
261 | default:
262 | {
263 | del = false;
264 | }
265 | break;
266 | }
267 | }
268 | else
269 | {
270 | del = false;
271 | }
272 |
273 | if(!del)
274 | {
275 | break;
276 | }
277 | }
278 |
279 | if(del)
280 | {
281 | currentStore.splice(i, 1);
282 | --i;
283 | }
284 | }
285 | }
286 |
287 | public static insert(store:EGAStore, newEntry:{[key:string]: any}, replace:boolean = false, replaceKey:string = null): void
288 | {
289 | var currentStore:Array<{[key:string]: any}> = GAStore.getStore(store);
290 |
291 | if(!currentStore)
292 | {
293 | return;
294 | }
295 |
296 | if(replace)
297 | {
298 | if(!replaceKey)
299 | {
300 | return;
301 | }
302 |
303 | var replaced:boolean = false;
304 |
305 | for(let i = 0; i < currentStore.length; ++i)
306 | {
307 | var entry:{[key:string]: any} = currentStore[i];
308 |
309 | if(entry[replaceKey] == newEntry[replaceKey])
310 | {
311 | for(let s in newEntry)
312 | {
313 | entry[s] = newEntry[s];
314 | }
315 | replaced = true;
316 | break;
317 | }
318 | }
319 |
320 | if(!replaced)
321 | {
322 | currentStore.push(newEntry);
323 | }
324 | }
325 | else
326 | {
327 | currentStore.push(newEntry);
328 | }
329 | }
330 |
331 | public static save(gameKey:string): void
332 | {
333 | if(!GAStore.isStorageAvailable())
334 | {
335 | GALogger.w("Storage is not available, cannot save.");
336 | return;
337 | }
338 |
339 | localStorage.setItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.EventsStoreKey), JSON.stringify(GAStore.instance.eventsStore));
340 | localStorage.setItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.SessionsStoreKey), JSON.stringify(GAStore.instance.sessionsStore));
341 | localStorage.setItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.ProgressionStoreKey), JSON.stringify(GAStore.instance.progressionStore));
342 | localStorage.setItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.ItemsStoreKey), JSON.stringify(GAStore.instance.storeItems));
343 | }
344 |
345 | public static load(gameKey:string): void
346 | {
347 | if(!GAStore.isStorageAvailable())
348 | {
349 | GALogger.w("Storage is not available, cannot load.");
350 | return;
351 | }
352 |
353 | try
354 | {
355 | GAStore.instance.eventsStore = JSON.parse(localStorage.getItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.EventsStoreKey)));
356 |
357 | if(!GAStore.instance.eventsStore)
358 | {
359 | GAStore.instance.eventsStore = [];
360 | }
361 | }
362 | catch(e)
363 | {
364 | GALogger.w("Load failed for 'events' store. Using empty store.");
365 | GAStore.instance.eventsStore = [];
366 | }
367 |
368 | try
369 | {
370 | GAStore.instance.sessionsStore = JSON.parse(localStorage.getItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.SessionsStoreKey)));
371 |
372 | if(!GAStore.instance.sessionsStore)
373 | {
374 | GAStore.instance.sessionsStore = [];
375 | }
376 | }
377 | catch(e)
378 | {
379 | GALogger.w("Load failed for 'sessions' store. Using empty store.");
380 | GAStore.instance.sessionsStore = [];
381 | }
382 |
383 | try
384 | {
385 | GAStore.instance.progressionStore = JSON.parse(localStorage.getItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.ProgressionStoreKey)));
386 |
387 | if(!GAStore.instance.progressionStore)
388 | {
389 | GAStore.instance.progressionStore = [];
390 | }
391 | }
392 | catch(e)
393 | {
394 | GALogger.w("Load failed for 'progression' store. Using empty store.");
395 | GAStore.instance.progressionStore = [];
396 | }
397 |
398 | try
399 | {
400 | GAStore.instance.storeItems = JSON.parse(localStorage.getItem(GAStore.StringFormat(GAStore.KeyFormat, gameKey, GAStore.ItemsStoreKey)));
401 |
402 | if(!GAStore.instance.storeItems)
403 | {
404 | GAStore.instance.storeItems = {};
405 | }
406 | }
407 | catch(e)
408 | {
409 | GALogger.w("Load failed for 'items' store. Using empty store.");
410 | GAStore.instance.progressionStore = [];
411 | }
412 | }
413 |
414 | public static setItem(gameKey:string, key:string, value:string): void
415 | {
416 | var keyWithPrefix:string = GAStore.StringFormat(GAStore.KeyFormat, gameKey, key);
417 |
418 | if(!value)
419 | {
420 | if(keyWithPrefix in GAStore.instance.storeItems)
421 | {
422 | delete GAStore.instance.storeItems[keyWithPrefix];
423 | }
424 | }
425 | else
426 | {
427 | GAStore.instance.storeItems[keyWithPrefix] = value;
428 | }
429 | }
430 |
431 | public static getItem(gameKey:string, key:string): string
432 | {
433 | var keyWithPrefix:string = GAStore.StringFormat(GAStore.KeyFormat, gameKey, key);
434 | if(keyWithPrefix in GAStore.instance.storeItems)
435 | {
436 | return GAStore.instance.storeItems[keyWithPrefix] as string;
437 | }
438 | else
439 | {
440 | return null;
441 | }
442 | }
443 |
444 | private static getStore(store:EGAStore): Array<{[key:string]: any}>
445 | {
446 | switch(store)
447 | {
448 | case EGAStore.Events:
449 | {
450 | return GAStore.instance.eventsStore;
451 | }
452 |
453 | case EGAStore.Sessions:
454 | {
455 | return GAStore.instance.sessionsStore;
456 | }
457 |
458 | case EGAStore.Progression:
459 | {
460 | return GAStore.instance.progressionStore;
461 | }
462 |
463 | default:
464 | {
465 | GALogger.w("GAStore.getStore(): Cannot find store: " + store);
466 | return null;
467 | }
468 | }
469 | }
470 | }
471 | }
472 | }
473 |
--------------------------------------------------------------------------------
/src/tasks/SdkErrorTask.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module tasks
4 | {
5 | import GAUtilities = gameanalytics.utilities.GAUtilities;
6 | import GALogger = gameanalytics.logging.GALogger;
7 |
8 | export class SdkErrorTask
9 | {
10 | private static readonly MaxCount:number = 10;
11 | private static readonly countMap:{[key:string]: number} = {};
12 | private static readonly timestampMap:{[key:string]: Date} = {};
13 |
14 | public static execute(url:string, type:string, payloadData:string, secretKey:string): void
15 | {
16 | var now:Date = new Date();
17 |
18 | if(!SdkErrorTask.timestampMap[type])
19 | {
20 | SdkErrorTask.timestampMap[type] = now;
21 | }
22 | if(!SdkErrorTask.countMap[type])
23 | {
24 | SdkErrorTask.countMap[type] = 0;
25 | }
26 | var diff:number = now.getTime() - SdkErrorTask.timestampMap[type].getTime();
27 | var diffSeconds:number = diff / 1000;
28 | if(diffSeconds >= 3600)
29 | {
30 | SdkErrorTask.timestampMap[type] = now;
31 | SdkErrorTask.countMap[type] = 0;
32 | }
33 |
34 | if(SdkErrorTask.countMap[type] >= SdkErrorTask.MaxCount)
35 | {
36 | return;
37 | }
38 |
39 | var hashHmac:string = GAUtilities.getHmac(secretKey, payloadData);
40 |
41 | var request:XMLHttpRequest = new XMLHttpRequest();
42 |
43 | request.onreadystatechange = () => {
44 | if(request.readyState === 4)
45 | {
46 | if(!request.responseText)
47 | {
48 | GALogger.d("sdk error failed. Might be no connection. Description: " + request.statusText + ", Status code: " + request.status);
49 | return;
50 | }
51 |
52 | if(request.status != 200)
53 | {
54 | GALogger.w("sdk error failed. response code not 200. status code: " + request.status + ", description: " + request.statusText + ", body: " + request.responseText);
55 | return;
56 | }
57 | else
58 | {
59 | SdkErrorTask.countMap[type] = SdkErrorTask.countMap[type] + 1;
60 | }
61 | }
62 | };
63 |
64 | request.open("POST", url, true);
65 | request.setRequestHeader("Content-Type", "application/json");
66 | request.setRequestHeader("Authorization", hashHmac);
67 |
68 | try
69 | {
70 | request.send(payloadData);
71 | }
72 | catch(e)
73 | {
74 | console.error(e);
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/threading/GAThreading.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module threading
4 | {
5 | import GALogger = gameanalytics.logging.GALogger;
6 | import GAUtilities = gameanalytics.utilities.GAUtilities;
7 | import GAStore = gameanalytics.store.GAStore;
8 | import EGAStoreArgsOperator = gameanalytics.store.EGAStoreArgsOperator;
9 | import EGAStore = gameanalytics.store.EGAStore;
10 | import GAState = gameanalytics.state.GAState;
11 | import GAEvents = gameanalytics.events.GAEvents;
12 | import GAHTTPApi = gameanalytics.http.GAHTTPApi;
13 |
14 | export class GAThreading
15 | {
16 | private static readonly instance:GAThreading = new GAThreading();
17 | public readonly blocks:PriorityQueue = new PriorityQueue(>{
18 | compare: (x:number, y:number) => {
19 | return x - y;
20 | }
21 | });
22 | private readonly id2TimedBlockMap:{[key:number]: TimedBlock} = {};
23 | private static runTimeoutId:NodeJS.Timeout;
24 | private static readonly ThreadWaitTimeInMs:number = 1000;
25 | private static ProcessEventsIntervalInSeconds:number = 8.0;
26 | private keepRunning:boolean;
27 | private isRunning:boolean;
28 |
29 | private constructor()
30 | {
31 | GALogger.d("Initializing GA thread...");
32 | GAThreading.startThread();
33 | }
34 |
35 | public static createTimedBlock(delayInSeconds:number = 0): TimedBlock
36 | {
37 | var time:Date = new Date();
38 | time.setUTCSeconds(time.getUTCSeconds() + delayInSeconds);
39 |
40 | var timedBlock:TimedBlock = new TimedBlock(time);
41 | return timedBlock;
42 | }
43 |
44 | public static performTaskOnGAThread(taskBlock:() => void, delayInSeconds:number = 0): void
45 | {
46 | var time:Date = new Date();
47 | time.setUTCSeconds(time.getUTCSeconds() + delayInSeconds);
48 |
49 | var timedBlock:TimedBlock = new TimedBlock(time);
50 | timedBlock.block = taskBlock;
51 | GAThreading.instance.id2TimedBlockMap[timedBlock.id] = timedBlock;
52 | GAThreading.instance.addTimedBlock(timedBlock);
53 | }
54 |
55 | public static performTimedBlockOnGAThread(timedBlock:TimedBlock): void
56 | {
57 | GAThreading.instance.id2TimedBlockMap[timedBlock.id] = timedBlock;
58 | GAThreading.instance.addTimedBlock(timedBlock);
59 | }
60 |
61 | public static scheduleTimer(interval:number, callback:() => void): number
62 | {
63 | var time:Date = new Date();
64 | time.setUTCSeconds(time.getUTCSeconds() + interval);
65 |
66 | var timedBlock:TimedBlock = new TimedBlock(time);
67 | timedBlock.block = callback;
68 | GAThreading.instance.id2TimedBlockMap[timedBlock.id] = timedBlock;
69 | GAThreading.instance.addTimedBlock(timedBlock);
70 |
71 | return timedBlock.id;
72 | }
73 |
74 | public static getTimedBlockById(blockIdentifier:number): TimedBlock
75 | {
76 | if (blockIdentifier in GAThreading.instance.id2TimedBlockMap)
77 | {
78 | return GAThreading.instance.id2TimedBlockMap[blockIdentifier]
79 | }
80 | else
81 | {
82 | return null;
83 | }
84 | }
85 |
86 | public static ensureEventQueueIsRunning(): void
87 | {
88 | GAThreading.instance.keepRunning = true;
89 |
90 | if(!GAThreading.instance.isRunning)
91 | {
92 | GAThreading.instance.isRunning = true;
93 | GAThreading.scheduleTimer(GAThreading.ProcessEventsIntervalInSeconds, GAThreading.processEventQueue);
94 | }
95 | }
96 |
97 | public static endSessionAndStopQueue(): void
98 | {
99 | if(GAState.isInitialized())
100 | {
101 | GALogger.i("Ending session.");
102 | GAThreading.stopEventQueue();
103 | if (GAState.isEnabled() && GAState.sessionIsStarted())
104 | {
105 | GAEvents.addSessionEndEvent();
106 | GAState.instance.sessionStart = 0;
107 | }
108 | }
109 | }
110 |
111 | public static stopEventQueue(): void
112 | {
113 | GAThreading.instance.keepRunning = false;
114 | }
115 |
116 | public static ignoreTimer(blockIdentifier:number): void
117 | {
118 | if (blockIdentifier in GAThreading.instance.id2TimedBlockMap)
119 | {
120 | GAThreading.instance.id2TimedBlockMap[blockIdentifier].ignore = true;
121 | }
122 | }
123 |
124 | public static setEventProcessInterval(interval:number): void
125 | {
126 | if (interval > 0)
127 | {
128 | GAThreading.ProcessEventsIntervalInSeconds = interval;
129 | }
130 | }
131 |
132 | private addTimedBlock(timedBlock:TimedBlock): void
133 | {
134 | this.blocks.enqueue(timedBlock.deadline.getTime(), timedBlock);
135 | }
136 |
137 | private static run(): void
138 | {
139 | clearTimeout(GAThreading.runTimeoutId);
140 |
141 | try
142 | {
143 | var timedBlock:TimedBlock;
144 |
145 | while ((timedBlock = GAThreading.getNextBlock()))
146 | {
147 | if (!timedBlock.ignore)
148 | {
149 | if(timedBlock.async)
150 | {
151 | if(!timedBlock.running)
152 | {
153 | timedBlock.running = true;
154 | timedBlock.block();
155 | break;
156 | }
157 | }
158 | else
159 | {
160 | timedBlock.block();
161 | }
162 | }
163 | }
164 |
165 | GAThreading.runTimeoutId = setTimeout(GAThreading.run, GAThreading.ThreadWaitTimeInMs);
166 | return;
167 | }
168 | catch (e)
169 | {
170 | GALogger.e("Error on GA thread");
171 | GALogger.e(e.stack);
172 | }
173 | GALogger.d("Ending GA thread");
174 | }
175 |
176 | private static startThread(): void
177 | {
178 | GALogger.d("Starting GA thread");
179 | GAThreading.runTimeoutId = setTimeout(GAThreading.run, 0);
180 | }
181 |
182 | private static getNextBlock(): TimedBlock
183 | {
184 | var now:Date = new Date();
185 |
186 | if (GAThreading.instance.blocks.hasItems() && GAThreading.instance.blocks.peek().deadline.getTime() <= now.getTime())
187 | {
188 | if(GAThreading.instance.blocks.peek().async)
189 | {
190 | if(GAThreading.instance.blocks.peek().running)
191 | {
192 | return GAThreading.instance.blocks.peek();
193 | }
194 | else
195 | {
196 | return GAThreading.instance.blocks.dequeue();
197 | }
198 | }
199 | else
200 | {
201 | return GAThreading.instance.blocks.dequeue();
202 | }
203 | }
204 |
205 | return null;
206 | }
207 |
208 | private static processEventQueue(): void
209 | {
210 | GAEvents.processEvents("", true);
211 | if(GAThreading.instance.keepRunning)
212 | {
213 | GAThreading.scheduleTimer(GAThreading.ProcessEventsIntervalInSeconds, GAThreading.processEventQueue);
214 | }
215 | else
216 | {
217 | GAThreading.instance.isRunning = false;
218 | }
219 | }
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/threading/PriorityQueue.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module threading
4 | {
5 | export interface IComparer
6 | {
7 | compare(x:T, y:T): number;
8 | }
9 |
10 | export class PriorityQueue
11 | {
12 | public _subQueues:{[key:number]: Array};
13 | public _sortedKeys:Array;
14 | private comparer:IComparer;
15 |
16 | public constructor(priorityComparer:IComparer)
17 | {
18 | this.comparer = priorityComparer;
19 | this._subQueues = {};
20 | this._sortedKeys = [];
21 | }
22 |
23 | public enqueue(priority:number, item:TItem): void
24 | {
25 | if(this._sortedKeys.indexOf(priority) === -1)
26 | {
27 | this.addQueueOfPriority(priority);
28 | }
29 |
30 | this._subQueues[priority].push(item);
31 | }
32 |
33 | private addQueueOfPriority(priority:number): void
34 | {
35 | this._sortedKeys.push(priority);
36 | this._sortedKeys.sort((x:number, y:number) => this.comparer.compare(x, y));
37 | this._subQueues[priority] = [];
38 | }
39 |
40 | public peek(): TItem
41 | {
42 | if(this.hasItems())
43 | {
44 | return this._subQueues[this._sortedKeys[0]][0];
45 | }
46 | else
47 | {
48 | throw new Error("The queue is empty");
49 | }
50 | }
51 |
52 | public hasItems(): boolean
53 | {
54 | return this._sortedKeys.length > 0;
55 | }
56 |
57 | public dequeue(): TItem
58 | {
59 | if(this.hasItems())
60 | {
61 | return this.dequeueFromHighPriorityQueue();
62 | }
63 | else
64 | {
65 | throw new Error("The queue is empty");
66 | }
67 | }
68 |
69 | private dequeueFromHighPriorityQueue(): TItem
70 | {
71 | var firstKey:number = this._sortedKeys[0];
72 | var nextItem:TItem = this._subQueues[firstKey].shift();
73 | if(this._subQueues[firstKey].length === 0)
74 | {
75 | this._sortedKeys.shift();
76 | delete this._subQueues[firstKey];
77 | }
78 |
79 | return nextItem;
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/threading/TimedBlock.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module threading
4 | {
5 | export class TimedBlock
6 | {
7 | public readonly deadline:Date;
8 | public block:() => void;
9 | public readonly id:number;
10 | public ignore:boolean;
11 | public async:boolean;
12 | public running:boolean;
13 | private static idCounter:number = 0;
14 |
15 | public constructor(deadline:Date)
16 | {
17 | this.deadline = deadline;
18 | this.ignore = false;
19 | this.async = false;
20 | this.running = false;
21 | this.id = ++TimedBlock.idCounter;
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/utilities/GAUtilities.ts:
--------------------------------------------------------------------------------
1 | module gameanalytics
2 | {
3 | export module utilities
4 | {
5 | import GALogger = gameanalytics.logging.GALogger;
6 |
7 | export class GAUtilities
8 | {
9 | public static getHmac(key:string, data:string): string
10 | {
11 | var encryptedMessage = CryptoJS.HmacSHA256(data, key);
12 | return CryptoJS.enc.Base64.stringify(encryptedMessage);
13 | }
14 |
15 | public static stringMatch(s:string, pattern:RegExp): boolean
16 | {
17 | if(!s || !pattern)
18 | {
19 | return false;
20 | }
21 |
22 | return pattern.test(s);
23 | }
24 |
25 | public static joinStringArray(v:Array, delimiter:string): string
26 | {
27 | var result:string = "";
28 |
29 | for (let i = 0, il = v.length; i < il; i++)
30 | {
31 | if (i > 0)
32 | {
33 | result += delimiter;
34 | }
35 | result += v[i];
36 | }
37 | return result;
38 | }
39 |
40 | public static stringArrayContainsString(array:Array, search:string): boolean
41 | {
42 | if (array.length === 0)
43 | {
44 | return false;
45 | }
46 |
47 | for(let s in array)
48 | {
49 | if(array[s] === search)
50 | {
51 | return true;
52 | }
53 | }
54 | return false;
55 | }
56 |
57 | private static readonly keyStr:string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
58 |
59 | public static encode64(input:string): string
60 | {
61 | input = encodeURI(input);
62 | var output:string = "";
63 | var chr1:number, chr2:number, chr3:number = 0;
64 | var enc1:number, enc2:number, enc3:number, enc4:number = 0;
65 | var i = 0;
66 |
67 | do
68 | {
69 | chr1 = input.charCodeAt(i++);
70 | chr2 = input.charCodeAt(i++);
71 | chr3 = input.charCodeAt(i++);
72 |
73 | enc1 = chr1 >> 2;
74 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
75 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
76 | enc4 = chr3 & 63;
77 |
78 | if (isNaN(chr2))
79 | {
80 | enc3 = enc4 = 64;
81 | }
82 | else if (isNaN(chr3))
83 | {
84 | enc4 = 64;
85 | }
86 |
87 | output = output +
88 | GAUtilities.keyStr.charAt(enc1) +
89 | GAUtilities.keyStr.charAt(enc2) +
90 | GAUtilities.keyStr.charAt(enc3) +
91 | GAUtilities.keyStr.charAt(enc4);
92 | chr1 = chr2 = chr3 = 0;
93 | enc1 = enc2 = enc3 = enc4 = 0;
94 | }
95 | while (i < input.length);
96 |
97 | return output;
98 | }
99 |
100 | public static decode64(input:string): string
101 | {
102 | var output:string = "";
103 | var chr1:number, chr2:number, chr3:number = 0;
104 | var enc1:number, enc2:number, enc3:number, enc4:number = 0;
105 | var i = 0;
106 |
107 | // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
108 | var base64test = /[^A-Za-z0-9\+\/\=]/g;
109 | if (base64test.exec(input)) {
110 | GALogger.w("There were invalid base64 characters in the input text. Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='. Expect errors in decoding.");
111 | }
112 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
113 |
114 | do
115 | {
116 | enc1 = GAUtilities.keyStr.indexOf(input.charAt(i++));
117 | enc2 = GAUtilities.keyStr.indexOf(input.charAt(i++));
118 | enc3 = GAUtilities.keyStr.indexOf(input.charAt(i++));
119 | enc4 = GAUtilities.keyStr.indexOf(input.charAt(i++));
120 |
121 | chr1 = (enc1 << 2) | (enc2 >> 4);
122 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
123 | chr3 = ((enc3 & 3) << 6) | enc4;
124 |
125 | output = output + String.fromCharCode(chr1);
126 |
127 | if (enc3 != 64) {
128 | output = output + String.fromCharCode(chr2);
129 | }
130 | if (enc4 != 64) {
131 | output = output + String.fromCharCode(chr3);
132 | }
133 |
134 | chr1 = chr2 = chr3 = 0;
135 | enc1 = enc2 = enc3 = enc4 = 0;
136 |
137 | }
138 | while (i < input.length);
139 |
140 | return decodeURI(output);
141 | }
142 |
143 | public static timeIntervalSince1970(): number
144 | {
145 | var date:Date = new Date();
146 | return Math.round(date.getTime() / 1000);
147 | }
148 |
149 | public static createGuid(): string
150 | {
151 | return ("10000000-1000-4000-8000-100000000000").replace(/[018]/g, c => (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16));
152 | }
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/test/testEvents.js:
--------------------------------------------------------------------------------
1 | describe("Events", function () {
2 | describe("configureAvailableCustomDimensions01", function () {
3 | it("should be added to queue", function() {
4 | gameanalytics.GameAnalytics.configureAvailableCustomDimensions01(["ninja", "samurai"]);
5 | expect(countPatternFoundInBlocks("setAvailableCustomDimensions01")).toEqual(1);
6 | GameAnalytics("configureAvailableCustomDimensions01", ["ninja", "samurai"]);
7 | expect(countPatternFoundInBlocks("setAvailableCustomDimensions01")).toEqual(2);
8 | });
9 | });
10 |
11 | describe("configureAvailableCustomDimensions02", function () {
12 | it("should be added to queue", function() {
13 | gameanalytics.GameAnalytics.configureAvailableCustomDimensions02(["ninja", "samurai"]);
14 | expect(countPatternFoundInBlocks("setAvailableCustomDimensions02")).toEqual(1);
15 | GameAnalytics("configureAvailableCustomDimensions02", ["ninja", "samurai"]);
16 | expect(countPatternFoundInBlocks("setAvailableCustomDimensions02")).toEqual(2);
17 | });
18 | });
19 |
20 | describe("configureAvailableCustomDimensions03", function () {
21 | it("should be added to queue", function() {
22 | gameanalytics.GameAnalytics.configureAvailableCustomDimensions03(["ninja", "samurai"]);
23 | expect(countPatternFoundInBlocks("setAvailableCustomDimensions03")).toEqual(1);
24 | GameAnalytics("configureAvailableCustomDimensions03", ["ninja", "samurai"]);
25 | expect(countPatternFoundInBlocks("setAvailableCustomDimensions03")).toEqual(2);
26 | });
27 | });
28 |
29 | describe("configureAvailableResourceCurrencies", function () {
30 | it("should be added to queue", function() {
31 | gameanalytics.GameAnalytics.configureAvailableResourceCurrencies(["gems", "gold"]);
32 | expect(countPatternFoundInBlocks("setAvailableResourceCurrencies")).toEqual(1);
33 | GameAnalytics("configureAvailableResourceCurrencies", ["gems", "gold"]);
34 | expect(countPatternFoundInBlocks("setAvailableResourceCurrencies")).toEqual(2);
35 | });
36 | });
37 |
38 | describe("configureAvailableResourceItemTypes", function () {
39 | it("should be added to queue", function() {
40 | gameanalytics.GameAnalytics.configureAvailableResourceItemTypes(["guns", "powerups"]);
41 | expect(countPatternFoundInBlocks("setAvailableResourceItemTypes")).toEqual(1);
42 | GameAnalytics("configureAvailableResourceItemTypes", ["guns", "powerups"]);
43 | expect(countPatternFoundInBlocks("setAvailableResourceItemTypes")).toEqual(2);
44 | });
45 | });
46 |
47 | describe("configureBuild", function () {
48 | it("should be added to queue", function() {
49 | gameanalytics.GameAnalytics.configureBuild("1.0.0");
50 | expect(countPatternFoundInBlocks("setBuild")).toEqual(1);
51 | GameAnalytics("configureBuild", "1.0.0");
52 | expect(countPatternFoundInBlocks("setBuild")).toEqual(2);
53 | });
54 | });
55 |
56 | describe("configureSdkGameEngineVersion", function () {
57 | it("should be added to queue", function() {
58 | gameanalytics.GameAnalytics.configureSdkGameEngineVersion("unity 1.0.0");
59 | expect(countPatternFoundInBlocks("GADevice.sdkGameEngineVersion")).toEqual(1);
60 | GameAnalytics("configureSdkGameEngineVersion", "unity 1.0.0");
61 | expect(countPatternFoundInBlocks("GADevice.sdkGameEngineVersion")).toEqual(2);
62 | });
63 | });
64 |
65 | describe("configureGameEngineVersion", function () {
66 | it("should be added to queue", function() {
67 | gameanalytics.GameAnalytics.configureGameEngineVersion("unity 1.0.0");
68 | expect(countPatternFoundInBlocks("GADevice.gameEngineVersion")).toEqual(1);
69 | GameAnalytics("configureGameEngineVersion", "unity 1.0.0");
70 | expect(countPatternFoundInBlocks("GADevice.gameEngineVersion")).toEqual(2);
71 | });
72 | });
73 |
74 | describe("configureUserId", function () {
75 | it("should be added to queue", function() {
76 | gameanalytics.GameAnalytics.configureUserId("custom_id");
77 | expect(countPatternFoundInBlocks("setUserId")).toEqual(1);
78 | GameAnalytics("configureUserId", "custom_id");
79 | expect(countPatternFoundInBlocks("setUserId")).toEqual(2);
80 | });
81 | });
82 |
83 | describe("initialize", function () {
84 | it("should be added to queue", function() {
85 | gameanalytics.GameAnalytics.initialize("gameKey", "secretKey");
86 | expect(countPatternFoundInBlocks("internalInitialize")).toEqual(1);
87 | GameAnalytics("initialize", "gameKey", "secretKey");
88 | expect(countPatternFoundInBlocks("internalInitialize")).toEqual(2);
89 | });
90 | });
91 |
92 | describe("addBusinessEvent", function () {
93 | it("should be added to queue", function() {
94 | gameanalytics.GameAnalytics.addBusinessEvent("USD", 100, "itemType", "itemId", "shop");
95 | expect(countPatternFoundInBlocks("addBusinessEvent")).toEqual(1);
96 | GameAnalytics("addBusinessEvent", "USD", 100, "itemType", "itemId", "shop");
97 | expect(countPatternFoundInBlocks("addBusinessEvent")).toEqual(2);
98 | });
99 | });
100 |
101 | describe("addResourceEvent", function () {
102 | it("should be added to queue", function() {
103 | gameanalytics.GameAnalytics.addResourceEvent(gameanalytics.EGAResourceFlowType.Sink, "gems", 100, "guns", "bigGun");
104 | expect(countPatternFoundInBlocks("addResourceEvent")).toEqual(1);
105 | GameAnalytics("addResourceEvent", "Sink", "gems", 100, "guns", "bigGun");
106 | expect(countPatternFoundInBlocks("addResourceEvent")).toEqual(2);
107 | });
108 | });
109 |
110 | describe("addProgressionEvent", function () {
111 | it("should be added to queue", function() {
112 | gameanalytics.GameAnalytics.addProgressionEvent(gameanalytics.EGAProgressionStatus.Start, "world1", "level1", "phase1", 1000);
113 | expect(countPatternFoundInBlocks("addProgressionEvent")).toEqual(1);
114 | GameAnalytics("addProgressionEvent", "Start", "world1", "level1", "phase1", 1000);
115 | expect(countPatternFoundInBlocks("addProgressionEvent")).toEqual(2);
116 | });
117 | });
118 |
119 | describe("addDesignEvent", function () {
120 | it("should be added to queue", function() {
121 | gameanalytics.GameAnalytics.addDesignEvent("eventId:string");
122 | expect(countPatternFoundInBlocks("addDesignEvent")).toEqual(1);
123 | GameAnalytics("addDesignEvent", "eventId:string");
124 | expect(countPatternFoundInBlocks("addDesignEvent")).toEqual(2);
125 | });
126 | });
127 |
128 | describe("addErrorEvent", function () {
129 | it("should be added to queue", function() {
130 | gameanalytics.GameAnalytics.addErrorEvent(gameanalytics.EGAErrorSeverity.Info, "test");
131 | expect(countPatternFoundInBlocks("addErrorEvent")).toEqual(1);
132 | GameAnalytics("addErrorEvent", "Info", "test");
133 | expect(countPatternFoundInBlocks("addErrorEvent")).toEqual(2);
134 | });
135 | });
136 |
137 | describe("setEnabledInfoLog", function () {
138 | it("should be added to queue", function() {
139 | gameanalytics.GameAnalytics.setEnabledInfoLog(true);
140 | expect(countPatternFoundInBlocks("setInfoLog")).toEqual(1);
141 | GameAnalytics("setEnabledInfoLog", false);
142 | expect(countPatternFoundInBlocks("setInfoLog")).toEqual(2);
143 | });
144 | });
145 |
146 | describe("setEnabledVerboseLog", function () {
147 | it("should be added to queue", function() {
148 | gameanalytics.GameAnalytics.setEnabledVerboseLog(true);
149 | expect(countPatternFoundInBlocks("setVerboseLog")).toEqual(1);
150 | GameAnalytics("setEnabledVerboseLog", false);
151 | expect(countPatternFoundInBlocks("setVerboseLog")).toEqual(2);
152 | });
153 | });
154 |
155 | describe("setEnabledManualSessionHandling", function () {
156 | it("should be added to queue", function() {
157 | gameanalytics.GameAnalytics.setEnabledManualSessionHandling(true);
158 | expect(countPatternFoundInBlocks("setManualSessionHandling")).toEqual(1);
159 | GameAnalytics("setEnabledManualSessionHandling", false);
160 | expect(countPatternFoundInBlocks("setManualSessionHandling")).toEqual(2);
161 | });
162 | });
163 |
164 | describe("setCustomDimension01", function () {
165 | it("should be added to queue", function() {
166 | gameanalytics.GameAnalytics.setCustomDimension01("ninja");
167 | expect(countPatternFoundInBlocks("setCustomDimension01")).toEqual(1);
168 | GameAnalytics("setCustomDimension01", "ninja");
169 | expect(countPatternFoundInBlocks("setCustomDimension01")).toEqual(2);
170 | });
171 | });
172 |
173 | describe("setCustomDimension02", function () {
174 | it("should be added to queue", function() {
175 | gameanalytics.GameAnalytics.setCustomDimension02("ninja");
176 | expect(countPatternFoundInBlocks("setCustomDimension02")).toEqual(1);
177 | GameAnalytics("setCustomDimension02", "ninja");
178 | expect(countPatternFoundInBlocks("setCustomDimension02")).toEqual(2);
179 | });
180 | });
181 |
182 | describe("setCustomDimension03", function () {
183 | it("should be added to queue", function() {
184 | gameanalytics.GameAnalytics.setCustomDimension03("ninja");
185 | expect(countPatternFoundInBlocks("setCustomDimension03")).toEqual(1);
186 | GameAnalytics("setCustomDimension03", "ninja");
187 | expect(countPatternFoundInBlocks("setCustomDimension03")).toEqual(2);
188 | });
189 | });
190 | });
191 |
--------------------------------------------------------------------------------
/test/testGAState.js:
--------------------------------------------------------------------------------
1 | describe("GAState", function () {
2 | describe("ValidateAndCleanCustomFields", function () {
3 | var GAState = gameanalytics.state.GAState;
4 |
5 | var map = {};
6 |
7 | it("should be valid custom fields", function() {
8 | map = {};
9 | for(var i = 0; i < 100; ++i)
10 | {
11 | map[getRandomString(4)] = getRandomString(4);
12 | }
13 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(50);
14 |
15 | map = {};
16 | for(var i = 0; i < 50; ++i)
17 | {
18 | map[getRandomString(4)] = getRandomString(4);
19 | }
20 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(50);
21 |
22 | map = {};
23 | map[getRandomString(4)] = "";
24 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(0);
25 |
26 | map = {};
27 | map[getRandomString(4)] = getRandomString(257);
28 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(0);
29 |
30 | map = {};
31 | map[""] = getRandomString(4);
32 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(0);
33 |
34 | map = {};
35 | map["___"] = getRandomString(4);
36 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(1);
37 |
38 | map = {};
39 | map["_&_"] = getRandomString(4);
40 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(0);
41 |
42 | map = {};
43 | map[getRandomString(65)] = getRandomString(4);
44 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(0);
45 |
46 | map = {};
47 | map[getRandomString(4)] = 100;
48 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(1);
49 |
50 | map = {};
51 | map[getRandomString(4)] = [100];
52 | map[getRandomString(4)] = true;
53 | expect(Object.keys(GAState.validateAndCleanCustomFields(map)).length).toEqual(0);
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/test/testUtilities.js:
--------------------------------------------------------------------------------
1 | function getRandomString(numberOfCharacters)
2 | {
3 | var letters = "abcdefghijklmfalsepqrstuvwxyzABCDEFGHIJKLMfalsePQRSTUVWXYZ0123456789";
4 |
5 | var result = "";
6 |
7 | for (var i = 0; i < numberOfCharacters; i++)
8 | {
9 | result += letters.charAt(Math.floor(Math.random() * (letters.length - 1)));
10 | }
11 |
12 | return result;
13 | }
14 |
15 | function countPatternFoundInBlocks(pattern)
16 | {
17 | var GAThreading = gameanalytics.threading.GAThreading;
18 | var result = 0;
19 | for (var i = 0; i < GAThreading.instance.blocks._sortedKeys.length; i++)
20 | {
21 | for (var j = 0; j < GAThreading.instance.blocks._subQueues[GAThreading.instance.blocks._sortedKeys[i]].length; j++)
22 | {
23 | if(GAThreading.instance.blocks._subQueues[GAThreading.instance.blocks._sortedKeys[i]][j].block.toString().indexOf(pattern) != -1)
24 | {
25 | result++;
26 | }
27 | }
28 | }
29 |
30 | return result;
31 | }
32 |
--------------------------------------------------------------------------------
/test/testValidator.js:
--------------------------------------------------------------------------------
1 | describe("Validator", function () {
2 | describe("Currency", function () {
3 | var GAValidator = gameanalytics.validators.GAValidator;
4 |
5 | it("should be valid currency", function() {
6 | expect(GAValidator.validateCurrency("USD")).toEqual(true);
7 | expect(GAValidator.validateCurrency("XXX")).toEqual(true);
8 | });
9 |
10 | it("should be invalid currency", function() {
11 | expect(GAValidator.validateCurrency("usd")).toEqual(false);
12 | expect(GAValidator.validateCurrency("US")).toEqual(false);
13 | expect(GAValidator.validateCurrency("KR")).toEqual(false);
14 | expect(GAValidator.validateCurrency("USDOLLARS")).toEqual(false);
15 | expect(GAValidator.validateCurrency("$")).toEqual(false);
16 | expect(GAValidator.validateCurrency("")).toEqual(false);
17 | expect(GAValidator.validateCurrency(null)).toEqual(false);
18 | expect(GAValidator.validateCurrency(undefined)).toEqual(false);
19 | });
20 | });
21 |
22 | describe("ResourceCurrencies", function () {
23 | var GAValidator = gameanalytics.validators.GAValidator;
24 |
25 | it("should be valid resource currency", function() {
26 | expect(GAValidator.validateResourceCurrencies(["gems", "gold"])).toEqual(true);
27 | });
28 |
29 | it("should be invalid resource currency", function() {
30 | expect(GAValidator.validateResourceCurrencies(["", "gold"])).toEqual(false);
31 | expect(GAValidator.validateResourceCurrencies([])).toEqual(false);
32 | expect(GAValidator.validateResourceCurrencies([null])).toEqual(false);
33 | expect(GAValidator.validateResourceCurrencies([undefined, "gold"])).toEqual(false);
34 | });
35 | });
36 |
37 | describe("ResourceItemTypes", function () {
38 | var GAValidator = gameanalytics.validators.GAValidator;
39 |
40 | it("should be valid resource item types", function() {
41 | expect(GAValidator.validateResourceItemTypes(["gems", "gold"])).toEqual(true);
42 | });
43 |
44 | it("should be invalid resource item types", function() {
45 | expect(GAValidator.validateResourceItemTypes(["", "gold"])).toEqual(false);
46 | expect(GAValidator.validateResourceItemTypes([])).toEqual(false);
47 | expect(GAValidator.validateResourceItemTypes([null])).toEqual(false);
48 | expect(GAValidator.validateResourceItemTypes([undefined, "gold"])).toEqual(false);
49 | });
50 | });
51 |
52 | describe("ProgressionEvent", function () {
53 | var GAValidator = gameanalytics.validators.GAValidator;
54 | var EGAProgressionStatus = gameanalytics.EGAProgressionStatus;
55 |
56 | it("should be valid progression event", function() {
57 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", "level_001", "phase_001") == null).toEqual(true);
58 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", "level_001", "") == null).toEqual(true);
59 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", "level_001", null) == null).toEqual(true);
60 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", "", "") == null).toEqual(true);
61 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", undefined, undefined) == null).toEqual(true);
62 | });
63 |
64 | it("should be invalid progression event", function() {
65 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "", "", "") == null).toEqual(false);
66 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, null, null, null) == null).toEqual(false);
67 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, undefined, undefined, undefined) == null).toEqual(false);
68 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", "", "phase_001") == null).toEqual(false);
69 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", null, "phase_001") == null).toEqual(false);
70 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "world_001", undefined, "phase_001") == null).toEqual(false);
71 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "", "level_001", "phase_001") == null).toEqual(false);
72 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, null, "level_001", "phase_001") == null).toEqual(false);
73 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, undefined, "level_001", "phase_001") == null).toEqual(false);
74 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "", "level_001", "") == null).toEqual(false);
75 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, null, "level_001", null) == null).toEqual(false);
76 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, undefined, "level_001", undefined) == null).toEqual(false);
77 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, "", "", "phase_001") == null).toEqual(false);
78 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, null, null, "phase_001") == null).toEqual(false);
79 | expect(GAValidator.validateProgressionEvent(EGAProgressionStatus.Start, undefined, undefined, "phase_001") == null).toEqual(false);
80 | });
81 | });
82 |
83 | describe("BusinessEvent", function () {
84 | var GAValidator = gameanalytics.validators.GAValidator;
85 |
86 | it("should be valid business event", function() {
87 | expect(GAValidator.validateBusinessEvent("USD", 99, "cartType", "itemType", "itemId") == null).toEqual(true);
88 | expect(GAValidator.validateBusinessEvent("USD", 99, "", "itemType", "itemId") == null).toEqual(true);
89 | expect(GAValidator.validateBusinessEvent("USD", 99, null, "itemType", "itemId") == null).toEqual(true);
90 | expect(GAValidator.validateBusinessEvent("USD", 99, undefined, "itemType", "itemId") == null).toEqual(true);
91 | expect(GAValidator.validateBusinessEvent("USD", 0, "cartType", "itemType", "itemId") == null).toEqual(true);
92 | });
93 |
94 | it("should be invalid business event", function() {
95 | expect(GAValidator.validateBusinessEvent("USD", -99, "cartType", "itemType", "itemId") == null).toEqual(false);
96 | expect(GAValidator.validateBusinessEvent("USD", 99, "", "", "itemId") == null).toEqual(false);
97 | expect(GAValidator.validateBusinessEvent("USD", 99, "", null, "itemId") == null).toEqual(false);
98 | expect(GAValidator.validateBusinessEvent("USD", 99, "", undefined, "itemId") == null).toEqual(false);
99 | expect(GAValidator.validateBusinessEvent("USD", 99, "cartType", "itemType", "") == null).toEqual(false);
100 | expect(GAValidator.validateBusinessEvent("USD", 99, "cartType", "itemType", null) == null).toEqual(false);
101 | expect(GAValidator.validateBusinessEvent("USD", 99, "cartType", "itemType", undefined) == null).toEqual(false);
102 | });
103 | });
104 |
105 | describe("ResourceEvent", function () {
106 | var GAValidator = gameanalytics.validators.GAValidator;
107 | var EGAResourceFlowType = gameanalytics.EGAResourceFlowType;
108 | var GAState = gameanalytics.state.GAState;
109 |
110 | GAState.setAvailableResourceCurrencies(["gems", "gold"]);
111 | GAState.setAvailableResourceItemTypes(["guns", "powerups"]);
112 |
113 | it("should be valid resource event", function() {
114 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 100, "guns", "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(true);
115 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gold", 100, "powerups", "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(true);
116 | });
117 |
118 | it("should be invalid resource event", function() {
119 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "iron", 100, "guns", "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
120 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 100, "cows", "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
121 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 0, "guns", "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
122 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", -10, "guns", "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
123 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 10, "guns", "", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
124 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 10, "guns", null, GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
125 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 10, "guns", undefined, GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
126 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 10, "", "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
127 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 10, null, "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
128 | expect(GAValidator.validateResourceEvent(EGAResourceFlowType.Sink, "gems", 10, undefined, "item", GAState.getAvailableResourceCurrencies(), GAState.getAvailableResourceItemTypes()) == null).toEqual(false);
129 | });
130 | });
131 |
132 | describe("DesignEvent", function () {
133 | var GAValidator = gameanalytics.validators.GAValidator;
134 |
135 | it("should be valid design event", function() {
136 | expect(GAValidator.validateDesignEvent("name:name") == null).toEqual(true);
137 | expect(GAValidator.validateDesignEvent("name:name:name") == null).toEqual(true);
138 | expect(GAValidator.validateDesignEvent("name:name:name:name") == null).toEqual(true);
139 | expect(GAValidator.validateDesignEvent("name:name:name:name:name") == null).toEqual(true);
140 | expect(GAValidator.validateDesignEvent("name:name") == null).toEqual(true);
141 | });
142 |
143 | it("should be invalid design event", function() {
144 | expect(GAValidator.validateDesignEvent("") == null).toEqual(false);
145 | expect(GAValidator.validateDesignEvent(null) == null).toEqual(false);
146 | expect(GAValidator.validateDesignEvent(undefined) == null).toEqual(false);
147 | expect(GAValidator.validateDesignEvent("name:name:name:name:name:name") == null).toEqual(false);
148 | });
149 | });
150 |
151 | describe("ErrorEvent", function () {
152 | var GAValidator = gameanalytics.validators.GAValidator;
153 | var EGAErrorSeverity = gameanalytics.EGAErrorSeverity;
154 |
155 | it("should be valid error event", function() {
156 | expect(GAValidator.validateErrorEvent(EGAErrorSeverity.Error, "This is a message") == null).toEqual(true);
157 | expect(GAValidator.validateErrorEvent(EGAErrorSeverity.Error, "") == null).toEqual(true);
158 | expect(GAValidator.validateErrorEvent(EGAErrorSeverity.Error, undefined) == null).toEqual(true);
159 | });
160 |
161 | it("should be invalid error event", function() {
162 | expect(GAValidator.validateErrorEvent(EGAErrorSeverity.Error, getRandomString(8193)) == null).toEqual(false);
163 | });
164 | });
165 |
166 | describe("SdkErrorEvent", function () {
167 | var GAValidator = gameanalytics.validators.GAValidator;
168 | var EGASdkErrorCategory = gameanalytics.events.EGASdkErrorCategory;
169 | var EGASdkErrorArea = gameanalytics.events.EGASdkErrorArea;
170 | var EGASdkErrorAction = gameanalytics.events.EGASdkErrorAction;
171 |
172 | it("should be valid sdk error event", function() {
173 | expect(GAValidator.validateSdkErrorEvent("c6cfc80ff69d1e7316bf1e0c8194eda6", "e0ae4809f70e2fa96916c7060f417ae53895f18d", EGASdkErrorCategory.EventValidation, EGASdkErrorArea.ResourceEvent, EGASdkErrorAction.InvalidFlowType)).toEqual(true);
174 | });
175 |
176 | it("should be invalid sdk error event", function() {
177 | expect(GAValidator.validateSdkErrorEvent("", "e0ae4809f70e2fa96916c7060f417ae53895f18d", EGASdkErrorCategory.EventValidation, EGASdkErrorArea.ResourceEvent, EGASdkErrorAction.InvalidFlowType)).toEqual(false);
178 | });
179 | });
180 |
181 | describe("CustomDimensions", function () {
182 | var GAValidator = gameanalytics.validators.GAValidator;
183 |
184 | it("should be valid custom dimensions", function() {
185 | expect(GAValidator.validateCustomDimensions(["abc", "def", "ghi"])).toEqual(true);
186 | });
187 |
188 | it("should be invalid custom dimensions", function() {
189 | expect(GAValidator.validateCustomDimensions(["abc", "def", "abc", "def", "abc", "def", "abc", "def", "abc", "def", "abc", "def", "abc", "def", "abc", "def", "abc", "def", "abc", "def", "abc", "def"])).toEqual(false);
190 | expect(GAValidator.validateCustomDimensions(["abc", ""])).toEqual(false);
191 | expect(GAValidator.validateCustomDimensions(["abc", null])).toEqual(false);
192 | expect(GAValidator.validateCustomDimensions(["abc", undefined])).toEqual(false);
193 | });
194 | });
195 |
196 | describe("SdkWrapperVersion", function () {
197 | var GAValidator = gameanalytics.validators.GAValidator;
198 |
199 | it("should be valid sdk wrapper version", function() {
200 | expect(GAValidator.validateSdkWrapperVersion("unity 1.2.3")).toEqual(true);
201 | expect(GAValidator.validateSdkWrapperVersion("unity 1233.101.0")).toEqual(true);
202 | expect(GAValidator.validateSdkWrapperVersion("unreal 1.2.3")).toEqual(true);
203 | });
204 |
205 | it("should be invalid sdk wrapper version", function() {
206 | expect(GAValidator.validateSdkWrapperVersion("123")).toEqual(false);
207 | expect(GAValidator.validateSdkWrapperVersion("test 1.2.x")).toEqual(false);
208 | expect(GAValidator.validateSdkWrapperVersion("unkfalsewn 1.5.6")).toEqual(false);
209 | expect(GAValidator.validateSdkWrapperVersion("unity 1.2.3.4")).toEqual(false);
210 | expect(GAValidator.validateSdkWrapperVersion("corona1.2.3")).toEqual(false);
211 | expect(GAValidator.validateSdkWrapperVersion("unity x.2.3")).toEqual(false);
212 | expect(GAValidator.validateSdkWrapperVersion("unity 1.x.3")).toEqual(false);
213 | expect(GAValidator.validateSdkWrapperVersion("unity 1.2.x")).toEqual(false);
214 | expect(GAValidator.validateSdkWrapperVersion("unity 1.2.123456")).toEqual(false);
215 | });
216 | });
217 |
218 | describe("Build", function () {
219 | var GAValidator = gameanalytics.validators.GAValidator;
220 |
221 | it("should be valid build", function() {
222 | expect(GAValidator.validateBuild("alpha 1.2.3")).toEqual(true);
223 | expect(GAValidator.validateBuild("ALPHA 1.2.3")).toEqual(true);
224 | expect(GAValidator.validateBuild("TES# sdf.fd3")).toEqual(true);
225 | });
226 |
227 | it("should be invalid build", function() {
228 | expect(GAValidator.validateBuild("")).toEqual(false);
229 | expect(GAValidator.validateBuild(null)).toEqual(false);
230 | expect(GAValidator.validateBuild(undefined)).toEqual(false);
231 | expect(GAValidator.validateBuild(getRandomString(40))).toEqual(false);
232 | });
233 | });
234 |
235 | describe("EngineVersion", function () {
236 | var GAValidator = gameanalytics.validators.GAValidator;
237 |
238 | it("should be valid engine version", function() {
239 | expect(GAValidator.validateEngineVersion("unity 1.2.3")).toEqual(true);
240 | expect(GAValidator.validateEngineVersion("unity 1.2")).toEqual(true);
241 | expect(GAValidator.validateEngineVersion("unity 1")).toEqual(true);
242 | expect(GAValidator.validateEngineVersion("unreal 1.2.3")).toEqual(true);
243 | expect(GAValidator.validateEngineVersion("cocos2d 1.2.3")).toEqual(true);
244 | });
245 |
246 | it("should be invalid engine version", function() {
247 | expect(GAValidator.validateEngineVersion("")).toEqual(false);
248 | expect(GAValidator.validateEngineVersion(null)).toEqual(false);
249 | expect(GAValidator.validateEngineVersion(undefined)).toEqual(false);
250 | expect(GAValidator.validateEngineVersion(getRandomString(40))).toEqual(false);
251 | expect(GAValidator.validateEngineVersion("uni 1.2.3")).toEqual(false);
252 | expect(GAValidator.validateEngineVersion("unity 123456.2.3")).toEqual(false);
253 | expect(GAValidator.validateEngineVersion("unity1.2.3")).toEqual(false);
254 | expect(GAValidator.validateEngineVersion("unity 1.2.3.4")).toEqual(false);
255 | expect(GAValidator.validateEngineVersion("Unity 1.2.3")).toEqual(false);
256 | expect(GAValidator.validateEngineVersion("UNITY 1.2.3")).toEqual(false);
257 | expect(GAValidator.validateEngineVersion("marmalade 1.2.3")).toEqual(false);
258 | expect(GAValidator.validateEngineVersion("xamarin 1.2.3")).toEqual(false);
259 | });
260 | });
261 |
262 | describe("Keys", function () {
263 | var GAValidator = gameanalytics.validators.GAValidator;
264 | var validGameKey = "123456789012345678901234567890ab";
265 | var validSecretKey = "123456789012345678901234567890123456789a";
266 | var tooLongKey = "123456789012345678901234567890123456789abcdefg";
267 |
268 | it("should be valid keys", function() {
269 | expect(GAValidator.validateKeys(validGameKey, validSecretKey)).toEqual(true);
270 | });
271 |
272 | it("should be invalid keys", function() {
273 | expect(GAValidator.validateKeys(validGameKey, "")).toEqual(false);
274 | expect(GAValidator.validateKeys(validGameKey, null)).toEqual(false);
275 | expect(GAValidator.validateKeys(validGameKey, undefined)).toEqual(false);
276 | expect(GAValidator.validateKeys(validGameKey, "123")).toEqual(false);
277 | expect(GAValidator.validateKeys(validGameKey, tooLongKey)).toEqual(false);
278 | expect(GAValidator.validateKeys("", validSecretKey)).toEqual(false);
279 | expect(GAValidator.validateKeys(null, validSecretKey)).toEqual(false);
280 | expect(GAValidator.validateKeys(undefined, validSecretKey)).toEqual(false);
281 | expect(GAValidator.validateKeys("123", validSecretKey)).toEqual(false);
282 | expect(GAValidator.validateKeys(tooLongKey, validSecretKey)).toEqual(false);
283 | });
284 | });
285 |
286 | describe("EventPartLength", function () {
287 | var GAValidator = gameanalytics.validators.GAValidator;
288 |
289 | it("should be valid event part length", function() {
290 | expect(GAValidator.validateEventPartLength("sdfdf", false)).toEqual(true);
291 | expect(GAValidator.validateEventPartLength("", true)).toEqual(true);
292 | expect(GAValidator.validateEventPartLength(null, true)).toEqual(true);
293 | expect(GAValidator.validateEventPartLength(undefined, true)).toEqual(true);
294 | expect(GAValidator.validateEventPartLength(getRandomString(32), true)).toEqual(true);
295 | expect(GAValidator.validateEventPartLength(getRandomString(40), true)).toEqual(true);
296 | expect(GAValidator.validateEventPartLength(getRandomString(40), false)).toEqual(true);
297 | });
298 |
299 | it("should be invalid event part length", function() {
300 | expect(GAValidator.validateEventPartLength(getRandomString(80), false)).toEqual(false);
301 | expect(GAValidator.validateEventPartLength(getRandomString(80), true)).toEqual(false);
302 | expect(GAValidator.validateEventPartLength("", false)).toEqual(false);
303 | expect(GAValidator.validateEventPartLength(null, false)).toEqual(false);
304 | expect(GAValidator.validateEventPartLength(undefined, false)).toEqual(false);
305 | });
306 | });
307 |
308 | describe("EventPartCharacters", function () {
309 | var GAValidator = gameanalytics.validators.GAValidator;
310 |
311 | it("should be valid event part characters", function() {
312 | expect(GAValidator.validateEventPartCharacters("sdfdffdgdfg")).toEqual(true);
313 | });
314 |
315 | it("should be invalid event part characters", function() {
316 | expect(GAValidator.validateEventPartCharacters("øææ")).toEqual(false);
317 | expect(GAValidator.validateEventPartCharacters("")).toEqual(false);
318 | expect(GAValidator.validateEventPartCharacters(null)).toEqual(false);
319 | expect(GAValidator.validateEventPartCharacters(undefined)).toEqual(false);
320 | expect(GAValidator.validateEventPartCharacters("*")).toEqual(false);
321 | expect(GAValidator.validateEventPartCharacters("))&%")).toEqual(false);
322 | });
323 | });
324 |
325 | describe("EventIdLength", function () {
326 | var GAValidator = gameanalytics.validators.GAValidator;
327 |
328 | it("should be valid event id length", function() {
329 | expect(GAValidator.validateEventIdLength(getRandomString(40))).toEqual(true);
330 | expect(GAValidator.validateEventIdLength(getRandomString(32))).toEqual(true);
331 | expect(GAValidator.validateEventIdLength("sdfdf")).toEqual(true);
332 | });
333 |
334 | it("should be invalid event id length", function() {
335 | expect(GAValidator.validateEventIdLength(getRandomString(80))).toEqual(false);
336 | expect(GAValidator.validateEventIdLength("")).toEqual(false);
337 | expect(GAValidator.validateEventIdLength(null)).toEqual(false);
338 | expect(GAValidator.validateEventIdLength(undefined)).toEqual(false);
339 | });
340 | });
341 |
342 | describe("EventIdCharacters", function () {
343 | var GAValidator = gameanalytics.validators.GAValidator;
344 |
345 | it("should be valid event id characters", function() {
346 | expect(GAValidator.validateEventIdCharacters("GHj:df(g?h d_fk7-58.9)3!47")).toEqual(true);
347 | });
348 |
349 | it("should be invalid event id characters", function() {
350 | expect(GAValidator.validateEventIdCharacters("GHj:df(g?h d_fk,7-58.9)3!47")).toEqual(false);
351 | expect(GAValidator.validateEventIdCharacters("")).toEqual(false);
352 | expect(GAValidator.validateEventIdCharacters(null)).toEqual(false);
353 | expect(GAValidator.validateEventIdCharacters(undefined)).toEqual(false);
354 | });
355 | });
356 |
357 | describe("ShortString", function () {
358 | var GAValidator = gameanalytics.validators.GAValidator;
359 |
360 | it("should be valid short string", function() {
361 | expect(GAValidator.validateShortString(getRandomString(32), false)).toEqual(true);
362 | expect(GAValidator.validateShortString(getRandomString(32), true)).toEqual(true);
363 | expect(GAValidator.validateShortString(getRandomString(10), false)).toEqual(true);
364 | expect(GAValidator.validateShortString(getRandomString(10), true)).toEqual(true);
365 | expect(GAValidator.validateShortString("", true)).toEqual(true);
366 | expect(GAValidator.validateShortString(null, true)).toEqual(true);
367 | expect(GAValidator.validateShortString(undefined, true)).toEqual(true);
368 | });
369 |
370 | it("should be invalid short string", function() {
371 | expect(GAValidator.validateShortString(getRandomString(40), false)).toEqual(false);
372 | expect(GAValidator.validateShortString(getRandomString(40), true)).toEqual(false);
373 | expect(GAValidator.validateShortString("", false)).toEqual(false);
374 | expect(GAValidator.validateShortString(null, false)).toEqual(false);
375 | expect(GAValidator.validateShortString(undefined, false)).toEqual(false);
376 | });
377 | });
378 |
379 | describe("String", function () {
380 | var GAValidator = gameanalytics.validators.GAValidator;
381 |
382 | it("should be valid string", function() {
383 | expect(GAValidator.validateString(getRandomString(64), false)).toEqual(true);
384 | expect(GAValidator.validateString(getRandomString(64), true)).toEqual(true);
385 | expect(GAValidator.validateString(getRandomString(10), false)).toEqual(true);
386 | expect(GAValidator.validateString(getRandomString(10), true)).toEqual(true);
387 | expect(GAValidator.validateString("", true)).toEqual(true);
388 | expect(GAValidator.validateString(null, true)).toEqual(true);
389 | expect(GAValidator.validateString(undefined, true)).toEqual(true);
390 | });
391 |
392 | it("should be invalid string", function() {
393 | expect(GAValidator.validateString(getRandomString(80), false)).toEqual(false);
394 | expect(GAValidator.validateString(getRandomString(80), true)).toEqual(false);
395 | expect(GAValidator.validateString("", false)).toEqual(false);
396 | expect(GAValidator.validateString(null, false)).toEqual(false);
397 | expect(GAValidator.validateString(undefined, false)).toEqual(false);
398 | });
399 | });
400 |
401 | describe("String", function () {
402 | var GAValidator = gameanalytics.validators.GAValidator;
403 |
404 | it("should be valid string", function() {
405 | expect(GAValidator.validateString(getRandomString(64), false)).toEqual(true);
406 | expect(GAValidator.validateString(getRandomString(64), true)).toEqual(true);
407 | expect(GAValidator.validateString(getRandomString(10), false)).toEqual(true);
408 | expect(GAValidator.validateString(getRandomString(10), true)).toEqual(true);
409 | expect(GAValidator.validateString("", true)).toEqual(true);
410 | expect(GAValidator.validateString(null, true)).toEqual(true);
411 | expect(GAValidator.validateString(undefined, true)).toEqual(true);
412 | });
413 |
414 | it("should be invalid string", function() {
415 | expect(GAValidator.validateString(getRandomString(80), false)).toEqual(false);
416 | expect(GAValidator.validateString(getRandomString(80), true)).toEqual(false);
417 | expect(GAValidator.validateString("", false)).toEqual(false);
418 | expect(GAValidator.validateString(null, false)).toEqual(false);
419 | expect(GAValidator.validateString(undefined, false)).toEqual(false);
420 | });
421 | });
422 |
423 | describe("LongString", function () {
424 | var GAValidator = gameanalytics.validators.GAValidator;
425 |
426 | it("should be valid long string", function() {
427 | expect(GAValidator.validateLongString(getRandomString(8192), false)).toEqual(true);
428 | expect(GAValidator.validateLongString(getRandomString(8192), true)).toEqual(true);
429 | expect(GAValidator.validateLongString(getRandomString(10), false)).toEqual(true);
430 | expect(GAValidator.validateLongString(getRandomString(10), true)).toEqual(true);
431 | expect(GAValidator.validateLongString("", true)).toEqual(true);
432 | expect(GAValidator.validateLongString(null, true)).toEqual(true);
433 | expect(GAValidator.validateLongString(undefined, true)).toEqual(true);
434 | });
435 |
436 | it("should be invalid long string", function() {
437 | expect(GAValidator.validateLongString(getRandomString(8193), false)).toEqual(false);
438 | expect(GAValidator.validateLongString(getRandomString(8193), true)).toEqual(false);
439 | expect(GAValidator.validateLongString("", false)).toEqual(false);
440 | expect(GAValidator.validateLongString(null, false)).toEqual(false);
441 | expect(GAValidator.validateLongString(undefined, false)).toEqual(false);
442 | });
443 | });
444 |
445 | describe("ArrayOfStrings", function () {
446 | var GAValidator = gameanalytics.validators.GAValidator;
447 |
448 | it("should be valid array of strings", function() {
449 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", [getRandomString(3), getRandomString(10), getRandomString(7)])).toEqual(true);
450 | expect(GAValidator.validateArrayOfStrings(3, 10, true, "test", [])).toEqual(true);
451 | });
452 |
453 | it("should be invalid array of strings", function() {
454 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", [getRandomString(3), getRandomString(12), getRandomString(7)])).toEqual(false);
455 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", [getRandomString(3), "", getRandomString(7)])).toEqual(false);
456 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", [getRandomString(3), null, getRandomString(7)])).toEqual(false);
457 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", [getRandomString(3), undefined, getRandomString(7)])).toEqual(false);
458 | expect(GAValidator.validateArrayOfStrings(2, 10, false, "test", [getRandomString(3), getRandomString(10), getRandomString(7)])).toEqual(false);
459 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", [])).toEqual(false);
460 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", null)).toEqual(false);
461 | expect(GAValidator.validateArrayOfStrings(3, 10, false, "test", undefined)).toEqual(false);
462 | });
463 | });
464 |
465 | describe("ClientTs", function () {
466 | var GAValidator = gameanalytics.validators.GAValidator;
467 | var GAUtilities = gameanalytics.utilities.GAUtilities;
468 |
469 | it("should be valid client ts", function() {
470 | expect(GAValidator.validateClientTs(GAUtilities.timeIntervalSince1970())).toEqual(true);
471 | expect(GAValidator.validateClientTs(4294967295)).toEqual(true);
472 | });
473 |
474 | it("should be invalid client ts", function() {
475 | expect(GAValidator.validateClientTs(-4294967295)).toEqual(false);
476 | });
477 | });
478 |
479 | describe("UserId", function () {
480 | var GAValidator = gameanalytics.validators.GAValidator;
481 |
482 | it("should be valid user id", function() {
483 | expect(GAValidator.validateUserId("fhjkdfghdfjkgh")).toEqual(true);
484 | });
485 |
486 | it("should be invalid user id", function() {
487 | expect(GAValidator.validateUserId("")).toEqual(false);
488 | expect(GAValidator.validateUserId(null)).toEqual(false);
489 | expect(GAValidator.validateUserId(undefined)).toEqual(false);
490 | });
491 | });
492 | });
493 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": true,
3 | "compilerOptions": {
4 | "sourceMap": true,
5 | "noImplicitAny": false,
6 | "removeComments": true,
7 | "noEmit": false,
8 | "outDir": "./dist",
9 | "declaration": true,
10 | "outFile": "./dist/GameAnalytics.js"
11 | },
12 | "files": [
13 | "./src/Enums.ts",
14 | "./src/PublicEnums.ts",
15 | "./src/logging/GALogger.ts",
16 | "./src/utilities/GAUtilities.ts",
17 | "./src/validators/GAValidator.ts",
18 | "./src/device/GADevice.ts",
19 | "./src/threading/TimedBlock.ts",
20 | "./src/threading/PriorityQueue.ts",
21 | "./src/store/GAStore.ts",
22 | "./src/state/GAState.ts",
23 | "./src/tasks/SdkErrorTask.ts",
24 | "./src/http/GAHTTPApi.ts",
25 | "./src/events/GAEvents.ts",
26 | "./src/threading/GAThreading.ts",
27 | "./src/GameAnalytics.ts"
28 | ],
29 | "include": [
30 | "./vendor/*.d.ts",
31 | "./src/ga.d.ts"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/vendor/bundle.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | CryptoJS v3.1.2
3 | code.google.com/p/crypto-js
4 | (c) 2009-2013 by Jeff Mott. All rights reserved.
5 | code.google.com/p/crypto-js/wiki/License
6 | */
7 | var CryptoJS=CryptoJS||function(h,s){var f={},g=f.lib={},q=function(){},m=g.Base={extend:function(a){q.prototype=this;var c=new q;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
8 | r=g.WordArray=m.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=s?c:4*a.length},toString:function(a){return(a||k).stringify(this)},concat:function(a){var c=this.words,d=a.words,b=this.sigBytes;a=a.sigBytes;this.clamp();if(b%4)for(var e=0;e>>2]|=(d[e>>>2]>>>24-8*(e%4)&255)<<24-8*((b+e)%4);else if(65535>>2]=d[e>>>2];else c.push.apply(c,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
9 | 32-8*(c%4);a.length=h.ceil(c/4)},clone:function(){var a=m.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],d=0;d>>2]>>>24-8*(b%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>3]|=parseInt(a.substr(b,
10 | 2),16)<<24-4*(b%8);return new r.init(d,c/2)}},n=l.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var d=[],b=0;b>>2]>>>24-8*(b%4)&255));return d.join("")},parse:function(a){for(var c=a.length,d=[],b=0;b>>2]|=(a.charCodeAt(b)&255)<<24-8*(b%4);return new r.init(d,c)}},j=l.Utf8={stringify:function(a){try{return decodeURIComponent(escape(n.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return n.parse(unescape(encodeURIComponent(a)))}},
11 | u=g.BufferedBlockAlgorithm=m.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=j.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,d=c.words,b=c.sigBytes,e=this.blockSize,f=b/(4*e),f=a?h.ceil(f):h.max((f|0)-this._minBufferSize,0);a=f*e;b=h.min(4*a,b);if(a){for(var g=0;gn;){var j;a:{j=k;for(var u=h.sqrt(j),t=2;t<=u;t++)if(!(j%t)){j=!1;break a}j=!0}j&&(8>n&&(m[n]=l(h.pow(k,0.5))),r[n]=l(h.pow(k,1/3)),n++);k++}var a=[],f=f.SHA256=q.extend({_doReset:function(){this._hash=new g.init(m.slice(0))},_doProcessBlock:function(c,d){for(var b=this._hash.words,e=b[0],f=b[1],g=b[2],j=b[3],h=b[4],m=b[5],n=b[6],q=b[7],p=0;64>p;p++){if(16>p)a[p]=
15 | c[d+p]|0;else{var k=a[p-15],l=a[p-2];a[p]=((k<<25|k>>>7)^(k<<14|k>>>18)^k>>>3)+a[p-7]+((l<<15|l>>>17)^(l<<13|l>>>19)^l>>>10)+a[p-16]}k=q+((h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25))+(h&m^~h&n)+r[p]+a[p];l=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);q=n;n=m;m=h;h=j+k|0;j=g;g=f;f=e;e=k+l|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+g|0;b[3]=b[3]+j|0;b[4]=b[4]+h|0;b[5]=b[5]+m|0;b[6]=b[6]+n|0;b[7]=b[7]+q|0},_doFinalize:function(){var a=this._data,d=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;
16 | d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=h.floor(b/4294967296);d[(e+64>>>9<<4)+15]=b;a.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var a=q.clone.call(this);a._hash=this._hash.clone();return a}});s.SHA256=q._createHelper(f);s.HmacSHA256=q._createHmacHelper(f)})(Math);
17 | (function(){var h=CryptoJS,s=h.enc.Utf8;h.algo.HMAC=h.lib.Base.extend({init:function(f,g){f=this._hasher=new f.init;"string"==typeof g&&(g=s.parse(g));var h=f.blockSize,m=4*h;g.sigBytes>m&&(g=f.finalize(g));g.clamp();for(var r=this._oKey=g.clone(),l=this._iKey=g.clone(),k=r.words,n=l.words,j=0;j>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d<
27 | e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
28 |
--------------------------------------------------------------------------------
/vendor/cryptojs.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for CryptoJS 3.1.2
2 | // Project: https://code.google.com/p/crypto-js/
3 | // Definitions by: Gia Bảo @ Sân Đình
4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped
5 |
6 | declare var CryptoJS: CryptoJS.CryptoJSStatic;
7 |
8 | declare module CryptoJS{
9 | module lib{
10 | interface Base{
11 | extend(overrides: Object): Object
12 | init(...args: any[]): void
13 | //arguments of create() is same as init(). This is true for all subclasses
14 | create(...args: any[]): Base
15 | mixIn(properties: Object): void
16 | clone(): Base
17 | }
18 |
19 | interface WordArray extends Base{
20 | words: number[]
21 | sigBytes: number
22 | init(words?: number[], sigBytes?: number): void
23 | create(words?: number[], sigBytes?: number): WordArray
24 |
25 | init(typedArray: ArrayBuffer): void
26 | init(typedArray: Int8Array): void
27 |
28 | //Because TypeScript uses a structural type system then we don't need (& can't)
29 | //declare oveload function init, create for the following type (same as Int8Array):
30 | //then Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array
31 | //Note also: Uint8ClampedArray is not defined in lib.d.ts & not supported in IE
32 | //@see http://compatibility.shwups-cms.ch/en/home?&property=Uint8ClampedArray
33 |
34 | create(typedArray: ArrayBuffer): WordArray
35 | create(typedArray: Int8Array): WordArray
36 |
37 | toString(encoder?: enc.IEncoder): string
38 | concat(wordArray: WordArray): WordArray
39 | clamp(): void
40 | clone(): WordArray
41 | random(nBytes: number): WordArray
42 | }
43 |
44 | interface BufferedBlockAlgorithm extends Base{
45 | reset(): void
46 | clone(): BufferedBlockAlgorithm
47 | }
48 |
49 | //tparam C - Configuration type
50 | interface IHasher extends BufferedBlockAlgorithm{
51 | cfg: C
52 | init(cfg?: C): void
53 | create(cfg?: C): IHasher
54 |
55 | update(messageUpdate: WordArray): Hasher
56 | update(messageUpdate: string): Hasher
57 |
58 | finalize(messageUpdate?: WordArray): WordArray
59 | finalize(messageUpdate?: string): WordArray
60 |
61 | blockSize: number
62 |
63 | _createHelper(hasher: Hasher): IHasherHelper
64 | _createHmacHelper(hasher: Hasher): IHasherHmacHelper
65 |
66 | clone(): IHasher
67 | }
68 | interface Hasher extends IHasher