{"version":3,"file":"main.js","sources":["typescript/lib/ts-lib/src/core/signal/Signal.ts","typescript/lib/ts-lib/src/core/node/signal/DOMEventSignal.ts","typescript/lib/ts-lib/src/core/node/signal/NodeSignal.ts","typescript/lib/ts-lib/src/core/node/Node.ts","typescript/lib/ts-lib/src/core/lookup/LookupDict.ts","typescript/lib/ts-lib/src/core/js.ts","typescript/lib/ts-lib/src/core/lookup/LookupObject.ts","typescript/lib/ts-lib/src/core/template/TemplateRenderer.ts","typescript/lib/ts-lib/src/core/template/TemplateManager.ts","typescript/lib/ts-lib/src/core/view/View.ts","typescript/lib/ts-lib/src/core/ioc/IoC.ts","typescript/lib/ts-lib/src/core/view/Scanner.ts","typescript/lib/ts-lib/src/core/model/Model.ts","typescript/lib/ts-lib/src/core/model/Mapper.ts","typescript/lib/ts-lib/src/core/linkedlist/LinkedList.ts","typescript/lib/ts-lib/src/core/tree/TreeNode.ts","typescript/lib/lernfragen/src/slides/Chapter/ChapterModel.ts","typescript/lib/lernfragen/src/slides/Slide/SlideModel.ts","typescript/lib/lernfragen/src/slides/Question/QuestionModel.ts","typescript/lib/lernfragen/src/slides/AnswerList/AnswerListModel.ts","typescript/lib/lernfragen/src/slides/SingleChoice/SingleChoiceModel.ts","typescript/lib/lernfragen/src/slides/MultipleChoice/MultipleChoiceModel.ts","typescript/lib/lernfragen/src/slides/Cloze/ClozeModel.ts","typescript/lib/lernfragen/src/slides/DragDrop/DragDropModel.ts","typescript/lib/lernfragen/src/slides/FreeText/FreetextModel.ts","typescript/lib/lernfragen/src/slides/HotSpot/HotspotModel.ts","typescript/lib/lernfragen/src/slides/Reveal/RevealModel.ts","typescript/lib/lernfragen/src/slides/ToolTip/TooltipModel.ts","typescript/lib/lernfragen/src/aggregators/Aggregator.ts","typescript/lib/lernfragen/src/aggregators/CorrectWrong.ts","typescript/lib/lernfragen/src/slides/Summary/SummaryModel.ts","typescript/lib/lernfragen/src/slides/DropDown/DropDownModel.ts","typescript/lib/lernfragen/src/slides/Order/OrderModel.ts","typescript/lib/lernfragen/src/slides/DragDropCloze/DragDropClozeModel.ts","typescript/lib/lernfragen/src/slides/DragDropSentence/DragDropSentenceModel.ts","typescript/lib/lernfragen/src/slides/MatrixChoice/MatrixChoiceModel.ts","typescript/lib/lernfragen/src/slides/MatrixBinaryChoice/MatrixBinaryChoiceModel.ts","typescript/lib/lernfragen/src/setup/models.ts","typescript/lib/lernfragen/src/decorators/Decorator.ts","typescript/lib/lernfragen/src/sliderenderer/SlideRenderer.ts","typescript/lib/lernfragen/src/navigation/NextPrevNavigation.ts","typescript/lib/lernfragen/src/navigation/NextSolutionNavigation.ts","typescript/lib/lernfragen/src/navigation/PointsNavigation.ts","typescript/lib/lernfragen/src/navigation/AnsweredButton.ts","typescript/lib/ts-lib/src/core/node/Window.ts","typescript/lib/lernfragen/src/navigation/ResponsiveNavigation.ts","typescript/lib/lernfragen/src/navigation/Status.ts","typescript/lib/lernfragen/src/setup/lernfragen.ts","typescript/lib/lernfragen/src/slides/Slide/Slide.ts","typescript/lib/lernfragen/src/slides/Question/Question.ts","typescript/lib/lernfragen/src/slides/Cloze/Cloze.ts","typescript/lib/ts-lib/src/core/list/List.ts","typescript/lib/ts-lib/src/core/list/ListRenderer.ts","node_modules/atoa/atoa.js","node_modules/ticky/ticky.js","node_modules/contra/debounce.js","node_modules/contra/emitter.js","node_modules/custom-event/index.js","node_modules/crossvent/src/eventmap.js","node_modules/crossvent/src/crossvent.js","node_modules/dragula/classes.js","node_modules/dragula/dragula.js","typescript/lib/ts-lib/src/ui/dragdrop/DragDrop.ts","typescript/lib/lernfragen/src/slides/DragDrop/DragDrop.ts","typescript/lib/lernfragen/src/slides/FreeText/Freetext.ts","typescript/lib/lernfragen/src/slides/HotSpot/Hotspot.ts","typescript/lib/lernfragen/src/slides/AnswerList/AnswerList.ts","typescript/lib/lernfragen/src/slides/MultipleChoice/MultipleChoice.ts","typescript/lib/lernfragen/src/slides/Reveal/Reveal.ts","typescript/lib/lernfragen/src/slides/SingleChoice/SingleChoice.ts","typescript/lib/lernfragen/src/slides/ToolTip/Tooltip.ts","typescript/lib/lernfragen/src/slides/DropDown/DropDown.ts","typescript/lib/lernfragen/src/slides/Order/Order.ts","typescript/lib/lernfragen/src/slides/DragDropCloze/DragDropCloze.ts","typescript/lib/lernfragen/src/slides/DragDropSentence/DragDropSentence.ts","typescript/lib/lernfragen/src/slides/MatrixChoice/MatrixChoice.ts","typescript/lib/lernfragen/src/slides/MatrixBinaryChoice/MatrixBinaryChoice.ts","typescript/lib/lernfragen/src/setup/slides.ts","typescript/lib/lernfragen/src/LernFragen.ts","typescript/src/templates/lernfragen.ts","typescript/src/templates/slides.ts","typescript/src/templates/icons.ts","typescript/lib/lernfragen/src/decorators/EvaluateAnswers.ts","typescript/lib/lernfragen/src/decorators/DisableInputs.ts","typescript/lib/lernfragen/src/decorators/ContainerFeedback.ts","typescript/src/slides/BooleanBranchModel.ts","typescript/src/slides/BinaryChoiceModel.ts","typescript/src/decorators/ChallengeFeedback.ts","typescript/src/slides/BooleanBranch.ts","typescript/src/navigation/ChallengeNavigation.ts","typescript/src/slides/BinaryChoice.ts","typescript/src/views/Result.ts","typescript/src/views/CertificateForm.ts","typescript/src/navigation/Status.ts","node_modules/es6-promise/dist/es6-promise.js","node_modules/es6-promise/auto.js","typescript/lib/ts-lib/src/core/request/Request.ts","typescript/lib/ts-lib/src/core/setup/Asset.ts","typescript/lib/ts-lib/src/core/setup/setup.ts","typescript/lib/ts-lib/src/core/lang/Lang.ts","typescript/src/Main.ts"],"sourcesContent":["\n\ninterface Slot {\n\tlistener:T;\n\tcontext:any;\n\tisOnce:boolean;\n}\n\n/**\n * A common interface for signal callback that return nothing\n */\nexport interface EmptyCallback {\n\t()\n}\n\n/**\n* A signal class that is inspired by https://github.com/robertpenner/as3-signals\n* This is an alternative to the classical Event System.\n* It wraps an event type into a property of a class and allows to register directly on the property\n*/\nexport class Signal {\n\t\n\tslots:Slot[] = [];\n\n\t/**\n\t\t* Registers a function to this signal.\n\t\t*/\n\tpublic add( listener:T, context:any = null ):void {\n\t\tthis.slots.push( { listener:listener, context:context, isOnce:false } );\n\t}\n\t\n\t/**\n\t\t* Registers a function to this signal only once\n\t\t*/\n\tpublic addOnce( listener:T, context:any = null ):void {\n\t\tthis.slots.push( { listener:listener, context:context, isOnce:true } );\n\t}\n\t\n\t/**\n\t\t* Removes a listener from the signal\n\t\t*/\n\tpublic remove( listener:T, context:any = null ) {\n\t\tthis.slots = this.slots.filter( function( slot ) {\n\t\t\treturn slot.listener != listener || slot.context != context;\n\t\t});\n\t}\n\n\t/**\n\t * Removes all listeners from signal\n\t */\n\tpublic removeAll() {\n\t\tthis.slots = [];\n\t}\n\t\n\t/**\n\t\t* Returns true is this signal has the given listener function added.\n\t\t*/\n\tpublic has( listener:T, context:any = null ) {\n\t\treturn this.slots.some( function( slot ) {\n\t\t\treturn slot.listener == listener && slot.context == context;\n\t\t});\n\t}\n\t\n\t\n\t/**\n\t\t* Dispatches an event with the given arguments\n\t\t*/\n\tpublic dispatch( ...args:any[] ):void {\n\t\t\n\t\tthis.slots.forEach( ( slot ) => {\n\t\t\tvar func:any = slot.listener;\n\t\t\tfunc.apply( slot.context, args );\n\t\t\tif( slot.isOnce ) this.remove( slot.listener, slot.context );\n\t\t});\n\t}\n}\n\n\n\nexport default Signal;","import Signal from '../../signal/Signal';\n\n/**\n\t* This class registers itself on native DOM events.\n\t*/\nclass DOMEventSignal extends Signal {\n\t\n\tprotected target:EventTarget;\n\tprotected event:string;\n\tprotected callback:( event ) => void = null;\n\t\n\tconstructor( target:EventTarget, event:string ) {\n\t\t\n\t\tsuper();\t\t\t\n\t\tthis.target = target;\n\t\tthis.event = event;\n\t}\n\t\n\t/**\n\t\t* Registers a function to this signal.\n\t\t*/\n\tpublic add( listener:T, context:any = null ):void {\n\t\t\n\t\tif( this.callback === null ) this.register();\n\t\tsuper.add( listener, context );\n\t}\n\t\n\t/**\n\t\t* Registers a function to this signal only once\n\t\t*/\n\tpublic addOnce( listener:T, context:any = null ):void {\n\t\tif( this.callback === null ) this.register();\n\t\tsuper.addOnce( listener, context );\n\t}\n\t\n\t/**\n\t\t* Removes a listener from the signal\n\t\t*/\n\tpublic remove( listener:T, context:any = null ) {\n\t\tsuper.remove( listener, context );\n\t\tif( this.slots.length == 0 ) this.unregister();\n\t}\n\t\n\t/**\n\t\t* Registers the event on the node and setups the callback function.\n\t\t*/\n\tpublic register() {\n\t\t\n\t\tthis.callback = ( event:Event ) => {\n\t\t\tthis.dispatchEvent( event );\n\t\t};\n\t\t\n\t\tthis.target.addEventListener( this.event, this.callback );\n\t}\n\t\n\t/**\n\t\t* Unregisters the event from the html element\n\t\t*/\n\tpublic unregister() {\n\t\tthis.target.removeEventListener( this.event, this.callback );\n\t\tthis.callback = null;\n\t}\n\t\n\t/**\n\t * \n\t */\n\tprotected dispatchEvent( event:Event ) {\n\t\tthis.dispatch( event );\n\t}\n}\n\nexport default DOMEventSignal;","import DOMEventSignal from './DOMEventSignal';\nimport Node from '../Node';\n\n/**\n * A common interface for all node signals\n */\nexport interface NodeEventCallback {\n\t( node: Node, event:Event )\n}\n\n/**\n * This class registers itself on native DOM events.\n */\nclass NodeSignal extends DOMEventSignal {\n\t\n\tconstructor( public node:Node, event:string ){\n\t\tsuper( node.native, event );\n\t}\n\t\n\tprotected dispatchEvent( event:Event ) {\n\t\tthis.dispatch( this.node, event );\n\t}\n}\n\nexport default NodeSignal;","import NodeSignal from './signal/NodeSignal';\n\n/**\n* #Node\n* \n* Wrapper class for the native HTMLElement node.\n* The class gives generalised access to HTMLElements\n* \n* The node class manages a container from type\n* [HTMLElement](https://developer.mozilla.org/de/docs/Web/API/HTMLElement)\n* and adaptes most common functionality.\n* \n*/\n\nexport class Node {\n\n\t// Properties\n\n\t/**\n\t * The native HTMLElement\n\t */\n\tnative: HTMLElement;\n\n\n\t/**\n\t * All signals\n\t */\n\tscroll: NodeSignal;\n\tmouseleave: NodeSignal;\n\tmouseenter: NodeSignal;\n\tmouseout: NodeSignal;\n\tmouseover: NodeSignal;\n\tmouseup: NodeSignal;\n\tmousedown: NodeSignal;\n\tmousemove: NodeSignal;\n\tclick: NodeSignal;\n\tkeypress: NodeSignal;\n\tkeydown: NodeSignal;\n\tkeyup: NodeSignal;\n\tfocus: NodeSignal;\n\tblur: NodeSignal;\n\tchange: NodeSignal;\n\n\t/**\n\t * Constructs a new node\n\t * \n\t * Dont use the constructor to get/create nodes. Use the Factory methods: Node.fromHTML, Node.fromTag\n\t * \n\t * @param HTMLElement native HTMLElement to wrapp\n\t */\n\tconstructor( native: HTMLElement ) {\n\n\t\tthis.native = native;\n\t\tthis.native[\"_lnNode\"] = this; // inject node instance for later retrieval.\n\n\t\tthis.setupSignals();\n\t}\n\n\t/**\n\t * Static create a node from a HTML string\n\t * or `null` if the string is not valid html\n\t * \n\t * Wrapps the given html string into a new node. If this\n\t * new node has more than on children, return the new node,\n\t * otherwise return the first child.\n\t * \n\t * __Example__\n\t * `var n = Node.fromHTML( '
Demo
' );`\n\t * \n\t * @param html The source for the node\n\t */\n\tstatic fromHTML( html: string ): Node {\n\n\t\t// Wrap the given html string into a new node\n\t\tvar tempDiv = Node.fromTag( 'div' );\n\t\ttempDiv.html = html;\n\n\t\tvar children = tempDiv.children();\n\n\t\t// If there is exactly one child the template has one root node - return this one.\n\t\t// otherwise if there are many or no children return the temp div as the root.\n\t\treturn ( children.length == 1 ) ? children[0] : tempDiv;\n\t}\n\n\t/**\n\t * Static create a new node from a HTML tag\n\t * or `null` if the given tag is invalid\n\t * \n\t * __Example__\n\t * `var n = ln.Node.fromTag( 'div' );`\n\t * \n\t * @param tag\n\t */\n\tstatic fromTag( tag: string ): Node {\n\t\treturn new Node( document.createElement( tag ) );\n\t}\n\n\t/**\n\t * Static function that returns the ln.Node from a native HTMLElement\n\t */\n\tstatic fromNative( native: HTMLElement ): Node {\n\t\tif( native == null ) return null;\n\t\treturn ( native[\"_lnNode\"] ) ? native[\"_lnNode\"] : new Node( native );\n\t}\n\n\t/**\n\t * Gets the value if an HTMLInputElement like an inputfields\n\t */\n\tget value() {\n\t\treturn ( this.native as HTMLInputElement ).value;\n\t}\n\n\t/**\n\t * Sets the value of an HTMLInputElement like an inputfield\n\t */\n\tset value( value: string ) {\n\t\t( this.native as HTMLInputElement ).value = value;\n\t}\n\n\t/**\n\t * Sets the innerHTML with the given html string.\n\t * @param html The HTML-String\n\t */\n\tset html( html: string ) {\n\t\tthis.native.innerHTML = html;\n\t}\n\n\t/**\n\t * Gets the inner html content of the node.\n\t */\n\tget html(): string {\n\t\treturn this.native.innerHTML;\n\t}\n\n\t/**\n\t * Returns the style to directly adjust it.\n\t */\n\tget style(): CSSStyleDeclaration {\n\t\treturn this.native.style;\n\t}\n\n\t/**\n\t * Returns true/false on a checkbox input if its checked or not\n\t */\n\tget checked(): boolean {\n\t\treturn (this.native as HTMLInputElement ).checked;\n\t}\n\tset checked( value:boolean ) {\n\t\t(this.native as HTMLInputElement ).checked = value;\n\t}\n\n\tget data(): any {\n\n\t\t// fallback for older browsers, create dataset object manually\n\t\tif ( this.native.dataset === undefined ) {\n\t\t\tthis.native.dataset = {};\n\t\t\tvar attrs = this.native.attributes;\n\t\t\tfor ( var i = 0; i < attrs.length; i++ ) {\n\t\t\t\tvar attr = attrs[i]\n\t\t\t\tif ( attr.name.substr( 0, 5 ) == \"data-\" ) this.native.dataset[attr.name.substr( 5 )] = attr.value;\n\t\t\t}\n\t\t}\n\n\t\treturn this.native.dataset\n\t}\n\n\t/**\n\t * Returns the full html content of the node.\n\t */\n\ttoString(): string {\n\t\treturn this.native.outerHTML;\n\t}\n\n\n\t/**\n\t * Add new class(es) to the node\n\t * \n\t * Classes are always added distinct\n\t * \n\t * __Example__\n\t * `node.addClass( \"class1\", \"class2\", ... )`\n\t * \n\t * @param classname\n\t * @param classlist Typescript restparameter: A list of optional strings\n\t */\n\taddClass( classname: string, ...classlist: string[] ): void {\n\t\tthis.setClasses( this.getClasses().concat( classlist.concat( classname ) ) );\n\t}\n\n\t/**\n\t * Remove classes from the node\n\t * \n\t * __Example__\n\t * see addClass()\n\t * \n\t * @param classname\n\t * @param classlist typescript restparameter: A list of optional strings\n\t */\n\tremoveClass( classname: string, ...classlist: string[] ): void {\n\n\t\tclasslist.push( classname );\n\n\t\t// return only the ones that are not in the classlist.\n\t\tvar filtered = this.getClasses().filter( function ( value ) {\n\t\t\treturn classlist.indexOf( value ) == -1;\n\t\t});\n\n\t\tthis.setClasses( filtered );\n\t}\n\n\t/**\n\t * Toggle a class from this node\n\t * \n\t * __Example__\n\t * `n.toggleClass( 'class2', true );`\n\t * Would result in `class2` still be set.\n\t * \n\t * @param classname\n\t * @param force When force is set to true, the class is set in any case. \n\t * When force is set to false, the class is removed in any case.\n\t */\n\ttoggleClass( classname: string, force?: boolean ): void {\n\n\t\tif ( force == undefined ) {\n\t\t\t( this.hasClass( classname ) ) ? this.removeClass( classname ) : this.addClass( classname );\n\t\t} else {\n\t\t\t( force ) ? this.addClass( classname ) : this.removeClass( classname );\n\t\t}\n\t}\n\n\t/**\n\t * Get the (distinct) classnames from this node as an array\n\t * \n\t * @return The classes are always distinct\n\t */\n\tprivate getClasses(): string[] {\n\n\t\tvar className:any = this.native.className;\n\t\t\n\t\t// fallback for svg elements\n\t\tif( className instanceof SVGAnimatedString ) className = className.baseVal;\n\n\t\treturn className === \"\" ? [] : className.split( ' ' );\n\t}\n\n\t/**\n\t * Set (distinct) classes to this node\n\t * \n\t * @param classnames\n\t */\n\tprivate setClasses( classnames: string[] ): void {\n\n\t\tvar distinct = classnames.filter( function ( value, index, self ) {\n\t\t\treturn self.indexOf( value ) === index;\n\t\t});\n\n\t\tthis.native.className = distinct.join( ' ' );\n\t}\n\n\t/**\n\t * Check if the classname exists in the classlist of this node\n\t * \n\t * @param classname Classname to be checked\n\t * @return `true` if the class exists, `false` else\n\t */\n\thasClass( classname: string ): boolean {\n\t\treturn this.getClasses().indexOf( classname ) > -1;\n\t}\n\n\t/**\n\t * Sets an attribute to this node\n\t * \n\t * @param name Name of the attribute\n\t * @param value Value of the attribute\n\t */\n\tsetAttribute( name: string, value: string ): void {\n\t\tthis.native.setAttribute( name, value );\n\t}\n\n\t/**\n\t * Gets the value of an attribute of this node\n\t * or `null` if the specified attribute does not exist\n\t * \n\t * @param name Name of the attribute\n\t */\n\tgetAttribute( name: string ): string {\n\t\treturn this.native.getAttribute( name );\n\t}\n\n\t/**\n\t * Append a child node to this node\n\t * \n\t * The node is inserted as last child of this node\n\t * \n\t * @param n Node to append\n\t */\n\tappend( n: Node | DocumentFragment | Node[] ): void {\n\n\t\t// handle array\n\t\tif ( Array.isArray( n ) ) {\n\n\t\t\tn.forEach( node => {\n\t\t\t\tthis.native.appendChild( node.native );\n\t\t\t});\n\n\t\t\treturn;\n\t\t}\n\n\t\t// handle node/fragment\n\t\tthis.native.appendChild(( n instanceof Node ) ? n.native : n );\n\t}\n\n\t/**\n\t * Prepend a child node to this element\n\t * \n\t * The node is inserted as first child of this node\n\t * \n\t * @param n Node to prepend \n\t */\n\tprepend( n: Node | DocumentFragment | Node[] ): void {\n\n\t\tvar firstChild = this.native.firstChild;\n\n\t\t// handle array\n\t\tif ( Array.isArray( n ) ) {\n\n\t\t\tn.forEach( node => {\n\t\t\t\tthis.native.insertBefore( node.native, firstChild );\n\t\t\t});\n\n\t\t\treturn;\n\t\t}\n\n\t\t// handle node/fragment\n\t\tthis.native.insertBefore(( n instanceof Node ) ? n.native : n, firstChild );\n\t}\n\n\t/** \n\t * Inserts a child node before a given node\n\t * \n\t * @param newNode Node to be inserted\n\t * @param index Position at which the node will be inserted.\n\t */\n\tinsert( n: Node | DocumentFragment, index: number = undefined ): void {\n\t\tthis.native.insertBefore(( n instanceof Node ) ? n.native : n, this.native.childNodes[index] || null );\n\t}\n\n\t/**\n\t * Replaces this node in its parent with the given new node\n\t */\n\treplace( n: Node ): void {\n\t\tthis.parent().native.replaceChild( n.native, this.native );\n\t}\n\n\t/**\n\t * Get all the child nodes from this element\n\t * which are HTMLElements ( no comments, no text, no HTMLDocuments )\n\t * and return them as a array of nodes.\n\t * \n\t * @return Array of child nodes\n\t * or an empty array if the node has no child nodes\n\t */\n\tchildren(): Node[] {\n\t\treturn this.toNodes( this.native.children );\n\t}\n\n\t/**\n\t * Checks if this node has child nodes ( child elements )\n\t * \n\t * @return `true` if this node has child nodes, `false` else\n\t */\n\thasChildren(): boolean {\n\t\treturn this.native.children.length !== 0;\n\t}\n\n\t/**\n\t * Checks if the node has the given node as child node\n\t * \n\t * @return `true` if this node has the given node as child, `false` else\n\t */\n\thasChild( n: Node ): boolean {\n\t\treturn this.children().some( function ( node ) {\n\t\t\treturn node === n;\n\t\t});\n\t}\n\n\t/**\n\t * Removes specified child node and returns the node\n\t * \n\t * @param child Child node to remove from this node\n\t * \n\t * Throws an exception if child is actually is not a child\n\t * of this node\n\t */\n\tremoveChild( child: Node ) {\n\t\tthis.native.removeChild( child.native );\n\t}\n\n\t/**\n\t * removes this node from the parent\n\t */\n\tremove() {\n\t\tif ( this.native.parentElement ) {\n\t\t\tthis.native.parentElement.removeChild( this.native );\n\t\t}\n\t}\n\n\t/**\n\t * Removes all children\n\t */\n\tempty() {\n\t\tfor ( var i = this.native.children.length; i--; ) {\n\t\t\tthis.native.removeChild( this.native.children[i] );\n\t\t}\n\t}\n\n\t/**\n\t * Returns the parent HTMLElement from this node\n\t * \n\t * @return The parent node of this node\n\t * or null if this node has no parent\n\t */\n\tparent(): Node {\n\n\t\tvar p = this.native.parentElement;\n\t\tif ( p === null ) return null;\n\n\t\treturn Node.fromNative( p );\n\t}\n\n\t/**\n\t * Returns the bounding box of this node including values for top, bottom, left, right\n\t * @param relative A flag that specifies if the bounding box coordinates should be relative to the viewport.\n\t */\n\tbounds( relative: boolean = false ): { top: number; left: number; right: number; bottom: number; height: number; width: number  } {\n\n\t\tvar c = this.native.getBoundingClientRect();\n\t\tvar r = {  top: c.top, left: c.left, right: c.right, bottom: c.bottom, height: c.height, width: c.width };\n\n\t\tif ( !relative ) {\n\t\t\tr.top += document.body.scrollTop || document.documentElement.scrollTop;\n\t\t\tr.bottom += document.body.scrollTop || document.documentElement.scrollTop;\n\t\t\tr.left += document.body.scrollLeft || document.documentElement.scrollLeft;\n\t\t\tr.right += document.body.scrollLeft || document.documentElement.scrollLeft;\n\t\t}\n\n\t\tif ( r.height == undefined ) r.height = r.bottom - r.top;\n\t\tif ( r.width == undefined ) r.width = r.right - r.left;\n\n\t\treturn r;\n\t}\n\n\t/**\n\t * Returns the computed css style.\n\t */\n\tcss( property ) {\n\t\treturn ( this.native.currentStyle ) ? this.native.currentStyle[ property ] : document.defaultView.getComputedStyle( this.native, null ).getPropertyValue( property );\n\t}\n\n\t/**\n\t * Queries this node for the first child node by the specified selector\n\t * and returns it without removing it\n\t * \n\t * @param selector A CSS selector\n\t * @return The first node with the specified selector or `null` if a matching node was not found\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tone( selector: string ): Node {\n\n\t\tvar htmlElement = this.native.querySelector( selector );\n\n\t\tif ( htmlElement !== null ) {\n\t\t\treturn Node.fromNative( htmlElement );\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Queries this node for all the child nodes by the specified selector\n\t * and returns them as an array\n\t * \n\t * @param selector A CSS selector\n\t * @return Array of nodes (An empty array if there are no elements with\n\t * the specified selector )\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tall( selector: string ): Node[] {\n\t\treturn this.toNodes( this.native.querySelectorAll( selector ) );\n\t}\n\n\t/**\n\t * Helper function for getting nodes with a js attribute from within the node.\n\t * Gets only the first node\n\t * \n\t * @param key A key used to build the CSS selector [js=key] \n\t * @return The first node or `null` if no matching node was found\n\t */\n\tjs( key: string ): Node {\n\t\treturn this.one( '[js=' + key + ']' );\n\t}\n\n\t/**\n\t * Queries the document for the first node by the specified selector\n\t * and returns it\n\t * \n\t * @param selector A CSS selector\n\t * @return The first node with the specified selector or `null` if a matching node was not found\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tstatic one( selector: string ): Node {\n\t\tvar tempNode = new Node( document.body );\n\t\treturn tempNode.one( selector );\n\t}\n\n\t/**\n\t * Queries the document for all the nodes by the specified selector\n\t * and returns them as an array\n\t * \n\t * @param selector A CSS selector\n\t * @return Array of nodes or an emtpy array if there are no elements\n\t * with the specified selector\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tstatic all( selector: string ): Node[] {\n\t\tvar tempNode = new Node( document.body );\n\t\treturn tempNode.all( selector );\n\t}\n\n\t/**\n\t * Helper function for getting nodes with a js attribute from the document.\n\t * Gets only the first occurence.\n\t * \n\t * @param key A key used to build the CSS selector [js-node=key] \n\t * @return The first node or `null` if no matching node was found\n\t */\n\tstatic js( key: string ): Node {\n\t\tvar tempNode = new Node( document.body );\n\t\treturn tempNode.js( key );\n\t}\n\n\tstatic get body():Node {\n\t\treturn Node.fromNative( document.body );\n\t}\n\n\t/**\n\t * Helper function that setups all signals properly\n\t */\n\tprivate setupSignals() {\n\t\tvar events = ['scroll',\n\t\t\t'mouseleave',\n\t\t\t'mouseenter',\n\t\t\t'mouseout',\n\t\t\t'mouseover',\n\t\t\t'mouseup',\n\t\t\t'mousedown',\n\t\t\t'mousemove',\n\t\t\t'click',\n\t\t\t'keypress',\n\t\t\t'keydown',\n\t\t\t'keyup',\n\t\t\t'focus',\n\t\t\t'blur',\n\t\t\t'change'];\n\n\t\tfor ( var i = 0; i < events.length; i++ ) {\n\t\t\tthis[events[i]] = new NodeSignal( this, events[i] );\n\t\t}\n\t}\n\n\t/**\n\t * Turns a string into a document fragment\n\t */\n\tpublic static fragment( html: string ): DocumentFragment {\n\n\t\t// browser check if its available\n\t\ttry {\n\t\t\treturn document.createRange().createContextualFragment( html );\n\t\t} catch ( e ) {\n\n\t\t\t// for older browsers make temp document and loop over nodes\n\t\t\tvar frag = document.createDocumentFragment(),\n\t\t\t\tbody = document.createElement( 'body' ), c;\n\t\t\tbody.innerHTML = html;\n\n\t\t\twhile ( c = body.firstElementChild ) frag.appendChild( c );\n\n\t\t\treturn frag;\n\t\t}\n\t}\n\n\t/**\n\t * Helper to turn a HTML Collection or any list into an array of nodes\n\t */\n\tprivate toNodes( collection: any ): Node[] {\n\n\t\tvar temp = [];\n\n\t\tfor ( var i = 0; i < collection.length; i++ ) {\n\t\t\tif ( collection[i] instanceof Element ) {\n\t\t\t\ttemp.push( Node.fromNative( collection[i] ) );\n\t\t\t}\n\t\t}\n\n\t\treturn temp;\n\t}\n}\n\nexport default Node;\n\n\n\n","\n/**\n * An interface for any source for the LookupDict\n */\nexport interface LookupSource {\n\tlookup( key:string ):any;\n}\n\n\n/**\n\t* This class provides a key:value lookup mechanism where multiple sources can be added.\n\t*/\nclass LookupDict {\n\t\n\tprivate sources:LookupSource[] = [];\n\t\n\t/**\n\t\t* Adds a new source to the dictionary to search for keys\n\t\t*/\n\tadd( source:LookupSource ) {\n\t\tthis.sources.push( source );\n\t}\n\t\n\t\n\t/**\n\t\t* Returns the value stored behind the given key in the first of all sources\n\t\t*/\n\tget( key:string, fallback?:string ):any {\n\t\t\n\t\tvar result, i, s = this.sources;\n\n\t\t// loop over sources to find key.\n\t\tfor( i = 0; i < s.length; i++ ) {\n\t\t\tresult = s[ i ].lookup( key );\n\t\t\tif( result != null ) return result;\n\t\t}\n\t\t\n\t\t// if fallback is provided show that\n\t\tif( fallback !== undefined ) return fallback;\n\t\t\n\t\t// not found\n\t\treturn '!{' + key + '}'; \n\t}\n}\n\n\n\nexport default LookupDict;","/**\n * A collection of js specific helper functions.\n */\n\ntype baseTypes = \"Array\" | \"Object\" | \"String\" | \"Date\" | \"RegExp\" | \"Function\" | \"Boolean\" | \"Number\" | \"Null\" | \"Undefined\";\n\n/**\n * Helper function that checks if the given input is any of the given types \n */\nexport function isType( input:any, checkOn:baseTypes | Array ):boolean {\n\n\t// catch single string input\n\tvar types:Array = ( !Array.isArray( checkOn ) ) ? [ checkOn ] : checkOn;\n\n\t// test on object.\n\tif( types.indexOf( \"Object\" ) >= 0 ) {\n\t\tvar match = input && input.constructor == Object;\n\t\tif( match ) return match;\n\n\t\ttypes.splice( types.indexOf( \"Object\" ), 1 );\n\t}\n\n\t// test anything else\n\tvar inputType = Object.prototype.toString.call( input ).slice(8, -1);\n\treturn types.indexOf( inputType ) >= 0;\n}\n\n\n/**\n * This function applies multiple mixins to the given extendedClass.\n */\nexport function mixin( extendedClass: any, mixins: any[] ) {\n mixins.forEach( baseCtor => {\n Object.getOwnPropertyNames( baseCtor.prototype ).forEach( name => {\n extendedClass.prototype[name] = baseCtor.prototype[name];\n });\n }); \n}\n\n/**\n * Merges multiple objects into one. No deep clone.\n * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\n */\nexport function assign( target:{} = {}, ...args:Object[] ) {\n\n\tvar copy = Object( target );\n\n\targs.forEach( ( source ) => {\n\t\tif( source ) {\n\t\t\tfor( var nextKey in source ) {\n\t\t\t\tif( source.hasOwnProperty( nextKey ) ) copy[ nextKey ] = source[ nextKey ];\n\t\t\t}\n\t\t}\n\t});\n\n\treturn copy;\n}\n\n\n/**\n * Adapted from: http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array\n */\nexport function shuffle(array) {\n var i = array.length, t, randomIndex;\n // While there remain elements to shuffle...\n while (0 !== i) {\n // Pick a remaining element...\n randomIndex = Math.floor(Math.random() * i);\n i -= 1;\n // And swap it with the current element.\n t = array[i];\n array[i] = array[randomIndex];\n array[randomIndex] = t;\n }\n return array;\n}","import { LookupSource } from './LookupDict';\nimport { isType } from '../js';\n\n/**\n\t* A lookup dictionary source that searches on an object for given keys.\n\t* It allows to search in nested objects too.\n\t*/\nclass LookupObject implements LookupSource {\n\t\n\tprivate obj:Object;\n\n\tconstructor( obj:Object ) {\n\t\tthis.obj = ( isType( obj, 'Object' ) ) ? obj : {};\n\t}\n\t\n\t/**\n\t\t* lookups in the object for the given key.\n\t\t*/\n\tlookup( key:string ):any {\n\t\tvar keys = key.split( '.' );\n\t\treturn this.search( this.obj, keys );\n\t}\n\t\n\t/**\n\t\t* Adds a prefix to the search keys on this object\n\t\t*/\n\tprefix( name:string ):void {\n\t\tvar temp = {};\n\t\ttemp[ name ] = this.obj;\n\t\tthis.obj = temp;\n\t}\n\t\n\t/**\n\t\t* Searches in the given object for the first key in the keys array.\n\t\t* If there are sub keys it digs deeper into object hierarchy.\n\t\t*/\n\tprivate search = function( current:Object, keys:string[] ) {\n\t\t\n\t\tvar top = keys.shift();\n\t\t\n\t\t// there are sub keys to search\n\t\tif( keys.length > 0 ) {\n\t\t\treturn ( current[ top ] != undefined ) ? this.search( current[ top ], keys ) : undefined;\n\t\t} else {\n\t\t\treturn current[ top ];\n\t\t}\n\t};\n}\n\nexport default LookupObject;","\n/**\n* An interface of the complied template function containing also its source\n*/\ninterface CompliedTemplate {\n ( data:any ):string;\n source:string;\n}\n\ninterface TemplatePass {\n regex:RegExp;\n mapper:( match:string, inner:string ) => string;\n}\n\n\n/**\n* This class adapts the template implementation of the underscore library:\n* https://github.com/jashkenas/underscore\n*/\nclass TemplateRenderer {\n \n public passes:TemplatePass[] = [];\n public context:any = { };\n \n \n constructor() {\n \n // inject escape function\n this.context.esc = this.escape;\n this.context.empty = this.empty;\n this.context.url = encodeURIComponent;\n\n this.passes = this.defaultPasses();\n }\n \n /**\n * Renders the given data into the given template string\n */\n render( template:string, data:any = {} ):string {\n return this.compile( template )( data );\n }\n \n /**\n * Compiles a template string into a template function.\n */\n compile( templateString:string ):CompliedTemplate {\n \n var source:string = this.parse( templateString );\n var render = new Function( 'data', source );\n var contx = this.context;\n \n // wrap function to adjust context\n var template = function( data ):string {\n return render.call( contx, data );\n }\n \n template.source = source;\n return template;\n }\n \n /**\n * Turns the given template into a function body code.\n */\n parse( template:string ):string {\n \n template = this.sanitize( template );\n \n // loop over all passes\n this.passes.forEach( function( pass ) {\n template = template.replace( pass.regex, pass.mapper );\n });\n \n // finalize full template instructions\n template = \"__t='\" + template + \"';\\n\";\n \n // adjust scope with 'with'\n template = 'with( data ) {\\n' + template + '}\\n';\n \n // add opt variable for optional access\n return 'var __t;\\nvar opt = data;\\nvar __c = this;' + template + 'return __t;'\n }\n \n /**\n * Escapes the given string with html entities\n */\n escape( text:string ):string {\n var reg:RegExp = /[&<>\"'\\/`]/g;\n var lookup = { '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''', '/': '/', '`': '`' };\n text = text + ''; // make string.\n return text.replace( reg, function( match:string ) {\n return lookup[ match ];\n });\n }\n \n /**\n * Tests if the value of the template is empty or not\n */\n empty( data:any ):string {\n return data || data === 0 ? data : '';\n }\n \n /**\n * Sanitizes the given string to avoid escaping of the source\n */\n sanitize( text:string ):string {\n var reg:RegExp = /'|\\\\|\\r|\\n|\\u2028|\\u2029/g;\n var lookup = { \"'\": \"\\\\'\", '\\\\': '\\\\\\\\', '\\r': '\\\\r', '\\n': '\\\\n', '\\u2028': '\\\\u2028', '\\u2029': '\\\\u2029' };\n return text.replace( reg, function( match:string ) {\n return lookup[ match ];\n });\n }\n \n /**\n * Reverts the sanitizes escapes\n */\n unsanitize( text:string ):string {\n var reg:RegExp = /\\\\\\\\|\\\\'|\\\\r|\\\\n|\\\\u2028|\\\\u2029/g;\n var lookup = { '\\\\\\\\': '\\\\', \"\\\\'\": \"'\", '\\\\r': '\\r', '\\\\n': '\\n', '\\\\u2028': '\\u2028', '\\\\u2029': '\\u2029' };\n return text.replace( reg, function( match:string ) {\n return lookup[ match ];\n });\n }\n \n /**\n * Returns the array of default template render passes\n * Most specific match has to be the first.\n */\n defaultPasses():TemplatePass[] {\n \n var revert = this.unsanitize;\n \n return [\n {\n regex:/\\[\\[=([\\s\\S]+?)\\]\\]/g,\n mapper: function( match, inner ) {\n return \"' + __c.empty( \" + revert( inner ) + \" ) + '\";\n }\n },\n {\n regex:/\\[\\[([\\s\\S]+?)\\]\\]/g,\n mapper: function( match, inner ) {\n return \"' + __c.esc( __c.empty( \" + revert( inner ) + \" ) ) + '\";\n }\n },\n {\n regex:/\\[%([\\s\\S]+?)%\\]/g,\n mapper: function( match, inner ) {\n return \"';\\n\" + revert( inner ) + \"\\n__t+='\";\n }\n }\n ]\n }\n}\n\nexport default TemplateRenderer;","import LookupDict from '../lookup/LookupDict';\nimport LookupObject from '../lookup/LookupObject';\nimport TemplateRenderer from './TemplateRenderer';\nimport Lang from '../lang/Lang';\nimport Node from '../node/Node';\n\n/**\n\t* This class manages template from different sources.\n\t* It will look up for templates in the stored sources and renders them with a TemplateRenderer.\n\t* For performance the manager keeps cached versions of looked up templates.\n\t*/\nclass TemplateManager {\n\t\n\tprivate cache:any = {};\n\tprivate lookup:LookupDict;\n\tpublic renderer:TemplateRenderer;\t\n\n\tconstructor() {\n\t\tthis.renderer = new TemplateRenderer();\n\t\tthis.lookup = new LookupDict();\n\t}\n\t\n\t/**\n\t* Renders the template with the given key with the given data into a string. \n\t*/\n\tpublic render( keyOrTemplate:string, data:any = {} ):string {\n\t\t\n\t\tif( this.cache[ keyOrTemplate ] == undefined ) {\n\t\t\tthis.cache[ keyOrTemplate ] = this.renderer.compile( this.lookup.get( keyOrTemplate, keyOrTemplate ) );\n\t\t}\n\t\t\n\t\treturn this.cache[ keyOrTemplate ]( data );\n\t}\n\n\n\t/**\n\t * Renders the template into a document fragment\n\t */\n\tpublic document( key:string, data:any = {} ):DocumentFragment {\n\t\treturn Node.fragment( this.render( key, data ) );\n\t}\n\t\n\tpublic get context():any {\n\t\treturn this.renderer.context;\n\t}\n\n\t/**\n\t * Adds a template lookup object\n\t * @param obj \n\t */\n\tpublic add( obj:Object ) {\n\t\tthis.lookup.add( new LookupObject( obj ) );\n\t}\n}\n\n// create singleton to export.\nvar singleton = new TemplateManager();\n\n/**\n * Add import function to TemplateRenderer context\n */\nsingleton.renderer.context[\"import\"] = function( key:string, data?:any ) {\n\treturn singleton.render( key, data );\n}\n\n\nexport default singleton;","import Node from '../node/Node';\nimport { scanner } from './Scanner';\nimport Template from '../template/TemplateManager';\n\n/**\n * A common view class that has a container to render content/template into.\n */\nclass View {\n\n\tpublic data:any\n\tpublic node:Node;\n\n\t// keep this for backward compatibility\n\tpublic defaultTemplate:string = \"\";\n\n\n\tconstructor( data:any = {} ) {\n\t\tthis.data = data;\n\t}\n\t\n\t\n\t/**\n\t * Renders either the given template as a node.\n\t * Or simply sets the given node.\n\t */\n\tpublic render( target?:Node ):View {\n\n\t\t// is there a target node given\n\t\tif( target instanceof Node ) {\n\t\t\tthis.node = target;\n\n\t\t\t// check if empty node - render template\n\t\t\tif( target.html == \"\" ) {\n\t\t\t\tvar temp = this.renderTemplate();\n\t\t\t\tthis.node.html = temp.html; // set only inner html\n\n\t\t\t\t// set css if there is no style defined on target\n\t\t\t\tif( !this.node.getAttribute( 'class' ) ) this.node.setAttribute( 'class', temp.getAttribute( 'class' ) );\n\t\t\t}\n\n\t\t} else {\n\t\t\tthis.node = this.renderTemplate();\n\t\t}\n\n\t\tthis.init();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * The function is called when rendering is done and the node is available.\n\t * Usefull for subclasses to initialize after the node is ready. \n\t */\n\tprotected init() {\n\t\t// do in subclass.\n\t}\n\n\t/**\n\t * This function returns the data that should be rendered into the template\n\t */\n\tprotected renderData():any {\n\t\treturn this.data;\n\t}\n\n\tprotected renderTemplate():Node {\n\n\t\t// for backward compatibility\n\t\tif( !this.data.template ) this.data.template = this.defaultTemplate;\n\n\t\treturn Node.fromHTML( Template.render( this.data.template, this.renderData() ) );\n\t}\n\n}\n\nexport default View;","/**\n\t* A class to register closure functions under a given key\n\t* ...\n\t*/\nclass IoC{\n\t\n\tprivate map:{ [index:string]: T };\n\t\n\tconstructor(){\n\t\tthis.map = {};\n\t}\n\t\n\t/**\n\t* Registers the given closure under the given key\n\t* @param key The key to register the closure\n\t* @param closure The closure function\n\t*/\n\tpublic add( key:string, closure:T ){\n\t\tthis.map[ key ] = closure;\n\t}\n\t\n\t/**\n\t* Returns the registered closure under the given key\n\t* @param key The key of registerd closure to look for\n\t* @return The closure under the given key or undefined, if\n\t* the key does not exist\n\t*/\n\tpublic get( key:string ):T{\n\t\tvar tmp = this.map[ key ];\n\t\ttmp = ( tmp ) ? tmp : this.map[ 'default' ];\n\n\t\tif( !tmp ) throw new Error( \"No key with name: '\" + key + \"' is registered and no default fallback is defined on ioc.\" );\n\t\t\n\t\treturn tmp;\n\t}\n\t\n\t/**\n\t* Returns this key is registered allready\n\t* @param key The key to check if it is registered allready\n\t*/\n\tpublic has( key:string ):boolean{\n\t\treturn this.map[ key ] != undefined;\n\t}\n\t\n\t/**\n\t* Returns all registered keys\n\t* @return Array of all the registered keys\n\t*/\n\tpublic keys():string[] {\n\t\treturn Object.keys( this.map );\n\t}\n\n\t/**\n\t * Creates an alias for an already registered closure\n\t * @param newName The alias name\n\t * @param oldName The name of the already registered function\n\t */\n\tpublic alias( newName:string, oldName:string ) {\n\t\tthis.map[ newName ] = this.map[ oldName ];\n\t}\n\n\t/**\n\t * Renames an already registered closure to a new name\n\t * @param newName The new name \n\t * @param oldName The old registered name \n\t */\n\tpublic rename( newName:string, oldName:string ) {\n\t\tthis.alias( newName, oldName );\n\t\tdelete this.map[ oldName ];\n\t}\n}\n\nexport default IoC;","import IoC from '../ioc/IoC';\nimport Node from '../node/Node';\nimport View from './View';\n\n\nexport interface FactoryFunction {\n\t( node:Node, data?:any ):View;\n}\n\n\n/**\n * This class scans over a html document for views to instantiate and render.\n */\nexport class Scanner {\n\n\tpublic ioc:IoC = new IoC();\n\n\t/**\n\t * Runs the scanner for all registered keys in the ioc.\n\t * @param root The root node where the scanner should search\n\t * @param data The data that is passed to the ioc function\n\t * @param order An optional order to make sure the scanner initializes in the correct order.\n\t */\n\tscan( root:Node, data:any = undefined, order:string[] = [] ):void {\n\n\t\t// sort keys based on given order\n\t\tvar keys = order.concat( this.ioc.keys() );\n\n\t\t// make sure keys are unique\n\t\tkeys = keys.filter( function( value, index, self ) { \n\t\t\treturn self.indexOf( value ) === index;\n\t\t});\n\n\t\t// get all registered keys.\n\t\tkeys.forEach( key => {\n\n\t\t\t// css lookup for all nodes with the given view attribute\n\t\t\tvar nodes = root.all( '[view=' + key + ']' );\n\t\t\t\n\t\t\tnodes.forEach( node => {\n\n\t\t\t\t// do not scan if its already created\n\t\t\t\tif( node.native.view ) return;\n\n\t\t\t\t// create view from ioc\n\t\t\t\tvar view = this.ioc.get( key )( node, data );\n\n\t\t\t\t// store instance on native node\n\t\t\t\tif( view ) node.native.view = view;\n\t\t\t});\n\t\t});\n\t}\n\n\tfirst( name:string, root?:Node ) {\n\t\tif( root == undefined ) root = Node.fromNative( document.body );\n\t\tvar node = root.one( '[view=' + name + ']' );\n\t\treturn ( node && node.native.view ) ? node.native.view : null;\n\t}\n\n\t\n\tall( name:string, root?:Node ) {\n\n\t\tif( root == undefined ) root = Node.fromNative( document.body );\n\n\t\tvar views = [];\n\t\t\n\t\tvar nodes = root.all( '[view=' + name + ']' );\n\t\tnodes.forEach( node => {\n\t\t\tif( node.native.view ) views.push( node.native.view );\n\t\t});\n\t\t\n\t\treturn views;\n\t}\n}\n\nexport var scanner = new Scanner();\n\nexport default Scanner;\n","import Signal from '../signal/Signal';\nimport * as js from '../js';\n\n/**\n\t* Function interface for listeners on the 'update' Signal. \n\t*/\nexport interface AttributeChangeListener {\n\t( name:string, newValue:any, oldValue:any ):void;\n}\n\n/**\n\t* A common model class that has getters and setters \n\t*/\nclass Model {\n\t\n\tprotected _obj:Object;\n\tpublic change:Signal;\n\t\n\t\n\tconstructor( obj:Object = {} ) {\n\t\tthis._obj = obj;\n\t\tthis.change = new Signal();\n\t\tthis.generateAccessors();\n\t}\n\t\n\t/**\n\t\t* Returns the value of the model with the given key.\n\t\t* If the key does not exists the given optional fallback value is returned.\n\t\t* @param key The key on the model to lookup\n\t\t* @param fallback The fallback value when the key does not exists.\n\t\t*/\n\tget( key:string, fallback:T = undefined ):T {\n\t\tvar value = this._obj[ key ];\n\t\treturn ( value !== undefined ) ? value : fallback;\n\t}\n\n\n\t/**\n\t\t* Sets the given value on the given key in the model.\n\t\t* @param key The key on the model to adjust its value\n\t\t* @param value The new value to set of the model\n\t\t*/\n\tset( key:string, value:any ) {\n\t\t\n\t\tvar isNew = this._obj[ key ] == undefined;\n\t\tvar old = this._obj[ key ];\n\t\tif( old != value ) {\n\t\t\tthis._obj[ key ] = value;\n\t\t\tthis.change.dispatch( key, value, old );\n\t\t}\n\t\tif( isNew ) this.generateAccessors();\n\t}\n\t\n\t/**\n\t * Return a clone of this object\n\t */\n\tpublic object():any{\n\t\tvar obj = {};\n\t\tfor( var attr in this._obj ){\n\t\t\tobj[attr] = this._obj[attr];\n\t\t}\n\t\treturn obj;\n\t}\n\t\n\t/**\n\t * Syncs the attributes of this model with the given object attributes\n\t * It only syncs builtin types. other types have to sync manually.\n\t */\n\tpublic sync( obj:any ) {\n\t\tfor( var property in obj ) {\n\t\t\tif( js.isType( obj[property], [ \"String\", \"Date\", \"Boolean\", \"Number\" ] ) ) {\n\t\t\t\tthis.set( property, obj[property] );\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic generateAccessors() {\n\n\t\tvar createProperty = ( name ) => {\n\t\t\tObject.defineProperty( this, name, {\n\t\t\t\tget() {\n\t\t\t\t\treturn this.get( name );\n\t\t\t\t},\n\t\t\t\tset( value ) {\n\t\t\t\t\tthis.set( name, value );\t\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tfor( var attr in this._obj ){\n\t\t\tif( Object.getOwnPropertyDescriptor( this, attr ) == undefined ) {\n\t\t\t\tcreateProperty( attr );\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport default Model;","import IoC from '../ioc/IoC';\nimport Model from './Model';\nimport { isType } from '../js';\n\nexport interface ModelFactoryFunction {\n\t( json:Object ):any\n}\n\nexport interface JsonFactoryFunction {\n\t( model:Model ):Object\n}\n\n/**\n * A class that helps to map models on a json structure\n */\nclass Mapper {\n\n\tpublic toModel:IoC = new IoC();\n\tpublic toJson:IoC = new IoC();\n\t\n\tpublic jsonLookup:( json:Object ) => string;\n\tpublic modelLookup:( model:Model ) => string;\n\n\n\n\n\tconstructor() {\n\n\t\t// setup default json to model conversion.\n\t\tthis.toModel.add( 'default', function( json:Object, data?:any ) {\n\t\t\treturn json;\n\t\t});\n\t\t\n\t\t// setup default model to json conversion\n\t\tthis.toJson.add( 'default', function( model:Model, data?:any ) {\n\t\t\treturn model;\n\t\t});\n\n\t\t// setup default lookup function for json\n\t\tthis.jsonLookup = function( json:Object ) {\n\t\t\treturn json[\"modelName\"];\n\t\t}\n\n\t\t// setup default lookup function for models\n\t\tthis.modelLookup = function( model:Model ) {\n\t\t\treturn ( model instanceof Model ) ? model.get( 'modelName' ) : 'default';\n\t\t}\n\t}\n\n\n\n\n\n\tpublic model( json:any ) {\n\n\t\t// if primitive type is given simply return it\n\t\tif( isType( json, [ \"String\", \"Date\", \"RegExp\", \"Function\", \"Boolean\", \"Number\", \"Null\", \"Undefined\" ] ) ) {\n\t\t\treturn json;\n\t\t}\n\n\t\t// if json array is given map each\n\t\tif( isType( json, \"Array\" ) ) {\n\t\t\treturn ( json as Array ).map( ( item ) => {\n\t\t\t\treturn this.model( item );\n\t\t\t});\n\t\t}\n\n\t\t// Object given make recursion over keys\n\t\tObject.keys( json ).forEach( ( name ) => {\n\t\t\tvar value = json[ name ];\n\n\t\t\t// only follow object or array references\n\t\t\tif( isType( value, [ \"Object\", \"Array\" ] ) ) json[ name ] = this.model( value );\n\t\t});\n\n\t\t// Finally turn object into model with ioc\n\t\treturn this.toModel.get( this.jsonLookup( json ) )( json );\n\t}\n\n\n\n\n\n\tpublic json( model:any ) {\n\n\t\t// if primitive type is given simply return it\n\t\tif( isType( model, [ \"String\", \"Date\", \"RegExp\", \"Function\", \"Boolean\", \"Number\", \"Null\", \"Undefined\" ] ) ) {\n\t\t\treturn model;\n\t\t}\n\n\t\t// if json array is given map each\n\t\tif( isType( model, \"Array\" ) ) {\n\t\t\treturn ( model as Array ).map( ( item ) => {\n\t\t\t\treturn this.json( item );\n\t\t\t});\n\t\t}\n\n\t\t// try to convert it with ioc\n\t\tmodel = this.toJson.get( this.modelLookup( model ) )( model );\n\n\t\t// if object given make recursion over keys\n\t\tObject.keys( model ).forEach( ( name ) => {\n\t\t\tmodel[ name ] = this.json( model[ name ] );\n\t\t});\n\n\t\t// any other types simply return\n\t\treturn model;\n\t}\n}\n\nexport var mapper = new Mapper();\n\nexport default Mapper;","\n/**\n\t* A class to link elements together as double linked list.\n\t* Each item of the list points to its next or previous element\n\t*/\nclass LinkedList {\n\t\n\tnext:LinkedList;\n\tprevious:LinkedList;\n\t\n\t/**\n\t\t* Get the first linked list node\n\t\t* @return The first element of the linked list\n\t\t*/\n\tpublic getFirst():LinkedList{\n\t\treturn ( !this.previous ) ? this : this.previous.getFirst();\n\t}\n\t\n\t/**\n\t\t* Get the last linked list node\n\t\t* @return The last element of the linked list\n\t\t*/\n\tpublic getLast():LinkedList{\n\t\treturn ( !this.next ) ? this : this.next.getLast();\n\t}\n\t\n\t/**\n\t* Sets the given node as next and this node as previous for the given one\n\t* Usefull to quickly connect nodes.\n\t* Does not connect the nodes if the given one has a previous element already\n\t* @param node The node that should be connected as next\n\t*/\n\tpublic addNext( node:LinkedList, force?:boolean ){\n\t\tif( node !== null && ( node.previous === null || force == true ) ){\n\t\t\tthis.next = node;\n\t\t\tnode.previous = this;\n\t\t}\n\t}\n\t\n\t/**\n\t* Sets the given node as previous and this node as next for the given one\n\t* Usefull to quickly connect nodes.\n\t* Does not connect the nodes if the given one has a next element already\n\t* @param node The node that should be connected as previous\n\t*/\n\tpublic addPrevious( node:LinkedList, force?:boolean ){\n\t\tif( node !== null && ( node.next === null || force == true ) ){\n\t\t\tthis.previous = node;\n\t\t\tnode.next = this;\n\t\t}\n\t}\n\t\n\t/**\n\t* Turns the linked list into a regular array.\n\t* @return An array of linkedlist elements\n\t*/\n\tpublic toArray():LinkedList[]{\n\t\tvar node:LinkedList = this.getFirst();\n\t\tvar result:LinkedList[] = [ node ];\n\t\t\n\t\twhile( node.next ){\n\t\t\tnode = node.next;\n\t\t\tresult.push( node );\n\t\t}\n\t\t\n\t\treturn result;\n\t}\n\t\n\t/**\n\t\t* Turns an array into a linked list.\n\t\t* @param array An array of objects\n\t\t* @return The first element of the created linked list\n\t\t*/\n\tpublic static fromArray( array:LinkedList[] ):LinkedList{\n\t\t\n\t\t// got elements?\n\t\tif( array === null || array.length <= 0 ){\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tvar lastIndex:number = array.length - 1, i;\n\t\tvar current:LinkedList;\n\t\t\n\t\tfor( i = 0; i < array.length; i++ ){\n\t\t\t\n\t\t\tcurrent = array[ i ];\n\t\t\t\n\t\t\t// if first element\n\t\t\tif( i === 0 && lastIndex > 0 ){\n\t\t\t\tcurrent.next = array[ i + 1 ]\n\t\t\t\tcurrent.previous = undefined;\n\t\t\t\t\n\t\t\t// if last element\n\t\t\t} else if( i === lastIndex && lastIndex > 0 ){\n\t\t\t\tcurrent.previous = array[ i - 1 ];\n\t\t\t\tcurrent.next = undefined;\n\t\t\t\t\n\t\t\t// somewhere in the middle\n\t\t\t} else {\n\t\t\t\tcurrent.next = array[ i + 1 ];\n\t\t\t\tcurrent.previous = array[ i - 1 ];\n\t\t\t}\n\t\t}\n\t\t\n\t\t// return the first element\n\t\treturn array[ 0 ];\n\t}\n}\n\nexport default LinkedList;","\nexport interface ITreeNode {\n\tparent: TreeNode;\n\treadonly children: TreeNode[];\n}\n\nclass TreeNode implements ITreeNode {\n\t\n\tpublic parent: TreeNode;\n\tpublic children: TreeNode[];\n\n\t/**\n\t * Assign this to all children as parent\n\t */\n\tpublic assignParent() {\n\t\tif ( !this.children ) return;\n\n\t\tthis.children.forEach( function ( child ) {\n\t\t\tchild.parent = this;\n\t\t}, this );\n\t\n\t}\n\n\t/**\n\t * Get the root node of a given node\n\t */\n\tpublic root(): TreeNode {\n\t\treturn ( this.parent == undefined ) ? this : this.parent.root();\n\t}\n\n\t/**\n\t * Get all nodes and leafs of one level\n\t */\n\tpublic level( level: number ): TreeNode[] {\n\t\treturn this._level( level, this, 0 );\n\t}\n\n\tprotected _level( level: number, currentTreeNode: TreeNode, currentDepth: number ): TreeNode[] {\n\t\tvar result: TreeNode[] = [];\n\n\t\tif ( level == currentDepth ) {\n\t\t\tresult.push( currentTreeNode );\n\t\t} else if ( level > currentDepth && Array.isArray( currentTreeNode.children ) ) {\n\t\t\tcurrentTreeNode.children.forEach(( child ) => {\n\t\t\t\tresult = result.concat( this._level( level, child, currentDepth + 1 ) );\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t}\n\n}\n\nexport default TreeNode;\n","// import lib\nimport Model from 'ln/model/Model';\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport TreeNode from 'ln/tree/TreeNode';\nimport { mixin } from 'ln/js';\n\nimport SlideModel from '../Slide/SlideModel';\nimport LernFragen from '../../LernFragen';\n\n\nclass ChapterModel extends Model implements TreeNode, LinkedList {\n\n\t// Mixin with TreeNode\n\tpublic assignParent: () => void;\n\tpublic root: () => TreeNode;\n\tpublic level: ( level:number ) => TreeNode[];\n\tpublic _level: ( level:number, current:TreeNode, currentDepth:number ) => TreeNode[];\n\tpublic parent: TreeNode;\n\n\t// Mixin with LinkedList\n\tpublic next:LinkedList = null;\n\tpublic previous:LinkedList = null;\n\tpublic getFirst: () => LinkedList;\n\tpublic getLast: () => LinkedList;\n\tpublic addNext: () => void;\n\tpublic addPrevious: () => void;\n\tpublic toArray: () => LinkedList[];\n\n\n\tget children():TreeNode[] {\n\t\tif( this.get( 'slides') != undefined ) {\n\t\t\treturn this.get( 'slides' ); \n\t\t} else if ( this.get( 'chapters') != undefined ) {\n\t\t\treturn this.get( 'chapters' );\n\t\t} \n\t}\n\n\t/* public parent():TreeNode {\n\t\treturn this.get( 'parent' ) as TreeNode;\n\t} */\n\n\tget slides():SlideModel[] {\n\t\treturn this.get( 'slides', [] );\n\t}\n\n\tget categories():ChapterModel[] {\n\t\treturn this.get( 'chapters', [] );\n\t}\n\n}\n\n/**\n * Apply the mixin of the classes\n */\nmixin( ChapterModel, [ LinkedList, TreeNode ] );\nexport default ChapterModel;\n\n\n\n\n","// import lib\nimport Model from 'ln/model/Model';\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport TreeNode from 'ln/tree/TreeNode';\nimport { mixin } from 'ln/js';\n// import project\nimport Chapter from '../Chapter/ChapterModel';\n\nclass SlideModel extends Model {\n\n\t// Mixin with TreeNode\n\tpublic parent(): TreeNode {\n\t\treturn this.get( 'parent' ) as TreeNode;\n\t}\n\tpublic children(): TreeNode[] {\n\t\treturn this.get( 'children' ) as TreeNode[];\n\t};\n\tpublic assignParent: () => void;\n\tpublic root: ( treeNode?: TreeNode ) => TreeNode;\n\n\t// Mixin with LinkedList\n\tpublic next: LinkedList = null;\n\tpublic previous: LinkedList = null;\n\tpublic getFirst: () => LinkedList;\n\tpublic getLast: () => LinkedList;\n\tpublic addNext: () => void;\n\tpublic addPrevious: () => void;\n\tpublic toArray: () => LinkedList[];\n\n\tget chapter(): Chapter {\n\t\tif ( this.parent() instanceof Chapter ) return this.parent() as Chapter;\n\t\treturn ( this.parent() as SlideModel ).chapter as Chapter;\n\t}\n\n\t/**\n\t * Returns the model name of this slide\n\t */\n\tget modelName(): string {\n\t\treturn this.get( 'modelName', '' );\n\t}\n\n\t/**\n\t * Returns the name for the aggregators's ioc lookup\n\t * This method may be overwritten by other slide types\n\t */\n\tget aggregatorKey():string {\n\t\treturn this.modelName;\n\t}\n\n\t/**\n\t * Return if this question has a user input\n\t * This is an abstract methode, because every model\n\t * can have different type of user inputs\n\t */\n\tpublic hasUserInput(): boolean {\n\t\treturn false;\n\t}\n\n\t/**\n\t * An abstract methode usually used for question slides\n\t */\n\tpublic markAsAnswered() {\n\t}\n\n}\n\n/**\n * Apply the mixin of the classes\n */\nmixin( SlideModel, [LinkedList, TreeNode] );\n\nexport default SlideModel;\n\n\n\n\n","// import library\nimport Signal from 'ln/signal/Signal';\nimport { EmptyCallback } from 'ln/signal/Signal';\n\n// import project\nimport SlideModel from '../Slide/SlideModel';\n\nexport class QuestionModel extends SlideModel {\n\n\tpublic answered: boolean = false;\n\tpublic userInput: Signal;\n\tpublic userAnswered: Signal;\n\n\t/**\n\t * Create a new question model instance\n\t * @param obj The object to create the model from\n\t */\n\tconstructor( obj: Object = {}) {\n\t\tsuper( obj );\n\n\t\tthis.userInput = new Signal();\n\t\tthis.userAnswered = new Signal();\n\t}\n\n\t/**\n\t * Mark this question as answered\n\t */\n\tpublic markAsAnswered() {\n\t\tthis.answered = true;\n\t\tthis.userAnswered.dispatch( this );\n\t}\n\n\t/**\n\t * Return if this question is answered\n\t */\n\tpublic isAnswered(): boolean {\n\t\treturn this.answered;\n\t}\n\n\t/**\n\t * Return if this question has a user input\n\t * This is an abstract methode, because every model\n\t * can have different type of user inputs\n\t */\n\tpublic hasUserInput(): boolean {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns true if the whole question is correctly solved\n\t * Abstract method - needs to be implemented in the subclasses\n\t */\n\tpublic isCorrect(): boolean {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Fire the event, that the question was answered\n\t */\n\tpublic fireUserAnswered() {\n\t\tthis.userAnswered.dispatch( this );\n\t}\n\n\t/**\n\t * Fire the event, that the question has a new user input\n\t */\n\tpublic fireUserInput() {\n\t\tthis.userInput.dispatch( this );\n\n\t\t// if the question is answered but there is a user input,\n\t\t// fire answered again\n\t\tif ( this.answered ) this.fireUserAnswered();\n\t}\n\n\t\n\n}\n\nexport default QuestionModel;","import QuestionModel from '../Question/QuestionModel';\n\n/**\n * An interface for answers\n */\nexport interface Answer {\n\ttext: string;\n\tcorrect?: boolean;\n\tselected?: boolean;\n}\n\n\n/**\n * A model class for answerlist slides.\n */\nexport class AnswerListModel extends QuestionModel {\n\n\t/**\n\t * The constructor\n\t * \n\t */\n\tconstructor( json ) {\n\t\tsuper( json );\n\t\t// verifiy that the answers have required flags\n\t\tthis.answers.forEach( ( answer ) => {\n\t\t\tif( answer.correct == undefined ) { answer.correct = false; }\n\t\t\tif( answer.selected == undefined ) { answer.selected = false; }\n\t\t});\n\t}\n\n\t/**\n\t * Checks on the slide model if the user has already made a selection.\n\t * This overwrites the default behaviour of the Question slide.\n\t * @return {Boolean} True if there is already something selected on the slide model.\n\t */\n\tpublic hasUserInput():boolean {\n\t\t\n\t\tvar answers = this.answers;\n\n\t\tif( this.answers.length == 0 ) { return true; }\n\n\t\tfor( var i = 0; i < this.answers.length; i++ ) {\n\t\t\tif( this.answers[i].selected ) { return true }\t\t\t\n\t\t}\n\n\t\treturn false;\n\t};\n\n\t/**\n\t * Returns true if all the correct answers are checked and the others not\n\t */\n\tpublic isCorrect():boolean {\n\t\tvar answers = this.answers;\n\t\tvar correct = true;\n\n\t\tfor( var i = 0; i < answers.length; i++ ) {\n\t\t\tcorrect = ( ( answers[i].correct && answers[i].selected ) || ( !answers[i].correct && !answers[i].selected ) ) && correct;\n\t\t}\n\n\t\treturn correct;\n\t};\t\n\n\t/**\n\t * Marks one of the answers as selected\n\t */\n\tpublic select( selectedAnswer:Answer ) {\n\t}\n\n\tget answers():Answer[] {\n\t\treturn this.get( 'answers', []) as Answer[];\n\t}\n\n\tset answers( a:Answer[] ) {\n\t\tthis.set( 'answers', a );\n\t}\n\n}\n\nexport default AnswerListModel;\n","import AnswerListModel from '../AnswerList/AnswerListModel';\nimport { Answer } from '../AnswerList/AnswerListModel';\nimport LernFragen from '../../LernFragen';\n\n/**\n * The model of a SingleChoice slide\n */\nclass SingleChoiceModel extends AnswerListModel {\n\n\t/**\n\t * Marks the given answer as selected. \n\t * This overwrites the default behaviour of AnswerList\n\t */\n\tpublic select( selectedAnswer: Answer ) {\n\n\t\tvar answers = this.answers;\n\t\tanswers.forEach(( answer ) => {\n\t\t\tanswer.selected = false;\n\t\t});\n\n\t\tselectedAnswer.selected = true;\n\t\tthis.fireUserInput();\n\t}\n}\n\nexport default SingleChoiceModel;\n","import { AnswerListModel, Answer } from '../AnswerList/AnswerListModel';\nimport LernFragen from '../../LernFragen';\n\n/**\n * The model of a MultipleChoice slide\n */\nclass MultipleChoiceModel extends AnswerListModel {\n\n\t/**\n\t * Marks the given answer as selected. \n\t */\n\tpublic select( selectedAnswer: Answer ) {\n\n\t\tthis.answers.forEach(( answer ) => {\n\t\t\tif ( answer === selectedAnswer ) { this.toggleSelection( answer ); }\n\t\t});\n\t\tthis.fireUserInput();\n\t}\n\n\t/**\n\t * Toggels the selected flag on an answer\n\t */\n\tprotected toggleSelection( answer: Answer ) {\n\t\tif ( answer.selected ) {\n\t\t\tanswer.selected = false;\n\t\t} else {\n\t\t\tanswer.selected = true;\n\t\t}\n\t}\n\n}\n\nexport default MultipleChoiceModel;\n","// import lib\nimport Model from 'ln/model/Model';\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport { mixin } from 'ln/js';\nimport QuestionModel from '../Question/QuestionModel';\nimport LernFragen from '../../LernFragen';\n\n\n/**\n * An interface for gaps object\n */\nexport interface Gaps {\n\t[index: string]: { answers: string[] };\n}\n\n/**\n * An interface for userInputs object\n */\nexport interface UserInputs {\n\t[key: string]: { value: string };\n}\n\n\n\n/**\n * The model of a Cloze slide\n */\nclass ClozeModel extends QuestionModel {\n\n\t//public gaps: { [index: string]: { answers: string[] } };\n\t//public userInputs: { [key: string]: { value: string } };\n\n\n\t/**\n\t * Create a new question model instance\n\t * @param obj The object to create the model from\n\t */\n\tconstructor( obj: Object = {} ) {\n\t\tsuper( obj );\n\n\t\t// this.gaps = {};\n\t\t// this.userInputs = {};\n\n\t\tthis.parseGaps();\n\t}\n\n\tget gaps():Gaps {\n\t\treturn this.get( 'gaps', {}) as Gaps;\n\t}\n\n\tset gaps( gaps:Gaps ) {\n\t\tthis.set( 'gaps' , gaps );\n\t}\n\n\tget userInputs():UserInputs {\n\t\treturn this.get( 'userInputs', {} ) as UserInputs;\n\t}\n\t\n\tset userInputs( userInputs:UserInputs ) {\n\t\tthis.set( 'userInputs' , userInputs );\n\t}\n\n\t\n\n\t/**\n\t * Returns true if all user inpts are correct\n\t * Overrides the corresponding method of the superclass\n\t */\n\tpublic isCorrect():boolean {\n\t\tvar correct: boolean = true;\n\n\t\tObject.keys( this.gaps ).forEach( ( key ) => {\n\t\t\tcorrect = correct && this.hasCorrectInput( key );\n\t\t});\n\n\t\treturn correct;\n\t}\n\n\t/**\n\t * Returns true if there is a user input\n\t * Overrides the corresponding method of the super class\n\t */\n\tpublic hasUserInput(): boolean {\n\t\treturn Object.keys( this.userInputs ).length > 0;\n\t}\t\n\n\t/**\n\t * Parses the text for gaps and builds the gap definition\n\t * It replaces the text definition with gap labels instead of the gap definition.\n\t */\n\tprivate parseGaps() {\n\n\t\tvar orgText: string = this.get( 'text' ) as string;\n\t\tvar found: RegExpExecArray;\n\t\tvar label: string;\n\t\tvar index: number = 0;\n\n\t\t// regex to match everything between curly bracets\n\t\tvar regex = /{(.*?)}/g;\n\n\t\t// loop over all matches and replace cloze definitions with gap labels.\n\t\twhile ( ( found = regex.exec( orgText ) ) != null ) {\n\t\t\tlabel = 'gap' + index++;\n\t\t\torgText = orgText.substr( 0, found.index ) + '{' + label + '}' + orgText.substr( regex.lastIndex );\n\t\t\tregex.lastIndex = found.index + label.length + 2;\n\n\t\t\tvar gaps = this.gaps;\n\t\t\tgaps[label] = { answers: found[1].split( ';' ).map( function ( value ) { return value.trim() } ) };\n\t\t\tthis.gaps = gaps;\n\t\t}\n\n\t\tthis.set( \"parsedText\", orgText );\n\t}\n\n\t/**\n\t * Returns an array of all the gap labels.\n\t */\n\tpublic getGapLabels():string[] {\n\t\treturn Object.keys( this.gaps );\n\t};\n\n\t/**\n\t * Gets a user input by gap label.\n\t * @param label Gab label\n\t */\n\tpublic getGapUserinput( label: string ):string {\n\t\tif( ! this.userInputs[label]) return null;\n\t\tvar userInputs = this.userInputs;\n\t\treturn userInputs[label].value.replace( /^\\s+|\\s+$|\\s+(?=\\s)/g, \"\" );\n\t}\n\n\t/**\n\t * Set the value of a user input\n\t * @param label Gap label\n\t * @param value The value of the user input field\n\t */\n\tpublic setGapUserinput( label: string, value: string ) {\n\t\tvar userInputs = this.userInputs;\n\n\t\tif ( userInputs[label] ) {\n\t\t\tuserInputs[label].value = value;\n\t\t} else {\n\t\t\tuserInputs[label] = { value: value };\n\t\t}\n\n\t\tthis.userInputs = userInputs;\n\n\t\tthis.fireUserInput();\n\t}\n\n\t/**\n\t * Get the answers of a given gap\n\t * @param label Gap label\n\t */\n\tpublic getGapAnswers( label: string ):string[] {\n\t\treturn this.gaps[label].answers;\n\t}\n\n\t/**\n\t * Is the user input evaluation case sensitive?\n\t */\n\tprivate isCaseSensitive():boolean {\n\t\treturn this.get( 'casesensitive' ) ? this.get( 'casesensitive' ) as boolean : true;\n\t}\n\n\t/**\n\t * Returns if this cloze gaps should all have different inputs\n\t */\n\tprivate isDistinct():boolean {\n\t\treturn this.get( 'distinct' ) ? this.get( 'distinct' ) as boolean : false;\n\t};\n\n\t/**\n\t * Checks if all user inputs ar distinct\n\t */\n\tprivate hasDistinctInput():boolean {\n\t\tvar inputs: Array = [];\n\n\t\tObject.keys( this.userInputs ).forEach( ( key ) => {\n\t\t\tinputs.push( this.userInputs[key].value );\n\t\t});\n\n\t\tvar uniques = inputs.filter( function ( value, index, items ) {\n\t\t\treturn inputs.indexOf( value ) === index;\n\t\t} );\n\n\t\treturn inputs.length === uniques.length;\n\t}\n\n\t/**\n\t * Returns true if the input of the given gap is correct\n\t * @param label Gap label\n\t */\n\tpublic hasCorrectInput( label: string ):boolean {\n\t\tvar answers = this.getGapAnswers( label );\n\t\tvar input = this.getGapUserinput( label );\n\t\tvar casesensitive = this.isCaseSensitive();\n\n\t\t// check for wrong input\n\t\tif ( !input ) return false;\n\n\t\t// check for distinct values\n\t\tif ( this.isDistinct() ) {\n\t\t\tvar distinct = this.hasDistinctInput();\n\t\t\tif ( !distinct ) return false;\n\t\t}\n\n\t\t// check over answers if one was hit\n\t\tfor ( var i = 0; i < answers.length; i++ ) {\n\n\t\t\t// check for casesensitivity\n\t\t\tif ( casesensitive ) {\n\t\t\t\tif ( input.toLowerCase() === answers[i].toLowerCase() ) return true;\n\t\t\t} else {\n\t\t\t\tif ( input === answers[i] ) return true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\n};\n\nexport default ClozeModel;\n","import QuestionModel from '../Question/QuestionModel';\nimport LernFragen from '../../LernFragen';\n\n/**\n * An interface for drag items\n */\nexport interface Drag {\n\ttext: string;\n\ttarget: number;\n\tdropped: number;\n}\n\n/**\n * An interface for drop items\n */\nexport interface Drop {\n}\n\n\n/**\n * A model class for DragDrop slides.\n */\nexport class DragDropModel extends QuestionModel {\n\n\t/**\n\t * The constructor\n\t */\n\tconstructor( json ) {\n\t\tsuper( json );\n\t\tthis.complete();\n\t}\n\n\t/**\n\t * Complete and adjusts the model\n\t */\n\tpublic complete() {\n\t\t// make shure that the dropped and target value is set correctly\n\t\tthis.drags.forEach( ( drag ) => {\n\t\t\tif( drag.dropped === undefined ) {\n\t\t\t\tdrag.dropped = 0;\n\t\t\t}\n\t\t\tif( drag.target == undefined || drag.target < 0 ) {\n\t\t\t\tdrag.target = 0;\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic hasUserInput():boolean {\n\t\tfor( var i = 0; i < this.drags.length; i++ ) {\n\t\t\tif( this.drags[i].dropped > 0 ) return true;\n\t\t}\n\t\treturn false;\n\t};\n\n\tpublic isCorrect():boolean {\n\t\tfor( var i = 0; i < this.drags.length; i++ ) {\n\t\t\tif( this.drags[i].dropped != this.drags[i].target ) { return false };\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tget drags():Drag[] {\n\t\treturn this.get( 'drags', []) as Drag[];\n\t}\n\n\tget drops():Drop[] {\n\t\treturn this.get( 'drops', []) as Drop[];\n\t}\n\n\n}\n\nexport default DragDropModel;","// import lib\nimport Model from 'ln/model/Model';\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport { mixin } from 'ln/js';\n\nimport QuestionModel from '../Question/QuestionModel';\nimport LernFragen from '../../LernFragen';\n\n/**\n * The model of a Freetext slide\n */\nclass FreetextModel extends QuestionModel {\n\n\n\tpublic hasUserInput(): boolean {\n\t\treturn this.value !== undefined && this.value.length > 0;\n\t}\n\n\tset value( v:string ) {\n\t\tthis.set( 'value', v );\n\t\tthis.fireUserInput();\n\t}\n\n\tget value(): string {\n\t\treturn this.get( 'value' ) as string;\n\t}\n\n\t/**\n\t * Only mark as correct if there is input.\n\t */\n\tpublic isCorrect(): boolean {\n\t\treturn this.hasUserInput();\n\t}\n\t\n}\n\nexport default FreetextModel;","import QuestionModel from '../Question/QuestionModel';\nimport LernFragen from '../../LernFragen';\n\ninterface Point {\n\tleft: number;\n\ttop: number;\n}\n\ninterface Area {\n\tleft: number;\n\ttop: number;\n\theight: number;\n\twidth: number;\n}\n\n/**\n * A model class for a HotSpot slide.\n */\n\nclass HotspotModel extends QuestionModel {\n\n\t/**\n\t * Checks on the slide model if the user has already made a selection.\n\t * This overwrites the default behaviour of the Question slide.\n\t * @return True if there is already something selected on the slide model.\n\t */\n\tpublic hasUserInput() {\n\t\treturn this.userPoints.length > 0;\n\t};\n\n\t/**\n\t * Adds a user input point to the model\n\t */\n\tpublic addPoint( left: number, top: number ) {\n\n\t\t// append new point\n\t\tthis.userPoints.push( { left: left, top: top });\n\n\t\t// remove too much userPoints\n\t\twhile ( this.userPoints.length > this.hotspotAreas.length ) {\n\t\t\tthis.userPoints.shift();\n\t\t}\n\t};\n\n\t/**\n\t * Returns the user points that lies on the given hotpot. If there is no one found an empty array is returned.\n\t */\n\tpublic getUserPointOn( hotspot: Area ): Point[] {\n\n\t\t// no valid data\n\t\tif ( this.userPoints.length == 0 ) return [];\n\n\t\treturn this.userPoints.filter( function ( userPoint ) {\n\n\t\t\tvar l = hotspot.left;\n\t\t\tvar r = l + hotspot.width;\n\t\t\tvar t = hotspot.top;\n\t\t\tvar b = t + hotspot.height;\n\n\t\t\t// test horizontally and vertically\n\t\t\treturn l <= userPoint.left && userPoint.left <= r && t <= userPoint.top && userPoint.top <= b;\n\t\t});\n\t};\n\n\n\t/**\n\t * Splits all userpoints into correct and wrong ones depending on they are on a hotspot or not.\n\t */\n\tpublic partitionUserPoints() {\n\n\t\tvar wrongPoints = this.userPoints.concat();\n\t\tvar correctPoints = [];\n\t\tvar t = this;\n\n\t\t// this function will remove the given point from the wrong ones\n\t\t// and add it to the correct ones.\n\t\tvar markCorrectPoint = function ( userPoint ) {\n\n\t\t\tvar i = wrongPoints.indexOf( userPoint );\n\t\t\tif ( i > -1 ) wrongPoints.splice( i, 1 );\n\n\t\t\tcorrectPoints.push( userPoint );\n\t\t};\n\n\t\t// loop over all hotspotAreas and check if there is a user point\n\t\tthis.hotspotAreas.forEach( function ( hotspot ) {\n\t\t\tvar correctPoint = t.getUserPointOn( hotspot );\n\t\t\t// if there are correct point mark the first as correct\n\t\t\tif ( correctPoint.length != 0 ) markCorrectPoint( correctPoint[0] );\n\t\t});\n\n\t\treturn { correct: correctPoints, wrong: wrongPoints };\n\t};\n\n\n\t/**\n\t * Returns true if the hotspot question is correctly solved\n\t */\n\tisCorrect() {\n\n\t\t// if there are no hotpots\n\t\tif ( this.userPoints.length === 0 && this.hotspotAreas.length === 0 ) return true;\n\n\t\t// regular check\n\t\tvar result = this.partitionUserPoints();\n\t\treturn result.correct.length === this.hotspotAreas.length;\n\t};\n\n\t/**\n\t * Returns the array of hotspotAreas form the slide model\n\t */\n\tget hotspotAreas(): Area[] {\n\t\tif ( this.get( 'hotspot_areas' ) == undefined ) this.set( 'hotspot_areas', new Array() );\n\t\treturn this.get( 'hotspot_areas' ) as Area[];\n\t}\n\n\t/**\n\t * Returns the array of userPoints form the slide model\n\t */\n\tget userPoints(): Point[] {\n\t\tif ( this.get( 'userPoints' ) == undefined ) this.set( 'userPoints', new Array() );\n\t\treturn this.get( 'userPoints' ) as Point[];\n\t}\n\n\n\n}\n\nexport default HotspotModel;","// import library\nimport Signal from 'ln/signal/Signal';\nimport { EmptyCallback } from 'ln/signal/Signal';\n\nimport SlideModel from '../Slide/SlideModel';\nimport LernFragen from '../../LernFragen';\n\n\n/**\n * An interface for a Button \n */\nexport interface Button {\n\tlabel: string;\n\ttext: string;\n}\n\n/**\n * The model for a reveal slide\n */\nexport class RevealModel extends SlideModel {\n\n\n\t/**\n\t * Returns the array of buttons\n\t */\n\tget buttons(): Button[] {\n\t\tif ( this.get( 'buttons' ) == undefined ) this.set( 'buttons', new Array