Commit 79038a4d authored by 李维's avatar 李维

new

parent 3695f7ce
......@@ -9,7 +9,6 @@
# dependencies
/node_modules
/publish
# profiling files
chrome-profiler-events*.json
......
This diff is collapsed.
......@@ -3,9 +3,13 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
"ng-template-generator": {
"ng-one": {
"projectType": "application",
"schematics": {},
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
......@@ -13,26 +17,39 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/ng-template-generator",
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets",
{ "glob": "**/*", "input": "src/assets/play/libs/service-worker/", "output": "/" },
{
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
},
{
"glob": "**/*",
"input": "./dist/game/",
"output": "/assets/cocos/"
}
],
"styles": [
"src/styles.scss",
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css"
"./node_modules/font-awesome/css/font-awesome.css",
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
"./node_modules/animate.css/animate.min.css"
],
"scripts": []
"scripts": [
"src/assets/play/libs/audio-recorder/lame.min.js",
"src/assets/play/libs/audio-recorder/worker.js",
"src/assets/play/libs/audio-recorder/recorder.js"
]
},
"configurations": {
"production": {
......@@ -47,39 +64,28 @@
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "ng-template-generator:build"
"browserTarget": "ng-one:build"
},
"configurations": {
"production": {
"browserTarget": "ng-template-generator:build:production"
"browserTarget": "ng-one:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "ng-template-generator:build"
"browserTarget": "ng-one:build"
}
},
"test": {
......@@ -94,8 +100,7 @@
"src/assets"
],
"styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"src/styles.css"
"src/styles.scss"
],
"scripts": []
}
......@@ -117,16 +122,15 @@
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "ng-template-generator:serve"
"devServerTarget": "ng-one:serve"
},
"configurations": {
"production": {
"devServerTarget": "ng-template-generator:serve:production"
"devServerTarget": "ng-one:serve:production"
}
}
}
}
}
},
"defaultProject": "ng-template-generator"
}
}},
"defaultProject": "ng-one"
}
\ No newline at end of file
......@@ -4,7 +4,7 @@
* 运行 npm run publish T_01,T_02,T_03,T_04 命令来分别打包 T_01,T_02,T_03,T_04 这四个模板,注意逗号要用英文的
* 运行 npm run publish all 命令来打包所有模板
*/
const spawn = require('child_process').spawn;
const path = require("path");
const fs = require("fs");
......@@ -12,9 +12,9 @@ const os = require('os');
const compressing = require("compressing");
//Linux系统上'Linux'
//macOS 系统上'Darwin'
//macOS 系统上'Darwin'
//Windows系统上'Windows_NT'
let sysType = os.type();
let sysType = os.type();
Date.prototype.Format = function(fmt) {
var o = {
......@@ -44,9 +44,9 @@ const runSpawn = async function (){
await new Promise(function(resolve,reject){
let pkg = require("../package.json");
let pkg = require("../package.json");
let ls;
let ls;
if(sysType==="Windows_NT"){
//ng build --prod --build--optimizer --base-href /ng-one/
ls = spawn("cmd.exe", ['/c', 'ng', 'build', '--prod', '--build--optimizer', '--base-href', '/template-base-href/'] );
......@@ -57,7 +57,7 @@ const runSpawn = async function (){
ls.stdout.on('data', (data) => {
console.log(` ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
reject();
......@@ -66,13 +66,13 @@ const runSpawn = async function (){
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
//要压缩的目录
let zippath = path.resolve(__dirname,"../dist", pkg.name);
let zippath = path.resolve(__dirname,"../dist");
//压缩包的存放目录
let date = new Date();
let zipname = pkg.name+"_"+date.Format("yyyyMMdd hh-mm-ss");
let zipdir = path.resolve(__dirname,"../publish/"+zipname+".zip");
clean(zipdir); //删除原有的包
const tarStream = new compressing.zip.Stream();
fs.readdir(zippath,function(err,files){
if(err){
......@@ -84,16 +84,16 @@ const runSpawn = async function (){
tarStream.addEntry(zippath+"/"+files[i]);
}
let writeStream = fs.createWriteStream(zipdir);
tarStream.pipe(writeStream);
tarStream.pipe(writeStream);
writeStream.on('close', () => {
console.log(`模板 ${pkg.name} 打包已完成!`);
resolve();
})
});
});
});
});
});
}
// let projects = "";
......@@ -101,7 +101,7 @@ const runSpawn = async function (){
// console.log("缺少参数");
// return;
// }
// projects = process.argv[2];
// projects = process.argv[2];
let exec = async function(){
//压缩模板
......@@ -110,5 +110,6 @@ let exec = async function(){
exec();
\ No newline at end of file
This diff is collapsed.
......@@ -2,56 +2,77 @@
"name": "ng-template-generator",
"version": "0.0.1",
"scripts": {
"start": "ng serve",
"start": "ng serve --host=0.0.0.0",
"build": "ng build --build--optimizer --aot --base-href /JGT/v3/",
"publish": "node ./bin/publish.js",
"ng": "ng",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
"publish": "node ./bin/publish.js"
},
"private": true,
"dependencies": {
"@angular/animations": "~9.0.2",
"@angular/common": "~9.0.2",
"@angular/compiler": "~9.0.2",
"@angular/core": "~9.0.2",
"@angular/forms": "~9.0.2",
"@angular/platform-browser": "~9.0.2",
"@angular/platform-browser-dynamic": "~9.0.2",
"@angular/router": "~9.0.2",
"@fortawesome/angular-fontawesome": "^0.6.0",
"@fortawesome/fontawesome-svg-core": "^1.2.27",
"@fortawesome/free-regular-svg-icons": "^5.12.1",
"@fortawesome/free-solid-svg-icons": "^5.12.1",
"@tweenjs/tween.js": "^18.5.0",
"ali-oss": "^6.5.1",
"compressing": "^1.5.0",
"ng-zorro-antd": "^8.5.2",
"rxjs": "~6.5.4",
"@angular/animations": "^7.2.10",
"@angular/cdk": "^7.2.2",
"@angular/common": "^7.2.10",
"@angular/compiler": "^7.2.10",
"@angular/core": "^7.2.10",
"@angular/flex-layout": "^7.0.0-beta.24",
"@angular/forms": "^7.2.10",
"@angular/http": "^7.2.10",
"@angular/material": "^7.2.2",
"@angular/platform-browser": "^7.2.10",
"@angular/platform-browser-dynamic": "^7.2.10",
"@angular/platform-server": "^7.2.10",
"@angular/router": "^7.2.10",
"@tweenjs/tween.js": "^17.3.0",
"ali-oss": "^6.0.0",
"angular-bootstrap-colorpicker": "^3.0.32",
"angular-cropperjs": "^1.0.1",
"angular2-draggable": "^2.1.9",
"angular2-fontawesome": "^5.2.1",
"angularx-qrcode": "^1.5.3",
"animate.css": "^3.7.0",
"bootstrap": "^4.1.1",
"browser-image-compression": "^1.0.5",
"compressing": "^1.4.0",
"core-js": "^2.6.1",
"cropperjs": "1.4.1",
"css-element-queries": "^1.0.2",
"decimal.js": "^10.0.1",
"font-awesome": "^4.7.0",
"hammerjs": "^2.0.8",
"install": "^0.12.2",
"karma-cli": "^2.0.0",
"lodash": "^4.17.10",
"nedb": "^1.8.0",
"ng-lottie": "^0.3.1",
"ng-zorro-antd": "^7.2.0",
"ngx-color-picker": "^9.0.0",
"node-sass": "^4.14.0",
"npm": "^6.5.0",
"rxjs": "^6.3.3",
"rxjs-compat": "^6.3.3",
"rxjs-tslint": "^0.1.6",
"spark-md5": "^3.0.0",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"
"webpack": "^4.28.2",
"zone.js": "^0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.900.3",
"@angular/cli": "~9.0.3",
"@angular/compiler-cli": "~9.0.2",
"@angular/language-service": "~9.0.2",
"@types/jasmine": "~3.5.0",
"@angular-devkit/build-angular": "^0.11.4",
"@angular/cli": "^7.2.10",
"@angular/compiler-cli": "^7.2.10",
"@angular/language-service": "^7.2.10",
"@types/jasmine": "^3.3.5",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^5.1.2",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.1.0",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"protractor": "~5.4.3",
"ts-node": "~8.3.0",
"tslint": "~5.18.0",
"typescript": "~3.7.5"
"@types/node": "^10.12.18",
"codelyzer": "^4.5.0",
"jasmine-core": "^3.3.0",
"jasmine-spec-reporter": "^4.2.1",
"karma": "^3.1.4",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.0",
"karma-jasmine": "^2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "^5.4.2",
"ts-node": "~5.0.1",
"tslint": "^5.12.0",
"typescript": "3.1.1"
}
}
import { ErrorHandler } from '@angular/core';
export class MyErrorHandler implements ErrorHandler {
handleError(error) {
console.log(error.stack);
(<any> window).courseware.sendErrorLog(error);
}
}
\ No newline at end of file
......@@ -5,12 +5,12 @@ import { Component , OnInit} from '@angular/core';
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
export class AppComponent implements OnInit{
type = 'play';
constructor() {
const tp = this.getQueryString('type');
if (tp) {
let tp = this.getQueryString("type");
if (tp){
this.type = tp;
}
}
......
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, ErrorHandler } from '@angular/core';
import {MyErrorHandler} from './MyError';
import { AppComponent } from './app.component';
import { NgZorroAntdModule, NZ_I18N, zh_CN } from 'ng-zorro-antd';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgZorroAntdModule, NZ_I18N, zh_CN } from 'ng-zorro-antd';
import { NzButtonModule } from 'ng-zorro-antd/button';
import {Angular2FontawesomeModule} from 'angular2-fontawesome/angular2-fontawesome';
import { AppComponent } from './app.component';
import { FormComponent } from './form/form.component';
import { PlayComponent } from "./play/play.component";
// import { ColorPickerModule } from 'ngx-color-picker';
import { LessonTitleConfigComponent } from './common/lesson-title-config/lesson-title-config.component';
import { AudioRecorderComponent } from './common/audio-recorder/audio-recorder.component';
import { PlayerContentWrapperComponent } from './common/player-content-wrapper/player-content-wrapper.component';
/** 配置 angular i18n **/
import { registerLocaleData } from '@angular/common';
import zh from '@angular/common/locales/zh';
import {FormComponent} from './form/form.component';
import {PlayComponent} from './play/play.component';
import {LessonTitleConfigComponent} from './common/lesson-title-config/lesson-title-config.component';
import {BackgroundImagePipe} from './pipes/background-image.pipe';
import {UploadImageWithPreviewComponent} from './common/upload-image-with-preview/upload-image-with-preview.component';
import {PlayerContentWrapperComponent} from './common/player-content-wrapper/player-content-wrapper.component';
import {CustomHotZoneComponent} from './common/custom-hot-zone/custom-hot-zone.component';
import {UploadVideoComponent} from './common/upload-video/upload-video.component';
import {TimePipe} from './pipes/time.pipe';
import {ResourcePipe} from './pipes/resource.pipe';
import {AudioRecorderComponent} from './common/audio-recorder/audio-recorder.component';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import {UploadImageWithPreviewComponent} from "./common/upload-image-with-preview/upload-image-with-preview.component";
import {BackgroundImagePipe} from "./pipes/background-image.pipe";
import {UploadVideoComponent} from "./common/upload-video/upload-video.component";
import {ResourcePipe} from "./pipes/resource.pipe";
import {TimePipe} from "./pipes/time.pipe";
import {CustomHotZoneComponent} from "./common/custom-hot-zone/custom-hot-zone.component";
registerLocaleData(zh);
@NgModule({
......@@ -40,26 +41,22 @@ registerLocaleData(zh);
TimePipe,
UploadVideoComponent,
CustomHotZoneComponent,
PlayerContentWrapperComponent
],
imports: [
BrowserModule,
NgZorroAntdModule,
FormsModule,
HttpClientModule,
BrowserAnimationsModule,
FontAwesomeModule
BrowserModule,
Angular2FontawesomeModule,
NgZorroAntdModule,
//ColorPickerModule
],
providers: [
{provide: ErrorHandler, useClass: MyErrorHandler},
/** 配置 ng-zorro-antd 国际化(文案 及 日期) **/
providers : [
{ provide: NZ_I18N, useValue: zh_CN }
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(library: FaIconLibrary) {
library.addIconPacks(fas, far);
}
}
export class AppModule { }
<div class="d-flex">
<div class="p-btn-record d-flex">
<div class="btn-clear" style="cursor: pointer" (click)="onBtnClearAudio()" *ngIf="withRmBtn && (audioUrl || audioBlob)">
<fa-icon icon="times"></fa-icon>
<div
class="btn-clear"
(click)="onBtnClearAudio()"
*ngIf="withRmBtn && (audioUrl || audioBlob)"
>
<fa name="close"></fa>
</div>
<div class="btn-record" *ngIf="type===Type.RECORD && !isUploading"
[class.p-recording]="isRecording"
(click)="onBtnRecord()">
<fa-icon icon="microphone"></fa-icon>
<div
class="btn-record"
*ngIf="type === Type.RECORD && !isUploading"
[class.p-recording]="isRecording"
(click)="onBtnRecord()"
>
<i nz-icon nzType="audio" nzTheme="outline"></i>
Record Audio
</div>
<nz-upload
[nzAccept] = "'.mp3'"
[nzShowUploadList]="false"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="handleChange($event)">
<div class="btn-upload" [ngClass]="{'has-clear': withRmBtn && (audioUrl || audioBlob)}" *ngIf="type===Type.UPLOAD && !isUploading">
<fa-icon icon="upload"></fa-icon>
[nzAccept]="'.mp3'"
[nzShowUploadList]="false"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="handleChange($event)"
>
<div
class="btn-upload ng-star-inserted"
[ngClass]="{ 'has-clear': withRmBtn && (audioUrl || audioBlob) }"
*ngIf="type === Type.UPLOAD && !isUploading"
>
<i nz-icon nzType="cloud-upload" nzTheme="outline"></i>
Upload Audio
</div>
</nz-upload>
<div class="p-upload-progress-bg" *ngIf="isUploading">
<div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-bg" [style.width]="progress + '%'"></div>
<div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon>
<i nz-icon nzType="loading" nzTheme="outline"></i>
Uploading...
</div>
</div>
<div
*ngIf="audioUrl && needRemove; then truthyTemplate; else falsyTemplate"
></div>
<div *ngIf="audioUrl && needRemove; then truthyTemplate else falsyTemplate"></div>
<ng-template #truthyTemplate >
<ng-template #truthyTemplate>
<div class="btn-delete" (click)="onBtnDeleteAudio()">
<fa-icon icon="close"></fa-icon>
<i nz-icon nzType="close" nzTheme="outline"></i>
</div>
</ng-template>
<ng-template #falsyTemplate>
<div class="btn-switch" (click)="onBtnSwitchType()">
<fa-icon icon="cog"></fa-icon>
<i nz-icon nzType="setting" nzTheme="outline"></i>
</div>
</ng-template>
</div>
<div class="p-progress ml-2" (click)="onBtnPlay()" *ngIf="audioUrl || audioBlob">
<nz-progress [nzPercent]="percent" [nzWidth]="30" [nzFormat]="progressText"
nzType="circle"></nz-progress>
<div class="p-btn-play" [style.left]="isPlaying?'8px':''">
<fa-icon [icon]="playIcon"></fa-icon>
<div
class="p-progress ml-2"
(click)="onBtnPlay()"
*ngIf="audioUrl || audioBlob"
>
<nz-progress
[nzPercent]="percent"
[nzWidth]="30"
[nzFormat]="progressText"
nzType="circle"
></nz-progress>
<div class="p-btn-play" [style.left]="isPlaying ? '8px' : ''">
<i nz-icon nzType="caret-right" nzTheme="outline"></i>
</div>
</div>
</div>
.d-flex{
display: flex;
}
.p-btn-record {
font-size: 0.9rem;
color: #555;
......@@ -91,6 +88,7 @@
.p-progress {
margin-top: 2px;
margin-left: 5px;
position: relative;
line-height: 26px;
.p-btn-play {
......@@ -105,3 +103,6 @@
line-height: 33px;
}
.d-flex{
display: flex;
}
\ No newline at end of file
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, NgZone, OnChanges} from '@angular/core';
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, NgZone, OnChanges} from '@angular/core';
import {NzMessageService, NzNotificationService, UploadFile} from 'ng-zorro-antd';
import {HttpClient, HttpEvent, HttpEventType, HttpRequest} from '@angular/common/http';
import {environment} from '../../../environments/environment';
import {environment} from '../../../environments/environment';
declare var Recorder;
......@@ -9,7 +9,7 @@ declare var Recorder;
selector: 'app-audio-recorder',
templateUrl: './audio-recorder.component.html',
styleUrls: ['./audio-recorder.component.scss']
})
})
export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
_audioUrl: string;
audio = new Audio();
......@@ -19,11 +19,10 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
isUploading = false;
type = Type.UPLOAD; // record | upload
Type = Type;
@Input()
withRmBtn = false;
uploadUrl;
uploadData;
uploadUrl = (<any>window).courseware.uploadUrl();
uploadData = (<any>window).courseware.uploadData();
@Input()
needRemove = false;
......@@ -33,7 +32,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
@Input()
set audioUrl(url) {
this._audioUrl = url;
this._audioUrl = url
if (url) {
this.audio.src = this._audioUrl;
this.audio.load();
......@@ -53,15 +52,9 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
recorder: any;
audioBlob: any;
constructor( private nzMessageService: NzMessageService ) {
constructor( private nzMessageService: NzMessageService ) {
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
}
init() {
......@@ -144,14 +137,13 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
}
// 开始录音
onBtnRecord = () => {
onBtnRecord = () => {
}
// 切换模式
onBtnSwitchType() {
onBtnSwitchType() {
}
onBtnClearAudio() {
this.audioUrl = null;
this.audioRemoved.emit();
}
......@@ -160,7 +152,7 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
this.audioRemoved.emit();
}
handleChange(info: { type: string, file: UploadFile, event: any }): void {
handleChange(info: { type: string, file: UploadFile, event: any }): void {
switch (info.type) {
case 'start':
this.isUploading = true;
......@@ -193,19 +185,22 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
}
return true;
}
beforeUpload = (file: File) => {
beforeUpload = (file: File) => {
this.audioUrl = null;
if (!this.checkSelectFile(file)) {
return false;
}
this.isUploading = true;
this.progress = 0;
}
uploadSuccess = (url) => {
this.nzMessageService.info('Upload Success');
this.isUploading = false;
this.audioUrl = url;
this.progress = 0;
}
uploadSuccess = (url) => {
this.nzMessageService.info('Upload Success');
this.isUploading = false;
if(typeof url == "string"){
this.audioUrl = url
}else{
this.audioUrl = url.url
}
}
uploadFailure = (err, file) => {
this.isUploading = false;
......
This diff is collapsed.
<div class="p-image-children-editor">
<div style="float: left; width: 60%;border-right: 2px solid #ddd; border-bottom: 2px solid #ddd;">
<h5 style="margin-left: 2.5%;">预览:</h5>
<div id="canvas-container" style="margin:5px;">
<div class="preview-box" #wrap style="margin-bottom: 20px">
<canvas id="canvas" #canvas></canvas>
</div>
</div>
<h5 style="margin-left: 2.5%;"> preview: </h5>
<div class="preview-box" #wrap>
<canvas id="canvas" #canvas></canvas>
</div>
<div nz-row nzType="flex" nzAlign="middle">
<div nz-col nzSpan="5" nzOffset="1">
<h5> add background: </h5>
<div style="text-align: center">
<button nz-button nzType="primary" [nzSize]="'small'" nzShape="round" (click)="saveClick()"
[disabled]="!hotZoneChanged">
<i nz-icon type="save"></i>
Save
</button>
</div>
<h5 style="border-top: 2px solid #ddd;">背景图:</h5>
<div style="width: 200px;margin: auto;">
<div class="bg-box">
<app-upload-image-with-preview
[picUrl]="bgItem?.url"
(imageUploaded)="onBackgroundUploadSuccess($event)">
<app-upload-image-with-preview [picUrl]="bgItem.url" (imageUploaded)="onBackgroundUploadSuccess($event)">
</app-upload-image-with-preview>
</div>
</div>
<div nz-col nzSpan="5" nzOffset="1" class="img-box"
*ngFor="let it of hotZoneArr; let i = index" >
<div style=" height: 40px;">
<h5> item-{{i+1}}
<i style="margin-left: 20px; margin-top: 2px; float: right; cursor:pointer" (click)="deleteItem($event, i)"
nz-icon [nzTheme]="'twotone'" [nzType]="'close-circle'" [nzTwotoneColor]="'#ff0000'"></i>
</h5>
</div>
<div style="float: left; width: 40%">
<div nzAlign="middle">
<div class="img-box clearfix" style="border-bottom: 1px solid #DDD; padding-bottom: 10px;">
<div style="float: left;">
<h3>音频素材</h3>
<app-audio-recorder id="index_audio_1" [audioUrl]="bgItem.audio_url" (audioUploaded)="onAudioUploadSuccess($event, bgItem)" > </app-audio-recorder>
</div>
</div>
<!--<div class="img-box-upload">-->
<!--<app-upload-image-with-preview-->
<!--[picUrl]="it.pic_url"-->
<!--(imageUploaded)="onImgUploadSuccessByImg($event, it)">-->
<!--</app-upload-image-with-preview>-->
<!--</div>-->
<!--<app-audio-recorder-->
<!--[audioUrl]="it.audio_url ? it.audio_url : null "-->
<!--(audioUploaded)="onAudioUploadSuccessByImg($event, it)"-->
<!--&gt;</app-audio-recorder>-->
</div>
<div nz-col nzSpan="5" nzOffset="1">
<div class="bg-box">
<button nz-button nzType="dashed" (click)="addBtnClick()"
class="add-btn">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
<!--Add Image-->
Add hot zone
</button>
<div nzAlign="middle">
<div class="img-box clearfix" *ngFor="let it of hotZoneArr; let i = index" style="border-bottom: 1px solid #DDD; padding-bottom: 10px;">
<div style="float: left; height: 115px; position: relative; width: 250px; ">
<h3> 选区-{{ i + 1 }}</h3>
<div style="display: flex; ">
<div style="flex:2">
显示选项
</div>
<div style="flex:4">
<nz-radio-group [(ngModel)]="it.type" (ngModelChange)="autoSave()">
<label nz-radio [nzValue]="'Text'">文字</label>
<label nz-radio [nzValue]="'Image'">图片</label>
</nz-radio-group>
</div>
</div>
</div>
<div style="float: left; position: relative; width: 250px; ">
<div *ngIf="it.type=='Text'" style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
文字
</div>
<div style="flex:5">
<input type="text" nz-input placeholder="" [(ngModel)]="it.text" (blur)="refreshHotZoneText(it);autoSave()" />
</div>
</div>
<div *ngIf="it.type=='Image'" style="display: flex; margin-bottom: 10px;">
<div style="flex:1">
图片
</div>
<div style="flex:5">
<div style="width:200px">
<app-upload-image-with-preview [picUrl]="it.image_url"
(imageUploaded)="onImgUploadSuccessByImg($event, it)"></app-upload-image-with-preview>
</div>
</div>
</div>
</div>
<div style="float: right; display: flex; padding-left: 10px;">
<button style="flex:1; margin-bottom: 5px;" nz-button nzType="dashed" (click)="handleMoveItemUp($event, i)"
[disabled]="i==0">
<i nz-icon nzType="up" nzTheme="outline"></i>
上移
</button>
<button style="flex:1; margin-bottom: 5px;" nz-button nzType="dashed" (click)="handleMoveItemDown($event, i)"
[disabled]="i==hotZoneArr.length-1">
<i nz-icon nzType="down" nzTheme="outline"></i>
下移
</button>
<button style="flex:1; margin-bottom: 5px;" nz-button nzType="danger" (click)="deleteItem($event, i)">
<i nz-icon nzType="delete" nzTheme="outline"></i>
删除
</button>
</div>
</div>
<div nz-col nzSpan="5" nzOffset="1">
<div class="bg-box">
<button nz-button nzType="dashed" (click)="addBtnClick()" class="add-btn">
<i nz-icon nzType="plus-circle" nzTheme="outline"></i>
Add hot zone
</button>
</div>
</div>
</div>
</div>
<nz-divider></nz-divider>
<div class="save-box">
<button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
(click)="saveClick()">
<i nz-icon nzType="save"></i>
Save
</button>
</div>
</div>
</div>
\ No newline at end of file
.p-image-children-editor {
width: 100%;
height: 100%;
border-radius: 0.5rem;
min-width: 1200px;
border: 2px solid #ddd;
.preview-box {
margin: auto;
width: 95%;
height: 35vw;
width: 100%;
// height: 35vw;
border: 2px dashed #ddd;
border-radius: 0.5rem;
background-color: #fafafa;
text-align: center;
color: #aaa;
.preview-img {
height: 100%;
width: auto;
}
}
.bg-box{
//width: 100%;
.bg-box {
margin-bottom: 1rem;
}
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.img-box {
margin-bottom: 1rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 1rem;
}
.img-box-upload{
width: 80%;
.img-box-upload {
width: 120px;
height: 80px;
}
.add-btn {
margin-top: 1rem;
width: 200px;
height: 90px;
display: flex;
align-items: center;
justify-content: center;
}
}
.save-box {
width: 100%;
......@@ -85,21 +72,6 @@ h5 {
margin-top: 1rem;
}
//@import '../../../style/common_mixin';
//
//.p-image-uploader {
......
<div class="title-config">
<div class="title-wrap">
<div class="row" style="margin: 5px;">
<div class="row" style="margin: 5px;">
<div class="p-content" style="width:100%">
<div class="p-tool-box d-flex" style="background: #fff;">
<div class="p-tool-box d-flex" style="background: #fff;">
<nz-select class="ml-1" style="width: 120px;" [(ngModel)]="__fontFamily"
(ngModelChange)="onChangeFontFamily($event)"
nzPlaceHolder="Font Family"
[nzDropdownMatchSelectWidth]="false">
<nz-option [nzValue]="font" nzCustomContent [nzLabel]="font" *ngFor="let font of fontFamilyList">
<span [ngStyle]="{'font-family': font}" >{{font}}</span>
</nz-option>
</nz-select>
[nzDropdownMatchSelectWidth]="false"
>
<nz-option [nzValue]="font" [nzLabel]="font" *ngFor="let font of fontFamilyList"></nz-option>
</nz-select>
<nz-select class="ml-1" style="width: 110px;" [(ngModel)]="__fontSize"
(ngModelChange)="onChangeFontSize()"
nzPlaceHolder="Font Size">
<nz-option [nzValue]="i" [nzLabel]="'Size - ' + i" *ngFor="let i of fontSizeRange"></nz-option>
</nz-select>
<div class="p-divider"></div>
</nz-select>
<div class="p-divider"></div>
<div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeBold()">
<!-- <div class="fa fa-bold"></div>-->
<fa-icon icon="bold"></fa-icon>
<div class="fa fa-bold"></div>
</div>
</div>
<div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeItalic()">
<!-- <div class="fa fa-italic"></div>-->
<fa-icon icon="italic"></fa-icon>
<div class="fa fa-italic"></div>
</div>
</div>
<div class="i-tool-font-btn d-flex mr-2">
<div class="position-relative fa-icon" (click)="onChangeUnderline()">
<!-- <div class="fa fa-underline"></div>-->
<fa-icon icon="underline"></fa-icon>
<div class="fa fa-underline"></div>
</div>
</div>
<div class="i-tool-font-btn d-flex">
<div class="position-relative fa-icon" (click)="onChangeStrikethrough()">
<!-- <div class="fa fa-strikethrough"></div>-->
<fa-icon icon="strikethrough"></fa-icon>
<div class="fa fa-strikethrough"></div>
</div>
</div>
</div>
<div class="p-divider"></div>
<div class="i-tool-font-color d-flex">
<div class="position-relative i-left flex-fill" (click)="onChangeFontColor($event)">
<!-- <div class="fa fa-font"></div>-->
<fa-icon icon="palette"></fa-icon>
<div class="fa fa-font"></div>
<div class="i-color" [style.background-color]="__fontColor"></div>
</div>
<div class="i-dropdown-menu" nzPlacement="bottom"
nz-popover [(nzVisible)]="isShowFontColorPane" nzTrigger="click"
[nzContent]="colorPane">
[nzContent]="colorPane">
<i nz-icon type="down" theme="outline"></i>
</div>
</div>
<div class="p-divider"></div>
<div style="background: #fff;display: block;">
<div class="position-relative">
<div class="position-relative" (click)="onChangeStrikethrough()">
<app-audio-recorder [audioUrl]="titleObj && titleObj.audio_url" (audioUploaded)="titleAudioUploaded($event)"></app-audio-recorder>
</div>
</div>
</div>
</div>
<div class="width-100 d-flex">
<iframe #titleEl id="titleContentEgret" frameborder="0" style="overflow: hidden;width: 100%; height:48px; margin: 0; padding: 0;"></iframe>
<div class="width-100 d-flex">
<iframe #titleEl frameborder="0" style="overflow: hidden;width: 100%; height:48px; margin: 0; padding: 0;"></iframe>
</div>
</div>
......@@ -83,11 +76,37 @@
<ng-container *ngIf="withIcon">
<div class="row type-row">
课程类型:
<nz-radio-group [(ngModel)]="titleObj && titleObj.type" (ngModelChange)="typeChange($event)">
<label nz-radio nzValue="a">单数课</label>
<label nz-radio nzValue="b">双数课</label>
<label nz-radio nzValue="c">复习课</label>
</nz-radio-group>
</div>
</ng-container>
</div>
<ng-container *ngIf="withIcon">
<div class="title-icons">
<div class="icons-list">
<nz-checkbox-wrapper style="width: 100%;clear:both" (nzOnChange)="iconsChanges($event)">
<div [class]="'icon-item icon-'+i" *ngFor="let i of groupIconsCount[titleObj.type];">
<div class="img-box">
<nz-badge class="icon-badge" [nzCount]="titleObj && titleObj.icons && titleObj.icons.indexOf(i) + 1">
<img [src]="'assets/title-icons/'+titleObj.type+'/icon-'+i+'.png'" alt="">
</nz-badge>
</div>
<label nz-checkbox [nzValue]="i" [ngModel]="titleObj && titleObj.icons && titleObj.icons.indexOf(i) > -1"></label>
</div>
</nz-checkbox-wrapper>
</div>
</div>
</ng-container>
</div>
@import '../../style/common_mixin.css';
@import '../../style/common_mixin';
.title-config {
.letter-wrap{
......@@ -11,73 +11,39 @@
.type-row{
margin: 0;padding-top: 1rem;
}
.icon-item{
margin-right: 16px;
float: left;
width: 45px;
height: 75px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.icon-badge{
position: absolute;
top: 0;
right: 0;
}
.img-box{
top: 0;
position: absolute;
width: 45px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
img{
max-width: 100%;
}
}
label{
position: absolute;
bottom: 0;
}
}
}
}
@font-face
{
font-family: 'BRLNSDB';
src: url("../../../assets/font/BRLNSDB.TTF") ;
}
@font-face
{
font-family: 'BRLNSB';
src: url("../../../assets/font/BRLNSB.TTF") ;
}
@font-face
{
font-family: 'BRLNSR';
src: url("../../../assets/font/BRLNSR.TTF") ;
}
@font-face
{
font-family: 'GOTHIC';
src: url("../../../assets/font/GOTHIC.TTF") ;
}
@font-face
{
font-family: 'GOTHICB';
src: url("../../../assets/font/GOTHICB.TTF") ;
}
@font-face
{
font-family: 'GOTHICBI';
src: url("../../../assets/font/GOTHICBI.TTF") ;
}
@font-face
{
font-family: 'GOTHICI';
src: url("../../../assets/font/GOTHICI.TTF") ;
}
@font-face
{
font-family: 'MMTextBook';
src: url("../../../assets/font/MMTextBook.otf") ;
}
@font-face
{
font-family: 'MMTextBook-Bold';
src: url("../../../assets/font/MMTextBook-Bold.otf") ;
}
@font-face
{
font-family: 'MMTextBook-BoldItalic';
src: url("../../../assets/font/MMTextBook-BoldItalic.otf") ;
}
@font-face
{
font-family: 'MMTextBook-Italic';
src: url("../../../assets/font/MMTextBook-Italic.otf") ;
}
@mixin tool-btn {
border: 1px solid #ddd;
display: flex;
......@@ -88,37 +54,16 @@
border-radius: 6px;
color: #555;
}
.d-flex{
display: flex;
}
.position-relative {
position: relative;
}
.flex-fill {
-webkit-box-flex: 1;
flex: 1 1 auto;
justify-content: center;
display: flex;
}
.i-dropdown-menu{
width: 15px;
font-size: 10px;
border-left: 1px solid #ddd;
display: -webkit-box;
display: flex;
-webkit-box-align: center;
align-items: center;
flex: 0
}
.p-title-box .p-title {
.p-title-box {
.p-title {
font-size: 20px;
}
.p-title-box input {
input {
width: 300px;
margin-left: 10px;
}
}
.p-content {
border: 1px solid #ddd;
......@@ -140,110 +85,115 @@
align-items: center;
border-bottom: 1px solid #ddd;
// save
.i-tool-save {
@include tool-btn();
color: white;
}
.i-tool-save:disabled {
color: #555;
}
// font-size
.i-tool-font-size {
@include tool-btn();
width: 37px;
}
// save
.i-tool-save {
//@include tool-btn();
color: white;
}
.i-tool-save:disabled {
color: #555;
}
// font-size
.i-tool-font-size {
//@include tool-btn();
width: 37px;
}
.i-tool-font-size:hover {
color: black;
border-color: #bbb;
}
// font-color
.i-tool-font-color, .i-tool-font-btn {
border: 1px solid #ddd;
//padding: 3px 7px;
border-radius: 6px;
width: 45px;
height: 31px;
background-color: white;
color: #555;
::ng-deep > span {
display: flex;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
padding: 3px 7px;
}
.i-left {
.fa-font,.fa-bold,.fa-italic,.fa-strikethrough, .fa-underline {
font-size: 10px;
& > span {
position: absolute;
color: #555;
left: 8px;
top: 7px;
top: -5px;
right: 5px;
}
.i-color {
width: 68%;
height: 5px;
background-color: black;
}
.i-tool-font-size:hover {
color: black;
border-color: #bbb;
}
// font-color
.i-tool-font-color, .i-tool-font-btn {
border: 1px solid #ddd;
//padding: 3px 7px;
border-radius: 6px;
width: 45px;
height: 31px;
background-color: white;
color: #555;
::ng-deep > span {
display: flex;
position: absolute;
top: 21px;
left: 5px;
left: 0;
right: 0;
top: 0;
bottom: 0;
padding: 3px 7px;
}
}
.i-dropdown-menu {
width: 15px;
font-size: 10px;
border-left: 1px solid #ddd;
display: flex;
align-items: center;
.anticon-down {
transform: scale(0.6);
.i-left {
.fa-font,.fa-bold,.fa-italic,.fa-strikethrough, .fa-underline {
font-size: 10px;
position: absolute;
color: #555;
left: 8px;
top: 7px;
}
.i-color {
width: 68%;
height: 5px;
background-color: black;
position: absolute;
top: 21px;
left: 5px;
}
}
.i-dropdown-menu {
width: 15px;
font-size: 10px;
border-left: 1px solid #ddd;
display: flex;
align-items: center;
.anticon-down {
transform: scale(0.6);
}
}
}
}
.i-tool-font-btn{
width: 31px;
}
.fa-icon{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
// bg-color
.i-tool-bg-color {
@include tool-btn();
padding: 0 9px;
::ng-deep > span {
.i-tool-font-btn{
width: 31px;
}
.fa-icon{
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
// bg-color
.i-tool-bg-color {
@include tool-btn();
padding: 0 9px;
::ng-deep > span {
display: flex;
align-items: center;
}
.i-color {
display: block;
width: 16px;
height: 16px;
background-color: white;
margin-left: 10px;
.i-color {
display: block;
width: 16px;
height: 16px;
background-color: white;
margin-left: 10px;
}
}
}
// horizontal-center
.i-tool-horizontal-center {
@include tool-btn();
width: 37px;
// horizontal-center
.i-tool-horizontal-center {
@include tool-btn();
width: 37px;
}
}
.p-box {
width: 1280px;
height: 720px;
......@@ -253,7 +203,9 @@
overflow: hidden;
}
.p-sentence {
@include k-no-select();
}
.p-animation-index-box {
.i-animation-index {
......@@ -326,6 +278,7 @@
::ng-deep .ant-radio-button-wrapper {
padding: 0 10px;
@include k-no-select();
}
.i-toolbox {
......@@ -356,6 +309,7 @@
cursor: pointer;
text-align: left;
display: flex;
@include k-no-select();
}
.i-active {
background-color: antiquewhite;
......
import {
Component,
ElementRef,
......@@ -11,82 +10,6 @@ import {
ViewChild
} from '@angular/core';
const editorTpl = `<html lang="en"><head><meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
<style>
@font-face{
font-family: 'BRLNSDB';
src: url("../../../assets/font/BRLNSDB.TTF") ;
}
@font-face{
font-family: 'BRLNSB';
src: url("../../../assets/font/BRLNSB.TTF") ;
}
@font-face{
font-family: 'BRLNSR';
src: url("../../../assets/font/BRLNSR.TTF") ;
}
@font-face{
font-family: 'GOTHIC';
src: url("../../../assets/font/GOTHIC.TTF") ;
}
@font-face{
font-family: 'GOTHICB';
src: url("../../../assets/font/GOTHICB.TTF") ;
}
@font-face{
font-family: 'GOTHICBI';
src: url("../../../assets/font/GOTHICBI.TTF") ;
}
@font-face{
font-family: 'GOTHICI';
src: url("../../../assets/font/GOTHICI.TTF") ;
}
@font-face{
font-family: 'MMTextBook';
src: url("../../../assets/font/MMTextBook.otf") ;
}
@font-face{
font-family: 'MMTextBook-Bold';
src: url("../../../assets/font/MMTextBook-Bold.otf") ;
}
@font-face{
font-family: 'MMTextBook-BoldItalic';
src: url("../../../assets/font/MMTextBook-BoldItalic.otf") ;
}
@font-face{
font-family: 'MMTextBook-Italic';
src: url("../../../assets/font/MMTextBook-Italic.otf") ;
}
html, body{
/*font-size: 30px;*/
}
body{
height:48px;
overflow: hidden;
margin: 0;
padding: 0 .5rem;
font-family: 'BRLNSB, BRLNSDB, BRLNSR, GOTHIC, GOTHICB, MMTextBook';
background: #FFF;
line-height: 48px;
}
</style>
</head>
<body>{{content}}</body>
</html>`;
@Component({
selector: 'app-lesson-title-config',
templateUrl: './lesson-title-config.component.html',
......@@ -96,45 +19,22 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
fontFamilyList = [
'Arial',
'BRLNSB',
'BRLNSDB',
'BRLNSR',
'GOTHIC',
'GOTHICB',
// "GOTHICBI",
// "GOTHICI",
'MMTextBook',
// "MMTextBook-Bold",
// "MMTextBook-Italic",
// "MMTextBook-BoldItalic",
'ARBLI'
];
colorList = [
'#000000',
'#111111',
'#ffffff',
'#595959',
'#0075c2',
'#c61c1e',
'#9cbc3a',
'#008000',
'#FF0000',
'#D2691E',
'#9cbc3a'
];
MIN_FONT_SIZE = 1;
MAX_FONT_SIZE = 7;
isShowFontColorPane = false;
isShowBGColorPane = false;
fontSizeRange = [
// {name: '1号', value: 9},
// {name: '2号', value: 13},
// {name: '3号', value: 16},
// {name: '4号', value: 18},
// {name: '5号', value: 24},
// {name: '6号', value: 32},
];
fontSizeRange: number[];
editorContent = '';
......@@ -145,17 +45,25 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
loopCnt = 0;
maxLoops = 20;
@ViewChild('titleEl', {static: true}) titleEl: ElementRef;
groupIconsCount = {
a: Array.from(Array(11).keys()),
b: Array.from(Array(8).keys()),
c: Array.from(Array(8).keys()),
};
prevIcons = [];
prevType = '';
@ViewChild('titleEl') titleEl: ElementRef;
titleEW = null;
@Input()
titleObj = {
type: 'a',
content: '',
icons: [],
audio_url: ''
};
@Input()
withIcon = true;
@Output()
titleUpdated = new EventEmitter();
......@@ -176,12 +84,16 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
let defObj = this.titleObj;
if (!vars.titleObj.currentValue) {
defObj = {
type: 'a',
content: '',
icons: [],
audio_url: ''
};
} else {
defObj = vars.titleObj.currentValue;
}
this.titleObj.icons = defObj.icons || [];
this.titleObj.type = defObj.type || 'a';
this.titleObj.content = defObj.content || '';
this.titleObj.audio_url = defObj.audio_url || '';
this.titleEW.document.body.innerHTML = this.titleObj.content;
......@@ -190,23 +102,33 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
ngOnInit() {
if (!this.titleObj) {
this.titleObj = {
type: 'a',
content: '',
icons: [],
audio_url: ''
};
}
this.titleObj.icons = this.titleObj.icons || [];
this.titleObj.type = this.titleObj.type || 'a';
this.titleObj.content = this.titleObj.content || '';
this.titleObj.audio_url = this.titleObj.audio_url || '';
this.editorContent = editorTpl.replace('{{content}}', this.titleObj.content) ;
this.editorContent = `<html lang="en"><head><meta charset="utf-8">
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
</head>
<body style="height:48px;overflow: hidden;margin: 0;padding: 0 .5rem;background: #FFF;line-height: 48px;">
${this.titleObj.content}
</body>
</html>`;
this.titleEW = this.titleEl.nativeElement.contentWindow;
console.log('this.titleEW', this.titleEW);
const tdoc = this.titleEW.document;
tdoc.designMode = 'on';
tdoc.designMode = "on";
tdoc.open('text/html', 'replace');
tdoc.write(this.editorContent);
tdoc.close();
tdoc.addEventListener('keypress', this.keyPress, true);
tdoc.addEventListener('blur', () => {
tdoc.addEventListener("keypress", this.keyPress, true);
tdoc.addEventListener("blur", () => {
if (this.titleObj.content === this.titleEW.document.body.innerHTML.trim()) {
return;
}
......@@ -237,7 +159,30 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
ngOnDestroy(): void {
}
iconsChanges(val) {
let a = this.titleObj.icons;
let b = val;
if (a.length > b.length) {
const diff = a.filter(x => !b.includes(x));
const ti = [...this.titleObj.icons];
for (let i = 0; i < diff.length; i++) {
const d = diff[i];
const idx = ti.indexOf(d);
ti.splice(idx, 1);
}
this.titleObj.icons = ti;
} else {
const diff = b.filter(x => !a.includes(x));
this.titleObj.icons = [...this.titleObj.icons, ...diff];
}
this.shouldSave();
}
typeChange(val) {
this.titleObj.icons = [];
this.shouldSave();
}
keyPress(evt) {
try {
......@@ -250,9 +195,9 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
const key = String.fromCharCode(evt.charCode).toLowerCase();
let cmd = '';
switch (key) {
case 'b': cmd = 'bold'; break;
case 'i': cmd = 'italic'; break;
case 'u': cmd = 'underline'; break;
case 'b': cmd = "bold"; break;
case 'i': cmd = "italic"; break;
case 'u': cmd = "underline"; break;
}
......@@ -269,13 +214,10 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
alert(e);
}
}
execEditorCommand(command, option?: any) {
console.log('sssss');
try {
this.titleEW.focus();
const result = this.titleEW.document.execCommand(command, false, option);
console.log(result);
this.titleEW.document.execCommand(command, false, option);
this.loopCnt = 0;
return false;
......@@ -287,7 +229,7 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
}, 100);
this.loopCnt += 1;
} else {
alert('Error executing command.');
alert("Error executing command.");
}
}
}
......@@ -300,7 +242,7 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
this.execEditorCommand('forecolor', this.__fontColor);
}
onChangeFontFamily(font) {
this.execEditorCommand('fontName', font);
this.execEditorCommand('fontname', font);
}
onChangeFontSize(size?: any) {
......@@ -330,10 +272,9 @@ export class LessonTitleConfigComponent implements OnInit, OnChanges, OnDestroy,
this.titleUpdated.emit(this.titleObj);
}
shouldSave = () => {
console.log('title shouldSave', this.titleObj);
console.log('title shouldSave');
this.titleObj.content = this.titleEW.document.body.innerHTML.trim();
this.titleUpdated.emit(this.titleObj);
}
}
@import '../../style/common_mixin.css';
@import '../../style/common_mixin';
.cmp-player-content-wrapper{
max-height: 100%;
......
import {
AfterViewInit,
Component,
ElementRef,
ElementRef,
Input,
OnChanges,
OnDestroy,
OnInit,
OnInit,
ViewChild
} from '@angular/core';
} from '@angular/core';
@Component({
......@@ -18,21 +18,21 @@ import {
export class PlayerContentWrapperComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
@ViewChild('wrapperEl', {static: true }) wrapperEl: ElementRef;
@ViewChild('wrapperEl') wrapperEl: ElementRef;
// // aspect ratio?
@Input() ratio;
_w: string;
_h: string;
constructor() {
constructor() {
if (window.innerHeight < window.innerWidth) {
this._h = '100%';
this._w = 'auto';
} else {
this._w = '100%';
this._h = 'auto';
}
}
}
ngOnInit() {
if (!this.ratio) {
......
<div class="position-relative">
<nz-upload class="p-image-uploader" [nzDisabled]="disableUpload"
[nzShowUploadList]="false"
nzAccept = "image/*"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="handleChange($event)">
<!--[nzBeforeUpload]="customUpload">-->
[nzShowUploadList]="false"
nzAccept = "image/*"
[nzAction]="uploadUrl"
[nzData]="uploadData"
(nzChange)="handleChange($event)">
<div class="p-box d-flex align-items-center">
<div class="p-upload-icon" *ngIf="!picUrl && !uploading">
<i nz-icon nzType="cloud-upload" nzTheme="outline" [style.font-size]="iconSize + 'em'"></i>
<i nz-icon type="cloud-upload" theme="outline" [style.font-size]="iconSize + 'em'"></i>
<div class="m-3"></div>
<span>{{TIP}}</span>
<!--<div class="mt-5 p-progress-bar" *ngIf="uploading">-->
<!--<div class="p-progress-bg" [style.width]="progress*0.2+'rem'"></div>-->
<!--<div class="p-progress-value">{{progress}}%</div>-->
<!--</div>-->
</div>
<div class="p-upload-progress-bg" *ngIf="uploading">
<div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon>
<fa name="cloud-upload"></fa>
Uploading...
</div>
</div>
<div class="p-preview" *ngIf="!uploading && picUrl "
[style.background-image]="picUrl | backgroundImage ">
[style.background-image]="picUrl | backgroundImage ">
</div>
</div>
</nz-upload>
<div class="p-btn-delete" *ngIf="canDelete"
nz-popconfirm nzTitle="Are you sure ?"
(nzOnConfirm)="onDelete()"
nz-popconfirm nzTitle="Are you sure ?"
(nzOnConfirm)="onDelete()"
>
<i nz-icon nzType="close" nzTheme="outline"></i>
<i nz-icon type="close" theme="outline"></i>
</div>
</div>
@import '../../style/common_mixin.css';
@import '../../style/common_mixin';
.p-image-uploader {
position: relative;
......@@ -52,15 +52,10 @@
.p-preview {
width: 100%;
height: 100%;
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
@include k-img-bg();
}
}
.d-flex{
display: flex;
}
}
.p-btn-delete {
......
......@@ -29,19 +29,10 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
@Input()
disableUpload = false;
uploadUrl;
uploadData;
uploadUrl = (<any> window).courseware.uploadUrl();
uploadData = (<any> window).courseware.uploadData();
constructor(private nzMessageService: NzMessageService) {
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
}
ngOnChanges() {
if (!this.picItem) {
......@@ -54,9 +45,6 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
handleChange(info: { type: string, file: UploadFile, event: any }): void {
console.log('info:' , info);
switch (info.type) {
case 'start':
// this.isUploading = true;
......
<div class="p-video-box">
<div class="up-video" style="display: flex;">
<!--<nz-upload class="" [nzDisabled]="!showUploadBtn"-->
<!--[nzShowUploadList]="false"-->
......@@ -16,7 +16,8 @@
<button type="button" nz-button nzType="default" *ngIf="showUploadBtn" [disabled]="uploading"
[nzLoading]="uploading" >
<i nz-icon nzType="plus" nzTheme="outline"></i>
<i nz-icon type="plus" theme="outline"></i>
<span>{{ uploading ? 'Uploading' : 'Select Video' }}</span>
<!--<span>Select Video</span>-->
</button>
......@@ -55,9 +56,9 @@
</div>
<div class="p-box d-flex align-items-center p-video-uploader">
<div class="p-box d-flex align-items-center p-video-uploader" style="top: 20px;">
<div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
<i nz-icon nzType="upload" nzTheme="outline"></i>
<i nz-icon type="upload" theme="outline"></i>
<div class="m-3"></div>
<span>Click here to upload video</span>
<div class="mt-5 p-progress-bar" *ngIf="uploading">
......@@ -69,26 +70,26 @@
[ngClass]="{'smart-bar': showUploadBtn}" >
<div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon>
<fa name="cloud-upload"></fa>
Uploading...
</div>
</div>
<div class="p-upload-check-bg" *ngIf="checking">
<div class="i-bg" [style.width]="progress+'%'"></div>
<div class="i-text">
<fa-icon icon="cloud-upload-alt"></fa-icon>
<i nz-icon nzType="loading" nzTheme="outline"></i>Checking...
<fa name="cloud-upload"></fa>
<i nz-icon type="loading" theme="outline"></i>Checking...
</div>
</div>
<div class="p-preview" *ngIf="!uploading && videoUrl " >
<div class="p-preview" *ngIf="!showUploadBtn && !uploading && videoUrl " >
<!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>-->
<video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video>
<video [src]="safeVideoUrl(videoUrl)" controls #videoNode (loadedmetadata)="videoLoadedMetaData()"></video>
</div>
</div>
<div [style.display]="!checkVideoExists?'none':''">
<span><i nz-icon nzType="loading" nzTheme="outline"></i> checking file to upload</span>
</div>
<span><i nz-icon type="loading" theme="outline"></i> checking file to upload</span>
</div>
@import '../../style/common_mixin.css';
/*.p-video-box{
bottom: 0;
border: 2px dashed #ddd;
border-radius: 0.5rem;
background-color: #fafafa;
text-align: center;
color: #aaa;
padding-top: 56.25%;
//font-size: 4rem;
position: relative;
.p-upload-icon{
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50% ,-50%);
}
}*/
@import '../../style/common_mixin';
.p-video-uploader {
position: relative;
display: block;
......@@ -33,56 +17,50 @@
background-color: #fafafa;
text-align: center;
color: #aaa;
.p-upload-icon {
text-align: center;
margin: auto;
.anticon-upload {
color: #888;
font-size: 5rem;
}
.p-progress-bar {
position: relative;
width: 20rem;
height: 1.5rem;
border: 1px solid #ccc;
border-radius: 1rem;
.p-progress-bg {
background-color: #1890ff;
border-radius: 1rem;
height: 100%;
}
.p-progress-value {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-shadow: 0 0 4px #000;
color: white;
text-align: center;
font-size: 0.9rem;
line-height: 1.5rem;
}
}
}
.p-preview {
width: 100%;
height: 100%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
video{
max-height: 100%;
max-width: 100%;
}
}
}
}
.p-upload-icon {
text-align: center;
margin: auto;
}
.p-upload-icon .anticon-upload {
color: #888;
font-size: 5rem;
}
p-progress-bar {
position: relative;
width: 20rem;
height: 1.5rem;
border: 1px solid #ccc;
border-radius: 1rem;
}
.p-progress-bar .p-progress-bg {
background-color: #1890ff;
border-radius: 1rem;
height: 100%;
}
.p-progress-bar .p-progress-value {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-shadow: 0 0 4px #000;
color: white;
text-align: center;
font-size: 0.9rem;
line-height: 1.5rem;
}
.p-preview {
width: 100%;
height: 100%;
//background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
}
.p-preview video{
max-height: 100%;
max-width: 100%;
position: absolute;
display: flex;
}
.p-btn-delete {
position: absolute;
right: -0.5rem;
......
import {Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SecurityContext, ViewChild} from '@angular/core';
import {NzMessageService, UploadChangeParam, UploadFile, UploadFileStatus} from 'ng-zorro-antd';
import {NzMessageService, UploadFile} from 'ng-zorro-antd';
import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
......@@ -24,7 +24,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
@Input()
videoUrl = '';
@ViewChild('videoNode', {static: true })
@ViewChild('videoNode')
videoNode: ElementRef;
......@@ -47,8 +47,8 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
item: any;
// videoItem = null;
uploadUrl;
uploadData;
uploadUrl = (<any> window).courseware.uploadUrl();
uploadData = (<any> window).courseware.uploadData();
constructor(private nzMessageService: NzMessageService,
private sanitization: DomSanitizer
......@@ -58,16 +58,6 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
this.uploading = false;
this.videoFile = null;
this.uploadUrl = (<any> window).courseware.uploadUrl();
this.uploadData = (<any> window).courseware.uploadData();
window['air'].getUploadCallback = (url, data) => {
this.uploadUrl = url;
this.uploadData = data;
};
}
ngOnChanges() {
// if (!this.videoFile || this.showUploadBtn) {
......@@ -81,7 +71,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
}
safeVideoUrl(url) {
console.log(url);
console.log(url)
return this.sanitization.bypassSecurityTrustResourceUrl(url); // `${url}`;
}
videoLoadedMetaData() {
......@@ -89,7 +79,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
}
handleChange(info: UploadChangeParam/* { type: string, file: UploadFile, event: any }*/): void {
handleChange(info: { type: string, file: UploadFile, event: any }): void {
console.log('info:' , info);
......@@ -119,7 +109,7 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
break;
case 'progress':
this.progress = info.event.percent;
this.progress = parseInt(info.event.percent, 10);
this.doProgress(this.progress);
break;
}
......@@ -162,9 +152,9 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
if (duration) {
duration = duration * 1000;
}
file.height = height;
file.width = width;
file.duration = duration;
file['height'] = height;
file['width'] = width;
file['duration'] = duration;
vid.preload = 'none';
vid.src = '';
vid.remove();
......
@import '../style/common_mixin.css';
.model-content {
width: 100%;
height: 100%;
}
<div class="model-content">
<div style="position: absolute; left: 200px; top: 100px; width: 800px;">
<input type="text" nz-input [(ngModel)]="item.text" (blur)="save()">
<app-upload-image-with-preview
[picUrl]="item.pic_url"
(imageUploaded)="onImageUploadSuccess($event, 'pic_url')"
></app-upload-image-with-preview>
<app-audio-recorder
[audioUrl]="item.audio_url"
(audioUploaded)="onAudioUploadSuccess($event, 'audio_url')"
></app-audio-recorder>
<app-custom-hot-zone></app-custom-hot-zone>
<app-upload-video></app-upload-video>
<app-lesson-title-config></app-lesson-title-config>
<div class="card-config">
<div class="card-item">
<div class="border">
</div>
</div>
</div>
</div>
\ No newline at end of file
@import "../style/common_mixin";
.model-content {
margin: 10px;
.card-config {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
flex-direction:column;
.card-item{
flex:1;
margin-bottom: 40px;
.border {
border-radius: 20px;
border-style: dashed;
padding:20px;
width: 100%;
}
.card-item-content{
.title {
font-size: 24px;
width: 100%;
text-align: center;
}
.section{
border-top: 1px solid ;
padding: 10px 0;
.section-title{
font-size: 24px;
width: 100%;
}
.section-content{
display: flex;
margin: 5px 0 10px 0;
}
}
.pic-sound-box {
width: 50%;
display: flex;
flex-direction: column;
}
.add-btn-box {
display: flex;
align-items: center;
justify-content: center;
height: 20vw;
padding: 10px;
padding-top: 5vw;
}
}
}
}
}
import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef, ChangeDetectorRef} from '@angular/core';
import {Component, EventEmitter, Input, OnDestroy, OnChanges, OnInit, Output, ApplicationRef} from '@angular/core';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css']
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit, OnChanges, OnDestroy {
// 储存数据用
saveKey = "test_0011";
// 储存对象
item;
export class FormComponent implements OnInit, OnChanges, OnDestroy {
_item: any;
dataArray: Array<Object> = [];
hotZoneItemArr: Array<Object> = [];
bgItem: Object;
constructor(private appRef: ApplicationRef,private changeDetectorRef: ChangeDetectorRef) {
KEY = 'DataKey_PU03';
set item(item) {
this._item = item;
}
get item() {
return this._item;
}
@Output()
update = new EventEmitter();
constructor(private appRef: ApplicationRef) {
ngOnInit() {
}
ngOnInit() {
this.item = {};
// 获取存储的数据
(<any> window).courseware.getData((data) => {
this.item.contentObj = {};
const getData = (<any> window).courseware.getData;
getData((data) => {
// console.log("读取数据", data)
if (data) {
this.item = data;
} else {
this.item = {};
}
if ( !this.item.contentObj ) {
this.item.contentObj = {};
}
this.init();
this.changeDetectorRef.markForCheck();
this.changeDetectorRef.detectChanges();
this.refresh();
}, this.KEY);
}
ngOnChanges() {
}, this.saveKey);
}
ngOnDestroy() {
}
saveData(e){
this.bgItem = e.bgItem;
this.hotZoneItemArr.length = 0;
e.hotZoneItemArr.forEach(item => {
this.hotZoneItemArr.push(item)
});
this.save();
}
ngOnChanges() {
init() {
if (this.item.contentObj.dataArray) {
this.dataArray = this.item.contentObj.dataArray;
} else {
this.dataArray = this.getDefaultPicArr();
this.item.contentObj.dataArray = this.dataArray;
}
if (this.item.contentObj.hotZoneItemArr) {
this.hotZoneItemArr = this.item.contentObj.hotZoneItemArr;
} else {
this.hotZoneItemArr = this.getDefaultPicArr();
this.item.contentObj.hotZoneItemArr = this.hotZoneItemArr;
}
if (this.item.contentObj.bgItem) {
this.bgItem = this.item.contentObj.bgItem;
} else {
this.bgItem = {}
this.item.contentObj.bgItem = this.bgItem;
}
}
ngOnDestroy() {
cardItemData(){
return {
audio_url: "",
image_url: "",
hotZoneIndex: -1
};
}
cardChoiceData(){
return { isText: true, text: "", image_url: "" }
}
getDefaultPicArr() {
let arr = [];
return arr;
}
init() {
initData() {
}
/**
* 储存图片数据
* @param e
*/
onImageUploadSuccess(e, key) {
this.item[key] = e.url;
this.save();
addChoice(questionIndex) {
// let item = this.cardChoiceData();
// this.dataArray[questionIndex].choice.incorrect.push(item);
// this.saveItem();
}
/**
* 储存音频数据
* @param e
*/
onAudioUploadSuccess(e, key) {
onImageUploadSuccessByItem(e, item) {
item.image_url = e.url
this.save();
}
onAudioUploadSuccessByItem(e, item) {
item.audio_url = e.url;
this.save();
}
this.item[key] = e.url;
onTitleAudioUploadSuccess(e) {
this.item.contentObj.titleAudio_url = e.url;
this.save();
}
addItem() {
let item = this.cardItemData();
this.dataArray.push(item);
this.saveItem();
}
radioClick(it, radioValue) {
it.radioValue = radioValue;
this.saveItem();
}
clickCheckBox() {
this.saveItem();
}
saveItem() {
this.save();
}
/**
* 储存数据
*/
save() {
(<any> window).courseware.setData(this.item, null, this.saveKey);
(<any> window).courseware.setData(this.item, null, this.KEY);
this.refresh();
// console.log("保存", this.item)
}
/**
* 刷新 渲染页面
*/
refresh() {
setTimeout(() => {
this.appRef.tick();
}, 1);
}
}
This diff is collapsed.
This diff is collapsed.
.game-container {
width: 100%;
height: 100%;
background: #ffffff;
background-size: cover;
}
#canvas {
}
@font-face
{
font-family: 'BRLNSDB';
src: url("../../assets/font/BRLNSDB.TTF") ;
}
This diff is collapsed.
This diff is collapsed.
const res = [
// ['bg', "assets/play/bg.jpg"],
['btn_left', "assets/play/btn_left.png"],
['btn_right', "assets/play/btn_right.png"],
// ['text_bg', "assets/play/text_bg.png"],
];
const resAudio = [
['click', "assets/play/music/click.mp3"],
];
export {res, resAudio};
const localImages = {
'_bg_1280_720_Ruler': 'assets/play/default/images/1280_720_Ruler.png',
'_mask_layer_1280_720': 'assets/play/default/images/mask_layer_1280_720.png',
'_bg_240_180': 'assets/play/default/images/bg_240_180.png',
'_bg_453_251': 'assets/play/default/images/bg_453_251.png',
'_bg_1280_222': 'assets/play/default/images/bg_1280_222.png',
'_bg_1280_720': 'assets/play/default/images/bg_1280_720.png',
'_bg_200_200': 'assets/play/default/images/bg_200_200.png',
'_bg_30_30': 'assets/play/default/images/bg_30_30.png',
'_bg_500_600': 'assets/play/default/images/bg_500_600.png',
'_bg_50_50': 'assets/play/default/images/bg_50_50.png',
'_bg_75_50': 'assets/play/default/images/bg_75_50.png',
'_bg_75_50_black': 'assets/play/default/images/bg_75_50_black.png',
'_flag': 'assets/play/default/images/flag.png',
'_go': 'assets/play/default/images/go.png',
'_header': 'assets/play/default/images/header.png',
'_ready': 'assets/play/default/images/ready.png',
'_replay': 'assets/play/default/images/replay.png',
'_scrap-pic-1': 'assets/play/default/images/scrap-pic-1.png',
'_scrap-pic-2': 'assets/play/default/images/scrap-pic-2.png',
'_scrap-pic-3': 'assets/play/default/images/scrap-pic-3.png',
'_sm-pic-1': 'assets/play/default/images/sm-pic-1.png',
'_sm-pic-2': 'assets/play/default/images/sm-pic-2.png',
'_sm-pic-3': 'assets/play/default/images/sm-pic-3.png',
'_sm-pic-4': 'assets/play/default/images/sm-pic-4.png',
'_star': 'assets/play/default/images/star.png',
'_bubble': 'assets/play/default/images/bubble.png',
'_image_none': 'assets/play/default/images/image_none.png',
};
const localAudios = {
'sm-back': "assets/play/default/audio/sm-back.mp3",
'sm-display': "assets/play/default/audio/sm-display.mp3",
'sm-wrong': "assets/play/default/audio/sm-wrong.mp3",
'sm-win': "assets/play/default/audio/sm-win.mp3",
'sm-in': "assets/play/default/audio/sm-in.mp3",
'sm-out': "assets/play/default/audio/sm-out.mp3",
'sm-click': "assets/play/default/audio/sm-click.mp3",
'sm-start': "assets/play/default/audio/sm-start.mp3",
'sm-correct': 'assets/play/default/audio/sm-correct.mp3',
'sm-star': "assets/play/default/audio/sm-star.mp3",
'sm-choice-complete': 'assets/play/default/audio/sm-choice-complete.mp3',
'sm-choice-correct': 'assets/play/default/audio/sm-choice-correct.mp3',
'sm-choice-error': 'assets/play/default/audio/sm-choice-error.mp3',
'sm-choice-in': 'assets/play/default/audio/sm-choice-in.mp3',
'sm-choice-show-answer': 'assets/play/default/audio/sm-choice-show-answer.mp3',
'sm-choice-timeup-0': 'assets/play/default/audio/sm-choice-timeup-0.mp3',
'sm-choice-timeup-3': 'assets/play/default/audio/sm-choice-timeup-3.mp3',
'sm-go': 'assets/play/default/audio/sm-go.mp3',
'sm-ready': 'assets/play/default/audio/sm-ready.mp3',
};
export {localImages, localAudios};
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 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 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 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 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 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 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 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 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