2015-12-02 04:31:15 +08:00
|
|
|
import storePretender from 'helpers/store-pretender';
|
|
|
|
import fixturePretender from 'helpers/fixture-pretender';
|
|
|
|
|
2017-02-01 04:42:12 +08:00
|
|
|
export function parsePostData(query) {
|
2015-03-14 00:08:28 +08:00
|
|
|
const result = {};
|
2014-08-01 02:51:10 +08:00
|
|
|
query.split("&").forEach(function(part) {
|
2015-03-14 00:08:28 +08:00
|
|
|
const item = part.split("=");
|
2015-04-10 02:54:17 +08:00
|
|
|
const firstSeg = decodeURIComponent(item[0]);
|
|
|
|
const m = /^([^\[]+)\[([^\]]+)\]/.exec(firstSeg);
|
|
|
|
|
|
|
|
const val = decodeURIComponent(item[1]).replace(/\+/g, ' ');
|
|
|
|
if (m) {
|
|
|
|
result[m[1]] = result[m[1]] || {};
|
|
|
|
result[m[1]][m[2]] = val;
|
|
|
|
} else {
|
|
|
|
result[firstSeg] = val;
|
|
|
|
}
|
|
|
|
|
2014-08-01 02:51:10 +08:00
|
|
|
});
|
|
|
|
return result;
|
2017-02-01 04:42:12 +08:00
|
|
|
};
|
2014-08-01 02:51:10 +08:00
|
|
|
|
2014-08-01 05:26:44 +08:00
|
|
|
function response(code, obj) {
|
2014-08-01 02:51:10 +08:00
|
|
|
if (typeof code === "object") {
|
|
|
|
obj = code;
|
|
|
|
code = 200;
|
|
|
|
}
|
2014-08-01 05:26:44 +08:00
|
|
|
return [code, {"Content-Type": "application/json"}, obj];
|
2014-08-01 02:51:10 +08:00
|
|
|
}
|
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
const success = () => response({ success: true });
|
2015-12-02 04:31:15 +08:00
|
|
|
const loggedIn = () => !!Discourse.User.current();
|
2014-10-08 04:03:51 +08:00
|
|
|
|
2015-08-19 05:15:46 +08:00
|
|
|
|
2015-12-02 04:31:15 +08:00
|
|
|
const helpers = { response, success, parsePostData };
|
2015-04-02 02:18:46 +08:00
|
|
|
|
2014-08-01 02:51:10 +08:00
|
|
|
export default function() {
|
2015-04-02 02:18:46 +08:00
|
|
|
|
2015-03-14 00:08:28 +08:00
|
|
|
const server = new Pretender(function() {
|
2015-12-02 04:31:15 +08:00
|
|
|
storePretender.call(this, helpers);
|
|
|
|
const fixturesByUrl = fixturePretender.call(this, helpers);
|
2014-08-02 05:26:43 +08:00
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get('/admin/plugins', () => response({ plugins: [] }));
|
2015-08-28 04:59:36 +08:00
|
|
|
|
2016-06-07 03:23:19 +08:00
|
|
|
this.get('/composer_messages', () => response({ composer_messages: [] }));
|
2015-04-02 02:18:46 +08:00
|
|
|
|
|
|
|
this.get("/latest.json", () => {
|
|
|
|
const json = fixturesByUrl['/latest.json'];
|
|
|
|
|
|
|
|
if (loggedIn()) {
|
|
|
|
// Stuff to let us post
|
|
|
|
json.topic_list.can_create_topic = true;
|
|
|
|
json.topic_list.draft_key = "new_topic";
|
|
|
|
json.topic_list.draft_sequence = 1;
|
|
|
|
}
|
|
|
|
return response(json);
|
|
|
|
});
|
|
|
|
|
2016-11-11 02:33:31 +08:00
|
|
|
this.get('/tags', () => {
|
|
|
|
return response({ tags: [{
|
|
|
|
id: 'eviltrout',
|
|
|
|
count: 1
|
|
|
|
}] });
|
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get(`/u/eviltrout/emails.json`, () => {
|
2016-11-23 03:02:47 +08:00
|
|
|
return response({ email: 'eviltrout@example.com' });
|
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get('/u/eviltrout.json', () => {
|
|
|
|
const json = fixturesByUrl['/u/eviltrout.json'];
|
2016-11-11 03:35:53 +08:00
|
|
|
json.user.can_edit = loggedIn();
|
2015-05-13 22:42:36 +08:00
|
|
|
return response(json);
|
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get('/u/eviltrout/summary.json', () => {
|
2016-08-03 01:21:06 +08:00
|
|
|
return response({
|
|
|
|
user_summary: {
|
|
|
|
topics: [],
|
|
|
|
topic_ids: [],
|
|
|
|
replies: [],
|
|
|
|
links: []
|
|
|
|
},
|
|
|
|
topics: [],
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get('/u/eviltrout/invited_count.json', () => {
|
2016-11-11 02:33:31 +08:00
|
|
|
return response({
|
|
|
|
"counts": { "pending": 1, "redeemed": 0, "total": 0 }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get('/u/eviltrout/invited.json', () => {
|
2016-11-11 02:33:31 +08:00
|
|
|
return response({ "invites": [ {id: 1} ] });
|
|
|
|
});
|
|
|
|
|
|
|
|
this.get('/topics/private-messages/eviltrout.json', () => {
|
|
|
|
return response({ topic_list: { topics: [] } });
|
|
|
|
});
|
|
|
|
|
2016-07-01 01:55:44 +08:00
|
|
|
this.get('/clicks/track', success);
|
|
|
|
|
2016-08-10 00:16:29 +08:00
|
|
|
this.get('/search', request => {
|
|
|
|
if (request.queryParams.q === 'posts') {
|
|
|
|
return response({
|
|
|
|
posts: [{
|
|
|
|
id: 1234
|
|
|
|
}]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return response({});
|
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.put('/u/eviltrout.json', () => response({ user: {} }));
|
2015-05-13 22:42:36 +08:00
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get("/t/280.json", () => response(fixturesByUrl['/t/280/1.json']));
|
|
|
|
this.get("/t/28830.json", () => response(fixturesByUrl['/t/28830/1.json']));
|
|
|
|
this.get("/t/9.json", () => response(fixturesByUrl['/t/9/1.json']));
|
2016-11-30 01:59:42 +08:00
|
|
|
this.get("/t/12.json", () => response(fixturesByUrl['/t/12/1.json']));
|
2015-08-14 00:49:13 +08:00
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get("/t/id_for/:slug", () => {
|
2014-09-17 23:18:41 +08:00
|
|
|
return response({id: 280, slug: "internationalization-localization", url: "/t/internationalization-localization/280"});
|
|
|
|
});
|
|
|
|
|
2016-07-01 01:55:44 +08:00
|
|
|
this.delete('/t/:id', success);
|
|
|
|
this.put('/t/:id/recover', success);
|
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get("/404-body", () => {
|
2014-08-02 05:26:43 +08:00
|
|
|
return [200, {"Content-Type": "text/html"}, "<div class='page-not-found'>not found</div>"];
|
|
|
|
});
|
|
|
|
|
2015-04-10 06:33:37 +08:00
|
|
|
this.delete('/draft.json', success);
|
2015-08-14 00:49:13 +08:00
|
|
|
this.post('/draft.json', success);
|
2015-04-10 06:33:37 +08:00
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get('/u/:username/staff-info.json', () => response({}));
|
2015-04-22 02:36:46 +08:00
|
|
|
|
2016-01-05 04:18:09 +08:00
|
|
|
this.get('/post_action_users', () => {
|
|
|
|
return response({
|
|
|
|
post_action_users: [
|
|
|
|
{id: 1, username: 'eviltrout', avatar_template: '/user_avatar/default/eviltrout/{size}/1.png', username_lower: 'eviltrout' }
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
this.get('/post_replies', () => {
|
|
|
|
return response({ post_replies: [{ id: 1234, cooked: 'wat' }] });
|
|
|
|
});
|
|
|
|
|
|
|
|
this.get('/post_reply_histories', () => {
|
|
|
|
return response({ post_reply_histories: [{ id: 1234, cooked: 'wat' }] });
|
|
|
|
});
|
|
|
|
|
2016-06-15 02:31:51 +08:00
|
|
|
this.get('/category_hashtags/check', () => {
|
|
|
|
return response({ valid: [{ slug: "bug", url: '/c/bugs' }] });
|
|
|
|
});
|
|
|
|
|
2016-08-30 04:47:44 +08:00
|
|
|
this.get("/categories_and_latest", () => response(fixturesByUrl["/categories_and_latest.json"]));
|
|
|
|
|
2016-01-05 04:18:09 +08:00
|
|
|
this.put('/categories/:category_id', request => {
|
2016-07-28 23:57:30 +08:00
|
|
|
|
2015-07-03 00:06:24 +08:00
|
|
|
const category = parsePostData(request.requestBody);
|
2016-07-28 23:57:30 +08:00
|
|
|
|
|
|
|
if (category.email_in === "duplicate@example.com") {
|
|
|
|
return response(422, {"errors": ['duplicate email']});
|
|
|
|
}
|
|
|
|
|
2015-07-03 00:06:24 +08:00
|
|
|
return response({category});
|
|
|
|
});
|
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get('/draft.json', () => response({}));
|
2015-03-13 14:45:55 +08:00
|
|
|
|
2015-04-11 05:00:50 +08:00
|
|
|
this.put('/queued_posts/:queued_post_id', function(request) {
|
|
|
|
return response({ queued_post: {id: request.params.queued_post_id } });
|
|
|
|
});
|
|
|
|
|
|
|
|
this.get('/queued_posts', function() {
|
|
|
|
return response({
|
2015-05-20 01:00:50 +08:00
|
|
|
queued_posts: [{id: 1, raw: 'queued post text', can_delete_user: true}]
|
2015-04-11 05:00:50 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-08-01 02:51:10 +08:00
|
|
|
this.post('/session', function(request) {
|
2015-03-14 00:08:28 +08:00
|
|
|
const data = parsePostData(request.requestBody);
|
2014-08-01 02:51:10 +08:00
|
|
|
|
|
|
|
if (data.password === 'correct') {
|
2014-08-01 05:26:44 +08:00
|
|
|
return response({username: 'eviltrout'});
|
2014-08-01 02:51:10 +08:00
|
|
|
}
|
2016-08-06 00:01:16 +08:00
|
|
|
|
|
|
|
if (data.password === 'not-activated') {
|
|
|
|
return response({ error: "not active",
|
|
|
|
reason: "not_activated",
|
|
|
|
sent_to_email: '<small>eviltrout@example.com</small>',
|
|
|
|
current_email: '<small>current@example.com</small>' });
|
|
|
|
}
|
|
|
|
|
2017-04-06 04:14:22 +08:00
|
|
|
if (data.password === 'not-activated-edit') {
|
|
|
|
return response({ error: "not active",
|
|
|
|
reason: "not_activated",
|
|
|
|
sent_to_email: 'eviltrout@example.com',
|
|
|
|
current_email: 'current@example.com' });
|
|
|
|
}
|
|
|
|
|
2014-08-01 05:26:44 +08:00
|
|
|
return response(400, {error: 'invalid login'});
|
2014-08-01 02:51:10 +08:00
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.post('/u/action/send_activation_email', success);
|
2017-04-06 04:14:22 +08:00
|
|
|
this.put('/u/update-activation-email', success);
|
2016-08-06 00:01:16 +08:00
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get('/u/hp.json', function() {
|
2014-08-01 05:26:44 +08:00
|
|
|
return response({"value":"32faff1b1ef1ac3","challenge":"61a3de0ccf086fb9604b76e884d75801"});
|
2014-08-01 02:51:10 +08:00
|
|
|
});
|
|
|
|
|
2014-08-01 05:06:00 +08:00
|
|
|
this.get('/session/csrf', function() {
|
2014-08-01 05:26:44 +08:00
|
|
|
return response({"csrf":"mgk906YLagHo2gOgM1ddYjAN4hQolBdJCqlY6jYzAYs="});
|
2014-08-01 05:06:00 +08:00
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.get('/u/check_username', function(request) {
|
2014-08-01 02:51:10 +08:00
|
|
|
if (request.queryParams.username === 'taken') {
|
2014-08-01 05:26:44 +08:00
|
|
|
return response({available: false, suggestion: 'nottaken'});
|
2014-08-01 02:51:10 +08:00
|
|
|
}
|
2014-08-01 05:26:44 +08:00
|
|
|
return response({available: true});
|
2014-08-01 02:51:10 +08:00
|
|
|
});
|
|
|
|
|
2017-03-29 00:16:58 +08:00
|
|
|
this.post('/u', () => response({success: true}));
|
2014-08-01 05:59:52 +08:00
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get('/login.html', () => [200, {}, 'LOGIN PAGE']);
|
2014-10-08 04:03:51 +08:00
|
|
|
|
|
|
|
this.delete('/posts/:post_id', success);
|
|
|
|
this.put('/posts/:post_id/recover', success);
|
2016-01-05 04:18:09 +08:00
|
|
|
this.get('/posts/:post_id/expand-embed', success);
|
2015-03-07 01:37:24 +08:00
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.put('/posts/:post_id', request => {
|
2015-04-10 02:54:17 +08:00
|
|
|
const data = parsePostData(request.requestBody);
|
|
|
|
data.post.id = request.params.post_id;
|
|
|
|
data.post.version = 2;
|
|
|
|
return response(200, data.post);
|
2015-04-02 02:18:46 +08:00
|
|
|
});
|
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get('/t/403.json', () => response(403, {}));
|
|
|
|
this.get('/t/404.json', () => response(404, "not found"));
|
|
|
|
this.get('/t/500.json', () => response(502, {}));
|
2015-05-28 01:53:49 +08:00
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.put('/t/:slug/:id', request => {
|
2015-04-02 02:18:46 +08:00
|
|
|
const data = parsePostData(request.requestBody);
|
|
|
|
|
|
|
|
return response(200, { basic_topic: {id: request.params.id,
|
|
|
|
title: data.title,
|
|
|
|
fancy_title: data.title,
|
2015-04-09 02:17:21 +08:00
|
|
|
slug: request.params.slug } });
|
2015-04-02 02:18:46 +08:00
|
|
|
});
|
2016-12-14 17:26:16 +08:00
|
|
|
|
|
|
|
this.get("groups", () => {
|
|
|
|
return response(200, fixturesByUrl['/groups.json']);
|
|
|
|
});
|
2015-04-02 02:18:46 +08:00
|
|
|
|
2016-04-12 01:16:37 +08:00
|
|
|
this.get("/groups/discourse/topics.json", () => {
|
|
|
|
return response(200, fixturesByUrl['/groups/discourse/posts.json']);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.get("/groups/discourse/mentions.json", () => {
|
|
|
|
return response(200, fixturesByUrl['/groups/discourse/posts.json']);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.get("/groups/discourse/messages.json", () => {
|
|
|
|
return response(200, fixturesByUrl['/groups/discourse/posts.json']);
|
|
|
|
});
|
|
|
|
|
2015-12-02 05:04:13 +08:00
|
|
|
this.get('/t/:topic_id/posts.json', request => {
|
|
|
|
const postIds = request.queryParams.post_ids;
|
|
|
|
const posts = postIds.map(p => ({id: parseInt(p), post_number: parseInt(p) }));
|
|
|
|
return response(200, { post_stream: { posts } });
|
|
|
|
});
|
|
|
|
|
|
|
|
this.get('/posts/:post_id/reply-history.json', () => {
|
|
|
|
return response(200, [ { id: 2222, post_number: 2222 } ]);
|
|
|
|
});
|
|
|
|
|
2016-07-01 01:55:44 +08:00
|
|
|
this.post('/user_badges', () => response(200, fixturesByUrl['/user_badges']));
|
|
|
|
this.delete('/user_badges/:badge_id', success);
|
|
|
|
|
2015-04-02 02:18:46 +08:00
|
|
|
this.post('/posts', function(request) {
|
|
|
|
const data = parsePostData(request.requestBody);
|
|
|
|
|
|
|
|
if (data.title === "this title triggers an error") {
|
|
|
|
return response(422, {errors: ['That title has already been taken']});
|
|
|
|
}
|
2015-04-10 06:33:37 +08:00
|
|
|
|
|
|
|
if (data.raw === "enqueue this content please") {
|
|
|
|
return response(200, { success: true, action: 'enqueued' });
|
|
|
|
}
|
|
|
|
|
|
|
|
return response(200, {
|
|
|
|
success: true,
|
|
|
|
action: 'create_post',
|
|
|
|
post: {id: 12345, topic_id: 280, topic_slug: 'internationalization-localization'}
|
|
|
|
});
|
2015-04-02 02:18:46 +08:00
|
|
|
});
|
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.post('/topics/timings', () => response(200, {}));
|
|
|
|
|
|
|
|
const siteText = {id: 'site.test', value: 'Test McTest'};
|
2015-12-01 04:22:58 +08:00
|
|
|
const overridden = {id: 'site.overridden', value: 'Overridden', overridden: true };
|
2016-08-06 00:01:16 +08:00
|
|
|
|
|
|
|
this.get('/admin/users/list/active.json', () => {
|
|
|
|
return response(200, [
|
|
|
|
{id: 1, username: 'eviltrout', email: '<small>eviltrout@example.com</small>'}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
2015-12-01 04:22:58 +08:00
|
|
|
this.get('/admin/customize/site_texts', request => {
|
|
|
|
|
|
|
|
if (request.queryParams.overridden) {
|
2015-12-02 04:31:15 +08:00
|
|
|
return response(200, {site_texts: [overridden] });
|
2015-12-01 04:22:58 +08:00
|
|
|
} else {
|
2015-12-02 04:31:15 +08:00
|
|
|
return response(200, {site_texts: [siteText, overridden] });
|
2015-12-01 04:22:58 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
this.get('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));
|
|
|
|
this.delete('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));
|
|
|
|
|
|
|
|
this.put('/admin/customize/site_texts/:key', request => {
|
|
|
|
const result = parsePostData(request.requestBody);
|
|
|
|
result.id = request.params.key;
|
|
|
|
result.can_revert = true;
|
|
|
|
return response(200, {site_text: result});
|
2015-04-02 02:18:46 +08:00
|
|
|
});
|
2016-07-06 02:58:58 +08:00
|
|
|
|
|
|
|
this.get('/tag_groups', () => response(200, {tag_groups: []}));
|
2016-07-01 01:55:44 +08:00
|
|
|
this.post('/admin/users/:user_id/generate_api_key', success);
|
|
|
|
this.delete('/admin/users/:user_id/revoke_api_key', success);
|
|
|
|
this.post('/admin/badges', success);
|
|
|
|
this.delete('/admin/badges/:id', success);
|
2016-12-09 05:08:44 +08:00
|
|
|
|
2017-06-29 04:56:44 +08:00
|
|
|
this.get('/admin/watched_words', () => {
|
|
|
|
return response(200, fixturesByUrl['/admin/watched_words.json']);
|
|
|
|
});
|
|
|
|
this.delete('/admin/watched_words/:id.json', success);
|
|
|
|
|
|
|
|
this.post('/admin/watched_words.json', request => {
|
|
|
|
const result = parsePostData(request.requestBody);
|
|
|
|
result.id = new Date().getTime();
|
|
|
|
return response(200, result);
|
|
|
|
});
|
|
|
|
|
2016-12-09 05:08:44 +08:00
|
|
|
this.get('/onebox', request => {
|
2017-05-19 01:36:45 +08:00
|
|
|
if (request.queryParams.url === 'http://www.example.com/has-title.html' ||
|
|
|
|
request.queryParams.url === 'http://www.example.com/has-title-and-a-url-that-is-more-than-80-characters-because-thats-good-for-seo-i-guess.html') {
|
2016-12-09 05:08:44 +08:00
|
|
|
return [
|
|
|
|
200,
|
|
|
|
{"Content-Type": "application/html"},
|
|
|
|
'<aside class="onebox"><article class="onebox-body"><h3><a href="http://www.example.com/article.html">An interesting article</a></h3></article></aside>'
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request.queryParams.url === 'http://www.example.com/no-title.html') {
|
|
|
|
return [
|
|
|
|
200,
|
|
|
|
{"Content-Type": "application/html"},
|
|
|
|
'<aside class="onebox"><article class="onebox-body"><p>No title</p></article></aside>'
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2017-02-09 04:34:42 +08:00
|
|
|
if (request.queryParams.url.indexOf('/internal-page.html') > -1) {
|
|
|
|
return [
|
|
|
|
200,
|
|
|
|
{"Content-Type": "application/html"},
|
|
|
|
'<aside class="onebox"><article class="onebox-body"><h3><a href="/internal-page.html">Internal Page 4 U</a></h3></article></aside>'
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2016-12-09 05:08:44 +08:00
|
|
|
return [404, {"Content-Type": "application/html"}, ''];;
|
|
|
|
});
|
2015-04-02 02:18:46 +08:00
|
|
|
});
|
2014-08-01 05:26:44 +08:00
|
|
|
|
|
|
|
server.prepareBody = function(body){
|
|
|
|
if (body && typeof body === "object") {
|
|
|
|
return JSON.stringify(body);
|
|
|
|
}
|
2014-08-02 05:26:43 +08:00
|
|
|
return body;
|
2014-08-01 05:26:44 +08:00
|
|
|
};
|
|
|
|
|
2014-08-01 02:51:10 +08:00
|
|
|
server.unhandledRequest = function(verb, path) {
|
2015-03-14 00:08:28 +08:00
|
|
|
const error = 'Unhandled request in test environment: ' + path + ' (' + verb + ')';
|
2014-08-01 06:44:32 +08:00
|
|
|
window.console.error(error);
|
|
|
|
throw error;
|
2014-08-01 02:51:10 +08:00
|
|
|
};
|
|
|
|
|
2015-11-24 05:45:05 +08:00
|
|
|
server.checkPassthrough = request => request.requestHeaders['Discourse-Script'];
|
2014-08-01 02:51:10 +08:00
|
|
|
return server;
|
2017-06-15 01:57:58 +08:00
|
|
|
}
|