Pull over Mix ink definition and storage
This commit is contained in:
Родитель
f240370aae
Коммит
49e4e64bea
|
@ -0,0 +1,22 @@
|
|||
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
|
||||
|
||||
# And set the default command to start the server
|
||||
CMD ["npm", "start"]
|
|
@ -5,7 +5,7 @@ services:
|
|||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- .:/usr/src/server
|
||||
- .:/usr/src/
|
||||
depends_on:
|
||||
- mongodb
|
||||
- redis
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* 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;
|
||||
|
||||
// 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,
|
||||
}
|
|
@ -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"]
|
|
@ -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
|
||||
|
|
|
@ -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,7 +8,7 @@ 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;
|
||||
|
|
|
@ -12,17 +12,18 @@ sharedb.types.register(otInk.type);
|
|||
|
||||
// tslint:disable:no-console
|
||||
|
||||
// App Class
|
||||
export class App {
|
||||
/**
|
||||
* Canvas app
|
||||
*/
|
||||
export default class Canvas {
|
||||
public ink: InkCanvas;
|
||||
|
||||
public handleKeys: boolean = true;
|
||||
public stickyCount: number = 0;
|
||||
|
||||
constructor() {
|
||||
|
||||
// register all of the different handlers
|
||||
let p: HTMLElement = utils.id("hitPlane");
|
||||
let p = document.getElementById("hitPlane");
|
||||
this.ink = new InkCanvas(p);
|
||||
|
||||
window.addEventListener("keydown", (evt) => this.keyPress(evt), false);
|
||||
|
@ -93,7 +94,7 @@ export class App {
|
|||
this.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
|
||||
// tslint:disable-next-line:prefer-for-of
|
||||
for (let i = 0; i < stickies.length; i++) {
|
||||
board.removeChild(stickies[i]);
|
||||
}
|
||||
|
@ -137,46 +138,3 @@ export class App {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// create the new app
|
||||
$("document").ready(() => {
|
||||
let appObject = new App();
|
||||
let sticky = new StickyNote(utils.id("content"));
|
||||
let mainBoard = new BackBoard(appObject, "hitPlane");
|
||||
// id("ToolBar").appendChild(new ToolBarButton("images/icons/pencil.svg").click(appObject.clear).elem());
|
||||
|
||||
let doc = connection.get("canvas", id);
|
||||
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: appObject });
|
||||
|
||||
doc.on("op", (op, source) => {
|
||||
if (source === appObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the canvas
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -4,202 +4,224 @@ import * as utils from "./utils";
|
|||
// tslint:disable:max-classes-per-file
|
||||
|
||||
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);
|
||||
// constructor
|
||||
constructor(parent: HTMLElement) {
|
||||
// setup canvas
|
||||
this.canvas = document.createElement("canvas");
|
||||
this.canvas.classList.add("drawSurface");
|
||||
parent.appendChild(this.canvas);
|
||||
|
||||
// get context
|
||||
this.context = this.canvas.getContext("2d");
|
||||
// get context
|
||||
this.context = this.canvas.getContext("2d");
|
||||
|
||||
let w: number = this.canvas.offsetWidth;
|
||||
let h: number = this.canvas.offsetHeight;
|
||||
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);
|
||||
|
||||
// set the width and height specified through CSS
|
||||
this.canvas.setAttribute("width", w.toString());
|
||||
this.canvas.setAttribute("height", h.toString());
|
||||
// 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
|
||||
|
||||
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);
|
||||
}
|
||||
// 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 tempEraseMode() {
|
||||
}
|
||||
|
||||
public restoreMode() {
|
||||
}
|
||||
|
||||
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) {
|
||||
this.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.
|
||||
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.context.beginPath();
|
||||
this.context.moveTo(pt.rawPosition.x, pt.rawPosition.y);
|
||||
|
||||
let pressureWidth = evt.pressure * 15;
|
||||
this.context.lineWidth = pressureWidth;
|
||||
evt.returnValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
public handlePointerMove(evt) {
|
||||
if (evt.pointerId === this.penID) {
|
||||
let pt = new EventPoint(evt);
|
||||
let w = 8;
|
||||
let h = 8;
|
||||
|
||||
if (evt.pointerType === "touch") {
|
||||
this.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);
|
||||
this.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") {
|
||||
this.context.strokeStyle = "rgba(0, 50, 0, 1)";
|
||||
w = w * (0.1 + evt.pressure);
|
||||
h = h * (0.1 + evt.pressure);
|
||||
} else { // just mouse
|
||||
this.context.strokeStyle = "rgba(250, 0, 0, 0.5)";
|
||||
}
|
||||
|
||||
this.context.lineWidth = w;
|
||||
this.context.lineTo(evt.clientX, evt.clientY);
|
||||
this.context.stroke();
|
||||
evt.returnValue = false;
|
||||
|
||||
// let pts = evt.intermediatePoints;
|
||||
// for (let i = pts.length - 1; i >= 0 ; i--) {
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public handlePointerUp(evt) {
|
||||
if (evt.pointerId === this.penID) {
|
||||
this.penID = -1;
|
||||
let pt = new EventPoint(evt);
|
||||
// ic.context.lineTo(pt.rawPosition.x, pt.rawPosition.y);
|
||||
// ic.context.stroke();
|
||||
this.context.closePath();
|
||||
this.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) {
|
||||
if (evt.pointerId === this.penID) {
|
||||
let pt = new EventPoint(evt);
|
||||
this.context.lineTo(pt.rawPosition.x, pt.rawPosition.y);
|
||||
this.context.stroke();
|
||||
this.context.closePath();
|
||||
this.penID = -1;
|
||||
this.renderAllStrokes();
|
||||
public restoreMode() {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public handleTap(evt) {
|
||||
// Anchor and clear any current selection.
|
||||
if (this.anySelected()) {
|
||||
this.anchorSelection();
|
||||
this.renderAllStrokes();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
if (!this.anySelected()) {
|
||||
this.selectAll();
|
||||
this.inkMode();
|
||||
public renderAllStrokes() {
|
||||
}
|
||||
|
||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
public anchorSelection() {
|
||||
}
|
||||
|
||||
this.renderAllStrokes();
|
||||
utils.displayStatus("");
|
||||
utils.displayError("");
|
||||
}
|
||||
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) {
|
||||
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.context.beginPath();
|
||||
this.context.moveTo(pt.rawPosition.x, pt.rawPosition.y);
|
||||
|
||||
let pressureWidth = evt.pressure * 15;
|
||||
this.context.lineWidth = pressureWidth;
|
||||
evt.returnValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
public handlePointerMove(evt) {
|
||||
if (evt.pointerId === this.penID) {
|
||||
let pt = new EventPoint(evt);
|
||||
let w = 8;
|
||||
let h = 8;
|
||||
|
||||
if (evt.pointerType === "touch") {
|
||||
this.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);
|
||||
this.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") {
|
||||
this.context.strokeStyle = "rgba(0, 50, 0, 1)";
|
||||
w = w * (0.1 + evt.pressure);
|
||||
h = h * (0.1 + evt.pressure);
|
||||
} else { // just mouse
|
||||
this.context.strokeStyle = "rgba(250, 0, 0, 0.5)";
|
||||
}
|
||||
|
||||
this.context.lineWidth = w;
|
||||
this.context.lineTo(evt.clientX, evt.clientY);
|
||||
this.context.stroke();
|
||||
evt.returnValue = false;
|
||||
|
||||
// let pts = evt.intermediatePoints;
|
||||
// for (let i = pts.length - 1; i >= 0 ; i--) {
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public handlePointerUp(evt) {
|
||||
if (evt.pointerId === this.penID) {
|
||||
this.penID = -1;
|
||||
let pt = new EventPoint(evt);
|
||||
// ic.context.lineTo(pt.rawPosition.x, pt.rawPosition.y);
|
||||
// ic.context.stroke();
|
||||
this.context.closePath();
|
||||
this.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) {
|
||||
if (evt.pointerId === this.penID) {
|
||||
let pt = new EventPoint(evt);
|
||||
this.context.lineTo(pt.rawPosition.x, pt.rawPosition.y);
|
||||
this.context.stroke();
|
||||
this.context.closePath();
|
||||
this.penID = -1;
|
||||
this.renderAllStrokes();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public handleTap(evt) {
|
||||
// Anchor and clear any current selection.
|
||||
if (this.anySelected()) {
|
||||
this.anchorSelection();
|
||||
this.renderAllStrokes();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
if (!this.anySelected()) {
|
||||
this.selectAll();
|
||||
this.inkMode();
|
||||
}
|
||||
|
||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
this.renderAllStrokes();
|
||||
utils.displayStatus("");
|
||||
utils.displayError("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the canvas
|
||||
*/
|
||||
private clearCanvas() {
|
||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
private redraw() {
|
||||
this.clearCanvas();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
// });
|
||||
// }
|
||||
// }
|
|
@ -53,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))
|
||||
|
@ -61,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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче