Slimmed down version of our Khan/PhET store page to serve as a basic template for future pages of this type.
Removes use of a backend service for search and instead stubs out some JSON parts for it.
This commit is contained in:
Коммит
f2466a2fe6
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,515 @@
|
|||
// Type definitions for Knockout 2.3
|
||||
// Project: http://knockoutjs.com
|
||||
// Definitions by: Boris Yankov <https://github.com/borisyankov/>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
|
||||
interface KnockoutSubscribableFunctions<T> {
|
||||
notifySubscribers(valueToWrite?: T, event?: string): void;
|
||||
}
|
||||
|
||||
interface KnockoutComputedFunctions<T> {
|
||||
}
|
||||
|
||||
interface KnockoutObservableFunctions<T> {
|
||||
equalityComparer(a: any, b: any): boolean;
|
||||
}
|
||||
|
||||
interface KnockoutObservableArrayFunctions<T> {
|
||||
// General Array functions
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
slice(start: number, end?: number): T[];
|
||||
splice(start: number): T[];
|
||||
splice(start: number, deleteCount: number, ...items: T[]): T[];
|
||||
pop(): T;
|
||||
push(...items: T[]): void;
|
||||
shift(): T;
|
||||
unshift(...items: T[]): number;
|
||||
reverse(): T[];
|
||||
sort(): void;
|
||||
sort(compareFunction: (left: T, right: T) => number): void;
|
||||
|
||||
// Ko specific
|
||||
replace(oldItem: T, newItem: T): void;
|
||||
|
||||
remove(item: T): T[];
|
||||
remove(removeFunction: (item: T) => boolean): T[];
|
||||
removeAll(items: T[]): T[];
|
||||
removeAll(): T[];
|
||||
|
||||
destroy(item: T): void;
|
||||
destroyAll(items: T[]): void;
|
||||
destroyAll(): void;
|
||||
}
|
||||
|
||||
interface KnockoutSubscribableStatic {
|
||||
fn: KnockoutSubscribableFunctions<any>;
|
||||
|
||||
new <T>(): KnockoutSubscribable<T>;
|
||||
}
|
||||
|
||||
interface KnockoutSubscription {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
interface KnockoutSubscribable<T> extends KnockoutSubscribableFunctions<T> {
|
||||
subscribe(callback: (newValue: T) => void, target?: any, event?: string): KnockoutSubscription;
|
||||
subscribe<TEvent>(callback: (newValue: TEvent) => void, target: any, event: string): KnockoutSubscription;
|
||||
extend(requestedExtenders: { [key: string]: any; }): KnockoutSubscribable<T>;
|
||||
getSubscriptionsCount(): number;
|
||||
}
|
||||
|
||||
interface KnockoutComputedStatic {
|
||||
fn: KnockoutComputedFunctions<any>;
|
||||
|
||||
<T>(): KnockoutComputed<T>;
|
||||
<T>(func: () => T, context?: any, options?: any): KnockoutComputed<T>;
|
||||
<T>(def: KnockoutComputedDefine<T>): KnockoutComputed<T>;
|
||||
(options?: any): KnockoutComputed<any>;
|
||||
}
|
||||
|
||||
interface KnockoutComputed<T> extends KnockoutObservable<T>, KnockoutComputedFunctions<T> {
|
||||
|
||||
dispose(): void;
|
||||
isActive(): boolean;
|
||||
getDependenciesCount(): number;
|
||||
extend(requestedExtenders: { [key: string]: any; }): KnockoutComputed<T>;
|
||||
}
|
||||
|
||||
interface KnockoutObservableArrayStatic {
|
||||
fn: KnockoutObservableArrayFunctions<any>;
|
||||
|
||||
<T>(value?: T[]): KnockoutObservableArray<T>;
|
||||
}
|
||||
|
||||
interface KnockoutObservableArray<T> extends KnockoutObservable<T[]>, KnockoutObservableArrayFunctions<T> {
|
||||
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservableArray<T>;
|
||||
}
|
||||
|
||||
interface KnockoutObservableStatic {
|
||||
fn: KnockoutObservableFunctions<any>;
|
||||
|
||||
<T>(value?: T): KnockoutObservable<T>;
|
||||
}
|
||||
|
||||
interface KnockoutObservable<T> extends KnockoutSubscribable<T>, KnockoutObservableFunctions<T> {
|
||||
(): T;
|
||||
(value: T): void;
|
||||
|
||||
peek(): T;
|
||||
valueHasMutated?:{(): void;};
|
||||
valueWillMutate?:{(): void;};
|
||||
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservable<T>;
|
||||
}
|
||||
|
||||
interface KnockoutComputedDefine<T> {
|
||||
read(): T;
|
||||
write? (value: T): void;
|
||||
disposeWhenNodeIsRemoved?: Node;
|
||||
disposeWhen? (): boolean;
|
||||
owner?: any;
|
||||
deferEvaluation?: boolean;
|
||||
}
|
||||
|
||||
interface KnockoutBindingContext {
|
||||
$parent: any;
|
||||
$parents: any[];
|
||||
$root: any;
|
||||
$data: any;
|
||||
$index?: number;
|
||||
$parentContext?: KnockoutBindingContext;
|
||||
|
||||
extend(properties: any): any;
|
||||
createChildContext(dataItemOrAccessor: any, dataItemAlias?: any, extendCallback?: Function): any;
|
||||
}
|
||||
|
||||
interface KnockoutBindingHandler {
|
||||
init?(element: any, valueAccessor: () => any, allBindingsAccessor: () => any, viewModel: any, bindingContext: KnockoutBindingContext): void;
|
||||
update?(element: any, valueAccessor: () => any, allBindingsAccessor: () => any, viewModel: any, bindingContext: KnockoutBindingContext): void;
|
||||
options?: any;
|
||||
}
|
||||
|
||||
interface KnockoutBindingHandlers {
|
||||
[bindingHandler: string]: KnockoutBindingHandler;
|
||||
|
||||
// Controlling text and appearance
|
||||
visible: KnockoutBindingHandler;
|
||||
text: KnockoutBindingHandler;
|
||||
html: KnockoutBindingHandler;
|
||||
css: KnockoutBindingHandler;
|
||||
style: KnockoutBindingHandler;
|
||||
attr: KnockoutBindingHandler;
|
||||
|
||||
// Control Flow
|
||||
foreach: KnockoutBindingHandler;
|
||||
if: KnockoutBindingHandler;
|
||||
ifnot: KnockoutBindingHandler;
|
||||
with: KnockoutBindingHandler;
|
||||
|
||||
// Working with form fields
|
||||
click: KnockoutBindingHandler;
|
||||
event: KnockoutBindingHandler;
|
||||
submit: KnockoutBindingHandler;
|
||||
enable: KnockoutBindingHandler;
|
||||
disable: KnockoutBindingHandler;
|
||||
value: KnockoutBindingHandler;
|
||||
hasfocus: KnockoutBindingHandler;
|
||||
checked: KnockoutBindingHandler;
|
||||
options: KnockoutBindingHandler;
|
||||
selectedOptions: KnockoutBindingHandler;
|
||||
uniqueName: KnockoutBindingHandler;
|
||||
|
||||
// Rendering templates
|
||||
template: KnockoutBindingHandler;
|
||||
}
|
||||
|
||||
interface KnockoutMemoization {
|
||||
memoize(callback: () => string): string;
|
||||
unmemoize(memoId: string, callbackParams: any[]): boolean;
|
||||
unmemoizeDomNodeAndDescendants(domNode: any, extraCallbackParamsArray: any[]): boolean;
|
||||
parseMemoText(memoText: string): string;
|
||||
}
|
||||
|
||||
interface KnockoutVirtualElement {}
|
||||
|
||||
interface KnockoutVirtualElements {
|
||||
allowedBindings: { [bindingName: string]: boolean; };
|
||||
emptyNode(node: KnockoutVirtualElement ): void;
|
||||
firstChild(node: KnockoutVirtualElement ): KnockoutVirtualElement;
|
||||
insertAfter( container: KnockoutVirtualElement, nodeToInsert: HTMLElement, insertAfter: HTMLElement ): void;
|
||||
nextSibling(node: KnockoutVirtualElement): HTMLElement;
|
||||
prepend(node: KnockoutVirtualElement, toInsert: HTMLElement ): void;
|
||||
setDomNodeChildren(node: KnockoutVirtualElement, newChildren: { length: number;[index: number]: HTMLElement; } ): void;
|
||||
childNodes(node: KnockoutVirtualElement ): HTMLElement[];
|
||||
}
|
||||
|
||||
interface KnockoutExtenders {
|
||||
throttle(target: any, timeout: number): KnockoutComputed<any>;
|
||||
notify(target: any, notifyWhen: string): any;
|
||||
}
|
||||
|
||||
interface KnockoutUtils {
|
||||
|
||||
//////////////////////////////////
|
||||
// utils.domManipulation.js
|
||||
//////////////////////////////////
|
||||
|
||||
simpleHtmlParse(html: string): any[];
|
||||
|
||||
jQueryHtmlParse(html: string): any[];
|
||||
|
||||
parseHtmlFragment(html: string): any[];
|
||||
|
||||
setHtml(node: Element, html: string): void;
|
||||
|
||||
setHtml(node: Element, html: () => string): void;
|
||||
|
||||
//////////////////////////////////
|
||||
// utils.domData.js
|
||||
//////////////////////////////////
|
||||
|
||||
domData: {
|
||||
get (node: Element, key: string): any;
|
||||
|
||||
set (node: Element, key: string, value: any): void;
|
||||
|
||||
getAll(node: Element, createIfNotFound: boolean): any;
|
||||
|
||||
clear(node: Element): boolean;
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// utils.domNodeDisposal.js
|
||||
//////////////////////////////////
|
||||
|
||||
domNodeDisposal: {
|
||||
addDisposeCallback(node: Element, callback: Function): void;
|
||||
|
||||
removeDisposeCallback(node: Element, callback: Function): void;
|
||||
|
||||
cleanNode(node: Element): Element;
|
||||
|
||||
removeNode(node: Element): void;
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// utils.js
|
||||
//////////////////////////////////
|
||||
|
||||
fieldsIncludedWithJsonPost: any[];
|
||||
|
||||
compareArrays<T>(a: T[], b: T[]): Array<KnockoutArrayChange<T>>;
|
||||
|
||||
arrayForEach<T>(array: T[], action: (item: T) => void): void;
|
||||
|
||||
arrayIndexOf<T>(array: T[], item: T): number;
|
||||
|
||||
arrayFirst<T>(array: T[], predicate: (item: T) => boolean, predicateOwner?: any): T;
|
||||
|
||||
arrayRemoveItem(array: any[], itemToRemove: any): void;
|
||||
|
||||
arrayGetDistinctValues<T>(array: T[]): T[];
|
||||
|
||||
arrayMap<T, U>(array: T[], mapping: (item: T) => U): U[];
|
||||
|
||||
arrayFilter<T>(array: T[], predicate: (item: T) => boolean): T[];
|
||||
|
||||
arrayPushAll<T>(array: T[], valuesToPush: T[]): T[];
|
||||
|
||||
arrayPushAll<T>(array: KnockoutObservableArray<T>, valuesToPush: T[]): T[];
|
||||
|
||||
extend(target: Object, source: Object): Object;
|
||||
|
||||
emptyDomNode(domNode: HTMLElement): void;
|
||||
|
||||
moveCleanedNodesToContainerElement(nodes: any[]): HTMLElement;
|
||||
|
||||
cloneNodes(nodesArray: any[], shouldCleanNodes: boolean): any[];
|
||||
|
||||
setDomNodeChildren(domNode: any, childNodes: any[]): void;
|
||||
|
||||
replaceDomNodes(nodeToReplaceOrNodeArray: any, newNodesArray: any[]): void;
|
||||
|
||||
setOptionNodeSelectionState(optionNode: any, isSelected: boolean): void;
|
||||
|
||||
stringTrim(str: string): string;
|
||||
|
||||
stringTokenize(str: string, delimiter: string): string;
|
||||
|
||||
stringStartsWith(str: string, startsWith: string): string;
|
||||
|
||||
domNodeIsContainedBy(node: any, containedByNode: any): boolean;
|
||||
|
||||
domNodeIsAttachedToDocument(node: any): boolean;
|
||||
|
||||
tagNameLower(element: any): string;
|
||||
|
||||
registerEventHandler(element: any, eventType: any, handler: Function): void;
|
||||
|
||||
triggerEvent(element: any, eventType: any): void;
|
||||
|
||||
unwrapObservable<T>(value: KnockoutObservable<T>): T;
|
||||
|
||||
peekObservable<T>(value: KnockoutObservable<T>): T;
|
||||
|
||||
toggleDomNodeCssClass(node: any, className: string, shouldHaveClass: boolean): void;
|
||||
|
||||
//setTextContent(element: any, textContent: string): void; // NOT PART OF THE MINIFIED API SURFACE (ONLY IN knockout-{version}.debug.js) https://github.com/SteveSanderson/knockout/issues/670
|
||||
|
||||
setElementName(element: any, name: string): void;
|
||||
|
||||
forceRefresh(node: any): void;
|
||||
|
||||
ensureSelectElementIsRenderedCorrectly(selectElement: any): void;
|
||||
|
||||
range(min: any, max: any): any;
|
||||
|
||||
makeArray(arrayLikeObject: any): any[];
|
||||
|
||||
getFormFields(form: any, fieldName: string): any[];
|
||||
|
||||
parseJson(jsonString: string): any;
|
||||
|
||||
stringifyJson(data: any, replacer?: Function, space?: string): string;
|
||||
|
||||
postJson(urlOrForm: any, data: any, options: any): void;
|
||||
|
||||
ieVersion: number;
|
||||
|
||||
isIe6: boolean;
|
||||
|
||||
isIe7: boolean;
|
||||
}
|
||||
|
||||
interface KnockoutArrayChange<T> {
|
||||
status: string;
|
||||
value: T;
|
||||
index: number;
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// templateSources.js
|
||||
//////////////////////////////////
|
||||
|
||||
interface KnockoutTemplateSourcesDomElement {
|
||||
|
||||
text(valueToWrite?: any): any;
|
||||
|
||||
data(key: string, valueToWrite?: any): any;
|
||||
}
|
||||
|
||||
|
||||
interface KnockoutTemplateSources {
|
||||
|
||||
domElement: KnockoutTemplateSourcesDomElement;
|
||||
|
||||
anonymousTemplate: {
|
||||
|
||||
prototype: KnockoutTemplateSourcesDomElement;
|
||||
|
||||
new (element: Element): KnockoutTemplateSourcesDomElement;
|
||||
};
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// nativeTemplateEngine.js
|
||||
//////////////////////////////////
|
||||
|
||||
interface KnockoutNativeTemplateEngine {
|
||||
|
||||
renderTemplateSource(templateSource: Object, bindingContext?: KnockoutBindingContext, options?: Object): any[];
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// templateEngine.js
|
||||
//////////////////////////////////
|
||||
|
||||
interface KnockoutTemplateEngine extends KnockoutNativeTemplateEngine {
|
||||
|
||||
createJavaScriptEvaluatorBlock(script: string): string;
|
||||
|
||||
makeTemplateSource(template: any, templateDocument?: Document): any;
|
||||
|
||||
renderTemplate(template: any, bindingContext: KnockoutBindingContext, options: Object, templateDocument: Document): any;
|
||||
|
||||
isTemplateRewritten(template: any, templateDocument: Document): boolean;
|
||||
|
||||
rewriteTemplate(template: any, rewriterCallback: Function, templateDocument: Document): void;
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
||||
interface KnockoutStatic {
|
||||
utils: KnockoutUtils;
|
||||
memoization: KnockoutMemoization;
|
||||
bindingHandlers: KnockoutBindingHandlers;
|
||||
virtualElements: KnockoutVirtualElements;
|
||||
extenders: KnockoutExtenders;
|
||||
|
||||
applyBindings(viewModel: any, rootNode?: any): void;
|
||||
applyBindingsToDescendants(viewModel: any, rootNode: any): void;
|
||||
applyBindingsToNode(node: Element, options: any, viewModel: any): void;
|
||||
|
||||
subscribable: KnockoutSubscribableStatic;
|
||||
observable: KnockoutObservableStatic;
|
||||
computed: KnockoutComputedStatic;
|
||||
observableArray: KnockoutObservableArrayStatic;
|
||||
|
||||
contextFor(node: any): any;
|
||||
isSubscribable(instance: any): boolean;
|
||||
toJSON(viewModel: any, replacer?: Function, space?: any): string;
|
||||
toJS(viewModel: any): any;
|
||||
isObservable(instance: any): boolean;
|
||||
isWriteableObservable(instance: any): boolean;
|
||||
isComputed(instance: any): boolean;
|
||||
dataFor(node: any): any;
|
||||
removeNode(node: Element): void;
|
||||
cleanNode(node: Element): Element;
|
||||
renderTemplate(template: Function, viewModel: any, options?: any, target?: any, renderMode?: any): any;
|
||||
renderTemplate(template: string, viewModel: any, options?: any, target?: any, renderMode?: any): any;
|
||||
unwrap(value: any): any;
|
||||
|
||||
//////////////////////////////////
|
||||
// templateSources.js
|
||||
//////////////////////////////////
|
||||
|
||||
templateSources: KnockoutTemplateSources;
|
||||
|
||||
//////////////////////////////////
|
||||
// templateEngine.js
|
||||
//////////////////////////////////
|
||||
|
||||
templateEngine: {
|
||||
|
||||
prototype: KnockoutTemplateEngine;
|
||||
|
||||
new (): KnockoutTemplateEngine;
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// templateRewriting.js
|
||||
//////////////////////////////////
|
||||
|
||||
templateRewriting: {
|
||||
|
||||
ensureTemplateIsRewritten(template: Node, templateEngine: KnockoutTemplateEngine, templateDocument: Document): any;
|
||||
ensureTemplateIsRewritten(template: string, templateEngine: KnockoutTemplateEngine, templateDocument: Document): any;
|
||||
|
||||
memoizeBindingAttributeSyntax(htmlString: string, templateEngine: KnockoutTemplateEngine): any;
|
||||
|
||||
applyMemoizedBindingsToNextSibling(bindings: any, nodeName: string): string;
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// nativeTemplateEngine.js
|
||||
//////////////////////////////////
|
||||
|
||||
nativeTemplateEngine: {
|
||||
|
||||
prototype: KnockoutNativeTemplateEngine;
|
||||
|
||||
new (): KnockoutNativeTemplateEngine;
|
||||
|
||||
instance: KnockoutNativeTemplateEngine;
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// jqueryTmplTemplateEngine.js
|
||||
//////////////////////////////////
|
||||
|
||||
jqueryTmplTemplateEngine: {
|
||||
|
||||
prototype: KnockoutTemplateEngine;
|
||||
|
||||
renderTemplateSource(templateSource: Object, bindingContext: KnockoutBindingContext, options: Object): Node[];
|
||||
|
||||
createJavaScriptEvaluatorBlock(script: string): string;
|
||||
|
||||
addTemplate(templateName: string, templateMarkup: string): void;
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// templating.js
|
||||
//////////////////////////////////
|
||||
|
||||
setTemplateEngine(templateEngine: KnockoutNativeTemplateEngine): void;
|
||||
|
||||
renderTemplate(template: Function, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||
renderTemplate(template: any, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||
renderTemplate(template: Function, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||
renderTemplate(template: any, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||
renderTemplate(template: Function, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||
renderTemplate(template: any, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||
renderTemplate(template: Function, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||
renderTemplate(template: any, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||
|
||||
renderTemplateForEach(template: Function, arrayOrObservableArray: any[], options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||
renderTemplateForEach(template: any, arrayOrObservableArray: any[], options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||
renderTemplateForEach(template: Function, arrayOrObservableArray: KnockoutObservable<any>, options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||
renderTemplateForEach(template: any, arrayOrObservableArray: KnockoutObservable<any>, options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||
|
||||
expressionRewriting: {
|
||||
bindingRewriteValidators: any;
|
||||
};
|
||||
|
||||
/////////////////////////////////
|
||||
|
||||
bindingProvider: any;
|
||||
|
||||
/////////////////////////////////
|
||||
// selectExtensions.js
|
||||
/////////////////////////////////
|
||||
|
||||
selectExtensions: {
|
||||
|
||||
readValue(element: HTMLElement): any;
|
||||
|
||||
writeValue(element: HTMLElement, value: any): void;
|
||||
};
|
||||
}
|
||||
|
||||
declare module "knockout" {
|
||||
export = ko;
|
||||
}
|
||||
|
||||
declare var ko: KnockoutStatic;
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,252 @@
|
|||
/* overload office */
|
||||
body {
|
||||
font-family: "Segoe UI", Verdana, Helvetica, Sans-Serif;
|
||||
/* font-size: 14px; */
|
||||
/* line-height: 1.428571429;*/
|
||||
color: #333333;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.office-app-icon {
|
||||
width: 98px;
|
||||
height: 98px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.office-app > .office-app-icon {
|
||||
float: left !important;
|
||||
margin-right: 25px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.office-app > .app-details {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.office-app > .app-actions {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
div.active-template {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#top {
|
||||
background-color: rgb(42, 50, 106);
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 0;
|
||||
padding: 0 20px;
|
||||
height: 147px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#searchInputBox {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
margin: 0px;
|
||||
font-size: 14px;
|
||||
position: absolute;
|
||||
top: 147px;
|
||||
left: 200px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
#domain-list-container {
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
background-color: #FF9933;
|
||||
height: 100%;
|
||||
z-index: 500;
|
||||
width: 200px;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
#categories-container {
|
||||
padding: 20px 15px;
|
||||
background-color: #FFFFFF;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.domain {
|
||||
padding: 147px 0px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
a.app-btn {
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.domain.selected {
|
||||
background-color: #793541;
|
||||
}
|
||||
|
||||
.categories {
|
||||
color: #935D67;
|
||||
}
|
||||
|
||||
.category {
|
||||
color: #99656E;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: inline-block;
|
||||
margin: 0px 5px 5px 0px;
|
||||
max-width: 124px;
|
||||
min-height: 84px;
|
||||
max-height: 84px;
|
||||
}
|
||||
|
||||
.content-image {
|
||||
width: 124px;
|
||||
height: 88px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
position: relative;
|
||||
margin-top: -28px;
|
||||
width: 124px;
|
||||
height: 28px;
|
||||
padding: 3px;
|
||||
color: #FFFFFF;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-color: #9B9188;
|
||||
}
|
||||
|
||||
/** detail page */
|
||||
#content-detail {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#content-detail-wrapper {
|
||||
height: 100%;
|
||||
background-color: #17234E;
|
||||
padding: 20px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
#detail-left {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#detail-right {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#detail-buttons {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
/** preview page */
|
||||
.preview-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#detail-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.btn-detail {
|
||||
padding: 2px 12px 3px 12px;
|
||||
border-width: 1px;
|
||||
border-color: #FFFFFF;
|
||||
border-style: solid;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-detail a {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/** view page */
|
||||
#view-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#edit-pages {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#select-contents {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.window-size {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: 25px;
|
||||
background: black;
|
||||
}
|
||||
|
||||
/* Banner images */
|
||||
#banner-title {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
#banner-title-logo {
|
||||
width: 223px;
|
||||
height: 106px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#banner-title-terms {
|
||||
}
|
||||
|
||||
#banner-title-terms a {
|
||||
margin-left: -1px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
#banner-title-terms a:active{
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#banner-logo {
|
||||
margin-top: 35px;
|
||||
width: 150px;
|
||||
height: 77px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.terms-header {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
height: 25px;
|
||||
text-align: right;
|
||||
padding-left: 10px;
|
||||
color: white;
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PhET Simulations</title>
|
||||
<link href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="site.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/html" id="loadingTemplate">
|
||||
Loading...
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="viewTemplate">
|
||||
<div class="terms-header">
|
||||
<div id="banner-title-terms">
|
||||
<a href="http://www.colorado.edu/physics/phet/dev/license.html" target="_blank">Privacy & Terms</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="window-size">
|
||||
<iframe id="view-frame" frameborder="0" data-bind="attr: {src: activeItemUrl }"></iframe>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="editTemplate">
|
||||
<div id="edit-pages">
|
||||
<!-- select page -->
|
||||
<!-- ko ifnot: item() -->
|
||||
<div id="select-contents">
|
||||
<div id="top">
|
||||
<img id="banner-logo" src="https://phetmixapp.cloudapp.net/Content/Icons/cu-logo-half.png" />
|
||||
|
||||
<div id="banner-title">
|
||||
<img id="banner-title-logo" src="https://phetmixapp.cloudapp.net/Content/Icons/phet-logo-117.svg" />
|
||||
<div id="banner-title-terms">
|
||||
<a id="banner-title-terms" href="http://www.colorado.edu/physics/phet/dev/license.html" target="_blank">Privacy & Terms</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="domain-list-container">
|
||||
<div class="domain">
|
||||
<b>SIMULATIONS</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="main-container">
|
||||
<div id="categories-container">
|
||||
<div class="categories" data-bind="with: activeDomain">
|
||||
<!-- ko foreach: { data: children, as: 'category' } -->
|
||||
<div class="category" data-bind="visible: children.length > 0">
|
||||
<div class="category-title">
|
||||
<span data-bind="text: title + ' >'"></span>
|
||||
</div>
|
||||
|
||||
<!-- ko foreach: children -->
|
||||
<div class="content">
|
||||
<a href="#" class="app-btn">
|
||||
<div><img class="content-image" data-bind="attr:{src:thumbnailUrl, alt:title}, click: function() {$root.moveToDetailPage($data);}" /></div>
|
||||
<div class="content-title">
|
||||
<span data-bind="text: (title.length > 30 ? title.substring(0, 30) : title), attr:{title: title}"
|
||||
data-toggle="tooltip">
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<!-- detail page -->
|
||||
<!-- ko if: item() -->
|
||||
<div id="content-detail">
|
||||
<div id="content-detail-wrapper" class="row">
|
||||
<div id="detail-left" class="col-sm-3 col-xs-5">
|
||||
<div id="detail-title">
|
||||
<span data-bind="text: item().title" />
|
||||
</div>
|
||||
<div id="detail-description">
|
||||
<span data-bind="text: item().description" />
|
||||
</div>
|
||||
<div id="detail-buttons">
|
||||
<div id="detail-insert" class="btn btn-detail">
|
||||
<a href="#" data-bind="click: onInsertClick">Insert</a>
|
||||
</div>
|
||||
<div id="detail-cancel" class="btn btn-detail">
|
||||
<a href="#" data-bind="click: moveToSelectPage">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="detail-right" class="col-sm-9 col-xs-7">
|
||||
<iframe id="detail-content-preview-frame" class="preview-frame" frameborder="0" data-bind="attr: {src: activeItemUrl }"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<div class="active-template" data-bind="template: { name: view() }">
|
||||
</div>
|
||||
|
||||
<script src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js" type="text/javascript"></script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"></script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>
|
||||
<script src="https://az592748.vo.msecnd.net/sdk/LabsJS-1.0.4/labs-1.0.4.min.js"></script>
|
||||
<script src="store.js"></script>
|
||||
|
||||
<script>
|
||||
var browseUrl = "/api/Phet/Browse";
|
||||
var categoryUrl = "/api/Phet/Simulations";
|
||||
var getUrl = "/api/Phet/GetSimulation";
|
||||
|
||||
var apps = [
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/ph-scale/latest/ph-scale_en.html", "providerId": "https://phet.colorado.edu/sims/html/ph-scale/latest/ph-scale_en.html", "readableId": "https://phet.colorado.edu/sims/html/ph-scale/latest/ph-scale_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "pH Scale", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/ph-scale/latest/ph-scale-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/ph-scale/latest/ph-scale_en.html", "author": null, "description": "Test the pH of things like coffee, spit, and soap to determine whether each is acidic, basic, or neutral. Visualize the relative number of hydroxide ions and hydronium ions in solution. Switch between logarithmic and linear scales. Investigate whether changing the volume or diluting with water affects the pH. Or you can design your own liquid!", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/beers-law-lab/latest/beers-law-lab_en.html", "providerId": "https://phet.colorado.edu/sims/html/beers-law-lab/latest/beers-law-lab_en.html", "readableId": "https://phet.colorado.edu/sims/html/beers-law-lab/latest/beers-law-lab_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "Beer's Law Lab", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/beers-law-lab/latest/beers-law-lab-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/beers-law-lab/latest/beers-law-lab_en.html", "author": null, "description": "Test the pH of things like coffee, spit, and soap to determine whether each is acidic, basic, or neutral. Visualize the relative number of hydroxide ions and hydronium ions in solution. Switch between logarithmic and linear scales. Investigate whether changing the volume or diluting with water affects the pH. Or you can design your own liquid!", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/concentration/latest/concentration_en.html", "providerId": "https://phet.colorado.edu/sims/html/concentration/latest/concentration_en.html", "readableId": "https://phet.colorado.edu/sims/html/concentration/latest/concentration_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "Concentration", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/concentration/latest/concentration-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/concentration/latest/concentration_en.html", "author": null, "description": "Watch your solution change color as you mix chemicals with water. Then check molarity with the concentration meter. What are all the ways you can change the concentration of your solution? Switch solutes to compare different chemicals and find out how concentrated you can go before you hit saturation!", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/build-an-atom/latest/build-an-atom_en.html", "providerId": "https://phet.colorado.edu/sims/html/build-an-atom/latest/build-an-atom_en.html", "readableId": "https://phet.colorado.edu/sims/html/build-an-atom/latest/build-an-atom_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "Build an Atom", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/build-an-atom/latest/build-an-atom-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/build-an-atom/latest/build-an-atom_en.html", "author": null, "description": "Build an atom out of protons, neutrons, and electrons, and see how the element, charge, and mass change. Then play a game to test your ideas!", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/john-travoltage/latest/john-travoltage_en.html", "providerId": "https://phet.colorado.edu/sims/html/john-travoltage/latest/john-travoltage_en.html", "readableId": "https://phet.colorado.edu/sims/html/john-travoltage/latest/john-travoltage_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "John Travoltage", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/john-travoltage/latest/john-travoltage-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/john-travoltage/latest/john-travoltage_en.html", "author": null, "description": "Make sparks fly with John Travoltage. Wiggle Johnnie's foot and he picks up charges from the carpet. Bring his hand close to the door knob and get rid of the excess charge.", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/forces-and-motion-basics/latest/forces-and-motion-basics_en.html", "providerId": "https://phet.colorado.edu/sims/html/forces-and-motion-basics/latest/forces-and-motion-basics_en.html", "readableId": "https://phet.colorado.edu/sims/html/forces-and-motion-basics/latest/forces-and-motion-basics_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "Forces and Motion: Basics", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/forces-and-motion-basics/latest/forces-and-motion-basics-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/forces-and-motion-basics/latest/forces-and-motion-basics_en.html", "author": null, "description": "Explore the forces at work in a tug of war or pushing a refrigerator, crate, or person. Create an applied force and see how it makes objects move. Change friction and see how it affects the motion of objects.", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/ph-scale-basics/latest/ph-scale-basics_en.html", "providerId": "https://phet.colorado.edu/sims/html/ph-scale-basics/latest/ph-scale-basics_en.html", "readableId": "https://phet.colorado.edu/sims/html/ph-scale-basics/latest/ph-scale-basics_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "pH Scale: Basics", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/ph-scale-basics/latest/ph-scale-basics-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/ph-scale-basics/latest/ph-scale-basics_en.html", "author": null, "description": "Test the pH of things like coffee, spit, and soap to determine whether each is acidic, basic, or neutral. Visualize the relative number of hydroxide ions and hydronium ions in solution. Switch between logarithmic and linear scales. Investigate whether changing the volume or diluting with water affects the pH. Or you can design your own liquid!", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/friction/latest/friction_en.html", "providerId": "https://phet.colorado.edu/sims/html/friction/latest/friction_en.html", "readableId": "https://phet.colorado.edu/sims/html/friction/latest/friction_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "Friction", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/friction/latest/friction-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/friction/latest/friction_en.html", "author": null, "description": "Learn how friction causes a material to heat up and melt. Rub two objects together and they heat up. When one reaches the melting temperature, particles break free as the material melts away.", "children": [] },
|
||||
{ "id": "phet-https://phet.colorado.edu/sims/html/balancing-act/latest/balancing-act_en.html", "providerId": "https://phet.colorado.edu/sims/html/balancing-act/latest/balancing-act_en.html", "readableId": "https://phet.colorado.edu/sims/html/balancing-act/latest/balancing-act_en.html", "youtubeId": null, "durationInSec": 0, "type": "exercise", "title": "Balancing Act", "depth": 2, "thumbnailUrl": "https://phet.colorado.edu/sims/html/balancing-act/latest/balancing-act-128.png", "contentUrl": "https://phet.colorado.edu/sims/html/balancing-act/latest/balancing-act_en.html", "author": null, "description": "Play with objects on a teeter totter to learn about balance. Test what you've learned by trying the Balance Challenge game.", "children": [] }]
|
||||
|
||||
var phetDriver = {
|
||||
loadCategories: function (maxDepth) {
|
||||
return $.when([{ title: "Browse All", id: "PhET", depth: 0, children: [{ title: "HTML5 Simulations", id: "HTML5 Simulations", depth: 1, children: [] }] }]);
|
||||
},
|
||||
|
||||
loadCategory: function (categoryId, searchText) {
|
||||
return $.when(apps);
|
||||
},
|
||||
|
||||
get: function (id) {
|
||||
var app = null;
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
if (apps[i].id === id) {
|
||||
app = apps[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $.when(app);
|
||||
},
|
||||
|
||||
buildConfiguration: function (content) {
|
||||
var appVersion = { major: 1, minor: 1 };
|
||||
var activityComponent = {
|
||||
type: Labs.Components.ActivityComponentType,
|
||||
name: content.title,
|
||||
values: {},
|
||||
data: {
|
||||
id: content.id
|
||||
},
|
||||
secure: false
|
||||
};
|
||||
|
||||
var configuration = {
|
||||
appVersion: appVersion,
|
||||
components: [activityComponent],
|
||||
name: content.title,
|
||||
timeline: null,
|
||||
analytics: null
|
||||
};
|
||||
|
||||
return configuration;
|
||||
},
|
||||
|
||||
getIdFromConfiguration: function (configuration) {
|
||||
return configuration.components[0].data.id;
|
||||
}
|
||||
};
|
||||
|
||||
Labs.DefaultHostBuilder = function () {
|
||||
if (window.location.href.indexOf("PostMessageLabHost") !== -1) {
|
||||
return new Labs.PostMessageLabHost("test", parent, "*");
|
||||
} else {
|
||||
return new Labs.OfficeJSLabHost();
|
||||
}
|
||||
};
|
||||
|
||||
MixAppBrowser.initialize(phetDriver);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,251 @@
|
|||
var MixAppBrowser;
|
||||
(function (MixAppBrowser) {
|
||||
/**
|
||||
* Helper method to create a ILabCallback for the given jQuery deferred object
|
||||
*/
|
||||
function createCallback(deferred) {
|
||||
return function (err, data) {
|
||||
if (err) {
|
||||
deferred.reject(err);
|
||||
} else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var MixAppBrowserViewModel = (function () {
|
||||
function MixAppBrowserViewModel(links, initialMode) {
|
||||
var _this = this;
|
||||
// The view the app is currently within
|
||||
this._appView = ko.observable();
|
||||
// The view selected by the user
|
||||
this._userView = ko.observable("edit");
|
||||
this._modeSwitchP = $.when();
|
||||
this._links = links;
|
||||
|
||||
// Setup knockout observables
|
||||
this.domains = ko.observableArray([]);
|
||||
this.activeDomain = ko.observable();
|
||||
this.item = ko.observable();
|
||||
this.searchText = ko.observable("");
|
||||
|
||||
this.activeItemUrl = ko.computed(function () {
|
||||
var item = _this.item();
|
||||
return item ? item.providerId : null;
|
||||
});
|
||||
|
||||
this.view = ko.computed(function () {
|
||||
var appView = _this._appView();
|
||||
var userView = _this._userView();
|
||||
|
||||
if (appView === "view" || (appView === "edit" && userView === "view")) {
|
||||
return "viewTemplate";
|
||||
} else if (appView === "edit") {
|
||||
return "editTemplate";
|
||||
} else {
|
||||
return "loadingTemplate";
|
||||
}
|
||||
});
|
||||
|
||||
// Events coming from the lab host
|
||||
Labs.on(Labs.Core.EventTypes.ModeChanged, function (data) {
|
||||
var modeChangedEventData = data;
|
||||
_this.switchToMode(Labs.Core.LabMode[modeChangedEventData.mode]);
|
||||
return $.when();
|
||||
});
|
||||
|
||||
Labs.on(Labs.Core.EventTypes.Activate, function (data) {
|
||||
});
|
||||
|
||||
Labs.on(Labs.Core.EventTypes.Deactivate, function (data) {
|
||||
});
|
||||
|
||||
// Set the initial mode
|
||||
this.switchToMode(initialMode);
|
||||
}
|
||||
MixAppBrowserViewModel.prototype.switchToMode = function (mode) {
|
||||
var _this = this;
|
||||
// wait for any previous mode switch to complete before performing the new one
|
||||
this._modeSwitchP = this._modeSwitchP.then(function () {
|
||||
var switchedStateDeferred = $.Deferred();
|
||||
|
||||
if (_this._labInstance) {
|
||||
_this._labInstance.done(createCallback(switchedStateDeferred));
|
||||
} else if (_this._labEditor) {
|
||||
_this._labEditor.done(createCallback(switchedStateDeferred));
|
||||
} else {
|
||||
switchedStateDeferred.resolve();
|
||||
}
|
||||
|
||||
// and now switch the state
|
||||
return switchedStateDeferred.promise().then(function () {
|
||||
_this._labEditor = null;
|
||||
_this._labInstance = null;
|
||||
|
||||
if (mode === Labs.Core.LabMode.Edit) {
|
||||
return _this.switchToEditMode();
|
||||
} else {
|
||||
return _this.switchToViewMode();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
MixAppBrowserViewModel.prototype.switchToEditMode = function () {
|
||||
var _this = this;
|
||||
var editLabDeferred = $.Deferred();
|
||||
Labs.editLab(createCallback(editLabDeferred));
|
||||
|
||||
return editLabDeferred.promise().then(function (labEditor) {
|
||||
_this._labEditor = labEditor;
|
||||
_this._appView("edit");
|
||||
|
||||
var configurationDeferred = $.Deferred();
|
||||
_this._labEditor.getConfiguration(createCallback(configurationDeferred));
|
||||
return configurationDeferred.promise().then(function (configuration) {
|
||||
if (configuration) {
|
||||
var id = _this._links.getIdFromConfiguration(configuration);
|
||||
|
||||
// This should just be a method to show
|
||||
_this._links.get(id).done(function (item) {
|
||||
_this._userView("view");
|
||||
_this.item(item);
|
||||
});
|
||||
} else {
|
||||
_this.loadDomain();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
MixAppBrowserViewModel.prototype.loadDomain = function () {
|
||||
var _this = this;
|
||||
if (this.domains().length > 0) {
|
||||
return $.when();
|
||||
}
|
||||
|
||||
return this._links.loadCategories(2).then(function (domains) {
|
||||
_this.domains(domains);
|
||||
_this.setActiveDomain(domains[0]);
|
||||
});
|
||||
};
|
||||
|
||||
MixAppBrowserViewModel.prototype.setActiveDomain = function (domain) {
|
||||
var _this = this;
|
||||
var clonedDomain = $.extend(true, {}, domain);
|
||||
clonedDomain.children.forEach(function (category) {
|
||||
category.children = [];
|
||||
});
|
||||
this.activeDomain(clonedDomain);
|
||||
|
||||
$.each(clonedDomain.children, function (index, category) {
|
||||
_this._links.loadCategory(category.providerId, _this.searchText()).then(function (list) {
|
||||
category.children = list;
|
||||
|
||||
// force a re-render of the page by updating the root binding
|
||||
// TODO replace this with view models for sub items
|
||||
_this.activeDomain(_this.activeDomain());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
MixAppBrowserViewModel.prototype.switchToViewMode = function () {
|
||||
var _this = this;
|
||||
var takeLabDeferred = $.Deferred();
|
||||
Labs.takeLab(createCallback(takeLabDeferred));
|
||||
return takeLabDeferred.promise().then(function (labInstance) {
|
||||
_this._labInstance = labInstance;
|
||||
_this.loadItem(labInstance);
|
||||
});
|
||||
};
|
||||
|
||||
MixAppBrowserViewModel.prototype.loadItem = function (labInstance) {
|
||||
var _this = this;
|
||||
var activityComponent = this._labInstance.components[0];
|
||||
|
||||
this._links.get(activityComponent.component.data.id).done(function (item) {
|
||||
var attemptsDeferred = $.Deferred();
|
||||
activityComponent.getAttempts(createCallback(attemptsDeferred));
|
||||
var attemptP = attemptsDeferred.promise().then(function (attempts) {
|
||||
var currentAttemptDeferred = $.Deferred();
|
||||
if (attempts.length > 0) {
|
||||
currentAttemptDeferred.resolve(attempts[attempts.length - 1]);
|
||||
} else {
|
||||
activityComponent.createAttempt(createCallback(currentAttemptDeferred));
|
||||
}
|
||||
|
||||
return currentAttemptDeferred.then(function (currentAttempt) {
|
||||
var resumeDeferred = $.Deferred();
|
||||
currentAttempt.resume(createCallback(resumeDeferred));
|
||||
return resumeDeferred.promise().then(function () {
|
||||
return currentAttempt;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return attemptP.then(function (attempt) {
|
||||
var completeDeferred = $.Deferred();
|
||||
if (attempt.getState() !== Labs.ProblemState.Completed) {
|
||||
attempt.complete(createCallback(completeDeferred));
|
||||
} else {
|
||||
completeDeferred.resolve();
|
||||
}
|
||||
|
||||
_this._appView("view");
|
||||
_this.item(item);
|
||||
|
||||
return completeDeferred.promise();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Action invoked when the user clicks on the insert button on the details page
|
||||
//
|
||||
MixAppBrowserViewModel.prototype.onInsertClick = function () {
|
||||
var _this = this;
|
||||
var configuration = this._links.buildConfiguration(this.item());
|
||||
if (this._labEditor) {
|
||||
this._labEditor.setConfiguration(configuration, function (err, unused) {
|
||||
_this._userView("view");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Method invoked when the user clicks on a selection and wants to move to the details page
|
||||
//
|
||||
MixAppBrowserViewModel.prototype.moveToDetailPage = function (content) {
|
||||
var _this = this;
|
||||
this._links.get(content.id).then(function (item) {
|
||||
_this.item(item);
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Moves back to the select page
|
||||
//
|
||||
MixAppBrowserViewModel.prototype.moveToSelectPage = function () {
|
||||
this.item(null);
|
||||
};
|
||||
|
||||
//
|
||||
// Callback inoked when a search occurs
|
||||
//
|
||||
MixAppBrowserViewModel.prototype.search = function () {
|
||||
this.setActiveDomain(this.activeDomain());
|
||||
};
|
||||
return MixAppBrowserViewModel;
|
||||
})();
|
||||
|
||||
function initialize(driver) {
|
||||
$(document).ready(function () {
|
||||
// Initialize Labs.JS
|
||||
Labs.connect(function (err, connectionResponse) {
|
||||
var viewModel = new MixAppBrowserViewModel(driver, connectionResponse.mode);
|
||||
ko.applyBindings(viewModel);
|
||||
});
|
||||
});
|
||||
}
|
||||
MixAppBrowser.initialize = initialize;
|
||||
})(MixAppBrowser || (MixAppBrowser = {}));
|
|
@ -0,0 +1,283 @@
|
|||
module MixAppBrowser {
|
||||
export interface AppDataDriver {
|
||||
loadCategories(maxDepth: number): JQueryPromise<AppContent[]>;
|
||||
|
||||
loadCategory(parentId: string, searchText: string): JQueryPromise<AppContent[]>;
|
||||
|
||||
get(id: string): JQueryPromise<AppContent>;
|
||||
|
||||
buildConfiguration(content: AppContent): Labs.Core.IConfiguration;
|
||||
|
||||
getIdFromConfiguration(configuration: Labs.Core.IConfiguration) : string;
|
||||
}
|
||||
|
||||
// Top-level ndoes in a topic tree
|
||||
export interface AppContent {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
providerId: string;
|
||||
readableId: string;
|
||||
youtubeId: string;
|
||||
durationInSec: number;
|
||||
thumbnailUrl: string;
|
||||
contentUrl: string;
|
||||
children: AppContent[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a ILabCallback for the given jQuery deferred object
|
||||
*/
|
||||
function createCallback<T>(deferred: JQueryDeferred<T>): Labs.Core.ILabCallback<T> {
|
||||
return (err, data) => {
|
||||
if (err) {
|
||||
deferred.reject(err);
|
||||
}
|
||||
else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class MixAppBrowserViewModel {
|
||||
view: KnockoutComputed<string>;
|
||||
|
||||
contentType: KnockoutObservable<string>;
|
||||
domains: KnockoutObservableArray<AppContent>;
|
||||
activeDomain: KnockoutObservable<AppContent>;
|
||||
item: KnockoutObservable<AppContent>;
|
||||
searchText: KnockoutObservable<string>;
|
||||
activeItemUrl: KnockoutComputed<string>;
|
||||
|
||||
// The view the app is currently within
|
||||
private _appView: KnockoutObservable<string> = ko.observable();
|
||||
// The view selected by the user
|
||||
private _userView: KnockoutObservable<string> = ko.observable("edit");
|
||||
|
||||
private _labEditor: Labs.LabEditor;
|
||||
private _labInstance: Labs.LabInstance;
|
||||
private _links: AppDataDriver;
|
||||
private _modeSwitchP: JQueryPromise<void> = $.when();
|
||||
|
||||
constructor(links: AppDataDriver, initialMode: Labs.Core.LabMode) {
|
||||
this._links = links;
|
||||
|
||||
// Setup knockout observables
|
||||
this.domains = ko.observableArray([]);
|
||||
this.activeDomain = ko.observable();
|
||||
this.item = ko.observable();
|
||||
this.searchText = ko.observable("");
|
||||
|
||||
this.activeItemUrl = ko.computed(()=> {
|
||||
var item = this.item();
|
||||
return item ? item.providerId : null;
|
||||
});
|
||||
|
||||
this.view = ko.computed(()=> {
|
||||
var appView = this._appView();
|
||||
var userView = this._userView();
|
||||
|
||||
if (appView === "view" || (appView === "edit" && userView === "view")) {
|
||||
return "viewTemplate";
|
||||
}
|
||||
else if (appView === "edit") {
|
||||
return "editTemplate";
|
||||
} else {
|
||||
return "loadingTemplate";
|
||||
}
|
||||
});
|
||||
|
||||
// Events coming from the lab host
|
||||
Labs.on(Labs.Core.EventTypes.ModeChanged, (data) => {
|
||||
var modeChangedEventData = <Labs.Core.ModeChangedEventData> data;
|
||||
this.switchToMode(Labs.Core.LabMode[modeChangedEventData.mode]);
|
||||
return $.when();
|
||||
});
|
||||
|
||||
Labs.on(Labs.Core.EventTypes.Activate, (data) => {
|
||||
});
|
||||
|
||||
Labs.on(Labs.Core.EventTypes.Deactivate, (data) => {
|
||||
});
|
||||
|
||||
// Set the initial mode
|
||||
this.switchToMode(initialMode);
|
||||
}
|
||||
|
||||
switchToMode(mode: Labs.Core.LabMode) {
|
||||
// wait for any previous mode switch to complete before performing the new one
|
||||
this._modeSwitchP = this._modeSwitchP.then(() => {
|
||||
var switchedStateDeferred = $.Deferred();
|
||||
|
||||
// End any existing operations
|
||||
if (this._labInstance) {
|
||||
this._labInstance.done(createCallback(switchedStateDeferred));
|
||||
}
|
||||
else if (this._labEditor) {
|
||||
this._labEditor.done(createCallback(switchedStateDeferred));
|
||||
} else {
|
||||
switchedStateDeferred.resolve();
|
||||
}
|
||||
|
||||
// and now switch the state
|
||||
return switchedStateDeferred.promise().then(() => {
|
||||
this._labEditor = null;
|
||||
this._labInstance = null;
|
||||
|
||||
if (mode === Labs.Core.LabMode.Edit) {
|
||||
return this.switchToEditMode();
|
||||
} else {
|
||||
return this.switchToViewMode();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private switchToEditMode(): JQueryPromise<void> {
|
||||
var editLabDeferred = $.Deferred();
|
||||
Labs.editLab(createCallback(editLabDeferred));
|
||||
|
||||
return editLabDeferred.promise().then((labEditor) => {
|
||||
this._labEditor = labEditor;
|
||||
this._appView("edit");
|
||||
|
||||
var configurationDeferred = $.Deferred();
|
||||
this._labEditor.getConfiguration(createCallback(configurationDeferred));
|
||||
return configurationDeferred.promise().then((configuration) => {
|
||||
if (configuration) {
|
||||
var id = this._links.getIdFromConfiguration(configuration);
|
||||
|
||||
// This should just be a method to show
|
||||
this._links.get(id).done((item: AppContent) => {
|
||||
this._userView("view");
|
||||
this.item(item);
|
||||
});
|
||||
} else {
|
||||
this.loadDomain();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private loadDomain(): JQueryPromise<void> {
|
||||
if (this.domains().length > 0) {
|
||||
return $.when();
|
||||
}
|
||||
|
||||
return this._links.loadCategories(2).then((domains) => {
|
||||
this.domains(domains);
|
||||
this.setActiveDomain(domains[0]);
|
||||
});
|
||||
}
|
||||
|
||||
private setActiveDomain(domain: AppContent) {
|
||||
var clonedDomain = $.extend(true, {}, domain);
|
||||
clonedDomain.children.forEach((category) => { category.children = [] });
|
||||
this.activeDomain(clonedDomain);
|
||||
|
||||
$.each(clonedDomain.children, (index, category) => {
|
||||
this._links.loadCategory(
|
||||
category.providerId,
|
||||
this.searchText()).then((list) => {
|
||||
category.children = list;
|
||||
// force a re-render of the page by updating the root binding
|
||||
// TODO replace this with view models for sub items
|
||||
this.activeDomain(this.activeDomain());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private switchToViewMode(): JQueryPromise<void> {
|
||||
var takeLabDeferred = $.Deferred();
|
||||
Labs.takeLab(createCallback(takeLabDeferred));
|
||||
return takeLabDeferred.promise().then((labInstance) => {
|
||||
this._labInstance = labInstance;
|
||||
this.loadItem(labInstance);
|
||||
});
|
||||
}
|
||||
|
||||
private loadItem(labInstance: Labs.LabInstance) {
|
||||
var activityComponent = <Labs.Components.ActivityComponentInstance> this._labInstance.components[0];
|
||||
|
||||
this._links.get(activityComponent.component.data.id).done((item) => {
|
||||
var attemptsDeferred = $.Deferred();
|
||||
activityComponent.getAttempts(createCallback(attemptsDeferred));
|
||||
var attemptP = attemptsDeferred.promise().then((attempts) => {
|
||||
var currentAttemptDeferred = $.Deferred();
|
||||
if (attempts.length > 0) {
|
||||
currentAttemptDeferred.resolve(attempts[attempts.length - 1]);
|
||||
} else {
|
||||
activityComponent.createAttempt(createCallback(currentAttemptDeferred));
|
||||
}
|
||||
|
||||
return currentAttemptDeferred.then((currentAttempt: Labs.Components.ActivityComponentAttempt) => {
|
||||
var resumeDeferred = $.Deferred();
|
||||
currentAttempt.resume(createCallback(resumeDeferred));
|
||||
return resumeDeferred.promise().then(() => {
|
||||
return currentAttempt;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return attemptP.then((attempt: Labs.Components.ActivityComponentAttempt) => {
|
||||
var completeDeferred = $.Deferred();
|
||||
if (attempt.getState() !== Labs.ProblemState.Completed) {
|
||||
attempt.complete(createCallback(completeDeferred));
|
||||
} else {
|
||||
completeDeferred.resolve();
|
||||
}
|
||||
|
||||
this._appView("view");
|
||||
this.item(item);
|
||||
|
||||
return completeDeferred.promise();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Action invoked when the user clicks on the insert button on the details page
|
||||
//
|
||||
onInsertClick() {
|
||||
var configuration = this._links.buildConfiguration(this.item());
|
||||
if (this._labEditor) {
|
||||
this._labEditor.setConfiguration(configuration, (err, unused) => {
|
||||
this._userView("view");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Method invoked when the user clicks on a selection and wants to move to the details page
|
||||
//
|
||||
moveToDetailPage(content: AppContent) {
|
||||
this._links.get(content.id).then((item: AppContent) => {
|
||||
this.item(item);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Moves back to the select page
|
||||
//
|
||||
moveToSelectPage() {
|
||||
this.item(null);
|
||||
}
|
||||
|
||||
//
|
||||
// Callback inoked when a search occurs
|
||||
//
|
||||
search() {
|
||||
this.setActiveDomain(this.activeDomain());
|
||||
}
|
||||
}
|
||||
|
||||
export function initialize(driver: AppDataDriver) {
|
||||
$(document).ready(() => {
|
||||
// Initialize Labs.JS
|
||||
Labs.connect((err, connectionResponse) => {
|
||||
var viewModel = new MixAppBrowserViewModel(driver, connectionResponse.mode);
|
||||
ko.applyBindings(viewModel);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче