@zoho_apptics/apptics-js-sdk 2.0.0-beta → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +111 -5
- package/README.md +399 -300
- package/dist/apptics.esm.min.js +2 -2
- package/dist/apptics.esm.min.js.map +1 -1
- package/dist/apptics.min.js +2 -2
- package/dist/apptics.min.js.map +1 -1
- package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
- package/dist/cjs/auto-init.d.ts +3 -3
- package/dist/cjs/auto-init.js +1 -1
- package/dist/cjs/core/attempts.js +1 -1
- package/dist/cjs/core/batch.js +2 -1
- package/dist/cjs/core/callsite.d.ts +6 -0
- package/dist/cjs/core/callsite.js +25 -3
- package/dist/cjs/core/consent.d.ts +5 -1
- package/dist/cjs/core/consent.js +8 -6
- package/dist/cjs/core/constants.d.ts +35 -10
- package/dist/cjs/core/constants.js +31 -15
- package/dist/cjs/core/db.d.ts +12 -4
- package/dist/cjs/core/db.js +167 -34
- package/dist/cjs/core/debug.js +1 -1
- package/dist/cjs/core/env.js +1 -1
- package/dist/cjs/core/framework-id.d.ts +1 -1
- package/dist/cjs/core/framework-id.js +3 -3
- package/dist/cjs/core/guard.d.ts +9 -0
- package/dist/cjs/core/guard.js +27 -13
- package/dist/cjs/core/hooks.d.ts +2 -4
- package/dist/cjs/core/hooks.js +2 -3
- package/dist/cjs/core/locks.d.ts +9 -0
- package/dist/cjs/core/locks.js +22 -0
- package/dist/cjs/core/logger.d.ts +3 -4
- package/dist/cjs/core/logger.js +4 -40
- package/dist/cjs/core/state.d.ts +3 -2
- package/dist/cjs/core/state.js +2 -2
- package/dist/cjs/core/storage.d.ts +20 -5
- package/dist/cjs/core/storage.js +115 -17
- package/dist/cjs/core/stylesheet.d.ts +8 -0
- package/dist/cjs/core/stylesheet.js +29 -0
- package/dist/cjs/core/transport.d.ts +2 -0
- package/dist/cjs/core/transport.js +19 -4
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +2 -2
- package/dist/cjs/device/appversion.d.ts +1 -6
- package/dist/cjs/device/appversion.js +13 -1
- package/dist/cjs/device/index.d.ts +11 -0
- package/dist/cjs/device/index.js +120 -20
- package/dist/cjs/engagement/api-rules.d.ts +32 -67
- package/dist/cjs/engagement/api-rules.js +144 -157
- package/dist/cjs/engagement/auto-api.js +2 -2
- package/dist/cjs/engagement/auto-screen.d.ts +2 -0
- package/dist/cjs/engagement/auto-screen.js +67 -3
- package/dist/cjs/engagement/defined-events.js +1 -1
- package/dist/cjs/engagement/send.js +6 -1
- package/dist/cjs/engagement/session.d.ts +12 -3
- package/dist/cjs/engagement/session.js +161 -25
- package/dist/cjs/engagement/track.js +45 -20
- package/dist/cjs/errors/send.js +1 -1
- package/dist/cjs/errors/track.d.ts +1 -1
- package/dist/cjs/errors/track.js +46 -6
- package/dist/cjs/feedback/controls.d.ts +6 -0
- package/dist/cjs/feedback/controls.js +31 -1
- package/dist/cjs/feedback/index.d.ts +8 -7
- package/dist/cjs/feedback/index.js +32 -13
- package/dist/cjs/feedback/logs.d.ts +10 -5
- package/dist/cjs/feedback/logs.js +52 -13
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +3 -0
- package/dist/cjs/feedback/send.js +28 -3
- package/dist/cjs/feedback/styles.d.ts +10 -0
- package/dist/cjs/feedback/styles.js +8 -0
- package/dist/cjs/feedback/template.d.ts +1 -1
- package/dist/cjs/feedback/template.js +12 -12
- package/dist/cjs/feedback/ui.d.ts +14 -6
- package/dist/cjs/feedback/ui.js +126 -48
- package/dist/cjs/heatmap/send.js +1 -1
- package/dist/cjs/heatmap/track.d.ts +1 -1
- package/dist/cjs/heatmap/track.js +87 -16
- package/dist/cjs/index.d.ts +17 -63
- package/dist/cjs/index.js +104 -92
- package/dist/cjs/lifecycle.d.ts +8 -1
- package/dist/cjs/lifecycle.js +258 -62
- package/dist/cjs/remoteconfig/country.d.ts +4 -0
- package/dist/cjs/remoteconfig/country.js +73 -0
- package/dist/cjs/remoteconfig/custom.d.ts +7 -7
- package/dist/cjs/remoteconfig/custom.js +31 -51
- package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
- package/dist/cjs/remoteconfig/evaluate.js +56 -43
- package/dist/cjs/remoteconfig/fetch.d.ts +3 -3
- package/dist/cjs/remoteconfig/fetch.js +9 -23
- package/dist/cjs/remoteconfig/index.d.ts +10 -8
- package/dist/cjs/remoteconfig/index.js +65 -39
- package/dist/cjs/remoteconfig/store.js +1 -1
- package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/cjs/remoteconfig/tz-countries.js +13 -0
- package/dist/cjs/remotelog/gate.d.ts +2 -11
- package/dist/cjs/remotelog/gate.js +9 -35
- package/dist/cjs/remotelog/index.d.ts +4 -21
- package/dist/cjs/remotelog/logger.d.ts +11 -17
- package/dist/cjs/remotelog/logger.js +30 -58
- package/dist/cjs/remotelog/send.js +10 -10
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.js +2 -2
- package/dist/cjs/types.d.ts +155 -82
- package/dist/cjs/user/index.js +11 -6
- package/dist/esm/auto-init.d.ts +3 -3
- package/dist/esm/auto-init.js +1 -1
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +2 -1
- package/dist/esm/core/callsite.d.ts +6 -0
- package/dist/esm/core/callsite.js +25 -4
- package/dist/esm/core/consent.d.ts +5 -1
- package/dist/esm/core/consent.js +9 -7
- package/dist/esm/core/constants.d.ts +35 -10
- package/dist/esm/core/constants.js +30 -15
- package/dist/esm/core/db.d.ts +12 -4
- package/dist/esm/core/db.js +164 -35
- package/dist/esm/core/debug.js +1 -1
- package/dist/esm/core/env.js +1 -1
- package/dist/esm/core/framework-id.d.ts +1 -1
- package/dist/esm/core/framework-id.js +3 -3
- package/dist/esm/core/guard.d.ts +9 -0
- package/dist/esm/core/guard.js +27 -14
- package/dist/esm/core/hooks.d.ts +2 -4
- package/dist/esm/core/hooks.js +2 -3
- package/dist/esm/core/locks.d.ts +9 -0
- package/dist/esm/core/locks.js +20 -0
- package/dist/esm/core/logger.d.ts +3 -4
- package/dist/esm/core/logger.js +4 -40
- package/dist/esm/core/state.d.ts +3 -2
- package/dist/esm/core/state.js +2 -2
- package/dist/esm/core/storage.d.ts +20 -5
- package/dist/esm/core/storage.js +113 -18
- package/dist/esm/core/stylesheet.d.ts +8 -0
- package/dist/esm/core/stylesheet.js +27 -0
- package/dist/esm/core/transport.d.ts +2 -0
- package/dist/esm/core/transport.js +19 -5
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +2 -2
- package/dist/esm/device/appversion.d.ts +1 -6
- package/dist/esm/device/appversion.js +13 -2
- package/dist/esm/device/index.d.ts +11 -0
- package/dist/esm/device/index.js +123 -24
- package/dist/esm/engagement/api-rules.d.ts +32 -67
- package/dist/esm/engagement/api-rules.js +143 -155
- package/dist/esm/engagement/auto-api.js +3 -3
- package/dist/esm/engagement/auto-screen.d.ts +2 -0
- package/dist/esm/engagement/auto-screen.js +67 -4
- package/dist/esm/engagement/defined-events.js +1 -1
- package/dist/esm/engagement/send.js +6 -1
- package/dist/esm/engagement/session.d.ts +12 -3
- package/dist/esm/engagement/session.js +162 -29
- package/dist/esm/engagement/track.js +52 -27
- package/dist/esm/errors/send.js +1 -1
- package/dist/esm/errors/track.d.ts +1 -1
- package/dist/esm/errors/track.js +46 -6
- package/dist/esm/feedback/controls.d.ts +6 -0
- package/dist/esm/feedback/controls.js +31 -2
- package/dist/esm/feedback/index.d.ts +8 -7
- package/dist/esm/feedback/index.js +34 -13
- package/dist/esm/feedback/logs.d.ts +10 -5
- package/dist/esm/feedback/logs.js +54 -15
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +3 -0
- package/dist/esm/feedback/send.js +28 -5
- package/dist/esm/feedback/styles.d.ts +10 -0
- package/dist/esm/feedback/styles.js +4 -0
- package/dist/esm/feedback/template.d.ts +1 -1
- package/dist/esm/feedback/template.js +12 -12
- package/dist/esm/feedback/ui.d.ts +14 -6
- package/dist/esm/feedback/ui.js +126 -49
- package/dist/esm/heatmap/send.js +1 -1
- package/dist/esm/heatmap/track.d.ts +1 -1
- package/dist/esm/heatmap/track.js +87 -16
- package/dist/esm/index.d.ts +17 -63
- package/dist/esm/index.js +108 -80
- package/dist/esm/lifecycle.d.ts +8 -1
- package/dist/esm/lifecycle.js +266 -71
- package/dist/esm/remoteconfig/country.d.ts +4 -0
- package/dist/esm/remoteconfig/country.js +70 -0
- package/dist/esm/remoteconfig/custom.d.ts +7 -7
- package/dist/esm/remoteconfig/custom.js +32 -52
- package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
- package/dist/esm/remoteconfig/evaluate.js +57 -44
- package/dist/esm/remoteconfig/fetch.d.ts +3 -3
- package/dist/esm/remoteconfig/fetch.js +9 -23
- package/dist/esm/remoteconfig/index.d.ts +10 -8
- package/dist/esm/remoteconfig/index.js +66 -40
- package/dist/esm/remoteconfig/store.js +1 -1
- package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/esm/remoteconfig/tz-countries.js +11 -0
- package/dist/esm/remotelog/gate.d.ts +2 -11
- package/dist/esm/remotelog/gate.js +10 -32
- package/dist/esm/remotelog/index.d.ts +4 -21
- package/dist/esm/remotelog/logger.d.ts +11 -17
- package/dist/esm/remotelog/logger.js +32 -60
- package/dist/esm/remotelog/send.js +12 -12
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.js +2 -2
- package/dist/esm/types.d.ts +155 -82
- package/dist/esm/user/index.js +12 -6
- package/dist/feedback.esm.min.js +2 -2
- package/dist/feedback.esm.min.js.map +1 -1
- package/package.json +14 -16
- package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
- package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
- package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
- package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
- package/dist/cjs/heatmap/index.d.ts +0 -16
- package/dist/cjs/heatmap/index.js +0 -22
- package/dist/cjs/remotelog/index.js +0 -55
- package/dist/esm/heatmap/index.d.ts +0 -16
- package/dist/esm/heatmap/index.js +0 -17
- package/dist/esm/remotelog/index.js +0 -51
- package/dist/heatmap.esm.min.js +0 -3
- package/dist/heatmap.esm.min.js.map +0 -1
- package/dist/style.css +0 -1
|
@@ -1,74 +1,54 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
-
import {
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { storageReady } from '../core/storage.js';
|
|
3
3
|
import { Console } from '../core/logger.js';
|
|
4
4
|
import { reresolve } from './store.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Custom condition values
|
|
8
|
-
* what this device is for
|
|
9
|
-
* values), and a condition on that key matches when the value set here is one of the listed ones.
|
|
7
|
+
* Custom condition values: a console condition on the custom criteria (`custom_sdk_value`) is
|
|
8
|
+
* answered by what the app says this device is for that condition, by the condition's name:
|
|
10
9
|
*
|
|
11
|
-
* AppticsRemoteConfig.setCustomConditionValue('
|
|
10
|
+
* AppticsRemoteConfig.setCustomConditionValue('is_custom', 'custom_color');
|
|
11
|
+
* AppticsRemoteConfig.setCustomConditionValue('is_custom', 'gold,platinum'); // several
|
|
12
12
|
*
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
* storage has loaded — before init(), say — is kept in memory and saved once it has.
|
|
16
|
-
*
|
|
17
|
-
* Assumed wire format, until the server confirms it: a custom criteria arrives with the custom
|
|
18
|
-
* key as its `key` and operator 1 ("is one of"), the same shape as the built-in criteria. A
|
|
19
|
-
* different shape fails closed — the condition does not match and the default is used.
|
|
13
|
+
* The condition matches when one of the comma separated values is one of the values it lists.
|
|
14
|
+
* Values are for this page: an app sets them as it starts, as it would any other app state.
|
|
20
15
|
*/
|
|
21
|
-
const STORE_KEY = 'remoteconfig_custom'; // no i18n
|
|
22
|
-
/** This page's values; null marks a key removed here, so a stored value no longer counts. */
|
|
23
16
|
var mine = {};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/** The value set for a custom criteria key, or undefined when none is. */
|
|
29
|
-
function customConditionValue(key) {
|
|
30
|
-
if (Object.prototype.hasOwnProperty.call(mine, key)) {
|
|
31
|
-
var here = mine[key];
|
|
32
|
-
return here === null ? undefined : here;
|
|
33
|
-
}
|
|
34
|
-
var saved = stored();
|
|
35
|
-
return Object.prototype.hasOwnProperty.call(saved, key) ? saved[key] : undefined;
|
|
17
|
+
/** The values set for a condition, by its name; empty when none are. */
|
|
18
|
+
function customConditionValues(conditionname) {
|
|
19
|
+
var name = String(conditionname || '').trim(); // no i18n
|
|
20
|
+
return Object.prototype.hasOwnProperty.call(mine, name) ? mine[name] : [];
|
|
36
21
|
}
|
|
37
22
|
/**
|
|
38
|
-
* Sets what this device is for a custom
|
|
39
|
-
* the stored rules
|
|
40
|
-
* fetch.
|
|
23
|
+
* Sets what this device is for a custom condition, by its name, as comma separated values; an
|
|
24
|
+
* empty value removes it. Re-applies the stored rules once storage is ready, so the next read sees
|
|
25
|
+
* the new result without a fetch.
|
|
41
26
|
*/
|
|
42
|
-
function setCustomConditionValue(
|
|
43
|
-
var name = typeof
|
|
27
|
+
function setCustomConditionValue(conditionname, value) {
|
|
28
|
+
var name = typeof conditionname === 'string' ? conditionname.trim() : ''; // no i18n
|
|
44
29
|
if (name === '') { // no i18n
|
|
45
|
-
Console.warn('AppticsRemoteConfig.setCustomConditionValue: a condition
|
|
30
|
+
Console.warn('AppticsRemoteConfig.setCustomConditionValue: a condition name is required'); // no i18n
|
|
46
31
|
return;
|
|
47
32
|
}
|
|
48
|
-
var
|
|
49
|
-
|
|
33
|
+
var values = (value === null || value === undefined ? '' : String(value)).split(',') // no i18n
|
|
34
|
+
.map(function (one) { return one.trim(); })
|
|
35
|
+
.filter(function (one, i, all) { return one !== '' && all.indexOf(one) === i; }); // no i18n
|
|
36
|
+
if (values.length === 0) {
|
|
37
|
+
delete mine[name];
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
mine[name] = values;
|
|
41
|
+
}
|
|
50
42
|
storageReady().then(function (backend) {
|
|
51
|
-
if (!backend) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
var saved = stored();
|
|
55
|
-
Object.keys(mine).forEach(function (one) {
|
|
56
|
-
var here = mine[one];
|
|
57
|
-
if (here === null || here === undefined) {
|
|
58
|
-
delete saved[one];
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
saved[one] = here;
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
user_defaults.add(STORE_KEY, saved);
|
|
65
43
|
// only once storage has loaded: re-resolving before it would read an empty rule set and keep it
|
|
66
|
-
|
|
44
|
+
if (backend) {
|
|
45
|
+
reresolve();
|
|
46
|
+
}
|
|
67
47
|
});
|
|
68
48
|
}
|
|
69
|
-
/** reset(): forgets the values set on this page
|
|
49
|
+
/** reset(): forgets the values set on this page. */
|
|
70
50
|
function clearCustomConditionValues() {
|
|
71
51
|
mine = {};
|
|
72
52
|
}
|
|
73
53
|
|
|
74
|
-
export { clearCustomConditionValues,
|
|
54
|
+
export { clearCustomConditionValues, customConditionValues, setCustomConditionValue };
|
|
@@ -10,10 +10,10 @@ export declare function conditionMatches(condition: Dict): boolean;
|
|
|
10
10
|
/**
|
|
11
11
|
* The value of one parameter on this device, as the string the server stores it as.
|
|
12
12
|
*
|
|
13
|
-
* `
|
|
14
|
-
*
|
|
13
|
+
* `conditions` is `all_condition` as the server sent it: its order is the order they are tried
|
|
14
|
+
* in. The parameter's own `conditions` say which of them apply to it, and with what value.
|
|
15
15
|
*/
|
|
16
|
-
export declare function resolve(parameter: Dict,
|
|
16
|
+
export declare function resolve(parameter: Dict, conditions: Dict[]): string;
|
|
17
17
|
/**
|
|
18
18
|
* Every parameter resolved against this device, as a flat name -> value map.
|
|
19
19
|
*
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { Console } from '../core/logger.js';
|
|
3
3
|
import { _ZAP } from '../core/state.js';
|
|
4
4
|
import { isEmpty } from '../core/utils.js';
|
|
5
|
-
import {
|
|
5
|
+
import { customConditionValues } from './custom.js';
|
|
6
|
+
import { deviceCountry } from './country.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Working out what a parameter is worth on this device.
|
|
@@ -11,11 +12,17 @@ import { customConditionValue } from './custom.js';
|
|
|
11
12
|
* condition with its criteria, and the mapping between them. Resolving it here rather than on
|
|
12
13
|
* the server is what lets a value be read with no network at all on the next visit.
|
|
13
14
|
*
|
|
14
|
-
* A parameter resolves by walking
|
|
15
|
-
*
|
|
15
|
+
* A parameter resolves by walking the conditions in the order of `all_condition`, the console's
|
|
16
|
+
* own order, and taking the value the parameter maps to the first one that both applies to it and
|
|
17
|
+
* matches this device. A condition matches when every one of its criteria does. Nothing matches,
|
|
18
|
+
* or nothing is mapped: the parameter's default value.
|
|
19
|
+
*
|
|
20
|
+
* Criteria are answered from what the browser knows about itself (app version, device type,
|
|
21
|
+
* country...), except a custom one (`custom_sdk_value`): that is answered by what the app set with
|
|
22
|
+
* setCustomConditionValue(conditionname, 'a,b'), and matches when one of those values is listed.
|
|
16
23
|
*
|
|
17
24
|
* Everything fails closed. An operator we do not recognise, a criteria key we cannot answer, a
|
|
18
|
-
* condition the parameter names but the server did not send
|
|
25
|
+
* condition the parameter names but the server did not send: each one means "does not match",
|
|
19
26
|
* never "matches". A config system that guesses hands an app a value its author never chose, on
|
|
20
27
|
* a device they cannot see, and that is far worse than falling back to the default.
|
|
21
28
|
*/
|
|
@@ -39,9 +46,12 @@ const FACTS = Object.freeze({
|
|
|
39
46
|
osversion: function () { return String(_ZAP._device_info.osversion || ''); }, // no i18n
|
|
40
47
|
devicetype: function () { return String(_ZAP._device_info.devicetype || ''); }, // no i18n
|
|
41
48
|
language: function () { return String(navigator.language || ''); }, // no i18n
|
|
49
|
+
country: deviceCountry,
|
|
42
50
|
customergroupid: function () { return String(_ZAP._user_info.groupid || ''); }, // no i18n
|
|
43
51
|
userid: function () { return String(_ZAP._user_info.userid || ''); }, // no i18n
|
|
44
52
|
});
|
|
53
|
+
/** The criteria key of a custom condition, answered by setCustomConditionValue. */
|
|
54
|
+
const CUSTOM_KEY = 'custom_sdk_value'; // no i18n
|
|
45
55
|
/** Codes and keys already complained about, so one unknown rule is not a log per read. */
|
|
46
56
|
var reported = {};
|
|
47
57
|
function sayOnce(key, message) {
|
|
@@ -63,8 +73,8 @@ function valuesOf(criteria) {
|
|
|
63
73
|
}
|
|
64
74
|
return raw.map(function (one) { return String(one); });
|
|
65
75
|
}
|
|
66
|
-
/** Whether one criteria holds on this device. */
|
|
67
|
-
function criteriaMatches(criteria) {
|
|
76
|
+
/** Whether one criteria holds on this device; `conditionname` answers a custom criteria. */
|
|
77
|
+
function criteriaMatches(criteria, conditionname) {
|
|
68
78
|
var key = String(criteria.key || ''); // no i18n
|
|
69
79
|
var operator = OPERATORS[String(criteria.status)];
|
|
70
80
|
if (operator === undefined) {
|
|
@@ -72,21 +82,31 @@ function criteriaMatches(criteria) {
|
|
|
72
82
|
+ '", so conditions using it will not match'); // no i18n
|
|
73
83
|
return false;
|
|
74
84
|
}
|
|
85
|
+
var listed = valuesOf(criteria);
|
|
86
|
+
if (key === CUSTOM_KEY) {
|
|
87
|
+
var mine = customConditionValues(conditionname);
|
|
88
|
+
if (mine.length === 0) {
|
|
89
|
+
sayOnce('custom:' + conditionname, 'AppticsRemoteConfig: no value for the custom condition "' + conditionname // no i18n
|
|
90
|
+
+ '"; set one with setCustomConditionValue, or it will not match'); // no i18n
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// 'in': one of the values the app set is one of the listed ones
|
|
94
|
+
return mine.some(function (one) { return listed.indexOf(one) !== -1; });
|
|
95
|
+
}
|
|
75
96
|
var fact = FACTS[key];
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
sayOnce('key:' + key, 'AppticsRemoteConfig: no value for criteria "' + key // no i18n
|
|
81
|
-
+ '"; set one with setCustomConditionValue, or conditions using it will not match'); // no i18n
|
|
97
|
+
if (fact === undefined) {
|
|
98
|
+
// not something a browser knows about itself
|
|
99
|
+
sayOnce('key:' + key, 'AppticsRemoteConfig: unknown criteria "' + key // no i18n
|
|
100
|
+
+ '", so conditions using it will not match'); // no i18n
|
|
82
101
|
return false;
|
|
83
102
|
}
|
|
103
|
+
var actual = fact();
|
|
84
104
|
if (actual === '') {
|
|
85
105
|
return false;
|
|
86
106
|
}
|
|
87
107
|
// 'in': the device's value is one of the listed ones. Compared as strings because that is
|
|
88
108
|
// what both sides are - '1.1' is a version, not the number 1.1
|
|
89
|
-
return
|
|
109
|
+
return listed.indexOf(actual) !== -1;
|
|
90
110
|
}
|
|
91
111
|
/**
|
|
92
112
|
* Whether a condition holds: every one of its criteria must. The template requires at least one
|
|
@@ -99,43 +119,40 @@ function conditionMatches(condition) {
|
|
|
99
119
|
return false;
|
|
100
120
|
}
|
|
101
121
|
for (var i = 0; i < criterias.length; i++) {
|
|
102
|
-
if (!criteriaMatches(criterias[i])) {
|
|
122
|
+
if (!criteriaMatches(criterias[i], String(condition.conditionname || ''))) { // no i18n
|
|
103
123
|
return false;
|
|
104
124
|
}
|
|
105
125
|
}
|
|
106
126
|
return true;
|
|
107
127
|
}
|
|
108
|
-
/** The conditions a parameter names, in the order the console put them in. */
|
|
109
|
-
function orderedConditions(parameter) {
|
|
110
|
-
var mapped = parameter.conditions;
|
|
111
|
-
if (!Array.isArray(mapped)) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
return mapped.slice().sort(function (a, b) {
|
|
115
|
-
return Number(a.conditionorder || 0) - Number(b.conditionorder || 0); // no i18n
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
128
|
/**
|
|
119
129
|
* The value of one parameter on this device, as the string the server stores it as.
|
|
120
130
|
*
|
|
121
|
-
* `
|
|
122
|
-
*
|
|
131
|
+
* `conditions` is `all_condition` as the server sent it: its order is the order they are tried
|
|
132
|
+
* in. The parameter's own `conditions` say which of them apply to it, and with what value.
|
|
123
133
|
*/
|
|
124
|
-
function resolve(parameter,
|
|
125
|
-
var
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (conditionMatches(condition)) {
|
|
134
|
+
function resolve(parameter, conditions) {
|
|
135
|
+
var links = {};
|
|
136
|
+
(Array.isArray(parameter.conditions) ? parameter.conditions : []).forEach(function (link) {
|
|
137
|
+
links[String(link.conditionid)] = link;
|
|
138
|
+
});
|
|
139
|
+
var sent = {};
|
|
140
|
+
for (var i = 0; i < conditions.length; i++) {
|
|
141
|
+
var condition = conditions[i];
|
|
142
|
+
var id = String(condition.conditionid);
|
|
143
|
+
sent[id] = true;
|
|
144
|
+
var link = links[id];
|
|
145
|
+
if (link !== undefined && conditionMatches(condition)) {
|
|
136
146
|
return String(link.value === undefined || link.value === null ? '' : link.value);
|
|
137
147
|
}
|
|
138
148
|
}
|
|
149
|
+
Object.keys(links).forEach(function (id) {
|
|
150
|
+
if (!sent[id]) {
|
|
151
|
+
// the parameter points at a condition the answer did not include
|
|
152
|
+
sayOnce('missing:' + id, 'AppticsRemoteConfig: parameter "' + parameter.paramname // no i18n
|
|
153
|
+
+ '" uses condition ' + id + ', which the server did not send'); // no i18n
|
|
154
|
+
}
|
|
155
|
+
});
|
|
139
156
|
return String(parameter.defaultvalue === undefined || parameter.defaultvalue === null ? '' : parameter.defaultvalue);
|
|
140
157
|
}
|
|
141
158
|
/**
|
|
@@ -149,16 +166,12 @@ function resolveAll(remoteconfig) {
|
|
|
149
166
|
var out = {};
|
|
150
167
|
var parameters = Array.isArray(remoteconfig.all_parameter) ? remoteconfig.all_parameter : [];
|
|
151
168
|
var conditions = Array.isArray(remoteconfig.all_condition) ? remoteconfig.all_condition : [];
|
|
152
|
-
var byId = {};
|
|
153
|
-
conditions.forEach(function (condition) {
|
|
154
|
-
byId[String(condition.conditionid)] = condition;
|
|
155
|
-
});
|
|
156
169
|
parameters.forEach(function (parameter) {
|
|
157
170
|
var name = String(parameter.paramname || ''); // no i18n
|
|
158
171
|
if (name === '') {
|
|
159
172
|
return;
|
|
160
173
|
}
|
|
161
|
-
out[name] = resolve(parameter,
|
|
174
|
+
out[name] = resolve(parameter, conditions);
|
|
162
175
|
});
|
|
163
176
|
return out;
|
|
164
177
|
}
|
|
@@ -15,10 +15,10 @@ export declare function fetchRemoteConfig(): Promise<Record<string, unknown>>;
|
|
|
15
15
|
export declare function resetRemoteConfig(): void;
|
|
16
16
|
/**
|
|
17
17
|
* Makes sure the values are this page's own: from a fetch that just succeeded, or one within
|
|
18
|
-
* CACHE_MS. Resolves true when they are, false when the fetch failed
|
|
19
|
-
*
|
|
18
|
+
* CACHE_MS. Resolves true when they are, false when the fetch failed or could not be made — the
|
|
19
|
+
* caller then decides whether stored values will do. Never rejects.
|
|
20
20
|
*
|
|
21
21
|
* Waits for start-up and for the device to be registered, which a first visit is still doing
|
|
22
22
|
* when ready() resolves: getupdates is refused without the device ids.
|
|
23
23
|
*/
|
|
24
|
-
export declare function obtain(
|
|
24
|
+
export declare function obtain(): Promise<boolean>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { SDK_MODULE } from '../core/constants.js';
|
|
3
3
|
import { hooks } from '../core/hooks.js';
|
|
4
4
|
import { _ZAP } from '../core/state.js';
|
|
@@ -7,8 +7,8 @@ import { nowMs } from '../core/utils.js';
|
|
|
7
7
|
import { rejectIfOffline, rejectIfNoDeviceIds, postToApptics, apiHeaders } from '../core/transport.js';
|
|
8
8
|
import { deviceReady } from '../device/index.js';
|
|
9
9
|
import { ready } from '../lifecycle.js';
|
|
10
|
-
import { reresolve, clearRemoteConfig, store, storeUnchanged, flagtime } from './store.js';
|
|
11
10
|
import { clearCustomConditionValues } from './custom.js';
|
|
11
|
+
import { store, storeUnchanged, flagtime, reresolve, clearRemoteConfig } from './store.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The getupdates call.
|
|
@@ -32,16 +32,11 @@ var inFlight = null;
|
|
|
32
32
|
var generation = 0;
|
|
33
33
|
/**
|
|
34
34
|
* How long a fetch that succeeded on this page answers for later ones, as on Android: a value
|
|
35
|
-
* read on every render must not be a request on every render.
|
|
35
|
+
* read on every render must not be a request on every render.
|
|
36
36
|
*/
|
|
37
37
|
const CACHE_MS = 5 * 60 * 1000;
|
|
38
|
-
/** coldFetch goes to the network every time, so it is limited: three in any minute, as on Android. */
|
|
39
|
-
const COLD_LIMIT = 3;
|
|
40
|
-
const COLD_WINDOW_MS = 60 * 1000;
|
|
41
38
|
/** When a fetch on this page last succeeded; 0 until one has. */
|
|
42
39
|
var fetchedAt = 0;
|
|
43
|
-
/** When the coldFetch calls of the last minute went out. */
|
|
44
|
-
var coldCalls = [];
|
|
45
40
|
/**
|
|
46
41
|
* Whether remote config is still worth asking about.
|
|
47
42
|
*
|
|
@@ -154,36 +149,27 @@ function resetRemoteConfig() {
|
|
|
154
149
|
generation++;
|
|
155
150
|
inFlight = null;
|
|
156
151
|
fetchedAt = 0;
|
|
157
|
-
coldCalls = [];
|
|
158
152
|
_ZAP._remoteConfigReported = true;
|
|
159
153
|
clearCustomConditionValues();
|
|
160
154
|
clearRemoteConfig();
|
|
161
155
|
}
|
|
162
156
|
/**
|
|
163
157
|
* Makes sure the values are this page's own: from a fetch that just succeeded, or one within
|
|
164
|
-
* CACHE_MS. Resolves true when they are, false when the fetch failed
|
|
165
|
-
*
|
|
158
|
+
* CACHE_MS. Resolves true when they are, false when the fetch failed or could not be made — the
|
|
159
|
+
* caller then decides whether stored values will do. Never rejects.
|
|
166
160
|
*
|
|
167
161
|
* Waits for start-up and for the device to be registered, which a first visit is still doing
|
|
168
162
|
* when ready() resolves: getupdates is refused without the device ids.
|
|
169
163
|
*/
|
|
170
|
-
function obtain(
|
|
164
|
+
function obtain() {
|
|
171
165
|
return ready().then(function (running) {
|
|
172
166
|
if (!running) {
|
|
173
167
|
return false;
|
|
174
168
|
}
|
|
175
169
|
var now = nowMs();
|
|
176
|
-
if (
|
|
170
|
+
if (fetchedAt > 0 && now - fetchedAt < CACHE_MS) {
|
|
177
171
|
return true;
|
|
178
172
|
}
|
|
179
|
-
if (coldFetch === true) {
|
|
180
|
-
coldCalls = coldCalls.filter(function (at) { return now - at < COLD_WINDOW_MS; });
|
|
181
|
-
if (coldCalls.length >= COLD_LIMIT) {
|
|
182
|
-
Console.warn(LABEL + ': coldFetch is limited to ' + COLD_LIMIT + ' a minute; this one is not sent'); // no i18n
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
coldCalls.push(now);
|
|
186
|
-
}
|
|
187
173
|
return deviceReady().then(function () {
|
|
188
174
|
return fetchRemoteConfig();
|
|
189
175
|
}).then(function () {
|
|
@@ -196,11 +182,11 @@ function obtain(coldFetch) {
|
|
|
196
182
|
}
|
|
197
183
|
/** Start-up's own fetch, so values are this session's without the app asking. */
|
|
198
184
|
function fetchOnStart() {
|
|
199
|
-
obtain(
|
|
185
|
+
obtain();
|
|
200
186
|
}
|
|
201
187
|
hooks.resetRemoteConfig = resetRemoteConfig;
|
|
202
188
|
hooks.remoteConfigStarted = fetchOnStart;
|
|
203
|
-
// a condition on the customer group answers differently once
|
|
189
|
+
// a condition on the customer group answers differently once AppticsUser.setUser has named one
|
|
204
190
|
hooks.remoteConfigUserChanged = reresolve;
|
|
205
191
|
// imported after start-up has already finished (a lazy import, say): fetch now, as start-up would have
|
|
206
192
|
if (_ZAP._ready) {
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
|
|
2
2
|
export declare const AppticsRemoteConfig: {
|
|
3
|
-
fetchValue: (name: string, callback?: (value: string | null) => void
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
fetchValue: (name: string, callback?: (value: string | null) => void) => Promise<string | null>;
|
|
4
|
+
/** The parameters named, or every parameter when none are: name -> value, null when there is none. */
|
|
5
|
+
fetchValues: {
|
|
6
|
+
(names?: string[], callback?: (values: Record<string, string | null>) => void): Promise<Record<string, string | null>>;
|
|
7
|
+
(callback: (values: Record<string, string | null>) => void): Promise<Record<string, string | null>>;
|
|
8
|
+
};
|
|
7
9
|
/**
|
|
8
|
-
* What this device is for a custom
|
|
9
|
-
*
|
|
10
|
+
* What this device is for a custom condition, by the condition's name, as comma separated
|
|
11
|
+
* values: it matches when one of them is listed. An empty value removes it.
|
|
10
12
|
*/
|
|
11
|
-
setCustomConditionValue: (
|
|
13
|
+
setCustomConditionValue: (conditionname: string, value: string | number | boolean | null) => void;
|
|
12
14
|
};
|
|
13
15
|
export default AppticsRemoteConfig;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
-
import { guard } from '../core/guard.js';
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { forward, guard } from '../core/guard.js';
|
|
3
3
|
import { Console } from '../core/logger.js';
|
|
4
4
|
import { obtain } from './fetch.js';
|
|
5
5
|
import { setCustomConditionValue } from './custom.js';
|
|
@@ -9,30 +9,21 @@ import { all } from './store.js';
|
|
|
9
9
|
* Remote config, shipped as its own entry point:
|
|
10
10
|
* import { AppticsRemoteConfig } from '@zoho_apptics/apptics-js-sdk/remote-config';
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* on
|
|
12
|
+
* fetchValue(name) reads one parameter, fetchValues(['a', 'b']) several, and fetchValues() every
|
|
13
|
+
* parameter the console has. Each parameter's value is the console's: the value of the first
|
|
14
|
+
* condition, in the console's order, that this device matches, else the parameter's default value.
|
|
15
|
+
* setCustomConditionValue(conditionname, 'a,b') answers the custom conditions.
|
|
16
|
+
* The rule set is stored and resolved on the device, so a fetch within five minutes answers without
|
|
17
|
+
* waiting on the network, and a failed one answers with the values fetched before. The SDK fetches
|
|
18
|
+
* on start-up, and re-applies the rules on its own when AppticsUser.setUser changes the user.
|
|
18
19
|
*
|
|
19
|
-
* A
|
|
20
|
-
*
|
|
21
|
-
* problem into the app's rendering code.
|
|
20
|
+
* A name the console does not have is null, never undefined: a config system whose failure mode
|
|
21
|
+
* is `undefined` moves the problem into the app's rendering code.
|
|
22
22
|
*/
|
|
23
23
|
function raw(name) {
|
|
24
24
|
var values = all();
|
|
25
25
|
return Object.prototype.hasOwnProperty.call(values, name) ? values[name] : undefined;
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Whether a fetch that failed still answers with what was fetched before. On by default, as
|
|
29
|
-
* `fallbackWithOfflineValue` is on Android: a page offline for a moment keeps its config.
|
|
30
|
-
*/
|
|
31
|
-
var fallbackWithOfflineValue = true;
|
|
32
|
-
/** Off: a failed fetch answers with null, or the caller's defaults, rather than stored values. */
|
|
33
|
-
function setFallbackWithOfflineValue(enabled) {
|
|
34
|
-
fallbackWithOfflineValue = typeof enabled === 'boolean' ? enabled : true; // no i18n
|
|
35
|
-
}
|
|
36
27
|
/** Hands a result to the app's callback, which must not be able to break the Promise. */
|
|
37
28
|
function answer(value, callback) {
|
|
38
29
|
if (typeof callback === 'function') { // no i18n
|
|
@@ -47,39 +38,74 @@ function answer(value, callback) {
|
|
|
47
38
|
}
|
|
48
39
|
/**
|
|
49
40
|
* One parameter, fetched for this page, as Android's fetchValue: resolves with its value, or null
|
|
50
|
-
* when the console has no such parameter
|
|
41
|
+
* when the console has no such parameter. A failed fetch answers with the value fetched before.
|
|
51
42
|
*/
|
|
52
|
-
function fetchValue(name, callback
|
|
53
|
-
return obtain(
|
|
54
|
-
var value =
|
|
43
|
+
function fetchValue(name, callback) {
|
|
44
|
+
return obtain().then(function () {
|
|
45
|
+
var value = raw(name);
|
|
55
46
|
return answer(value === undefined ? null : value, callback);
|
|
56
47
|
});
|
|
57
48
|
}
|
|
58
49
|
/**
|
|
59
|
-
* Several parameters at once
|
|
60
|
-
*
|
|
50
|
+
* Several parameters at once: `names` lists the ones wanted, and the result has exactly those
|
|
51
|
+
* names, null for one the console does not have. With no names (or an empty list), every
|
|
52
|
+
* parameter the console has. A callback may come first when there are no names.
|
|
61
53
|
*/
|
|
62
|
-
function fetchValues(
|
|
63
|
-
|
|
54
|
+
function fetchValues(names, callback) {
|
|
55
|
+
if (typeof names === 'function') { // no i18n
|
|
56
|
+
// fetchValues(callback)
|
|
57
|
+
callback = names;
|
|
58
|
+
names = undefined;
|
|
59
|
+
}
|
|
60
|
+
var wanted = wantedNames(names);
|
|
61
|
+
var done = typeof callback === 'function' ? callback : undefined; // no i18n
|
|
62
|
+
return obtain().then(function () {
|
|
63
|
+
var values = all();
|
|
64
64
|
var out = {};
|
|
65
|
-
Object.keys(
|
|
66
|
-
var value =
|
|
67
|
-
out[name] = value === undefined ?
|
|
65
|
+
(wanted === null ? Object.keys(values) : wanted).forEach(function (name) {
|
|
66
|
+
var value = raw(name);
|
|
67
|
+
out[name] = value === undefined ? null : value;
|
|
68
68
|
});
|
|
69
|
-
return answer(out,
|
|
69
|
+
return answer(out, done);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** The names asked for, or null for every parameter. Anything but a list of names counts as none. */
|
|
73
|
+
function wantedNames(names) {
|
|
74
|
+
if (names === undefined || names === null) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(names)) {
|
|
78
|
+
Console.warn('AppticsRemoteConfig.fetchValues: takes a list of parameter names, as fetchValues([\'a\', \'b\']); answering with every parameter'); // no i18n
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
var out = [];
|
|
82
|
+
names.forEach(function (name) {
|
|
83
|
+
var one = typeof name === 'string' ? name.trim() : ''; // no i18n
|
|
84
|
+
if (one !== '' && out.indexOf(one) === -1) { // no i18n
|
|
85
|
+
out.push(one);
|
|
86
|
+
}
|
|
70
87
|
});
|
|
88
|
+
return out.length === 0 ? null : out;
|
|
71
89
|
}
|
|
72
|
-
|
|
90
|
+
const fetchValueGuarded = guard('AppticsRemoteConfig.fetchValue', fetchValue); // no i18n
|
|
91
|
+
const fetchValuesGuarded = guard('AppticsRemoteConfig.fetchValues', fetchValues); // no i18n
|
|
92
|
+
const setCustomConditionValueGuarded = guard('AppticsRemoteConfig.setCustomConditionValue', setCustomConditionValue); // no i18n
|
|
93
|
+
/** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
|
|
73
94
|
const AppticsRemoteConfig = {
|
|
74
|
-
fetchValue:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
fetchValue: function fetchValue(name, callback) {
|
|
96
|
+
return forward(fetchValueGuarded, arguments);
|
|
97
|
+
},
|
|
98
|
+
/** The parameters named, or every parameter when none are: name -> value, null when there is none. */
|
|
99
|
+
fetchValues: function fetchValues(names, callback) {
|
|
100
|
+
return forward(fetchValuesGuarded, arguments);
|
|
101
|
+
}, // no i18n
|
|
78
102
|
/**
|
|
79
|
-
* What this device is for a custom
|
|
80
|
-
*
|
|
103
|
+
* What this device is for a custom condition, by the condition's name, as comma separated
|
|
104
|
+
* values: it matches when one of them is listed. An empty value removes it.
|
|
81
105
|
*/
|
|
82
|
-
setCustomConditionValue:
|
|
106
|
+
setCustomConditionValue: function setCustomConditionValue(conditionname, value) {
|
|
107
|
+
forward(setCustomConditionValueGuarded, arguments);
|
|
108
|
+
},
|
|
83
109
|
};
|
|
84
110
|
|
|
85
111
|
export { AppticsRemoteConfig, AppticsRemoteConfig as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { Console } from '../core/logger.js';
|
|
3
3
|
import { user_defaults } from '../core/storage.js';
|
|
4
4
|
import { resolveAll, resetEvaluationWarnings } from './evaluate.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The country of every IANA time zone, by its ISO 3166 code: generated by
|
|
3
|
+
* scripts/tz-countries.mjs from tzdata 2026d-rearguard (zone.tab, and the old names its links keep).
|
|
4
|
+
* Do not edit by hand.
|
|
5
|
+
*
|
|
6
|
+
* Packed as "CC:Area/City,City|Area/City;CC:...": a country, then its zones grouped by area.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TZ_COUNTRIES = "AD:Europe/Andorra;AE:Asia/Dubai;AF:Asia/Kabul;AG:America/Antigua;AI:America/Anguilla;AL:Europe/Tirane;AM:Asia/Yerevan;AO:Africa/Luanda;AQ:Antarctica/Casey,Davis,DumontDUrville,Mawson,McMurdo,Palmer,Rothera,Syowa,Troll,Vostok;AR:America/Argentina/Buenos_Aires,Argentina/Catamarca,Argentina/ComodRivadavia,Argentina/Cordoba,Argentina/Jujuy,Argentina/La_Rioja,Argentina/Mendoza,Argentina/Rio_Gallegos,Argentina/Salta,Argentina/San_Juan,Argentina/San_Luis,Argentina/Tucuman,Argentina/Ushuaia,Buenos_Aires,Catamarca,Cordoba,Jujuy,Mendoza,Rosario;AS:Pacific/Pago_Pago,Samoa|US/Samoa;AT:Europe/Vienna;AU:Antarctica/Macquarie|Australia/ACT,Adelaide,Brisbane,Broken_Hill,Canberra,Currie,Darwin,Eucla,Hobart,LHI,Lindeman,Lord_Howe,Melbourne,NSW,North,Perth,Queensland,South,Sydney,Tasmania,Victoria,West,Yancowinna;AW:America/Aruba;AX:Europe/Mariehamn;AZ:Asia/Baku;BA:Europe/Sarajevo;BB:America/Barbados;BD:Asia/Dacca,Dhaka;BE:CET,MET|Europe/Brussels;BF:Africa/Ouagadougou;BG:Europe/Sofia;BH:Asia/Bahrain;BI:Africa/Bujumbura;BJ:Africa/Porto-Novo;BL:America/St_Barthelemy;BM:Atlantic/Bermuda;BN:Asia/Brunei;BO:America/La_Paz;BQ:America/Kralendijk;BR:America/Araguaina,Bahia,Belem,Boa_Vista,Campo_Grande,Cuiaba,Eirunepe,Fortaleza,Maceio,Manaus,Noronha,Porto_Acre,Porto_Velho,Recife,Rio_Branco,Santarem,Sao_Paulo|Brazil/Acre,DeNoronha,East,West;BS:America/Nassau;BT:Asia/Thimbu,Thimphu;BW:Africa/Gaborone;BY:Europe/Minsk;BZ:America/Belize;CA:America/Atikokan,Blanc-Sablon,Cambridge_Bay,Creston,Dawson,Dawson_Creek,Edmonton,Fort_Nelson,Glace_Bay,Goose_Bay,Halifax,Inuvik,Iqaluit,Moncton,Montreal,Nipigon,Pangnirtung,Rainy_River,Rankin_Inlet,Regina,Resolute,St_Johns,Swift_Current,Thunder_Bay,Toronto,Vancouver,Whitehorse,Winnipeg,Yellowknife|Canada/Atlantic,Central,Eastern,Mountain,Newfoundland,Pacific,Saskatchewan,Yukon;CC:Indian/Cocos;CD:Africa/Kinshasa,Lubumbashi;CF:Africa/Bangui;CG:Africa/Brazzaville;CH:Europe/Zurich;CI:Africa/Abidjan,Timbuktu|Iceland;CK:Pacific/Rarotonga;CL:America/Coyhaique,Punta_Arenas,Santiago|Chile/Continental,EasterIsland|Pacific/Easter;CM:Africa/Douala;CN:Asia/Chongqing,Chungking,Harbin,Kashgar,Shanghai,Urumqi|PRC;CO:America/Bogota;CR:America/Costa_Rica;CU:America/Havana|Cuba;CV:Atlantic/Cape_Verde;CW:America/Curacao;CX:Indian/Christmas;CY:Asia/Famagusta,Nicosia|Europe/Nicosia;CZ:Europe/Prague;DE:Atlantic/Jan_Mayen|Europe/Berlin,Busingen;DJ:Africa/Djibouti;DK:Europe/Copenhagen;DM:America/Dominica;DO:America/Santo_Domingo;DZ:Africa/Algiers;EC:America/Guayaquil|Pacific/Galapagos;EE:Europe/Tallinn;EG:Africa/Cairo|Egypt;EH:Africa/El_Aaiun;ER:Africa/Asmara;ES:Africa/Ceuta|Atlantic/Canary|Europe/Madrid;ET:Africa/Addis_Ababa;FI:Europe/Helsinki;FJ:Pacific/Fiji;FK:Atlantic/Stanley;FM:Pacific/Chuuk,Kosrae,Pohnpei;FO:Atlantic/Faeroe,Faroe;FR:Europe/Paris;GA:Africa/Libreville;GB:Europe/Belfast,London|GB,GB-Eire;GD:America/Grenada;GE:Asia/Tbilisi;GF:America/Cayenne;GG:Europe/Guernsey;GH:Africa/Accra;GI:Europe/Gibraltar;GL:America/Danmarkshavn,Godthab,Nuuk,Scoresbysund,Thule;GM:Africa/Banjul;GN:Africa/Conakry;GP:America/Guadeloupe;GQ:Africa/Malabo;GR:EET|Europe/Athens;GS:Atlantic/South_Georgia;GT:America/Guatemala;GU:Pacific/Guam;GW:Africa/Bissau;GY:America/Guyana;HK:Asia/Hong_Kong|Hongkong;HN:America/Tegucigalpa;HR:Europe/Zagreb;HT:America/Port-au-Prince;HU:Europe/Budapest;ID:Asia/Jakarta,Jayapura,Makassar,Pontianak,Ujung_Pandang;IE:Eire|Europe/Dublin;IL:Asia/Jerusalem,Tel_Aviv|Israel;IM:Europe/Isle_of_Man;IN:Asia/Calcutta,Kolkata;IO:Indian/Chagos;IQ:Asia/Baghdad;IR:Asia/Tehran|Iran;IS:Atlantic/Reykjavik;IT:Europe/Rome;JE:Europe/Jersey;JM:America/Jamaica|Jamaica;JO:Asia/Amman;JP:Asia/Tokyo|Japan;KE:Africa/Asmera,Nairobi;KG:Asia/Bishkek;KH:Asia/Phnom_Penh;KI:Pacific/Enderbury,Kanton,Kiritimati,Tarawa;KM:Indian/Comoro;KN:America/St_Kitts;KP:Asia/Pyongyang;KR:Asia/Seoul|ROK;KW:Asia/Kuwait;KY:America/Cayman;KZ:Asia/Almaty,Aqtau,Aqtobe,Atyrau,Oral,Qostanay,Qyzylorda;LA:Asia/Vientiane;LB:Asia/Beirut;LC:America/St_Lucia;LI:Europe/Vaduz;LK:Asia/Colombo;LR:Africa/Monrovia;LS:Africa/Maseru;LT:Europe/Vilnius;LU:Europe/Luxembourg;LV:Europe/Riga;LY:Africa/Tripoli|Libya;MA:Africa/Casablanca;MC:Europe/Monaco;MD:Europe/Chisinau,Tiraspol;ME:Europe/Podgorica;MF:America/Marigot;MG:Indian/Antananarivo;MH:Kwajalein|Pacific/Kwajalein,Majuro;MK:Europe/Skopje;ML:Africa/Bamako;MM:Asia/Rangoon,Yangon;MN:Asia/Choibalsan,Hovd,Ulaanbaatar,Ulan_Bator;MO:Asia/Macao,Macau;MP:Pacific/Saipan;MQ:America/Martinique;MR:Africa/Nouakchott;MS:America/Montserrat;MT:Europe/Malta;MU:Indian/Mauritius;MV:Indian/Maldives;MW:Africa/Blantyre;MX:America/Bahia_Banderas,Cancun,Chihuahua,Ciudad_Juarez,Ensenada,Hermosillo,Matamoros,Mazatlan,Merida,Mexico_City,Monterrey,Ojinaga,Santa_Isabel,Tijuana|Mexico/BajaNorte,BajaSur,General;MY:Asia/Kuala_Lumpur,Kuching;MZ:Africa/Maputo;NA:Africa/Windhoek;NC:Pacific/Noumea;NE:Africa/Niamey;NF:Pacific/Norfolk;NG:Africa/Lagos;NI:America/Managua;NL:Europe/Amsterdam;NO:Europe/Oslo;NP:Asia/Kathmandu,Katmandu;NR:Pacific/Nauru;NU:Pacific/Niue;NZ:Antarctica/South_Pole|NZ,NZ-CHAT|Pacific/Auckland,Chatham;OM:Asia/Muscat;PA:America/Coral_Harbour,Panama|EST;PE:America/Lima;PF:Pacific/Gambier,Marquesas,Tahiti;PG:Pacific/Bougainville,Port_Moresby,Truk,Yap;PH:Asia/Manila;PK:Asia/Karachi;PL:Europe/Warsaw|Poland;PM:America/Miquelon;PN:Pacific/Pitcairn;PR:America/Puerto_Rico,Virgin;PS:Asia/Gaza,Hebron;PT:Atlantic/Azores,Madeira|Europe/Lisbon|Portugal,WET;PW:Pacific/Palau;PY:America/Asuncion;QA:Asia/Qatar;RE:Indian/Reunion;RO:Europe/Bucharest;RS:Europe/Belgrade;RU:Asia/Anadyr,Barnaul,Chita,Irkutsk,Kamchatka,Khandyga,Krasnoyarsk,Magadan,Novokuznetsk,Novosibirsk,Omsk,Sakhalin,Srednekolymsk,Tomsk,Ust-Nera,Vladivostok,Yakutsk,Yekaterinburg|Europe/Astrakhan,Kaliningrad,Kirov,Moscow,Samara,Saratov,Ulyanovsk,Volgograd|W-SU;RW:Africa/Kigali;SA:Asia/Riyadh;SB:Pacific/Guadalcanal,Ponape;SC:Indian/Mahe;SD:Africa/Khartoum;SE:Europe/Stockholm;SG:Asia/Singapore|Singapore;SH:Atlantic/St_Helena;SI:Europe/Ljubljana;SJ:Arctic/Longyearbyen;SK:Europe/Bratislava;SL:Africa/Freetown;SM:Europe/San_Marino;SN:Africa/Dakar;SO:Africa/Mogadishu;SR:America/Paramaribo;SS:Africa/Juba;ST:Africa/Sao_Tome;SV:America/El_Salvador;SX:America/Lower_Princes;SY:Asia/Damascus;SZ:Africa/Mbabane;TC:America/Grand_Turk;TD:Africa/Ndjamena;TF:Indian/Kerguelen;TG:Africa/Lome;TH:Asia/Bangkok;TJ:Asia/Dushanbe;TK:Pacific/Fakaofo;TL:Asia/Dili;TM:Asia/Ashgabat,Ashkhabad;TN:Africa/Tunis;TO:Pacific/Tongatapu;TR:Asia/Istanbul|Europe/Istanbul|Turkey;TT:America/Port_of_Spain;TV:Pacific/Funafuti;TW:Asia/Taipei|ROC;TZ:Africa/Dar_es_Salaam;UA:Europe/Kiev,Kyiv,Simferopol,Uzhgorod,Zaporozhye;UG:Africa/Kampala;UM:Pacific/Midway,Wake;US:America/Adak,Anchorage,Atka,Boise,Chicago,Denver,Detroit,Fort_Wayne,Indiana/Indianapolis,Indiana/Knox,Indiana/Marengo,Indiana/Petersburg,Indiana/Tell_City,Indiana/Vevay,Indiana/Vincennes,Indiana/Winamac,Indianapolis,Juneau,Kentucky/Louisville,Kentucky/Monticello,Knox_IN,Los_Angeles,Louisville,Menominee,Metlakatla,New_York,Nome,North_Dakota/Beulah,North_Dakota/Center,North_Dakota/New_Salem,Phoenix,Shiprock,Sitka,Yakutat|HST,MST,Navajo|Pacific/Honolulu,Johnston|US/Alaska,Aleutian,Arizona,Central,East-Indiana,Eastern,Hawaii,Indiana-Starke,Michigan,Mountain,Pacific;UY:America/Montevideo;UZ:Asia/Samarkand,Tashkent;VA:Europe/Vatican;VC:America/St_Vincent;VE:America/Caracas;VG:America/Tortola;VI:America/St_Thomas;VN:Asia/Ho_Chi_Minh,Saigon;VU:Pacific/Efate;WF:Pacific/Wallis;WS:Pacific/Apia;YE:Asia/Aden;YT:Indian/Mayotte;ZA:Africa/Johannesburg;ZM:Africa/Lusaka;ZW:Africa/Harare";
|