瀏覽代碼

WIP. adding keyword/category blocking

tags/v1.0.1
Jonathan Cobb 4 年之前
父節點
當前提交
fbe67ca747
共有 8 個檔案被更改,包括 510 行新增77 行删除
  1. +6
    -0
      bubble-server/src/main/java/bubble/model/app/AppMessage.java
  2. +6
    -3
      bubble-server/src/main/java/bubble/resources/stream/FilterAppMessagesResource.java
  3. +6
    -1
      bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs
  4. +40
    -13
      bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs
  5. +86
    -57
      bubble-server/src/main/resources/bubble/rule/social/block/site/FB.js.hbs
  6. +12
    -3
      bubble-server/src/main/resources/models/apps/user_block/bubbleApp_userBlock.json
  7. +18
    -0
      bubble-server/src/main/resources/models/apps/user_block/fb/bubbleApp_userBlock_fb_data.json
  8. +336
    -0
      bubble-server/src/main/resources/models/apps/user_block/shadowban-unblock-icon.svg

+ 6
- 0
bubble-server/src/main/java/bubble/model/app/AppMessage.java 查看文件

@@ -87,6 +87,12 @@ public class AppMessage extends IdentifiableBase implements AppTemplateEntity, H
.toArray(NameAndValue[]::new);
}

public NameAndValue[] getMessagesWithPrefix (String prefix) {
return Arrays.stream(getMessages())
.filter(m -> m.getName().startsWith(prefix))
.toArray(NameAndValue[]::new);
}

public boolean hasMessage(String name) { return !empty(getMessage(name)); }

@ECSearchable @ECField(index=50) @Column(nullable=false)


+ 6
- 3
bubble-server/src/main/java/bubble/resources/stream/FilterAppMessagesResource.java 查看文件

