@shaxpir/duiduidui-models 1.0.0 → 1.1.0
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/dist/models/{content/ArrayView.d.ts → ArrayView.d.ts} +1 -1
- package/dist/models/{content/ArrayView.js → ArrayView.js} +14 -14
- package/dist/models/BayesianScore.d.ts +9 -0
- package/dist/models/BayesianScore.js +27 -0
- package/dist/models/{markup/ChangeModel.d.ts → ChangeModel.d.ts} +2 -3
- package/dist/models/{markup/ChangeModel.js → ChangeModel.js} +20 -10
- package/dist/models/{content/Content.d.ts → Content.d.ts} +9 -10
- package/dist/models/{content/Content.js → Content.js} +2 -6
- package/dist/models/{content/ContentKind.d.ts → ContentKind.d.ts} +3 -0
- package/dist/models/{content/ContentKind.js → ContentKind.js} +5 -1
- package/dist/models/{content/Device.d.ts → Device.d.ts} +15 -7
- package/dist/models/{content/Device.js → Device.js} +14 -18
- package/dist/models/Hanzi.d.ts +15 -0
- package/dist/models/{content/Manifest.d.ts → Manifest.d.ts} +7 -3
- package/dist/models/{content/Manifest.js → Manifest.js} +12 -8
- package/dist/models/{content/Media.d.ts → Media.d.ts} +2 -3
- package/dist/models/{content/Media.js → Media.js} +4 -5
- package/dist/models/{content/Metric.d.ts → Metric.d.ts} +2 -3
- package/dist/models/{content/Metric.js → Metric.js} +18 -20
- package/dist/models/{content/Model.d.ts → Model.d.ts} +3 -5
- package/dist/models/{content/Model.js → Model.js} +5 -6
- package/dist/models/{markup/Operation.d.ts → Operation.d.ts} +2 -5
- package/dist/models/{markup/Operation.js → Operation.js} +30 -25
- package/dist/models/Phrase.d.ts +39 -0
- package/dist/models/{content/Profile.d.ts → Profile.d.ts} +1 -2
- package/dist/models/{content/Profile.js → Profile.js} +7 -9
- package/dist/models/Progress.d.ts +26 -0
- package/dist/models/Progress.js +39 -0
- package/dist/models/Review.d.ts +14 -0
- package/dist/models/{content/Session.d.ts → Session.d.ts} +7 -15
- package/dist/models/Session.js +103 -0
- package/dist/models/Term.d.ts +36 -0
- package/dist/models/Term.js +82 -0
- package/dist/models/User.d.ts +32 -0
- package/dist/models/User.js +65 -0
- package/dist/models/Workspace.d.ts +37 -0
- package/dist/models/Workspace.js +94 -0
- package/dist/models/index.d.ts +22 -4
- package/dist/models/index.js +22 -4
- package/dist/repo/PermissiveJson1.d.ts +16 -16
- package/dist/repo/PermissiveJson1.js +17 -7
- package/dist/repo/ShareSync.d.ts +6 -6
- package/dist/repo/ShareSync.js +26 -19
- package/dist/repo/index.d.ts +1 -0
- package/dist/repo/index.js +1 -0
- package/dist/util/index.d.ts +0 -14
- package/dist/util/index.js +0 -14
- package/lib/index.ts +5 -0
- package/lib/models/{content/ArrayView.ts → ArrayView.ts} +2 -2
- package/lib/models/BayesianScore.ts +32 -0
- package/lib/models/{markup/ChangeModel.ts → ChangeModel.ts} +3 -4
- package/lib/models/{content/Content.ts → Content.ts} +15 -18
- package/lib/models/{content/ContentKind.ts → ContentKind.ts} +6 -1
- package/lib/models/{content/Device.ts → Device.ts} +26 -22
- package/lib/models/Hanzi.ts +16 -0
- package/lib/models/{content/Manifest.ts → Manifest.ts} +15 -6
- package/lib/models/{content/Media.ts → Media.ts} +3 -4
- package/lib/models/Metric.ts +233 -0
- package/lib/models/{content/Model.ts → Model.ts} +4 -6
- package/lib/models/{markup/Operation.ts → Operation.ts} +6 -16
- package/lib/models/{content/Permissions.ts → Permissions.ts} +0 -2
- package/lib/models/Phrase.ts +53 -0
- package/lib/models/{content/Profile.ts → Profile.ts} +4 -6
- package/lib/models/Progress.ts +66 -0
- package/lib/models/Review.ts +18 -0
- package/lib/models/Session.ts +140 -0
- package/lib/models/Term.ts +139 -0
- package/lib/models/User.ts +97 -0
- package/lib/models/Workspace.ts +129 -0
- package/lib/models/index.ts +24 -0
- package/lib/repo/ShareSync.ts +9 -13
- package/lib/repo/index.ts +6 -0
- package/lib/util/index.ts +4 -0
- package/package.json +4 -15
- package/dist/models/OutboundMessage.d.ts +0 -18
- package/dist/models/OutboundMessage.js +0 -25
- package/dist/models/content/Activity.d.ts +0 -10
- package/dist/models/content/Billing.d.ts +0 -144
- package/dist/models/content/Billing.js +0 -418
- package/dist/models/content/Book.d.ts +0 -77
- package/dist/models/content/Book.js +0 -407
- package/dist/models/content/Category.d.ts +0 -16
- package/dist/models/content/Category.js +0 -20
- package/dist/models/content/Checkpointable.d.ts +0 -21
- package/dist/models/content/Checkpointable.js +0 -156
- package/dist/models/content/Comment.d.ts +0 -19
- package/dist/models/content/Comment.js +0 -53
- package/dist/models/content/ConceptArt.d.ts +0 -31
- package/dist/models/content/ConceptArt.js +0 -84
- package/dist/models/content/Context.d.ts +0 -28
- package/dist/models/content/Context.js +0 -162
- package/dist/models/content/DevEnv.d.ts +0 -5
- package/dist/models/content/DevEnv.js +0 -9
- package/dist/models/content/Dictionary.d.ts +0 -31
- package/dist/models/content/Dictionary.js +0 -5
- package/dist/models/content/DictionaryEntry.d.ts +0 -20
- package/dist/models/content/DictionaryEntry.js +0 -2
- package/dist/models/content/ElasticModel.d.ts +0 -149
- package/dist/models/content/ElasticModel.js +0 -179
- package/dist/models/content/Environment.d.ts +0 -61
- package/dist/models/content/Environment.js +0 -124
- package/dist/models/content/ExportOptions.d.ts +0 -64
- package/dist/models/content/ExportOptions.js +0 -213
- package/dist/models/content/Folder.d.ts +0 -16
- package/dist/models/content/Folder.js +0 -33
- package/dist/models/content/Fragment.d.ts +0 -54
- package/dist/models/content/Fragment.js +0 -181
- package/dist/models/content/Hanzi.d.ts +0 -21
- package/dist/models/content/HighlightRule.d.ts +0 -9
- package/dist/models/content/HighlightRule.js +0 -2
- package/dist/models/content/Migration.d.ts +0 -68
- package/dist/models/content/Migration.js +0 -155
- package/dist/models/content/Phrase.d.ts +0 -8
- package/dist/models/content/Placeholder.d.ts +0 -8
- package/dist/models/content/Placeholder.js +0 -36
- package/dist/models/content/RichText.d.ts +0 -58
- package/dist/models/content/RichText.js +0 -79
- package/dist/models/content/Session.js +0 -173
- package/dist/models/content/Speech.d.ts +0 -67
- package/dist/models/content/Speech.js +0 -97
- package/dist/models/content/Stub.d.ts +0 -24
- package/dist/models/content/Stub.js +0 -179
- package/dist/models/content/Time.d.ts +0 -56
- package/dist/models/content/Time.js +0 -295
- package/dist/models/content/User.d.ts +0 -36
- package/dist/models/content/User.js +0 -95
- package/dist/models/content/Workspace.d.ts +0 -71
- package/dist/models/content/Workspace.js +0 -237
- package/dist/models/content/index.d.ts +0 -36
- package/dist/models/content/index.js +0 -53
- package/dist/models/legacy/LegacyBodyFormat.d.ts +0 -9
- package/dist/models/legacy/LegacyBodyFormat.js +0 -2
- package/dist/models/legacy/LegacyComment.d.ts +0 -12
- package/dist/models/legacy/LegacyComment.js +0 -2
- package/dist/models/legacy/LegacyContent.d.ts +0 -53
- package/dist/models/legacy/LegacyContent.js +0 -55
- package/dist/models/legacy/LegacyConversion.d.ts +0 -55
- package/dist/models/legacy/LegacyConversion.js +0 -401
- package/dist/models/legacy/LegacyFragment.d.ts +0 -21
- package/dist/models/legacy/LegacyFragment.js +0 -2
- package/dist/models/legacy/LegacyLocator.d.ts +0 -8
- package/dist/models/legacy/LegacyLocator.js +0 -31
- package/dist/models/legacy/LegacyOutboundMessage.d.ts +0 -16
- package/dist/models/legacy/LegacyOutboundMessage.js +0 -13
- package/dist/models/legacy/LegacyPicture.d.ts +0 -14
- package/dist/models/legacy/LegacyPicture.js +0 -2
- package/dist/models/legacy/LegacyProfile.d.ts +0 -9
- package/dist/models/legacy/LegacyProfile.js +0 -2
- package/dist/models/legacy/LegacySession.d.ts +0 -41
- package/dist/models/legacy/LegacySession.js +0 -35
- package/dist/models/legacy/LegacyStory.d.ts +0 -23
- package/dist/models/legacy/LegacyStory.js +0 -2
- package/dist/models/legacy/LegacyStub.d.ts +0 -15
- package/dist/models/legacy/LegacyStub.js +0 -2
- package/dist/models/legacy/LegacyTransaction.d.ts +0 -14
- package/dist/models/legacy/LegacyTransaction.js +0 -49
- package/dist/models/legacy/LegacyUser.d.ts +0 -28
- package/dist/models/legacy/LegacyUser.js +0 -32
- package/dist/models/legacy/LegacyWorkspace.d.ts +0 -23
- package/dist/models/legacy/LegacyWorkspace.js +0 -6
- package/dist/models/legacy/index.d.ts +0 -15
- package/dist/models/legacy/index.js +0 -32
- package/dist/models/markup/BodyFormat.d.ts +0 -14
- package/dist/models/markup/BodyFormat.js +0 -190
- package/dist/models/markup/DeltaOps.d.ts +0 -5
- package/dist/models/markup/DeltaOps.js +0 -74
- package/dist/models/markup/HtmlMarkup.d.ts +0 -4
- package/dist/models/markup/HtmlMarkup.js +0 -21
- package/dist/models/markup/index.d.ts +0 -6
- package/dist/models/markup/index.js +0 -23
- package/dist/util/Async.d.ts +0 -8
- package/dist/util/Async.js +0 -18
- package/dist/util/Base62.d.ts +0 -6
- package/dist/util/Base62.js +0 -47
- package/dist/util/BinarySearch.d.ts +0 -7
- package/dist/util/BinarySearch.js +0 -46
- package/dist/util/CachingHasher.d.ts +0 -8
- package/dist/util/CachingHasher.js +0 -41
- package/dist/util/Color.d.ts +0 -32
- package/dist/util/Color.js +0 -204
- package/dist/util/Dispatch.d.ts +0 -15
- package/dist/util/Dispatch.js +0 -79
- package/dist/util/EditDistance.d.ts +0 -13
- package/dist/util/EditDistance.js +0 -184
- package/dist/util/NumberFormat.d.ts +0 -14
- package/dist/util/NumberFormat.js +0 -224
- package/dist/util/Struct.d.ts +0 -4
- package/dist/util/Struct.js +0 -15
- package/dist/util/Template.d.ts +0 -16
- package/dist/util/Template.js +0 -128
- package/dist/util/Text.d.ts +0 -45
- package/dist/util/Text.js +0 -243
- package/dist/util/Tuples.d.ts +0 -9
- package/dist/util/Tuples.js +0 -135
- package/dist/util/Validate.d.ts +0 -4
- package/dist/util/Validate.js +0 -11
- package/dist/util/Vocabulary.d.ts +0 -3
- package/dist/util/Vocabulary.js +0 -35
- package/lib/models/content/Billing.ts +0 -558
- package/lib/models/content/DevEnv.ts +0 -5
- package/lib/models/content/DictionaryEntry.ts +0 -22
- package/lib/models/content/Hanzi.ts +0 -25
- package/lib/models/content/Phrase.ts +0 -10
- package/lib/models/content/Time.ts +0 -328
- package/lib/models/content/User.ts +0 -130
- package/lib/models/markup/DeltaOps.ts +0 -71
- package/lib/util/Base62.ts +0 -47
- package/lib/util/CachingHasher.ts +0 -38
- package/lib/util/Dispatch.ts +0 -92
- package/lib/util/NumberFormat.ts +0 -194
- package/lib/util/Struct.ts +0 -14
- package/lib/util/Tuples.ts +0 -131
- /package/dist/models/{content/GeoLocation.d.ts → GeoLocation.d.ts} +0 -0
- /package/dist/models/{content/GeoLocation.js → GeoLocation.js} +0 -0
- /package/dist/models/{content/Hanzi.js → Hanzi.js} +0 -0
- /package/dist/models/{content/Permissions.d.ts → Permissions.d.ts} +0 -0
- /package/dist/models/{content/Permissions.js → Permissions.js} +0 -0
- /package/dist/models/{content/Phrase.js → Phrase.js} +0 -0
- /package/dist/models/{content/Activity.js → Review.js} +0 -0
- /package/dist/{models/markup → repo}/TextEditOps.d.ts +0 -0
- /package/dist/{models/markup → repo}/TextEditOps.js +0 -0
- /package/lib/models/{content/GeoLocation.ts → GeoLocation.ts} +0 -0
- /package/lib/{models/markup → repo}/TextEditOps.ts +0 -0
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
import dayjs from 'dayjs';
|
|
2
|
-
import utc from 'dayjs/plugin/utc';
|
|
3
|
-
import timezone from 'dayjs/plugin/timezone';
|
|
4
|
-
import dayOfYear from 'dayjs/plugin/dayOfYear';
|
|
5
|
-
dayjs.extend(utc);
|
|
6
|
-
dayjs.extend(timezone);
|
|
7
|
-
dayjs.extend(dayOfYear);
|
|
8
|
-
|
|
9
|
-
enum CompactDateTimeBrand {}
|
|
10
|
-
enum CompactDateBrand {}
|
|
11
|
-
|
|
12
|
-
export type CompactDateTime = string & CompactDateTimeBrand;
|
|
13
|
-
export type CompactDate = string & CompactDateBrand;
|
|
14
|
-
|
|
15
|
-
export interface SingleTime {
|
|
16
|
-
utc_time:CompactDateTime;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface MultiTime {
|
|
20
|
-
utc_time:CompactDateTime;
|
|
21
|
-
local_time:CompactDateTime;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class MultiClock {
|
|
25
|
-
|
|
26
|
-
public static utcNow():MultiTime {
|
|
27
|
-
const localtime:CompactDateTime = Time.local();
|
|
28
|
-
const utcTime:CompactDateTime = Time.utc(localtime);
|
|
29
|
-
return {
|
|
30
|
-
local_time: utcTime,
|
|
31
|
-
utc_time: utcTime
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
public static now():MultiTime {
|
|
36
|
-
const localtime:CompactDateTime = Time.local();
|
|
37
|
-
return {
|
|
38
|
-
local_time: localtime,
|
|
39
|
-
utc_time: Time.utc(localtime)
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class Time {
|
|
45
|
-
|
|
46
|
-
public static readonly DATETIME_FORMAT_LEGACY:string = "YYYY-MM-DDTHH:mm:ss.SSSZ";
|
|
47
|
-
|
|
48
|
-
public static readonly DATE_FORMAT:string = "YYYYMMDD";
|
|
49
|
-
public static readonly DATETIME_FORMAT_COMPACT:string = "YYYYMMDDHHmmssSSS";
|
|
50
|
-
|
|
51
|
-
public static readonly DATE_NO_YEAR:string = "MMMM D";
|
|
52
|
-
public static readonly DATE_BEAUTIFIED:string = "MMMM D, YYYY";
|
|
53
|
-
public static readonly DATE_FULLY_BEAUTIFIED:string = "dddd, MMMM D, YYYY";
|
|
54
|
-
|
|
55
|
-
public static readonly YEAR_ONLY:string = "YYYY";
|
|
56
|
-
|
|
57
|
-
public static asCompactDateTime(text:string):CompactDateTime {
|
|
58
|
-
// A compact datetime is always 17 digits long and starts with '20'
|
|
59
|
-
if (text.startsWith("20") && /^\d{17}$/.test(text)) {
|
|
60
|
-
return text as CompactDateTime;
|
|
61
|
-
}
|
|
62
|
-
return null as CompactDateTime;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public static asCompactDate(text:string):CompactDate|null {
|
|
66
|
-
// A compact date is always 8 digits long and starts with '20'
|
|
67
|
-
if (text.startsWith("20") && /^\d{8}$/.test(text)) {
|
|
68
|
-
return text as CompactDate;
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public static isDateBefore(a:CompactDate, b:CompactDate):boolean {
|
|
74
|
-
return Time.compareDate(a, b) < 0;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
public static isDateAfter(a:CompactDate, b:CompactDate):boolean {
|
|
78
|
-
return Time.compareDate(a, b) > 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
public static compareDate(a:CompactDate, b:CompactDate):number {
|
|
82
|
-
if (a == null && b == null) {
|
|
83
|
-
return 0;
|
|
84
|
-
} else if (a == null) {
|
|
85
|
-
return -1;
|
|
86
|
-
} else if (b == null) {
|
|
87
|
-
return 1;
|
|
88
|
-
}
|
|
89
|
-
return (a as string).localeCompare(b as string);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public static isDateTimeBefore(a:CompactDateTime, b:CompactDateTime):boolean {
|
|
93
|
-
return Time.compareDateTime(a, b) < 0;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
public static isDateTimeAfter(a:CompactDateTime, b:CompactDateTime):boolean {
|
|
97
|
-
return Time.compareDateTime(a, b) > 0;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
public static compareDateTime(a:CompactDateTime, b:CompactDateTime):number {
|
|
101
|
-
if (a == null && b == null) {
|
|
102
|
-
return 0;
|
|
103
|
-
} else if (a == null) {
|
|
104
|
-
return -1;
|
|
105
|
-
} else if (b == null) {
|
|
106
|
-
return 1;
|
|
107
|
-
}
|
|
108
|
-
return (a as string).localeCompare(b as string);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
public static sortByUtcTime(array:MultiTime[]):MultiTime[] {
|
|
112
|
-
return array.sort((a, b) => Time.compareDateTime(a.utc_time, b.utc_time));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
public static dateFrom(timestamp:CompactDateTime):CompactDate {
|
|
116
|
-
const timestampText = timestamp as string;
|
|
117
|
-
if (timestampText.length >= 8 && /^\d{8}.*/.test(timestampText)) {
|
|
118
|
-
return timestampText.substring(0, 8) as CompactDate;
|
|
119
|
-
}
|
|
120
|
-
if (typeof(timestamp) === "string") {
|
|
121
|
-
throw new Error("can\x27t parse date from malformed timestamp: " + timestamp);
|
|
122
|
-
} else {
|
|
123
|
-
throw new Error("can\x27t parse date from malformed timestamp: " + JSON.stringify(timestamp));
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
public static datesBetween(minDate:CompactDate, maxDate:CompactDate):CompactDate[] {
|
|
128
|
-
const dates:CompactDate[] = new Array<CompactDate>();
|
|
129
|
-
let d:string = minDate;
|
|
130
|
-
while (d.localeCompare(minDate) >= 0 && d.localeCompare(maxDate) <= 0) {
|
|
131
|
-
dates.push(d as CompactDate);
|
|
132
|
-
d = dayjs(d, Time.DATE_FORMAT).add(1, 'days').format(Time.DATE_FORMAT) as CompactDate;
|
|
133
|
-
}
|
|
134
|
-
return dates;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
public static plus(timestamp:CompactDateTime, amount:number, units:dayjs.ManipulateType):CompactDateTime {
|
|
138
|
-
const time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
|
|
139
|
-
return time.add(amount, units).format(Time.DATETIME_FORMAT_COMPACT) as CompactDateTime;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
public static todayLocal():CompactDate {
|
|
143
|
-
return Time.dateFrom(Time.local());
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
public static todayUtc():CompactDate {
|
|
147
|
-
return Time.dateFrom(Time.utc());
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public static startOfMonth(date:CompactDate):CompactDate {
|
|
151
|
-
let text = date as string;
|
|
152
|
-
if (text.startsWith("20") && /^\d{8}$/.test(text)) {
|
|
153
|
-
text = text.substring(0, 6) + "01;"
|
|
154
|
-
}
|
|
155
|
-
return text as CompactDate;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
public static local(utc?:CompactDateTime):CompactDateTime {
|
|
159
|
-
let result;
|
|
160
|
-
if (typeof(utc) === "undefined") {
|
|
161
|
-
result = dayjs().format(Time.DATETIME_FORMAT_COMPACT);
|
|
162
|
-
} else {
|
|
163
|
-
const timestamp = dayjs.utc(utc, Time.DATETIME_FORMAT_COMPACT)
|
|
164
|
-
.local()
|
|
165
|
-
;
|
|
166
|
-
result = timestamp.format(Time.DATETIME_FORMAT_COMPACT);
|
|
167
|
-
}
|
|
168
|
-
return result as CompactDateTime;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// TODO: Should this actually be called localFromEpochSeconds? Passing zero
|
|
172
|
-
// returns timestamp that is local-timezone hours offset from UTC epoch time.
|
|
173
|
-
public static utcFromEpochSeconds(seconds:number):CompactDateTime {
|
|
174
|
-
return dayjs.unix(seconds).utc().format(Time.DATETIME_FORMAT_COMPACT) as CompactDateTime;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
public static utc(local?:CompactDateTime):CompactDateTime {
|
|
178
|
-
let result;
|
|
179
|
-
if (typeof(local) === "undefined") {
|
|
180
|
-
result = dayjs.utc().format(Time.DATETIME_FORMAT_COMPACT);
|
|
181
|
-
} else {
|
|
182
|
-
const timestamp = dayjs(local, Time.DATETIME_FORMAT_COMPACT)
|
|
183
|
-
.utc()
|
|
184
|
-
;
|
|
185
|
-
result = timestamp.format(Time.DATETIME_FORMAT_COMPACT);
|
|
186
|
-
}
|
|
187
|
-
return result as CompactDateTime;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
public static format(timestamp:CompactDateTime, pattern:string, isLocal:boolean):CompactDateTime {
|
|
191
|
-
let time = null;
|
|
192
|
-
if (isLocal) {
|
|
193
|
-
time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
|
|
194
|
-
} else {
|
|
195
|
-
time = dayjs.utc(timestamp, Time.DATETIME_FORMAT_COMPACT)
|
|
196
|
-
.local()
|
|
197
|
-
;
|
|
198
|
-
}
|
|
199
|
-
return time.format(pattern) as CompactDateTime;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
public static formatFromDate(date:Date, pattern:string):string {
|
|
203
|
-
const time = dayjs(date);
|
|
204
|
-
return time.format(pattern);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
public static beautify(
|
|
208
|
-
timestamp:CompactDateTime,
|
|
209
|
-
isLocal:boolean = false,
|
|
210
|
-
amount?:number,
|
|
211
|
-
units?:string
|
|
212
|
-
):string {
|
|
213
|
-
const now:any = dayjs();
|
|
214
|
-
let time:any = null;
|
|
215
|
-
if (isLocal) {
|
|
216
|
-
time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
|
|
217
|
-
} else {
|
|
218
|
-
time = dayjs.utc(timestamp, Time.DATETIME_FORMAT_COMPACT)
|
|
219
|
-
.local()
|
|
220
|
-
;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// If the 'num' and 'units' params exist, then use them to manipulate the timestamp
|
|
224
|
-
if (typeof(amount) === "number" && typeof(units) === "string") {
|
|
225
|
-
time = time.add(amount, units);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
let datePart:string = "", timePart:string = "";
|
|
229
|
-
|
|
230
|
-
const timeDayOfYear:number = time.dayOfYear();
|
|
231
|
-
const nowDayOfYear:number = now.dayOfYear();
|
|
232
|
-
const timeYear:number = time.year();
|
|
233
|
-
const nowYear:number = now.year();
|
|
234
|
-
if (nowDayOfYear === timeDayOfYear && nowYear === timeYear) {
|
|
235
|
-
datePart = "Today";
|
|
236
|
-
} else if (nowDayOfYear - timeDayOfYear === 1 && nowYear === timeYear) {
|
|
237
|
-
datePart = "Yesterday";
|
|
238
|
-
} else if (nowDayOfYear === 1 && timeDayOfYear === 365 && timeYear % 4 !== 0 && nowYear - timeYear === 1) {
|
|
239
|
-
datePart = "Yesterday";
|
|
240
|
-
} else if (nowDayOfYear === 1 && timeDayOfYear === 366 && timeYear % 4 === 0 && nowYear - timeYear === 1) {
|
|
241
|
-
datePart = "Yesterday";
|
|
242
|
-
|
|
243
|
-
} else if (timeDayOfYear - nowDayOfYear === 1 && timeYear === nowYear) {
|
|
244
|
-
datePart = "Tomorrow";
|
|
245
|
-
} else if (timeDayOfYear === 1 && nowDayOfYear === 365 && nowYear % 4 !== 0 && timeYear - nowYear === 1) {
|
|
246
|
-
datePart = "Tomorrow";
|
|
247
|
-
} else if (timeDayOfYear === 1 && nowDayOfYear === 366 && nowYear % 4 === 0 && timeYear - nowYear === 1) {
|
|
248
|
-
datePart = "Tomorrow";
|
|
249
|
-
|
|
250
|
-
} else if (Math.abs(now.diff(time, "days")) <= 30) {
|
|
251
|
-
datePart = time.format(Time.DATE_FULLY_BEAUTIFIED);
|
|
252
|
-
} else {
|
|
253
|
-
datePart = time.format(Time.DATE_BEAUTIFIED);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (Math.abs(now.diff(time, "days")) <= 7) {
|
|
257
|
-
timePart = time.format("h:mm a");
|
|
258
|
-
timePart = timePart.replace(" am", "a");
|
|
259
|
-
timePart = timePart.replace(" pm", "p");
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (timePart.length === 0) {
|
|
263
|
-
return datePart;
|
|
264
|
-
} else {
|
|
265
|
-
return datePart + ", " + timePart;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
public static absDiffMinutes(a:CompactDateTime, b:CompactDateTime):number {
|
|
270
|
-
const aMoment = dayjs(a, Time.DATETIME_FORMAT_COMPACT);
|
|
271
|
-
const bMoment = dayjs(b, Time.DATETIME_FORMAT_COMPACT);
|
|
272
|
-
return Math.abs(aMoment.diff(bMoment, 'minutes', true));
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
public static elapsed(timestamp:CompactDateTime, isLocal:boolean):string {
|
|
276
|
-
let time = null;
|
|
277
|
-
if (isLocal) {
|
|
278
|
-
time = dayjs(timestamp, Time.DATETIME_FORMAT_COMPACT);
|
|
279
|
-
} else {
|
|
280
|
-
time = dayjs.utc(timestamp, Time.DATETIME_FORMAT_COMPACT)
|
|
281
|
-
.local()
|
|
282
|
-
;
|
|
283
|
-
}
|
|
284
|
-
const difference = dayjs().diff(time, 'seconds');
|
|
285
|
-
if (difference < 60) {
|
|
286
|
-
return difference === 1 ? "1 second ago" : difference + " seconds ago";
|
|
287
|
-
} else if (difference < 3 * 60) {
|
|
288
|
-
const minutes = Math.floor(difference / 60);
|
|
289
|
-
const seconds = Math.floor(difference % 60);
|
|
290
|
-
const minText = minutes === 1 ? "1 minute" : minutes + " minutes";
|
|
291
|
-
const secText = seconds === 1 ? "1 second" : seconds + " seconds";
|
|
292
|
-
return minText + ", " + secText + " ago";
|
|
293
|
-
} else if (difference < 60 * 60) {
|
|
294
|
-
const minutes = Math.round(difference / 60);
|
|
295
|
-
return "about " + minutes + " minutes ago";
|
|
296
|
-
} else if (difference < 24 * 60 * 60) {
|
|
297
|
-
const hours = Math.round(difference / (60 * 60));
|
|
298
|
-
return "about " + hours + " hours ago";
|
|
299
|
-
} else {
|
|
300
|
-
return Time.beautify(timestamp, isLocal);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
public static timeUntil(utcTime:CompactDateTime, units:dayjs.UnitType):number {
|
|
305
|
-
return - Time.timeSince(utcTime, units);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
public static timeSince(utcTime:CompactDateTime, units:dayjs.UnitType):number {
|
|
309
|
-
const then = dayjs.utc(utcTime, Time.DATETIME_FORMAT_COMPACT);
|
|
310
|
-
const now = dayjs().utc();
|
|
311
|
-
return now.diff(then, units, true);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
public static currentYear():string {
|
|
315
|
-
return dayjs().format(Time.YEAR_ONLY);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
public static humanizedDatePacific(epochSeconds:number):string {
|
|
319
|
-
return dayjs.unix(epochSeconds).tz("America/Los_Angeles").format("MMMM D, YYYY");
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// TODO: Should this actually expect seconds instead of milliseconds?
|
|
323
|
-
public static humanizedTimePacific(epochSeconds:number):string {
|
|
324
|
-
return dayjs(epochSeconds).tz("America/Los_Angeles").format("h:mm:ss a") + " (Pacific)";
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { Doc } from '@shaxpir/sharedb/lib/client';
|
|
2
|
-
import { ShareSync, ShareSyncFactory } from '../../repo/ShareSync';
|
|
3
|
-
import { CachingHasher } from '../../util/CachingHasher';
|
|
4
|
-
import { BatchOperation } from '../markup/Operation';
|
|
5
|
-
import { Content, ContentId, ContentMeta } from "./Content";
|
|
6
|
-
import { ContentKind } from './ContentKind';
|
|
7
|
-
import { MultiClock, MultiTime } from "./Time";
|
|
8
|
-
|
|
9
|
-
export interface UserPayload {
|
|
10
|
-
email:string;
|
|
11
|
-
phone:string;
|
|
12
|
-
force_password_reset:boolean;
|
|
13
|
-
pass_md5_salt_md5:string;
|
|
14
|
-
salt:string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface ModernUserBody {
|
|
18
|
-
meta:ContentMeta;
|
|
19
|
-
payload:UserPayload;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class ModernUser extends Content {
|
|
23
|
-
|
|
24
|
-
public static readonly MIN_PASSWORD_LENGTH:number = 8;
|
|
25
|
-
|
|
26
|
-
constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
|
|
27
|
-
super(doc, shouldAcquire, shareSync);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public get payload():UserPayload {
|
|
31
|
-
return this.doc.data.payload as UserPayload;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public static create(
|
|
35
|
-
userId:ContentId,
|
|
36
|
-
payload:UserPayload,
|
|
37
|
-
createdAt?:MultiTime
|
|
38
|
-
):ModernUser {
|
|
39
|
-
// User creation always happens in UTC time
|
|
40
|
-
const now = MultiClock.utcNow();
|
|
41
|
-
// If the createdAt time is not provided, use the current time
|
|
42
|
-
createdAt ??= now;
|
|
43
|
-
return ShareSyncFactory.get().createContent(
|
|
44
|
-
{
|
|
45
|
-
meta : {
|
|
46
|
-
ref : userId,
|
|
47
|
-
is_head : true,
|
|
48
|
-
kind : ContentKind.USER,
|
|
49
|
-
id : userId,
|
|
50
|
-
owner : userId,
|
|
51
|
-
created_at : createdAt,
|
|
52
|
-
updated_at : now
|
|
53
|
-
},
|
|
54
|
-
payload : payload
|
|
55
|
-
}
|
|
56
|
-
) as ModernUser;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public get email():string {
|
|
60
|
-
return this.payload.email;
|
|
61
|
-
}
|
|
62
|
-
public setEmail(value:string) {
|
|
63
|
-
if (this.email != value) {
|
|
64
|
-
const batch = new BatchOperation(this);
|
|
65
|
-
batch.setPathValue([ 'payload', 'email' ] , value);
|
|
66
|
-
batch.commit();
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public get forcePasswordReset():boolean {
|
|
71
|
-
return this.payload.force_password_reset;
|
|
72
|
-
}
|
|
73
|
-
public setForcePasswordReset(value:boolean) {
|
|
74
|
-
if (this.forcePasswordReset != value) {
|
|
75
|
-
const batch = new BatchOperation(this);
|
|
76
|
-
batch.setPathValue([ 'payload', 'force_password_reset' ] , value);
|
|
77
|
-
batch.commit();
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
public get passMd5SaltMd5():string {
|
|
82
|
-
return this.payload.pass_md5_salt_md5;
|
|
83
|
-
}
|
|
84
|
-
public setPassMd5SaltMd5(value:string) {
|
|
85
|
-
if (this.passMd5SaltMd5 != value) {
|
|
86
|
-
const batch = new BatchOperation(this);
|
|
87
|
-
batch.setPathValue([ 'payload', 'pass_md5_salt_md5' ] , value);
|
|
88
|
-
batch.commit();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public get salt():string {
|
|
93
|
-
return this.payload.salt;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
public hasValidPassword(password:string):boolean {
|
|
97
|
-
const passMd5 = ModernUser.makePassMd5(password);
|
|
98
|
-
return this.hasValidPassMd5(passMd5);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
public hasValidPassMd5(passMd5:string):boolean {
|
|
102
|
-
return ModernUser.isValidPassMd5(passMd5, this.payload);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
public static makePassMd5SaltMd5(salt:string, password:string):string {
|
|
106
|
-
const passMd5 = ModernUser.makePassMd5(password);
|
|
107
|
-
return ModernUser.addSaltToPassMd5(salt, passMd5);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
public static addSaltToPassMd5(salt:string, passMd5:string):string {
|
|
111
|
-
return CachingHasher.makeMd5Hash(passMd5 + salt);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public static makePassMd5(password:string):string {
|
|
115
|
-
return CachingHasher.makeMd5Hash(password);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public static isValidPassMd5(passMd5:string, userPayload:UserPayload):boolean {
|
|
119
|
-
const passMd5SaltMd5 = ModernUser.addSaltToPassMd5(userPayload.salt, passMd5);
|
|
120
|
-
return passMd5SaltMd5 === userPayload.pass_md5_salt_md5;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
public static async findByEmail(email:string):Promise<ModernUser[]> {
|
|
124
|
-
const shareSync = ShareSyncFactory.get();
|
|
125
|
-
return shareSync.findAndAcquire(
|
|
126
|
-
ContentKind.USER, { "payload.email" : email }
|
|
127
|
-
) as Promise<ModernUser[]>;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { AttributeMap, Op as DeltaOp } from "@shaxpir/quill-delta";
|
|
2
|
-
import { Struct } from "../../util/Struct";
|
|
3
|
-
|
|
4
|
-
export class DeltaOpsModel {
|
|
5
|
-
|
|
6
|
-
private static readonly BLOCK_ATTR_KEYS:any = {
|
|
7
|
-
"align" : true,
|
|
8
|
-
"blockquote" : true,
|
|
9
|
-
"header" : true,
|
|
10
|
-
"indent" : true,
|
|
11
|
-
"list" : true,
|
|
12
|
-
"caption" : true,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
public static splitOnNewlines(ops:DeltaOp[]):DeltaOp[] {
|
|
16
|
-
let newOps:DeltaOp[] = [];
|
|
17
|
-
for (let i:number = 0, iLen:number = ops.length; i < iLen; i++) {
|
|
18
|
-
let op:DeltaOp = ops[i];
|
|
19
|
-
let text:any = "\n";
|
|
20
|
-
let embed:AttributeMap = null;
|
|
21
|
-
let blockAttr:AttributeMap = null;
|
|
22
|
-
let rangeAttr:AttributeMap = null;
|
|
23
|
-
if (typeof(op.insert) === "string") {
|
|
24
|
-
text = op.insert;
|
|
25
|
-
if (op.hasOwnProperty("attributes")) {
|
|
26
|
-
for (let key in op.attributes) {
|
|
27
|
-
let val:any = (op.attributes as any)[key];
|
|
28
|
-
if (DeltaOpsModel.BLOCK_ATTR_KEYS.hasOwnProperty(key)) {
|
|
29
|
-
if (blockAttr == null) {
|
|
30
|
-
blockAttr = {};
|
|
31
|
-
}
|
|
32
|
-
(blockAttr as any)[key] = val;
|
|
33
|
-
} else {
|
|
34
|
-
if (rangeAttr == null) {
|
|
35
|
-
rangeAttr = {};
|
|
36
|
-
}
|
|
37
|
-
(rangeAttr as any)[key] = val;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
} else if (op.hasOwnProperty("insert")) {
|
|
43
|
-
embed = Struct.clone(op.insert);
|
|
44
|
-
}
|
|
45
|
-
let parts:string[] = text.split(/(\n)/g);
|
|
46
|
-
let nonEmptyParts:string[] = [];
|
|
47
|
-
for (let j:number = 0, jLen:number = parts.length; j < jLen; j++) {
|
|
48
|
-
let part:string = parts[j];
|
|
49
|
-
if (part.length > 0) {
|
|
50
|
-
nonEmptyParts.push(part);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
parts = nonEmptyParts;
|
|
54
|
-
for (let j:number = 0, jLen:number = parts.length, jLast = jLen - 1; j < jLen; j++) {
|
|
55
|
-
let part:string = parts[j];
|
|
56
|
-
let newOp:DeltaOp = { "insert" : part, "attributes" : {} };
|
|
57
|
-
if (embed !== null) {
|
|
58
|
-
newOp.insert = embed;
|
|
59
|
-
} else if (part === "\n") {
|
|
60
|
-
if (j === jLast && blockAttr != null) {
|
|
61
|
-
newOp.attributes = blockAttr;
|
|
62
|
-
}
|
|
63
|
-
} else if (rangeAttr != null) {
|
|
64
|
-
newOp.attributes = rangeAttr;
|
|
65
|
-
}
|
|
66
|
-
newOps.push(newOp);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return newOps;
|
|
70
|
-
}
|
|
71
|
-
}
|
package/lib/util/Base62.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export class Base62 {
|
|
2
|
-
|
|
3
|
-
public static readonly CHARS:string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
4
|
-
|
|
5
|
-
public static encode(value:number, padToLength:number = 1):string {
|
|
6
|
-
if (value < 0) {
|
|
7
|
-
throw new Error("number out of range: " + value);
|
|
8
|
-
}
|
|
9
|
-
let result:string = "";
|
|
10
|
-
while (value > 0) {
|
|
11
|
-
let index:number = value % 62;
|
|
12
|
-
let digit:string = Base62.CHARS.charAt(index);
|
|
13
|
-
result = digit + result;
|
|
14
|
-
value = Math.floor(value / 62);
|
|
15
|
-
}
|
|
16
|
-
while (result.length < padToLength) {
|
|
17
|
-
result = "0" + result;
|
|
18
|
-
}
|
|
19
|
-
return result;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public static decode(value:string) {
|
|
23
|
-
let result = 0;
|
|
24
|
-
for (let i:number = 0, len:number = value.length; i < len; i++) {
|
|
25
|
-
let c:string = value.charAt(i);
|
|
26
|
-
result *= 62;
|
|
27
|
-
result += Base62.CHARS.indexOf(c);
|
|
28
|
-
}
|
|
29
|
-
return result;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public static fromHexHash(hex:string, length:number):string {
|
|
33
|
-
// NOTE: the crazy one-liner below is equivalent to this commented code...
|
|
34
|
-
// const b64 = Buffer.from(hex, 'hex').toString('base64');
|
|
35
|
-
// ...but it doesn't require Buffer class, which is only available in Node.js, not in browsers.
|
|
36
|
-
// This one-liner came from stackoverflow: https://stackoverflow.com/questions/23190056/hex-to-base64-converter-for-javascript
|
|
37
|
-
const b64 = btoa(hex.match(/\w{2}/g).map((a) => { return String.fromCharCode(parseInt(a, 16)) }).join(""));
|
|
38
|
-
let alphanum = b64.replace(/[^a-zA-Z0-9]/g, "");
|
|
39
|
-
if (alphanum.length < length) {
|
|
40
|
-
alphanum = alphanum.padEnd(length, "0");
|
|
41
|
-
} else if (alphanum.length > length) {
|
|
42
|
-
alphanum = alphanum.substring(0, length);
|
|
43
|
-
}
|
|
44
|
-
return alphanum;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import md5 from 'md5';
|
|
2
|
-
import { ContentId } from '../models/content/Content';
|
|
3
|
-
import { Base62 } from './Base62';
|
|
4
|
-
|
|
5
|
-
export class CachingHasher {
|
|
6
|
-
|
|
7
|
-
private static readonly MAX_SIZE:number = 1000;
|
|
8
|
-
|
|
9
|
-
private static md5HashLookup:Map<string, string> = new Map<string, string>();
|
|
10
|
-
private static contentIdLookup:Map<string, ContentId> = new Map<string, ContentId>();
|
|
11
|
-
|
|
12
|
-
public static makeMd5Hash(text:string):string {
|
|
13
|
-
if (CachingHasher.md5HashLookup.size > CachingHasher.MAX_SIZE) {
|
|
14
|
-
CachingHasher.md5HashLookup.clear();
|
|
15
|
-
}
|
|
16
|
-
if (CachingHasher.md5HashLookup.has(text)) {
|
|
17
|
-
return CachingHasher.md5HashLookup.get(text);
|
|
18
|
-
} else {
|
|
19
|
-
const rawHash = md5(text);
|
|
20
|
-
this.md5HashLookup.set(text, rawHash);
|
|
21
|
-
return rawHash;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public static makeMd5ContentId(text:string):ContentId {
|
|
26
|
-
if (CachingHasher.contentIdLookup.size > CachingHasher.MAX_SIZE) {
|
|
27
|
-
CachingHasher.contentIdLookup.clear();
|
|
28
|
-
}
|
|
29
|
-
if (CachingHasher.contentIdLookup.has(text)) {
|
|
30
|
-
return CachingHasher.contentIdLookup.get(text);
|
|
31
|
-
} else {
|
|
32
|
-
const rawHash = md5(text);
|
|
33
|
-
const asBase62 = Base62.fromHexHash(rawHash, 16) as ContentId;
|
|
34
|
-
this.contentIdLookup.set(text, asBase62);
|
|
35
|
-
return asBase62;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|