Replace all CoffeeScript code by TypeScript

This commit is contained in:
Matthias Schiffer 2016-01-06 17:10:19 +01:00
parent 4fa246628b
commit c64ead08a3
24 changed files with 315 additions and 228 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/build
/node_modules
/typings

View file

@ -4,16 +4,14 @@
"start": "webpack-dev-server -d --content-base static",
"build": "webpack"
},
"devDependencies": {
"coffee-loader": "^0.7.2",
"coffee-script": "^1.10.0",
"dependencies": {
"css-loader": "^0.23.1",
"html-webpack-plugin": "^1.7.0",
"lodash": "^3.10.1",
"style-loader": "^0.13.0",
"ts-loader": "^0.7.2",
"typescript": "^1.7.5",
"webpack": "^1.12.9",
"webpack-dev-server": "^1.14.0"
},
"dependencies": {
"lodash": "^3.10.1"
}
}

5
require.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
declare var require: {
<T>(path: string): T;
(paths: string[], callback: (...modules: any[]) => void): void;
ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void) => void;
};

View file

@ -1,21 +0,0 @@
'use strict'
require './style.css'
window._ = require 'lodash'
MapData = require './model/MapData'
MapContext = require './control/MapContext'
mapContext = null
window.onload = ->
xhr = new XMLHttpRequest()
xhr.onload = ->
mapDef = new MapData(JSON.parse this.responseText)
mapContext = new MapContext mapDef
xhr.open 'GET', 'resources/map/test.json', true
xhr.send()

26
src/app.ts Normal file
View file

@ -0,0 +1,26 @@
'use strict';
require('./style.css');
import * as lodash from 'lodash';
_ = lodash;
import MapContext from './control/MapContext';
import MapData from './model/MapData';
var mapContext: MapContext;
window.onload = () => {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
let mapDef = new MapData(JSON.parse(this.responseText));
mapContext = new MapContext(mapDef);
}
xhr.open('GET', 'resources/map/test.json', true);
xhr.send();
};

View file

@ -1,29 +0,0 @@
'use strict'
Direction = require '../model/Direction'
Entity = require '../model/Entity'
EntityPosition = require '../model/EntityPosition'
Position = require '../model/Position'
MapView = require '../view/MapView'
class MapContext
constructor: (@map) ->
@entities = {}
@playerEntity = new EntityPosition(
new Entity('square'),
new Position(8, 8),
Direction.EAST)
@addEntity(@playerEntity)
@mavView = new MapView @map, @entities
addEntity: (entity) =>
@entities[entity.position.asString()] = entity
module.exports = MapContext

34
src/control/MapContext.ts Normal file
View file

@ -0,0 +1,34 @@
'use strict';
import Direction from '../model/Direction';
import Entity from '../model/Entity';
import EntityPosition from '../model/EntityPosition';
import MapData from '../model/MapData';
import Position from '../model/Position';
import MapView from '../view/MapView';
export default class MapContext {
view: MapView;
entities: {[key: string]: EntityPosition} = {};
playerEntity: EntityPosition;
constructor(public map: MapData) {
this.playerEntity = new EntityPosition(
new Entity('square'),
new Position(8, 8),
Direction.East
);
this.addEntity(this.playerEntity);
this.view = new MapView(map, this.entities);
}
addEntity(entity: EntityPosition) {
this.entities[entity.position.asString()] = entity;
}
}

View file

@ -1,13 +0,0 @@
'use strict'
Direction =
NORTH: 0
EAST: 1
SOUTH: 2
WEST: 3
reverse: (d) -> (d+2)%4
module.exports = Direction

13
src/model/Direction.ts Normal file
View file

@ -0,0 +1,13 @@
'use strict';
export enum Direction {
North,
East,
South,
West
};
export function reverse(r: Direction): Direction { return (r+2) % 4; }
export default Direction;

View file

@ -1,8 +0,0 @@
'use strict'
class Entity
constructor: (@name) ->
module.exports = Entity

6
src/model/Entity.ts Normal file
View file

@ -0,0 +1,6 @@
'use strict';
export default class Entity {
constructor(public name: string) {}
}

View file

@ -1,8 +0,0 @@
'use strict'
class EntityPosition
constructor: (@entity, @position, @direction) ->
module.exports = EntityPosition

View file

@ -0,0 +1,11 @@
'use strict';
import Direction from '../model/Direction';
import Entity from './Entity';
import Position from './Position';
export default class EntityPosition {
constructor(public entity: Entity, public position: Position, public direction: Direction) {}
}

View file

@ -1,9 +0,0 @@
'use strict'
class MapData
constructor: (data) ->
{@tiles, @collition, @layers} = data
module.exports = MapData

20
src/model/MapData.ts Normal file
View file

@ -0,0 +1,20 @@
'use strict';
interface Input {
tiles: {[key: string]: {file: string}};
collision: string[];
layers: string[][][];
}
export default class MapData {
tiles: {[key: string]: {file: string}};
public collision: string[];
public layers: string[][][];
constructor(data: Input) {
this.tiles = data.tiles;
this.collision = data.collision;
this.layers = data.layers;
}
}

View file

@ -1,10 +0,0 @@
'use strict'
class Position
constructor: (@x, @y) ->
asString: => "#{@x},#{@y}"
module.exports = Position

10
src/model/Position.ts Normal file
View file

@ -0,0 +1,10 @@
'use strict';
export default class Position {
constructor(public x: number, public y: number) {}
asString(): string {
return `${this.x},${this.y}`;
}
}

View file

@ -1,13 +0,0 @@
'use strict'
module.exports =
mapPromises: (promises) ->
p = []
ret = {}
for own k, v of promises
do (k, v) ->
p.push(v.then (r) -> ret[k] = r)
Promise.all(p).then -> ret

13
src/util.ts Normal file
View file

@ -0,0 +1,13 @@
'use strict';
export function mapPromises<T>(promises: {[key: string]: Promise<T>}): Promise<{[key: string]: T}> {
var p: Promise<void>[] = []
var ret: {[key: string]: T} = {}
_.forOwn(promises, (v, k) => {
p.push(v.then(r => {ret[k] = r;}));
});
return Promise.all(p).then(() => ret);
}

View file

@ -1,108 +0,0 @@
'use strict'
util = require '../util'
tileSize = 32
body = document.getElementsByTagName('body')[0]
loadImage = (url) ->
new Promise (resolve, reject) ->
img = new Image()
img.addEventListener 'load', -> resolve img
img.addEventListener 'error', -> reject Error('Failed to load ' + url)
img.src = url
loadImages = (imgs) ->
util.mapPromises(_.mapValues imgs, loadImage)
loadTiles = (tiles) ->
loadImages(_.mapValues tiles, (t) -> "resources/sprite/tile/#{t.file}.png")
loadEntities = (entities) ->
p = {}
for e in entities
do (e) ->
p[e.entity.name] = loadImage "resources/sprite/entity/#{e.entity.name}.png"
util.mapPromises p
class MapView
constructor: (@map, @entities) ->
@redrawPending = false
@canvas = document.createElement 'canvas'
@canvas.style.position = 'absolute'
body.appendChild @canvas
@ctx = @canvas.getContext '2d'
window.addEventListener 'resize', @setSize
@setSize()
tilesReady = loadTiles(@map.tiles).then (tiles) =>
@tiles = tiles
entitiesReady = loadEntities(_.values @entities).then (entities) =>
@entitySprites = entities
tilesReady.then(entitiesReady).then =>
@redraw()
return
drawTile: (x, y, tile) =>
return unless tile
@ctx.drawImage tile, x, y
drawEntity: (e) =>
sprite = @entitySprites[e.entity.name]
return unless sprite
@ctx.drawImage(
sprite,
e.direction*tileSize, 0,
tileSize, tileSize,
e.position.x*tileSize, e.position.y*tileSize,
tileSize, tileSize)
draw: =>
@redrawPending = false
@ctx.clearRect 0, 0, @canvas.width, @canvas.height
return unless @tiles and @entitySprites
for layer in @map.layers
y = 0
for row in layer
x = 0
for tile in row
@drawTile x, y, @tiles[tile]
x += tileSize
y += tileSize
for e in _.values @entities
@drawEntity e
redraw: =>
unless @redrawPending
@redrawPending = true
window.requestAnimationFrame @draw
setSize: =>
e = document.documentElement
@canvas.width = window.innerWidth || e.clientWidth || body.clientWidth
@canvas.height = window.innerHeight || e.clientHeight || body.clientHeight
@redraw()
module.exports = MapView

142
src/view/MapView.ts Normal file
View file

