merge master
This commit is contained in:
commit
df0450f208
149
4chan_x.js
149
4chan_x.js
@ -2,7 +2,7 @@
|
||||
// @name 4chan x
|
||||
// @namespace aeosynth
|
||||
// @description Adds various features.
|
||||
// @version 2.2.2
|
||||
// @version 2.3.0
|
||||
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
|
||||
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
|
||||
// @include http://boards.4chan.org/*
|
||||
@ -72,6 +72,7 @@
|
||||
'404 Redirect': [true, 'Redirect dead threads'],
|
||||
'Anonymize': [false, 'Make everybody anonymous'],
|
||||
'Auto Watch': [true, 'Automatically watch threads that you start'],
|
||||
'Auto Watch Reply': [false, 'Automatically watch threads that you reply to'],
|
||||
'Comment Expansion': [true, 'Expand too long comments'],
|
||||
'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)'],
|
||||
'Image Auto-Gif': [false, 'Animate gif thumbnails'],
|
||||
@ -95,7 +96,7 @@
|
||||
'Unread Count': [true, 'Show unread post count in tab title']
|
||||
},
|
||||
textarea: {
|
||||
flavors: ['http://regex.info/exif.cgi?url=', 'http://iqdb.org/?url=', 'http://tineye.com/search?url='].join('\n')
|
||||
flavors: ['http://regex.info/exif.cgi?url=', 'http://iqdb.org/?url=', 'http://tineye.com/search?url=', '#http://saucenao.com/search.php?db=999&url='].join('\n')
|
||||
}
|
||||
},
|
||||
updater: {
|
||||
@ -180,9 +181,9 @@
|
||||
},
|
||||
dragstart: function(e) {
|
||||
var d, el, rect;
|
||||
e.preventDefault();
|
||||
ui.el = el = e.target.parentNode;
|
||||
d = document;
|
||||
d.body.className = 'noselect';
|
||||
d.addEventListener('mousemove', ui.drag, true);
|
||||
d.addEventListener('mouseup', ui.dragend, true);
|
||||
rect = el.getBoundingClientRect();
|
||||
@ -193,6 +194,7 @@
|
||||
},
|
||||
drag: function(e) {
|
||||
var bottom, el, left, right, top;
|
||||
e.preventDefault();
|
||||
el = ui.el;
|
||||
left = e.clientX - ui.dx;
|
||||
if (left < 20) {
|
||||
@ -220,7 +222,6 @@
|
||||
localStorage["" + id + "Left"] = el.style.left;
|
||||
localStorage["" + id + "Top"] = el.style.top;
|
||||
d = document;
|
||||
d.body.className = '';
|
||||
d.removeEventListener('mousemove', ui.drag, true);
|
||||
return d.removeEventListener('mouseup', ui.dragend, true);
|
||||
}
|
||||
@ -1019,9 +1020,20 @@
|
||||
return _results;
|
||||
},
|
||||
submit: function(e) {
|
||||
var form, isQR;
|
||||
var form, id, isQR, op;
|
||||
form = e.target;
|
||||
isQR = form.parentNode.id === 'qr';
|
||||
if ($.config('Auto Watch Reply') && $.config('Thread Watcher')) {
|
||||
if (g.REPLY && $('img.favicon').src === Favicon.empty) {
|
||||
watcher.watch(null, g.THREAD_ID);
|
||||
} else {
|
||||
id = $('input[name=resto]').value;
|
||||
op = d.getElementById(id);
|
||||
if ($('img.favicon', op).src === Favicon.empty) {
|
||||
watcher.watch(op, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isQR) {
|
||||
$('#error').textContent = '';
|
||||
}
|
||||
@ -1090,7 +1102,7 @@
|
||||
},
|
||||
cooldownStart: function(duration) {
|
||||
var submit, submits, _i, _len;
|
||||
submits = $$('#qr input[type=submit], form[name=post] input[type=submit]');
|
||||
submits = $$('#com_submit');
|
||||
for (_i = 0, _len = submits.length; _i < _len; _i++) {
|
||||
submit = submits[_i];
|
||||
submit.value = duration;
|
||||
@ -1102,7 +1114,7 @@
|
||||
cooldownCB: function() {
|
||||
var submit, submits, _i, _len;
|
||||
qr.duration = qr.duration - 1;
|
||||
submits = $$('#qr input[type=submit], form[name=post] input[type=submit]');
|
||||
submits = $$('#com_submit');
|
||||
for (_i = 0, _len = submits.length; _i < _len; _i++) {
|
||||
submit = submits[_i];
|
||||
if (qr.duration === 0) {
|
||||
@ -1439,67 +1451,64 @@
|
||||
};
|
||||
watcher = {
|
||||
init: function() {
|
||||
var dialog, favicon, html, id, input, inputs, src, watched, watchedBoard, _i, _len, _results;
|
||||
var dialog, favicon, html, input, inputs, _i, _len;
|
||||
html = '<div class=move>Thread Watcher</div>';
|
||||
dialog = ui.dialog('watcher', {
|
||||
top: '50px',
|
||||
left: '0px'
|
||||
}, html);
|
||||
$.append(d.body, dialog);
|
||||
watched = $.getValue('watched', {});
|
||||
watcher.refresh(watched);
|
||||
watchedBoard = watched[g.BOARD] || {};
|
||||
inputs = $$('form > input[value=delete], div.thread > input[value=delete]');
|
||||
_results = [];
|
||||
for (_i = 0, _len = inputs.length; _i < _len; _i++) {
|
||||
input = inputs[_i];
|
||||
id = input.name;
|
||||
if (id in watchedBoard) {
|
||||
src = Favicon["default"];
|
||||
} else {
|
||||
src = Favicon.empty;
|
||||
}
|
||||
favicon = $.el('img', {
|
||||
src: src,
|
||||
className: 'favicon'
|
||||
});
|
||||
$.bind(favicon, 'click', watcher.cb.toggle);
|
||||
_results.push($.before(input, favicon));
|
||||
$.before(input, favicon);
|
||||
}
|
||||
return _results;
|
||||
watcher.refresh($.getValue('watched', {}));
|
||||
return setInterval((function() {
|
||||
if (watcher.lastUpdated < $.getValue('watcher.lastUpdated', 0)) {
|
||||
return watcher.refresh($.getValue('watched', {}));
|
||||
}
|
||||
}), 1000);
|
||||
},
|
||||
refresh: function(watched) {
|
||||
var board, div, id, props, _i, _len, _ref, _results;
|
||||
_ref = $$('#watcher > div:not(.move)');
|
||||
var board, dialog, div, favicon, id, link, props, watchedBoard, x, _i, _j, _len, _len2, _ref, _ref2, _ref3;
|
||||
dialog = $('#watcher');
|
||||
_ref = $$('div:not(.move)', dialog);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
div = _ref[_i];
|
||||
$.remove(div);
|
||||
}
|
||||
_results = [];
|
||||
for (board in watched) {
|
||||
_results.push((function() {
|
||||
var _ref2, _results2;
|
||||
_ref2 = watched[board];
|
||||
_results2 = [];
|
||||
for (id in _ref2) {
|
||||
props = _ref2[id];
|
||||
_results2.push(watcher.addLink(props, $('#watcher')));
|
||||
}
|
||||
return _results2;
|
||||
})());
|
||||
_ref2 = watched[board];
|
||||
for (id in _ref2) {
|
||||
props = _ref2[id];
|
||||
div = $.el('div');
|
||||
x = $.el('a', {
|
||||
textContent: 'X'
|
||||
});
|
||||
$.bind(x, 'click', watcher.cb.x);
|
||||
link = $.el('a', props);
|
||||
$.append(div, x, $.tn(' '), link);
|
||||
$.append(dialog, div);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
},
|
||||
addLink: function(props, dialog) {
|
||||
var div, link, x;
|
||||
div = $.el('div');
|
||||
x = $.el('a', {
|
||||
textContent: 'X'
|
||||
});
|
||||
$.bind(x, 'click', watcher.cb.x);
|
||||
link = $.el('a', props);
|
||||
$.append(div, x, $.tn(' '), link);
|
||||
return $.append(dialog, div);
|
||||
watchedBoard = watched[g.BOARD] || {};
|
||||
_ref3 = $$('img.favicon');
|
||||
for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
|
||||
favicon = _ref3[_j];
|
||||
id = favicon.nextSibling.name;
|
||||
if (id in watchedBoard) {
|
||||
favicon.src = Favicon["default"];
|
||||
} else {
|
||||
favicon.src = Favicon.empty;
|
||||
}
|
||||
}
|
||||
$.setValue('watcher.lastUpdated', Date.now());
|
||||
return watcher.lastUpdated = Date.now();
|
||||
},
|
||||
cb: {
|
||||
toggle: function(e) {
|
||||
@ -1516,30 +1525,20 @@
|
||||
favicon = $('img.favicon', thread);
|
||||
id = favicon.nextSibling.name;
|
||||
if (favicon.src === Favicon.empty) {
|
||||
return watcher.watch(thread);
|
||||
return watcher.watch(thread, id);
|
||||
} else {
|
||||
return watcher.unwatch(g.BOARD, id);
|
||||
}
|
||||
},
|
||||
unwatch: function(board, id) {
|
||||
var favicon, input, watched;
|
||||
if (input = $("input[name=\"" + id + "\"]")) {
|
||||
favicon = input.previousSibling;
|
||||
favicon.src = Favicon.empty;
|
||||
}
|
||||
var watched;
|
||||
watched = $.getValue('watched', {});
|
||||
delete watched[board][id];
|
||||
$.setValue('watched', watched);
|
||||
return watcher.refresh(watched);
|
||||
},
|
||||
watch: function(thread) {
|
||||
var favicon, id, props, tc, watched, _name;
|
||||
favicon = $('img.favicon', thread);
|
||||
if (favicon.src === Favicon["default"]) {
|
||||
return;
|
||||
}
|
||||
favicon.src = Favicon["default"];
|
||||
id = favicon.nextSibling.name;
|
||||
watch: function(thread, id) {
|
||||
var props, tc, watched, _name;
|
||||
tc = $('span.filetitle', thread).textContent || $('blockquote', thread).textContent;
|
||||
props = {
|
||||
textContent: "/" + g.BOARD + "/ - " + tc.slice(0, 25),
|
||||
@ -1580,8 +1579,19 @@
|
||||
},
|
||||
cb: {
|
||||
node: function(root) {
|
||||
var i, link, names, prefix, prefixes, span, suffix, _i, _len, _ref, _results;
|
||||
prefixes = $.config('flavors').split('\n');
|
||||
var i, link, names, prefix, prefixes, s, span, suffix, _i, _len, _ref, _results;
|
||||
prefixes = (function() {
|
||||
var _i, _len, _ref, _results;
|
||||
_ref = $.config('flavors').split('\n');
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
s = _ref[_i];
|
||||
if (s[0] !== '#') {
|
||||
_results.push(s);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
})();
|
||||
names = (function() {
|
||||
var _i, _len, _results;
|
||||
_results = [];
|
||||
@ -1659,7 +1669,7 @@
|
||||
for (_i = 0, _len = arr.length; _i < _len; _i++) {
|
||||
el = arr[_i];
|
||||
a = $.el('a', {
|
||||
textContent: '[ ! ]'
|
||||
innerHTML: '[ ! ]'
|
||||
});
|
||||
$.bind(a, 'click', quickReport.cb.report);
|
||||
$.after(el, a);
|
||||
@ -1767,7 +1777,6 @@
|
||||
case '3':
|
||||
case 'adv':
|
||||
case 'an':
|
||||
case 'c':
|
||||
case 'ck':
|
||||
case 'co':
|
||||
case 'fa':
|
||||
@ -2173,8 +2182,8 @@
|
||||
if ($.config('Unread Count')) {
|
||||
unread.init();
|
||||
}
|
||||
if ($.config('Auto Watch') && location.hash === '#watch') {
|
||||
watcher.watch();
|
||||
if ($.config('Auto Watch') && $.config('Thread Watcher') && location.hash === '#watch' && $('img.favicon').src === Favicon.empty) {
|
||||
watcher.watch(null, g.THREAD_ID);
|
||||
}
|
||||
} else {
|
||||
threading.init();
|
||||
@ -2349,14 +2358,6 @@
|
||||
#watcher > div:last-child {\
|
||||
padding-bottom: 5px;\
|
||||
}\
|
||||
\
|
||||
body.noselect {\
|
||||
-webkit-user-select: none;\
|
||||
-khtml-user-select: none;\
|
||||
-moz-user-select: none;\
|
||||
-o-user-select: none;\
|
||||
user-select: none;\
|
||||
}\
|
||||
'
|
||||
};
|
||||
main.init();
|
||||
|
||||
@ -1,3 +1,11 @@
|
||||
2.3.0
|
||||
- mayhem:
|
||||
refresh watcher list on un/watch
|
||||
auto refresh watcher list
|
||||
update no-ip.org archive
|
||||
fix auto-watch
|
||||
- flavor comments
|
||||
|
||||
2.2.2
|
||||
- hopefully fix upgrading issues
|
||||
|
||||
|
||||
2
header
2
header
@ -2,7 +2,7 @@
|
||||
// @name 4chan x
|
||||
// @namespace aeosynth
|
||||
// @description Adds various features.
|
||||
// @version 2.2.2
|
||||
// @version 2.3.0
|
||||
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
|
||||
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
|
||||
// @include http://boards.4chan.org/*
|
||||
|
||||
101
script.coffee
101
script.coffee
@ -15,6 +15,7 @@ config =
|
||||
'404 Redirect': [true, 'Redirect dead threads']
|
||||
'Anonymize': [false, 'Make everybody anonymous']
|
||||
'Auto Watch': [true, 'Automatically watch threads that you start']
|
||||
'Auto Watch Reply': [false, 'Automatically watch threads that you reply to']
|
||||
'Comment Expansion': [true, 'Expand too long comments']
|
||||
'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)']
|
||||
'Image Auto-Gif': [false, 'Animate gif thumbnails']
|
||||
@ -41,6 +42,7 @@ config =
|
||||
'http://regex.info/exif.cgi?url='
|
||||
'http://iqdb.org/?url='
|
||||
'http://tineye.com/search?url='
|
||||
'#http://saucenao.com/search.php?db=999&url='
|
||||
].join '\n'
|
||||
updater:
|
||||
checkbox:
|
||||
@ -98,9 +100,10 @@ ui =
|
||||
(-> el.parentNode.removeChild(el)), true
|
||||
el
|
||||
dragstart: (e) ->
|
||||
#prevent text selection
|
||||
e.preventDefault()
|
||||
ui.el = el = e.target.parentNode
|
||||
d = document
|
||||
d.body.className = 'noselect'
|
||||
d.addEventListener 'mousemove', ui.drag, true
|
||||
d.addEventListener 'mouseup', ui.dragend, true
|
||||
#distance from pointer to el edge is constant; calculate it here.
|
||||
@ -112,6 +115,7 @@ ui =
|
||||
ui.width = document.body.clientWidth - el.offsetWidth
|
||||
ui.height = document.body.clientHeight - el.offsetHeight
|
||||
drag: (e) ->
|
||||
e.preventDefault()
|
||||
{el} = ui
|
||||
left = e.clientX - ui.dx
|
||||
if left < 20 then left = '0px'
|
||||
@ -134,7 +138,6 @@ ui =
|
||||
localStorage["#{id}Left"] = el.style.left
|
||||
localStorage["#{id}Top"] = el.style.top
|
||||
d = document
|
||||
d.body.className = ''
|
||||
d.removeEventListener 'mousemove', ui.drag, true
|
||||
d.removeEventListener 'mouseup', ui.dragend, true
|
||||
|
||||
@ -770,6 +773,15 @@ qr =
|
||||
form = e.target
|
||||
isQR = form.parentNode.id == 'qr'
|
||||
|
||||
if $.config('Auto Watch Reply') and $.config('Thread Watcher')
|
||||
if g.REPLY and $('img.favicon').src is Favicon.empty
|
||||
watcher.watch null, g.THREAD_ID
|
||||
else
|
||||
id = $('input[name=resto]').value
|
||||
op = d.getElementById id
|
||||
if $('img.favicon', op).src is Favicon.empty
|
||||
watcher.watch op, id
|
||||
|
||||
if isQR
|
||||
$('#error').textContent = ''
|
||||
|
||||
@ -834,7 +846,7 @@ qr =
|
||||
return true
|
||||
|
||||
cooldownStart: (duration) ->
|
||||
submits = $$ '#qr input[type=submit], form[name=post] input[type=submit]'
|
||||
submits = $$ '#com_submit'
|
||||
for submit in submits
|
||||
submit.value = duration
|
||||
submit.disabled = true
|
||||
@ -844,7 +856,7 @@ qr =
|
||||
cooldownCB: ->
|
||||
qr.duration = qr.duration - 1
|
||||
|
||||
submits = $$ '#qr input[type=submit], form[name=post] input[type=submit]'
|
||||
submits = $$ '#com_submit'
|
||||
for submit in submits
|
||||
if qr.duration == 0
|
||||
submit.disabled = false
|
||||
@ -1161,41 +1173,46 @@ watcher =
|
||||
dialog = ui.dialog 'watcher', top: '50px', left: '0px', html
|
||||
$.append d.body, dialog
|
||||
|
||||
#populate watcher
|
||||
watched = $.getValue 'watched', {}
|
||||
watcher.refresh watched
|
||||
|
||||
#add watch buttons
|
||||
watchedBoard = watched[g.BOARD] or {}
|
||||
inputs = $$ 'form > input[value=delete], div.thread > input[value=delete]'
|
||||
for input in inputs
|
||||
id = input.name
|
||||
if id of watchedBoard
|
||||
src = Favicon.default
|
||||
else
|
||||
src = Favicon.empty
|
||||
favicon = $.el 'img',
|
||||
src: src
|
||||
className: 'favicon'
|
||||
$.bind favicon, 'click', watcher.cb.toggle
|
||||
$.before input, favicon
|
||||
|
||||
#populate watcher, display watch buttons
|
||||
watcher.refresh $.getValue 'watched', {}
|
||||
|
||||
setInterval (->
|
||||
if watcher.lastUpdated < $.getValue 'watcher.lastUpdated', 0
|
||||
watcher.refresh $.getValue 'watched', {}
|
||||
), 1000
|
||||
|
||||
refresh: (watched) ->
|
||||
for div in $$ '#watcher > div:not(.move)'
|
||||
dialog = $ '#watcher'
|
||||
for div in $$ 'div:not(.move)', dialog
|
||||
$.remove div
|
||||
for board of watched
|
||||
for id, props of watched[board]
|
||||
watcher.addLink props, $ '#watcher'
|
||||
div = $.el 'div'
|
||||
x = $.el 'a',
|
||||
textContent: 'X'
|
||||
$.bind x, 'click', watcher.cb.x
|
||||
link = $.el 'a', props
|
||||
|
||||
addLink: (props, dialog) ->
|
||||
div = $.el 'div'
|
||||
x = $.el 'a',
|
||||
textContent: 'X'
|
||||
$.bind x, 'click', watcher.cb.x
|
||||
link = $.el 'a', props
|
||||
$.append div, x, $.tn(' '), link
|
||||
$.append dialog, div
|
||||
|
||||
$.append div, x, $.tn(' '), link
|
||||
$.append dialog, div
|
||||
watchedBoard = watched[g.BOARD] or {}
|
||||
for favicon in $$ 'img.favicon'
|
||||
id = favicon.nextSibling.name
|
||||
if id of watchedBoard
|
||||
favicon.src = Favicon.default
|
||||
else
|
||||
favicon.src = Favicon.empty
|
||||
$.setValue 'watcher.lastUpdated', Date.now()
|
||||
watcher.lastUpdated = Date.now()
|
||||
|
||||
cb:
|
||||
toggle: (e) ->
|
||||
@ -1209,29 +1226,18 @@ watcher =
|
||||
favicon = $ 'img.favicon', thread
|
||||
id = favicon.nextSibling.name
|
||||
if favicon.src == Favicon.empty
|
||||
watcher.watch thread
|
||||
watcher.watch thread, id
|
||||
else # favicon.src == Favicon.default
|
||||
watcher.unwatch g.BOARD, id
|
||||
|
||||
unwatch: (board, id) ->
|
||||
if input = $ "input[name=\"#{id}\"]"
|
||||
favicon = input.previousSibling
|
||||
favicon.src = Favicon.empty
|
||||
|
||||
watched = $.getValue 'watched', {}
|
||||
delete watched[board][id]
|
||||
$.setValue 'watched', watched
|
||||
|
||||
watcher.refresh watched
|
||||
|
||||
watch: (thread) ->
|
||||
favicon = $ 'img.favicon', thread
|
||||
|
||||
#this happens if we try to auto-watch an already watched thread.
|
||||
return if favicon.src is Favicon.default
|
||||
|
||||
favicon.src = Favicon.default
|
||||
id = favicon.nextSibling.name
|
||||
watch: (thread, id) ->
|
||||
tc = $('span.filetitle', thread).textContent or $('blockquote', thread).textContent
|
||||
props =
|
||||
textContent: "/#{g.BOARD}/ - #{tc[...25]}"
|
||||
@ -1263,7 +1269,7 @@ sauce =
|
||||
g.callbacks.push sauce.cb.node
|
||||
cb:
|
||||
node: (root) ->
|
||||
prefixes = $.config('flavors').split '\n'
|
||||
prefixes = (s for s in ($.config('flavors').split '\n') when s[0] != '#')
|
||||
names = (prefix.match(/(\w+)\./)[1] for prefix in prefixes)
|
||||
for span in $$ 'span.filesize', root
|
||||
suffix = $('a', span).href
|
||||
@ -1312,7 +1318,7 @@ quickReport =
|
||||
arr = $$ 'span[id^=no]', root
|
||||
for el in arr
|
||||
a = $.el 'a',
|
||||
textContent: '[ ! ]'
|
||||
innerHTML: '[ ! ]'
|
||||
$.bind a, 'click', quickReport.cb.report
|
||||
$.after el, a
|
||||
$.after el, $.tn(' ')
|
||||
@ -1389,7 +1395,7 @@ redirect = ->
|
||||
url = "http://green-oval.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}"
|
||||
when 'jp', 'm', 'tg'
|
||||
url = "http://archive.easymodo.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}"
|
||||
when '3', 'adv', 'an', 'c', 'ck', 'co', 'fa', 'fit', 'int', 'k', 'mu', 'n', 'o', 'p', 'po', 'soc', 'sp', 'toy', 'trv', 'v', 'vp', 'x'
|
||||
when '3', 'adv', 'an', 'ck', 'co', 'fa', 'fit', 'int', 'k', 'mu', 'n', 'o', 'p', 'po', 'soc', 'sp', 'toy', 'trv', 'v', 'vp', 'x'
|
||||
url = "http://archive.no-ip.org/#{g.BOARD}/thread/#{g.THREAD_ID}"
|
||||
else
|
||||
url = "http://boards.4chan.org/#{g.BOARD}"
|
||||
@ -1697,8 +1703,9 @@ main =
|
||||
if $.config 'Unread Count'
|
||||
unread.init()
|
||||
|
||||
if $.config('Auto Watch') and location.hash is '#watch'
|
||||
watcher.watch()
|
||||
if $.config('Auto Watch') and $.config('Thread Watcher') and
|
||||
location.hash is '#watch' and $('img.favicon').src is Favicon.empty
|
||||
watcher.watch null, g.THREAD_ID
|
||||
|
||||
else #not reply
|
||||
threading.init()
|
||||
@ -1869,14 +1876,6 @@ main =
|
||||
#watcher > div:last-child {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
body.noselect {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
'
|
||||
|
||||
main.init()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user