This commit is contained in:
Steven Lucco 2017-02-01 10:49:26 -08:00
Родитель 7e2593a490 0f232c00fc
Коммит fa6c11842e
50 изменённых файлов: 1068 добавлений и 373 удалений

Просмотреть файл

23
Dockerfile Normal file
Просмотреть файл

@ -0,0 +1,23 @@
FROM node:6.9.4-alpine
# Install gulp for building services
RUN npm install -g gulp
# Copy over and build ot-ink
COPY ./ot-ink /usr/src/ot-ink
WORKDIR /usr/src/ot-ink
RUN npm install
RUN gulp
# Copy over and build the server
COPY ./server /usr/src/server
WORKDIR /usr/src/server
RUN npm install
RUN gulp
# Expose the port the app runs under
EXPOSE 3000
EXPOSE 5858
# And set the default command to start the server
CMD ["npm", "start"]

Просмотреть файл

@ -4,12 +4,14 @@ services:
build: .
ports:
- "3000:3000"
- "5858:5858"
volumes:
- .:/usr/src/server
- .:/usr/src/
depends_on:
- mongodb
- redis
command: node --debug /usr/src/server/dist/www.js
redis:
image: "redis:alpine"
mongodb:
image: "mongo:3.4.1"
image: "mongo:3.4.1"

2
ot-ink/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
node_modules/
dist/

0
ot-ink/.npmignore Normal file
Просмотреть файл

36
ot-ink/gulpfile.js Normal file
Просмотреть файл

@ -0,0 +1,36 @@
var clean = require("gulp-clean");
var gulp = require("gulp");
var path = require('path');
var sourcemaps = require('gulp-sourcemaps');
var ts = require("gulp-typescript");
var tslint = require("gulp-tslint");
var tsProject = ts.createProject('tsconfig.json');
// Cleans up generated files
gulp.task("clean", function () {
return gulp.src(['dist'], { read: false }).pipe(clean());
})
// Builds the server JavaScript code
gulp.task("build", function () {
return gulp.src(['src/**/*.ts'])
.pipe(sourcemaps.init())
.pipe(tsProject())
.pipe(sourcemaps.write('.', {
includeContent: false, sourceRoot: function (file) {
return path.join(file.cwd, './src');
}
}))
.pipe(gulp.dest("dist"))
});
// Linting to validate style guide
gulp.task("tslint", () =>
gulp.src(['src/**/*.ts'])
.pipe(tslint({
formatter: "verbose"
}))
.pipe(tslint.report())
);
gulp.task("default", ["tslint", "build"]);

20
ot-ink/package.json Normal file
Просмотреть файл

@ -0,0 +1,20 @@
{
"name": "ot-ink",
"version": "0.1.0",
"description": "Ink OT type",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"author": "Microsoft",
"devDependencies": {
"gulp": "^3.9.1",
"gulp-clean": "^0.3.2",
"gulp-sourcemaps": "^1.9.1",
"gulp-tslint": "^7.0.1",
"gulp-typescript": "^3.1.3",
"tslint": "^4.0.2",
"typescript": "^2.1.4"
},
"dependencies": {
"@types/node": "^6.0.52"
}
}

61
ot-ink/src/definitions.ts Normal file
Просмотреть файл

@ -0,0 +1,61 @@
/**
* Kind of MixInk action which is used to decide if current action is for draring, moving or clearing canvas
*/
export enum MixInkActionKind {
Move = 0,
Draw = 1,
Clear = 2,
}
/**
* The action which is used to draw strokes
*/
export interface IMixInkAction {
// Milliseconds since start of MixInking when this stroke should be drawn
time: number;
// Move or darw
kind: MixInkActionKind;
// x coordinate
x: number;
// y coordinate
y: number;
// TODO - should the ink pressure exist here
// Pen data if the pen has changed with this stroke
pen?: IPen;
};
/**
* Pen data for the current stroke
*/
export interface IPen {
// Color in web format #rrggbb
color: string;
// Thickness of pen in pixels
thickness: number;
// Width and height for highlighter brush type
width?: number;
height?: number;
// Brush type, by default b is 0
brush?: number;
}
export enum MixInkBlush {
Pen = 0,
Eraser = 1,
Highlighter = 2,
}
export enum SegmentCircleInclusive {
None,
Both,
Start,
End,
}

5
ot-ink/src/index.ts Normal file
Просмотреть файл

@ -0,0 +1,5 @@
// Export the ink definition
import * as ink from "./ink";
export { ink as type };
export * from "./definitions";

48
ot-ink/src/ink.ts Normal file
Просмотреть файл

@ -0,0 +1,48 @@
// This is a really simple OT type.
//
// Its included for demonstration purposes and its used in the meta unit tests.
//
// This defines a really simple text OT type which only allows inserts. (No deletes).
//
// Ops look like:
// {position:#, text:"asdf"}
//
// Document snapshots look like:
// {str:string}
export const name = "simple";
export const uri = "http://sharejs.org/types/simple";
// Create a new document snapshot. Initial data can be passed in.
export function create(initial) {
if (initial === null) {
initial = "";
}
return {str: initial};
}
// Apply the given op to the document snapshot. Returns the new snapshot.
export function apply(snapshot, op) {
if (op.position < 0 || op.position > snapshot.str.length) {
throw new Error("Invalid position");
}
let str = snapshot.str;
str = str.slice(0, op.position) + op.text + str.slice(op.position);
return { str };
}
// Transform op1 by op2. Returns transformed version of op1.
// Sym describes the symmetry of the operation. Its either 'left' or 'right'
// depending on whether the op being transformed comes from the client or the
// server.
export function transform(op1, op2, sym) {
let pos = op1.position;
if (op2.position < pos || (op2.position === pos && sym === "left")) {
pos += op2.text.length;
}
return {position: pos, text: op1.text};
}

17
ot-ink/tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,17 @@
{
"exclude": [
"node_modules",
"dist"
],
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
}
}

3
ot-ink/tslint.json Normal file
Просмотреть файл

@ -0,0 +1,3 @@
{
"extends": "tslint:recommended"
}

Просмотреть файл

@ -1,16 +0,0 @@
FROM node:6.9.4-alpine
# Copy over the app source
COPY . /usr/src/server
WORKDIR /usr/src/server
# Install packages and then install the app
RUN npm install -g gulp
RUN npm install
RUN gulp
# Expose the port the app runs under
EXPOSE 3000
# And set the default command to start the server
CMD ["npm", "start"]

Просмотреть файл

@ -28,5 +28,8 @@
"redis": {
"host": "redis",
"port": 6379
},
"mongo": {
"connectionString": "mongodb://mongodb:27017"
}
}

Просмотреть файл

@ -52,7 +52,7 @@ let controllers = [
{ name: "api", src: "src/api/index.ts", outFile: "api.js", folder: "public/dist/api", standalone: "pronet" },
{ name: "calendar", src: "src/calendar/driver.ts", outFile: "driver.js", folder: "public/dist/views/calendar", standalone: "calendar" },
{ name: "collab", src: "src/collab/collab.ts", outFile: "collab.js", folder: "public/dist/collab", standalone: "collab" },
{ name: "canvas", src: "src/canvas/canvas.ts", outFile: "canvas.js", folder: "public/dist/canvas", standalone: "canvas" },
{ name: "canvas", src: "src/canvas/index.ts", outFile: "canvas.js", folder: "public/dist/canvas", standalone: "canvas" },
];
// Generate tasks for the browserified code

