~ubuntu-branches/debian/stretch/downthemall/stretch

« back to all changes in this revision

Viewing changes to modules/manager/matcher.js

  • Committer: Package Import Robot
  • Author(s): Michael Meskes
  • Date: 2016-09-21 13:33:55 UTC
  • mto: This revision was merged to the branch mainline in revision 11.
  • Revision ID: package-import@ubuntu.com-20160921133355-ylst3mzzo82mghyw
Tags: upstream-3.0.6
ImportĀ upstreamĀ versionĀ 3.0.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This Source Code Form is subject to the terms of the Mozilla Public
 
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 
3
 * You can obtain one at http://mozilla.org/MPL/2.0/ */
 
4
"use strict";
 
5
 
 
6
const {FilterManager} = require("support/filtermanager");
 
7
const constants = require("constants");
 
8
const {COMPLETE, FINISHING} = constants;
 
9
 
 
10
const {
 
11
        StringBundles,
 
12
        filterInSitu
 
13
} = require("utils");
 
14
 
 
15
const _ = (function(global) {
 
16
        let bundles = new StringBundles([
 
17
                "chrome://dta/locale/common.properties",
 
18
                "chrome://dta/locale/manager.properties"
 
19
                ]);
 
20
        return function() {
 
21
                if (arguments.length === 1) {
 
22
                        return bundles.getString(arguments[0]);
 
23
                }
 
24
                return bundles.getFormattedString.apply(bundles, arguments);
 
25
        };
 
26
})(this);
 
27
 
 
28
const TextMatch = {
 
29
        get name() {
 
30
                return 'textmatch';
 
31
        },
 
32
        getMatcher: function(params) {
 
33
                params = new RegExp(
 
34
                        params.map(e => e
 
35
                                .replace(/^\s+|\s+$/g, '')
 
36
                                .replace(/([/{}()\[\]\\^$.])/g, "\\$1")
 
37
                                .replace(/\*/g, ".*")
 
38
                                .replace(/\?/g, '.')
 
39
                        ).join('|'),
 
40
                        'i'
 
41
                );
 
42
                return function TextMatcher(d) {
 
43
                        return params.test(
 
44
                                [d.urlManager.usable, d.description, d.fileName, d.destinationName].join(' ')
 
45
                        );
 
46
                };
 
47
        }
 
48
};
 
49
 
 
50
const FilterMatch = {
 
51
        get name() {
 
52
                return 'filtermatch';
 
53
        },
 
54
        getItems: function*() {
 
55
                for (let f of FilterManager.enumAll()) {
 
56
                        if (f.id === "deffilter-all") {
 
57
                                continue;
 
58
                        }
 
59
                        yield {
 
60
                                label: f.label,
 
61
                                param: f.id
 
62
                        };
 
63
                }
 
64
        },
 
65
        getMatcher: function(params) {
 
66
                let filters = [];
 
67
                for (let id of params) {
 
68
                        try {
 
69
                                filters.push(FilterManager.getFilter(id));
 
70
                        }
 
71
                        catch (ex) {
 
72
                                log(LOG_ERROR, "not a filter: " + id, ex);
 
73
                                // no op; might have changed
 
74
                        }
 
75
                }
 
76
                if (!filters.length) {
 
77
                        log(LOG_DEBUG, "No filters available for: " + params);
 
78
                        return null;
 
79
                }
 
80
                let _m = FilterManager.getMatcherFor(filters);
 
81
                return function FilterMatcher(d) { return _m(d.urlManager.spec); };
 
82
        }
 
83
};
 
84
 
 
85
const PathMatch = {
 
86
        get name() {
 
87
                return 'pathmatch';
 
88
        },
 
89
        getItems: function*(downloads) {
 
90
                let paths = filterInSitu(
 
91
                        downloads.map(d => d.destinationPath),
 
92
                        function(e) {
 
93
                                return !((e in this) || (this[e] = null));
 
94
                        },
 
95
                        {});
 
96
                paths.sort();
 
97
                for (let p of paths) {
 
98
                        yield {
 
99
                                label: p,
 
100
                                param: btoa(p)
 
101
                        };
 
102
                }
 
103
        },
 
104
        getMatcher: function(params) {
 
105
                params = params.map(e => atob(e));
 
106
                return function PathMatcher(d) {
 
107
                        return params.indexOf(d.destinationPath) >= 0;
 
108
                };
 
109
        }
 
110
};
 