@@ -28,7 +28,8 @@ import static org.cobbzilla.util.string.StringUtil.splitAndTrim;
@Slf4j
public class FilterAppMessagesResource {

public static final String PARAM_FILTER_MESSAGE = "find";
public static final String PARAM_FILTER = "find";
public static final String FILTER_PREFIX = "prefix:";

private final Account account;
private final BubbleApp app;
@@ -47,11 +48,13 @@ public class FilterAppMessagesResource {
public AppMessage find(@Context ContainerRequest ctx,
@PathParam("locale") String locale) {
final Map<String, String> params = queryParams(ctx.getRequestUri().getQuery());
final String filter = params.get(PARAM_FILTER_MESSAGE);
final String filter = params.get(PARAM_FILTER);
final String cacheKey = app.getUuid() + ":" + locale + ":" + filter;
return singleMessageCache.computeIfAbsent(cacheKey, k -> {
final AppMessage messages = appMessageDAO.findByAccountAndAppAndLocale(account.getUuid(), app.getUuid(), locale);
return new AppMessage().setMessages(messages.getMessages(splitAndTrim(filter, ",")));
return filter.startsWith(FILTER_PREFIX)
? new AppMessage().setMessages(messages.getMessagesWithPrefix(filter.substring(FILTER_PREFIX.length())))
: new AppMessage().setMessages(messages.getMessages(splitAndTrim(filter, ",")));
});
}



+ 6
- 1
bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs 查看文件

@@ -1,6 +1,7 @@
{{JS_PREFIX}}_load_messages = function (messages, set_func) {
const requestOptions = { method: 'GET' };
fetch('/__bubble/api/filter/messages/{{BUBBLE_REQUEST_ID}}/{{BUBBLE_APP_NAME}}/{{ACCOUNT_LOCALE}}?find='+messages.join(","), requestOptions)
const find = Array.isArray(messages) ? messages.join(",") : 'prefix:' + messages;
fetch('/__bubble/api/filter/messages/{{BUBBLE_REQUEST_ID}}/{{BUBBLE_APP_NAME}}/{{ACCOUNT_LOCALE}}?find='+find, requestOptions)
.then(response => response.json())
.then(appMessages => {
const map = {};
@@ -20,6 +21,10 @@ if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') {

let {{PAGE_PREFIX}}_icon_status = [];

{{PAGE_PREFIX}}_msg_or_default = function (messages, msg, def) {
return msg in messages ? messages[msg] : def;
}

{{PAGE_PREFIX}}_log = function (data) {
const logData = JSON.stringify(data);
const requestOptions = {


+ 40
- 13
bubble-server/src/main/resources/bubble/rule/social/block/JsUserBlockerRuleDriver.js.hbs 查看文件

@@ -1,4 +1,6 @@
let {{JS_PREFIX}}_blocked_users = null;
let {{JS_PREFIX}}_blocked_keywords = null;
let {{JS_PREFIX}}_blocked_links = null;
const {{JS_PREFIX}}_request_id = '{{BUBBLE_REQUEST_ID}}';

let {{JS_PREFIX}}_unblocked_users_needs_refresh = false;
@@ -23,6 +25,13 @@ const {{JS_PREFIX}}_create_block_img = function(size) {
return img;
}

const {{JS_PREFIX}}_create_unblock_img = function(size) {
const img = document.createElement('img');
img.src = '/__bubble/api/filter/assets/{{BUBBLE_REQUEST_ID}}/UserBlocker/unblock-icon?raw=true';
img.width = typeof size !== 'undefined' ? size : 16;
return img;
}

const {{JS_PREFIX}}_stop_refreshing_blocks = function(e) {
if ({{JS_PREFIX}}_interval !== null) {
window.clearInterval({{JS_PREFIX}}_interval);
@@ -31,6 +40,7 @@ const {{JS_PREFIX}}_stop_refreshing_blocks = function(e) {
}
const {{JS_PREFIX}}_refresh_blocks = function(e) {
{{PAGE_PREFIX}}_log('_refresh_blocks!');
{{JS_PREFIX}}_last_applied = Date.now();
{{JS_PREFIX}}_stop_refreshing_blocks();
{{JS_PREFIX}}_apply_blocks({{JS_PREFIX}}_blocked_users);
{{JS_PREFIX}}_interval = window.setInterval(function () {
@@ -46,10 +56,21 @@ function {{JS_PREFIX}}_fetch_blocks (do_apply) {
.then(resp => resp.json())
.then(data => {
const blocked_users = [];
const blocked_keywords = [];
const blocked_links = [];
for (let i=0; i<data.length; i++) {
blocked_users.push(data[i]);
let token = data[i];
if (token.startsWith('kw:')) {
blocked_keywords.push(token);
} else if (token.startsWith('link:')) {
blocked_links.push(token);
} else {
blocked_users.push(token);
}
}
{{JS_PREFIX}}_blocked_users = blocked_users;
{{JS_PREFIX}}_blocked_keywords = blocked_keywords;
{{JS_PREFIX}}_blocked_links = blocked_links;
{{JS_PREFIX}}_refresh_blocks();
});
}
@@ -64,13 +85,16 @@ function {{JS_PREFIX}}_handleVisibilityChange() {
}
}

document.addEventListener("visibilitychange", {{JS_PREFIX}}_handleVisibilityChange, false);
document.addEventListener("click", function (ev) {
function {{JS_PREFIX}}_check_stale_refresher(ev) {
if ({{JS_PREFIX}}_last_applied === null || Date.now() - {{JS_PREFIX}}_last_applied > 2*{{JS_PREFIX}}_idle_interval) {
{{PAGE_PREFIX}}_log('document.click forcing refresh');
{{PAGE_PREFIX}}_log('check_stale_refresher forcing refresh upon document event.type='+ev.type);
{{JS_PREFIX}}_refresh_blocks();
}
}, false);
}

document.addEventListener("visibilitychange", {{JS_PREFIX}}_handleVisibilityChange, false);
document.addEventListener("click", {{JS_PREFIX}}_check_stale_refresher, false);
document.addEventListener("scroll", {{JS_PREFIX}}_check_stale_refresher, false);

function {{JS_PREFIX}}_update_user (author, do_block) {
{{JS_PREFIX}}_apply_blocks({{JS_PREFIX}}_blocked_users);
@@ -101,7 +125,7 @@ function {{JS_PREFIX}}_unblock_user (author) {
let {{JS_PREFIX}}_app_details = false;

function {{JS_PREFIX}}_create_unblock_control(authorName) {
const imgHolder = {{JS_PREFIX}}_create_block_img();
const imgHolder = {{JS_PREFIX}}_create_unblock_img();
const blockSpan = document.createElement('span');
const unblockLink = document.createElement('a');
unblockLink.addEventListener("click", function (e) {
@@ -135,6 +159,7 @@ function {{JS_PREFIX}}_hide_app_details() {

function {{JS_PREFIX}}_show_app_details() {
{{PAGE_PREFIX}}_log('show_app_details called');
{{JS_PREFIX}}_refresh_blocks();
const detailsDivId = '{{JS_PREFIX}}_detailsDiv';
let detailsDiv = document.getElementById(detailsDivId);
let blocks = {{JS_PREFIX}}_blocked_users;
@@ -169,17 +194,19 @@ function {{JS_PREFIX}}_show_app_details() {
window.location.reload();
return false;
});
const refreshMessage = 'refreshPage' in {{JS_PREFIX}}_messages
? {{JS_PREFIX}}_messages['refreshPage']
: 'refresh'
const refreshMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_refreshPage', 'refresh');
refreshControl.appendChild(document.createTextNode(refreshMessage));
detailsDiv.appendChild(refreshControl);
detailsDiv.appendChild(document.createElement('hr'));
}
const usersHeaderText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_blockedUsers', 'Users');
const usersHeader = document.createElement('strong');
usersHeader.appendChild(document.createTextNode(usersHeaderText));
detailsDiv.appendChild(usersHeader);
detailsDiv.appendChild(document.createElement('hr'));

if (blocks === null || blocks.length === 0) {
const emptyMessage = 'noUsersBlocked' in {{JS_PREFIX}}_messages
? {{JS_PREFIX}}_messages['noUsersBlocked']
: '(empty)'
const emptyMessage = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_noUsersBlocked', '(empty)');
const entryDiv = document.createElement('div');
const entryText = document.createTextNode(emptyMessage);
entryDiv.appendChild(entryText);
@@ -214,7 +241,7 @@ function {{JS_PREFIX}}_toggle_app_details(ev) {
link: {{JS_PREFIX}}_toggle_app_details,
icon: 'icon',
onReady: function () {
{{JS_PREFIX}}_load_messages(['noUsersBlocked'], function (messages) {
{{JS_PREFIX}}_load_messages('web_', function (messages) {
{{JS_PREFIX}}_messages = messages;
})
{{JS_PREFIX}}_fetch_blocks();


+ 86
- 57
bubble-server/src/main/resources/bubble/rule/social/block/site/FB.js.hbs 查看文件

@@ -10,6 +10,27 @@ function {{JS_PREFIX}}_getElementsByXPath(xpath, parent) {
const {{JS_PREFIX}}_site_host = location.protocol + '//' + window.location.hostname + '/';
const {{JS_PREFIX}}_mobile = window.location.hostname === "m.facebook.com";

const {{JS_PREFIX}}_appendChild = Element.prototype.appendChild;
Element.prototype.appendChild = function() {

try {
if (arguments[0].tagName) {
if ({{JS_PREFIX}}_mobile && arguments[0].tagName.toUpperCase() === 'SECTION') {
console.log('>>> intercepted appendChild for mobile article');
} else if ({{JS_PREFIX}}_mobile && arguments[0].tagName.toUpperCase() === 'DIV' && arguments[0].role && arguments[0].role === 'article') {
console.log('>>> intercepted appendChild for desktop article');
}
}
} catch (e) {
console.log('>>> error inspecting: e='+e);
}
try {
{{JS_PREFIX}}_appendChild.apply(this, arguments);
} catch (e) {
console.log('>>> error calling document.appendChild: arg[0].tagName = '+arguments[0].tagName+' e='+e);
}
};

const log = {{PAGE_PREFIX}}_log;

function {{JS_PREFIX}}_is_ad(article) {
@@ -61,7 +82,8 @@ function {{JS_PREFIX}}_create_block_control(article, authorName) {
return false;
});
blockLink.appendChild(imgHolder);
blockSpan.appendChild(blockLink)
blockSpan.appendChild(document.createTextNode('\u00A0\u00A0'));
blockSpan.appendChild(blockLink);

blockSpan.id = 'blockSpan_'+{{JS_PREFIX}}_uuidv4();
return blockSpan;
@@ -84,9 +106,68 @@ function {{JS_PREFIX}}_is_author_link(href) {
return matches === null;
}

function {{JS_PREFIX}}_should_block(blocked_users, article) {
const sitePrefix = {{JS_PREFIX}}_site_host;
const mobile = {{JS_PREFIX}}_mobile;
const log = {{PAGE_PREFIX}}_log;

const authorLinks = Array.from(article.getElementsByTagName('a'))
.filter(a => {{JS_PREFIX}}_is_author_link(a.href))
.filter(a => a.getElementsByTagName('svg').length === 0)
.filter(a => mobile ? a.parentElement.tagName.toUpperCase() === 'STRONG' : a.getElementsByTagName('span').length === 1);

for (let authIndex=0; authIndex<authorLinks.length; authIndex++) {
const authorLink = authorLinks[authIndex];
let authorHref = authorLink.href;
let authorName = authorHref.startsWith(sitePrefix) ? authorHref.substring(sitePrefix.length) : authorHref;
const qPos = authorName.indexOf('?');
if (qPos !== -1) {
authorName = authorName.substring(0, qPos);
authorLink.href = sitePrefix + authorName;
}
if (authorName.endsWith('/')) authorName = authorName.substring(0, authorName.length-1);
if (authorName === 'profile.php') {
return true;
}
const authorDisplay = mobile
? authorLink
: authorLink.getElementsByTagName('span')[0];
const authorDisplayName = authorDisplay.innerHTML;
if ({{JS_PREFIX}}_is_ad(article)) {
log('removing ad ('+authorDisplayName+')');
return authorName == null ? true : authorName;

} else if (blocked_users !== null && blocked_users.includes(authorName)) {
return authorName == null ? true : authorName;

} else {
// have we visited this authorLink before?
try {
if (authorLink.id === "") {
// log('VISITING author: ' + authorName + ' (' + authorDisplayName + ')');
authorLink.id = 'bubble_author_' + {{JS_PREFIX}}_uuidv4();
// log('assigned id='+authorLink.id+' for authorLink by '+authorName);

} else if (authorDisplay.parentNode.innerHTML.indexOf('__bubble') !== -1) {
// log('NOT RE-VISITING article node for author: ' + authorName + ' (' + authorDisplayName + '), display.inner='+authorDisplay.parentNode.innerHTML);
continue;

} else {
// log('RE-VISITING article node for author: ' + authorName + ' (' + authorDisplayName + '), display.inner='+authorDisplay.parentNode.innerHTML);
}
let b = {{JS_PREFIX}}_create_block_control(article, authorName);
// log('inserting span='+b.id+' for article by '+authorName);
authorDisplay.parentNode.appendChild(b);

} catch (e) {
log('badness: '+e);
}
}
}
}

function {{JS_PREFIX}}_apply_blocks(blocked_users) {

const sitePrefix = {{JS_PREFIX}}_site_host;
const mobile = {{JS_PREFIX}}_mobile;
const log = {{PAGE_PREFIX}}_log;

@@ -96,61 +177,9 @@ function {{JS_PREFIX}}_apply_blocks(blocked_users) {

for (let i=0; i<articles.length; i++) {
const article = articles[i];
const authorLinks = Array.from(article.getElementsByTagName('a'))
.filter(a => {{JS_PREFIX}}_is_author_link(a.href))
.filter(a => a.getElementsByTagName('svg').length === 0)
.filter(a => mobile ? a.parentElement.tagName.toUpperCase() === 'STRONG' : a.getElementsByTagName('span').length === 1);

for (let authIndex=0; authIndex<authorLinks.length; authIndex++) {
const authorLink = authorLinks[authIndex];
let authorHref = authorLink.href;
let authorName = authorHref.startsWith(sitePrefix) ? authorHref.substring(sitePrefix.length) : authorHref;
const qPos = authorName.indexOf('?');
if (qPos !== -1) {
authorName = authorName.substring(0, qPos);
authorLink.href = sitePrefix + authorName;
}
if (authorName.endsWith('/')) authorName = authorName.substring(0, authorName.length-1);
if (authorName === 'profile.php') {
{{JS_PREFIX}}_remove_article(article, null);
break;
}
const authorDisplay = mobile
? authorLink
: authorLink.getElementsByTagName('span')[0];
const authorDisplayName = authorDisplay.innerHTML;
if ({{JS_PREFIX}}_is_ad(article)) {
log('removing ad ('+authorDisplayName+')');
{{JS_PREFIX}}_remove_article(article, authorName);
break;

} else if (blocked_users !== null && blocked_users.includes(authorName)) {
{{JS_PREFIX}}_remove_article(article, authorName);
break;

} else {
// have we visited this authorLink before?
try {
if (authorLink.id === "") {
// log('VISITING author: ' + authorName + ' (' + authorDisplayName + ')');
authorLink.id = 'bubble_author_' + {{JS_PREFIX}}_uuidv4();
// log('assigned id='+authorLink.id+' for authorLink by '+authorName);

} else if (authorDisplay.parentNode.innerHTML.indexOf('__bubble') !== -1) {
// log('NOT RE-VISITING article node for author: ' + authorName + ' (' + authorDisplayName + '), display.inner='+authorDisplay.parentNode.innerHTML);
continue;

} else {
// log('RE-VISITING article node for author: ' + authorName + ' (' + authorDisplayName + '), display.inner='+authorDisplay.parentNode.innerHTML);
}
let b = {{JS_PREFIX}}_create_block_control(article, authorName);
// log('inserting span='+b.id+' for article by '+authorName);
authorDisplay.parentNode.appendChild(b);

} catch (e) {
log('badness: '+e);
}
}
const authorName = {{JS_PREFIX}}_should_block(blocked_users, article);
if (authorName) {
{{JS_PREFIX}}_remove_article(article, authorName === true ? null : authorName);
}
}
}

+ 12
- 3
bubble-server/src/main/resources/models/apps/user_block/bubbleApp_userBlock.json 查看文件

@@ -27,9 +27,8 @@
"messages": [
{"name": "name", "value": "Shadow Ban"},
{"name": "icon", "value": "classpath:models/apps/user_block/shadowban-icon.svg"},
{"name": "unblock-icon", "value": "classpath:models/apps/user_block/shadowban-unblock-icon.svg"},
{"name": "summary", "value": "User Blocker"},
{"name": "noUsersBlocked", "value": "(empty)"},
{"name": "refreshPage", "value": "refresh"},
{"name": "description", "value": "Throw the garbage to the curb!"},
{"name": "view.blocked_users", "value": "Manage Blocked Users"},
{"name": "field.key", "value": "Username"},
@@ -37,7 +36,17 @@
{"name": "field.ctime", "value": "Created"},
{"name": "action.enable", "value": "Enforce Block"},
{"name": "action.disable", "value": "Disable Block"},
{"name": "action.delete", "value": "Delete Block"}
{"name": "action.delete", "value": "Delete Block"},

{"name": "web_blockedUsers", "value": "Users"},
{"name": "web_noUsersBlocked", "value": "(empty)"},
{"name": "web_blockedKeywords", "value": "Keywords"},
{"name": "web_noKeywordsBlocked", "value": "(empty)"},
{"name": "web_addKeyword", "value": "Add"},
{"name": "web_blockedCategories", "value": "Categories"},
{"name": "web_cat_us_news", "value": "US News Media"},
{"name": "web_cat_us_politics", "value": "US Politics"},
{"name": "web_refreshPage", "value": "refresh"}
]
}]
}

+ 18
- 0
bubble-server/src/main/resources/models/apps/user_block/fb/bubbleApp_userBlock_fb_data.json 查看文件

@@ -0,0 +1,18 @@
[{
"name": "UserBlocker",
"children": {
"AppData": [{
"site": "Facebook",
"template": true,
"matcher": "FBMatcher",
"key": "cat~us_news",
"value": "https://raw.githubusercontent.com/getbubblenow/bubble-filter-lists/master/sites_us_news.txt"
}, {
"site": "Facebook",
"template": true,
"matcher": "FBMatcher",
"key": "cat~us_politics",
"value": "https://raw.githubusercontent.com/getbubblenow/bubble-filter-lists/master/us_politics.txt"
}]
}
}]

+ 336
- 0
bubble-server/src/main/resources/models/apps/user_block/shadowban-unblock-icon.svg 查看文件

@@ -0,0 +1,336 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48.000000px"
height="48.000000px"
id="svg6361"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docbase="/home/jimmac/gfx/ximian/tango-icon-theme/scalable/actions"
sodipodi:docname="process-stop.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs3">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective52" />
<linearGradient
id="linearGradient2256">
<stop
style="stop-color:#565656;stop-opacity:1;"
offset="0"
id="stop2258" />
<stop
style="stop-color:#bcbcbc;stop-opacity:1;"
offset="1"
id="stop2260" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient2248">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop2250" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop2252" />
</linearGradient>
<linearGradient
id="linearGradient9647">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop9649" />
<stop
style="stop-color:#dbdbdb;stop-opacity:1;"
offset="1"
id="stop9651" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient21644">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop21646" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop21648" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient21644"
id="radialGradient21650"
cx="25.125"
cy="36.75"
fx="25.125"
fy="36.75"
r="15.75"
gradientTransform="matrix(1.000000,0.000000,0.000000,0.595238,-2.300678e-15,14.87500)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient7895">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop7897" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop7899" />
</linearGradient>
<linearGradient
id="linearGradient4981">
<stop
style="stop-color:#444444;stop-opacity:1;"
offset="0"
id="stop4983" />
<stop
style="stop-color:#3b3b3b;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop4985" />
</linearGradient>
<linearGradient
id="linearGradient15762"
inkscape:collect="always">
<stop
id="stop15764"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop15766"
offset="1"
style="stop-color:#ffffff;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient14236">
<stop
id="stop14238"
offset="0.0000000"
style="stop-color:#797979;stop-opacity:1.0000000;" />
<stop
id="stop14240"
offset="1.0000000"
style="stop-color:#363636;stop-opacity:1.0000000;" />
</linearGradient>
<linearGradient
id="linearGradient11780">
<stop
style="stop-color:#b1b1b1;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop11782" />
<stop
style="stop-color:#606060;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop11784" />
</linearGradient>
<linearGradient
id="linearGradient11014">
<stop
style="stop-color:#383838;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop11016" />
<stop
style="stop-color:#424242;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop13245" />
<stop
style="stop-color:#4c4c4c;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop11018" />
</linearGradient>
<linearGradient
y2="9.6507530"
x2="9.8940229"
y1="5.3855424"
x1="5.7365270"
gradientTransform="matrix(-1.000000,0.000000,0.000000,-1.000000,31.72170,31.29079)"
gradientUnits="userSpaceOnUse"
id="linearGradient15772"
xlink:href="#linearGradient15762"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient11780"
id="linearGradient2057"
x1="15.737001"
y1="12.503600"
x2="53.570126"
y2="47.374317"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0.000000,-2.000000)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4981"
id="linearGradient4987"
x1="23.995985"
y1="20.105337"
x2="41.047836"
y2="37.959785"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0.000000,-2.000000)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient7895"
id="linearGradient7901"
x1="15.578875"
y1="16.285088"
x2="32.166405"
y2="28.394291"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient9647"
id="radialGradient2239"
cx="24.30225"
cy="33.30225"
fx="24.30225"
fy="33.30225"
r="12.30225"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.693981,-5.775714e-16,5.775714e-16,1.693981,-16.86529,-25.11111)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4981"
id="linearGradient2243"
gradientUnits="userSpaceOnUse"
x1="23.995985"
y1="20.105337"
x2="41.047836"
y2="37.959785"
gradientTransform="matrix(0.988373,0.000000,0.000000,0.988373,0.279002,0.278984)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2248"
id="radialGradient2254"
cx="16.75"
cy="10.666344"
fx="16.75"
fy="10.666344"
r="21.25"
gradientTransform="matrix(4.154957,-2.979206e-24,3.255657e-24,3.198723,-52.84553,-23.50921)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2256"
id="linearGradient2262"
x1="21.75"
y1="15.80225"
x2="24.30225"
y2="35.05225"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0.000000,-2.000000)" />
</defs>
<sodipodi:namedview
inkscape:guide-bbox="true"
showguides="true"
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="0.15294118"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="0.007276"
inkscape:cy="7.0544576"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="786"
inkscape:window-height="688"
inkscape:window-x="488"
inkscape:window-y="160"
inkscape:showpageshadow="false" />
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Stop</dc:title>
<dc:date>2005-10-16</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Andreas Nilsson</dc:title>
</cc:Agent>
</dc:creator>
<dc:subject>
<rdf:Bag>
<rdf:li>stop</rdf:li>
<rdf:li>halt</rdf:li>
<rdf:li>error</rdf:li>
</rdf:Bag>
</dc:subject>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
<dc:contributor>
<cc:Agent>
<dc:title>Jakub Steiner</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
sodipodi:type="arc"
style="opacity:0.63068183;color:#000000;fill:url(#radialGradient21650);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
id="path21642"
sodipodi:cx="25.125"
sodipodi:cy="36.75"
sodipodi:rx="15.75"
sodipodi:ry="9.375"
d="M 40.875 36.75 A 15.75 9.375 0 1 1 9.375,36.75 A 15.75 9.375 0 1 1 40.875 36.75 z"
transform="matrix(1.173803,0.000000,0.000000,0.600000,-5.265866,19.57500)" />
<path
style="fill:url(#linearGradient4987);fill-opacity:1;fill-rule:evenodd;stroke:#2c2c2c;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 15.591006,0.4919213 L 32.676311,0.4919213 L 45.497585,13.586385 L 45.497585,31.48003 L 32.848986,43.496929 L 15.418649,43.496929 L 2.4943857,30.658264 L 2.4943857,13.464078 L 15.591006,0.4919213 z "
id="path9480"
sodipodi:nodetypes="ccccccccc" />
<path
style="opacity:0.81318683;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2057);stroke-width:1.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
d="M 16.020655,1.5003424 L 32.248563,1.5003424 L 44.496456,13.922717 L 44.496456,31.037001 L 32.638472,42.48783 L 15.870253,42.48783 L 3.5090792,30.208718 L 3.5090792,13.84561 L 16.020655,1.5003424 z "
id="path9482"
sodipodi:nodetypes="ccccccccc" />
<path
style="opacity:0.28977272;fill:url(#radialGradient2254);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 15.6875,0.75 L 2.75,13.5625 L 2.75,30.5625 L 5.6875,33.46875 C 22.450041,33.526299 22.164665,20.450067 45.25,21.59375 L 45.25,13.6875 L 32.5625,0.75 L 15.6875,0.75 z "
id="path2241"
sodipodi:nodetypes="cccccccc" />
<path
style="fill:url(#radialGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2262);stroke-width:0.99999958;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 16.767175,10.5 L 12.5,14.767175 L 20.035075,22.30225 L 12.5,29.837325 L 16.767175,34.104501 L 24.30225,26.569425 L 31.837325,34.104501 L 36.104501,29.837325 L 28.569425,22.30225 L 36.104501,14.767175 L 31.837325,10.5 L 24.30225,18.035075 L 16.767175,10.5 z "
id="path2787" />
</g>
</svg>

Loading…
取消
儲存