Просмотреть файл

@ -50,6 +50,7 @@
"morgan": "~1.6.1",
"nconf": "^0.8.4",
"node-uuid": "^1.4.7",
"ot-ink": "file:../ot-ink",
"passport": "^0.3.2",
"passport-facebook": "^2.1.1",
"passport-google-oauth": "^1.0.0",
@ -65,7 +66,7 @@
"serve-favicon": "~2.3.0",
"sharedb": "^1.0.0-beta.6",
"sharedb-mingo-memory": "^1.0.0-beta",
"sharedb-mongo": "^1.0.0-beta.3",
"sharedb-mongo": "git://github.com/kurtb/sharedb-mongo.git",
"sharedb-redis-pubsub": "^1.0.0-beta.1",
"socket.io-redis": "^1.1.1",
"systemjs": "0.19.40",

Просмотреть файл

@ -39,7 +39,7 @@ export function createOrGetUser(
details: IUserDetails): Promise<any> {
return accounts.getAccount(provider, providerId).then((account) => {
// Check to see if there is an account - if not we need to create a new user
// Check to see if there is an account - if not we need to create a new user
let userIdP;
if (account === null) {
// Create a user first and then link this account to it

Просмотреть файл

@ -30,7 +30,7 @@ export class PostMessageHost implements IHost {
public start() {
this.host = postMessageSockets.getOrCreateHost(this.window);
// TODO for security we may need to define a set of allowed hosts -
// TODO for security we may need to define a set of allowed hosts -
// especially if the iframe conveys secret information to the host
this.socketP = this.host.connect(window.parent, "*");

Просмотреть файл

@ -54,7 +54,7 @@ if (nconf.get("redis")) {
servername: nconf.get("redis:host"),
};
// Azure seems to lose our Redis client for SSL connections - we ping it to keep it alive.
// Azure seems to lose our Redis client for SSL connections - we ping it to keep it alive.
setInterval(() => {
redisClient.ping((error, result) => {
if (error) {
@ -301,7 +301,7 @@ app.use((request, response, next) => {
if (!request.session) {
return next(new Error("Session not available"));
} else {
next(); // otherwise continue
next(); // otherwise continue
}
});
app.use(passport.initialize());

Просмотреть файл

@ -15,8 +15,8 @@ import {
} from "../api/index";
import { ICalendar, ICalendarEvent } from "./interfaces";
// The TypeScript compiler will elide these two libraries since they aren"t directly accessed.
// They are jquery plugins and so internally attach themselves to the $ object. We do the import
// The TypeScript compiler will elide these two libraries since they aren"t directly accessed.
// They are jquery plugins and so internally attach themselves to the $ object. We do the import
// below to force their inclusion while also keeping the version above to get the typing information
import "fullcalendar";
import "qtip2";
@ -155,7 +155,7 @@ class CalendarViewModel {
}
this.tableP.then((table) => {
// Longer term we should standardize on some format here so there
// Longer term we should standardize on some format here so there
// isn't a disconnect between Office and moment
const columnTimeFormatString = "m/d/yy h:mm AM/PM";
let columns = [
@ -168,7 +168,7 @@ class CalendarViewModel {
{ name: "responseStatus", format: null },
];
// Get and store the rows we will bind to the pnhost table -
// Get and store the rows we will bind to the pnhost table -
// we convert times to a format easier to parse by hosts (i.e. Excel)
const formatString = "M/D/YY h:mm A";
this.tableBoundRows = [];
@ -190,7 +190,7 @@ class CalendarViewModel {
// Load the rows into the hosted table
table.loadData(columns, this.tableBoundRows);
// Setup a table listener if it doesn"t already exist
// Setup a table listener if it doesn"t already exist
if (!this.tableListener) {
this.tableListener = new TableListener(this);
table.addListener(this.tableListener);
@ -200,7 +200,7 @@ class CalendarViewModel {
private loadAndCacheCalendars(): Promise<ICalendar[]> {
return this.calendar.getCalendars().then((calendars) => {
// Clear any pending deletes - a reload resets any interactions
// Clear any pending deletes - a reload resets any interactions
this.pendingDeletes = [];
// Initialize the custom UI once we load the first batch of data

Просмотреть файл

@ -1,4 +1,4 @@
import App from "./canvas";
import Canvas from "./canvas";
import * as utils from "./utils";
export default class BackBoard {
@ -8,16 +8,20 @@ export default class BackBoard {
private div: HTMLElement;
private gesture: MSGesture;
constructor(private appObject: App, htmlId: string) {
constructor(private appObject: Canvas, htmlId: string) {
this.div = utils.id(htmlId);
// tslint:disable-next-line:no-string-literal
this.div["sysObject"] = this;
this.gesture = new MSGesture();
this.gesture.target = this.div;
// tslint:disable-next-line:no-string-literal
if (window["MSGesture"]) {
this.gesture = new MSGesture();
this.gesture.target = this.div;
this.div.addEventListener("MSGestureChange", this.gestureListener, false);
this.div.addEventListener("MSGestureTap", this.gestureListener, false);
}
this.div.addEventListener("MSGestureChange", this.gestureListener, false);
this.div.addEventListener("MSGestureTap", this.gestureListener, false);
this.div.addEventListener("pointerdown", this.eventListener, false);
}
@ -30,7 +34,9 @@ export default class BackBoard {
} else {
// so.pointerId = evt.pointerId;
if (evt.type === "pointerdown") {
so.gesture.addPointer(evt.pointerId);
if (so.gesture) {
so.gesture.addPointer(evt.pointerId);
}
}
}
}

Просмотреть файл

@ -1,145 +1,141 @@
// The main app code
import * as $ from "jquery";
import * as otInk from "ot-ink";
import * as sharedb from "sharedb/lib/client";
import BackBoard from "./backBoard";
import InkCanvas from "./inkCanvas";
import StickyNote from "./stickyNote";
import * as utils from "./utils";
let appObject;
let sticky;
let mainBoard;
// Register the use of the rich text OT format
sharedb.types.register(otInk.type);
// App Class
export default class App {
public ink: InkCanvas;
// tslint:disable:no-console
public handleKeys: boolean = true;
public stickyCount: number = 0;
/**
* Canvas app
*/
export default class Canvas {
public ink: InkCanvas;
constructor() {
public handleKeys: boolean = true;
public stickyCount: number = 0;
// register all of the different handlers
let p: HTMLElement = utils.id("hitPlane");
this.ink = new InkCanvas(p);
constructor() {
// register all of the different handlers
let p = document.getElementById("hitPlane");
this.ink = new InkCanvas(p);
window.addEventListener("keydown", this.keyPress, false);
window.addEventListener("keyup", this.keyRelease, false);
window.addEventListener("keydown", (evt) => this.keyPress(evt), false);
window.addEventListener("keyup", (evt) => this.keyRelease(evt), false);
// toolbar buttons
document.querySelector("#strokeColors").addEventListener("click", (e) => { appObject.ink.inkColor(); }, false);
document.querySelector("#clearButton").addEventListener("click", (e) => { appObject.clear(); }, false);
document.querySelector("#undoButton").addEventListener("click", (e) => { appObject.ink.undo(); }, false);
document.querySelector("#redoButton").addEventListener("click", (e) => { appObject.ink.redo(); }, false);
document.querySelector("#testButton").addEventListener("click", (e) => { appObject.test(e); }, false);
document.querySelector("#turnOnInk").addEventListener("click", (e) => { appObject.test(e); }, false);
}
// Key Handlers:
// Escape
// ^C Copy
// ^V Paste
// ^F Find
// ^O Load
// ^S Save
// ^R Recognize
// ^Q Quit (shuts down the sample app)
// tslint:disable-next-line:no-empty
public keyRelease(evt) {
}
public keyPress(evt) {
if (this.handleKeys === false) {
return false;
// toolbar buttons
document.querySelector("#strokeColors").addEventListener("click", (e) => { this.ink.inkColor(); }, false);
document.querySelector("#clearButton").addEventListener("click", (e) => { this.clear(); }, false);
document.querySelector("#undoButton").addEventListener("click", (e) => { this.ink.undo(); }, false);
document.querySelector("#redoButton").addEventListener("click", (e) => { this.ink.redo(); }, false);
document.querySelector("#testButton").addEventListener("click", (e) => { this.test(e); }, false);
document.querySelector("#turnOnInk").addEventListener("click", (e) => { this.test(e); }, false);
document.querySelector("#replay").addEventListener("click", (e) => { this.ink.replay(); }, false);
}
if (evt.keyCode === 27) { // Escape
evt.preventDefault();
utils.displayStatus("Escape");
} else if (evt.ctrlKey === true && evt.keyCode !== 17) { // look for keys while control down
utils.displayStatus("KeyCode: " + evt.keyCode);
if (evt.keyCode === 67) { // Control c
evt.preventDefault();
utils.displayStatus("CTRL-C");
} else if (evt.keyCode === 86) { // Control v
evt.preventDefault();
utils.displayStatus("CTRL-V");
} else if (evt.keyCode === 79) { // Control o
evt.preventDefault();
utils.displayStatus("CTRL-O");
} else if (evt.keyCode === 83) { // Control s
evt.preventDefault();
utils.displayStatus("CTRL-S");
} else if (evt.keyCode === 82) { // Control r
evt.preventDefault();
utils.displayStatus("CTRL-R");
} else if (evt.keyCode === 81) { // Control q
evt.preventDefault();
utils.displayStatus("CTRL-Q");
} else if (evt.keyCode === 89) { // Control y
evt.preventDefault();
utils.displayStatus("CTRL-Y");
} else if (evt.keyCode === 90) { // Control z
evt.preventDefault();
utils.displayStatus("CTRL-Z");
}
// Key Handlers:
// Escape
// ^C Copy
// ^V Paste
// ^F Find
// ^O Load
// ^S Save
// ^R Recognize
// ^Q Quit (shuts down the sample app)
// tslint:disable-next-line:no-empty
public keyRelease(evt) {
}
}
// this method will try up the entire board
public clear() {
appObject.ink.clear();
let board = utils.id("content");
let stickies = document.querySelectorAll(".stickyNote");
// tslint:disable-next-line:prefer-for-of:stickies is NodeListOf not an array
for (let i = 0; i < stickies.length; i++) {
board.removeChild(stickies[i]);
}
}
// find all of the things that are selected and unselect them
public unselectAll() {
let sel = document.querySelectorAll(".stickySelected");
let elem;
if (sel.length > 0) {
for (let i = 0; i < sel.length; i++) {
elem = sel.item(i);
if (elem.classList.contains("stickySelected")) {
elem.classList.remove("stickySelected");
elem.style.zIndex = "1";
public keyPress(evt) {
if (this.handleKeys === false) {
return false;
}
}
}
}
public makeInkable() {
let sel = document.querySelectorAll(".stickySelected");
let elem;
if (sel.length > 0) {
for (let i = 0; i < sel.length; i++) {
elem = sel.item(i);
elem.classList.add("stickyInkable");
let ic = new InkCanvas(elem);
}
if (evt.keyCode === 27) { // Escape
evt.preventDefault();
utils.displayStatus("Escape");
} else if (evt.ctrlKey === true && evt.keyCode !== 17) { // look for keys while control down
utils.displayStatus("KeyCode: " + evt.keyCode);
if (evt.keyCode === 67) { // Control c
evt.preventDefault();
utils.displayStatus("CTRL-C");
} else if (evt.keyCode === 86) { // Control v
evt.preventDefault();
utils.displayStatus("CTRL-V");
} else if (evt.keyCode === 79) { // Control o
evt.preventDefault();
utils.displayStatus("CTRL-O");
} else if (evt.keyCode === 83) { // Control s
evt.preventDefault();
utils.displayStatus("CTRL-S");
} else if (evt.keyCode === 82) { // Control r
evt.preventDefault();
utils.displayStatus("CTRL-R");
} else if (evt.keyCode === 81) { // Control q
evt.preventDefault();
utils.displayStatus("CTRL-Q");
} else if (evt.keyCode === 89) { // Control y
evt.preventDefault();
utils.displayStatus("CTRL-Y");
} else if (evt.keyCode === 90) { // Control z
evt.preventDefault();
utils.displayStatus("CTRL-Z");
}
}
}
}
// this is the handler for the test tube
public test(e) {
if (e.target.id === "testButton") {
this.unselectAll();
let x = new StickyNote(utils.id("content"));
// this method will try up the entire board
public clear() {
this.ink.clear();
let board = utils.id("content");
let stickies = document.querySelectorAll(".stickyNote");
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < stickies.length; i++) {
board.removeChild(stickies[i]);
}
}
if (e.target.id === "turnOnInk") {
this.makeInkable();
}
}
// find all of the things that are selected and unselect them
public unselectAll() {
let sel = document.querySelectorAll(".stickySelected");
let elem;
if (sel.length > 0) {
for (let i = 0; i < sel.length; i++) {
elem = sel.item(i);
if (elem.classList.contains("stickySelected")) {
elem.classList.remove("stickySelected");
elem.style.zIndex = "1";
}
}
}
}
public makeInkable() {
let sel = document.querySelectorAll(".stickySelected");
let elem;
if (sel.length > 0) {
for (let i = 0; i < sel.length; i++) {
elem = sel.item(i);
elem.classList.add("stickyInkable");
let ic = new InkCanvas(elem);
}
}
}
// this is the handler for the test tube
public test(e) {
if (e.target.id === "testButton") {
this.unselectAll();
let x = new StickyNote(utils.id("content"));
}
if (e.target.id === "turnOnInk") {
this.makeInkable();
}
}
}
// create the new app
document.addEventListener("DOMContentLoaded", () => {
appObject = new App();
sticky = new StickyNote(utils.id("content"));
mainBoard = new BackBoard(appObject, "hitPlane");
// id("ToolBar").appendChild(new ToolBarButton("images/icons/pencil.svg").click(appObject.clear).elem());
});

Просмотреть файл

@ -0,0 +1,2 @@
export * from "./point";
export * from "./vector";

Просмотреть файл

@ -0,0 +1,10 @@
export interface IPoint {
x: number;
y: number;
}
export class Point implements IPoint {
// Constructor
constructor(public x: number, public y: number) {
}
}

Просмотреть файл

@ -0,0 +1,33 @@
export interface IVector {
x: number;
y: number;
length(): number;
}
export class Vector implements IVector {
/**
* Returns the vector resulting from rotating vector by angle
*/
public static rotate(vector: Vector, angle: number): Vector {
return new Vector(
vector.x * Math.cos(angle) - vector.y * Math.sin(angle),
vector.x * Math.sin(angle) + vector.y * Math.cos(angle));
}
/**
* Returns the normalized form of the given vector
*/
public static normalize(vector: Vector): Vector {
let length = vector.length();
return new Vector(vector.x / length, vector.y / length);
}
// Constructor
constructor(public x: number, public y: number) {
}
public length(): number {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
}

Просмотреть файл

@ -0,0 +1,63 @@
// The main app code
import * as $ from "jquery";
import * as otInk from "ot-ink";
import * as sharedb from "sharedb/lib/client";
import BackBoard from "./backBoard";
import Canvas from "./canvas";
import InkCanvas from "./inkCanvas";
import StickyNote from "./stickyNote";
import * as utils from "./utils";
// tslint:disable:no-console
// Register the use of the rich text OT format
sharedb.types.register(otInk.type);
// throttle resize events and replace with an optimized version
utils.throttle("resize", "throttled-resize");
// TODO export the ability to get events?
export function initialize(id: string) {
// Open WebSocket connection to ShareDB server
let protocol = window.location.protocol.indexOf("https") !== -1 ? "wss" : "ws";
let socket = new WebSocket(`${protocol}://${window.location.host}`);
let connection = new sharedb.Connection(socket);
let doc = connection.get("canvas", id);
// create the new app
$("document").ready(() => {
let canvas = new Canvas();
let sticky = new StickyNote(utils.id("content"));
let mainBoard = new BackBoard(canvas, "hitPlane");
// id("ToolBar").appendChild(new ToolBarButton("images/icons/pencil.svg").click(appObject.clear).elem());
doc.subscribe((err) => {
if (err) {
throw err;
}
// If there is no type we need to create the document
if (!doc.type) {
console.log("Creating new document");
doc.create("Hello", otInk.type.name);
}
console.log(doc.data);
// To write more data
doc.submitOp(
{ position: 3, text: "World, " },
{ source: canvas });
doc.on("op", (op, source) => {
if (source === canvas) {
return;
}
// Update the canvas
});
});
});
}

Просмотреть файл

@ -1,216 +1,390 @@
import * as ink from "ot-ink";
import * as geometry from "./geometry/index";
import { Circle, IShape, Polygon } from "./shapes/index";
import * as utils from "./utils";
// TODO split classes into separate files
// tslint:disable:max-classes-per-file
// TODO remove before commit
// tslint:disable:no-console
interface IPtrEvtPoint {
x: number;
y: number;
x: number;
y: number;
}
interface IPointerPointProps {
isEraser: boolean;
isEraser: boolean;
}
class EventPoint {
public rawPosition: IPtrEvtPoint;
public properties: IPointerPointProps;
public rawPosition: IPtrEvtPoint;
public properties: IPointerPointProps;
constructor(evt: PointerEvent) {
this.rawPosition = { x: evt.x, y: evt.y };
this.properties = { isEraser: false };
}
constructor(evt: PointerEvent) {
this.rawPosition = { x: evt.x, y: evt.y };
this.properties = { isEraser: false };
}
}
export default class InkCanvas {
public canvas: HTMLCanvasElement;
public context: CanvasRenderingContext2D;
public penID: number = -1;
public gesture: MSGesture;
public canvas: HTMLCanvasElement;
public context: CanvasRenderingContext2D;
public penID: number = -1;
public gesture: MSGesture;
// constructor
constructor(parent: HTMLElement) {
// setup canvas
this.canvas = document.createElement("canvas");
this.canvas.classList.add("drawSurface");
parent.appendChild(this.canvas);
// tslint:disable-next-line:no-string-literal
this.canvas["inkCanvas"] = this;
// get context
this.context = this.canvas.getContext("2d");
private strokes: ink.IMixInkAction[] = [];
let w: number = this.canvas.offsetWidth;
let h: number = this.canvas.offsetHeight;
// constructor
constructor(parent: HTMLElement) {
// setup canvas
this.canvas = document.createElement("canvas");
this.canvas.classList.add("drawSurface");
parent.appendChild(this.canvas);
// set the width and height specified through CSS
this.canvas.setAttribute("width", w.toString());
this.canvas.setAttribute("height", h.toString());
// tslint:disable-next-line:no-string-literal
window["strokes"] = this.strokes;
let bb = false;
this.canvas.addEventListener("pointerdown", this.handlePointerDown, bb);
this.canvas.addEventListener("pointermove", this.handlePointerMove, bb);
this.canvas.addEventListener("pointerup", this.handlePointerUp, bb);
}
// tslint:disable:no-empty
// Stubs for bunch of functions that are being called in the code below
// this will make it easier to fill some code in later or just delete them
// get context
this.context = this.canvas.getContext("2d");
public tempEraseMode() {
}
let bb = false;
this.canvas.addEventListener("pointerdown", (evt) => this.handlePointerDown(evt), bb);
this.canvas.addEventListener("pointermove", (evt) => this.handlePointerMove(evt), bb);
this.canvas.addEventListener("pointerup", (evt) => this.handlePointerUp(evt), bb);
public restoreMode() {
}
// Set the initial size of hte canvas and then register for resize events to be able to update it
this.resize(this.canvas.offsetWidth, this.canvas.offsetHeight);
window.addEventListener("throttled-resize", (event) => {
this.resize(this.canvas.offsetWidth, this.canvas.offsetHeight);
});
}
// tslint:disable:no-empty
// Stubs for bunch of functions that are being called in the code below
// this will make it easier to fill some code in later or just delete them
public renderAllStrokes() {
}
public anchorSelection() {
}
public selectAll() {
}
public inkMode() {
}
public inkColor() {
}
public undo() {
}
public redo() {
}
// tslint:enable:no-empty
public anySelected(): boolean {
return false;
}
// We will accept pen down or mouse left down as the start of a stroke.
// We will accept touch down or mouse right down as the start of a touch.
public handlePointerDown(evt) {
let ic = this.getInkCanvas();
ic.penID = evt.pointerId;
if (evt.pointerType === "touch") {
// ic.gesture.addPointer(evt.pointerId);
public tempEraseMode() {
}
if ((evt.pointerType === "pen") || ((evt.pointerType === "mouse") && (evt.button === 0))) {
// Anchor and clear any current selection.
ic.anchorSelection();
let pt = new EventPoint(evt);
if (pt.properties.isEraser) { // The back side of a pen, which we treat as an eraser
ic.tempEraseMode();
} else {
ic.restoreMode();
}
ic.context.beginPath();
ic.context.moveTo(pt.rawPosition.x, pt.rawPosition.y);
let pressureWidth = evt.pressure * 15;
ic.context.lineWidth = pressureWidth;
evt.returnValue = false;
}
}
public handlePointerMove(evt) {
let ic = this.getInkCanvas();
if (evt.pointerId === ic.penID) {
let pt = new EventPoint(evt);
let w = 8;
let h = 8;
if (evt.pointerType === "touch") {
ic.context.strokeStyle = "gray";
w = evt.width;
h = evt.height;
// context.strokeRect(evt.x - w/2 - 1, evt.y - h/2 -1 , w+1, h+1);
ic.context.clearRect(evt.x - w / 4, evt.y - h / 4, w / 2, h / 2);
evt.returnValue = false;
return false; // we"re going to clearRect instead
}
if (evt.pointerType === "pen") {
ic.context.strokeStyle = "rgba(0, 50, 0, 1)";
w = w * (0.1 + evt.pressure);
h = h * (0.1 + evt.pressure);
} else { // just mouse
ic.context.strokeStyle = "rgba(250, 0, 0, 0.5)";
}
ic.context.lineWidth = w;
ic.context.lineTo(evt.clientX, evt.clientY);
ic.context.stroke();
evt.returnValue = false;
// let pts = evt.intermediatePoints;
// for (let i = pts.length - 1; i >= 0 ; i--) {
// }
}
return false;
}
public handlePointerUp(evt) {
let ic = this.getInkCanvas();
if (evt.pointerId === ic.penID) {
ic.penID = -1;
let pt = new EventPoint(evt);
// ic.context.lineTo(pt.rawPosition.x, pt.rawPosition.y);
// ic.context.stroke();
ic.context.closePath();
ic.renderAllStrokes();
evt.returnValue = false;
}
return false;
}
// We treat the event of the pen leaving the canvas as the same as the pen lifting;
// it completes the stroke.
public handlePointerOut(evt) {
let ic = this.getInkCanvas();
if (evt.pointerId === ic.penID) {
let pt = new EventPoint(evt);
ic.context.lineTo(pt.rawPosition.x, pt.rawPosition.y);
ic.context.stroke();
ic.context.closePath();
ic.penID = -1;
ic.renderAllStrokes();
public restoreMode() {
}
return false;
}
public handleTap(evt) {
// Anchor and clear any current selection.
let ic = this.getInkCanvas();
if (ic.anySelected()) {
ic.anchorSelection();
ic.renderAllStrokes();
}
return false;
}
public clear() {
if (!this.anySelected()) {
this.selectAll();
this.inkMode();
public anchorSelection() {
}
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
public selectAll() {
}
this.renderAllStrokes();
utils.displayStatus("");
utils.displayError("");
}
public inkMode() {
}
private getInkCanvas(): InkCanvas {
// tslint:disable-next-line:no-string-literal
return this["inkCanvas"] as InkCanvas;
}
public inkColor() {
}
public undo() {
}
public redo() {
}
// tslint:enable:no-empty
public anySelected(): boolean {
return false;
}
// We will accept pen down or mouse left down as the start of a stroke.
// We will accept touch down or mouse right down as the start of a touch.
public handlePointerDown(evt) {
this.penID = evt.pointerId;
if (evt.pointerType === "touch") {
// ic.gesture.addPointer(evt.pointerId);
}
if ((evt.pointerType === "pen") || ((evt.pointerType === "mouse") && (evt.button === 0))) {
// Anchor and clear any current selection.
this.anchorSelection();
let pt = new EventPoint(evt);
if (pt.properties.isEraser) { // The back side of a pen, which we treat as an eraser
this.tempEraseMode();
} else {
this.restoreMode();
}
this.addAndDrawStroke(pt.rawPosition, ink.MixInkActionKind.Move, evt.pressure);
evt.returnValue = false;
}
}
public handlePointerMove(evt) {
if (evt.pointerId === this.penID) {
// if (evt.pointerType === "touch") {
// if (evt.pointerType === "pen") {
// } else {
// }
this.addAndDrawStroke({ x: evt.clientX, y: evt.clientY }, ink.MixInkActionKind.Draw, evt.pressure);
evt.returnValue = false;
}
return false;
}
public handlePointerUp(evt) {
if (evt.pointerId === this.penID) {
this.penID = -1;
let pt = new EventPoint(evt);
evt.returnValue = false;
this.addAndDrawStroke(pt.rawPosition, ink.MixInkActionKind.Draw, evt.pressure);
}
return false;
}
// We treat the event of the pen leaving the canvas as the same as the pen lifting;
// it completes the stroke.
public handlePointerOut(evt) {
if (evt.pointerId === this.penID) {
let pt = new EventPoint(evt);
this.penID = -1;
this.addAndDrawStroke(pt.rawPosition, ink.MixInkActionKind.Draw, evt.pressure);
}
return false;
}
public handleTap(evt) {
// Anchor and clear any current selection.
if (this.anySelected()) {
this.anchorSelection();
}
return false;
}
public clear() {
if (!this.anySelected()) {
this.selectAll();
this.inkMode();
}
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
utils.displayStatus("");
utils.displayError("");
}
public replay() {
this.clearCanvas();
if (this.strokes.length > 0) {
this.animateStroke(0);
}
}
private animateStroke(index: number) {
// Draw the requested stroke
let currentStroke = this.strokes[index];
let previousStroke = index - 1 >= 0 ? this.strokes[index - 1] : null;
this.drawStroke(currentStroke, previousStroke);
// And then ask for the next one
let nextStroke = index + 1 < this.strokes.length ? this.strokes[index + 1] : null;
if (nextStroke) {
let time = nextStroke.time - currentStroke.time;
setTimeout(() => this.animateStroke(index + 1), time);
}
}
/**
* Clears the canvas
*/
private clearCanvas() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
private redraw() {
this.clearCanvas();
let previousStroke: ink.IMixInkAction = null;
for (let stroke of this.strokes) {
this.drawStroke(stroke, previousStroke);
previousStroke = stroke;
}
}
private drawStroke(stroke: ink.IMixInkAction, previous: ink.IMixInkAction) {
let shapes: IShape[];
switch (stroke.kind) {
case ink.MixInkActionKind.Move:
shapes = this.getShapes(stroke, stroke, ink.SegmentCircleInclusive.End);
break;
case ink.MixInkActionKind.Draw:
shapes = this.getShapes(previous, stroke, ink.SegmentCircleInclusive.End);
break;
case ink.MixInkActionKind.Clear:
this.clearCanvas();
break;
default:
break;
}
if (shapes) {
for (let shape of shapes) {
this.context.beginPath();
shape.render(this.context);
this.context.closePath();
this.context.fill();
}
}
}
/**
* Resizes the canvas
*/
private resize(width: number, height: number) {
// Updates the size of the canvas
this.canvas.width = width;
this.canvas.height = height;
// And then redraw the canvas
this.redraw();
}
private addAndDrawStroke(
pt: IPtrEvtPoint,
kind: ink.MixInkActionKind,
pressure: number,
color: string = "rgba(0, 50, 0, 1)") {
let thickness = pressure * 15;
// store the stroke command
let pen: ink.IPen = {
brush: ink.MixInkBlush.Pen,
color,
thickness,
};
let stroke: ink.IMixInkAction = {
kind,
pen,
time: new Date().getTime(),
x: pt.x,
y: pt.y,
};
this.strokes.push(stroke);
let lastStroke = this.strokes.length > 1 ? this.strokes[this.strokes.length - 2] : null;
this.drawStroke(stroke, lastStroke);
}
/***
* given start point and end point, get MixInk shapes to render. The returned MixInk
* shapes may contain one or two circles whose center is either start point or end point.
* Enum SegmentCircleInclusive determins whether circle is in the return list.
* Besides circles, a trapezoid that serves as a bounding box of two stroke point is also returned.
*/
private getShapes(
startPoint: ink.IMixInkAction,
endPoint: ink.IMixInkAction,
circleInclusive: ink.SegmentCircleInclusive): IShape[] {
let dirVector = new geometry.Vector(endPoint.x - startPoint.x,
endPoint.y - startPoint.y);
let len = dirVector.length();
let shapes = new Array<IShape>();
let trapezoidP0: geometry.IPoint;
let trapezoidP1: geometry.IPoint;
let trapezoidP2: geometry.IPoint;
let trapezoidP3: geometry.IPoint;
let normalizedLateralVector: geometry.IVector;
let widthAtStart = startPoint.pen.thickness / 2;
let widthAtEnd = endPoint.pen.thickness / 2;
// Just draws a circle on small values??
if (len + Math.min(widthAtStart, widthAtEnd) <= Math.max(widthAtStart, widthAtEnd)) {
let center = widthAtStart >= widthAtEnd ? startPoint : endPoint;
shapes.push(new Circle({ x: center.x, y: center.y }, center.pen.thickness / 2));
return shapes;
}
if (len === 0) {
return null;
}
if (widthAtStart !== widthAtEnd) {
let angle = Math.acos(Math.abs(widthAtStart - widthAtEnd) / len);
if (widthAtStart < widthAtEnd) {
angle = Math.PI - angle;
}
normalizedLateralVector = geometry.Vector.normalize(geometry.Vector.rotate(dirVector, -angle));
trapezoidP0 = new geometry.Point(
startPoint.x + widthAtStart * normalizedLateralVector.x,
startPoint.y + widthAtStart * normalizedLateralVector.y);
trapezoidP3 = new geometry.Point(
endPoint.x + widthAtEnd * normalizedLateralVector.x,
endPoint.y + widthAtEnd * normalizedLateralVector.y);
normalizedLateralVector = geometry.Vector.normalize(geometry.Vector.rotate(dirVector, angle));
trapezoidP2 = new geometry.Point(
endPoint.x + widthAtEnd * normalizedLateralVector.x,
endPoint.y + widthAtEnd * normalizedLateralVector.y);
trapezoidP1 = new geometry.Point(
startPoint.x + widthAtStart * normalizedLateralVector.x,
startPoint.y + widthAtStart * normalizedLateralVector.y);
} else {
normalizedLateralVector = new geometry.Vector(-dirVector.y / len, dirVector.x / len);
trapezoidP0 = new geometry.Point(
startPoint.x + widthAtStart * normalizedLateralVector.x,
startPoint.y + widthAtStart * normalizedLateralVector.y);
trapezoidP1 = new geometry.Point(
startPoint.x - widthAtStart * normalizedLateralVector.x,
startPoint.y - widthAtStart * normalizedLateralVector.y);
trapezoidP2 = new geometry.Point(
endPoint.x - widthAtEnd * normalizedLateralVector.x,
endPoint.y - widthAtEnd * normalizedLateralVector.y);
trapezoidP3 = new geometry.Point(
endPoint.x + widthAtEnd * normalizedLateralVector.x,
endPoint.y + widthAtEnd * normalizedLateralVector.y);
}
let polygon = new Polygon([trapezoidP0, trapezoidP3, trapezoidP2, trapezoidP1]);
shapes.push(polygon);
switch (circleInclusive) {
case ink.SegmentCircleInclusive.None:
break;
case ink.SegmentCircleInclusive.Both:
shapes.push(new Circle({ x: startPoint.x, y: startPoint.y }, startPoint.pen.thickness / 2));
shapes.push(new Circle({ x: endPoint.x, y: endPoint.y }, endPoint.pen.thickness / 2));
break;
case ink.SegmentCircleInclusive.Start:
shapes.push(new Circle({ x: startPoint.x, y: startPoint.y }, startPoint.pen.thickness / 2));
break;
case ink.SegmentCircleInclusive.End:
shapes.push(new Circle({ x: endPoint.x, y: endPoint.y }, endPoint.pen.thickness / 2));
break;
default:
break;
}
return shapes;
}
}

111
server/src/canvas/mix.ts Normal file
Просмотреть файл

@ -0,0 +1,111 @@
// private clearCanvas() {
// this._context.clearRect(0, 0, this._width, this._height);
// }
// private draw() {
// //nothing to do if we are already drawing
// if (this._drawing || !this._active || this._index >= this._inkData.length) return;
// this._drawing = true;
// var strokeTime = this._inkData[this._index].t;
// //draw MixInk until we reach lesson time
// while (strokeTime <= this._currentTime && this._index < this._inkData.length) {
// this.processAction();
// if (++this._index >= this._inkData.length) {
// //MixInking finished
// this._drawing = false;
// //this.stop();
// return;
// }
// strokeTime = this._inkData[this._index].t;
// }
// this._drawing = false;
// }
// /***
// * processes the MixInk action from MixInk timeline for current index
// * returns: false if the MixInk has ended or doesn't need to be processed, true otherwise
// */
// private processAction() {
// var action = this._inkData[this._index];
// // Prepare pen
// this.updatePen(action.p, action.k === MixPlayerModels.MixInkActionKind.Draw);
// if (!this._pen) {
// this._logger.warn("MixInk.pen is not set on processAction");
// // bad pen data.
// return;
// }
// var shapes: Array<MixInk.IShape>;
// var scaledDrawPoint: MixInk.IPoint;
// var stylusPoint: MixInk.IStylusPoint;
// if (this._pen.b === MixPlayerModels.MixInkBlush.Highlighter) {
// var scaledWidth = Math.max(1, this._pen.w * this._scaleX);
// var scaledHeight = Math.max(2, this._pen.h * this._scaleY);
// scaledDrawPoint = { x: this._scaleX * action.x, y: this._scaleY * action.y };
// stylusPoint = new MixInk.StylusPoint(scaledDrawPoint, 1, scaledWidth, scaledHeight);
// //process MixInk action as per the kind
// switch (action.k) {
// case MixPlayerModels.MixInkActionKind.Draw:
// shapes = this.getHighliterShapes(this._lastStylusPoint, stylusPoint, false);
// this._lastStylusPoint = stylusPoint;
// break;
// case MixPlayerModels.MixInkActionKind.Move:
// shapes = this.getHighliterShapes(stylusPoint, stylusPoint, true);
// this._lastStylusPoint = stylusPoint;
// break;
// case MixPlayerModels.MixInkActionKind.Clear:
// this.clearCanvas();
// this._lastStylusPoint = null;
// break;
// default:
// this._logger.warn("MixInk.unsupported MixInk action. " + action.k);
// break;
// }
// } else {
// var scaledThickness = Math.max(1, this._pen.th * Math.max(0.5, this._scaleX));
// scaledDrawPoint = { x: this._scaleX * action.x, y: this._scaleY * action.y };
// stylusPoint = new MixInk.StylusPoint(
// scaledDrawPoint, scaledThickness,
// MixInkPlayer.defaultHighlighterTipWidth, MixInkPlayer.defaultHighlighterTipHeight);
// //process MixInk action as per the kind
// switch (action.k) {
// case MixPlayerModels.MixInkActionKind.Draw:
// shapes = this.getShapes(this._lastStylusPoint, stylusPoint, SegmentCircleInclusive.End);
// this._lastStylusPoint = stylusPoint;
// break;
// case MixPlayerModels.MixInkActionKind.Move:
// shapes = this.getShapes(stylusPoint, stylusPoint, SegmentCircleInclusive.End);
// this._lastStylusPoint = stylusPoint;
// break;
// case MixPlayerModels.MixInkActionKind.Clear:
// //clear the canvas
// this.clearCanvas();
// this._lastStylusPoint = null;
// break;
// default:
// this._logger.warn("MixInk.unsupported MixInk action. " + action.k);
// break;
// }
// }
// // Render shapes if there is any
// if (shapes) {
// shapes.forEach((item: MixInk.IShape) => {
// this._context.beginPath();
// item.render(this._context);
// this._context.closePath();
// this._context.fill();
// });
// }
// }

Просмотреть файл

@ -0,0 +1,17 @@
import * as geometry from "../geometry/index";
import { IShape } from "./shape";
export interface ICircle extends IShape {
center: geometry.IPoint;
radius: number;
}
export class Circle implements ICircle {
constructor(public center: geometry.IPoint, public radius: number) {
}
public render(context2D: CanvasRenderingContext2D) {
context2D.moveTo(this.center.x, this.center.y);
context2D.arc(this.center.x, this.center.y, this.radius, 0, Math.PI * 2);
}
}

Просмотреть файл

@ -0,0 +1,3 @@
export * from "./circle";
export * from "./polygon";
export * from "./shape";

Просмотреть файл

@ -0,0 +1,34 @@
import * as geometry from "../geometry/index";
import { IShape } from "./shape";
export interface IPolygon extends IShape {
points: geometry.IPoint[];
}
export class Polygon implements IPolygon {
private isDirty: boolean = true;
/**
* Constructs a new polygon composed of the given points. The polygon
* takes ownership of the passed in array of points.
*/
constructor(public points: geometry.IPoint[]) {
}
public render(context: CanvasRenderingContext2D) {
if (this.points.length === 0) {
return;
}
// Move to the first point
context.moveTo(this.points[0].x, this.points[0].y);
// Draw the rest of the segments
for (let i = 1; i < this.points.length; i++) {
context.lineTo(this.points[i].x, this.points[i].y);
}
// And then close the shape
context.lineTo(this.points[0].x, this.points[0].y);
}
}

Просмотреть файл

@ -0,0 +1,3 @@
export interface IShape {
render(context2D: CanvasRenderingContext2D);
}

Просмотреть файл

@ -18,12 +18,16 @@ export default class StickyNote {
this.div.style.top = "100px";
this.div.style.left = "100px";
this.gesture = new MSGesture();
this.gesture.target = this.div;
// tslint:disable-next-line:no-string-literal
if (window["MSGesture"]) {
this.gesture = new MSGesture();
this.gesture.target = this.div;
this.div.addEventListener("MSGestureChange", this.eventListener, false);
this.div.addEventListener("MSGestureTap", this.eventListener, false);
this.div.addEventListener("pointerdown", this.eventListener, false);
this.div.addEventListener("MSGestureChange", (evt) => this.eventListener(evt), false);
this.div.addEventListener("MSGestureTap", (evt) => this.eventListener(evt), false);
}
this.div.addEventListener("pointerdown", (evt) => this.eventListener(evt), false);
// insert the child into the DOM
parent.appendChild(this.div);
@ -49,7 +53,7 @@ export default class StickyNote {
}
public manipulateElement(e) {
// Uncomment the following code if you want to disable the built-in inertia
// Uncomment the following code if you want to disable the built-in inertia
// provided by dynamic gesture recognition
// if (false && (e.detail == e.MSGESTURE_FLAG_INERTIA))
@ -57,10 +61,10 @@ export default class StickyNote {
// manipulate only with touch
if (1 || e.pointerType === "touch") {
// Get the latest CSS transform on the element
// Get the latest CSS transform on the element
let m;
// Get the latest CSS transform on the element in MS Edge
// Get the latest CSS transform on the element in MS Edge
m = new WebKitCSSMatrix(window.getComputedStyle(this.gesture.target, null).transform);
if (m) {

Просмотреть файл

@ -141,3 +141,23 @@ export function parseURL(url) {
source: url,
};
}
// Following recomendations of https://developer.mozilla.org/en-US/docs/Web/Events/resize to
// throttle computationally expensive events
export function throttle(type: string, name: string, obj?: any) {
obj = obj || window;
let running = false;
obj.addEventListener(
type,
() => {
if (running) {
return;
}
running = true;
requestAnimationFrame(() => {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
});
};

Просмотреть файл

@ -70,7 +70,7 @@ function _inherits(subClass, superClass) {
}
}
// tslint:disable:only-arrow-functions:simplify to work around es6 classes to typescript difficulties
// tslint:disable:only-arrow-functions
// tslint:disable-next-line:variable-name
let VideoBlot: any = function (_BlockEmbed3) {
// tslint:disable-next-line:no-var-keyword no-shadowed-variable
@ -126,7 +126,7 @@ let host = new ivy.Host({
secret: "IvyBearerToken",
});
// tslint:disable:only-arrow-functions:simplify to work around es6 classes to typescript difficulties
// tslint:disable:only-arrow-functions
// tslint:disable-next-line:variable-name
let ChartBlot: any = function (_BlockEmbed3) {
// tslint:disable-next-line:no-var-keyword no-shadowed-variable
@ -220,7 +220,7 @@ export function connect(id: string, sync: boolean) {
let range = quill.getSelection(true);
quill.insertText(range.index, "\n", Quill.sources.USER);
// Disable rules for simplicity with Ivy input format
// Disable rules for simplicity with Ivy input format
let chartDef = {
chartTitleEdge: 1,
chartTitlePosition: 1,

Просмотреть файл

@ -1,5 +1,6 @@
import * as _ from "lodash";
import * as nconf from "nconf";
import * as ink from "ot-ink";
import * as redis from "redis";
import * as richText from "rich-text";
import * as ShareDB from "sharedb";
@ -25,10 +26,12 @@ subOptions.return_buffers = true;
let client = redis.createClient(redisPort, redisHost, pubOptions);
let observer = redis.createClient(redisPort, redisHost, subOptions);
// Register rich type as one of our OT formats
// Register our OT formats
ShareDB.types.register(richText.type);
ShareDB.types.register(ink.type);
let db = new ShareDBMongo("mongodb://mongodb:27017");
let mongoConnectionString = nconf.get("mongo:connectionString");
let db = new ShareDBMongo(mongoConnectionString);
let pubsub = new ShareDBRedisPub({ client, observer });
let shareDb = new ShareDB({
db,

Просмотреть файл

@ -72,7 +72,7 @@ class TableService implements ITableService {
templateUrl: "templates/document.component.html",
})
export class DocumentComponent implements OnInit {
// Loading flag for the document
// Loading flag for the document
public loaded: boolean = false;
public url: string;

Просмотреть файл

@ -17,7 +17,7 @@ export class InteractiveDocumentFrameComponent implements AfterViewInit {
}
public ngAfterViewInit() {
// Load in the bound view
// Load in the bound view
let iframe = this.elementRef.nativeElement.querySelector("iframe");
iframe.setAttribute("src", this.url);
}

Просмотреть файл

@ -1,4 +1,4 @@
// The require here is deliberate. The TypeScript browserify will elide this line if
// The require here is deliberate. The TypeScript browserify will elide this line if
// a standard import given no modules are used. We are including the polyfills so we pick
// them up with our browserify package.

Просмотреть файл

@ -80,7 +80,7 @@ export class PostMessageHost implements IPostMessageHost {
* Client is requesting to connect to the server
*/
private processConnect(event: MessageEvent, message: IPacket): void {
// Ignore connection events if we aren"t listening
// Ignore connection events if we aren"t listening
if (!this.connectionCallback) {
// tslint:disable-next-line:no-console
console.log("Client is attempting to connect but the server is not listening");
@ -106,7 +106,7 @@ export class PostMessageHost implements IPostMessageHost {
}
/**
* Retrieves a new number to represent a connection
* Retrieves a new number to represent a connection
*/
private getConnectionId(): number {
return this.nextConnectionId++;

Просмотреть файл

@ -55,7 +55,7 @@ export class PostMessageSocket implements IPostMessageSocket {
public processMessageReceipt(message: IMessage) {
// reject the message if no listener is defined.
// Alternatively if needed we could buffer messages until one is defined. But the former is simpler.
// Alternatively if needed we could buffer messages until one is defined. But the former is simpler.
if (!this.listener) {
this.postMessage(MessageType.Failure, { message: "No handler defined" });
return;

Просмотреть файл

@ -61,7 +61,7 @@ router.get("/", (req: express.Request, response: express.Response) => {
if (error) {
return reject(error);
} else {
// MSFT strings are in UTC but don"t place the UTC marker in the date string -
// MSFT strings are in UTC but don"t place the UTC marker in the date string -
// convert to this format to standardize the input to CalendarEvent
let microsoftResults: ICalendarEvent[] = body.value.map((item) => {
let loc = item.location ? item.location.displayName : "";
@ -136,7 +136,8 @@ router.get("/", (req: express.Request, response: express.Response) => {
});
router.delete("/:provider/:id", (req: express.Request, response: express.Response) => {
// tslint:disable:no-string-literal:easier access to param data
// easier access to param data
// tslint:disable:no-string-literal
let provider = req.params["provider"];
let eventId = req.params["id"];
// tslint:enable:no-string-literal

Просмотреть файл

@ -4,12 +4,15 @@ import { defaultPartials } from "./partials";
let router = express.Router();
router.get("/:id", (req: express.Request, response: express.Response) => {
// tslint:disable-next-line:no-string-literal
let id = req.params["id"];
response.render(
"canvas",
{
// tslint:disable-next-line:no-string-literal
id: `Canvas - ${req.params["id"]}`,
id,
partials: defaultPartials,
title: `Canvas - ${id}`,
});
});

Просмотреть файл

@ -3,7 +3,8 @@ import * as passport from "passport";
import * as accounts from "../accounts";
import * as authOptions from "./authOptions";
// tslint:disable-next-line:no-var-requires:simpler code path and module not setup for import
// simpler code path and module not setup for import
// tslint:disable-next-line:no-var-requires
let ensureLoggedIn = require("connect-ensure-login").ensureLoggedIn;
let router = express.Router();

Просмотреть файл

@ -8,8 +8,8 @@ import * as request from "request";
import * as accounts from "../accounts";
import { defaultPartials } from "./partials";
// tslint:disable:no-console:Server side code
// tslint:disable:max-line-length:TODO split get into helper functions
// tslint:disable:no-console
// tslint:disable:max-line-length
let router = express.Router();

Просмотреть файл

@ -1,7 +1,8 @@
import * as express from "express";
import { defaultPartials } from "./partials";
// tslint:disable-next-line:no-var-requires:simpler code path and module not setup for import
// simpler code path and module not setup for import
// tslint:disable-next-line:no-var-requires
let ensureLoggedIn = require("connect-ensure-login").ensureLoggedIn;
let router = express.Router();

Просмотреть файл

@ -3,7 +3,8 @@ import * as _ from "lodash";
import { IUser } from "../accounts";
import { defaultPartials } from "./partials";
// tslint:disable-next-line:no-var-requires:simpler code path and module not setup for import
// simpler code path and module not setup for import
// tslint:disable-next-line:no-var-requires
let ensureLoggedIn = require("connect-ensure-login").ensureLoggedIn;
let router = express.Router();

Просмотреть файл

@ -15,7 +15,7 @@ class View implements IView {
}
class Views implements IViews {
// tslint:disable:variable-name:JSON format contains _
// tslint:disable:variable-name
public _links: { [rel: string]: ILink | ILink[] };
public _embedded: { [rel: string]: View[] };
// tslint:enable:variable-name

Просмотреть файл

@ -5,7 +5,7 @@ import * as nconf from "nconf";
import * as path from "path";
// Setup the configuration system - pull arguments, then environment letiables
nconf.argv().env().file(path.join(__dirname, "../config.json")).use("memory");
nconf.argv().env(<any> "__").file(path.join(__dirname, "../config.json")).use("memory");
/**
* Module dependencies.

Просмотреть файл

@ -33,10 +33,14 @@
<button id="clearButton" value="clear">Clear</button>
<button id="testButton">Reco</button>
<button id="turnOnInk">ink</button>
<button id="replay">Replay</button>
</div>
{{/content}}
{{$scripts}}
<script src="/dist/canvas/canvas.js"></script>
<script>
canvas.initialize("{{id}}");
</script>
{{/scripts}}
{{/layout}}