111
 
 
112
const RemainderMatch = {
 
113
        get name() {
 
114
                return 'remainder';
 
115
        },
 
116
        getItems: function*() {
 
117
                yield {
 
118
                        label: _('soonfinished'),
 
119
                        param: '120',
 
120
                        radio: 'est'
 
121
                };
 
122
                yield {
 
123
                        label: _('next10mins'),
 
124
                        param: '600',
 
125
                        radio: 'est'
 
126
                };
 
127
                yield {
 
128
                        label: _('nexthour'),
 
129
                        param: '3600',
 
130
                        radio: 'est'
 
131
                };
 
132
                yield {
 
133
                        label: _('next6hours'),
 
134
                        param: '21600',
 
135
                        radio: 'est'
 
136
                };
 
137
                yield {
 
138
                        label: _('nextday'),
 
139
                        param: '86400',
 
140
                        radio: 'est'
 
141
                };
 
142
        },
 
143
        getMatcher: function(params) {
 
144
                let state = 0;
 
145
                let est = 0;
 
146
                for (let p of params) {
 
147
                        let n = parseInt(p, 10);
 
148
                        if (isFinite(n) && n > est) {
 
149
                                est = n;
 
150
                        }
 
151
                }
 
152
                return function RemainderMatcher(d) { return d.estimated <= est; };
 
153
        }
 
154
};
 
155
const StatusMatch = {
 
156
        get name() {
 
157
                return 'statusmatch';
 
158
        },
 
159
        getItems: function*() {
 
160
                for (let s of ['QUEUED', 'PAUSED', 'RUNNING', 'COMPLETE', 'CANCELED']) {
 
161
                        yield {
 
162
                                label: _(s.toLowerCase()),
 
163
                                param: s
 
164
                        };
 
165
                }
 
166
        },
 
167
        getMatcher: function(params) {
 
168
                let state = params.reduce((p, c) =>  p | constants[c], 0);
 
169
                if (state & COMPLETE) {
 
170
                        state |= FINISHING;
 
171
                }
 
172
                return function StatusMatcher(d) { return d.state & state; };
 
173
        }
 
174
};
 
175
requireJoined(StatusMatch, "constants");
 
176
 
 
177
const SIZES = [
 
178
        ['-0', _('unknown')],
 
179
        ['0-1024', _('verysmallfiles')],
 
180
        ['1024-1048576', _('smallfiles')],
 
181
        ['1048576-262144000', _('mediumfiles')],
 
182
        ['262144000-524288000', _('largefiles')],
 
183
        ['524288000-4294967296', _('verylargefiles')],
 
184
        ['4294967296-', _('hugefiles')]
 
185
];
 
186
const SizeMatch = {
 
187
        get name() {
 
188
                return 'sizematch';
 
189
        },
 
190
        getItems: function*() {
 
191
                for (let [p, l] of SIZES) {
 
192
                        yield {
 
193
                                label: l,
 
194
                                param: p
 
195
                        };
 
196
                }
 
197
        },
 
198
        getMatcher: function(params) {
 
199
                function parseInt10(v) {
 
200
                        return parseInt(v, 10);
 
201
                }
 
202
                let ranges = [];
 
203
                for (let x of params) {
 
204
                        let [l,h] = x.split('-').map(parseInt10);
 
205
                        ranges.push({low: l, high: h});
 
206
                }
 
207
                if (!ranges.length) {
 
208
                        return null;
 
209
                }
 
210
                // combine ranges
 
211
                for (let i = ranges.length - 2; i >= 0; --i) {
 
212
                        if (ranges[i].high === ranges[i+1].low) {
 
213
                                ranges[i].high = ranges[i+1].high;
 
214
                                ranges.splice(i+1,1);
 
215
                        }
 
216
                }
 
217
 
 
218
                // map to fastpath functions
 
219
                ranges = ranges.map(function(r) {
 
220
                        let low = r.low;
 
221
                        let high = r.high;
 
222
                        if (!isFinite(low)) {
 
223
                                return function(size) { return size <= high; };
 
224
                        }
 
225
                        if (!isFinite(high)) {
 
226
                                return function(size) { return size > low; };
 
227
                        }
 
228
                        return function(size) { return size > low && size <= high; };
 
229
                });
 
230
 
 
231
                if (ranges.length === 1) {
 
232
                        let rf = ranges.shift();
 
233
                        return function(d) { return rf(d.totalSize); };
 
234
                }
 
235
                return function(d) {
 
236
                        return ranges.some(function(rf) { return rf(d.totalSize); });
 
237
                };
 
238
        }
 
239
};
 
