6
6
import 'dart:convert' ;
7
7
import "package:http/http.dart" as http;
8
8
9
- UserScriptModel userScriptModelFromJson (String str) => UserScriptModel .fromJson (json.decode (str));
9
+ UserScriptModel userScriptModelFromJson (String str) =>
10
+ UserScriptModel .fromJson (json.decode (str));
10
11
11
- String userScriptModelToJson (UserScriptModel data) => json.encode (data.toJson ());
12
+ String userScriptModelToJson (UserScriptModel data) =>
13
+ json.encode (data.toJson ());
12
14
13
15
enum UserScriptTime { start, end }
14
16
@@ -32,7 +34,8 @@ class UserScriptModel {
32
34
this .time = UserScriptTime .end,
33
35
this .url,
34
36
this .updateStatus = UserScriptUpdateStatus .noRemote,
35
- required this .isExample});
37
+ required this .isExample,
38
+ this .grants = const []});
36
39
37
40
bool enabled;
38
41
List <String > matches;
@@ -44,23 +47,31 @@ class UserScriptModel {
44
47
String ? url;
45
48
UserScriptUpdateStatus updateStatus;
46
49
bool isExample;
50
+ List <String > grants;
47
51
48
52
factory UserScriptModel .fromJson (Map <String , dynamic > json) {
49
53
// First check if is old model
50
54
if (json["exampleCode" ] is int ) {
51
55
final bool enabled = json["enabled" ] is bool ? json["enabled" ] : true ;
52
56
final String source = json["source" ] is String ? json["source" ] : "" ;
53
- final List <String > matches = json["urls" ] is List <dynamic > ? json["urls" ].cast <String >() : tryGetMatches (source);
57
+ final List <String > matches = json["urls" ] is List <dynamic >
58
+ ? json["urls" ].cast <String >()
59
+ : tryGetMatches (source);
54
60
final String name = json["name" ] is String ? json["name" ] : "Unknown" ;
55
- final String version = json["version" ] is String ? json["version" ] : tryGetVersion (source) ?? "0.0.0" ;
61
+ final String version = json["version" ] is String
62
+ ? json["version" ]
63
+ : tryGetVersion (source) ?? "0.0.0" ;
56
64
final bool edited = json["edited" ] is bool ? json["edited" ] : false ;
57
- final UserScriptTime time = json["time" ] == "start" ? UserScriptTime .start : UserScriptTime .end;
58
- final bool isExample = json["isExample" ] ?? (json["exampleCode" ] ?? 0 ) > 0 ;
65
+ final UserScriptTime time =
66
+ json["time" ] == "start" ? UserScriptTime .start : UserScriptTime .end;
67
+ final bool isExample =
68
+ json["isExample" ] ?? (json["exampleCode" ] ?? 0 ) > 0 ;
59
69
final url = json["url" ] is String
60
70
? json["url" ]
61
- : tryGetUrl (json["source" ]) ?? (isExample ? exampleScriptURLs[json["exampleCode" ] - 1 ] : null );
62
- final updateStatus =
63
- UserScriptUpdateStatus .values.byName (json["updateStatus" ] ?? (url is String ? "upToDate" : "noRemote" ));
71
+ : tryGetUrl (json["source" ]) ??
72
+ (isExample ? exampleScriptURLs[json["exampleCode" ] - 1 ] : null );
73
+ final updateStatus = UserScriptUpdateStatus .values.byName (
74
+ json["updateStatus" ] ?? (url is String ? "upToDate" : "noRemote" ));
64
75
return UserScriptModel (
65
76
enabled: enabled,
66
77
matches: matches,
@@ -72,19 +83,27 @@ class UserScriptModel {
72
83
url: url,
73
84
updateStatus: updateStatus,
74
85
isExample: isExample,
86
+ grants: < String > [] // Old model does not have grants
75
87
);
76
88
} else {
77
89
return UserScriptModel (
78
90
enabled: json["enabled" ],
79
- matches: json["matches" ] is List <dynamic > ? json["matches" ].cast <String >() : const ["*" ],
91
+ matches: json["matches" ] is List <dynamic >
92
+ ? json["matches" ].cast <String >()
93
+ : const ["*" ],
80
94
name: json["name" ],
81
95
version: json["version" ],
82
96
edited: json["edited" ],
83
97
source: json["source" ],
84
- time: json["time" ] == "start" ? UserScriptTime .start : UserScriptTime .end,
98
+ time:
99
+ json["time" ] == "start" ? UserScriptTime .start : UserScriptTime .end,
85
100
url: json["url" ],
86
- updateStatus: UserScriptUpdateStatus .values.byName (json["updateStatus" ] ?? "noRemote" ),
101
+ updateStatus: UserScriptUpdateStatus .values
102
+ .byName (json["updateStatus" ] ?? "noRemote" ),
87
103
isExample: json["isExample" ] ?? (json["exampleCode" ] ?? 0 ) > 0 ,
104
+ grants: json["grants" ] is List <dynamic >
105
+ ? json["grants" ].cast <String >()
106
+ : const [],
88
107
);
89
108
}
90
109
}
@@ -110,12 +129,17 @@ class UserScriptModel {
110
129
matches: metaMap["matches" ] ?? ["*" ],
111
130
url: url ?? metaMap["downloadURL" ],
112
131
updateStatus: updateStatus,
113
- time: time ?? (metaMap["injectionTime" ] == "document-start" ? UserScriptTime .start : UserScriptTime .end),
132
+ time: time ??
133
+ (metaMap["injectionTime" ] == "document-start"
134
+ ? UserScriptTime .start
135
+ : UserScriptTime .end),
114
136
isExample: isExample ?? false ,
137
+ grants: metaMap["grant" ] ?? < String > [],
115
138
);
116
139
}
117
140
118
- static Future <({bool success, String message, UserScriptModel ? model})> fromURL (String url, {bool ? isExample}) async {
141
+ static Future <({bool success, String message, UserScriptModel ? model})>
142
+ fromURL (String url, {bool ? isExample}) async {
119
143
try {
120
144
final response = await http.get (Uri .parse (url));
121
145
if (response.statusCode == 200 ) {
@@ -124,7 +148,9 @@ class UserScriptModel {
124
148
success: true ,
125
149
message: "Success" ,
126
150
model: UserScriptModel .fromMetaMap (metaMap,
127
- url: url, updateStatus: UserScriptUpdateStatus .upToDate, isExample: isExample ?? false ),
151
+ url: url,
152
+ updateStatus: UserScriptUpdateStatus .upToDate,
153
+ isExample: isExample ?? false ),
128
154
);
129
155
} else {
130
156
return (
@@ -151,7 +177,8 @@ class UserScriptModel {
151
177
final List <String > version1List = version1.split ("." );
152
178
final List <String > version2List = version2.split ("." );
153
179
for (int i = 0 ; i < version1List.length; i++ ) {
154
- if (version2List.length <= i || int .parse (version1List[i]) > int .parse (version2List[i])) {
180
+ if (version2List.length <= i ||
181
+ int .parse (version1List[i]) > int .parse (version2List[i])) {
155
182
return true ;
156
183
}
157
184
}
@@ -168,18 +195,22 @@ class UserScriptModel {
168
195
"url" : url,
169
196
"updateStatus" : updateStatus.name,
170
197
"isExample" : isExample,
198
+ "grants" : grants,
171
199
"time" : time == UserScriptTime .start ? "start" : "end" ,
172
200
};
173
201
174
202
static Map <String , dynamic > parseHeader (String source) {
175
203
// Thanks to [ViolentMonkey](https://github.com/violentmonkey/violentmonkey) for the following two regexes
176
- String ? meta =
177
- RegExp (r"((?:^|\n)\s*\/\/\x20==UserScript==)([\s\S]*?\n)\s*\/\/\x20==\/UserScript==|$" ).stringMatch (source);
204
+ String ? meta = RegExp (
205
+ r"((?:^|\n)\s*\/\/\x20==UserScript==)([\s\S]*?\n)\s*\/\/\x20==\/UserScript==|$" )
206
+ .stringMatch (source);
178
207
if (meta == null || meta.isEmpty) {
179
208
throw Exception ("No header found in userscript." );
180
209
}
181
- Iterable <RegExpMatch > metaMatches = RegExp (r"^(?:^|\n)\s*\/\/\x20(@\S+)(.*)$" , multiLine: true ).allMatches (meta);
182
- Map <String , dynamic > metaMap = {"@match" : < String > []};
210
+ Iterable <RegExpMatch > metaMatches =
211
+ RegExp (r"^(?:^|\n)\s*\/\/\x20(@\S+)(.*)$" , multiLine: true )
212
+ .allMatches (meta);
213
+ Map <String , dynamic > metaMap = {"@match" : < String > [], "@grant" : < String > []};
183
214
for (final match in metaMatches) {
184
215
if (match.groupCount < 2 ) {
185
216
continue ;
@@ -189,6 +220,11 @@ class UserScriptModel {
189
220
}
190
221
if (match.group (1 )? .toLowerCase () == "@match" ) {
191
222
metaMap["@match" ].add (match.group (2 )! .trim ());
223
+ } else if (match.group (1 )? .toLowerCase () == "@grant" ) {
224
+ if (match.group (2 )? .trim () == "none" ) {
225
+ continue ;
226
+ }
227
+ metaMap["@grant" ].add (match.group (2 )! .trim ());
192
228
} else {
193
229
metaMap[match.group (1 )! .trim ().toLowerCase ()] = match.group (2 )! .trim ();
194
230
}
@@ -201,14 +237,16 @@ class UserScriptModel {
201
237
"injectionTime" : metaMap["@run-at" ] ?? "document-end" ,
202
238
"downloadURL" : metaMap["@downloadurl" ],
203
239
"updateURL" : metaMap["@updateurl" ],
240
+ "grant" : metaMap["@grant" ],
204
241
"source" : source,
205
242
};
206
243
}
207
244
208
245
shouldInject (String url, [UserScriptTime ? time]) =>
209
246
enabled &&
210
247
(this .time == time || time == null ) &&
211
- matches.any ((match) => (match == "*" || url.contains (match.replaceAll ("*" , "" ))));
248
+ matches.any (
249
+ (match) => (match == "*" || url.contains (match.replaceAll ("*" , "" ))));
212
250
213
251
void update ({
214
252
bool ? enabled,
@@ -219,6 +257,7 @@ class UserScriptModel {
219
257
String ? source,
220
258
UserScriptTime ? time,
221
259
String ? url,
260
+ List <String >? grants,
222
261
required UserScriptUpdateStatus updateStatus,
223
262
}) {
224
263
if (source != null ) {
@@ -231,11 +270,16 @@ class UserScriptModel {
231
270
if (metaMap["matches" ] != null ) {
232
271
this .matches = metaMap["matches" ];
233
272
}
273
+ if (metaMap["grant" ] != null ) {
274
+ this .grants = metaMap["grant" ];
275
+ }
234
276
if (metaMap["name" ] != null ) {
235
277
this .name = metaMap["name" ];
236
278
}
237
279
if (metaMap["injectionTime" ] != null ) {
238
- this .time = metaMap["injectionTime" ] == "document-start" ? UserScriptTime .start : UserScriptTime .end;
280
+ this .time = metaMap["injectionTime" ] == "document-start"
281
+ ? UserScriptTime .start
282
+ : UserScriptTime .end;
239
283
}
240
284
if (metaMap["downloadURL" ] != null ) {
241
285
this .url = metaMap["downloadURL" ];
@@ -302,6 +346,15 @@ class UserScriptModel {
302
346
}
303
347
}
304
348
349
+ static List <String > tryGetGrants (String source) {
350
+ try {
351
+ final metaMap = UserScriptModel .parseHeader (source);
352
+ return metaMap["grant" ] ?? < String > [];
353
+ } catch (e) {
354
+ return const [];
355
+ }
356
+ }
357
+
305
358
static String ? tryGetUrl (String source) {
306
359
try {
307
360
final metaMap = UserScriptModel .parseHeader (source);
0 commit comments