TouchDevelop/officemix/officemix.ts

1139 строки
42 KiB
TypeScript

///<reference path='labs-1.0.4.d.ts'/>
// full docs: https://labsjs.blob.core.windows.net/sdk/LabsJS-1.0.4/labs.html
// use http://labsjs.blob.core.windows.net/sdk/LabsJS-1.0.4/labshost.html for testing
/*
This host acts as a proxy between the labs APIs and 2 TouchDevelop web apps: the editor web app and the view web app.
# Editor App
The editor app provides a way to pick the web app to execute. This app is provided by TouchDevelop.
* script selected, IScriptMessage
{ kind : 'script',
data : IScript
releaseid?: ... }
Optionaly, the releaseid can be specified as a field
# View App
Each request, and optional response, has the following shape,
{ kind : string, data : any }
The view app may be any TouchDevelop app. Additionally some apps may also decide to store state.
* 'connect' : returns 'mode' as 'edit' or 'view'.
* 'labGetState' : request state stored in current lab
{ kind : 'labGetState' }
Upon receiving this message, the state (IStateMessage) is sent to the web app.
{ kind : 'labGetState', data : state }
* 'labSetState' : app wants state to be saved (IStateMessage)
{ kind : 'setState', data : state }
*/
module TDev {
var _origin = 'https://www.touchdevelop.com';
export var _storeUrl = 'https://www.touchdevelop.com/users/TouchDevelopSamples/touchdevelopedu/';
var _webAppRootUrl = 'https://www.touchdevelop.com/';
export var _dbg = false;
export var _standAloneApp = false;
var _editor: Labs.LabEditor;
var _lab: Labs.LabInstance;
var _currentApp: IScriptMessage;
var _isPreviewMode: boolean = false;
var _leaderboardIndex: number = 1;
var _tutorialIndex: number = 2;
var _indexMap: Map[] = [{ value: 0, mapped: 0 }];
var _isStore = false;
var _onDeactivate: number = 0;
export interface Map {
value: number;
mapped: number;
}
export interface IMessage {
kind: string;
data?: any;
}
export interface IScript {
id: string;
name: string;
userid: string;
releaseId?: string;
}
export interface IStateMessage extends IMessage {
data: any;
}
export interface IScriptMessage extends IMessage {
data: IScript;
releaseid?: string;
}
export interface IMessageToApp {
kind: string;
code: string;
data?: any;
}
// data that gets serialized in a component
export interface IComponentData {
auto: boolean;
userData: any;
indexMap?: Map[];
}
export interface IHints {
[type: string]: Labs.Core.IValue[];
}
export interface IConfigData {
name: string;
index: number;
data: IComponentData;
values?: IHints;
maxScore?: number;
hasAnswer?: boolean;
answer?: any;
choices?: Labs.Components.IChoice[];
}
export interface IActivityConfigData extends IConfigData { }
export interface IInputConfigData extends IConfigData {
hasAnswer: boolean;
answer: string;
maxScore: number;
}
export interface IChoiceConfigData extends IConfigData {
choices: Labs.Components.IChoice[];
hasAnswer: boolean;
answer: any;
maxScore: number;
}
export interface IAttemptData {
index: number;
score: number;
answer: any;
isComplete: boolean;
hintIndex?: number;
}
export interface IActivityAttemptData {
index: number;
hintIndex?: number;
}
export interface IInputAttemptData extends IAttemptData{
answer: string;
}
export interface IChoiceAttemptData extends IAttemptData {
answer: string[];
}
export interface IChoiceAttemptDataNumAns extends IAttemptData {
answer: number[];
}
interface IAttemptCallback<T1,T2> {
(attempt: T1, data: T2): void;
}
export function handleError(err: any) {
log('error: ' + err);
return err;
}
function getAppUrl(data: IScriptMessage) {
var url = _webAppRootUrl + "users/@" + data.data.userid + "-/@" + data.data.id + "-";
if (data.releaseid) url += "?releaseid=" + data.releaseid;
log("td: AppUrl - " + url);
return url;
}
function getTutorialUrl(data: IScriptMessage) {
var url = _webAppRootUrl + "app/beta?temporaryStorage=1#hub:follow:" + data.data.id + ":officemix";
log("td: TutUrl - " + url);
return url;
}
export function setUrl(url: string) {
if (url == _storeUrl) _isStore = true;
else _isStore = false;
var frame = <HTMLIFrameElement>document.getElementById("tdFrame");
frame.src = url;
log(" td: new src= " + frame.src);
}
function loadApp(data: IScriptMessage, mode: Labs.Core.LabMode) {
log("td: data recieved in load app = " + JSON.stringify(data));
if(!_standAloneApp) show("another");
_currentApp = data;
if ((!data.data.userid) || data.data.userid == "") setUrl(getTutorialUrl(data));
else setUrl(getAppUrl(data));
document.getElementById("AppStatus").innerText = data.data.name;
updateChrome();
}
function loadTutorial(data: IScriptMessage, mode: Labs.Core.LabMode) {
log("td: setting up tutorial metadata");
if(!_standAloneApp) show("another");
_currentApp = data;
document.getElementById("AppStatus").innerText = "Selected " + data.data.name;
updateChrome();
}
function loadStore() {
_currentApp = undefined;
setUrl(_storeUrl);
elt("AppStatus").innerText = "Select an App";
updateChrome();
}
function setHeight(id: string, htPercent: number, htPixel: number) {
elt(id).style.height = "calc(" + htPercent + "% - " + htPixel + "px)";
elt(id).style.height = "-moz-calc(" + htPercent + "% - " + htPixel + "px)";
elt(id).style.height = "-webkit-calc(" + htPercent + "% - " + htPixel + "px)";
}
export function updateChrome() {
log("td: updating chrome");
if (_editor) {
show("preview");
hide("edit");
show("chrome");
setHeight("mainContent", 100, 50);
}
else {
hide("preview");
show("edit");
if (_isPreviewMode) {
show("chrome");
setHeight("mainContent", 100, 50);
}
else {
hide("chrome");
setHeight("mainContent", 100, 10);
}
}
log("td: disablling preview mode");
_isPreviewMode = false;
}
function currentMode(): Labs.Core.LabMode {
if (_editor) {
return Labs.Core.LabMode.Edit;
}
return Labs.Core.LabMode.View;
}
function createHints(values: string[]): IHints {
return {
hints: values.map(v => <Labs.Core.IValue>{
isHint: true,
value: v
})
}
}
function addHints(index: number, hints: string[]): void {
_editor.getConfiguration((err, config) => {
if (err) return postError("addHints", err);
if (config.components[index]) {
config.components[index].values = createHints(hints);
_editor.setConfiguration(config, (err,unused) => {
if (err) postError("addHints", err);
else postSuccess("addHints");
});
}
else postError("addHints", "invalid config index");
});
}
function createActivityComponent(data: IActivityConfigData): Labs.Components.IActivityComponent {
var comp : Labs.Components.IActivityComponent = {
name: data.name,
type: Labs.Components.ActivityComponentType,
secure: false,
values: data.values,
data: data.data
};
return comp;
}
function createChoiceComponent(data: IChoiceConfigData): Labs.Components.IChoiceComponent {
var comp: Labs.Components.IChoiceComponent = {
name: data.name,
type: Labs.Components.ChoiceComponentType,
secure: false,
choices: data.choices,
maxAttempts: 0,
values: data.values,
maxScore: data.maxScore,
hasAnswer: data.hasAnswer,
answer: data.answer,
timeLimit: 0,
data: data.data
};
return comp;
}
function createInputComponent(data: IInputConfigData): Labs.Components.IInputComponent {
var comp: Labs.Components.IInputComponent = {
name: data.name,
type: Labs.Components.InputComponentType,
secure: false,
values: data.values,
maxScore: data.maxScore,
hasAnswer: data.hasAnswer,
answer: data.answer,
timeLimit : 0,
data: data.data
};
return comp;
}
function getAppComponentData(componentNumber: number) {
var comp = (<Labs.Components.ActivityComponentInstance>_lab.components[componentNumber]);
if (comp && comp.component && comp.component.data) {
var cdata = <IComponentData>comp.component.data;
if (cdata.userData) return postData('labGetComponentConfig', cdata.userData );
}
postError("labGetComponentConfig", "invalid component number");
}
function completeGetAppConfigurationAsync(comp: Labs.Core.IComponent) {
if (!comp) return postError("editorGetConfig","invalid component");
log("td: sending config: ");
postData('editorGetConfig',(<IComponentData>comp.data).userData);
}
function getAppConfigurationAsync(configNumber: number): void {
var cn = configNumber;
log('td: get config ' + configNumber);
_editor.getConfiguration((err, config) => {
if (err) return postError("editorGetConfig",err);
log("td: component size: " + config.components.length);
if (config.components[cn]) {
log("td: " + JSON.stringify(config.components[cn]));
completeGetAppConfigurationAsync(config.components[cn]);
return;
}
postError("editorGetConfig","invalid config number");
});
}
function saveAppConfiguration(data: IConfigData, type: string) {
if (!_editor) return postError("editorSetConfig","while trying to save configuration, editor not opened");
log("td: saving app config: " + JSON.stringify(data));
var newComp: Labs.Core.IComponent = undefined;
var configNumber: number = data.index;
switch (type) {
case "activity":
newComp = createActivityComponent(<IActivityConfigData>data);
break;
case "input":
newComp = createInputComponent(<IInputConfigData>data);
break;
case "choice":
newComp = createChoiceComponent(<IChoiceConfigData>data);
break;
default:
return postError("editorSetConfig","invalid component type");
}
_editor.getConfiguration((err, config) => {
if (err) return postError("editorSetConfig",err);
log("td: component size: " + config.components.length);
config.components[configNumber] = newComp;
config.components[0].data.indexMap = _indexMap;
_editor.setConfiguration(config, (err, unused) => {
if (err) return postError("editorSetConfig",err);
log("td: configuration modified at level " + configNumber);
postSuccess("editorSetConfig");
});
});
}
function saveEditorConfiguration(data: IScriptMessage, leaderboard: IInputConfigData, tutorial: IActivityConfigData) {
if (!_editor) {
log("td: error while trying to save configuration, editor not opened");
return;
}
if (!data) {
log("td: clearing config");
var comp = createActivityComponent(<IActivityConfigData>{ name: undefined, data: undefined });
_editor.setConfiguration({
appVersion: { major: 1, minor: 0 },
components: [comp],
analytics: undefined,
timeline: undefined,
name: undefined
}, (err, unused) => {
if (err) return handleError(err);
log("td: configuration cleared");
});
return;
}
log("td: saving configuration");
log("td: datakind " + data.kind + " id:" + data.data.id + " name:" + data.data.name + " userid:" + data.data.userid);
var name = undefined;
if (data.data.name) name = data.data.name;
var editComp = createActivityComponent(<IActivityConfigData>{
name: name,
data: <IComponentData>{ auto: false, userData: data, indexMap: _indexMap }
});
var leadComp = createInputComponent(leaderboard);
var components: Labs.Core.IComponent[] = [editComp, leadComp];
if (tutorial) {
var tutComp = createActivityComponent(tutorial);
components.push(tutComp);
}
_editor.setConfiguration({
appVersion: { major: 1, minor: 0 },
components: components,
analytics: undefined,
timeline: undefined,
name : name
}, (err, unused) => {
if (err) return handleError(err);
log("td: configuration saved");
});
}
//Sample URL for standalone app: officemix.html?standAloneApp&id=lhvra&userid=pboj&name=rachit Optional:[&isTutorial][&releaseid=123]
//For tutorial userid not required
function queryUrl(param: string) : string{
var params: string[] = window.location.search.substring(1).split("&");
while (Array.isArray(params) && params.length) {
var pairs : string[] = params.shift().split("=");
if (pairs[0] === param) return pairs[1];
}
return undefined;
}
function loadStandAloneAppConfig() {
var data: IScriptMessage = {
kind: "scriptSelected",
data: {
id: queryUrl("id"),
name: queryUrl("name"),
userid: queryUrl("userid")
},
releaseid: queryUrl("releaseid")
};
log("td: data: " + JSON.stringify(data));
if (/isTutorial/.test(window.location.href)) receiveTutorialSelected(data);
else receiveScriptSelected(data);
}
function loadStoreOrApp() {
if (_editor) {
log("td: found editor");
_editor.getConfiguration((err, config) => {
log("td: erditor config found");
if (err) return handleError(err);
if (config && config.components[0] && config.components[0].data) {
log("td: lab-data found!");
_indexMap = ((<IComponentData>config.components[0].data).indexMap);
loadApp((<IScriptMessage>(<IComponentData>config.components[0].data).userData), currentMode());
return;
}
else {
if (_standAloneApp) {
log("td: Stand Alone app detected.");
loadStandAloneAppConfig();
}
else {
log("td: no lab data found. Restoring the store.");
loadStore();
}
}
});
}
else if (_lab) {
log("td: lab found");
var comp = (<Labs.Components.ActivityComponentInstance>_lab.components[0]);
if (comp && comp.component && <IComponentData>comp.component.data && (<IComponentData>comp.component.data).userData) {
var appConfig = <IScriptMessage>((<IComponentData>comp.component.data).userData);
log("td: app-config: " + JSON.stringify(appConfig));
_indexMap = ((<IComponentData>comp.component.data).indexMap);
loadApp(<IScriptMessage>appConfig, currentMode());
return;
}
else {
if (_standAloneApp) {
log("td: Stand Alone app detected.");
loadStandAloneAppConfig();
}
else {
log("td: Select app to load from the store before starting lab. Restoring the store");
loadStore();
return;
}
}
}
else {
log("td: no lab/editor found.");
}
}
function saveState(state: any, callback: Labs.Core.ILabCallback<void>) {
if (!_lab) {
log("td: error while trying to save state, lab not opened");
return;
}
_lab.setState(state, (err, unused) => {
log("td: state saved");
callback(err, unused);
});
}
function createAttempt(index : number): void {
var compA = (<Labs.ComponentInstance<Labs.Components.ActivityComponentAttempt>>_lab.components[index]);
if (!compA) return postError("attemptComplete","invalid component index");
compA.createAttempt((err, attempt) => {
if (err) return postError("attemptComplete", err);
else postSuccess("attemptComplete");
});
}
function completeActivityAttempt(attempt: Labs.Components.ActivityComponentAttempt) {
attempt.complete((err, dummy) => {
if (err) return postError("attemptComplete", err);
else postSuccess("attemptComplete");
});
}
function handleActivityAttempt(data: IActivityAttemptData, callback: IAttemptCallback<Labs.Components.ActivityComponentAttempt, IActivityAttemptData>) {
var compA = (<Labs.Components.ActivityComponentInstance>_lab.components[data.index]);
if (!compA) return postError("attemptComplete","invalid component index "+data.index);
//if (compA.component.type != Labs.Components.ActivityComponentInstanceType) return attemptCompleteFail("component type mismatch");
compA.getAttempts((attempts: Labs.Components.ActivityComponentAttempt[]) => {
if (attempts && attempts.length) {
attempts[attempts.length - 1].resume(() => {
callback(attempts[attempts.length - 1], data);
});
}
else {
compA.createAttempt((err, attempt) => {
if (err) return postError("attemptComplete",err);
attempt.resume(() => {
callback(attempt, data);
});
});
}
});
}
function completeInputAttempt(attempt: Labs.Components.InputComponentAttempt, data: IInputAttemptData) {
log("td: complete input attempt");
attempt.submit(
new Labs.Components.InputComponentAnswer(data.answer),
new Labs.Components.InputComponentResult(data.score, data.isComplete),
(err, submission) => {
if (err) return postError("attemptComplete",err);
postSuccess("attemptComplete");
});
}
function handleInputAttempt(data: IInputAttemptData, callback: IAttemptCallback<Labs.Components.InputComponentAttempt,IInputAttemptData>) {
var compI = < Labs.Components.InputComponentInstance>_lab.components[data.index];
if (!compI) return postError("attemptComplete","invalid component index");
if (compI.component.type != Labs.Components.InputComponentInstanceType) return postError("attemptComplete","component type mismatch");
compI.getAttempts((attempts: Labs.Components.InputComponentAttempt[]) => {
log("td: checking for incomplete attempts");
if (attempts && attempts.length) {
attempts[attempts.length - 1].resume((err) => {
if (err) return postError("attemptComplete",err);
callback(attempts[attempts.length - 1], data);
});
}
else {
compI.createAttempt((err, attempt) => {
if (err) return postError("attemptComplete",err);
attempt.resume(() => {
callback(attempt, data);
});
});
}
});
}
function completeChoiceAttempt(attempt: Labs.Components.ChoiceComponentAttempt, data: IChoiceAttemptDataNumAns) {
log("td: complete input attempt");
attempt.submit(
new Labs.Components.InputComponentAnswer(data.answer),
new Labs.Components.InputComponentResult(data.score, data.isComplete),
(err, submission) => {
if (err) return handleError(err);
postSuccess("attemptComplete");
});
}
function handleChoiceAttempt(data: IChoiceAttemptData, callback: IAttemptCallback<Labs.Components.ChoiceComponentAttempt,IChoiceAttemptDataNumAns>) {
var answer: string[] = data.answer;
var answerID: number[] = [];
var compC = < Labs.Components.ChoiceComponentInstance>_lab.components[data.index];
if (!compC) return postError("attemptComplete","invalid component index");
if (compC.component.type != Labs.Components.ChoiceComponentInstanceType) return postError("attemptComplete","component type mismatch");
if (compC && compC.component && compC.component.choices && answer.length) {
for (var it in answer) {
var choices: Labs.Components.IChoice[] = compC.component.choices;
for (var it2 in choices) {
if (answer[it] == choices[it2].name) {
answerID.push(parseInt(choices[it2].id));
break;
}
}
}
var dataNumAns: IChoiceAttemptDataNumAns = { index: data.index, score: data.score, answer: answerID, isComplete: data.isComplete };
if (data.hintIndex) dataNumAns.hintIndex = data.hintIndex;
if (answer.length == answerID.length) {
compC.getAttempts((attempts: Labs.Components.ChoiceComponentAttempt[]) => {
log("td: checking for incomplete attempts");
if (attempts && attempts.length) {
attempts[attempts.length - 1].resume((err) => {
if (err) return postError("attemptComplete",err);
callback(attempts[attempts.length - 1], dataNumAns);
});
}
else {
compC.createAttempt((err, attempt) => {
if (err) postError("attemptComplete",err);
attempt.resume(() => {
callback(attempt, dataNumAns);
});
});
}
});
}
}
return handleError("invalid component");
}
function handleTutorialComplete() : void{
var comp = <Labs.Components.ActivityComponentInstance>_lab.components[1];
if (comp.component && comp.component.data && (<IComponentData>comp.component.data).auto) {
var data: IActivityAttemptData = { index: _tutorialIndex };
handleActivityAttempt(data, completeActivityAttempt);
}
else {
log("td: User config already set. Ignoring leaderboard message");
}
}
function handleLeaderboardComplete(score: number) : void{
var comp = <Labs.Components.ActivityComponentInstance>_lab.components[1];
if (comp.component && comp.component.data && (<IComponentData>comp.component.data).auto) {
var leaderboardAttemptData: IInputAttemptData = {
index: _leaderboardIndex,
score: score,
answer: score.toString(),
isComplete: true
};
handleInputAttempt(<IInputAttemptData>leaderboardAttemptData, completeInputAttempt);
}
else {
log("td: User config already set. Ignoring leaderboard message");
}
}
function postError(kind: string, err: string) {
handleError(err);
postMessageToApp({ kind: kind, code: "failed" });
}
function postSuccess(kind: string) {
postMessageToApp({ kind: kind, code: "success" });
}
function postData(kind: string, data: any) {
postMessageToApp({ kind: kind, code: "success", data: data });
}
function postMessageToApp(msg: IMessageToApp) {
log('td: post message to app: ' + JSON.stringify(msg));
var frame = <HTMLIFrameElement>document.getElementById("tdFrame");
frame.contentWindow.postMessage(msg, _origin);
log("posted to app")
}
function setDefaultConfigs(data: IScriptMessage, isTutortial: boolean) {
log("td: setting default configs");
_indexMap = [{ value: 0, mapped: 0 }];
if (currentMode() == Labs.Core.LabMode.Edit) {
var leaderboardData: IInputConfigData = {
name: "Leaderboard",
data: {
auto: true,
userData: ""
},
hasAnswer: false,
answer: "",
maxScore: 1,
index: _leaderboardIndex
};
_indexMap.push({value: _leaderboardIndex, mapped:_leaderboardIndex});
var tutorialData: IActivityConfigData = {
name: "Tutorial",
data: {
auto: true,
userData: ""
},
index: _tutorialIndex
};
if (isTutortial) _indexMap.push({ value: _tutorialIndex, mapped: _tutorialIndex });
else tutorialData = undefined;
saveEditorConfiguration(data, leaderboardData, tutorialData);
}
}
function receiveScriptSelected(data: IScriptMessage) {
log("selected app " + data.data.name);
setDefaultConfigs(data, false);
loadApp(data, currentMode());
}
function receiveTutorialSelected(data: IScriptMessage) {
log("selected tut " + data.data.name);
setDefaultConfigs(data, true);
loadTutorial(data, currentMode());
}
function serializeData(data: any, type: string): IConfigData {
var hints : IHints= {};
if (data.values) {
hints = createHints(data.values);
}
var config = <IConfigData> {
name: data.name,
index: data.index,
data: {
auto: false,
userData: data.data
},
values: hints
}
switch (type) {
case "activity":
break;
case "input":
config.maxScore= data.maxScore;
config.hasAnswer= data.hasAnswer;
config.answer= data.answer;
break;
case "choice":
config.maxScore= data.maxScore;
config.hasAnswer= data.hasAnswer;
config.answer = data.answer;
config.choices = [];
for (var it in data.choices) {
var choice: Labs.Components.IChoice = { id: data.choices[it].id, name: data.choices[it].name, value: data.choices[it].value };
config.choices.push(choice);
}
break;
default:
log("td: invalid config type");
break;
}
log("td returning " + JSON.stringify(config));
return config;
}
function fillEmptyIndices(index: number, toInsert: boolean): number {
if (index == 0 || index == 1 || index == 2) return 0;
var ret: Map[] = _indexMap.filter(v => {
if (v.value == index) return true;
return false;
});
if (ret.length > 0) {
return ret[0].mapped;
}
if (!toInsert) return 0;
var maxElem = Math.max.apply(null, _indexMap.map(v=> { return v.mapped; }));
_indexMap.push({ value: index, mapped: maxElem+1});
return maxElem + 1;
}
function normalizeIndex(index: number, toInsert: boolean): number {
if (index == undefined || isNaN(Math.floor(index))) return 0;
index = Math.floor(index);
var newin = fillEmptyIndices(index + 3, toInsert);
log("td new index: " + newin);
return newin;
}
function receiveEditorMessage(event) {
log("td: recieving edit message");
if (event.origin !== _origin) return;
var data = <IMessage>event.data;
data.kind = data.kind.split("__")[0];
switch (data.kind) {
case "scriptSelected":
receiveScriptSelected(<IScriptMessage>data);
break
case "tutorialSelected":
receiveTutorialSelected(<IScriptMessage>data);
break;
case "connect":
log("td: sending connection accept message. to edit mode");
postData('connect',{ mode: 'edit' });
break;
case "editorSetConfig":
log("td: recieved: " + JSON.stringify(data));
data.data.index = normalizeIndex(data.data.index, true);
if (data.data.index == 0) postError("editorSetConfig","config number starts from 0");
else {
var appConfig: IConfigData = undefined;
switch (data.data.type) {
case "activity":
appConfig = <IActivityConfigData>serializeData(<IActivityConfigData>data.data, "activity");
break;
case "input":
appConfig = <IInputConfigData>serializeData(<IInputConfigData>(data.data), "input");
case "choice":
appConfig = <IChoiceConfigData>serializeData(<IChoiceConfigData>(data.data), "choice");
break;
default:
postError("editorSetConfig","invalid component type");
break;
}
if (appConfig) {
log("td: serialized app config: " + JSON.stringify(appConfig));
saveAppConfiguration(appConfig, data.data.type);
}
}
break;
case "editorGetConfig":
log("td: recieved get config: " + JSON.stringify(data));
data.data.index = normalizeIndex(data.data.index, false);
if (data.data.index == 0) postError("editorGetConfig","config number starts from 0");
else {
_editor.getConfiguration((err, config) => {
if (err) postError("editorGetConfig",err);
else getAppConfigurationAsync(data.data.index);
});
}
break;
case "addHints":
log("td: recieved addHint: "+JSON.stringify(data));
data.data.index = normalizeIndex(data.data.index, false);
if (data.data.index == 0) postError("addHints", "config number starts from 0");
else {
addHints(data.data.index, data.data.hints);
}
break;
default:
postError(data.kind, "unknown message type");
break;
}
}
function receiveViewMessage(event) {
if (event.origin !== _origin) return;
var data = <IMessage>event.data;
data.kind = data.kind.split("__")[0];
log("td: receiving view message - " + data.kind);
switch (data.kind) {
case "connect":
log("td: sending connection accept message. to view mode");
postData("connect", { mode: 'view' });
break;
case "labGetState":
_lab.getState((err, state) => {
if (err) postError("labGetState", err);
else {
log("td: sending state: " + JSON.stringify(state));
postData("labGetState", state);
}
});
break;
case "labSetState":
var state = (<IStateMessage>data).data;
saveState(state, (err, unused) => {
if (err) postError("labSetState", err);
else postSuccess("labSetState");
});
break;
case "tutorialComplete":
log("td: recieved tutorial complete");
handleTutorialComplete();
break;
case "leaderboardScore":
log("td: recieved leadervoard complete");
handleLeaderboardComplete(<number>data.data.score);
break;
case "attemptStart":
log("td attempt starting - data recieved : " + JSON.stringify(data.data));
data.data.index = normalizeIndex(data.data.index, false);
if (data.data.index == 0) postError("attemptStart","index starts from 0");
else createAttempt(data.data.index);
break;
case "attemptComplete":
log("td attempt complete - data recieved : " + JSON.stringify(data.data));
data.data.index = normalizeIndex(data.data.index, false);
if (data.data.index == 0) postError("attemptComplete","index starts from 0");
else {
switch (data.data.type) {
case "activity":
handleActivityAttempt(<IActivityAttemptData>data.data, completeActivityAttempt);
break;
case "input":
handleInputAttempt(<IInputAttemptData>data.data, completeInputAttempt);
break;
case "choice":
handleChoiceAttempt(<IChoiceAttemptData>data.data, completeChoiceAttempt);
break;
default:
postError("attemptComplete","invalid activity type");
}
}
break;
case "timelineNext":
Labs.getTimeline().next({}, (err, unused) => {
postSuccess("timelineNext");
});
break;
case "getHint":
log("td: sending hint for: " + JSON.stringify(data.data));
data.data.index = normalizeIndex(data.data.index, false);
if (data.data.index == 0) postError("getHint", "index starts from 0");
else {
handleActivityAttempt(data.data, (attempt, data) => {
var hints = attempt.getValues("hints");
hints[data.hintIndex].getValue((err, hint) => {
if (err) postError("getHint", err);
else postData("getHint", { hint: hint });
});
});
}
break;
case "labGetComponentConfig":
data.data.index = normalizeIndex(data.data.index, false);
if (data.data.index == 0) postError("labGetComponentConfig", "index starts from 0");
else getAppComponentData(data.data.index);
break;
case "onDeactivate":
_onDeactivate = 1;
postSuccess("onDeactivate");
break;
case "deactivateDone":
_onDeactivate = 0;
break;
default:
postError(data.kind, "unknown message type");
break;
}
}
export function switchMode(mode: Labs.Core.LabMode) {
log("td: on " + mode);
window.removeEventListener("message", receiveEditorMessage, false);
window.removeEventListener("message", receiveViewMessage, false);
switch (mode) {
case Labs.Core.LabMode.Edit:
if (_lab) {
log("td: lab done");
_lab.done((err, unused) => {
_lab = undefined;
switchMode(mode);
});
}
else {
Labs.editLab((err, labEditor) => {
if (err) return handleError(err);
_editor = labEditor;
log("td: editor opened");
window.addEventListener("message", receiveEditorMessage, false);
loadStoreOrApp();
updateChrome();
});
}
break;
case Labs.Core.LabMode.View:
if (_editor) {
log("td: editor done");
_editor.done((err, unused) => {
_editor = undefined;
switchMode(mode);
});
}
else if (_lab) {
log("td: restarting lab.");
_lab.done((err, unused) => {
_lab = undefined;
switchMode(mode);
});
}
else {
Labs.takeLab((err, lab) => {
if (err) return handleError(err);
_lab = lab;
log('td: lab loaded');
window.addEventListener("message", receiveViewMessage, false);
loadStoreOrApp();
});
}
break;
default:
log("unknown mode: " + mode);
}
}
export function activate() {
log("td: activate");
_isPreviewMode = false;
updateChrome();
return;
if (_lab) {
var comp = <Labs.Components.ActivityComponentInstance>_lab.components[0];
if (comp && comp.component.data) {
var data = <TDev.IScriptMessage>comp.component.data;
log("td: starting " + data.data.id);
window.addEventListener("message", receiveViewMessage, false);
var url = getAppUrl(data);
setUrl(url);
}
else {
log('td: no lab information');
setUrl("");
}
}
else {
log('td: trying to activate lab, but lab not opened');
return;
}
}
export function deactivate() {
log("td: deactivate. Calling destroy");
if (_onDeactivate == 0) {
setUrl("");
log("td: Destroy successful");
}
switch (_onDeactivate) {
case 0:
setUrl("");
log("td: Destroy successful");
break;
case 1:
_onDeactivate = 2;
postSuccess("deactivate");
break;
}
}
export function log(msg: string) {
if (console.log) console.log(msg);
}
export function elt(name: string): HTMLElement {
return document.getElementById(name);
}
export function hide(name: string) {
var el: HTMLElement = elt(name);
el.style.display = 'none';
}
export function show(name: string) {
var el: HTMLElement = elt(name);
el.style.display = '';
}
export function init() {
hide("edit");
hide("preview");
hide("another");
TDev.elt("edit").onclick = function () {
switchMode(Labs.Core.LabMode.Edit);
}
TDev.elt("preview").onclick = function () {
_isPreviewMode = true;
switchMode(Labs.Core.LabMode.View);
}
TDev.elt("another").onclick = function () { destroy(); }
log("td: onload");
if (/dbg=1/.test(window.location.href)) {
_dbg = true;
log("td: dbg mode");
}
if(/standAloneApp/.test(window.location.href)) {
_standAloneApp = true;
log("td: stand alone app mode");
}
Labs.DefaultHostBuilder = function () {
if (_dbg) {
log("td: labs dbg mode");
return <Labs.Core.ILabHost>new Labs.PostMessageLabHost("test", parent, "*");
} else {
return <Labs.Core.ILabHost>new Labs.OfficeJSLabHost();
}
};
log("td: connecting");
Labs.connect((err, response) => {
if (err) return handleError(err);
log("td: connected: " + response.mode);
switchMode(response.mode);
Labs.on(Labs.Core.EventTypes.ModeChanged, (modeChangedEvent) => {
switchMode(<Labs.Core.LabMode>(<any>Labs.Core.LabMode)[modeChangedEvent.mode]);
});
Labs.on(Labs.Core.EventTypes.Activate, () => activate());
Labs.on(Labs.Core.EventTypes.Deactivate, () => deactivate());
});
}
export function destroy() {
_isPreviewMode = false;
hide("edit");
hide("preview");
hide("another");
if (currentMode() === Labs.Core.LabMode.View) switchMode(Labs.Core.LabMode.Edit);
saveEditorConfiguration(undefined, undefined, undefined);
loadStoreOrApp();
updateChrome();
}
}
window.onload = () => {
TDev.init();
};