Compare commits

...

115 commits

Author SHA1 Message Date
29fe54895d disable charting 2023-05-21 18:47:15 +02:00
6809d45f4b Merge branch 'update-mfm-core' into mk.absturztau.be 2023-05-21 18:46:11 +02:00
7e808d663a Merge branch 'main' into mk.absturztau.be 2023-05-21 16:39:43 +02:00
3fc984ad2d Merge branch 'refactor-deliver-actor' into mk.absturztau.be 2023-05-06 19:59:36 +02:00
c3a6dcd6f0 Revert "Merge branch 'refactor-deliver-actor' into mk.absturztau.be"
This reverts commit 76b4e4ee08, reversing
changes made to 22f2ac427f.
2023-05-06 19:59:18 +02:00
3d1a991051 Merge branch 'main' into mk.absturztau.be 2023-05-06 19:54:28 +02:00
ba77a81cc0
fix: require async for deliver queue 2023-05-04 21:43:09 +02:00
b04ef21b6e
fix missing parameter name 2023-05-01 20:45:47 +02:00
965ee4b041
fix for delivering multiple activities at once 2023-05-01 12:41:39 +02:00
b374a79eb1
activitypub: no longer deliver chat read
Prospectively, the messaging messages will be removed as objects so
it does not make sense to refactor this to match the remainder of
refactoring away the user from deliver. It is just easier to remove
this use of delivering something that is not an activity.

Changelog: Removed
2023-04-24 22:55:38 +02:00
c978a687e9
remove unused user parameter from deliver 2023-04-24 22:55:36 +02:00
4dc8822d05
remove unnecesary intermediate variables 2023-04-24 22:54:23 +02:00
ff4b6d932e
refactor deliver to extract actor from activity 2023-04-24 22:54:12 +02:00
a786fd99fc
make delivery manager author agnostic 2023-04-24 22:07:05 +02:00
76b4e4ee08 Merge branch 'refactor-deliver-actor' into mk.absturztau.be 2023-04-22 12:05:52 +02:00
22f2ac427f Merge branch 'main' into mk.absturztau.be 2023-04-22 12:04:37 +02:00
4683e1a93e
activitypub: no longer deliver chat read
Prospectively, the messaging messages will be removed as objects so
it does not make sense to refactor this to match the remainder of
refactoring away the user from deliver. It is just easier to remove
this use of delivering something that is not an activity.