240
 
 
241
const DomainMatch = {
 
242
        get name() {
 
243
                return 'domainmatch';
 
244
        },
 
245
        getItems: function*(downloads) {
 
246
                let domains = filterInSitu(
 
247
                                downloads.map(d => d.urlManager.domain),
 
248
                                function(e) {
 
249
                                        return !((e in this) || (this[e] = null));
 
250
                                },
 
251
                                {});
 
252
                domains.sort();
 
253
                for (let p of domains) {
 
254
                        yield {
 
255
                                label: p,
 
256
                                param: btoa(p)
 
257
                        };
 
258
                }
 
259
        },
 
260
        getMatcher: function(params) {
 
261
                params = params.map(e => atob(e));
 
262
                return function(d) { return params.indexOf(d.urlManager.domain) >= 0; };
 
263
        }
 
264
};
 
265
 
 
266
function MatcherTee(a, b) {
 
267
        this.a = a;
 
268
        this.b = b;
 
269
}
 
270
MatcherTee.prototype = {
 
271
        get name() {
 
272
                return this.a + ";" + this.b;
 
273
        },
 
274
        getItems: function*(downloads) {
 
275
                for (let a of this.a.getItems(downloads)) {
 
276
                        a.param = "a:" + a.param;
 
277
                        yield a;
 
278
                }
 
279
                yield {label: '-'};
 
280
                for (let b of this.b.getItems(downloads)) {
 
281
                        b.param = "b:" + b.param;
 
282
                        yield b;
 
283
                }
 
284
        },
 
285
        getMatcher: function(params) {
 
286
                let a = [], b = [];
 
287
                params.forEach(function(p) {
 
288
                        return this[p[0]].push(p.substr(2));
 
289
                }, {a:a, b:b});
 
290
                if (a.length && !b.length) {
 
291
                        return this.a.getMatcher(a);
 
292
                }
 
293
                if (!a.length && b.length) {
 
294
                        return this.b.getMatcher(b);
 
295
                }
 
296
                if (!a.length && !b.length) {
 
297
                        return null;
 
298
                }
 
299
                a = this.a.getMatcher(a);
 
300
                b = this.b.getMatcher(b);
 
301
                return function(d) { return a(d) && b(d); };
 
302
        }
 
303
};
 
304
 
 
305
function Matcher() {
 
306
        this._matchers = [];
 
307
        this._matchersLength = 0;
 
308
}
 
309
Matcher.prototype = {
 
310
        _available: {
 
311
                'textmatch': TextMatch,
 
312
                'downloadmatch': new MatcherTee(FilterMatch, DomainMatch),
 
313
                'pathmatch': PathMatch,
 
314
                'statusmatch': new MatcherTee(StatusMatch, RemainderMatch),
 
315
                'sizematch': SizeMatch,
 
316
                'domainmatch': DomainMatch
 
317
        },
 
318
        getItems: function*(name, downloads) {
 
319
                for (let i of this._available[name].getItems(downloads)) {
 
320
                        yield i;
 
321
                }
 
322
        },
 
323
        addMatcher: function(name, params) {
 
324
                if (!(name in this._available)) {
 
325
                        log(LOG_ERROR, "trying to add a matcher that does not exist");
 
326
                        return;
 
327
                }
 
328
                this.removeMatcher(name);
 
329
                let m = this._available[name].getMatcher(params);
 
330
                if (m) {
 
331
                        log(LOG_DEBUG, "adding the matcher");
 
332
                        this._matchers.push({name: name, isMatch: m});
 
333
                        this._matchersLength = this._matchers.length;
 
334
                }
 
335
        },
 
336
        removeMatcher: function(name) {
 
337
                this._matchersLength = filterInSitu(this._matchers, m => m.name !== name).length;
 
338
        },
 
339
        get filtering() {
 
340
                return !!this._matchersLength;
 
341
        },
 
342
        filter: function(array) {
 
343
                // jshint strict:true, globalstrict:true, loopfunc:true
 
344
                let rv;
 
345
                for (let i = 0, e = this._matchers.length; i < e; ++i) {
 
346
                        let m = this._matchers[i];
 
347
                        let j = 0;
 
348
                        let fnm = function(e) {
 
349
                                let rv = m.isMatch(e);
 
350
                                e.filteredPosition = rv ? j++ : -1;
 
351
                                return rv;
 
352
                        };
 
353
                        if (!rv) {
 
354
                                rv = array.filter(fnm);
 
355
                        }
 
356
                        else {
 
357
                                filterInSitu(rv, fnm);
 
358
                        }
 
359
                }
 
360
                return rv;
 
361
        },
 
362
        shouldDisplay: function(d) {
 
363
                for (let i = 0, e = this._matchers.length; i < e; ++i) {
 
364
                        if (!this._matchers[i].isMatch(d)) {
 
365
                                return false;
 
366
                        }
 
367
                }
 
368
                return true;
 
369
        }
 
370
};
 
371
 
 
372
exports.Matcher = Matcher;