@ -0,0 +1,142 @@
'use strict';
import * as util from '../util';
import EntityPosition from '../model/EntityPosition';
import MapData from '../model/MapData';
const tileSize = 32;
const body = document.getElementsByTagName('body')[0];
function loadImage(url: string): Promise<HTMLImageElement> {
return new Promise(function(resolve, reject) {
var img = new Image();
img.addEventListener('load', () => { resolve(img); });
img.addEventListener('error', () => { reject(Error('Failed to load ' + url)); });
img.src = url;
});
}
function loadImages(imgs: {[key: string]: string}): Promise<{[key: string]: HTMLImageElement}> {
return util.mapPromises(_.mapValues(imgs, loadImage));
}
function loadTiles(tiles: {[key: string]: {file: string}}): Promise<{[key: string]: HTMLImageElement}> {
return loadImages(_.mapValues(tiles, (t) => `resources/sprite/tile/${t.file}.png`));
}
function loadEntities(entities: EntityPosition[]): Promise<{[key: string]: HTMLImageElement}> {
var p: {[key: string]: Promise<HTMLImageElement>} = {};
entities.forEach(e => {
p[e.entity.name] = loadImage(`resources/sprite/entity/${e.entity.name}.png`);
});
return util.mapPromises(p);
}
export default class MapView {
redrawPending: boolean = false;
canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
tiles: {[key: string]: HTMLImageElement};
entitySprites: {[key: string]: HTMLImageElement};
constructor(private map: MapData, private entities: {[key: string]: EntityPosition}) {
this.canvas = document.createElement('canvas');
this.canvas.style.position = 'absolute';
body.appendChild(this.canvas);
this.ctx = this.canvas.getContext('2d');
window.addEventListener('resize', () => this.setSize());
this.setSize();
var tilesReady = loadTiles(map.tiles).then((tiles) => {
this.tiles = tiles;
});
var entitiesReady = loadEntities(this.getEntities()).then((entities) => {
this.entitySprites = entities;
});
Promise.all([tilesReady, entitiesReady]).then(() => {
this.redraw();
});
}
getEntities(): EntityPosition[] {
return _.valuesIn<EntityPosition>(this.entities);
}
setSize() {
var e = document.documentElement;
this.canvas.width = window.innerWidth || e.clientWidth || body.clientWidth;
this.canvas.height = window.innerHeight || e.clientHeight || body.clientHeight;
this.redraw()
}
drawTile(x: number, y: number, tile: HTMLImageElement) {
if (!tile)
return;
this.ctx.drawImage(tile, x, y);
}
drawEntity(e: EntityPosition) {
var sprite = this.entitySprites[e.entity.name];
if (!sprite)
return;
this.ctx.drawImage(
sprite,
e.direction*tileSize, 0,
tileSize, tileSize,
e.position.x*tileSize, e.position.y*tileSize,
tileSize, tileSize
);
}
draw() {
this.redrawPending = false;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (!this.tiles || !this.entitySprites)
return;
this.map.layers.forEach((layer) => {
let y = 0;
layer.forEach((row) => {
let x = 0;
for (let tile in row) {
this.drawTile(x, y, this.tiles[row[tile]]);
x += tileSize;
}
y += tileSize;
});
});
this.getEntities().forEach(e => {
this.drawEntity(e);
});
}
redraw() {
if (this.redrawPending)
return;
this.redrawPending = true;
window.requestAnimationFrame(() => this.draw());
}
}

12
tsconfig.json Normal file
View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": true,
"sourceMap": true
},
"files": [
"require.d.ts",
"typings/tsd.d.ts"
]
}

15
tsd.json Normal file
View file

@ -0,0 +1,15 @@
{
"version": "v4",
"repo": "borisyankov/DefinitelyTyped",
"ref": "master",
"path": "typings",
"bundle": "typings/tsd.d.ts",
"installed": {
"lodash/lodash.d.ts": {
"commit": "f11e49cfb77f99678657ddfe5d43017849675c64"
},
"es6-promise/es6-promise.d.ts": {
"commit": "f11e49cfb77f99678657ddfe5d43017849675c64"
}
}
}

View file

@ -1,7 +1,7 @@
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.coffee',
entry: './src/app.ts',
output: {
path: './build',
filename: 'bundle.js'
@ -14,10 +14,10 @@ module.exports = {
module: {
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.coffee$/, loader: 'coffee-loader' }
{ test: /\.ts$/, loader: 'ts-loader' }
]
},
resolve: {
extensions: ['', '.js', '.json', '.coffee']
extensions: ['', '.ts']
}
};