Changelog: Removed
2023-04-20 22:46:15 +02:00
449a436f3e
remove unused user parameter from deliver 2023-04-20 22:46:15 +02:00
8842a6958c
remove unnecesary intermediate variables 2023-04-20 22:46:14 +02:00
071f4fa82a
refactor deliver to extract actor from activity 2023-04-20 22:46:14 +02:00
cec77dafa8
make delivery manager author agnostic 2023-04-20 22:46:13 +02:00
e4ab208f68 Merge branch 'fix/download-button-in-audio-player' into mk.absturztau.be 2023-03-19 12:55:32 +01:00
43f5f7f839 fix download button in wavesurfer 2023-03-19 12:46:07 +01:00
6a8128f255 Merge branch 'main' into mk.absturztau.be 2023-03-05 08:47:50 +01:00
eef3f3276d Merge branch 'main' into mk.absturztau.be 2023-02-19 11:53:56 +01:00
198bb55c4b resolve merge conflicts 2023-02-19 11:51:24 +01:00
bb12bbd179 resolve merge conflicts 2023-02-19 11:50:31 +01:00
504ad8ed68 Merge branch 'main' into mk.absturztau.be 2023-02-19 11:49:47 +01:00
16902a26a3 resolve merge conflicts 2023-02-12 08:58:17 +01:00
5d6cceda49 Merge branch 'main' into mk.absturztau.be 2023-02-11 08:27:24 +01:00
e4890de172 Merge branch 'main' into mk.absturztau.be 2023-02-05 09:03:59 +01:00
5d99ccacfd resolve merge conflicts 2023-01-21 08:39:53 +01:00
9354950c23 Revert "fix remote files?"
This reverts commit 8cd47bbcce.
2022-12-29 23:29:58 +01:00
8cd47bbcce fix remote files? 2022-12-29 23:13:38 +01:00
98a845eda4 resolve merge conflicts 2022-12-29 21:51:57 +01:00
1b3d89c40d Merge branch 'fix/endpoint-children' into mk.absturztau.be 2022-12-26 09:09:41 +01:00
55d8d5a626 Merge branch 'main' into mk.absturztau.be 2022-12-26 09:09:34 +01:00
e89c0135bd removing joins to avatar and banners in children endpoint 2022-12-26 09:09:11 +01:00
3900d2f716 Merge branch 'main' into mk.absturztau.be 2022-12-18 14:43:12 +01:00
376c36bde7 Merge branch 'main' into mk.absturztau.be 2022-12-03 11:57:11 +01:00
556a5c2c46 Merge branch 'web-worker' into mk.absturztau.be 2022-11-27 09:27:07 +01:00
4b82d23594 Merge branch 'main' into mk.absturztau.be 2022-11-27 09:24:34 +01:00
61ca61295c Merge branch 'main' into mk.absturztau.be 2022-11-20 08:57:30 +01:00
a6474ea701 resolve merge conflicts 2022-10-19 18:11:41 +02:00
21ad9db806 increase image description to 2048 2022-09-17 10:49:07 +02:00
6930a7e3e6 increase image description to 2048 2022-09-17 10:48:58 +02:00
e3f48872b0 yarny stuff 2022-09-17 09:08:02 +02:00
Puniko
8fb8e60ae3 resolve merge conflicts 2022-08-28 10:14:26 +02:00
Puniko
dfcc9299b2 resolve merge conflicts 2022-08-07 11:14:33 +02:00
Puniko
8ed30be47c Merge branch 'develop' into mk.absturztau.be 2022-06-11 13:44:54 +02:00
Puniko
64c3e40d2c Merge branch 'develop' into mk.absturztau.be 2022-05-24 17:46:11 +02:00
Puniko
3ea802d882 add searx.absturztau.be to search mfm 2022-05-21 13:32:59 +02:00
Puniko
ab0f2dc922 Merge branch 'develop' into mk.absturztau.be 2022-05-21 13:15:15 +02:00
Puniko
e06c7552bf fix libopenmpt and ansi 2022-05-14 17:13:48 +02:00
Puniko
b4515f653c resolve merge conflicts 2022-05-14 16:34:21 +02:00
Puniko
1d34d037cb add star-to-fav patch 2022-04-26 19:56:54 +02:00
Puniko
c305d990ba Merge branch 'develop' into mk.absturztau.be 2022-04-26 19:35:07 +02:00
Puniko
38c98ab0bf add first draft of the ansi viewer 2022-04-15 10:34:46 +02:00
Puniko
443bbeeeaf resolve merge conflicts 2022-04-15 08:51:11 +02:00
Puniko
d71f91836a resolve merge conflicts 2022-04-05 17:49:22 +02:00
Puniko
58b943f58a Merge branch 'develop' into mk.absturztau.be 2022-03-16 19:23:13 +01:00
Puniko
a55238bdc5 resolve merge conflicts 2022-03-09 19:17:29 +01:00
Puniko
f38eb5cde8 Merge branch 'develop' into mk.absturztau.be 2022-02-18 16:24:16 +01:00
Puniko
12ae4766cc add wavesurfer 2022-02-18 16:23:47 +01:00
Puniko
85dde2e251 Merge branch 'develop' into mk.absturztau.be 2022-02-16 16:45:47 +01:00
Puniko
ba953b90ed Merge branch 'develop' into mk.absturztau.be 2022-02-11 14:22:25 +01:00
Puniko
b304214f82 Merge branch 'develop' into mk.absturztau.be 2022-02-09 08:48:25 +01:00
Puniko
b7ee038522 Merge branch 'develop' into mk.absturztau.be 2022-02-07 08:08:17 +01:00
Puniko
b293e66f61 fix settings 2022-02-02 10:30:29 +01:00
Puniko
52d61835e3 resolve merge conflicts 2022-02-01 16:50:57 +01:00
Puniko
ec9867a614 fix preview types for modules 2022-01-26 17:08:31 +01:00
Puniko
6f42a5408b resolve merge conflicts 2022-01-26 17:02:57 +01:00
Puniko
862123abf8 fix emojis 2022-01-24 17:52:13 +01:00
Puniko
3edb610ec0 revert chat back 2022-01-24 17:28:18 +01:00
Puniko
1af0a99f61 hotfix clips 2022-01-24 17:08:01 +01:00
Puniko
18caf3ee3a Merge branch 'develop' into mk.absturztau.be 2022-01-24 16:33:32 +01:00
Puniko
0bd7017828 Merge branch 'develop' into mk.absturztau.be 2022-01-21 10:29:13 +01:00
Puniko
d838168cb5 resolve conflicts 2022-01-21 10:26:11 +01:00
Puniko
3edf4f1c97 resolve merge conflicts 2022-01-11 09:57:48 +01:00
Puniko
40254ab54a Merge branch 'develop' into mk.absturztau.be 2022-01-03 16:43:02 +01:00
Puniko
5cd1e2cb8a Merge branch 'develop' into mk.absturztau.be 2021-12-17 20:33:23 +01:00
Puniko
db2ce161c6 resolve conflicts 2021-12-15 20:35:54 +01:00
Puniko
a5b10b7413 Merge branch 'develop' into mk.absturztau.be 2021-12-04 19:59:42 +01:00
Puniko
52995fcb11 Merge branch 'develop' into mk.absturztau.be 2021-11-29 19:53:25 +01:00
Puniko
1bb685cd1d resolve merge conflicts and s3m, it support 2021-11-23 20:31:11 +01:00
Puniko
1b21d6aa6f Merge branch 'develop' into mk.absturztau.be 2021-11-13 09:22:08 +01:00
Puniko
d4c61bef5c resolve merge conflicts and patch fixes 2021-11-13 09:17:17 +01:00
Puniko
96c410a6f1 resolve merge conflicts 2021-10-31 12:28:46 +01:00
Puniko
ae6038a257 resolve merge conflicts 2021-10-25 18:52:47 +02:00
Puniko
bd5192beb3 fix value on theme manage 2021-10-16 14:29:56 +02:00
Puniko
32f3863712 resolve conflicts 2021-10-16 14:21:30 +02:00
Puniko
91b5a7dbb5 resolve merge conflicts 2021-10-03 11:26:37 +02:00
Puniko
bad9dd8280 resolve merge conflicts 2021-09-22 18:01:16 +02:00
Puniko
5613585a51 Merge branch 'develop' into mk.absturztau.be 2021-09-13 20:21:07 +02:00
Puniko
11f2a8888e Merge branch 'develop' into mk.absturztau.be 2021-09-05 09:36:51 +02:00
Puniko
79fed0b14b fix search engine 2021-09-05 09:36:47 +02:00
Puniko
eea03fcbe0 Merge branch 'feature/user-instance-mute' into mk.absturztau.be 2021-08-29 11:13:08 +02:00
Puniko
295aec9dca applied local user mentioning 2021-08-29 11:09:03 +02:00
Puniko
29ef35064c Merge branch 'develop' into mk.absturztau.be 2021-08-29 10:57:12 +02:00
romaboo
0b63f17748
Update ja-JP instanceMuteDescription based on sousuke's suggestion
Co-authored-by: sousuke0422 <sousuke20xx@gmail.com>
2021-08-28 14:42:14 +01:00
romaboo
3d75979ec6
Changed Ja-JP instance mute title string to one suggested by sousuke
Co-authored-by: sousuke0422 <sousuke20xx@gmail.com>
2021-08-28 12:48:45 +01:00
romaboo
9f846a6d9c
Apply japanese string suggestions from robflop
Co-authored-by: Robin B. <robflop98@outlook.com>
2021-08-27 20:43:36 +01:00
puffaboo
9d793f268f Added missing semicolon 2021-08-26 22:02:29 +01:00
puffaboo
56c4d83b46 Updated changelog 2021-08-26 22:00:32 +01:00
puffaboo
78cc88c556 Added notification & streaming timeline muting 2021-08-26 19:29:58 +01:00
puffaboo
ccfa680772 cleaned up filtering of bad instance mutes and added a refresh at the end for the list on the client 2021-08-26 19:29:26 +01:00
puffaboo
b1d5ac36a4 Added filtering and trimming for instance mutes 2021-08-26 19:18:33 +01:00
puffaboo
ef5709406c added psql query for removal of muted notes 2021-08-26 18:07:02 +01:00
puffaboo
ffdbf751df Added settable config for muted instances 2021-08-26 16:38:32 +01:00
Puniko
e03c48298a Merge branch 'develop' into mk.absturztau.be 2021-08-24 20:58:19 +02:00
Puniko
5101ab0b85 Merge branch 'develop' into mk.absturztau.be 2021-08-21 11:39:36 +02:00
b2ae821c8a Merge branch 'mod-player' into mk.absturztau.be 2021-08-21 00:07:14 +02:00
9287c08bc5 added proper controls 2021-08-18 22:58:39 +02:00
5774ad440a first version of module player using libopenmpt 2021-08-17 23:50:38 +02:00
f684894c60 add vimlocal to ignore 2021-08-17 23:50:10 +02:00
38 changed files with 1009 additions and 133 deletions

