Commit 8736dfa4 authored by 神楽坂玲奈's avatar 神楽坂玲奈

refactor

parent 49433c4b
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "mycard"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico",
{ "glob": "**/*", "input": "../node_modules/candy/res", "output": "res" }
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "mycard",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {
"spec": false
},
"directive": {
"spec": false
},
"class": {
"spec": false
},
"guard": {
"spec": false
},
"module": {
"spec": false
},
"pipe": {
"spec": false
},
"service": {
"spec": false
}
}
}
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
/bin/
/app/**/*.js
/app/**/*.js.map
/app/*.metadata.json
/app/*.shim.ts
/app/*.ngfactory.ts
/aot
!/aot/index.html
/node_modules/
/dist/
/cache/
/typings/
/npm-debug.log*
/.idea/
# See http://help.github.com/ignore-files/ for more about ignoring files.
/bin
/*.js
/*.js.map
/result/*.js
/result/*.js.map
!/common.js
/public
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
messages.xlf
messages.xlf.bak
locale/*.xlf.bak
/package-lock.json
language: node_js
node_js: node
os:
- linux
- osx
dist: trusty
sudo: required
env:
npm_config_target: 1.4.15
npm_config_arch: x64
npm_config_target_arch: x64
npm_config_disturl: https://atom.io/download/atom-shell
npm_config_runtime: electron
npm_config_build_from_source: true
global:
secure: KsebO9wNxM2RfUGg6Y0E4hRdXzQLNe1fdB1AOV5U1LddLGZYTYvsknPL6oyjOV3vY5ed7wueErt1GCDjEZJMdox0rMUEZ9HH8umwUoJi2uS6LoaU31yWNCCbPbpdtJw3rohzNvEtxd1Y01U5msKPuUd4M5mt/RKlPPPR/L5H178=
addons:
apt:
packages:
- icnsutils
- graphicsmagick
- xz-utils
cache:
directories:
- node_modules
- $HOME/.electron
- $HOME/.cache
before_install:
- env
- openssl aes-256-cbc -K $encrypted_9f35b7f09ebe_key -iv $encrypted_9f35b7f09ebe_iv
-in ssh-key.enc -out $HOME/.ssh/id_ecdsa -d
- chmod 600 $HOME/.ssh/id_ecdsa
install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir -p bin; curl --location --retry
5 https://github.com/aria2/aria2/releases/download/release-1.29.0/aria2-1.29.0-osx-darwin.tar.bz2
| tar --strip-components=2 -C bin -jxf - aria2-1.29.0/bin/aria2c; fi
- npm install
- npm prune
script:
- npm run dist
......@@ -57,4 +57,4 @@ Languages & Frameworks
Node.js and NPM
Coding Assistance
Enable
```
\ No newline at end of file
```
<!--<h2>MyCard 招募公告</h2>-->
<!--<p>MyCard 伴随大家已经有 6 年了,在这 6 年间 MyCard 作为一个同人平台很感谢得到大家的支持,现在 MyCard 为了给支持的大家带来更好的体验,正在努力进行全新的改版的开发工作,希望可以得到大家的支持和帮助。</p>--><!--<p>职位:(前端)开发工程师</p>--><!--<p>负责平台客户端的开发,及网站和论坛相关的改版工作。</p>--><!--<p>职位描述:对 ACG 领域有一定的了解,会js等编程领域的专业技能,对软件开发具有一定的热情和自主能动性,认真严谨和团队意识。</p>--><!--<p>联系邮箱:hr@mycard.moe</p>--><!--<p>工作地点:上海市长宁区(工资面议)</p>-->
<!--<h2>联系我们</h2>--><!--<dl>--><!--<dt>应聘</dt>--><!--<dd>hr@mycard.com</dd>--><!--<dt>投稿、合作、侵权投诉</dt>--><!--<dd>business@mycard.com</dd>--><!--<dt>问题反馈</dt>--><!--<dd>support@mycard.moe</dd>--><!--</dl>-->
/**
* Created by zh99998 on 16/9/2.
*/
import {Component} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'about',
templateUrl: 'about.component.html',
styleUrls: ['about.component.css'],
})
export class AboutComponent {
}
:host {
flex-grow: 1;
position: relative;
padding: 1rem 1rem 0 1rem;
background-blend-mode: color;
background-size: 100% auto !important;
background-repeat: no-repeat !important;
}
.list-group {
width: 20rem;
}
progress {
margin: 2px 0 0;
}
.carousel-inner img {
width: 100%;
}
.dependency {
margin-right: 0.8em;
}
#news p {
margin-bottom: 0;
}
#news a {
display: block;
}
#network {
display: inline-block;
vertical-align: middle;
width: 230px;
}
#network .input-group-btn > .btn:not(:last-child):not(.dropdown-toggle) {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
#network .input-group-btn > .dropdown-toggle {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
.i-b{
display: inline-block;
}
.custom-file {
width: 100%;
}
.custom-file-control:lang(en)::after {
content: initial;
}
.custom-file-control {
overflow: hidden;
white-space: nowrap;
}
h1 {
font-size: 28px;
}
#status {
font-size: 15px;
}
h2 {
font-size: 20px;
margin-bottom: 0;
}
.cover {
width: 128px;
height: 128px;
object-fit: contain;
box-shadow: 0 0 4px #ccc;
}
.banner {
width: 120px;
height: 45px;
object-fit: cover;
}
#main {
display: flex;
flex-direction: row;
}
.panel {
border: 1px solid #eceeef;
border-radius: 6px;
background: rgba(255, 255, 255, .7);
padding: .8rem;
margin-bottom: 1rem;
box-shadow: 0 0 15px rgba(0, 0, 0, .05);
position: relative;
}
#news h3 > .title {
font-size: 1rem;
color: inherit;
}
#news h3 {
padding-top: .8rem;
margin-bottom: 0;
}
#news p {
font-size: 14px;
color: #888;
}
#news a {
font-size: 14px;
color: #00a4d9;
}
#news span {
font-size: 12px;
color: #ccc;
}
.moreinfo {
color: #00a4d9;
display: block;
position: absolute;
top: 12px;
right: 18px;
font-size: 14px;
}
#local h2 {
margin-bottom: .8rem;
}
#main {
display: flex;
flex-direction: row;
}
#right {
margin-left: 1rem;
}
h1 {
font-size: 28px;
margin-bottom: 0;
}
#time {
font-size: 14px;
margin-bottom: .6rem;
visibility: hidden;
}
th {
width: 25%;
}
.moreinfo {
color: #00a4d9;
display: block;
position: absolute;
top: 12px;
right: 18px;
font-size: 14px;
}
#arena {
position: relative;
}
.btn-primary {
background-color: #00a4d9;
border-color: #008dbb;
}
/* 竞技场 */
h2 {
font-size: 20px;
}
dt, dd {
font-size: 14px;
}
table {
margin-top: .5rem;
margin-bottom: 0;
}
table th, table td {
border-top: none;
font-size: 14px;
font-weight: normal;
}
#game_info {
font-size: 14px;
margin-right: 8px;
display: flex;
flex-direction: column;
flex-grow: 1;
}
#game_info p {
flex-grow: 1;
}
#game_info_2 {
width: 160px;
flex-shrink: 0;
}
.tag {
font-size: 12px;
padding: 2px 5px;
}
#purchase-form .form-check {
padding-right: 8px;
}
#purchase-form legend {
font-size: 1rem;
margin-bottom: 0;
margin-top: .5rem;
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<div id="candy" data-MinOrMax="default">
</div>
<div style="position:absolute; top:5px; right:10px;">
<i id="minimize" class="fa fa-minus hover-color" (click)="minimize()" data-size="" i18n-title title="最小化"></i>
<i id="unminimize" class="fa fa-minus hover-color" (click)="restore()" data-size="" i18n-title title="取消最小化" hidden></i>
<i id="restore" class="fa fa fa-chevron-down hover-color" (click)="restore()" data-size="" i18n-title title="还原" hidden></i>
<i id="maximize" class="fa fa fa-chevron-up hover-color" (click)="maximize()" i18n-title title="最大化"></i>
</div>
This diff is collapsed.
/**
* Created by zh99998 on 2017/6/1.
*/
import * as Raven from 'raven-js';
import { ErrorHandler } from '@angular/core';
Raven
.config('https://2c5fa0d0f13c43b5b96346f4eff2ea60@sentry.io/174769')
.install();
export class RavenErrorHandler implements ErrorHandler {
handleError(err: any): void {
Raven.captureException(err.originalError || err);
}
}
\ No newline at end of file
import {TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID} from '@angular/core';
import {remote} from 'electron';
export async function getTranslationProviders (): Promise<Object[]> {
let locale = localStorage.getItem('locale');
if (!locale) {
locale = remote.app.getLocale();
localStorage.setItem('locale', locale);
}
const noProviders: Object[] = [];
if (!locale || locale === 'zh-CN') {
return noProviders;
}
const translationFile = `./locale/messages.${locale}.xlf`;
try {
let translations = await getTranslationsWithSystemJs(translationFile);
return [
{provide: TRANSLATIONS, useValue: translations},
{provide: TRANSLATIONS_FORMAT, useValue: 'xlf'},
{provide: LOCALE_ID, useValue: locale}
];
} catch (error) {
return noProviders;
}
}
declare const System: any;
function getTranslationsWithSystemJs (file: string) {
return System.import(file + '!text'); // relies on text plugin
}
:host {
display: flex;
height: 100%;
}
#right {
display: flex;
flex-direction: column;
flex-grow: 1;
}
#main {
display: flex;
flex-grow: 1;
}
#candy-wrapper {
background-color: #444;
height: 230px;
flex-shrink: 0;
position: relative;
}
roster {
width: 190px;
background-color: #f7f7f9;
flex-shrink: 0;
}
/*a {*/
/*display: block;*/
/*padding: 10px 20px 10px 20px;*/
/*}*/
/*a:focus, a:hover {*/
/*text-decoration: none;*/
/*}*/
/*.active {*/
/*background-color: #428bca;*/
/*}*/
/*.active > a {*/
/*color: #fff;*/
/*}*/
span {
margin: 12px 0 8px 8px;
color: #a7a7a7;
font-size: 14px;
display: block;
}
.actions {
margin-bottom: 1em;
}
.progress {
height: 1em;
width: 1em;
float: right;
margin: 14px;
position: relative;
}
.pie {
height: 100%;
width: 100%;
clip: rect(0, 1em, 1em, 0.5em);
left: 0;
position: absolute;
top: 0;
}
.half-circle {
height: 100%;
width: 100%;
border: 0.2em solid #3498db;
border-radius: 50%;
clip: rect(0, 0.5em, 1em, 0);
left: 0;
position: absolute;
top: 0;
}
.shadow {
height: 100%;
width: 100%;
border: 0.2em solid #bdc3c7;
border-radius: 50%;
}
.right-side {
display: none;
}
.half-circle {
/*border-color: #e74c3c;*/
border-color: rgb(0, 116, 217);
}
.left-side {
/*transform: rotate(1turn);*/
/*在前台用Angular填写*/
}
.second-half {
clip: rect(auto, auto, auto, auto);
}
.second-half > .right-side {
display: inherit;
transform: rotate(0.5turn);
}
.fa-spin {
margin: 14px;
color: #0275d8;
font-weight: bold;
float: right;
}
#nav-wrapper {
width: 190px;
height: 100%;
flex-shrink: 0;
background-color: #f7f7f9;
border-right: 1px solid #eee;
padding-left: 0;
padding-right: 0;
padding-top: 20px;
/*resize: horizontal;*/
position: relative;
}
nav {
height: 100%;
}
.sidebar .nav {
margin-bottom: 20px;
}
.sidebar .nav-item {
width: 100%;
}
.sidebar .nav-item + .nav-item {
margin-left: 0;
}
.sidebar .nav-link {
border-radius: 0;
}
.nav-link {
padding: .3em 1em;
color: black;
font-size: 15px;
}
.nav-link.active {
background-color: #ebf3f8;
color: #00a4d9;
}
a {
cursor: default;
}
.search {
background-color: #ebf3f8;
border: none;
}
i.search {
color: #a7a7a7;
}
input.search {
padding-left: 0;
font-size: 14px;
font-family: -apple-system, Arial, 'Source Sans Pro', "Microsoft YaHei", 'Microsoft JhengHei', "WenQuanYi Micro Hei", sans-serif;
}
input.search::-webkit-input-placeholder {
color: #a7a7a7;
}
#search {
padding: 0 10px;
}
.icon {
width: 16px;
height: 16px;
}
nav::-webkit-resizer {
/*width: 100px;*/
/*height: 100px;*/
/*background-color: red;*/
border: 2px solid yellow;
background: blue;
box-shadow: 0 0 2px 5px red;
outline: 2px dashed green;
/*size does not work*/
display: block;
width: 150px !important;
height: 150px !important;
position: fixed;
}
.resize-wrapper {
position: relative;
}
.resize {
position: absolute;
}
.resize-right .resize {
width: 4px;
right: -2px;
top: 0;
bottom: 0;
cursor: col-resize;
}
.resize-top .resize {
height: 4px;
top: -2px;
left: 0;
right: 0;
cursor: row-resize;
}
#nav-wrapper {
z-index: 90;
box-shadow: 0 0 5px rgba(0, 0, 0, .2);
}
#candy-wrapper {
z-index: 80;
box-shadow: 0 0 5px rgba(0, 0, 0, .2);
}
roster {
z-index: 90;
}
/*#right-shadow {*/
/*width: 190px;*/
/*z-index: 95;*/
/*box-shadow: 0 0 5px rgba(0, 0, 0, .2);*/
/*position: absolute;*/
/*top: 0;*/
/*right: 0;*/
/*bottom: 0;*/
/*}*/
\ No newline at end of file
<!-- Begin page content -->
<div #nav id="nav-wrapper" class="resize-wrapper resize-right">
<nav id="apps" *ngIf="apps" class="bg-faded sidebar scroll">
<div id="search" class="input-group">
<i class="fa fa-search input-group-addon search" id="basic-addon1"></i>
<input #search id="search-input" type="text" class="form-control search" placeholder="搜索游戏" aria-describedby="basic-addon1">
</div>
<span i18n *ngIf="grouped_apps.installed">已安装</span>
<ul *ngIf="grouped_apps.installed" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.installed" class="nav-item">
<a (click)="$event.preventDefault(); chooseApp(app)" class="nav-link" [class.active]="app===currentApp" [href]="'https://mycard.moe/' + app.id">
<img *ngIf="app.icon" class="icon" [src]="app.icon">
{{app.name}}<i *ngIf="!app.isReady() && !app.status.total" class="spin fa fa-circle-o-notch fa-spin fa-fw"></i>
<div *ngIf="!app.isReady() && app.status.total" class="progress">
<div class="pie" [class.second-half]="app.status.progress/app.status.total>0.5">
<div class="left-side half-circle" [style.transform]="'rotate('+(app.status.progress/app.status.total).toString()+'turn)'"></div>
<div class="right-side half-circle"></div>
</div>
<div class="shadow"></div>
</div>
</a>
</li>
</ul>
<span *ngIf="grouped_apps.test">测试</span>
<ul *ngIf="grouped_apps.test" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.test" class="nav-item">
<a (click)="$event.preventDefault(); chooseApp(app)" [href]="'https://mycard.moe/' + app.id" class="nav-link" [class.active]="app===currentApp">
<img *ngIf="app.icon" class="icon" [src]="app.icon">
{{app.name}}
</a>
</li>
</ul>
<span i18n *ngIf="grouped_apps.recommend">推荐</span>
<ul *ngIf="grouped_apps.recommend" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.recommend" class="nav-item">
<a (click)="$event.preventDefault(); chooseApp(app)" [href]="'https://mycard.moe/' + app.id" class="nav-link" [class.active]="app===currentApp">
<img *ngIf="app.icon" class="icon" [src]="app.icon">
{{app.name}}
</a>
</li>
</ul>
<span i18n *ngIf="grouped_apps.mysterious">迷之物体</span>
<ul *ngIf="grouped_apps.mysterious" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.mysterious" class="nav-item">
<a (click)="$event.preventDefault(); chooseApp(app)" [href]="'https://mycard.moe/' + app.id" class="nav-link" [class.active]="app===currentApp">
<img *ngIf="app.icon" class="icon" [src]="app.icon">
{{app.name}}
</a>
</li>
</ul>
<span i18n *ngIf="grouped_apps.touhou">东方 Project</span>
<ul *ngIf="grouped_apps.touhou" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.touhou" class="nav-item">
<a (click)="$event.preventDefault(); chooseApp(app)" [href]="'https://mycard.moe/' + app.id" class="nav-link" [class.active]="app===currentApp">
<img *ngIf="app.icon" class="icon" [src]="app.icon">
{{app.name}}
</a>
</li>
</ul>
<span i18n *ngIf="grouped_apps.touhou_pc98">东方旧作</span>
<ul *ngIf="grouped_apps.touhou_pc98" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.touhou_pc98" class="nav-item">
<a (click)="$event.preventDefault(); chooseApp(app)" [href]="'https://mycard.moe/' + app.id" class="nav-link" [class.active]="app===currentApp">
<img *ngIf="app.icon" class="icon" [src]="app.icon">
{{app.name}}
</a>
</li>
</ul>
<span i18n *ngIf="grouped_apps.runtime_installed">已安装的运行库</span>
<ul *ngIf="grouped_apps.runtime_installed" class="nav nav-pills flex-column">
<li *ngFor="let app of grouped_apps.runtime_installed" class="nav-item">
<a (click)="$event.preventDefault(); chooseApp(app)" [href]="'https://mycard.moe/' + app.id" class="nav-link" [class.active]="app===currentApp">
<img *ngIf="app.icon" class="icon" [src]="app.icon">
{{app.name}}
</a>
</li>
</ul>
</nav>
<div class="resize" (mousedown)="mousedown($event)"></div>
</div>
<div id="right">
<div id="main">
<app-detail class="scroll" *ngIf="currentApp" [currentApp]="currentApp"></app-detail>
<roster class="scroll"></roster>
</div>
<div id="candy-wrapper" class="resize-wrapper resize-top" style="max-height: calc( 100% - 180px )">
<div class="resize" (mousedown)="mousedown($event)"></div>
<candy *ngIf="currentApp" [currentApp]="currentApp"></candy>
</div>
</div>
<div id="right-shadow"></div>
\ No newline at end of file
/**
* Created by zh99998 on 16/9/2.
*/
import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { AppsService } from './apps.service';
import { LoginService } from './login.service';
import { App, Category } from './app';
import { shell } from 'electron';
import { SettingsService } from './settings.sevices';
const ReconnectingWebSocket = require('reconnecting-websocket');
// import 'typeahead.js';
// import Options = Twitter.Typeahead.Options;
@Component({
moduleId: module.id,
selector: 'lobby',
templateUrl: 'lobby.component.html',
styleUrls: ['lobby.component.css'],
})
export class LobbyComponent implements OnInit {
currentApp: App;
private apps: Map<string, App>;
resizing: HTMLElement | undefined;
offset: number;
@ViewChild('search')
search: ElementRef;
private messages: WebSocket;
constructor(private appsService: AppsService, private loginService: LoginService,
private settingsService: SettingsService, private ref: ChangeDetectorRef) {
}
async ngOnInit() {
this.apps = await this.appsService.loadApps();
if (this.apps.size > 0) {
this.chooseApp(this.appsService.lastVisited || this.apps.get('ygopro')!);
await this.appsService.migrate();
for (let app of this.apps.values()) {
await this.appsService.update(app);
}
} else {
if (confirm('获取程序列表失败,是否重试?')) {
location.reload();
} else {
window.close();
}
}
// 特化个 YGOPRO 国际服聊天室。其他的暂时没需求。
if (!this.settingsService.getLocale().startsWith('zh')) {
this.apps.get('ygopro')!.conference = 'ygopro-international';
}
this.ref.detectChanges();
let url = new URL('wss://api.moecube.com:3100');
let params: URLSearchParams = url.searchParams;
params.set('user_id', this.loginService.user.email);
this.messages = new ReconnectingWebSocket(url);
this.messages.onmessage = async(event) => {
let data = JSON.parse(event.data);
console.log(data);
this.apps = await this.appsService.loadApps();
this.currentApp = this.apps.get(this.currentApp.id)!;
};
// $(this.search.nativeElement).typeahead(<any>{
// minLength: 1,
// highlight: true
// }, {
// name: 'apps',
// source: (query, syncResults) => {
// query = query.toLowerCase();
// let result = Array.from(this.apps.values())
// .filter((app: App) => [Category.game, Category.music, Category.book].includes(app.category))
// .filter((app: App) => app.id.includes(query) || app.name.toLowerCase().includes(query))
// .map((app: App) => app.name);
// console.log(result);
// syncResults(result);
// }
// });
document.addEventListener('mousemove', (event: MouseEvent) => {
if (!this.resizing) {
return;
}
if (this.resizing.classList.contains('resize-right')) {
let width = this.offset + event.clientX;
if (width < 190) {
width = 190;
}
if (width > 400) {
width = 400;
}
this.resizing.style.width = `${width}px`;
} else {
let height = this.offset - event.clientY;
let main_height = event.clientY - document.getElementById('navbar')!.clientHeight;
// console.log(event.clientY);
if (height > 150 && main_height > 180) {
if (height < 230) {
height = 230;
}
this.resizing.style.height = `${height}px`;
if ($('#candy').attr('data-minormax') !== 'default') {
$('#candy').attr('data-minormax', 'default');
$('#mobile-roster-icon').css('display', 'block');
$('#chat-toolbar').css('display', 'block');
$('#chat-rooms').css('display', 'block');
$('#context-menu').css('display', 'block');
$('#mobile-roster-icon').css('display', 'block');
$('#minimize').show();
$('#unminimize').hide();
$('#restore').hide();
$('#maximize').show();
}
} else if (height <= 150) {
$('#candy').attr('data-minormax', 'min');
this.resizing.style.height = '31px';
$('#mobile-roster-icon').css('display', 'none');
$('#chat-toolbar').css('display', 'none');
$('#chat-rooms').css('display', 'none');
$('#context-menu').css('display', 'none');
$('#mobile-roster-icon').css('display', 'none');
$('#minimize').hide();
$('#unminimize').show();
$('#restore').hide();
$('#maximize').show();
} else if (main_height <= 180) {
$('#candy').attr('data-minormax', 'max');
this.resizing.style.height = 'calc( 100% - 180px )';
$('#minimize').show();
$('#unminimize').hide();
$('#restore').show();
$('#maximize').hide();
}
}
});
document.addEventListener('mouseup', (event: MouseEvent) => {
document.body.classList.remove('resizing');
this.resizing = undefined;
});
}
mousedown(event: MouseEvent) {
// console.log(()
document.body.classList.add('resizing');
this.resizing = <HTMLElement>(<HTMLElement>event.target).parentNode;
if (this.resizing.classList.contains('resize-right')) {
this.offset = this.resizing.offsetWidth - event.clientX;
} else {
this.offset = this.resizing.offsetHeight + event.clientY;
}
}
chooseApp(app: App) {
this.currentApp = app;
this.appsService.lastVisited = app;
}
get grouped_apps() {
let contains = ['game', 'music', 'book'].map((value) => Category[value]);
let result = {runtime: []};
for (let app of this.apps.values()) {
let tag: string;
if (contains.includes(app.category)) {
if (app.isInstalled()) {
tag = 'installed';
} else {
tag = app.tags ? app.tags[0] : 'test';
}
} else {
if (app.isInstalled()) {
tag = 'runtime_installed';
} else {
tag = 'runtime';
}
}
if (!result[tag]) {
result[tag] = [];
}
result[tag].push(app);
}
return result;
}
openExternal(url: string) {
shell.openExternal(url);
}
}
<webview [src]="url" (will-navigate)="return_sso($event.url)" (did-get-redirect-request)="return_sso($event.newURL)" (new-window)="openExternal($event.url)"></webview>
\ No newline at end of file
/**
* Created by zh99998 on 16/9/2.
*/
import { Component } from '@angular/core';
import { LoginService } from './login.service';
import * as crypto from 'crypto';
import { shell } from 'electron';
@Component({
moduleId: module.id,
selector: 'login',
templateUrl: 'login.component.html',
styleUrls: ['login.component.css'],
})
export class LoginComponent {
url: string;
readonly return_sso_url = 'https://mycard.moe/login_callback'; // 这个url不会真的被使用,可以填写不存在的
constructor(private loginService: LoginService) {
let params = new URLSearchParams();
params.set('return_sso_url', this.return_sso_url);
let payload = Buffer.from(params.toString()).toString('base64');
let url = new URL('https://accounts.moecube.com');
params = url['searchParams'];
params.set('sso', payload);
params.set('sig', crypto.createHmac('sha256', 'zsZv6LXHDwwtUAGa').update(payload).digest('hex'));
this.url = url.toString();
if (this.loginService.logging_out) {
url = new URL('https://ygobbs.com/logout');
params = url['searchParams'];
// params.set('redirect', this.url);
// 暂时 hack 一下登出,因为聊天室现在没办法重新初始化,于是登出后刷新页面。
params.set('redirect', 'https://mycard.moe/logout_callback');
this.url = url.toString();
}
}
return_sso(return_url: string) {
if (return_url === 'https://mycard.moe/logout_callback') {
return location.reload();
}
if (!return_url.startsWith(this.return_sso_url)) {
return;
}
let token = new URL(return_url)['searchParams'].get('sso');
if (!token) {
return;
}
let user = this.toObject(new URLSearchParams(Buffer.from(token, 'base64').toString()));
this.loginService.login(user);
}
toObject(entries: Iterable<[string, any]>): any {
let result = {};
for (let [key, value] of entries) {
result[key] = value;
}
return result;
}
openExternal(url: string) {
shell.openExternal(url);
}
}
/**
* Created by zh99998 on 2016/10/25.
*/
import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
export interface User {
admin: boolean;
avatar_url: string;
email: string;
external_id: number;
moderator: boolean;
name: string;
username: string;
}
@Injectable()
export class LoginService {
user: User;
logged_in = false;
logging_out = false;
constructor (private http: Http) {
let data = localStorage.getItem('login');
if (data) {
this.user = JSON.parse(data);
this.logged_in = true;
}
}
login (user: User) {
this.user = user;
this.logged_in = true;
localStorage.setItem('login', JSON.stringify(user));
}
logout () {
this.logging_out = true;
this.logged_in = false;
localStorage.removeItem('login');
}
}
// import {MyCardNgFactory} from '../aot/app/mycard.module.ngfactory';
// import {getTranslationProviders} from './i18n-providers';
// import {enableProdMode} from '@angular/core';
// import {platformBrowser} from '@angular/platform-browser';
// enableProdMode();
//
// getTranslationProviders().then(providers => {
// const options = {providers};
// platformBrowser().bootstrapModuleFactory(MyCardNgFactory);
// });
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {getTranslationProviders} from './i18n-providers';
import {MyCard} from './mycard.module';
getTranslationProviders().then(providers => {
const options = {providers};
platformBrowserDynamic().bootstrapModule(MyCard, options);
});
/*:host {*/
/*background-color: white;*/
/*}*/
.page {
flex-grow: 1;
/*margin-bottom: 60px;*/
}
#avatar {
display: block;
float: left;
border-radius: 10%;
height: 1.5rem;
margin-top: 0.475rem;
}
.item {
display: block;
float: left;
padding-top: .425rem;
padding-bottom: .425rem;
margin: 0 0.8rem;
text-decoration: none;
color: #a7a7a7;
}
.item-icon {
color: #a7a7a7;
font-size: 18px;
margin: .6rem .3rem;
}
.item:hover, .item-icon:hover, .nav-link:hover {
color: #5e5e5e;
}
#update-status > i {
line-height: 1.5;
color: #5e5e5e;
padding-top: .425rem;
padding-bottom: .425rem;
}
a {
cursor: default;
}
/* https://github.com/electron/electron/issues/7661#event-827104990 */
lobby[hidden], webview[hidden] {
width: 0;
height: 0;
flex: 0 1;
display: inherit !important;
overflow: hidden;
}
/* 不加这个切到有 Webview 的页面,上面的圆角会消失 */
/* 即使加了这个,下面的圆角也会消失 */
/*webview {*/
/*overflow: hidden;*/
/*}*/
#navbar {
background-color: #f7f7f9!important;
padding: 0;
}
#navbar-brand {
color: #00a4d9;
font-size: 24px;
width: 190px;
margin: 0;
text-align: center;
}
#navbar .nav-link {
font-size: 18px;
color: #a7a7a7;
padding: .8rem 1.2em;
}
.nav-item.active {
background-color: white;
}
#navbar .nav-item.active .nav-link {
color: #00a4d9;
}
#border {
margin: .2rem 0.4rem;
color: #a7a7a7;
font-size: 1.2rem;
}
#navbar {
z-index: 100;
}
\ No newline at end of file
<nav id="navbar" class="navbar navbar-toggleable-md navbar-light">
<a id="navbar-brand" class="navbar-brand" href="#">MyCard</a>
<ul class="navbar-nav mr-auto">
<li *ngIf="!loginService.logged_in" class="nav-item active">
<a i18n class="nav-link" href="#">登录</a>
</li>
<!--<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'store'}" class="nav-item">-->
<!--<a (click)="currentPage = 'store'" class="nav-link" href="#">商店</a>-->
<!--</li>-->
<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'lobby'}" class="nav-item">
<a i18n (click)="currentPage='lobby'" class="nav-link" href="#">游戏</a>
</li>
<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'community'}" class="nav-item">
<a i18n (click)="currentPage='community'" class="nav-link" href="#">社区</a>
</li>
<!--<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'moesound'}" class="nav-item">-->
<!--<a i18n (click)="currentPage='moesound'" class="nav-link" href="#">萌音</a>-->
<!--</li>-->
<!--<li *ngIf="loginService.logged_in" [ngClass]="{active: currentPage === 'about'}" class="nav-item">-->
<!--<a i18n (click)="currentPage='about'" class="nav-link" href="#">关于</a>-->
<!--</li>-->
</ul>
<div id="navbar-right">
<div id="update-status">
<i #error [hidden]="update_status != 'error'" (click)="update_retry()" class="fa fa-exclamation-circle" data-toggle="tooltip" i18n-title title="更新出错,点击重试"></i>
<i #checking_for_update [hidden]="update_status != 'checking-for-update'" class="fa fa-spinner fa-pulse fa-spin" data-toggle="tooltip" i18n-title title="正在检查更新"></i>
<i #update_available [hidden]="update_status != 'update-available'" class="fa fa-refresh fa-spin" data-toggle="tooltip" i18n-title title="正在下载更新"></i>
<i #update_downloaded [hidden]="update_status != 'update-downloaded'" (click)="update_install()" class="fa fa-angle-double-up" data-toggle="tooltip" i18n-title title="下载更新完成,点击安装"></i>
</div>
<div id="user" *ngIf="loginService.logged_in">
<a href="#" class="profile"><img id="avatar" [src]="loginService.user.avatar_url" alt="image"></a>
<a href="#" class="profile item" id="username">{{loginService.user.username}}</a>
<i i18n (click)="loginService.logout()" class="fa fa-sign-out item-icon" aria-hidden="true" i18n-title title="切换用户"></i>
<i i18n data-toggle="modal" data-target="#settings-modal" class="fa fa-cog item-icon" aria-hidden="true" i18n-title title="设置"></i>
</div>
<div id="border">|</div>
<div id="window-buttons">
<i i18n (click)="currentWindow.minimize()" class="fa fa-minus" i18n-title title="最小化"></i>
<i i18n *ngIf="!currentWindow.isMaximized()" (click)="currentWindow.maximize()" class="fa fa-expand" i18n-title title="最大化"></i>
<i i18n *ngIf="currentWindow.isMaximized()" (click)="currentWindow.unmaximize()" class="fa fa-clone" i18n-title title="还原"></i>
<i i18n (click)="currentWindow.hide()" class="fa fa-times" i18n-title title="关闭"></i>
</div>
</div>
</nav>
<login class="page" *ngIf="!loginService.logged_in"></login>
<store class="page" *ngIf="loginService.logged_in" [hidden]="currentPage != 'store'"></store>
<lobby class="page" *ngIf="loginService.logged_in" [hidden]="currentPage != 'lobby'"></lobby>
<webview class="page" *ngIf="loginService.logged_in" [hidden]="currentPage != 'community'" src="https://ygobbs.com" (new-window)="openExternal($event.url)"></webview>
<!--<webview #moesound preload="./moesound.js" class="page" *ngIf="loginService.logged_in" [hidden]="currentPage != 'moesound'" src="http://moesound.com/" (new-window)="moesound_newwindow($event.url)" (did-finish-load)="moesound_loaded()"></webview>-->
<about class="page" *ngIf="loginService.logged_in" [hidden]="currentPage != 'about'"></about>
<!-- Modal -->
<div class="modal fade" id="settings-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 i18n class="modal-title" id="myModalLabel">MyCard 设置</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form (submit)="submit()">
<div class="modal-body">
<div class="container">
<div class="form-group row">
<label i18n for="locale" class="col-sm-2 col-form-label">语言</label>
<div class="col-sm-10">
<select class="form-control" id="locale" [(ngModel)]="locale" name="locale">
<option value="en-US">English</option>
<option value="zh-CN">简体中文</option>
</select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button i18n type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button i18n type="submit" class="btn btn-primary">确定</button>
</div>
</form>
</div>
</div>
</div>
import { ChangeDetectorRef, Component, ElementRef, OnInit, Renderer, ViewChild } from '@angular/core';
import 'bootstrap';
import { remote, shell } from 'electron';
import * as $ from 'jquery';
import * as Tether from 'tether';
import { LoginService } from './login.service';
import { SettingsService } from './settings.sevices';
window['Tether'] = Tether;
const autoUpdater: Electron.AutoUpdater = remote.getGlobal('autoUpdater');
@Component({
moduleId: module.id,
selector: 'mycard',
templateUrl: 'mycard.component.html',
styleUrls: ['mycard.component.css'],
})
export class MyCardComponent implements OnInit {
currentPage: string = 'lobby';
update_status: string | undefined = remote.getGlobal('update_status');
update_error: string | undefined;
currentWindow = remote.getCurrentWindow();
window = window;
@ViewChild('error')
error: ElementRef;
@ViewChild('checking_for_update')
checking_for_update: ElementRef;
@ViewChild('update_available')
update_available: ElementRef;
@ViewChild('update_downloaded')
update_downloaded: ElementRef;
update_elements: Map<string, ElementRef>;
locale: string;
resizing: HTMLElement | null;
@ViewChild('moesound')
moesound: ElementRef;
ngOnInit() {
this.update_elements = new Map(Object.entries({
'error': this.error,
'checking-for-update': this.checking_for_update,
'update-available': this.update_available,
'update-downloaded': this.update_downloaded
}));
// document.addEventListener('drop', (event)=>{
// console.log('drop', event);
// event.preventDefault();
//
// });
}
constructor(private renderer: Renderer, private loginService: LoginService, private ref: ChangeDetectorRef,
private settingsService: SettingsService) {
// renderer.listenGlobal('window', 'message', (event) => {
// console.log(event);
// // Do something with 'event'
// });
this.currentWindow.on('maximize', () => this.ref.detectChanges());
this.currentWindow.on('unmaximize', () => this.ref.detectChanges());
autoUpdater.on('error', (error) => {
this.set_update_status('error');
});
autoUpdater.on('checking-for-update', () => {
this.set_update_status('checking-for-update');
});
autoUpdater.on('update-available', () => {
this.set_update_status('update-available');
});
autoUpdater.on('update-not-available', () => {
this.set_update_status('update-not-available');
});
autoUpdater.on('update-downloaded', (event) => {
this.set_update_status('update-downloaded');
});
this.locale = this.settingsService.getLocale();
}
update_retry() {
autoUpdater.checkForUpdates();
}
update_install() {
autoUpdater.quitAndInstall();
}
set_update_status(status: string) {
console.log('autoUpdater', status);
if (this.update_status) {
let element = this.update_elements.get(this.update_status);
if (element) {
$(element.nativeElement).tooltip('dispose');
}
}
this.update_status = status;
this.ref.detectChanges();
let element = this.update_elements.get(this.update_status);
if (element) {
$(element.nativeElement).tooltip({ placement: 'bottom', container: 'body' });
}
}
openExternal(url: string) {
shell.openExternal(url);
}
submit() {
if (this.locale !== this.settingsService.getLocale()) {
localStorage.setItem(SettingsService.SETTING_LOCALE, this.locale);
remote.app.relaunch();
remote.app.quit();
}
}
//
// moesound_loaded() {
// this.moesound.nativeElement.insertCSS(`
// body > section > header, #bjax-target > div.row.m-t-lg.m-b-lg, #bjax-target > section {
// display: none;
// }
// body > section > section {
// top: 0!important;
// }
// `);
// }
//
// moesound_newwindow(url: string) {
// console.log(url);
// }
}
import { ErrorHandler, LOCALE_ID, NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { MyCardComponent } from './mycard.component';
import { LoginComponent } from './login.component';
import { StoreComponent } from './store.component';
import { LobbyComponent } from './lobby.component';
import { AppDetailComponent } from './app-detail.component';
import { RosterComponent } from './roster.component';
import { YGOProComponent } from './ygopro.component';
import { AppsService } from './apps.service';
import { SettingsService } from './settings.sevices';
import { LoginService } from './login.service';
import { DownloadService } from './download.service';
import { AboutComponent } from './about.component';
import { CandyComponent } from './candy.component';
import { RavenErrorHandler } from './error-handler';
import { NetworkComponent } from './network.component';
@NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule, HttpModule],
declarations: [
MyCardComponent, LoginComponent, StoreComponent, LobbyComponent,
AppDetailComponent, RosterComponent, YGOProComponent, AboutComponent, CandyComponent, NetworkComponent
],
bootstrap: [MyCardComponent],
providers: [
AppsService, SettingsService, LoginService, DownloadService
, {
provide: LOCALE_ID,
deps: [SettingsService],
useFactory: (settingsService: SettingsService) => settingsService.getLocale()
}
// , {
// provide: ErrorHandler, useClass: RavenErrorHandler
// }
],
schemas: [NO_ERRORS_SCHEMA]
})
export class MyCard {
}
#network {
display: inline-block;
vertical-align: middle;
width: 230px;
}
#network .input-group-btn > .btn:not(:last-child):not(.dropdown-toggle) {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
#network .input-group-btn > .dropdown-toggle {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
\ No newline at end of file
<div id="network" *ngIf="currentApp.network && currentApp.network.protocol == 'maotama'">
<!--<button (click)="log(appsService)">test</button>-->
<div class="input-group input-group-sm">
<input *ngIf="appsService.connections.get(currentApp)" [value]="appsService.connections.get(currentApp).address || 'Loading...'" readonly type="text" class="form-control" title="address">
<div class="input-group-btn" style="flex-direction: row">
<button i18n *ngIf="!appsService.connections.get(currentApp)" [disabled]="!appsService.allReady(currentApp)" (click)="appsService.network(currentApp, currentApp.network.servers[0])" type="button" class="btn btn-secondary btn-sm">联机</button>
<button i18n *ngIf="appsService.connections.get(currentApp)" (click)="copy(appsService.connections.get(currentApp).address)" [disabled]="!appsService.connections.get(currentApp).address" type="button" class="btn btn-secondary btn-sm">复制</button>
<button type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu" [class.dropdown-menu-right]="appsService.connections.get(currentApp)">
<h6 i18n class="dropdown-header">选择服务器</h6>
<a *ngFor="let server of currentApp.network.servers" (click)="appsService.network(currentApp, server)" class="dropdown-item" href="#">{{server.id}}</a>
<div *ngIf="appsService.connections.get(currentApp)" class="dropdown-divider"></div>
<a i18n *ngIf="appsService.connections.get(currentApp)" (click)="appsService.connections.get(currentApp).connection.close()" class="dropdown-item" href="#">取消</a>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { ChangeDetectorRef, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges, Injectable } from '@angular/core';
import { AppsService } from './apps.service';
import {App} from './app';
@Component({
moduleId: module.id,
selector: 'network',
templateUrl: 'network.component.html',
styleUrls: ['network.component.css'],
})
@Injectable()
export class NetworkComponent {
@Input()
currentApp: App;
constructor(private appsService: AppsService) {
console.log( 'constructor' );
}
}
:host {
position: fixed;
top: 0;
right: 0;
background: white;
}
#friend_list > ul > li > div > p {
font-size: 12px;
padding: 3px 5px;
margin: 0;
cursor: default;
}
#friend_list > ul > li > img {
border-radius: 25.5px;
}
#friend_list > ul > li {
padding: 3px;
list-style-type: none;
position: relative;
}
#friend_list > ul > li:hover {
padding: 3px;
list-style-type: none;
position: relative;
background: #aaa;
}
#friend_list > ul > li > i {
position: absolute;
top: 38px;
left: 38px;
}
#friend_list > ul {
padding: 0;
}
.fl {
float: left;
}
#friend_list i {
font-size: 10px;
}
.red {
color: red;
}
.green_light {
color: #8f8;
}
.red_light {
color: #f88;
}
.grey {
color: grey;
}
.blue_light {
color: #88f;
}
\ No newline at end of file
<!--<link href="roster.component.css" type="text/css" rel="stylesheet">--><!--<link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css">--><!--<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">--><!--<script src="../node_modules/core-js/client/shim.min.js"></script>-->
<!--<script src="../node_modules/zone.js/dist/zone.js"></script>--><!--<script src="../node_modules/reflect-metadata/Reflect.js"></script>--><!--<script src="../node_modules/systemjs/dist/system.src.js"></script>-->
<!--<script src="../systemjs.config.js"></script>-->
<!--<div>-->
<!--<div class="input-group" id="friend_search">-->
<!--<div class="input-group ">-->
<!--<i class="fa fa-search input-group-addon search" id="basic-addon1"></i>-->
<!--<input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">-->
<!--&lt;!&ndash;<span class="input-group-addon">+</span>&ndash;&gt;-->
<!--<button type="button" class="input-group-addon btn btn-sm btn-primary blue_light">+</button>-->
<!--</div>-->
<!--</div>-->
<!--<div id="friend_list">-->
<!--<ul>-->
<!--<li *ngFor="let contact of roster">-->
<!--<img class="fl" src="https://ygobbs.com//letter_avatar_proxy/v2/letter/q/cdc98d/45.png">-->
<!--<i class="fa fa-circle green_light"></i>-->
<!--<div class="fl">-->
<!--<p>{{contact.getName()}}</p>-->
<!--<p *ngIf="contact.getSubscription() != 'both'">等待确认</p>-->
<!--<p *ngIf="contact.getSubscription() == 'both'">{{contact.getStatus()}}</p>-->
<!--</div>-->
<!--<div style="clear: both"></div>-->
<!--</li>-->
<!--<li>-->
<!--<img class="fl" src="https://ygobbs.com//letter_avatar_proxy/v2/letter/q/cdc98d/45.png">-->
<!--<i class="fa fa-circle green_light"></i>-->
<!--<div class="fl">-->
<!--<p>我叫什么名来着</p>-->
<!--<p>发呆ing</p>-->
<!--</div>-->
<!--<div style="clear: both"></div>-->
<!--</li>-->
<!--<li>-->
<!--<img class="fl" src="https://ygobbs.com/user_avatar/ygobbs.com/sky%E7%A5%9E%E6%99%BA/45/16501_1.png">-->
<!--<i class="fa fa-play-circle red_light"></i>-->
<!--<div class="fl">-->
<!--<p>我叫什么名来着</p>-->
<!--<p class="red">东方妖妖梦</p>-->
<!--</div>-->
<!--<div style="clear: both"></div>-->
<!--</li>-->
<!--<li>-->
<!--<img class="fl" src="https://ygobbs.com/user_avatar/ygobbs.com/%E6%98%9F%E5%85%89pokeboy/45/10237_1.png">-->
<!--<i class="fa fa-question-circle blue_light"></i>-->
<!--<div class="fl">-->
<!--<p>我叫什么名来着</p>-->
<!--<p>发呆ing</p>-->
<!--</div>-->
<!--<div style="clear: both"></div>-->
<!--</li>-->
<!--<li>-->
<!--<img class="fl" src="https://ygobbs.com//user_avatar/ygobbs.com/%E7%B1%B3%E7%B1%B3%E7%B1%B3%E5%BE%B7%E6%8B%89%E4%BB%80/45/17187_1.png">-->
<!--<i class="fa fa-clock-o grey"></i>-->
<!--<div class="fl">-->
<!--<p>我叫什么名来着</p>-->
<!--<p>发呆ing</p>-->
<!--</div>-->
<!--<div style="clear: both"></div>-->
<!--</li>-->
<!--</ul>-->
<!--</div>-->
<!--</div>-->
/**
* Created by zh99998 on 16/9/2.
*/
import {Component, Input, EventEmitter, Output, OnInit, OnChanges} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'roster',
templateUrl: 'roster.component.html',
styleUrls: ['roster.component.css'],
})
export class RosterComponent implements OnInit, OnChanges {
@Input()
roster: any;
@Output()
chat = new EventEmitter<string>();
ngOnInit() {
// console.log(this.roster);
}
ngOnChanges() {
// console.log(this.roster);
}
}
/**
* Created by weijian on 2016/10/24.
*/
import {Injectable} from '@angular/core';
import {remote} from 'electron';
import * as path from 'path';
export interface Library {
'default': boolean;
path: string;
}
@Injectable()
export class SettingsService {
static SETTING_LIBRARY = 'library';
static defaultLibraries = [
{
'default': true,
path: path.join(remote.app.getPath('appData'), 'MyCardLibrary')
},
];
static SETTING_LOCALE = 'locale';
static defaultLocale = remote.app.getLocale();
locale: string;
libraries: Library[];
getLibraries () {
if (!this.libraries) {
let data = localStorage.getItem(SettingsService.SETTING_LIBRARY);
if (!data) {
this.libraries = SettingsService.defaultLibraries;
localStorage.setItem(SettingsService.SETTING_LIBRARY,
JSON.stringify(SettingsService.defaultLibraries));
} else {
this.libraries = JSON.parse(data);
}
}
return this.libraries;
}
addLibrary (libraryPath: string, isDefault: boolean) {
let libraries = this.getLibraries();
if (isDefault) {
libraries.forEach((l) => {
l.default = false;
});
}
libraries.push({'default': isDefault, path: libraryPath});
this.libraries = libraries;
localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries));
}
setDefaultLibrary (library: Library) {
let libraries = this.getLibraries();
libraries.forEach((l) => {
l.default = library.path === l.path;
});
this.libraries = libraries;
localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries));
}
getDefaultLibrary (): Library {
if (!this.libraries) {
this.getLibraries();
}
let result = this.libraries.find((item) => item.default === true);
if (result) {
return result;
} else {
throw('no default library found');
}
}
getLocale (): string {
if (!this.locale) {
let locale = localStorage.getItem(SettingsService.SETTING_LOCALE);
if (!locale) {
this.locale = SettingsService.defaultLocale;
localStorage.setItem(SettingsService.SETTING_LOCALE, SettingsService.defaultLocale);
} else {
this.locale = locale;
}
}
return this.locale;
}
setLocale (locale: string) {
this.locale = locale;
localStorage.setItem(SettingsService.SETTING_LOCALE, locale);
}
}
[{"__symbolic":"module","version":3,"metadata":{"ComparableSet":{"__symbolic":"class","extends":{"__symbolic":"reference","name":"Set"},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":4,"character":25,"context":{"typeName":"Iterable"}}]}],"isSuperset":[{"__symbolic":"method"}],"union":[{"__symbolic":"method"}],"intersection":[{"__symbolic":"method"}],"difference":[{"__symbolic":"method"}]}}}},{"__symbolic":"module","version":1,"metadata":{"ComparableSet":{"__symbolic":"class","extends":{"__symbolic":"reference","name":"Set"},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":4,"character":25,"context":{"typeName":"Iterable"}}]}],"isSuperset":[{"__symbolic":"method"}],"union":[{"__symbolic":"method"}],"intersection":[{"__symbolic":"method"}],"difference":[{"__symbolic":"method"}]}}}}]
\ No newline at end of file
store
\ No newline at end of file
/**
* Created by zh99998 on 16/9/2.
*/
import {Component} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'store',
templateUrl: 'store.component.html',
styleUrls: ['store.component.css'],
})
export class StoreComponent {
}
This diff is collapsed.
This diff is collapsed.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
'use strict';
Object.defineProperty(exports, '__esModule', {value: true});
const webpack = require('webpack');
const path = require('path');
const glob_copy_webpack_plugin_1 = require('../../plugins/glob-copy-webpack-plugin');
const named_lazy_chunks_webpack_plugin_1 = require('../../plugins/named-lazy-chunks-webpack-plugin');
const utils_1 = require('./utils');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
/**
* Enumerate loaders and their dependencies from this file to let the dependency validator
* know they are used.
*
* require('source-map-loader')
* require('raw-loader')
* require('script-loader')
* require('json-loader')
* require('url-loader')
* require('file-loader')
* require('@angular-devkit/build-optimizer')
*/
function getCommonConfig(wco) {
const {projectRoot, buildOptions, appConfig} = wco;
const appRoot = path.resolve(projectRoot, appConfig.root);
const nodeModules = path.resolve(projectRoot, 'node_modules');
let extraPlugins = [];
let extraRules = [];
let entryPoints = {};
if (appConfig.main) {
entryPoints['main'] = [path.resolve(appRoot, appConfig.main)];
}
if (appConfig.polyfills) {
entryPoints['polyfills'] = [path.resolve(appRoot, appConfig.polyfills)];
}
// determine hashing format
const hashFormat = utils_1.getOutputHashFormat(buildOptions.outputHashing);
// process global scripts
if (appConfig.scripts.length > 0) {
const globalScripts = utils_1.extraEntryParser(appConfig.scripts, appRoot, 'scripts');
// add entry points and lazy chunks
globalScripts.forEach(script => {
let scriptPath = `script-loader!${script.path}`;
entryPoints[script.entry] = (entryPoints[script.entry] || []).concat(scriptPath);
});
}
// process asset entries
if (appConfig.assets) {
extraPlugins.push(new glob_copy_webpack_plugin_1.GlobCopyWebpackPlugin({
patterns: appConfig.assets,
globOptions: {cwd: appRoot, dot: true, ignore: '**/.gitkeep'}
}));
}
if (buildOptions.progress) {
extraPlugins.push(new ProgressPlugin({profile: buildOptions.verbose, colors: true}));
}
if (buildOptions.showCircularDependencies) {
extraPlugins.push(new CircularDependencyPlugin({
exclude: /(\\|\/)node_modules(\\|\/)/
}));
}
if (buildOptions.buildOptimizer) {
extraRules.push({
test: /\.js$/,
use: [{
loader: '@angular-devkit/build-optimizer/webpack-loader',
options: {sourceMap: buildOptions.sourcemaps}
}]
});
}
if (buildOptions.namedChunks) {
extraPlugins.push(new named_lazy_chunks_webpack_plugin_1.NamedLazyChunksWebpackPlugin());
}
return {
target: 'electron-renderer',
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', nodeModules],
symlinks: !buildOptions.preserveSymlinks
},
resolveLoader: {
modules: [nodeModules, 'node_modules']
},
context: __dirname,
entry: entryPoints,
output: {
path: path.resolve(projectRoot, buildOptions.outputPath),
publicPath: buildOptions.deployUrl,
filename: `[name]${hashFormat.chunk}.bundle.js`,
chunkFilename: `[id]${hashFormat.chunk}.chunk.js`
},
module: {
rules: [
{enforce: 'pre', test: /\.js$/, loader: 'source-map-loader', exclude: [nodeModules]},
{test: /\.json$/, loader: 'json-loader'},
{test: /\.html$/, loader: 'raw-loader'},
{test: /\.(eot|svg)$/, loader: `file-loader?name=[name]${hashFormat.file}.[ext]`},
{
test: /\.(jpg|png|webp|gif|otf|ttf|woff|woff2|cur|ani)$/,
loader: `url-loader?name=[name]${hashFormat.file}.[ext]&limit=10000`
},
{test: require.resolve('bootstrap'), use: 'imports-loader?jQuery=jquery,Tether=tether'},
{test: require.resolve('candy/libs.bundle.js'), use: 'imports-loader?jQuery=jquery'},
{test: require.resolve('candy/libs.bundle.js'), use: 'exports-loader?Mustache,Strophe,Base64,MD5'},
{
test: require.resolve('candy'),
use: 'imports-loader?jQuery=jquery,{Mustache%2CStrophe%2CBase64%2CMD5}=candy/libs.bundle.js'
},
{test: require.resolve('candy'), use: 'exports-loader?Candy'},
{test: require.resolve('candy-shop/me-does/candy.js'), use: 'imports-loader?jQuery=jquery,Candy=candy'},
{test: require.resolve('candy-shop/me-does/candy.js'), use: 'exports-loader?CandyShop.MeDoes'},
{test: require.resolve('candy-shop/modify-role/candy.js'), use: 'imports-loader?jQuery=jquery,Candy=candy'},
{test: require.resolve('candy-shop/modify-role/candy.js'), use: 'exports-loader?CandyShop.ModifyRole'},
{test: require.resolve('candy-shop/namecomplete/candy.js'), use: 'imports-loader?jQuery=jquery,Candy=candy'},
{test: require.resolve('candy-shop/namecomplete/candy.js'), use: 'exports-loader?CandyShop.NameComplete'},
{test: require.resolve('candy-shop/notifications/candy.js'), use: 'imports-loader?jQuery=jquery,Candy=candy'},
{test: require.resolve('candy-shop/notifications/candy.js'), use: 'exports-loader?CandyShop.Notifications'},
{test: require.resolve('candy-shop/notifyme/candy.js'), use: 'imports-loader?jQuery=jquery,Candy=candy'},
{test: require.resolve('candy-shop/notifyme/candy.js'), use: 'exports-loader?CandyShop.NotifyMe'},
{test: require.resolve('candy-shop/refocus/candy.js'), use: 'imports-loader?jQuery=jquery,Candy=candy'},
{test: require.resolve('candy-shop/refocus/candy.js'), use: 'exports-loader?CandyShop.Refocus'}
].concat(extraRules)
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
].concat(extraPlugins),
node: {
fs: 'empty',
// `global` should be kept true, removing it resulted in a
// massive size increase with Build Optimizer on AIO.
global: true,
crypto: 'empty',
tls: 'empty',
net: 'empty',
process: true,
module: false,
clearImmediate: false,
setImmediate: false
},
externals: {
bufferutil: "require('bufferutil')",
'utf-8-validate': "require('utf-8-validate')",
iconv: "require('iconv')",
'iconv-loader': "require('iconv')",
}
};
}
exports.getCommonConfig = getCommonConfig;
//# sourceMappingURL=/users/hansl/sources/angular-cli/models/webpack-configs/common.js.map
This diff was suppressed by a .gitattributes entry.
<!DOCTYPE html>
<html>
<head>
<title>MyCard</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="node_modules/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch((error) => {
document.getElementById('loading').setAttribute('hidden', 'hidden');
document.getElementById('failed').removeAttribute('hidden');
document.getElementById('error').removeAttribute('hidden');
document.getElementById('error').textContent = error;
});
</script>
</head>
<body>
<mycard>
<!--<div id="loading">MyCard <span id="version"></span> Loading...</div>-->
<div id="loading-bar">
<span class="navbar-brand">MyCard</span>
<i class="fa fa-times close" i18n="" i18n-title="" title="关闭" onclick="window.close()"></i>
</div>
<div class="loading">
<img src="./images/CubbitLogo.png">
<p>
LOADING
<span>.</span>
<span>.</span>
<span>.</span>
</p>
</div>
<div id="failed" hidden>发生了错误,请复制以下错误信息并联系 support@mycard.moe</div>
<pre id="error" hidden></pre>
</mycard>
<script>
document.body.classList.add(process.platform);
// document.getElementById('version').innerHTML = require('electron').remote.app.getVersion();
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
</body>
</html>
This diff is collapsed.
import * as child_process from 'child_process';
import {ChildProcess} from 'child_process';
import {app, BrowserWindow, Menu, shell, Tray} from 'electron';
import * as isDev from 'electron-is-dev';
import * as Store from 'electron-store';
import {autoUpdater} from 'electron-updater';
import * as net from 'net';
import {Socket} from 'net';
import {EOL} from 'os';
import * as path from 'path';
import * as readline from 'readline';
import {argv} from 'yargs';
class Main {
static aria2: ChildProcess;
static mainWindow: Electron.BrowserWindow | null;
static updateWindow: Electron.BrowserWindow | null;
static tray: Electron.Tray;
static store = new Store();
// 提权
static handleElevate() {
if (argv['e']) {
if (process.platform === 'darwin') {
app.dock.hide();
}
const elevate = JSON.parse(new Buffer(argv['e'], 'base64').toString());
net.connect(elevate['ipc'], function (this: Socket) {
process.send = (message, sendHandle) => this.write(JSON.stringify(message) + EOL);
this.on('end', () => process.emit('disconnect', () => null));
readline.createInterface({input: this}).on('line', (line) => process.emit('message', JSON.parse(line)));
process.argv = elevate['arguments'][1];
require('./' + elevate['arguments'][0]);
});
return true;
}
}
static handleSingleInstance() {
// Someone tried to run a second instance, we should focus our window.
if (this.mainWindow) {
if (this.mainWindow.isMinimized()) {
this.mainWindow.restore();
}
if (!this.mainWindow.isVisible()) {
this.mainWindow.show();
}
this.mainWindow.focus();
}
}
static createAria2() {
const aria2c = path.join(process.env['NODE_ENV'] === 'production' ? process.resourcesPath! : app.getAppPath(),
'bin', process.platform === 'win32' ? 'aria2c.exe' : 'aria2c');
return child_process.spawn(aria2c, [
'--enable-rpc',
'--rpc-allow-origin-all',
'--continue',
'--split=10',
'--min-split-size=1M',
'--max-connection-per-server=10',
'--remove-control-file',
'--allow-overwrite'
], {stdio: 'ignore'});
}
static createWindow() {
// Create the browser window.
this.mainWindow = new BrowserWindow({
width: 1024,
height: 640,
minWidth: 1024,
minHeight: 640,
frame: process.platform === 'darwin',
titleBarStyle: process.platform === 'darwin' ? 'hidden' : undefined,
webPreferences: {
webSecurity: false
}
});
let locale = this.store.get('locale') || app.getLocale();
locale = locale.startsWith('zh') ? 'zh-CN' : 'en-US';
// and load the index.html of the app.
this.mainWindow.loadURL(argv.url || `file://${__dirname}/index.html`);
// Open the DevTools.
if (isDev) {
this.mainWindow.webContents.openDevTools();
}
// 在浏览器中打开新窗口
this.mainWindow.webContents.on('new-window', function (event, url) {
event.preventDefault();
shell.openExternal(url);
});
// Emitted when the window is closed.
this.mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
this.mainWindow = null;
});
}
static createTray() {
const icon = path.join(process.env['NODE_ENV'] === 'production' ? process.resourcesPath! : app.getAppPath(), 'assets', 'icon.ico');
this.tray = new Tray(icon);
this.tray.setToolTip('MoeCube');
this.tray.setContextMenu(Menu.buildFromTemplate([
{label: '显示主界面', type: 'normal', click: this.toggleMainWindow},
{label: '退出', type: 'normal', click: app.quit}
]));
}
static toggleMainWindow() {
if (this.mainWindow!.isVisible()) {
this.mainWindow!.hide();
} else {
this.mainWindow!.show();
}
}
static main() {
if (this.handleElevate()) {
return;
}
// 单例
if (app.makeSingleInstance(this.handleSingleInstance)) {
return;
}
// 调试模式
if (!process.env['NODE_ENV']) {
process.env['NODE_ENV'] = isDev ? 'development' : 'production';
}
this.aria2 = this.createAria2();
global['autoUpdater'] = autoUpdater;
autoUpdater.on('update-downloaded', () => {
this.updateWindow = new BrowserWindow({width: 640, height: 360});
this.updateWindow.loadURL(`file://${__dirname}/update/index.html`);
this.updateWindow.on('closed', () => this.updateWindow = null);
});
app.on('ready', () => {
this.createWindow();
if (process.platform === 'win32') {
this.createTray();
}
if (process.env['NODE_ENV'] === 'production') {
autoUpdater.checkForUpdates();
}
});
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (!this.mainWindow) {
this.createWindow();
}
});
app.on('quit', () => {
// windows 在非 detach 模式下会自动退出子进程
if (process.platform !== 'win32') {
this.aria2.kill();
}
});
}
}
Main.main();
This diff is collapsed.
This diff is collapsed.
document.addEventListener("DOMContentLoaded", function(event) {
$('#nav,.navbar-header').removeClass('nav-xs');
});
This diff is collapsed.
This diff is collapsed.
......@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<link href="./node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
<style>
html,div,body,td,tr{
......@@ -93,12 +93,12 @@
<span id="myScore"></span>
<span id="myExp"></span>
<span id="myGold"></span>
</div>
</div>
</div>
<div style="height:100%; float:right; display:table;">
<span id="win" class="winOrLose"></span>
<span id="lose" class="winOrLose"></span>
</div>
</div>
</div>
<div class="clear:both"></div>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<webview src="https://ygobbs.com" (new-window)="shell.openExternal($event.url)"></webview>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff was suppressed by a .gitattributes entry.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment