{"version":3,"file":"main.js","sources":["../../typescript/lib/ts/src/core/signal/Signal.ts","../../typescript/lib/ts/src/core/node/signal/DOMEventSignal.ts","../../typescript/lib/ts/src/core/node/signal/NodeSignal.ts","../../typescript/lib/ts/src/core/node/Node.ts","../../typescript/lib/ts/src/core/lookup/LookupDict.ts","../../typescript/lib/ts/src/core/js.ts","../../typescript/lib/ts/src/core/lookup/LookupObject.ts","../../typescript/lib/ts/src/core/template/TemplateRenderer.ts","../../typescript/lib/ts/src/core/template/TemplateManager.ts","../../typescript/lib/ts/src/core/ioc/IoC.ts","../../typescript/lib/ts/src/core/view/Scanner.ts","../../typescript/lib/ts/src/core/view/View.ts","../../typescript/lib/ts/src/core/model/Model.ts","../../typescript/lib/ts/src/core/model/Mapper.ts","../../node_modules/es6-promise/dist/es6-promise.js","../../node_modules/es6-promise/auto.js","../../typescript/lib/ts/src/core/request/Request.ts","../../typescript/lib/ts/src/core/setup/Asset.ts","../../typescript/lib/ts/src/core/setup/setup.ts","../../typescript/lib/ts/src/core/node/Window.ts","../../typescript/lib/lernbuch/src/ScrollMonitor.ts","../../typescript/lib/ts/src/core/list/List.ts","../../typescript/lib/ts/src/core/list/ListRenderer.ts","../../typescript/lib/lernbuch/src/elements/Chapter.ts","../../typescript/lib/lernbuch/src/LernBuch.ts","../../typescript/src/views/Overview.ts","../../typescript/src/views/Header.ts","../../typescript/src/views/Footer.ts","../../typescript/src/views/Taskitem.ts","../../typescript/src/views/Chapter.ts","../../typescript/src/views/Lernpfad.ts","../../typescript/lib/lernbuch/src/elements/Element.ts","../../typescript/lib/ts/src/core/list/ModifiableList.ts","../../typescript/src/views/MaterialcardImage.ts","../../typescript/src/views/MaterialcardYT.ts","../../typescript/src/views/Materiallist.ts","../../typescript/src/views/SaveDialog.ts","../../typescript/lib/ts/src/core/linkedlist/LinkedList.ts","../../typescript/lib/ts/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/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","../../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/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/lib/lernfragen/src/decorators/ModelBasedDecorator.ts","../../typescript/lib/lernfragen/src/decorators/ContainerFeedback.ts","../../typescript/lib/lernfragen/src/decorators/RelatedFeedback.ts","../../typescript/src/lernfragen/FeedbackButtonDecorator.ts","../../typescript/src/quiz/Quiz.ts","../../typescript/src/views/Thema.ts","../../typescript/src/views/Task.ts","../../typescript/src/views/Article.ts","../../typescript/src/views/Filterbutton.ts","../../typescript/lib/ts/src/ui/filter/ModelFilter.ts","../../typescript/lib/ts/src/ui/filter/TermFilter.ts","../../typescript/src/views/MaterialcardText.ts","../../typescript/src/views/Materialpool.ts","../../typescript/src/templates/elements.ts","../../typescript/src/templates/lernbuch.ts","../../typescript/src/templates/icons.ts","../../typescript/src/templates/lernfragen.ts","../../typescript/src/templates/slides.ts","../../typescript/lib/lernbuch/src/models/ElementModel.ts","../../typescript/src/models/ElementModel.ts","../../typescript/lib/ts/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\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;","/**\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 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;","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 * @overview es6-promise - a tiny implementation of Promises/A+.\n * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)\n * @license Licensed under MIT license\n * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE\n * @version v4.2.5+7f2b526d\n */\n\n(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n\ttypeof define === 'function' && define.amd ? define(factory) :\n\t(global.ES6Promise = factory());\n}(this, (function () { 'use strict';\n\nfunction objectOrFunction(x) {\n var type = typeof x;\n return x !== null && (type === 'object' || type === 'function');\n}\n\nfunction isFunction(x) {\n return typeof x === 'function';\n}\n\n\n\nvar _isArray = void 0;\nif (Array.isArray) {\n _isArray = Array.isArray;\n} else {\n _isArray = function (x) {\n return Object.prototype.toString.call(x) === '[object Array]';\n };\n}\n\nvar isArray = _isArray;\n\nvar len = 0;\nvar vertxNext = void 0;\nvar customSchedulerFn = void 0;\n\nvar asap = function asap(callback, arg) {\n queue[len] = callback;\n queue[len + 1] = arg;\n len += 2;\n if (len === 2) {\n // If len is 2, that means that we need to schedule an async flush.\n // If additional callbacks are queued before the queue is flushed, they\n // will be processed by this flush that we are scheduling.\n if (customSchedulerFn) {\n customSchedulerFn(flush);\n } else {\n scheduleFlush();\n }\n }\n};\n\nfunction setScheduler(scheduleFn) {\n customSchedulerFn = scheduleFn;\n}\n\nfunction setAsap(asapFn) {\n asap = asapFn;\n}\n\nvar browserWindow = typeof window !== 'undefined' ? window : undefined;\nvar browserGlobal = browserWindow || {};\nvar BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;\nvar isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';\n\n// test for web worker but not in IE10\nvar isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';\n\n// node\nfunction useNextTick() {\n // node version 0.10.x displays a deprecation warning when nextTick is used recursively\n // see https://github.com/cujojs/when/issues/410 for details\n return function () {\n return process.nextTick(flush);\n };\n}\n\n// vertx\nfunction useVertxTimer() {\n if (typeof vertxNext !== 'undefined') {\n return function () {\n vertxNext(flush);\n };\n }\n\n return useSetTimeout();\n}\n\nfunction useMutationObserver() {\n var iterations = 0;\n var observer = new BrowserMutationObserver(flush);\n var node = document.createTextNode('');\n observer.observe(node, { characterData: true });\n\n return function () {\n node.data = iterations = ++iterations % 2;\n };\n}\n\n// web worker\nfunction useMessageChannel() {\n var channel = new MessageChannel();\n channel.port1.onmessage = flush;\n return function () {\n return channel.port2.postMessage(0);\n };\n}\n\nfunction useSetTimeout() {\n // Store setTimeout reference so es6-promise will be unaffected by\n // other code modifying setTimeout (like sinon.useFakeTimers())\n var globalSetTimeout = setTimeout;\n return function () {\n return globalSetTimeout(flush, 1);\n };\n}\n\nvar queue = new Array(1000);\nfunction flush() {\n for (var i = 0; i < len; i += 2) {\n var callback = queue[i];\n var arg = queue[i + 1];\n\n callback(arg);\n\n queue[i] = undefined;\n queue[i + 1] = undefined;\n }\n\n len = 0;\n}\n\nfunction attemptVertx() {\n try {\n var vertx = Function('return this')().require('vertx');\n vertxNext = vertx.runOnLoop || vertx.runOnContext;\n return useVertxTimer();\n } catch (e) {\n return useSetTimeout();\n }\n}\n\nvar scheduleFlush = void 0;\n// Decide what async method to use to triggering processing of queued callbacks:\nif (isNode) {\n scheduleFlush = useNextTick();\n} else if (BrowserMutationObserver) {\n scheduleFlush = useMutationObserver();\n} else if (isWorker) {\n scheduleFlush = useMessageChannel();\n} else if (browserWindow === undefined && typeof require === 'function') {\n scheduleFlush = attemptVertx();\n} else {\n scheduleFlush = useSetTimeout();\n}\n\nfunction then(onFulfillment, onRejection) {\n var parent = this;\n\n var child = new this.constructor(noop);\n\n if (child[PROMISE_ID] === undefined) {\n makePromise(child);\n }\n\n var _state = parent._state;\n\n\n if (_state) {\n var callback = arguments[_state - 1];\n asap(function () {\n return invokeCallback(_state, child, callback, parent._result);\n });\n } else {\n subscribe(parent, child, onFulfillment, onRejection);\n }\n\n return child;\n}\n\n/**\n `Promise.resolve` returns a promise that will become resolved with the\n passed `value`. It is shorthand for the following:\n\n ```javascript\n let promise = new Promise(function(resolve, reject){\n resolve(1);\n });\n\n promise.then(function(value){\n // value === 1\n });\n ```\n\n Instead of writing the above, your code now simply becomes the following:\n\n ```javascript\n let promise = Promise.resolve(1);\n\n promise.then(function(value){\n // value === 1\n });\n ```\n\n @method resolve\n @static\n @param {Any} value value that the returned promise will be resolved with\n Useful for tooling.\n @return {Promise} a promise that will become fulfilled with the given\n `value`\n*/\nfunction resolve$1(object) {\n /*jshint validthis:true */\n var Constructor = this;\n\n if (object && typeof object === 'object' && object.constructor === Constructor) {\n return object;\n }\n\n var promise = new Constructor(noop);\n resolve(promise, object);\n return promise;\n}\n\nvar PROMISE_ID = Math.random().toString(36).substring(2);\n\nfunction noop() {}\n\nvar PENDING = void 0;\nvar FULFILLED = 1;\nvar REJECTED = 2;\n\nvar TRY_CATCH_ERROR = { error: null };\n\nfunction selfFulfillment() {\n return new TypeError(\"You cannot resolve a promise with itself\");\n}\n\nfunction cannotReturnOwn() {\n return new TypeError('A promises callback cannot return that same promise.');\n}\n\nfunction getThen(promise) {\n try {\n return promise.then;\n } catch (error) {\n TRY_CATCH_ERROR.error = error;\n return TRY_CATCH_ERROR;\n }\n}\n\nfunction tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {\n try {\n then$$1.call(value, fulfillmentHandler, rejectionHandler);\n } catch (e) {\n return e;\n }\n}\n\nfunction handleForeignThenable(promise, thenable, then$$1) {\n asap(function (promise) {\n var sealed = false;\n var error = tryThen(then$$1, thenable, function (value) {\n if (sealed) {\n return;\n }\n sealed = true;\n if (thenable !== value) {\n resolve(promise, value);\n } else {\n fulfill(promise, value);\n }\n }, function (reason) {\n if (sealed) {\n return;\n }\n sealed = true;\n\n reject(promise, reason);\n }, 'Settle: ' + (promise._label || ' unknown promise'));\n\n if (!sealed && error) {\n sealed = true;\n reject(promise, error);\n }\n }, promise);\n}\n\nfunction handleOwnThenable(promise, thenable) {\n if (thenable._state === FULFILLED) {\n fulfill(promise, thenable._result);\n } else if (thenable._state === REJECTED) {\n reject(promise, thenable._result);\n } else {\n subscribe(thenable, undefined, function (value) {\n return resolve(promise, value);\n }, function (reason) {\n return reject(promise, reason);\n });\n }\n}\n\nfunction handleMaybeThenable(promise, maybeThenable, then$$1) {\n if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {\n handleOwnThenable(promise, maybeThenable);\n } else {\n if (then$$1 === TRY_CATCH_ERROR) {\n reject(promise, TRY_CATCH_ERROR.error);\n TRY_CATCH_ERROR.error = null;\n } else if (then$$1 === undefined) {\n fulfill(promise, maybeThenable);\n } else if (isFunction(then$$1)) {\n handleForeignThenable(promise, maybeThenable, then$$1);\n } else {\n fulfill(promise, maybeThenable);\n }\n }\n}\n\nfunction resolve(promise, value) {\n if (promise === value) {\n reject(promise, selfFulfillment());\n } else if (objectOrFunction(value)) {\n handleMaybeThenable(promise, value, getThen(value));\n } else {\n fulfill(promise, value);\n }\n}\n\nfunction publishRejection(promise) {\n if (promise._onerror) {\n promise._onerror(promise._result);\n }\n\n publish(promise);\n}\n\nfunction fulfill(promise, value) {\n if (promise._state !== PENDING) {\n return;\n }\n\n promise._result = value;\n promise._state = FULFILLED;\n\n if (promise._subscribers.length !== 0) {\n asap(publish, promise);\n }\n}\n\nfunction reject(promise, reason) {\n if (promise._state !== PENDING) {\n return;\n }\n promise._state = REJECTED;\n promise._result = reason;\n\n asap(publishRejection, promise);\n}\n\nfunction subscribe(parent, child, onFulfillment, onRejection) {\n var _subscribers = parent._subscribers;\n var length = _subscribers.length;\n\n\n parent._onerror = null;\n\n _subscribers[length] = child;\n _subscribers[length + FULFILLED] = onFulfillment;\n _subscribers[length + REJECTED] = onRejection;\n\n if (length === 0 && parent._state) {\n asap(publish, parent);\n }\n}\n\nfunction publish(promise) {\n var subscribers = promise._subscribers;\n var settled = promise._state;\n\n if (subscribers.length === 0) {\n return;\n }\n\n var child = void 0,\n callback = void 0,\n detail = promise._result;\n\n for (var i = 0; i < subscribers.length; i += 3) {\n child = subscribers[i];\n callback = subscribers[i + settled];\n\n if (child) {\n invokeCallback(settled, child, callback, detail);\n } else {\n callback(detail);\n }\n }\n\n promise._subscribers.length = 0;\n}\n\nfunction tryCatch(callback, detail) {\n try {\n return callback(detail);\n } catch (e) {\n TRY_CATCH_ERROR.error = e;\n return TRY_CATCH_ERROR;\n }\n}\n\nfunction invokeCallback(settled, promise, callback, detail) {\n var hasCallback = isFunction(callback),\n value = void 0,\n error = void 0,\n succeeded = void 0,\n failed = void 0;\n\n if (hasCallback) {\n value = tryCatch(callback, detail);\n\n if (value === TRY_CATCH_ERROR) {\n failed = true;\n error = value.error;\n value.error = null;\n } else {\n succeeded = true;\n }\n\n if (promise === value) {\n reject(promise, cannotReturnOwn());\n return;\n }\n } else {\n value = detail;\n succeeded = true;\n }\n\n if (promise._state !== PENDING) {\n // noop\n } else if (hasCallback && succeeded) {\n resolve(promise, value);\n } else if (failed) {\n reject(promise, error);\n } else if (settled === FULFILLED) {\n fulfill(promise, value);\n } else if (settled === REJECTED) {\n reject(promise, value);\n }\n}\n\nfunction initializePromise(promise, resolver) {\n try {\n resolver(function resolvePromise(value) {\n resolve(promise, value);\n }, function rejectPromise(reason) {\n reject(promise, reason);\n });\n } catch (e) {\n reject(promise, e);\n }\n}\n\nvar id = 0;\nfunction nextId() {\n return id++;\n}\n\nfunction makePromise(promise) {\n promise[PROMISE_ID] = id++;\n promise._state = undefined;\n promise._result = undefined;\n promise._subscribers = [];\n}\n\nfunction validationError() {\n return new Error('Array Methods must be provided an Array');\n}\n\nvar Enumerator = function () {\n function Enumerator(Constructor, input) {\n this._instanceConstructor = Constructor;\n this.promise = new Constructor(noop);\n\n if (!this.promise[PROMISE_ID]) {\n makePromise(this.promise);\n }\n\n if (isArray(input)) {\n this.length = input.length;\n this._remaining = input.length;\n\n this._result = new Array(this.length);\n\n if (this.length === 0) {\n fulfill(this.promise, this._result);\n } else {\n this.length = this.length || 0;\n this._enumerate(input);\n if (this._remaining === 0) {\n fulfill(this.promise, this._result);\n }\n }\n } else {\n reject(this.promise, validationError());\n }\n }\n\n Enumerator.prototype._enumerate = function _enumerate(input) {\n for (var i = 0; this._state === PENDING && i < input.length; i++) {\n this._eachEntry(input[i], i);\n }\n };\n\n Enumerator.prototype._eachEntry = function _eachEntry(entry, i) {\n var c = this._instanceConstructor;\n var resolve$$1 = c.resolve;\n\n\n if (resolve$$1 === resolve$1) {\n var _then = getThen(entry);\n\n if (_then === then && entry._state !== PENDING) {\n this._settledAt(entry._state, i, entry._result);\n } else if (typeof _then !== 'function') {\n this._remaining--;\n this._result[i] = entry;\n } else if (c === Promise$1) {\n var promise = new c(noop);\n handleMaybeThenable(promise, entry, _then);\n this._willSettleAt(promise, i);\n } else {\n this._willSettleAt(new c(function (resolve$$1) {\n return resolve$$1(entry);\n }), i);\n }\n } else {\n this._willSettleAt(resolve$$1(entry), i);\n }\n };\n\n Enumerator.prototype._settledAt = function _settledAt(state, i, value) {\n var promise = this.promise;\n\n\n if (promise._state === PENDING) {\n this._remaining--;\n\n if (state === REJECTED) {\n reject(promise, value);\n } else {\n this._result[i] = value;\n }\n }\n\n if (this._remaining === 0) {\n fulfill(promise, this._result);\n }\n };\n\n Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) {\n var enumerator = this;\n\n subscribe(promise, undefined, function (value) {\n return enumerator._settledAt(FULFILLED, i, value);\n }, function (reason) {\n return enumerator._settledAt(REJECTED, i, reason);\n });\n };\n\n return Enumerator;\n}();\n\n/**\n `Promise.all` accepts an array of promises, and returns a new promise which\n is fulfilled with an array of fulfillment values for the passed promises, or\n rejected with the reason of the first passed promise to be rejected. It casts all\n elements of the passed iterable to promises as it runs this algorithm.\n\n Example:\n\n ```javascript\n let promise1 = resolve(1);\n let promise2 = resolve(2);\n let promise3 = resolve(3);\n let promises = [ promise1, promise2, promise3 ];\n\n Promise.all(promises).then(function(array){\n // The array here would be [ 1, 2, 3 ];\n });\n ```\n\n If any of the `promises` given to `all` are rejected, the first promise\n that is rejected will be given as an argument to the returned promises's\n rejection handler. For example:\n\n Example:\n\n ```javascript\n let promise1 = resolve(1);\n let promise2 = reject(new Error(\"2\"));\n let promise3 = reject(new Error(\"3\"));\n let promises = [ promise1, promise2, promise3 ];\n\n Promise.all(promises).then(function(array){\n // Code here never runs because there are rejected promises!\n }, function(error) {\n // error.message === \"2\"\n });\n ```\n\n @method all\n @static\n @param {Array} entries array of promises\n @param {String} label optional string for labeling the promise.\n Useful for tooling.\n @return {Promise} promise that is fulfilled when all `promises` have been\n fulfilled, or rejected if any of them become rejected.\n @static\n*/\nfunction all(entries) {\n return new Enumerator(this, entries).promise;\n}\n\n/**\n `Promise.race` returns a new promise which is settled in the same way as the\n first passed promise to settle.\n\n Example:\n\n ```javascript\n let promise1 = new Promise(function(resolve, reject){\n setTimeout(function(){\n resolve('promise 1');\n }, 200);\n });\n\n let promise2 = new Promise(function(resolve, reject){\n setTimeout(function(){\n resolve('promise 2');\n }, 100);\n });\n\n Promise.race([promise1, promise2]).then(function(result){\n // result === 'promise 2' because it was resolved before promise1\n // was resolved.\n });\n ```\n\n `Promise.race` is deterministic in that only the state of the first\n settled promise matters. For example, even if other promises given to the\n `promises` array argument are resolved, but the first settled promise has\n become rejected before the other promises became fulfilled, the returned\n promise will become rejected:\n\n ```javascript\n let promise1 = new Promise(function(resolve, reject){\n setTimeout(function(){\n resolve('promise 1');\n }, 200);\n });\n\n let promise2 = new Promise(function(resolve, reject){\n setTimeout(function(){\n reject(new Error('promise 2'));\n }, 100);\n });\n\n Promise.race([promise1, promise2]).then(function(result){\n // Code here never runs\n }, function(reason){\n // reason.message === 'promise 2' because promise 2 became rejected before\n // promise 1 became fulfilled\n });\n ```\n\n An example real-world use case is implementing timeouts:\n\n ```javascript\n Promise.race([ajax('foo.json'), timeout(5000)])\n ```\n\n @method race\n @static\n @param {Array} promises array of promises to observe\n Useful for tooling.\n @return {Promise} a promise which settles in the same way as the first passed\n promise to settle.\n*/\nfunction race(entries) {\n /*jshint validthis:true */\n var Constructor = this;\n\n if (!isArray(entries)) {\n return new Constructor(function (_, reject) {\n return reject(new TypeError('You must pass an array to race.'));\n });\n } else {\n return new Constructor(function (resolve, reject) {\n var length = entries.length;\n for (var i = 0; i < length; i++) {\n Constructor.resolve(entries[i]).then(resolve, reject);\n }\n });\n }\n}\n\n/**\n `Promise.reject` returns a promise rejected with the passed `reason`.\n It is shorthand for the following:\n\n ```javascript\n let promise = new Promise(function(resolve, reject){\n reject(new Error('WHOOPS'));\n });\n\n promise.then(function(value){\n // Code here doesn't run because the promise is rejected!\n }, function(reason){\n // reason.message === 'WHOOPS'\n });\n ```\n\n Instead of writing the above, your code now simply becomes the following:\n\n ```javascript\n let promise = Promise.reject(new Error('WHOOPS'));\n\n promise.then(function(value){\n // Code here doesn't run because the promise is rejected!\n }, function(reason){\n // reason.message === 'WHOOPS'\n });\n ```\n\n @method reject\n @static\n @param {Any} reason value that the returned promise will be rejected with.\n Useful for tooling.\n @return {Promise} a promise rejected with the given `reason`.\n*/\nfunction reject$1(reason) {\n /*jshint validthis:true */\n var Constructor = this;\n var promise = new Constructor(noop);\n reject(promise, reason);\n return promise;\n}\n\nfunction needsResolver() {\n throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');\n}\n\nfunction needsNew() {\n throw new TypeError(\"Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.\");\n}\n\n/**\n Promise objects represent the eventual result of an asynchronous operation. The\n primary way of interacting with a promise is through its `then` method, which\n registers callbacks to receive either a promise's eventual value or the reason\n why the promise cannot be fulfilled.\n\n Terminology\n -----------\n\n - `promise` is an object or function with a `then` method whose behavior conforms to this specification.\n - `thenable` is an object or function that defines a `then` method.\n - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).\n - `exception` is a value that is thrown using the throw statement.\n - `reason` is a value that indicates why a promise was rejected.\n - `settled` the final resting state of a promise, fulfilled or rejected.\n\n A promise can be in one of three states: pending, fulfilled, or rejected.\n\n Promises that are fulfilled have a fulfillment value and are in the fulfilled\n state. Promises that are rejected have a rejection reason and are in the\n rejected state. A fulfillment value is never a thenable.\n\n Promises can also be said to *resolve* a value. If this value is also a\n promise, then the original promise's settled state will match the value's\n settled state. So a promise that *resolves* a promise that rejects will\n itself reject, and a promise that *resolves* a promise that fulfills will\n itself fulfill.\n\n\n Basic Usage:\n ------------\n\n ```js\n let promise = new Promise(function(resolve, reject) {\n // on success\n resolve(value);\n\n // on failure\n reject(reason);\n });\n\n promise.then(function(value) {\n // on fulfillment\n }, function(reason) {\n // on rejection\n });\n ```\n\n Advanced Usage:\n ---------------\n\n Promises shine when abstracting away asynchronous interactions such as\n `XMLHttpRequest`s.\n\n ```js\n function getJSON(url) {\n return new Promise(function(resolve, reject){\n let xhr = new XMLHttpRequest();\n\n xhr.open('GET', url);\n xhr.onreadystatechange = handler;\n xhr.responseType = 'json';\n xhr.setRequestHeader('Accept', 'application/json');\n xhr.send();\n\n function handler() {\n if (this.readyState === this.DONE) {\n if (this.status === 200) {\n resolve(this.response);\n } else {\n reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));\n }\n }\n };\n });\n }\n\n getJSON('/posts.json').then(function(json) {\n // on fulfillment\n }, function(reason) {\n // on rejection\n });\n ```\n\n Unlike callbacks, promises are great composable primitives.\n\n ```js\n Promise.all([\n getJSON('/posts'),\n getJSON('/comments')\n ]).then(function(values){\n values[0] // => postsJSON\n values[1] // => commentsJSON\n\n return values;\n });\n ```\n\n @class Promise\n @param {Function} resolver\n Useful for tooling.\n @constructor\n*/\n\nvar Promise$1 = function () {\n function Promise(resolver) {\n this[PROMISE_ID] = nextId();\n this._result = this._state = undefined;\n this._subscribers = [];\n\n if (noop !== resolver) {\n typeof resolver !== 'function' && needsResolver();\n this instanceof Promise ? initializePromise(this, resolver) : needsNew();\n }\n }\n\n /**\n The primary way of interacting with a promise is through its `then` method,\n which registers callbacks to receive either a promise's eventual value or the\n reason why the promise cannot be fulfilled.\n ```js\n findUser().then(function(user){\n // user is available\n }, function(reason){\n // user is unavailable, and you are given the reason why\n });\n ```\n Chaining\n --------\n The return value of `then` is itself a promise. This second, 'downstream'\n promise is resolved with the return value of the first promise's fulfillment\n or rejection handler, or rejected if the handler throws an exception.\n ```js\n findUser().then(function (user) {\n return user.name;\n }, function (reason) {\n return 'default name';\n }).then(function (userName) {\n // If `findUser` fulfilled, `userName` will be the user's name, otherwise it\n // will be `'default name'`\n });\n findUser().then(function (user) {\n throw new Error('Found user, but still unhappy');\n }, function (reason) {\n throw new Error('`findUser` rejected and we're unhappy');\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.\n // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.\n });\n ```\n If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.\n ```js\n findUser().then(function (user) {\n throw new PedagogicalException('Upstream error');\n }).then(function (value) {\n // never reached\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // The `PedgagocialException` is propagated all the way down to here\n });\n ```\n Assimilation\n ------------\n Sometimes the value you want to propagate to a downstream promise can only be\n retrieved asynchronously. This can be achieved by returning a promise in the\n fulfillment or rejection handler. The downstream promise will then be pending\n until the returned promise is settled. This is called *assimilation*.\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // The user's comments are now available\n });\n ```\n If the assimliated promise rejects, then the downstream promise will also reject.\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // If `findCommentsByAuthor` fulfills, we'll have the value here\n }, function (reason) {\n // If `findCommentsByAuthor` rejects, we'll have the reason here\n });\n ```\n Simple Example\n --------------\n Synchronous Example\n ```javascript\n let result;\n try {\n result = findResult();\n // success\n } catch(reason) {\n // failure\n }\n ```\n Errback Example\n ```js\n findResult(function(result, err){\n if (err) {\n // failure\n } else {\n // success\n }\n });\n ```\n Promise Example;\n ```javascript\n findResult().then(function(result){\n // success\n }, function(reason){\n // failure\n });\n ```\n Advanced Example\n --------------\n Synchronous Example\n ```javascript\n let author, books;\n try {\n author = findAuthor();\n books = findBooksByAuthor(author);\n // success\n } catch(reason) {\n // failure\n }\n ```\n Errback Example\n ```js\n function foundBooks(books) {\n }\n function failure(reason) {\n }\n findAuthor(function(author, err){\n if (err) {\n failure(err);\n // failure\n } else {\n try {\n findBoooksByAuthor(author, function(books, err) {\n if (err) {\n failure(err);\n } else {\n try {\n foundBooks(books);\n } catch(reason) {\n failure(reason);\n }\n }\n });\n } catch(error) {\n failure(err);\n }\n // success\n }\n });\n ```\n Promise Example;\n ```javascript\n findAuthor().\n then(findBooksByAuthor).\n then(function(books){\n // found books\n }).catch(function(reason){\n // something went wrong\n });\n ```\n @method then\n @param {Function} onFulfilled\n @param {Function} onRejected\n Useful for tooling.\n @return {Promise}\n */\n\n /**\n `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same\n as the catch block of a try/catch statement.\n ```js\n function findAuthor(){\n throw new Error('couldn't find that author');\n }\n // synchronous\n try {\n findAuthor();\n } catch(reason) {\n // something went wrong\n }\n // async with promises\n findAuthor().catch(function(reason){\n // something went wrong\n });\n ```\n @method catch\n @param {Function} onRejection\n Useful for tooling.\n @return {Promise}\n */\n\n\n Promise.prototype.catch = function _catch(onRejection) {\n return this.then(null, onRejection);\n };\n\n /**\n `finally` will be invoked regardless of the promise's fate just as native\n try/catch/finally behaves\n \n Synchronous example:\n \n ```js\n findAuthor() {\n if (Math.random() > 0.5) {\n throw new Error();\n }\n return new Author();\n }\n \n try {\n return findAuthor(); // succeed or fail\n } catch(error) {\n return findOtherAuther();\n } finally {\n // always runs\n // doesn't affect the return value\n }\n ```\n \n Asynchronous example:\n \n ```js\n findAuthor().catch(function(reason){\n return findOtherAuther();\n }).finally(function(){\n // author was either found, or not\n });\n ```\n \n @method finally\n @param {Function} callback\n @return {Promise}\n */\n\n\n Promise.prototype.finally = function _finally(callback) {\n var promise = this;\n var constructor = promise.constructor;\n\n if (isFunction(callback)) {\n return promise.then(function (value) {\n return constructor.resolve(callback()).then(function () {\n return value;\n });\n }, function (reason) {\n return constructor.resolve(callback()).then(function () {\n throw reason;\n });\n });\n }\n\n return promise.then(callback, callback);\n };\n\n return Promise;\n}();\n\nPromise$1.prototype.then = then;\nPromise$1.all = all;\nPromise$1.race = race;\nPromise$1.resolve = resolve$1;\nPromise$1.reject = reject$1;\nPromise$1._setScheduler = setScheduler;\nPromise$1._setAsap = setAsap;\nPromise$1._asap = asap;\n\n/*global self*/\nfunction polyfill() {\n var local = void 0;\n\n if (typeof global !== 'undefined') {\n local = global;\n } else if (typeof self !== 'undefined') {\n local = self;\n } else {\n try {\n local = Function('return this')();\n } catch (e) {\n throw new Error('polyfill failed because global object is unavailable in this environment');\n }\n }\n\n var P = local.Promise;\n\n if (P) {\n var promiseToString = null;\n try {\n promiseToString = Object.prototype.toString.call(P.resolve());\n } catch (e) {\n // silently ignored\n }\n\n if (promiseToString === '[object Promise]' && !P.cast) {\n return;\n }\n }\n\n local.Promise = Promise$1;\n}\n\n// Strange compat..\nPromise$1.polyfill = polyfill;\nPromise$1.Promise = Promise$1;\n\nreturn Promise$1;\n\n})));\n\n\n\n//# sourceMappingURL=es6-promise.map\n","// This file can be required in Browserify and Node.js for automatic polyfill\n// To use it: require('es6-promise/auto');\n'use strict';\nmodule.exports = require('./').polyfill();\n","import \"es6-promise/auto\";\nimport Signal from '../signal/Signal';\n\n\ninterface ProgressSignal {\n\t( loaded:number, total:number )\n}\n\n/**\n * HttpRequest Class\n */\n\nexport class HttpRequest {\n\t\n\tprotected _method:string = 'GET';\n\tprotected _url:string;\n\tprotected _headers:{} = null;\n\tprotected request:XMLHttpRequest;\n\n\tpublic progress:Signal = new Signal();\n\t\n\t\n\t/**\n\t * Create a new instance with the given url\n\t * @param url\n\t */\n\tconstructor( url:string ) {\n\t\tthis._url = url;\n\n\t\tthis.request = new XMLHttpRequest();\n\t\tthis.request.onprogress = ( event ) => {\n\t\t\tthis.progress.dispatch( event.loaded, event.total );\n\t\t}\n\t}\n\t\n\t/**\n\t * Set the method for the request\n\t * @param method\n\t */\n\tpublic method( method:string ):HttpRequest {\n\t\tthis._method = method;\n\t\treturn this;\n\t}\n\t\n\t/**\n\t * Set the headers for the request\n\t * @param headers\n\t */\n\tpublic headers( headers:{} ):HttpRequest {\n\t\tthis._headers = headers;\n\t\treturn this\n\t}\n\t\n\t/**\n\t * Send/Load the request without sending data\n\t */\n\tpublic load():Promise {\n\t\treturn this.sendRequest().then( this.parseJson );\n\t}\n\t\n\t/**\n\t * Send the request with the given data\n\t * @param data\n\t */\n\tpublic send( data:any ):Promise {\n\t\treturn this.sendRequest( data ).then( this.parseJson );\n\t}\n\n\tpublic url():string {\n\t\treturn this._url;\n\t}\n\t\n\t/**\n\t * Open and send the request with the given parameters\n\t * and data\n\t * @param data\n\t */\n\tprivate sendRequest( data:any = null ):Promise {\n\t\t\n\t\tvar request = this.request;\n\t\tvar promise = this.promise( request );\n\t\t\n\t\trequest.open( this._method, this._url );\n\t\t\n\t\tfor( var key in this._headers ){\n\t\t\trequest.setRequestHeader( key, this._headers[key] );\n\t\t}\n\t\t\n\t\trequest.send( data );\n\t\t\n\t\treturn promise;\n\t}\n\t\n\t/**\n\t * Create a promise with the request\n\t * The promise resolves in case of a successful request - jejects otherwise\n\t */\n\tprivate promise( request:XMLHttpRequest ) {\n\t\treturn new Promise( ( resolve, reject ) => {\n\t\t\t\n\t\t\tvar errorHandler = function() {\n\t\t\t\tRequest.error.dispatch( request.statusText );\n\t\t\t\treject( Error( request.statusText ) );\n\t\t\t}\n\t\t\t\n\t\t\trequest.onload = function() {\n\t\t\t\tif( request.status >= 200 && request.status < 300 ) {\n\t\t\t\t\tresolve( request.responseText );\n\t\t\t\t} else {\n\t\t\t\t\terrorHandler();\n\t\t\t\t}\n\t\t\t};\n\t\t\t\n\t\t\trequest.onerror = errorHandler;\n\t\t});\n\t}\n\n\t/**\n\t * tries to parse response otherwise returns original string data\n\t */\n\tprivate parseJson( data:string ):string|{} {\n\t\ttry {\n\t\t\tdata = JSON.parse( data )\n\t\t\treturn data;\n\t\t} catch( e ) {\n\t\t\treturn data;\n\t\t}\n\t}\n}\n\ninterface ErrorSignal {\n\t( error:string )\n}\n\n/**\n * Static Request Class\n */\nexport class Request {\n\t\n\tpublic static error:Signal = new Signal();\n\t/**\n\t * Creates a new post HttpRequest\n\t */\n\tpublic static post( url:string ):HttpRequest{\n\t\treturn new HttpRequest( url ).method( 'POST' );\n\t}\n\t\n\t/**\n\t * Creates a new get HttpRequest\n\t */\n\tpublic static get( url:string ):HttpRequest{\n\t\treturn new HttpRequest( url ).method( 'GET' );\n\t}\n\t\n\t/**\n\t * Creates a new put HttpRequest\n\t */\n\tpublic static put( url:string ):HttpRequest{\n\t\treturn new HttpRequest( url ).method( 'PUT' );\n\t}\n\t\n\t/**\n\t * Creates a new delete HttpRequest\n\t */\n\tpublic static delete( url:string ):HttpRequest{\n\t\treturn new HttpRequest( url ).method( 'DELETE' );\n\t}\n}\n\n\nexport default Request;","\nexport interface AssetConfig {\n\turl: string,\n\ttype?: \"js\" | \"css\",\n}\n\nexport class Asset {\n\n\t// store which assets are already loaded.\n\tstatic promises:{ [index:string]: Promise } = {};\n\n\tconfig:AssetConfig;\n\n\n\tconstructor( config:AssetConfig ) {\n\t\tif( !config.type ) config.type = this.guessType( config.url );\n\t\tthis.config = config;\n\t}\n\n\n\tload():Promise {\n\n\t\t// if not loaded create promise\n\t\tif( !Asset.promises[ this.config.url ] ) {\n\t\t\t( this.config.type == \"js\" ) ? this.loadScript() : this.loadCSS();\n\t\t}\n\n\t\treturn Asset.promises[ this.config.url ];\n\t}\n\n\t/**\n\t * Loads a js file into dom\n\t */\n\tprivate loadScript() {\n\n\t\tvar url = this.config.url;\n\n\t\tAsset.promises[ url ] = new Promise( function( resolve, reject ) {\n\t\t\tvar element = document.createElement( 'script' );\n\t\t\telement.src = url;\n\t\t\telement.addEventListener( 'load', resolve );\n\t\t\telement.addEventListener( 'error', reject );\n\t\t\tdocument.body.appendChild( element );\n\t\t}).then( function( event:Event ) {\n\t\t\tthis.finalize( url );\n\t\t\treturn event;\n\t\t}.bind( this ));\n\t}\n\n\t/**\n\t * Loads a css file into the dom\n\t */\n\tprivate loadCSS() {\n\n\t\tvar url = this.config.url;\n\n\t\tAsset.promises[ url ] = new Promise( function( resolve, reject ) {\n\t\t\tvar element = document.createElement( 'link' );\n\t\t\telement.type = 'text/css';\n element.rel = 'stylesheet';\n\t\t\telement.href = url;\n\t\t\telement.addEventListener( 'load', resolve );\n\t\t\telement.addEventListener( 'error', reject );\n\t\t\tdocument.head.appendChild( element );\n\t\t}).then( function( event:Event ) {\n\t\t\tthis.finalize( url );\n\t\t\treturn event;\n\t\t}.bind( this ));\n\t}\n\n\tprivate finalize( url:string ) {\n\t\tAsset[ url ] = new Promise( function( resolve, reject ) { resolve() } );\n\t}\n\n\t/**\n\t * Guesses the type based on the given url\n\t */\n\tprivate guessType( url:string ):\"js\"|\"css\" {\n\n\t\tif( url.split('.').pop() == \"js\" ) return \"js\";\n\t\tif( url.split('.').pop() == \"css\" ) return \"css\";\n\n\t\treturn \"js\";\n\t} \n}\n\nexport default Asset;","import LookupDict from '../lookup/LookupDict';\nimport LookupObject from '../lookup/LookupObject';\nimport { HttpRequest } from '../request/Request';\nimport Template from '../template/TemplateManager';\nimport Asset from './Asset';\n\nexport interface SetupConfig {\n\troutes:any,\n\tdata:any,\n\tassets:any,\n}\n\n/**\n * A class that manages external routes, data and assets.\n */\nexport class Setup {\n\n\tprivate lookup:LookupDict;\n\n\tconstructor() {\n\t\tthis.lookup = new LookupDict();\n\t}\n\n\tinit( data:SetupConfig ) {\n\t\tthis.lookup.add( new LookupObject( data ) );\n\t\tTemplate.context.setup = this;\n\t}\n\n\t/**\n\t * Returns a request that can load an save data to the url defined in the configuration\n\t * @param key The key under the routes definition\n\t * @param urlParams The parameters for the url\n\t */\n\troute( key:string, urlParams:{} = {} ):HttpRequest {\n\n\t\tvar config = this.search( \"routes.\" + key );\n\t\tvar url = Template.render( config.url, urlParams );\n\t\tvar request = new HttpRequest( url );\n\n\t\tif( config.method ) request.method( config.method );\n\t\tif( config.headers ) request.headers( config.headers );\n\t\t\n\t\treturn request;\n\t}\n\n\t/**\n\t * Returns any data that is defined under the data configuration\n\t */\n\tdata( key?:string ):any {\n\t\tif( key === undefined ) return this.search( 'data' );\n\t\treturn this.search( \"data.\" + key );\n\t}\n\n\t/**\n\t * Returns a asset loader that can load additional scripts and css files.\n\t */\n\tasset( key:string ):Asset {\n\t\treturn new Asset( this.search( \"assets.\" + key ) );\n\t}\n\n\tprivate search( key ):any {\n\t\tvar config = this.lookup.get( key, null );\n\t\tif( !config ) throw new Error( \"Could not find defintion for api with key: \" + key );\n\t\treturn config;\n\t}\n}\n\n\nvar setup = new Setup();\nexport default setup;","import DOMEventSignal from './signal/DOMEventSignal';\n\n\nexport interface EventCallback {\n\t( event:Event ):void;\n}\n\n/**\n\t* Defines scroll infomations like top, left and width, height \n\t* The width/height is the max value for the scrollTop/scrollLeft value.\n\t*/\nexport interface ScrollInfo {\n\ttop:number;\n\theight:number;\n\tleft:number;\n\twidth:number;\n}\n\n/**\n\t* Defines a dimension with width & height \n\t*/\nexport interface Dimension {\n\theight:number;\n\twidth:number;\n}\n\n/**\n\t* #Window\n\t* \n\t* A class that provides signals for window events\n\t*/\n\nexport class Window {\n\t\n\tpublic scroll = new DOMEventSignal( window, 'scroll' );\n\tpublic resize = new DOMEventSignal( window, 'resize' );\n\tpublic hashchange = new DOMEventSignal( window, 'hashchange' );\n\tpublic popstate = new DOMEventSignal( window, 'popstate' );\n\t\n\t/**\n\t\t* Returns the scroll top position of the window/body.\n\t\t*/\n\tpublic scrollInfo():ScrollInfo {\n\t\t\n\t\tvar e = document.documentElement;\n\t\tvar b = document.body;\n\t\tvar doc = this.document();\n\t\tvar view = this.viewport();\n\t\t\n\t\treturn {\n\t\t\ttop: ( e && e.scrollTop ) || b.scrollTop,\n\t\t\tleft: ( e && e.scrollLeft ) || b.scrollLeft,\n\t\t\twidth: doc.width - view.width,\n\t\t\theight: doc.height - view.height\n\t\t}\n\t}\n\n\t\n\t/**\n\t\t* Returns the dimensions of the document without the scrollbars\n\t\t*/\n\tpublic document():Dimension {\n\t\treturn {\n\t\t\twidth: document.documentElement.offsetWidth || document.body.offsetWidth,\n\t\t\theight: Math.max(\t\t// Hack for IE 10 document height\n\t\t\t\tdocument.body.scrollHeight, document.documentElement.scrollHeight,\n\t\t\t\tdocument.body.offsetHeight, document.documentElement.offsetHeight,\n\t\t\t\tdocument.body.clientHeight, document.documentElement.clientHeight\n\t\t\t)\n\t\t}\n\t}\n\t\n\t/**\n\t\t* Returns the dimensions of the viewport including the scrollbars\n\t\t*/\n\tpublic viewport():Dimension {\n\t\treturn {\n\t\t\twidth: window.innerWidth || document.body.clientWidth,\n\t\t\theight: window.innerHeight || document.body.clientHeight\n\t\t}\n\t}\n}\n\nexport default new Window();","import Signal from 'ln/signal/Signal';\nimport Node from 'ln/node/Node';\nimport View from 'ln/view/View';\nimport Window from 'ln/node/Window';\nimport Element from './elements/Element';\nimport Chapter from './elements/Chapter';\nimport ChapterModel from './models/ChapterModel';\nimport IChapterLink from './LernBuch';\n\n/**\n * Interface for all listener function that listen on the top signal\n */\ninterface ScrollChangeListener {\n\t( visible:Array, ...args:any[] );\n}\n\n/**\n * A class that monitors the visible elements of a lernbuch\n */\nclass ScrollMonitor {\n\t\n\t// Offest-Top and Offset-Bottom (in case of fixed header / footer)\n\tpublic offsetTop:number = 0;\n\tpublic offsetBottom:number = 0;\n\t\n\tprivate visibleElements:Array = [];\n\tprivate _elements:Array = [];\n\t\n\t// event when the top visible element has changed\n\tpublic change:Signal = new Signal();\n\t\n\tconstructor( elements:Array = [] ) {\n\t\t\n\t\tthis._elements = elements;\n\n\t\t// register on window events\n\t\tWindow.resize.add( this.update, this );\n\t\tWindow.scroll.add( this.update, this );\n\n\t}\n\n\t/**\n\t * Set the elements for the scrollMonitor\n\t */\n\tset elements( elements:Array ){\n\t\tthis._elements = elements;\n\t\tthis.visibleElements = [];\n\t\tthis.update();\n\t}\n\t\n\t/**\n\t * Iterates over all the rendered Elements and updates the visibleElements array\n\t */\n\tpublic update() {\n\t\t\n\t\tvar scrollInfo = Window.scrollInfo();\n\t\tvar viewport = Window.viewport();\n\t\t\n\t\tvar viewportTop:number = scrollInfo.top + this.offsetTop;\n\t\tvar viewportHeight:number = viewport.height - (this.offsetTop + this.offsetBottom);\n\t\tvar viewportBottom:number = viewportTop + viewportHeight;\n\t\t\n\t\tvar currentElements:Array = [];\n\t\t\n\t\t// loop over the rendered elements\n\t\tthis._elements.forEach( ( element:Element ) => {\n\t\t\t\n\t\t\tvar bounds = element.node.bounds();\n\t\t\t\n\t\t\t// element larger than viewPort\n\t\t\tif (bounds.top < viewportTop && bounds.bottom > viewportBottom) {\n\t\t\t\tcurrentElements.push( element );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t// element fully inside viewport\n\t\t\tif( bounds.top >= viewportTop && bounds.bottom <= viewportBottom) {\n\t\t\t\tcurrentElements.push( element );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t// partial visible, calc overlappping height,\n\t\t\t// if more than 50% of viewport is covered -> add\n\t\t\tvar visibleTop = Math.max( bounds.top, viewportTop );\n\t\t\tvar visibleBot = Math.min( bounds.bottom, viewportBottom );\n\t\t\tif( ( visibleBot - visibleTop ) / viewportHeight >= 0.5 ) {\n\t\t\t\tcurrentElements.push( element );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t});\n\t\t\t\n\t\t// compare \n\t\tthis.compareElements( currentElements );\n\t}\n\t\n\t/**\n\t * Compares the given current elements and check with the visibleElements which are still visible or not.\n */\n\tprivate compareElements( currentElements:Array ) {\n\t\t\n\t\tvar hasChange = false;\n\t\t\n\t\t// check if current element is new visible\n\t\tcurrentElements.forEach( ( element:View, index )=> {\n\t\t\tif( this.visibleElements.indexOf(element) == -1 ) {\n\t\t\t\thasChange = true;\n\t\t\t}\n\t\t});\n\t\t\n\t\t// check if any of the old elements are not visible anymore.\n\t\tthis.visibleElements.forEach( ( element, index )=> {\n\t\t\tif( currentElements.indexOf(element) == -1 ) {\n\t\t\t\thasChange = true;\n\t\t\t}\n\t\t});\n\t\t\n\t\t// check if top element has changed\n\t\thasChange = hasChange || currentElements[0] != this.visibleElements[0];\n\t\t\n\t\t// update the visible element\n\t\tthis.visibleElements = currentElements;\n\t\t\t\n\t\tif( hasChange ) this.change.dispatch( currentElements );\n\t}\n\n\tpublic scrollToElementHash(){\n\n\t\t// no element hash, scroll to page top\n\t\tif( window.location.hash == '' ){\n\t\t\twindow.scrollTo( 0, 0 );\n\t\t} else {\n\n\t\t\tNode.one( window.location.hash ).native.scrollIntoView();\n\t\n\t\t\t// plus add scrolling for offset\n\t\t\tif( this.offsetTop != 0 ){\n\t\t\t\twindow.scrollBy( 0, -this.offsetTop );\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport default ScrollMonitor;\n","import Signal from '../signal/Signal';\n\n/**\n * Function interface for listeners on the 'add' Signal. \n */\nexport interface ListAddedListener {\n\t( newItem:T, newIndex?:number ):void;\n}\n/**\n * Function interface for listeners on the 'remove' Signal. \n */\nexport interface ListRemovedListener {\n\t( removedItem:T, index:number ):void;\n}\n/**\n * Function interface for listeners on the 'filled' Signal. \n */\nexport interface ListFilledListener {\n\t( items:T[] ):void;\n}\n\n\n/**\n * A generic representation of a list that throws event on add, remove and fill\n * \n */\nexport class List {\n\n\tpublic added:Signal>;\n\tpublic removed:Signal>;\n\tpublic filled:Signal>;\n\tpublic sorted:Signal>;\n\n\tprotected items:T[];\n\t\n\tconstructor( items?:T[] ) {\n\t\tthis.added = new Signal>();\n\t\tthis.removed = new Signal>();\n\t\tthis.filled = new Signal>();\n\t\tthis.sorted = new Signal>();\n\t\t\n\t\tthis.items = ( items ) ? items : [];\n\t}\n\t\n\t/**\n\t * Returns a copy of the internal array. \n\t */\n\tall():T[] {\n\t\treturn this.items.slice();\n\t}\n\t\n\t/**\n\t * Refill the list with items\n\t * @param items to fill the list with \n\t */\n\tfill( items:T[], silent:boolean = false ) {\n\t\tthis.items = items; // do not add the original array.\n\t\tif( !silent ) this.filled.dispatch( items );\n\t}\n\n\t\n\t/**\n\t * Adds an item to the list at the given index. If the index is outside of the array the item is appended at the end\n\t * @param item The item to be added\n\t * @param index The index at which the item is added.\n\t */\n\tadd( item:T, index:number ):number {\n\t\t\n\t\t// make sure index is in array length\n\t\tindex = Math.max( Math.min( index, this.items.length ), 0 );\n\n\t\tthis.items.splice( index, 0, item );\n\t\tthis.added.dispatch( item, index );\n\t\treturn index;\n\t}\n\n\t/**\n\t * Removes the item at the given index\n\t * @param index The index at which the item will be removed\n\t */ \n\tremove( item:T ):boolean {\n\t\t\n\t\tvar index = this.index( item );\n\n\t\tif( index >= 0 ) return this.removeAt( index );\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Removes an item at the given index\n\t */\n\tremoveAt( index:number ):boolean {\n\n\t\tvar res = this.items.splice( index, 1 );\n\t\tvar item = res[0];\n\n\t\tif( item ) {\n\t\t\tthis.removed.dispatch( item, index );\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Appends a new item to the list\n\t */\n\tappend( item:T ) {\n\t\tthis.add( item, this.items.length );\n\t}\n\t\n\t/**\n\t * Prepends a new item to the list\n\t */\n\tprepend( item:T ) {\n\t\tthis.add( item, 0 );\n\t}\n\t\n\t/**\n\t * Get the item with the given index\n\t * @param index The index of the item\n\t */\n\tget( index:number ):T {\n\t\treturn this.items[ index ];\n\t}\n\t\n\t/**\n\t * Returns the first item in the list\n\t */\n\tfirst():T {\n\t\treturn this.items[0];\n\t}\n\t\n\t/**\n\t * Returns the last item in the list\n\t */\n\tlast():T {\n\t\treturn this.items[ this.items.length - 1 ];\n\t}\n\n\t/**\n\t * Shortcut to empty the list\n\t */\n\tempty() {\n\t\tthis.fill( [] );\n\t}\n\n\t/**\n\t * Finds an element in the list with the given closure\n\t */\n\tfind( closure:( item:T, index?:number, array?:Array ) => boolean ) {\n\t\treturn this.all().filter( closure )[ 0 ];\n }\n \n\n\t\n\t/**\n\t * Returns the index of the given item\n\t */\n\tindex( searchItem:T ):number {\n\t\tvar index = -1;\n\t\tthis.items.forEach( ( item, i ) => {\n\t\t\tif( item === searchItem ) index = i;\n\t\t});\n\t\treturn index;\n\t}\n\n\t/**\n\t * Tests if the given item exists in the list\n\t */\n\tcontains( item:T ):boolean {\n\t\treturn this.index( item ) >= 0;\n\t}\n\n\t/**\n\t * Returns the length of the list\n\t */\n\tget length():number {\n\t\treturn this.items.length;\n\t}\n\n\t/**\n\t * Resorts the items based on the given array of items\n\t */\n\tsort( items:T[] ) {\n\n\t\t// check if items size match\n\t\t// no check for same instances...\n\t\tif( this.items.length != items.length ) throw \"Sort items should be the same size\";\n\t\t\n\t\tthis.items = items;\n\t\tthis.sorted.dispatch( items );\n\t}\n\n\t/**\n\t * Sorts the list by the closures return value\n\t */\n\tsortBy( closure:( item:T )=>any ) {\n\t\tvar items = this.items.map( function( x:T ) {\n\t\t\treturn [ x, closure( x ) ];\n\t\t}).sort(function (a, b) {\n\t\t\treturn a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0;\n\t\t}).map(function (x) {\n\t\t\treturn x[0];\n\t\t});\n\t\tthis.sort( items );\n\t}\n}\n\nexport default List;","import IoC from '../ioc/IoC';\r\nimport Model from '../model/Model';\r\nimport View from '../view/View';\r\nimport { List, ListAddedListener, ListFilledListener, ListRemovedListener } from '../list/List';\r\nimport Node from '../node/Node';\r\nimport Signal from '../signal/Signal';\r\nimport Template from '../template/TemplateManager';\r\n\r\n/**\r\n * Specific interface for closure function used in the ioc\r\n */\r\nexport interface IoCFactoryFunction {\r\n\t( item:T, index?:number ): { node:Node };\r\n}\r\n\r\n/**\r\n * A function that defines how to a map the registered keywords in the ioc with the item:T\r\n * Example if T is Model:\r\n * \t\tfunction( model:Model ) {\r\n * \t\t\treturn model.modelName;\r\n * \t\t}\r\n * \r\n */\r\nexport interface IoCSelectorFunction {\r\n\t( item: T ): string;\r\n}\r\n\r\n\r\n/**\r\n * An interface for links that combine the node and its item\r\n */\r\nexport interface ILink {\r\n\tnode: Node;\r\n\titem: T;\r\n}\r\n\r\n/**\r\n * An interface that defines what is required as a source for the ListRenderer \r\n */\r\nexport interface ListRendererSource {\r\n\tadded:Signal>;\r\n\tremoved:Signal>;\r\n\tfilled:Signal>;\r\n\tsorted:Signal>;\r\n\tall():T[];\r\n\tfill( data:T[] );\r\n}\r\n\r\n\r\n/**\r\n * Renders a list with items of type T into a template. Listens on list signals for rerendering.\r\n * The rendering of the list items is defined through an ioc of for simpler usage with a render function.\r\n * \r\n */\r\nexport class ListRenderer = List> {\r\n\r\n\tpublic ioc: IoC> = new IoC>();\r\n\tpublic selectorFunction: IoCSelectorFunction;\r\n\tpublic links = new List>();\r\n\tpublic container:Node;\r\n\r\n\tprotected _source:G;\r\n\t \r\n\tconstructor( container?:Node ) {\r\n\t\tthis.container = container;\r\n\r\n\t\t// the default selector function to use without ioc definitions.\r\n\t\tthis.selectorFunction = function( item:T ) {\r\n\t\t\treturn ( item instanceof Model ) ? item.get( 'modelName' ) : 'default';\r\n\t\t}\r\n\r\n\t\tthis.source = new List( [] );\r\n\t}\r\n\t\r\n\tget source():G {\r\n\t\treturn this._source;\r\n\t}\r\n\t\r\n\tset source( items:G ) {\r\n\t\tif( this._source ) this.removeListeners();\r\n\t\tthis._source = items;\r\n\t\tthis.addListeners();\r\n\t\t\r\n\t\tif( this.container ) this.onFilled( items.all() );\r\n\t}\r\n\r\n\tpublic defaultRender( closure:IoCFactoryFunction ) {\r\n\t\tthis.ioc.add( 'default', closure );\r\n\t}\r\n\t\r\n\tprotected addListeners(){\r\n\t\tthis._source.filled.add( this.onFilled, this );\r\n\t\tthis._source.added.add( this.onAdded, this );\r\n\t\tthis._source.removed.add( this.onRemoved, this );\r\n\t\tthis._source.sorted.add( this.onSorted, this );\r\n\t}\r\n\t\r\n\tprotected removeListeners(){\r\n\t\tthis._source.filled.remove( this.onFilled, this );\r\n\t\tthis._source.added.remove( this.onAdded, this );\r\n\t\tthis._source.removed.remove( this.onRemoved, this );\r\n\t\tthis._source.sorted.remove( this.onSorted, this );\r\n\t}\r\n\t\r\n\tprotected renderList( items:T[] ) {\r\n\r\n\t\tvar fragment = document.createDocumentFragment();\r\n\t\t\r\n\t\titems.forEach( function( item, index ) {\r\n\r\n\t\t\tvar obj = >this.ioc.get( this.selectorFunction( item ) )( item, index );\r\n\t\t\tobj.item = item; // inject item\r\n\t\t\tthis.links.append( obj );\r\n\r\n\t\t\t// add to fragment\r\n\t\t\tfragment.appendChild( ( obj.node instanceof Node ) ? obj.node.native : obj.node );\r\n\t\t}, this);\r\n\r\n\t\t// append fragment to container.\r\n\t\tthis.container.append( fragment );\r\n\t}\r\n\r\n\tprotected onFilled( items:T[] ) {\r\n\t\t// remove all links\r\n\t\tthis.links.all().forEach( link => {\r\n\t\t\tlink.node.remove();\r\n\t\t});\r\n\t\t\r\n\t\tthis.links.empty();\t\r\n\t\tthis.renderList( items );\r\n\t}\r\n\r\n\tprotected onAdded( newItem:T, newIndex:number ) {\r\n\t\tvar obj = >this.ioc.get( this.selectorFunction( newItem ) )( newItem, newIndex );\r\n\t\tobj.item = newItem;\r\n\t\tthis.container.insert( obj.node, newIndex );\r\n\t\tthis.links.add( obj , newIndex );\r\n\t} \r\n\r\n\tprotected onRemoved( removedItem:T, index:number ) {\r\n\t\tvar link = this.links.get( index );\r\n\t\tthis.links.removeAt( index );\r\n\t\tthis.container.removeChild( link.node );\r\n\t\tlink.node = undefined;\r\n\t}\r\n\r\n\tprotected onSorted( items:T[] ) {\r\n\t\tvar links = [];\r\n\t\tfor( var i = 0; i < items.length; i++ ) {\r\n\t\t\tvar link = this.linkOf( items[i] );\r\n\t\t\tthis.container.insert( link.node, i );\r\n\t\t\tlinks.push( link );\r\n\t\t}\r\n\t\tthis.links.fill( links );\r\n\t}\r\n\r\n\tpublic linkOf( item:T | Node ):ILink {\r\n\r\n\t\t// define on which attribute to check\r\n\t\tvar attr = ( item instanceof Node ) ? \"node\" : \"item\";\r\n\r\n\t\treturn this.links.find( function( link ) {\r\n\t\t\treturn link[attr] === item; \r\n\t\t});\r\n\t}\r\n}\r\n\r\nexport default ListRenderer;\r\n","// import lib\r\nimport Node from 'ln/node/Node';\r\nimport View from 'ln/view/View';\r\nimport { ListRenderer, ILink } from 'ln/list/ListRenderer';\r\nimport List from 'ln/list/List';\r\nimport IoC from 'ln/ioc/IoC';\r\nimport { IoCFactoryFunction } from 'ln/list/ListRenderer';\r\n\r\n// import project\r\nimport Element from './Element';\r\nimport LernBuch from '../LernBuch';\r\nimport ChapterModel from '../models/ChapterModel';\r\nimport ElementModel from '../models/ElementModel';\r\n\r\n\r\nexport interface ChapterElementFactoryFunction extends IoCFactoryFunction {\r\n\t( item:ElementModel ):Element;\r\n}\r\n\r\nexport var ioc = new IoC();\r\n\r\n/**\r\n * This class renders a chapter element\r\n */\r\nclass Chapter extends View {\r\n\t\r\n\tpublic elements:ListRenderer;\r\n\t\r\n\tconstructor( public model:ChapterModel, public lernbuch:LernBuch) {\r\n\t\t\r\n\t\tsuper();\r\n\t\tthis.elements = new ListRenderer();\r\n\t\tthis.elements.ioc = ioc;\r\n\t\t\r\n\t\tthis.defaultTemplate = this.model.get( 'template', 'lb.chapter-element' );\r\n\t}\r\n\r\n\t/**\r\n\t * Renders the chapter into its container.\r\n\t */\r\n\tpublic init() {\r\n\t\t\r\n\t\t// render elements\r\n\t\tthis.elements.container = this.node.one( '.elements' );\r\n\t\tthis.elements.source = this.model.elements;\r\n\r\n\t\tthis.elements.links.all().forEach( link => {\r\n\t\t\t( link as Element ).chapter = this;\r\n\t\t});\r\n\t\t\r\n\t}\r\n\r\n\t/**\r\n\t * Define the data to render on the element\r\n\t */\r\n\tprotected renderData(){\r\n\t\treturn this.model.object();\r\n\t}\r\n\t\r\n}\r\n\r\nexport default Chapter;","import View from 'ln/view/View';\nimport Node from 'ln/node/Node';\nimport Signal from 'ln/signal/Signal';\nimport IoC from 'ln/ioc/IoC';\nimport { FactoryFunction } from 'ln/view/Scanner'; \nimport { ILink } from 'ln/list/ListRenderer';\nimport Mapper from 'ln/model/Mapper';\nimport Scanner from 'ln/view/Scanner';\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport setup from 'ln/setup/setup';\nimport BookModel from './models/BookModel';\nimport ChapterModel from './models/ChapterModel';\nimport ElementModel from './models/ElementModel';\nimport ScrollMonitor from './ScrollMonitor';\nimport Element from './elements/Element';\nimport Chapter from './elements/Chapter';\nimport ChapterNavigation from './navigation/chapter/Navigation';\nimport SearchInput from './search/SearchInput';\nimport SearchOutput from './search/SearchOutput';\nimport OutlineNavigation from './navigation/outline/OutlineNavigation';\n\n\n/**\n * Interface for all listener function that listen on the top signal\n */\nexport interface ChapterChangedCallback {\n ( chapter:Chapter );\n}\n\nexport var scanner = new Scanner();\nexport var mapper = new Mapper();\n\n\n\n/**\n * This class setups all required elements for a default lernbuch application.\n * It instantiates the LernBuchView that renders the chapters and elements.\n * It instantiates a backend that gets all the configurations for the chapters and elements \n */\nexport class LernBuch extends View {\n\n\tpublic book:BookModel;\n\tpublic scrollMonitor:ScrollMonitor;\n\n\tprivate _currentChapter:Chapter = null;\n\tprivate currentLocation:string = null;\n\n\t// signal for chapter change event\n\tpublic chapterChanged = new Signal();\n\n\tconstructor( data:any, template:string = 'lb.lernbuch' ) {\n\t\t\n\t\tsuper();\n\n\t\tthis.scrollMonitor = new ScrollMonitor();\n\t\tthis.defaultTemplate = template;\n\n\t\tthis.book = mapper.model( data ) as BookModel;\n\t\t\n\t}\n\t\n\t/**\n\t * Execute things after render\n\t */\n\tpublic init() {\n\t\tscanner.run( this.node, this );\n\t}\n\t\n\t/**\n\t * Define data to render\n\t */\n\tprotected renderData(){\n\t\treturn this.book;\n\t}\n\n\t/**\n\t * Show the chapter by the given model\n\t * @param chapter ChapterModel\n\t * @param element ElementModel\n\t */\n\tpublic show( chapter:ChapterModel, element:ElementModel = null ) {\n\t\t\n\t\tif( !this.chapter || this.chapter.model != chapter ){\n\n\t\t\t\n\t\t\tthis.node.js( 'chapter' ).empty();\n\t\t\t\n\t\t\tthis.chapter = new Chapter( chapter, this ).render( this.node.js( 'chapter' ) ) as Chapter;\n\n\t\t\t// dispatch chapter changed event\n\t\t\tthis.chapterChanged.dispatch( this.chapter );\n\n\t\t\t// reset scrollMonitor\n\t\t\tthis.scrollMonitor.elements = this.chapter.elements.links.all().map( ( link ) => {\n\t\t\t\treturn link as any;\n\t\t\t});\n\t\t\t\n\t\t}\n\n\t\tthis.updateURL( chapter, element );\n\t\tthis.scrollMonitor.scrollToElementHash();\n\t}\n\n\tprivate updateURL( chapter:ChapterModel, element:ElementModel ){\n\t\tvar newLocation = setup.route( 'chapter', { book: this.book.slug, chapter: chapter.slug, uid: (element) ? element.uid : '' } ).url();\n\t\tvar history = window.history.pushState( null, chapter.slug, newLocation );\n\t}\n\t\n\tget chapter():Chapter {\n\t\treturn this._currentChapter;\n\t}\n\n\tset chapter( chapter:Chapter ){\n\t\tthis._currentChapter = chapter;\n\t}\n\t\n\tget navigation():ChapterNavigation {\n\t\treturn scanner.first( 'navigation' ) as ChapterNavigation;\t\n\t}\n\n\tget outlineNavigation():OutlineNavigation {\n\t\treturn scanner.first( 'outline-navigation' ) as OutlineNavigation;\n\t}\n\t\n\tget searchInput():SearchInput {\n\t\treturn scanner.first( 'search-input' ) as SearchInput;\n\t}\n\t\n\tget searchOutput():SearchOutput {\n\t\treturn scanner.first( 'search-output' ) as SearchOutput;\n\t}\n}\n\nexport default LernBuch;","import View from 'ln/view/View';\nimport Template from 'ln/template/TemplateManager';\nimport { scanner } from 'ln/view/Scanner';\nimport Node from 'ln/node/Node';\nimport { ListRenderer } from 'ln/list/ListRenderer';\nimport setup from 'ln/setup/setup';\n\nclass Overview extends View {\n //public data;\n\n constructor( ) {\n super( { 'template': 'lb.overview' } );\n }\n\n init() {\n var listRenderer = new ListRenderer( this.node.js('themen') );\n\n listRenderer.defaultRender(function( item ) {\n var node = Node.fromHTML( Template.render( 'lb.thema-card', item ));\n node.click.add( function() {\n window.location.href = setup.route( 'thema', { \"thema\": item.slug, \"uid\": '' } ).url()\n });\n return { node: node }\n });\n\n listRenderer.source.fill( setup.data( 'themen' ) )\n }\n\n /*setData( data:any) {\n this.data = data;\n }*/ \n}\n\nexport default Overview;\n\nscanner.ioc.add( 'overview', function( node:Node ) {\n return new Overview( ).render( node );\n});","import View from 'ln/view/View';\nimport Template from 'ln/template/TemplateManager';\nimport { scanner } from 'ln/view/Scanner';\nimport Node from 'ln/node/Node';\nimport { ListRenderer } from 'ln/list/ListRenderer';\nimport setup from 'ln/setup/setup';\n\nclass Header extends View {\n\n private currentThema;\n\n constructor(){\n\n var themen, thema\n\n try {\n themen = setup.data( 'themen' )\n } catch(e) {}\n\n try {\n thema = setup.data( 'thema' )\n } catch(e) {}\n\n super( { template: 'lb.header', 'themen': themen, 'thema': thema } );\n\n this.currentThema = thema\n }\n\n init() {\n // this.node.js('theme-menu').\n var listRenderer = new ListRenderer( this.node.js('theme-menu') );\n\n var currentId = '';\n\n if( this.currentThema) currentId = this.currentThema.id\n\n\n\n listRenderer.defaultRender(function( item ) {\n return new View( { 'template': 'lb.theme-menuitem', 'active': (item.id == currentId) ,'thema': item, 'url': setup.route( 'thema', { 'thema': item['slug'], 'uid':'' }).url() }).render();\n }.bind( this ));\n listRenderer.source.fill( this.data.themen )\n\n this.node.js( 'menu-toggler' ).click.add( function() {\n this.node.js( 'theme-menu' ).toggleClass( 'hidden' );\n }, this);\n\n }\n}\n\nexport default Header;\n\nscanner.ioc.add( 'header', function( node:Node ) {\n return new Header( ).render( node );\n});","import View from 'ln/view/View';\nimport Template from 'ln/template/TemplateManager';\nimport { scanner } from 'ln/view/Scanner';\nimport Node from 'ln/node/Node';\nimport { ListRenderer } from 'ln/list/ListRenderer';\nimport setup from 'ln/setup/setup';\n\nclass Footer extends View {\n constructor(){\n super( { template: 'lb.footer'} );\n }\n}\n\nexport default Footer;\n\nscanner.ioc.add( 'footer', function( node:Node ) {\n return new Footer( ).render( node );\n});","import View from 'ln/view/View';\nimport setup from 'ln/setup/setup';\nimport Signal from 'ln/signal/Signal';\nimport Request from 'ln/request/Request';\n\n\ninterface StateChangedCallback{\n\t( item:Taskitem )\n}\n\nclass Taskitem extends View {\n public data;\n\tpublic clicked:Signal;\n\n constructor( ) {\n super( { 'template': 'lb.task-listitem' } );\n this.clicked = new Signal();\n }\n\n init() {\n\n if( this.data.data.checked ) {\n this.checked = true;\n this.clicked.dispatch( this );\n }\n \n this.node.js( 'task-done' ).click.add( function( node ) {\n this.data.data.checked = this.checked;\n this.clicked.dispatch( this );\n this.sendUserData();\n }, this );\n \n }\n\n renderData() {\n return this.data.data;\n }\n\n setData( data:any) {\n this.data.data = data;\n }\n\n set checked( c ) {\n this.node.js( 'task-done' ).checked = c;\n }\n\n get checked() {\n return this.node.js( 'task-done' ).checked;\n }\n\n sendUserData() {\n\n if( !setup.data( 'savestate' ).id ) return;\n\n var data = {\n 'savedModelName': this.data.data.modelName,\n 'savedModelId': this.data.data.id,\n 'attribute': 'checked',\n 'value': this.checked,\n };\n\n Request.post( setup.route( 'userData' ).url() )\n .headers( { 'Content-Type':'application/json' } )\n .send( JSON.stringify( data ));\n }\n}\n\nexport default Taskitem;","import View from 'ln/view/View';\nimport Template from 'ln/template/TemplateManager';\nimport { scanner } from 'ln/view/Scanner';\nimport Node from 'ln/node/Node';\nimport Taskitem from './Taskitem';\nimport Task from './Task';\nimport { ListRenderer } from 'ln/list/ListRenderer';\nimport setup from 'ln/setup/setup';\nimport ElementModel from '../models/ElementModel';\n\n\nclass Chapter extends View {\n public data;\n private count = 0;\n private open = false\n\n constructor( ) {\n super( { 'template': 'lb.chapter' } );\n }\n\n init() {\n var listRenderer = new ListRenderer( this.node.js('tasks') );\n\n listRenderer.defaultRender(function( item ) {\n var view = new Taskitem();\n view.setData( item );\n view.clicked.add( function( taskitem:Taskitem ) {\n if( taskitem.checked ) {\n this.count ++;\n } else {\n this.count --;\n }\n this.setCounter()\n }, this)\n view.render();\n return view;\n }.bind( this ));\n\n listRenderer.source.fill( this.renderData().tasks )\n\n listRenderer.links.all().forEach( function( link ) {\n var item = link;\n item.data.parent = this.data.data;\n }, this)\n\n this.node.js( 'toggle' ).click.add( function() {\n if( ! this.open ) {\n this.showTasks();\n } else {\n this.hideTasks()\n }\n }, this)\n\n this.setCounter()\n if( this.loadLocalstorageUserData( 'chapter-state') == 'closed') {\n this.hideTasks();\n }else{\n this.showTasks();\n }\n }\n\n renderData() {\n return this.data.data;\n }\n\n showTasks() {\n this.node.js( 'toggle' ).addClass( 'open' )\n this.node.js( 'tasks' ).removeClass( 'hidden' )\n this.open = true;\n this.saveLocalstorageUserData( 'chapter-state', 'open' );\n }\n\n hideTasks() {\n this.node.js( 'toggle' ).removeClass( 'open' )\n this.node.js( 'tasks' ).addClass( 'hidden' )\n this.open = false;\n this.saveLocalstorageUserData( 'chapter-state', 'closed' );\n }\n\n setData( data:any) {\n this.data.data = data;\n }\n\n setCounter() {\n var total = this.renderData().tasks.length\n var count = this.count\n this.node.js( 'counter' ).html = count + '/' + total;\n this.node.js( 'progress' ).setAttribute( 'style', 'background-position:' + (100-(count/total*100)) + '% 0' )\n }\n\n saveLocalstorageUserData( attribute, value ) {\n localStorage.setItem( this.data.data.id + '-' + attribute, value );\n\n }\n\n loadLocalstorageUserData( attribute ) {\n return localStorage.getItem( this.data.data.id + '-' + attribute );\n } \n\n}\n\nexport default Chapter;","import View from 'ln/view/View';\nimport Template from 'ln/template/TemplateManager';\nimport { scanner } from 'ln/view/Scanner';\nimport Node from 'ln/node/Node';\nimport { ListRenderer } from 'ln/list/ListRenderer';\nimport setup from 'ln/setup/setup';\nimport Chapter from './Chapter';\n\nclass Lernpfad extends View {\n\n public data;\n\n constructor( ) {\n super( { 'template': 'lb.lernpfad' } );\n }\n\n init() {\n \n var listRenderer = new ListRenderer( this.node.js('chapters') );\n\n listRenderer.defaultRender(function( item ) {\n var view = new Chapter();\n view.setData( item );\n view.render();\n return view;\n });\n\n listRenderer.source.fill( this.renderData().chapters )\n }\n\n renderData() {\n return this.data.data;\n }\n\n setData( data:any) {\n this.data.data = data;\n } \n}\n\nexport default Lernpfad;","// improt lib\nimport Signal from 'ln/signal/Signal';\nimport View from 'ln/view/View';\nimport { EmptyCallback } from 'ln/signal/Signal';\n\n// import project\nimport ElementModel from '../models/ElementModel';\nimport LernBuch from '../LernBuch';\nimport Chapter from './Chapter';\n\n/**\n * A base class for elements that can be rendered in a chapter\n */\nclass Element extends View {\n\n\tpublic model:ElementModel;\n\tpublic chapter:Chapter\n\n\t/**\n\t * Sets the configuration for an element\n\t * @param model ElementModel\n\t */\n\tconstructor( model:ElementModel ) {\n\t\tsuper();\n\t\tthis.model = model;\n\t\tthis.defaultTemplate = ( this.model ) ? this.model.get( 'template', 'lb.' + this.model.modelName.toLowerCase().replace( 'app\\\\', '' ) + '-element' ) : '';\n\t}\n\n\tget lernbuch():LernBuch{\n\t\treturn this.chapter.lernbuch;\n\t}\n\t\n\t/**\n\t * Execute things after element was rendered\n\t * For example register to events on the element\n\t */\n\tpublic init() {\n\t\t\n\t\tthis.node.setAttribute( 'name', this.model.uid );\n\t\tthis.node.setAttribute( 'id', this.model.uid );\n\t}\n\n\t/**\n\t * Define the data to render on the element\n\t */\n\tprotected renderData(){\n\t\treturn this.model.object();\n\t}\n}\n\nexport default Element;","import Signal from '../signal/Signal';\r\nimport { List, ListAddedListener, ListFilledListener, ListRemovedListener } from './List';\r\n\r\n\r\nexport interface ModifierClosure {\r\n\t( items:T[] ):T[]\r\n}\r\n/**\r\n * A generic representation of a modifiable (fiterable, sortable) list. \r\n * \r\n */\r\nexport class ModifiableList {\r\n\r\n\tpublic added:Signal>;\r\n\tpublic removed:Signal>;\r\n\tpublic filled:Signal>;\t\r\n\tpublic sorted:Signal>;\t\r\n\t\r\n\tpublic modifiers:List>;\r\n\tpublic originals:List;\r\n\r\n\tprivate cache:List;\r\n\r\n\tconstructor( items?:T[] ) {\r\n\r\n\t\tthis.added = new Signal>();\r\n\t\tthis.removed = new Signal>();\r\n\t\tthis.filled = new Signal>();\r\n\t\tthis.sorted = new Signal>();\r\n\r\n\t\tthis.modifiers = new List>();\r\n\t\tthis.modifiers.added.add( this.onModifiersChanged, this );\r\n\t\tthis.modifiers.removed.add( this.onModifiersChanged, this );\r\n\t\tthis.modifiers.filled.add( this.onModifiersChanged, this );\r\n\r\n\t\tthis.originals = new List( items );\r\n\t\tthis.originals.added.add( this.onOriginalsAdded, this );\r\n\t\tthis.originals.removed.add( this.onOriginalsRemoved, this );\r\n\t\tthis.originals.filled.add( this.onOriginalsFilled, this );\r\n\r\n\t\tthis.update();\r\n\t}\r\n\t\r\n\t/**\r\n\t * Returns only the modified items \r\n\t */\r\n\tall():T[] {\r\n\t\treturn this.cache.all();\r\n\t}\r\n\r\n\t/**\r\n\t * Delegate\r\n\t */\r\n\tfill( items:T[] ) {\r\n\t\tthis.originals.fill( items );\r\n\t}\r\n\r\n\t/**\r\n\t * Delegate\r\n\t */\r\n\tsort( items:T[] ) {\r\n\t\tthis.originals.sort( items );\r\n\t\tthis.sorted.dispatch( items );\r\n\t}\r\n\r\n\t/**\r\n\t * forces to reapply modifiers\r\n\t */\r\n\tupdate() {\r\n\t\tthis.buildCache();\r\n\t\tthis.filled.dispatch( this.cache.all() );\r\n\t}\r\n\r\n\t/**\r\n\t * Callback when the modifies list has changed.\r\n\t */\r\n\tprivate onModifiersChanged() {\r\n\t\tthis.update();\r\n\t}\r\n\t\r\n\t/**\r\n\t * Callback when something is added to the originals\r\n\t * Check it needs to be added to the modified items\r\n\t */\r\n\tprivate onOriginalsAdded( item:T, index:number ) {\r\n\t\tthis.buildCache();\r\n\r\n\t\t// check if it should be displayed\r\n\t\tvar modIndex = this.cache.index( item );\r\n\t\tif( modIndex >= 0 ) this.added.dispatch( item, modIndex );\r\n\t}\r\n\t\r\n\t/**\r\n\t * Callback when something is removed from the originals\r\n\t * Check it needs to be removed from the modified items\r\n\t */\r\n\tprivate onOriginalsRemoved( item:T, index:number ) {\r\n\r\n\t\tvar oldIndex = this.cache.index( item );\r\n\r\n\t\tthis.buildCache();\r\n\r\n\t\tif( oldIndex >= 0 ) this.removed.dispatch( item, oldIndex );\r\n\t}\r\n\r\n\t/**\r\n\t * Callback when list is filled\r\n\t */\r\n\tprivate onOriginalsFilled( item:T[] ) {\r\n\t\tthis.onModifiersChanged();\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * Updates the results of the modifications into the cache var\r\n\t */\r\n\tprivate buildCache() {\r\n\r\n\t\tvar data = this.originals.all();\r\n\t\tthis.modifiers.all().forEach( function( modifier ) {\r\n\t\t\tdata = modifier( data );\t\t\t\r\n\t\t});\r\n\r\n\t\tthis.cache = new List( data );\r\n\t}\r\n}\r\n\r\nexport default ModifiableList;\r\n","// improt lib\nimport Signal from 'ln/signal/Signal';\nimport View from 'ln/view/View';\nimport { EmptyCallback } from 'ln/signal/Signal';\n\n// import project\nimport ElementModel from '../models/ElementModel';\nimport LernBuch from 'lb/LernBuch';\nimport Chapter from './Chapter';\n\n/**\n * A base class for elements that can be rendered in a chapter\n */\nclass MaterialcardImage extends View {\n\n\tpublic model:ElementModel;\n\t//public chapter:Chapter\n\n\t/**\n\t * Sets the configuration for an element\n\t * @param model ElementModel\n\t */\n\tconstructor( model:ElementModel ) {\n\t\tsuper();\n\t\tthis.model = model;\n\t\tthis.defaultTemplate = ( this.model ) ? this.model.get( 'template', 'lb.' + this.model.modelName.toLowerCase().replace( 'app\\\\', '' ) + '-element' ) : '';\n\t}\n\t\n\t/**\n\t * Execute things after element was rendered\n\t * For example register to events on the element\n\t */\n\tpublic init() {\n\t\t\n\t\tthis.node.setAttribute( 'name', this.model.uid );\n this.node.setAttribute( 'id', this.model.uid );\n \n /*this.node.click.add( function() {\n // view image?\n })*/\n\t}\n\n\t/**\n\t * Define the data to render on the element\n\t */\n\tprotected renderData(){\n\t\treturn this.model.object();\n\t}\n}\n\nexport default MaterialcardImage;","// improt lib\nimport Signal from 'ln/signal/Signal';\nimport View from 'ln/view/View';\nimport { EmptyCallback } from 'ln/signal/Signal';\n\n// import project\nimport ElementModel from '../models/ElementModel';\nimport LernBuch from 'lb/LernBuch';\nimport Chapter from './Chapter';\n\n/**\n * A base class for elements that can be rendered in a chapter\n */\nclass MaterialcardImage extends View {\n\n\tpublic model:ElementModel;\n\t//public chapter:Chapter\n\n\t/**\n\t * Sets the configuration for an element\n\t * @param model ElementModel\n\t */\n\tconstructor( model:ElementModel ) {\n\t\tsuper();\n\t\tthis.model = model;\n\t\tthis.defaultTemplate = ( this.model ) ? this.model.get( 'template', 'lb.' + this.model.modelName.toLowerCase().replace( 'app\\\\', '' ) + '-element' ) : '';\n\t}\n\t\n\t/**\n\t * Execute things after element was rendered\n\t * For example register to events on the element\n\t */\n\tpublic init() {\n\t\t\n\t\tthis.node.setAttribute( 'name', this.model.uid );\n this.node.setAttribute( 'id', this.model.uid );\n \n /*this.node.click.add( function() {\n // view image?\n })*/\n\t}\n\n\t/**\n\t * Define the data to render on the element\n\t */\n\tprotected renderData(){\n\t\treturn this.model.object();\n\t}\n}\n\nexport default MaterialcardImage;","import View from 'ln/view/View';\nimport Template from 'ln/template/TemplateManager';\nimport { scanner } from 'ln/view/Scanner';\nimport Node from 'ln/node/Node';\nimport { ListRenderer } from 'ln/list/ListRenderer';\nimport setup from 'ln/setup/setup';\nimport { mapper } from 'lb/LernBuch';\nimport Element from 'lb/elements/Element';\nimport ElementModel from 'lb/models/ElementModel';\nimport ModifiableList from 'ln/list/ModifiableList';\nimport Filterbutton from './Filterbutton';\nimport TermFilter from 'lnui/filter/TermFilter';\n\nimport MaterialcardText from './MaterialcardText';\nimport MaterialcardImage from './MaterialcardImage';\nimport MaterialcardYT from './MaterialcardYT';\n\nclass Materiallist extends View {\n public data;\n\n public filterThemen = [];\n public filterDimensionen = [];\n public filterCompetencies = [];\n public filterGoals = [];\n private listRenderer\n\n constructor( model:any) {\n super( { 'template': (model.materials.length == 1)?'lb.materiallist-embed':'lb.materiallist' } );\n this.setData( model );\n }\n\n init( ) {\n\n var listRendererMaterials = new ListRenderer( this.node.js( 'materials' ));\n\n listRendererMaterials.ioc.add( 'default', function( model:ElementModel ) {\n return new Element( model ).render() as Element;\n });\n\n listRendererMaterials.ioc.add( 'App\\\\MaterialImage', function ( model:ElementModel ) {\n return new MaterialcardImage( model ).render() as Element;\n });\n\n listRendererMaterials.ioc.add( 'App\\\\MaterialYT', function( model:ElementModel ) {\n if( this.data.data.materials.length == 1 ) model.set('template','lb.materialyt-embed');\n \n return new MaterialcardYT( model ).render() as Element;\n }.bind( this ));\n\n listRendererMaterials.source.fill( this.data.data.materials );\n\n }\n\n renderData() {\n return this.data.data;\n }\n\n setData( data:any) {\n this.data.data = data;\n return this;\n } \n}\n\nexport default Materiallist;\n","import View from 'ln/view/View';\nimport Template from 'ln/template/TemplateManager';\nimport { scanner } from 'ln/view/Scanner';\nimport Node from 'ln/node/Node';\nimport { ListRenderer } from 'ln/list/ListRenderer';\nimport setup from 'ln/setup/setup';\nimport { mapper } from 'lb/LernBuch';\nimport Element from 'lb/elements/Element';\nimport ElementModel from 'lb/models/ElementModel';\nimport Taskitem from './Taskitem';\nimport Request from 'ln/request/Request';\nimport Lernpfad from './Lernpfad';\n\nimport Lang from 'ln/lang/Lang';\n\nclass SaveDialog extends View {\n\n private tasks = [];\n private currentThema;\n private currentLernpfad:Lernpfad;\n private themaTasks = [];\n private code = '';\n private counters = {\n 'oekologie' : 0,\n 'oekonomie' : 0,\n 'soziales' : 0\n }\n\n constructor( ) {\n super( { 'template': 'lb.savedialog' } );\n\n }\n\n init() {\n this.currentThema = setup.data( 'thema' );\n this.calculateDimensionCounts();\n\n // template stuff\n this.node.js( 'open-dialog' ).click.add( function() {\n this.update()\n this.node.js( 'dialog' ).removeClass( 'hidden' )\n }.bind( this ))\n\n this.node.js( 'close-dialog').click.add( function() {\n this.node.js( 'dialog' ).addClass( 'hidden' )\n }.bind( this ))\n\n if( this.node.js( 'save' ) ) {\n this.node.js( 'save' ).click.add( function() {\n this.enableSavestate();\n }.bind( this ))\n }\n\n if( this.node.js( 'loadstateurl' ) ) {\n this.node.js( 'copyloadstateurl' ).click.add( function() {\n var native = this.node.js( 'loadstateurl' ).native;\n native.setSelectionRange(0, native.value.length );\n native.focus();\n document.execCommand( 'copy' );\n }, this)\n }\n\n if( setup.data( 'savestate' ).code ) {\n this.node.js( 'save-link-panel').style.display = 'none'; \n this.node.js( 'copy-link-panel').style.display = 'block';\n }else {\n this.node.js( 'save-link-panel').style.display = 'block'; \n this.node.js( 'copy-link-panel').style.display = 'none';\n }\n\n }\n\n calculateDimensionCounts() {\n this.currentThema.lernpfade.forEach( function( lernpfad ) {\n var tasks = [];\n\n lernpfad.chapters.forEach( function( chapter ) {\n tasks = tasks.concat( chapter.tasks )\n chapter.tasks.forEach(element => {\n if( element.dimensions.indexOf( 'ökologie' ) > -1 ) this.counters['oekologie'] ++;\n if( element.dimensions.indexOf( 'ökonomie' ) > -1 ) this.counters['oekonomie'] ++;\n if( element.dimensions.indexOf( 'soziales' ) > -1 ) this.counters['soziales'] ++;\n }, this);\n }, this)\n\n this.themaTasks.push( { 'lernpfad': lernpfad, 'tasks': tasks } )\n }, this)\n }\n\n getCurrentDimensionCounts() {\n var counters = {\n 'ökologie' : 0,\n 'ökonomie' : 0,\n 'soziales' : 0,\n };\n\n this.themaTasks.forEach( function( obj ) {\n if( obj.lernpfad.id == this.currentLernpfad.id ) {\n obj.tasks.forEach( function( task ) {\n if( task.dimensions.indexOf( 'ökologie' ) > -1 ) counters['ökologie'] ++;\n if( task.dimensions.indexOf( 'ökonomie' ) > -1 ) counters['ökonomie'] ++;\n if( task.dimensions.indexOf( 'soziales' ) > -1 ) counters['soziales'] ++;\n });\n }\n }, this);\n\n return counters;\n }\n\n renderData() {\n var counts = this.getCurrentDimensionCounts();\n var data = {\n 'saveCount': this.tasks.length,\n 'oekologie': counts['ökologie'] > 0 ? Math.round( this.getTaskCount( 'ökologie' ) / counts['ökologie'] * 100 ) : 0,\n 'oekonomie': counts['ökonomie'] > 0 ? Math.round( this.getTaskCount( 'ökonomie' ) / counts['ökonomie'] * 100 ) : 0,\n 'soziales': counts['soziales'] > 0 ? Math.round( this.getTaskCount( 'soziales' ) / counts['soziales'] * 100 ) : 0,\n 'thema': this.currentThema,\n 'code' : setup.data( 'savestate' ).code,\n 'enableServersideUserDataStorageUrl' : setup.route( 'enableServersideUserDataStorage' ).url(),\n 'loadstateurl' : setup.route( 'loadstate' ).url()\n }\n\n return data;\n }\n\n getTaskCount( dimension:string ) {\n var currentLernpfad = setup.data( 'thema' ).lernpfade.filter( lernpfad => {\n return lernpfad.id == this.currentLernpfad.id;\n });\n\n var count = 0;\n currentLernpfad[0].chapters.forEach( chapter => {\n chapter.tasks.forEach( task => {\n if( task.dimensions.indexOf( dimension) > -1 && task.checked ) count++;\n });\n });\n return count;\n }\n\n setData( data:any) {\n this.data.data = data;\n return this;\n }\n\n setCurrentLernpfad( lernpfad:Lernpfad ) {\n this.currentLernpfad = lernpfad;\n }\n\n enableSavestate() {\n var data = {\n 'currentThema': this.currentThema.id,\n };\n\n Request.post( setup.route( 'enableServersideUserDataStorage' ).url() )\n .headers( { 'Content-Type':'application/json' } )\n .send( JSON.stringify( data ))\n .then( function( data ) {\n setup.data( 'savestate' ).code = data.code;\n setup.data( 'savestate' ).id = data.id;\n this.saveTaskStates();\n this.saveLernpfadState();\n this.update();\n }.bind( this ));\n\n }\n\n update() {\n\n // change the labels\n this.node.js( 'label-oekologie' ).html = this.renderData().oekologie.toString() + '%'\n this.node.js( 'label-oekonomie' ).html = this.renderData().oekonomie.toString() + '%'\n this.node.js( 'label-soziales' ).html = this.renderData().soziales.toString() + '%'\n this.node.js( 'thema-title' ).html = this.renderData().thema.title\n\n // modify the circles in the svg\n this.node.js( 'oekologie').setAttribute( 'rx', (29 + ( 41 * (this.renderData().oekologie / 100 ))).toString() )\n this.node.js( 'oekologie').setAttribute( 'ry', (29 + ( 41 * (this.renderData().oekologie / 100 ))).toString() )\n\n this.node.js( 'oekonomie').setAttribute( 'rx', (29 + ( 41 * (this.renderData().oekonomie / 100 ))).toString() )\n this.node.js( 'oekonomie').setAttribute( 'ry', (29 + ( 41 * (this.renderData().oekonomie / 100 ))).toString() )\n\n this.node.js( 'soziales').setAttribute( 'rx', (29 + ( 41 * (this.renderData().soziales / 100 ))).toString() )\n this.node.js( 'soziales').setAttribute( 'ry', (29 + ( 41 * (this.renderData().soziales / 100 ))).toString() )\n\n if( setup.data( 'savestate' ).code ) {\n this.node.js( 'save-link-panel').style.display = 'none'; \n this.node.js( 'copy-link-panel').style.display = 'block';\n }else {\n this.node.js( 'save-link-panel').style.display = 'block'; \n this.node.js( 'copy-link-panel').style.display = 'none';\n }\n\n this.node.js( 'loadstateurl' ).value = setup.route( 'loadstate' ).url() + '/' + setup.data( 'savestate' ).code;\n }\n\n hasTask( task:any ) {\n return ( this.tasks.indexOf( task.data.data.id ) > -1 )\n }\n\n saveTaskStates() {\n var currentLernpfad = setup.data( 'thema' ).lernpfade.filter( lernpfad => {\n return lernpfad.id == this.currentLernpfad.id;\n });\n\n currentLernpfad[0].chapters.forEach( chapter => {\n chapter.tasks.forEach( task => {\n if( task.checked ) {\n var data = {\n 'savedModelName': task.modelName,\n 'savedModelId': task.id,\n 'attribute': 'checked',\n 'value': true,\n };\n \n Request.post( setup.route( 'userData' ).url() )\n .headers( { 'Content-Type':'application/json' } )\n .send( JSON.stringify( data ));\n }\n });\n });\n }\n\n saveLernpfadState() {\n var data = {\n 'savedModelName': this.currentThema.modelName,\n 'savedModelId': this.currentThema.id,\n 'attribute': 'difficulty',\n 'value': this.currentThema.difficulty,\n };\n\n Request.post( setup.route( 'userData' ).url() )\n .headers( { 'Content-Type':'application/json' } )\n .send( JSON.stringify( data ));\n\n var data = {\n 'savedModelName': this.currentThema.modelName,\n 'savedModelId': this.currentThema.id,\n 'attribute': 'duration',\n 'value': this.currentThema.duration,\n };\n\n Request.post( setup.route( 'userData' ).url() )\n .headers( { 'Content-Type':'application/json' } )\n .send( JSON.stringify( data ));\n }\n }\n\nvar singleton = new SaveDialog();\n\nexport default singleton;\n\nscanner.ioc.add( 'savedialog', function( node:Node, data ) {\n return singleton.setData( data ).render( node );\n});","\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