2
.gitignore vendored
View file

@ -10,6 +10,8 @@
# nano
.swp
# vim
/.vimlocal
# vimlocal
.vimlocal

View file

@ -789,6 +789,7 @@ auto: "Auto"
themeColor: "Instance Ticker Color"
size: "Size"
numberOfColumn: "Number of columns"
searchByGoogle: "SearX"
instanceDefaultLightTheme: "Instance-wide default light theme"
instanceDefaultDarkTheme: "Instance-wide default dark theme"
instanceDefaultThemeDescription: "Enter the theme code in object format."

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1,15 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class userInstanceBlocks1629968054000 implements MigrationInterface {
name = 'userInstanceBlocks1629968054000'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user_profile" ADD "mutedInstances" jsonb NOT NULL DEFAULT '[]'`);
await queryRunner.query(`COMMENT ON COLUMN "user_profile"."mutedInstances" IS 'List of instances muted by the user.'`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "mutedInstances"`);
}
}

View file

@ -170,7 +170,7 @@ export async function toHtml(mfmText: string, mentions?: string[]): Promise<stri
async search(node) {
const a = doc.createElement('a');
a.href = `https://www.google.com/search?q=${node.props.query}`;
a.href = `https://searx.absturztau.be/?q=${node.props.query}`;
a.textContent = node.props.content;
return a;
},

View file

@ -7,6 +7,7 @@ import { Webhook, webhookEventTypes } from '@/models/entities/webhook.js';
import { IActivity } from '@/remote/activitypub/type.js';
import { envOption } from '@/env.js';
import { MINUTE } from '@/const.js';
import { DbResolver } from '@/remote/activitypub/db-resolver.js';
import processDeliver from './processors/deliver.js';
import processInbox from './processors/inbox.js';
@ -83,27 +84,47 @@ webhookDeliverQueue
.on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`))
.on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`));
export function deliver(user: ThinUser, content: unknown, to: string | null) {
export async function deliver(content: IActivity|IActivity[], to: string | null) {
if (content == null) return null;
if (to == null) return null;
const data = {
user: {
id: user.id,
},
content,
to,
};
// group activities by actor
const contentArray = Array.isArray(content) ? content : [content];
let byActor = contentArray.reduce((acc, activity) => {
if (activity.actor == null) throw new Error("Cannot deliver activity without actor.");
if (!(activity.actor in acc)) {
acc[activity.actor] = [];
}
acc[activity.actor].push(activity);
return acc;
}, []);
return deliverQueue.add(data, {
attempts: config.deliverJobMaxAttempts,
timeout: MINUTE,
backoff: {
type: 'apBackoff',
},
removeOnComplete: true,
removeOnFail: true,
});
// add groups to deliver queue
const dbResolver = new DbResolver();
for (const actor in byActor) {
// extract user from the Activity
const user = await dbResolver.getUserFromApId(actor);
if (!user) throw new Error("Actor not found, cannot deliver.");
if (user.host != null) throw new Error("Cannot deliver for remote actor.");
// add item to deliver queue
const data = {
user: {
id: user.id,
},
content: byActor[actor],
to,
};
deliverQueue.add(data, {
attempts: config.deliverJobMaxAttempts,
timeout: MINUTE,
backoff: {
type: 'apBackoff',
},
removeOnComplete: true,
removeOnFail: true,
});
}
}
export function inbox(activity: IActivity, signature: httpSignature.IParsedSignature) {

View file

@ -9,7 +9,7 @@ export type DeliverJobData = {
/** Actor */
user: ThinUser;
/** Activity */
content: unknown;
content: IActivity;
/** inbox URL to deliver */
to: string;
};

View file

@ -14,6 +14,7 @@ interface IEveryoneRecipe extends IRecipe {
interface IFollowersRecipe extends IRecipe {
type: 'Followers';
followee: ILocalUser;
}
interface IDirectRecipe extends IRecipe {
@ -32,26 +33,24 @@ const isDirect = (recipe: any): recipe is IDirectRecipe =>
//#endregion
export class DeliverManager {
private actor: { id: User['id']; host: null; };
private activity: any;
private recipes: IRecipe[] = [];
/**
* Constructor
* @param actor Actor
* @param activity Activity to deliver
*/
constructor(actor: { id: User['id']; host: null; }, activity: any) {
this.actor = actor;
constructor(activity: any) {
this.activity = activity;
}
/**
* Add recipe for followers deliver
*/
public addFollowersRecipe() {
public addFollowersRecipe(followee: ILocalUser) {
const deliver = {
type: 'Followers',
followee,
} as IFollowersRecipe;
this.addRecipe(deliver);
@ -89,8 +88,6 @@ export class DeliverManager {
* Execute delivers
*/
public async execute() {
if (!Users.isLocalUser(this.actor)) return;
const inboxes = new Set<string>();
/*
@ -116,21 +113,25 @@ export class DeliverManager {
}
}
if (this.recipes.some(r => isFollowers(r))) {
// followers deliver
const followers = await Followings.createQueryBuilder('followings')
// return either the shared inbox (if available) or the individual inbox
.select('COALESCE(followings.followerSharedInbox, followings.followerInbox)', 'inbox')
// so we don't have to make our inboxes Set work as hard
.distinct(true)
// ...for the specific actors followers
.where('followings.followeeId = :actorId', { actorId: this.actor.id })
// don't deliver to ourselves
.andWhere('followings.followerHost IS NOT NULL')
.getRawMany();
followers.forEach(({ inbox }) => inboxes.add(inbox));
}
await Promise.all(
this.recipes.filter(isFollowers)
.map(recipe => {
// followers deliver
return Followings.createQueryBuilder('followings')
// return either the shared inbox (if available) or the individual inbox
.select('COALESCE(followings.followerSharedInbox, followings.followerInbox)', 'inbox')
// so we don't have to make our inboxes Set work as hard
.distinct(true)
// ...for the specific actors followers
.where('followings.followeeId = :actorId', { actorId: recipe.followee.id })
// don't deliver to ourselves
.andWhere('followings.followerHost IS NOT NULL')
.getRawMany()
.then(followers =>
followers.forEach(({ inbox }) => inboxes.add(inbox))
);
})
);
this.recipes.filter((recipe): recipe is IDirectRecipe =>
// followers recipes have already been processed
@ -155,7 +156,7 @@ export class DeliverManager {
// skip instances as indicated
if (instancesToSkip.includes(new URL(inbox).host)) continue;
deliver(this.actor, this.activity, inbox);
await deliver(this.activity, inbox);
}
}
}
@ -166,9 +167,9 @@ export class DeliverManager {
* @param activity Activity
* @param from Followee
*/
export async function deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) {
const manager = new DeliverManager(actor, activity);
manager.addFollowersRecipe();
export async function deliverToFollowers(actor: ILocalUser, activity: any) {
const manager = new DeliverManager(activity);
manager.addFollowersRecipe(actor);
await manager.execute();
}
@ -177,8 +178,8 @@ export async function deliverToFollowers(actor: { id: ILocalUser['id']; host: nu
* @param activity Activity
* @param to Target user
*/
export async function deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) {
const manager = new DeliverManager(actor, activity);
export async function deliverToUser(activity: any, to: IRemoteUser) {
const manager = new DeliverManager(activity);
manager.addDirectRecipe(to);
await manager.execute();
}

View file

@ -1,9 +0,0 @@
import config from '@/config/index.js';
import { User } from '@/models/entities/user.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
export const renderReadActivity = (user: { id: User['id'] }, message: MessagingMessage) => ({
type: 'Read',
actor: `${config.url}/users/${user.id}`,
object: message.uri,
});

View file

@ -7,10 +7,6 @@ import { MessagingMessages, UserGroupJoinings, Users } from '@/models/index.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { UserGroup } from '@/models/entities/user-group.js';
import { toArray } from '@/prelude/array.js';
import { renderReadActivity } from '@/remote/activitypub/renderer/read.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import { deliver } from '@/queue/index.js';
import orderedCollection from '@/remote/activitypub/renderer/ordered-collection.js';
/**
* Mark messages as read
@ -133,18 +129,3 @@ export async function readGroupMessagingMessage(
}
}
}
export async function deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) {
const contents = toArray(messages)
.filter(x => x.uri)
.map(x => renderReadActivity(user, x));
if (contents.length > 1) {
const collection = orderedCollection(null, contents.length, undefined, undefined, contents);
deliver(user, renderActivity(collection), recipient.inbox);
} else {
for (const content of contents) {
deliver(user, renderActivity(content), recipient.inbox);
}
}
}

View file

@ -29,7 +29,7 @@ export default define(meta, paramDef, async (ps, me) => {
const actor = await getInstanceActor();
const targetUser = await Users.findOneByOrFail({ id: report.targetUserId });
deliver(actor, renderActivity(renderFlag(actor, report)), targetUser.inbox);
await deliver(renderActivity(renderFlag(actor, report)), targetUser.inbox);
}
await AbuseUserReports.update(report.id, {

View file

@ -4,7 +4,7 @@ import define from '@/server/api/define.js';
import { ApiError } from '@/server/api/error.js';
import { getUser } from '@/server/api/common/getters.js';
import { makePaginationQuery } from '@/server/api/common/make-pagination-query.js';
import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '@/server/api/common/read-messaging-message.js';
import { readUserMessagingMessage, readGroupMessagingMessage } from '@/server/api/common/read-messaging-message.js';
export const meta = {
tags: ['messaging'],
@ -75,11 +75,6 @@ export default define(meta, paramDef, async (ps, user) => {
// Mark all as read
if (ps.markAsRead) {
readUserMessagingMessage(user.id, recipient.id, messages.filter(m => m.recipientId === user.id).map(x => x.id));
// リモートユーザーとのメッセージだったら既読配信
if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) {
deliverReadActivity(user, recipient, messages);
}
}
return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, {

View file

@ -133,7 +133,7 @@ export default define(meta, paramDef, async (ps, user) => {
if (note.userHost != null) {
const pollOwner = await Users.findOneByOrFail({ id: note.userId }) as IRemoteUser;
deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox);
await deliver(renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox);
}
// リモートフォロワーにUpdate配信

View file

@ -1,7 +1,7 @@
import { UserGroupJoinings, Users, MessagingMessages } from '@/models/index.js';
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js';
import { UserGroup } from '@/models/entities/user-group.js';
import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '@/server/api/common/read-messaging-message.js';
import { readUserMessagingMessage, readGroupMessagingMessage } from '@/server/api/common/read-messaging-message.js';
import Channel from '@/server/api/stream/channel.js';
import { StreamMessages } from '@/server/api/stream/types.js';
@ -69,13 +69,6 @@ export default class extends Channel {
case 'read':
if (this.otherpartyId) {
readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]);
// リモートユーザーからのメッセージだったら既読配信
if (Users.isLocalUser(this.user!) && Users.isRemoteUser(this.otherparty!)) {
MessagingMessages.findOneBy({ id: body.id }).then(message => {
if (message) deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message);
});
}
} else if (this.groupId) {
readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]);
}

View file

@ -34,7 +34,7 @@ export default async function(blocker: User, blockee: User): Promise<void> {
if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee) && blocker.federateBlocks) {
const content = renderActivity(renderBlock(blocking));
deliver(blocker, content, blockee.inbox);
deliver(content, blockee.inbox);
}
}
@ -78,13 +78,13 @@ async function cancelRequest(follower: User, followee: User): Promise<void> {
// Send Undo Follow if followee is remote
if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
const content = renderActivity(renderUndo(renderFollow(follower, followee), follower));
deliver(follower, content, followee.inbox);
deliver(content, followee.inbox);
}
// Send Reject if follower is remote
if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) {
const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId!), followee));
deliver(followee, content, follower.inbox);
deliver(content, follower.inbox);
}
}
@ -125,7 +125,7 @@ async function unFollow(follower: User, followee: User): Promise<void> {
// Send Undo Follow if follower is remote
if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
const content = renderActivity(renderUndo(renderFollow(follower, followee), follower));
deliver(follower, content, followee.inbox);
deliver(content, followee.inbox);
}
}

View file

@ -29,6 +29,6 @@ export default async function(blocker: User, blockee: User) {
// deliver if remote bloking
if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) {
const content = renderActivity(renderUndo(renderBlock(blocking), blocker));
deliver(blocker, content, blockee.inbox);
await deliver(content, blockee.inbox);
}
}

View file

@ -28,6 +28,7 @@ export const perUserDriveChart = new PerUserDriveChart();
export const apRequestChart = new ApRequestChart();
const charts = [
/*
federationChart,
notesChart,
usersChart,
@ -40,6 +41,7 @@ const charts = [
perUserFollowingChart,
perUserDriveChart,
apRequestChart,
*/
];
// Write memory information to DB every 20 minutes

View file

@ -146,7 +146,7 @@ export default async function(_follower: { id: User['id'] }, _followee: { id: Us
if (Users.isRemoteUser(follower) && Users.isLocalUser(followee) && blocked) {
// リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。
const content = renderActivity(renderReject(renderFollow(follower, followee, requestId), followee));
deliver(followee , content, follower.inbox);
deliver(content, follower.inbox);
return;
} else if (Users.isRemoteUser(follower) && Users.isLocalUser(followee) && blocking) {
// リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。
@ -195,6 +195,6 @@ export default async function(_follower: { id: User['id'] }, _followee: { id: Us
if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) {
const content = renderActivity(renderAccept(renderFollow(follower, followee, requestId), followee));
deliver(followee, content, follower.inbox);
deliver(content, follower.inbox);
}
}

View file

@ -52,13 +52,13 @@ export default async function(follower: { id: User['id']; host: User['host']; ur
if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
const content = renderActivity(renderUndo(renderFollow(follower, followee), follower));
deliver(follower, content, followee.inbox);
deliver(content, followee.inbox);
}
if (Users.isLocalUser(followee) && Users.isRemoteUser(follower)) {
// local user has null host
const content = renderActivity(renderReject(renderFollow(follower, followee), followee));
deliver(followee, content, follower.inbox);
deliver(content, follower.inbox);
}
}

View file

@ -99,7 +99,7 @@ async function deliverReject(followee: Local, follower: Remote): Promise<void> {
});
const content = renderActivity(renderReject(renderFollow(follower, followee, request?.requestId || undefined), followee));
deliver(followee, content, follower.inbox);
deliver(content, follower.inbox);
}
/**

View file

@ -27,7 +27,7 @@ export async function acceptFollowRequest(followee: User, follower: User): Promi
if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) {
const content = renderActivity(renderAccept(renderFollow(follower, followee, request.requestId!), followee));
deliver(followee, content, follower.inbox);
await deliver(content, follower.inbox);
}
Users.pack(followee.id, followee, {

View file

@ -17,7 +17,7 @@ export async function cancelFollowRequest(followee: User, follower: User): Promi
const content = renderActivity(renderUndo(renderFollow(follower, followee), follower));
if (Users.isLocalUser(follower)) {
deliver(follower, content, followee.inbox);
await deliver(content, followee.inbox);
}
}

View file

@ -64,6 +64,6 @@ export async function createFollowRequest(follower: User, followee: User, reques
if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
const content = renderActivity(renderFollow(follower, followee));
deliver(follower, content, followee.inbox);
await deliver(content, followee.inbox);
}
}

View file

@ -97,7 +97,7 @@ export async function createMessage(user: { id: User['id']; host: User['host'];
const activity = renderActivity(renderCreate(await renderNote(note, false, true), note));
deliver(user, activity, recipientUser.inbox);
await deliver(activity, recipientUser.inbox);
}
return messageObj;
}

View file

@ -22,7 +22,7 @@ async function postDeleteMessage(message: MessagingMessage): Promise<void> {
if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) {
const activity = renderActivity(renderDelete(renderTombstone(`${config.url}/notes/${message.id}`), user));
deliver(user, activity, recipient.inbox);
await deliver(activity, recipient.inbox);
}
} else if (message.groupId) {
publishGroupMessagingStream(message.groupId, 'deleted', message.id);

View file

@ -432,7 +432,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
if (Users.isLocalUser(user) && !data.localOnly) {
(async () => {
const noteActivity = renderActivity(await renderNoteOrRenoteActivity(note));
const dm = new DeliverManager(user, noteActivity);
const dm = new DeliverManager(noteActivity);
// Delivered to remote users who have been mentioned
for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) {
@ -453,7 +453,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
// Deliver to followers
if (['public', 'home', 'followers'].includes(note.visibility)) {
dm.addFollowersRecipe();
dm.addFollowersRecipe(user);
}
if (['public'].includes(note.visibility)) {

View file

@ -60,8 +60,8 @@ export async function deleteNotes(notes: Note[], user?: User): Promise<void> {
// Compute addressing information.
// Since we do not send any actual content, we send all note deletions to everyone.
const manager = new DeliverManager(fetchedUser, content);
manager.addFollowersRecipe();
const manager = new DeliverManager(content);
manager.addFollowersRecipe(fetchedUser);
manager.addEveryone();
// Check mentioned users, since not all may have a shared inbox.
await Promise.all(

View file

@ -130,14 +130,14 @@ export async function createReaction(user: { id: User['id']; host: User['host'];
//#region 配信
if (Users.isLocalUser(user) && !note.localOnly) {
const content = renderActivity(await renderLike(record, note));
const dm = new DeliverManager(user, content);
const dm = new DeliverManager(content);
if (note.userHost !== null) {
const reactee = await Users.findOneBy({ id: note.userId });
dm.addDirectRecipe(reactee as IRemoteUser);
}
if (['public', 'home', 'followers'].includes(note.visibility)) {
dm.addFollowersRecipe();
dm.addFollowersRecipe(user);
} else if (note.visibility === 'specified') {
const visibleUsers = await Promise.all(note.visibleUserIds.map(id => Users.findOneBy({ id })));
for (const u of visibleUsers.filter(u => u && Users.isRemoteUser(u))) {

View file

@ -46,12 +46,12 @@ export async function deleteReaction(user: { id: User['id']; host: User['host'];
//#region 配信
if (Users.isLocalUser(user) && !note.localOnly) {
const content = renderActivity(renderUndo(await renderLike(exist, note), user));
const dm = new DeliverManager(user, content);
const dm = new DeliverManager(content);
if (note.userHost !== null) {
const reactee = await Users.findOneBy({ id: note.userId });
dm.addDirectRecipe(reactee as IRemoteUser);
}
dm.addFollowersRecipe();
dm.addFollowersRecipe(user);
dm.execute();
}
//#endregion

View file

@ -36,9 +36,8 @@ export async function addRelay(inbox: string): Promise<Relay> {
}).then(x => Relays.findOneByOrFail(x.identifiers[0]));
const relayActor = await getRelayActor();
const follow = renderFollowRelay(relay, relayActor);
const activity = renderActivity(follow);
deliver(relayActor, activity, relay.inbox);
const activity = renderActivity(renderFollowRelay(relay, relayActor));
await deliver(activity, relay.inbox);
return relay;
}
@ -53,17 +52,14 @@ export async function removeRelay(inbox: string): Promise<void> {
}
const relayActor = await getRelayActor();
const follow = renderFollowRelay(relay, relayActor);
const undo = renderUndo(follow, relayActor);
const activity = renderActivity(undo);
deliver(relayActor, activity, relay.inbox);
const activity = renderActivity(renderUndo(renderFollowRelay(relay, relayActor), relayActor));
await deliver(activity, relay.inbox);
await Relays.delete(relay.id);
}
export async function listRelay(): Promise<Relay[]> {
const relays = await Relays.find();
return relays;
return await Relays.find();
}
export async function relayAccepted(id: string): Promise<string> {
@ -93,9 +89,9 @@ export async function deliverToRelays(user: { id: User['id']; host: null; }, act
const signed = await attachLdSignature(copy, user);
for (const relay of relays) {
deliver(user, signed, relay.inbox);
}
await Promise.all(relays.map(relay =>
deliver(signed, relay.inbox)
));
}
export async function deliverMultipleToRelays(user: User, activities: any[]): Promise<void> {
@ -111,7 +107,7 @@ export async function deliverMultipleToRelays(user: User, activities: any[]): Pr
return attachLdSignature(copy, user);
}));
for (const relay of relays) {
deliver(user, content, relay.inbox);
}
await Promise.all(relays.map(relay =>
deliver(content, relay.inbox)
));
}

View file

@ -13,7 +13,7 @@ export async function doPostSuspend(user: { id: User['id']; host: User['host'] }
const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user));
// deliver to all of known network
const dm = new DeliverManager(user, content);
const dm = new DeliverManager(content);
dm.addEveryone();
await dm.execute();
}

View file

@ -32,7 +32,7 @@ export async function doPostUnsuspend(user: User): Promise<void> {
}
for (const inbox of queue) {
deliver(user as any, content, inbox);
await deliver(content, inbox);
}
}
}

View file

@ -12,8 +12,8 @@
tabindex="0"
@click="chosen(emoji, $event)"
>
<!--<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>-->
<img :src="disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
<img v-else :src="disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
</button>
</div>
<div v-if="searchResultUnicode.length > 0">

View file

@ -0,0 +1,61 @@
<template>
<div v-if="hide" class="media-ansi-hidden" @click="hide = false">
<div class="text">
<b><i class="fas fa-exclamation-triangle"></i> {{ $ts.sensitive }}</b>
<span>{{ $ts.clickToShow }}</span>
</div>
</div>
<div v-else class="media-ansi">
<a
ref="imageContainer"
:href="ansiFile.url"
:title="ansiFile.name"
></a>
<button v-tooltip="$ts.hide" class="_button hide" @click="hide = true"><i class="fas fa-eye-slash"></i></button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import '@/scripts/escapes'
export default defineComponent({
props: {
ansiFile: {
type: Object,
required: true
}
},
data() {
return {
hide: false
}
},
mounted () {
const self = this;
escapes(this.ansiFile.url + "?.ans", function () {
this.alt = self.ansiFile.comment;
this.title = self.ansiFile.comment;
this.classList.add("render");
self.$refs.imageContainer.appendChild(this);
});
}
});
</script>
<style lang="scss">
.media-ansi {
a {
position: relative;
width: 100%;
height: 100%;
}
.render {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
}
</style>

View file

@ -5,6 +5,7 @@
<div ref="gallery" :data-count="mediaList.filter(media => previewable(media)).length">
<template v-for="media in mediaList.filter(media => previewable(media))">
<XVideo v-if="media.type.startsWith('video')" :key="media.id" :video="media"/>
<XAnsi :ansi-file="media" :key="media.id" v-else-if="media.name.endsWith('.ans')"/>
<XImage v-else-if="media.type.startsWith('image')" :key="media.id" class="image" :data-id="media.id" :image="media" :raw="raw"/>
<XModPlayer v-else-if="isModule(media)" :key="media.id" :module="media"/>
</template>
@ -23,6 +24,7 @@ import XBanner from './media-banner.vue';
import XImage from './media-image.vue';
import XVideo from './media-video.vue';
import XModPlayer from './mod-player.vue';
import XAnsi from './media-ansi.vue';
import { FILE_TYPE_BROWSERSAFE, FILE_TYPE_TRACKER_MODULES, FILE_EXT_TRACKER_MODULES } from '@/const';
const props = defineProps<{
@ -124,6 +126,7 @@ onMounted(() => {
const previewable = (file: foundkey.entities.DriveFile): boolean => {
if (file.type === 'image/svg+xml') return true; // svgwebpublic/thumbnailpngtrue
if (file.name.endsWith(".ans")) return true;
// FILE_TYPE_BROWSERSAFE
if (isModule(file)) return true;
return (file.type.startsWith('video') || file.type.startsWith('image')) && FILE_TYPE_BROWSERSAFE.includes(file.type);

View file

@ -214,6 +214,16 @@ import { getAccountFromId } from '@/scripts/get-account-from-id';
return rootEl;
})();
if (!document.querySelector("script[src='/static-assets/libopenmpt.js']")) {
const chiplib = document.createElement('script');
chiplib.type = 'text/javascript';
chiplib.src = '/static-assets/libopenmpt.js';
chiplib.onload = () => {
window.libopenmpt = Module;
};
document.body.appendChild(chiplib);
}
app.mount(rootEl);
// boot.jsのやつを解除

View file

@ -0,0 +1,803 @@
//
// _)
// _ \ __| __| _` | __ \ _ \ __| | __|
// __/ \__ \ ( ( | | | __/ \__ \ | \__ \
// \___| ____/ \___| \__,_| .__/ \___| ____/ _) | ____/
// _| ___/
//
// escapes.js, version 0.1
// http://atdt.github.com/escapes.js/
//
// Copyright (C) 2012 Ori Livneh
// Licensed under the MIT and GPL licenses
// See https://github.com/atdt/escapes.js/blob/master/LICENSE
// greetz to deniz.
/*jslint forin: true, bitwise: true, browser: true, plusplus: true, regexp: true */
(function (global) {
"use strict";
var escapes,
BLACK = 0,
RED = 1,
GREEN = 2,
YELLOW = 3,
BLUE = 4,
MAGENTA = 5,
CYAN = 6,
WHITE = 7,
NONE = 0x0,
BRIGHT = 0x1,
UNDERLINE = 0x4,
BLINK = 0x5,
REVERSE = 0x7,
INVISIBLE = 0x9,
ANSICOLORS = [
[ 0, 0, 0, 255], // Black
[170, 0, 0, 255], // Red
[ 0, 170, 0, 255], // Green
[170, 85, 0, 255], // Yellow
[ 0, 0, 170, 255], // Blue
[170, 0, 170, 255], // Magenta
[ 0, 170, 170, 255], // Cyan
[170, 170, 170, 255], // White
// Bright:
[ 85, 85, 85, 255],
[255, 85, 85, 255],
[ 85, 255, 85, 255],
[255, 255, 85, 255],
[ 85, 85, 255, 255],
[255, 85, 255, 255],
[ 85, 255, 255, 255],
[255, 255, 255, 255]
],
BINCOLORS = [
[ 0, 0, 0, 255], // Black
[ 0, 0, 170, 255], // Blue
[ 0, 170, 0, 255], // Green
[ 0, 170, 170, 255], // Cyan
[170, 0, 0, 255], // Red
[170, 0, 170, 255], // Magenta
[170, 85, 0, 255], // Yellow
[170, 170, 170, 255], // White
// Bright:
[ 85, 85, 85, 255],
[ 85, 85, 255, 255],
[ 85, 255, 85, 255],
[ 85, 255, 255, 255],
[255, 85, 85, 255],
[255, 85, 255, 255],
[255, 255, 85, 255],
[255, 255, 255, 255]
],
MAX_HEIGHT = 12000, // Or 750 lines at 16 px/line
font,
jQuery = global.jQuery;
function jQueryPluginSetup() {
jQuery.ansiRender = function (target, callback) {
var deferred = jQuery.Deferred();
if (typeof callback !== 'undefined') {
deferred.then(callback);
}
escapes(target, function (cursor) {
deferred.resolveWith(this, [cursor]);
});
return deferred.promise();
};
}
function Canvas(width) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = MAX_HEIGHT;
canvas.toDownloadURL = function () {
var url = canvas.toDataURL();
return 'image/octet-stream' + url.substring(14);
};
canvas.toImageTag = function () {
var img = document.createElement('img');
img.src = this.toDataURL();
return img;
};
return canvas;
}
function httpGet(url, binary, success, error) {
var req = new XMLHttpRequest();
req.onreadystatechange = function () {
if (req.readyState === req.DONE) {
if (req.status === 200 || req.status === 0) {
success(binary ? new Uint8Array(req.response) : req.responseText);
} else if (typeof error !== 'undefined') {
error(req);
}
}
};
req.open('GET', url, true);
if (binary) {
req.setRequestHeader('Content-Type', 'application/octet-stream');
req.responseType = 'arraybuffer';
} else {
req.overrideMimeType('text/plain; charset=x-user-defined');
}
req.send(null);
}
function parseIntArray(array) {
var i = array.length;
while (i--) {
array[i] = parseInt(array[i], 10);
}
return array;
}
function stupidCopy(obj) {
return JSON.parse(JSON.stringify(obj));
}
function Cursor(options) {
if (!(this instanceof Cursor)) {
return new Cursor();
}
// Canvas
this.canvas = new Canvas(640);
this.context = this.canvas.getContext('2d');
this.image_data = this.context.createImageData(8, 16);
// Position
this.column = 1;
this.row = 1;
this.scrollback = 0;
// Graphic mode
this.palette = stupidCopy(ANSICOLORS);
this.clearCanvas();
this.resetColor();
return this;
}
Cursor.prototype = {
moveCursorBy: function (columns, rows) {
this.column += columns;
this.row += rows;
// Enforce boundaries
this.column = Math.max(this.column, 1);
this.column = Math.min(this.column, 80);
this.row = Math.max(this.row, 1);
this.row = Math.min(this.row, 25);
},
clearCanvas: function () {
this.context.fillStyle = 'rgba(' + this.getColor(BLACK).toString() + ')';
this.context.fillRect(0, 0, 640, this.canvas.height);
this.flags = NONE;
},
trimCanvas: function () {
var new_height = (this.row + this.scrollback - 1) * 16,
image_data = this.context.getImageData(0, 0, 640, new_height);
this.canvas.height = new_height;
this.clearCanvas();
this.resetColor();
this.context.putImageData(image_data, 0, 0);
},
savePosition: function () {
this.saved = {};
this.saved.row = this.row;
this.saved.column = this.column;
},
loadPosition: function () {
this.column = this.saved.column;
this.row = this.saved.row;
delete this.saved;
},
getColor: function (code, bright) {
return this.palette[bright ? code + 8 : code];
},
resetColor: function () {
this.foreground = WHITE;
this.background = BLACK;
},
renderChar: function (charcode, foreground, background) {
var data, bitmap, bits, row, col, offset, color, channel;
data = this.image_data.data;
bitmap = font[charcode];
for (row = 0; row < 16; row++) {
bits = bitmap[row] || 0x00;
for (col = 7; col >= 0; col--) {
offset = (32 * row) + (4 * col);
color = bits & 1 ? foreground : background;
for (channel = 0; channel < 4; channel++) {
data[offset + channel] = color[channel];
}
bits >>= 1;
}
}
return this.image_data;
},
parse: function (buffer, options) {
var re = /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g,
pos = 0,
opcode,
args,
match;
// DOS treats Ctrl-Z (SUB) as EOF. Some ANSI artists hid their alias in a file
// by placing it after the EOF.
buffer = buffer.split(String.fromCharCode(0x1a), 1)[0];
do {
pos = re.lastIndex;
match = re.exec(buffer);
if (match !== null) {
if (match.index > pos) {
options.onLiteral.call(this, buffer.slice(pos, match.index));
}
opcode = match[2];
args = parseIntArray(match[1].split(';'));
options.onEscape.call(this, opcode, args);
}
} while (re.lastIndex !== 0);
if (pos < buffer.length) {
options.onLiteral.call(this, buffer.slice(pos));
}
this.trimCanvas();
options.onComplete.call(this.canvas, this);
return this;
},
escape: function (opcode, args) {
var arg, i, length;
switch (opcode) {
case 'A': // Cursor Up
arg = args[0] || 1;
this.moveCursorBy(0, -arg);
break;
case 'B': // Cursor Down
arg = args[0] || 1;
this.moveCursorBy(0, arg);
break;
case 'C': // Cursor Forward
arg = args[0] || 1;
this.moveCursorBy(arg, 0);
break;
case 'D': // Cursor Backward
arg = args[0] || 1;
this.moveCursorBy(-arg, 0);
break;
case 'f': // Horizontal & Vertical Position
case 'H': // Cursor Position
this.row = args[0] || 1;
this.column = args[1] || 1;
break;
case 's': // Save Cursor Position
this.savePosition();
break;
case 'u': // Restore Cursor Position
this.loadPosition();
break;
case 'm': // Set Graphics Rendition
for (i = 0, length = args.length; i < length; i++) {
arg = args[i];
if (arg === NONE) {
this.flags = NONE;
this.resetColor();
} else {
switch (Math.floor(arg / 10)) {
case 0:
this.flags |= arg;
break;
case 3:
this.foreground = arg - 30;
break;
case 4:
this.background = arg - 40;
break;
}
}
}
break;
case 'J': // Erase Display
if (args[0] === 2) {
this.clearCanvas();
}
break;
}
},
write: function (text) {
var CR = 0x0d,
LF = 0x0a,
cursor = this,
image_data,
background,
foreground,
charcode,
x,
y,
i,
length;
foreground = this.getColor(this.foreground, this.flags & BRIGHT);
background = this.getColor(this.background);
for (i = 0, length = text.length; i < length; i++) {
charcode = text.charCodeAt(i) & 0xff; // truncate to 8 bits
switch (charcode) {
case CR:
cursor.column = 1;
break;
case LF:
cursor.row++;
break;
default:
x = (cursor.column - 1) * 8;
y = (cursor.row + cursor.scrollback - 1) * 16;
image_data = this.renderChar(charcode, foreground, background);
this.context.putImageData(image_data, x, y);
if (cursor.column === 80) {
cursor.column = 1;
cursor.row++;
} else {
cursor.column++;
}
break;
}
// The value of 'row' represents current position relative to the top of the
// screen and therefore cannot exceed 25. Vertical scroll past the 25th line
// increments the scrollback buffer instead.
if (cursor.row === 26) {
cursor.scrollback++;
cursor.row--;
}
}
}
};
function Bin(options) {
if (!(this instanceof Bin)) {
return new Bin();
}
// Canvas
this.canvas = new Canvas(1280);
this.context = this.canvas.getContext('2d');
this.image_data = this.context.createImageData(8, 16);
// Position
this.column = 1;
this.row = 1;
this.scrollback = 0;
// Graphic mode
this.palette = stupidCopy(BINCOLORS);
this.clearCanvas();
return this;
}
Bin.prototype = {
parse: function (buffer, options) {
var i, x, y, image_data;
for (i = this.row = this.column = 0; i < buffer.length; i += 2) {
image_data = this.renderChar(buffer[i], this.palette[buffer[i + 1] & 15], this.palette[buffer[i + 1] >> 4]);
x = (this.column - 1) * 8;
y = (this.row + this.scrollback - 1) * 16;
this.context.putImageData(image_data, x, y);
if (++this.column === 160) {
this.column = 0;
this.row++;
}
}
this.trimCanvas();
options.onComplete.call(this.canvas, this);
return this;
},
trimCanvas: function () {
var new_height = (this.row + this.scrollback) * 16,
image_data = this.context.getImageData(0, 0, 1280, new_height);
this.canvas.height = new_height;
this.clearCanvas();
this.context.putImageData(image_data, 0, 0);
},
clearCanvas: function () {
this.context.fillStyle = 'rgba(' + this.getColor(BLACK).toString() + ')';
this.context.fillRect(0, 0, 1280, this.canvas.height);
this.flags = NONE;
},
getColor: function (code, bright) {
return this.palette[bright ? code + 8 : code];
},
renderChar: function (charcode, foreground, background) {
var data, bitmap, bits, row, col, offset, color, channel;
data = this.image_data.data;
bitmap = font[charcode];
for (row = 0; row < 16; row++) {
bits = bitmap[row] || 0x00;
for (col = 7; col >= 0; col--) {
offset = (32 * row) + (4 * col);
color = bits & 1 ? foreground : background;
for (channel = 0; channel < 4; channel++) {
data[offset + channel] = color[channel];
}
bits >>= 1;
}
}
return this.image_data;
}
};
escapes = function (url, callback, options) {
var property, cursor;
switch (url.substr(-3).toLowerCase()) {
case "ans":
cursor = new Cursor();
options = options || {};
for (property in options) {
cursor[property] = options[property];
}
if (options.transparent) {
cursor.palette[BLACK][3] = 0;
}
httpGet(url, false, function (data) {
cursor.parse(data, {
onEscape : cursor.escape,
onLiteral : cursor.write,
onComplete : callback
});
});
break;
case "bin":
cursor = new Bin();
httpGet(url, true, function (data) {
cursor.parse(data, {
onComplete : callback
});
});
break;
}
return cursor;
};
escapes.Cursor = Cursor;
global.escapes = escapes;
if (typeof jQuery !== 'undefined' && jQuery.Deferred) {
jQueryPluginSetup();
}
// Bitmap font. Each element in the array is a glyph in the CP437 character
// set, indexed by its character code. Each 8px x 16px character is represented
// by a sixteen-element sub-array, with each element representing a row of
// pixels.
font = [
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00],
[0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff],
[0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00],
[0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xd6, 0xd6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00],
[0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38, 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00],
[0x00, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x1c, 0x36, 0x32, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00],
[0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00],
[0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x18, 0x70, 0x00, 0x00],
[0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x70, 0x00, 0x00],
[0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x38, 0x6c, 0x38, 0x10, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x0c, 0x18, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00],
[0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00],
[0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x60, 0xe0, 0x62, 0x66, 0x6c, 0x18, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00],
[0x00, 0x60, 0xe0, 0x62, 0x66, 0x6c, 0x18, 0x30, 0x66, 0xce, 0x9a, 0x3f, 0x06, 0x06, 0x00, 0x00],
[0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44],
[0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa],
[0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36],
[0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
[0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0],
[0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f],
[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],
[0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x6c, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x3c, 0x66, 0x0c, 0x18, 0x32, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
];
}(this));