{"version":3,"file":"heimatkunde.js","sources":["typescript/libs/ts-lib/src/core/signal/Signal.ts","typescript/libs/ts-lib/src/core/node/signal/DOMEventSignal.ts","typescript/libs/ts-lib/src/core/node/signal/NodeSignal.ts","typescript/libs/ts-lib/src/core/node/Node.ts","typescript/libs/ts-lib/src/core/lookup/LookupDict.ts","typescript/libs/ts-lib/src/core/js.ts","typescript/libs/ts-lib/src/core/lookup/LookupObject.ts","typescript/libs/ts-lib/src/core/template/TemplateRenderer.ts","typescript/libs/ts-lib/src/core/template/TemplateManager.ts","typescript/libs/ts-lib/src/core/list/List.ts","typescript/libs/ts-lib/src/core/list/ModifiableList.ts","typescript/libs/ts-lib/src/core/ioc/IoC.ts","typescript/libs/ts-lib/src/core/model/Model.ts","typescript/libs/ts-lib/src/core/list/ListRenderer.ts","typescript/src/ChapterFilter.ts","typescript/libs/ts-lib/src/core/view/View.ts","typescript/libs/ts-lib/src/core/model/Mapper.ts","typescript/libs/ts-lib/src/core/view/Scanner.ts","node_modules/es6-promise/dist/es6-promise.js","node_modules/es6-promise/auto.js","typescript/libs/ts-lib/src/core/request/Request.ts","typescript/libs/ts-lib/src/core/setup/Asset.ts","typescript/libs/ts-lib/src/core/setup/setup.ts","typescript/libs/ts-lib/src/core/node/Window.ts","typescript/libs/lernbuch/src/ScrollMonitor.ts","typescript/libs/lernbuch/src/elements/Chapter.ts","typescript/libs/lernbuch/src/LernBuch.ts","typescript/libs/ts-lib/src/core/linkedlist/LinkedList.ts","typescript/libs/lernbuch/src/elements/Element.ts","typescript/libs/lernbuch/src/elements/Gallery.ts","typescript/libs/lernbuch/src/navigation/chapter/tree/TreeView.ts","typescript/libs/lernbuch/src/navigation/chapter/ChapterElement.ts","typescript/libs/lernbuch/src/navigation/chapter/Chapter.ts","typescript/libs/ts-lib/src/core/tree/TreeNode.ts","typescript/libs/lernbuch/src/navigation/chapter/tree/TreeNode.ts","typescript/libs/lernbuch/src/navigation/chapter/tree/TreeBuilder.ts","typescript/libs/lernbuch/src/navigation/chapter/Navigation.ts","typescript/libs/lernbuch/src/navigation/outline/OutlineNavigation.ts","typescript/libs/lernbuch/src/navigation/paging/PagingNavigation.ts","typescript/libs/lernbuch/src/search/SearchResult.ts","typescript/libs/lernbuch/src/search/SearchPattern.ts","typescript/libs/lernbuch/src/search/AttributeSearch.ts","typescript/libs/lernbuch/src/search/ArrayModelSearch.ts","typescript/libs/lernbuch/src/search/ArraySearch.ts","typescript/libs/lernbuch/src/search/Search.ts","typescript/libs/lernbuch/src/search/SearchInput.ts","typescript/libs/lernbuch/src/search/SearchOutput.ts","typescript/libs/lernbuch/src/share/ShareDialog.ts","typescript/libs/lernbuch/src/models/BookModel.ts","typescript/libs/lernbuch/src/models/ElementModel.ts","typescript/libs/lernbuch/src/models/ChapterModel.ts","typescript/libs/lernbuch/src/models/GalleryImageModel.ts","typescript/libs/lernbuch/src/models/GalleryModel.ts","typescript/libs/lernbuch/src/elements/Section.ts","typescript/libs/lernbuch/src/elements/ToC.ts","typescript/libs/lernbuch/src/setup/defaultsetup.ts","typescript/src/ImageView.ts","typescript/src/Carousel.ts","typescript/src/Paragraph.ts","typescript/libs/lernfragen/src/slides/Chapter/ChapterModel.ts","typescript/libs/lernfragen/src/slides/Slide/SlideModel.ts","typescript/libs/lernfragen/src/slides/Question/QuestionModel.ts","typescript/libs/lernfragen/src/slides/AnswerList/AnswerListModel.ts","typescript/libs/lernfragen/src/slides/SingleChoice/SingleChoiceModel.ts","typescript/libs/lernfragen/src/slides/MultipleChoice/MultipleChoiceModel.ts","typescript/libs/lernfragen/src/slides/Cloze/ClozeModel.ts","typescript/libs/lernfragen/src/slides/DragDrop/DragDropModel.ts","typescript/libs/lernfragen/src/slides/FreeText/FreetextModel.ts","typescript/libs/lernfragen/src/slides/HotSpot/HotspotModel.ts","typescript/libs/lernfragen/src/slides/Reveal/RevealModel.ts","typescript/libs/lernfragen/src/slides/ToolTip/TooltipModel.ts","typescript/libs/lernfragen/src/aggregators/Aggregator.ts","typescript/libs/lernfragen/src/aggregators/CorrectWrong.ts","typescript/libs/lernfragen/src/slides/Summary/SummaryModel.ts","typescript/libs/lernfragen/src/slides/DropDown/DropDownModel.ts","typescript/libs/lernfragen/src/slides/Order/OrderModel.ts","typescript/libs/lernfragen/src/slides/DragDropCloze/DragDropClozeModel.ts","typescript/libs/lernfragen/src/slides/DragDropSentence/DragDropSentenceModel.ts","typescript/libs/lernfragen/src/slides/MatrixChoice/MatrixChoiceModel.ts","typescript/libs/lernfragen/src/slides/MatrixBinaryChoice/MatrixBinaryChoiceModel.ts","typescript/libs/lernfragen/src/setup/models.ts","typescript/libs/lernfragen/src/decorators/Decorator.ts","typescript/libs/lernfragen/src/sliderenderer/SlideRenderer.ts","typescript/libs/lernfragen/src/navigation/NextPrevNavigation.ts","typescript/libs/lernfragen/src/navigation/NextSolutionNavigation.ts","typescript/libs/lernfragen/src/navigation/PointsNavigation.ts","typescript/libs/lernfragen/src/navigation/AnsweredButton.ts","typescript/libs/lernfragen/src/navigation/ResponsiveNavigation.ts","typescript/libs/lernfragen/src/navigation/Status.ts","typescript/libs/lernfragen/src/setup/lernfragen.ts","typescript/libs/lernfragen/src/slides/Slide/Slide.ts","typescript/libs/lernfragen/src/slides/Question/Question.ts","typescript/libs/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/libs/ts-lib/src/ui/dragdrop/DragDrop.ts","typescript/libs/lernfragen/src/slides/DragDrop/DragDrop.ts","typescript/libs/lernfragen/src/slides/FreeText/Freetext.ts","typescript/libs/lernfragen/src/slides/HotSpot/Hotspot.ts","typescript/libs/lernfragen/src/slides/AnswerList/AnswerList.ts","typescript/libs/lernfragen/src/slides/MultipleChoice/MultipleChoice.ts","typescript/libs/lernfragen/src/slides/Reveal/Reveal.ts","typescript/libs/lernfragen/src/slides/SingleChoice/SingleChoice.ts","typescript/libs/lernfragen/src/slides/ToolTip/Tooltip.ts","typescript/libs/lernfragen/src/slides/DropDown/DropDown.ts","typescript/libs/lernfragen/src/slides/Order/Order.ts","typescript/libs/lernfragen/src/slides/DragDropCloze/DragDropCloze.ts","typescript/libs/lernfragen/src/slides/DragDropSentence/DragDropSentence.ts","typescript/libs/lernfragen/src/slides/MatrixChoice/MatrixChoice.ts","typescript/libs/lernfragen/src/slides/MatrixBinaryChoice/MatrixBinaryChoice.ts","typescript/libs/lernfragen/src/setup/slides.ts","typescript/libs/lernfragen/src/Lernfragen.ts","typescript/libs/lernfragen/src/decorators/EvaluateAnswers.ts","typescript/libs/lernfragen/src/decorators/ContainerFeedback.ts","typescript/libs/lernfragen/src/decorators/RelatedFeedback.ts","typescript/libs/lernfragen/src/decorators/ShowCorrect.ts","typescript/libs/lernfragen/src/decorators/ModelBasedDecorator.ts","typescript/src/FeedbackButtonDecorator.ts","typescript/src/Quiz.ts","typescript/src/elements/questionElementsIoC.ts","typescript/src/QuestionContainerView.ts","typescript/src/BookContent.ts","typescript/src/CommonPage.ts","typescript/src/templates/elements.ts","typescript/src/templates/lernbuch.ts","typescript/src/templates/slides.ts","typescript/src/templates/lernfragen.ts","typescript/src/Main.ts"],"sourcesContent":["\n\ninterface Slot {\n\tlistener:T;\n\tcontext:any;\n\tisOnce:boolean;\n}\n\n/**\n * A common interface for signal callback that return nothing\n */\nexport interface EmptyCallback {\n\t()\n}\n\n/**\n* A signal class that is inspired by https://github.com/robertpenner/as3-signals\n* This is an alternative to the classical Event System.\n* It wraps an event type into a property of a class and allows to register directly on the property\n*/\nexport class Signal {\n\t\n\tslots:Slot[] = [];\n\n\t/**\n\t\t* Registers a function to this signal.\n\t\t*/\n\tpublic add( listener:T, context:any = null ):void {\n\t\tthis.slots.push( { listener:listener, context:context, isOnce:false } );\n\t}\n\t\n\t/**\n\t\t* Registers a function to this signal only once\n\t\t*/\n\tpublic addOnce( listener:T, context:any = null ):void {\n\t\tthis.slots.push( { listener:listener, context:context, isOnce:true } );\n\t}\n\t\n\t/**\n\t\t* Removes a listener from the signal\n\t\t*/\n\tpublic remove( listener:T, context:any = null ) {\n\t\tthis.slots = this.slots.filter( function( slot ) {\n\t\t\treturn slot.listener != listener || slot.context != context;\n\t\t});\n\t}\n\n\t/**\n\t * Removes all listeners from signal\n\t */\n\tpublic removeAll() {\n\t\tthis.slots = [];\n\t}\n\t\n\t/**\n\t\t* Returns true is this signal has the given listener function added.\n\t\t*/\n\tpublic has( listener:T, context:any = null ) {\n\t\treturn this.slots.some( function( slot ) {\n\t\t\treturn slot.listener == listener && slot.context == context;\n\t\t});\n\t}\n\t\n\t\n\t/**\n\t\t* Dispatches an event with the given arguments\n\t\t*/\n\tpublic dispatch( ...args:any[] ):void {\n\t\t\n\t\tthis.slots.forEach( ( slot ) => {\n\t\t\tvar func:any = slot.listener;\n\t\t\tfunc.apply( slot.context, args );\n\t\t\tif( slot.isOnce ) this.remove( slot.listener, slot.context );\n\t\t});\n\t}\n}\n\n\n\nexport default Signal;","import Signal from '../../signal/Signal';\n\n/**\n\t* This class registers itself on native DOM events.\n\t*/\nclass DOMEventSignal extends Signal {\n\t\n\tprotected target:EventTarget;\n\tprotected event:string;\n\tprotected callback:( event ) => void = null;\n\t\n\tconstructor( target:EventTarget, event:string ) {\n\t\t\n\t\tsuper();\t\t\t\n\t\tthis.target = target;\n\t\tthis.event = event;\n\t}\n\t\n\t/**\n\t\t* Registers a function to this signal.\n\t\t*/\n\tpublic add( listener:T, context:any = null ):void {\n\t\t\n\t\tif( this.callback === null ) this.register();\n\t\tsuper.add( listener, context );\n\t}\n\t\n\t/**\n\t\t* Registers a function to this signal only once\n\t\t*/\n\tpublic addOnce( listener:T, context:any = null ):void {\n\t\tif( this.callback === null ) this.register();\n\t\tsuper.addOnce( listener, context );\n\t}\n\t\n\t/**\n\t\t* Removes a listener from the signal\n\t\t*/\n\tpublic remove( listener:T, context:any = null ) {\n\t\tsuper.remove( listener, context );\n\t\tif( this.slots.length == 0 ) this.unregister();\n\t}\n\t\n\t/**\n\t\t* Registers the event on the node and setups the callback function.\n\t\t*/\n\tpublic register() {\n\t\t\n\t\tthis.callback = ( event:Event ) => {\n\t\t\tthis.dispatchEvent( event );\n\t\t};\n\t\t\n\t\tthis.target.addEventListener( this.event, this.callback );\n\t}\n\t\n\t/**\n\t\t* Unregisters the event from the html element\n\t\t*/\n\tpublic unregister() {\n\t\tthis.target.removeEventListener( this.event, this.callback );\n\t\tthis.callback = null;\n\t}\n\t\n\t/**\n\t * \n\t */\n\tprotected dispatchEvent( event:Event ) {\n\t\tthis.dispatch( event );\n\t}\n}\n\nexport default DOMEventSignal;","import DOMEventSignal from './DOMEventSignal';\nimport Node from '../Node';\n\n/**\n * A common interface for all node signals\n */\nexport interface NodeEventCallback {\n\t( node: Node, event:Event )\n}\n\n/**\n * This class registers itself on native DOM events.\n */\nclass NodeSignal extends DOMEventSignal {\n\t\n\tconstructor( public node:Node, event:string ){\n\t\tsuper( node.native, event );\n\t}\n\t\n\tprotected dispatchEvent( event:Event ) {\n\t\tthis.dispatch( this.node, event );\n\t}\n}\n\nexport default NodeSignal;","import NodeSignal from './signal/NodeSignal';\n\n/**\n* #Node\n* \n* Wrapper class for the native HTMLElement node.\n* The class gives generalised access to HTMLElements\n* \n* The node class manages a container from type\n* [HTMLElement](https://developer.mozilla.org/de/docs/Web/API/HTMLElement)\n* and adaptes most common functionality.\n* \n*/\n\nexport class Node {\n\n\t// Properties\n\n\t/**\n\t * The native HTMLElement\n\t */\n\tnative: HTMLElement;\n\n\n\t/**\n\t * All signals\n\t */\n\tscroll: NodeSignal;\n\tmouseleave: NodeSignal;\n\tmouseenter: NodeSignal;\n\tmouseout: NodeSignal;\n\tmouseover: NodeSignal;\n\tmouseup: NodeSignal;\n\tmousedown: NodeSignal;\n\tmousemove: NodeSignal;\n\tclick: NodeSignal;\n\tkeypress: NodeSignal;\n\tkeydown: NodeSignal;\n\tkeyup: NodeSignal;\n\tfocus: NodeSignal;\n\tblur: NodeSignal;\n\tchange: NodeSignal;\n\n\t/**\n\t * Constructs a new node\n\t * \n\t * Dont use the constructor to get/create nodes. Use the Factory methods: Node.fromHTML, Node.fromTag\n\t * \n\t * @param HTMLElement native HTMLElement to wrapp\n\t */\n\tconstructor( native: HTMLElement ) {\n\n\t\tthis.native = native;\n\t\tthis.native[\"_lnNode\"] = this; // inject node instance for later retrieval.\n\n\t\tthis.setupSignals();\n\t}\n\n\t/**\n\t * Static create a node from a HTML string\n\t * or `null` if the string is not valid html\n\t * \n\t * Wrapps the given html string into a new node. If this\n\t * new node has more than on children, return the new node,\n\t * otherwise return the first child.\n\t * \n\t * __Example__\n\t * `var n = Node.fromHTML( '
Demo
' );`\n\t * \n\t * @param html The source for the node\n\t */\n\tstatic fromHTML( html: string ): Node {\n\n\t\t// Wrap the given html string into a new node\n\t\tvar tempDiv = Node.fromTag( 'div' );\n\t\ttempDiv.html = html;\n\n\t\tvar children = tempDiv.children();\n\n\t\t// If there is exactly one child the template has one root node - return this one.\n\t\t// otherwise if there are many or no children return the temp div as the root.\n\t\treturn ( children.length == 1 ) ? children[0] : tempDiv;\n\t}\n\n\t/**\n\t * Static create a new node from a HTML tag\n\t * or `null` if the given tag is invalid\n\t * \n\t * __Example__\n\t * `var n = ln.Node.fromTag( 'div' );`\n\t * \n\t * @param tag\n\t */\n\tstatic fromTag( tag: string ): Node {\n\t\treturn new Node( document.createElement( tag ) );\n\t}\n\n\t/**\n\t * Static function that returns the ln.Node from a native HTMLElement\n\t */\n\tstatic fromNative( native: HTMLElement ): Node {\n\t\tif( native == null ) return null;\n\t\treturn ( native[\"_lnNode\"] ) ? native[\"_lnNode\"] : new Node( native );\n\t}\n\n\t/**\n\t * Gets the value if an HTMLInputElement like an inputfields\n\t */\n\tget value() {\n\t\treturn ( this.native as HTMLInputElement ).value;\n\t}\n\n\t/**\n\t * Sets the value of an HTMLInputElement like an inputfield\n\t */\n\tset value( value: string ) {\n\t\t( this.native as HTMLInputElement ).value = value;\n\t}\n\n\t/**\n\t * Sets the innerHTML with the given html string.\n\t * @param html The HTML-String\n\t */\n\tset html( html: string ) {\n\t\tthis.native.innerHTML = html;\n\t}\n\n\t/**\n\t * Gets the inner html content of the node.\n\t */\n\tget html(): string {\n\t\treturn this.native.innerHTML;\n\t}\n\n\t/**\n\t * Returns the style to directly adjust it.\n\t */\n\tget style(): CSSStyleDeclaration {\n\t\treturn this.native.style;\n\t}\n\n\t/**\n\t * Returns true/false on a checkbox input if its checked or not\n\t */\n\tget checked(): boolean {\n\t\treturn (this.native as HTMLInputElement ).checked;\n\t}\n\tset checked( value:boolean ) {\n\t\t(this.native as HTMLInputElement ).checked = value;\n\t}\n\n\tget data(): any {\n\n\t\t// fallback for older browsers, create dataset object manually\n\t\tif ( this.native.dataset === undefined ) {\n\t\t\tthis.native.dataset = {};\n\t\t\tvar attrs = this.native.attributes;\n\t\t\tfor ( var i = 0; i < attrs.length; i++ ) {\n\t\t\t\tvar attr = attrs[i]\n\t\t\t\tif ( attr.name.substr( 0, 5 ) == \"data-\" ) this.native.dataset[attr.name.substr( 5 )] = attr.value;\n\t\t\t}\n\t\t}\n\n\t\treturn this.native.dataset\n\t}\n\n\t/**\n\t * Returns the full html content of the node.\n\t */\n\ttoString(): string {\n\t\treturn this.native.outerHTML;\n\t}\n\n\n\t/**\n\t * Add new class(es) to the node\n\t * \n\t * Classes are always added distinct\n\t * \n\t * __Example__\n\t * `node.addClass( \"class1\", \"class2\", ... )`\n\t * \n\t * @param classname\n\t * @param classlist Typescript restparameter: A list of optional strings\n\t */\n\taddClass( classname: string, ...classlist: string[] ): void {\n\t\tthis.setClasses( this.getClasses().concat( classlist.concat( classname ) ) );\n\t}\n\n\t/**\n\t * Remove classes from the node\n\t * \n\t * __Example__\n\t * see addClass()\n\t * \n\t * @param classname\n\t * @param classlist typescript restparameter: A list of optional strings\n\t */\n\tremoveClass( classname: string, ...classlist: string[] ): void {\n\n\t\tclasslist.push( classname );\n\n\t\t// return only the ones that are not in the classlist.\n\t\tvar filtered = this.getClasses().filter( function ( value ) {\n\t\t\treturn classlist.indexOf( value ) == -1;\n\t\t});\n\n\t\tthis.setClasses( filtered );\n\t}\n\n\t/**\n\t * Toggle a class from this node\n\t * \n\t * __Example__\n\t * `n.toggleClass( 'class2', true );`\n\t * Would result in `class2` still be set.\n\t * \n\t * @param classname\n\t * @param force When force is set to true, the class is set in any case. \n\t * When force is set to false, the class is removed in any case.\n\t */\n\ttoggleClass( classname: string, force?: boolean ): void {\n\n\t\tif ( force == undefined ) {\n\t\t\t( this.hasClass( classname ) ) ? this.removeClass( classname ) : this.addClass( classname );\n\t\t} else {\n\t\t\t( force ) ? this.addClass( classname ) : this.removeClass( classname );\n\t\t}\n\t}\n\n\t/**\n\t * Get the (distinct) classnames from this node as an array\n\t * \n\t * @return The classes are always distinct\n\t */\n\tprivate getClasses(): string[] {\n\n\t\tvar className:any = this.native.className;\n\t\t\n\t\t// fallback for svg elements\n\t\tif( className instanceof SVGAnimatedString ) className = className.baseVal;\n\n\t\treturn className === \"\" ? [] : className.split( ' ' );\n\t}\n\n\t/**\n\t * Set (distinct) classes to this node\n\t * \n\t * @param classnames\n\t */\n\tprivate setClasses( classnames: string[] ): void {\n\n\t\tvar distinct = classnames.filter( function ( value, index, self ) {\n\t\t\treturn self.indexOf( value ) === index;\n\t\t});\n\n\t\tthis.native.className = distinct.join( ' ' );\n\t}\n\n\t/**\n\t * Check if the classname exists in the classlist of this node\n\t * \n\t * @param classname Classname to be checked\n\t * @return `true` if the class exists, `false` else\n\t */\n\thasClass( classname: string ): boolean {\n\t\treturn this.getClasses().indexOf( classname ) > -1;\n\t}\n\n\t/**\n\t * Sets an attribute to this node\n\t * \n\t * @param name Name of the attribute\n\t * @param value Value of the attribute\n\t */\n\tsetAttribute( name: string, value: string ): void {\n\t\tthis.native.setAttribute( name, value );\n\t}\n\n\t/**\n\t * Gets the value of an attribute of this node\n\t * or `null` if the specified attribute does not exist\n\t * \n\t * @param name Name of the attribute\n\t */\n\tgetAttribute( name: string ): string {\n\t\treturn this.native.getAttribute( name );\n\t}\n\n\t/**\n\t * Append a child node to this node\n\t * \n\t * The node is inserted as last child of this node\n\t * \n\t * @param n Node to append\n\t */\n\tappend( n: Node | DocumentFragment | Node[] ): void {\n\n\t\t// handle array\n\t\tif ( Array.isArray( n ) ) {\n\n\t\t\tn.forEach( node => {\n\t\t\t\tthis.native.appendChild( node.native );\n\t\t\t});\n\n\t\t\treturn;\n\t\t}\n\n\t\t// handle node/fragment\n\t\tthis.native.appendChild(( n instanceof Node ) ? n.native : n );\n\t}\n\n\t/**\n\t * Prepend a child node to this element\n\t * \n\t * The node is inserted as first child of this node\n\t * \n\t * @param n Node to prepend \n\t */\n\tprepend( n: Node | DocumentFragment | Node[] ): void {\n\n\t\tvar firstChild = this.native.firstChild;\n\n\t\t// handle array\n\t\tif ( Array.isArray( n ) ) {\n\n\t\t\tn.forEach( node => {\n\t\t\t\tthis.native.insertBefore( node.native, firstChild );\n\t\t\t});\n\n\t\t\treturn;\n\t\t}\n\n\t\t// handle node/fragment\n\t\tthis.native.insertBefore(( n instanceof Node ) ? n.native : n, firstChild );\n\t}\n\n\t/** \n\t * Inserts a child node before a given node\n\t * \n\t * @param newNode Node to be inserted\n\t * @param index Position at which the node will be inserted.\n\t */\n\tinsert( n: Node | DocumentFragment, index: number = undefined ): void {\n\t\tthis.native.insertBefore(( n instanceof Node ) ? n.native : n, this.native.childNodes[index] || null );\n\t}\n\n\t/**\n\t * Replaces this node in its parent with the given new node\n\t */\n\treplace( n: Node ): void {\n\t\tthis.parent().native.replaceChild( n.native, this.native );\n\t}\n\n\t/**\n\t * Get all the child nodes from this element\n\t * which are HTMLElements ( no comments, no text, no HTMLDocuments )\n\t * and return them as a array of nodes.\n\t * \n\t * @return Array of child nodes\n\t * or an empty array if the node has no child nodes\n\t */\n\tchildren(): Node[] {\n\t\treturn this.toNodes( this.native.children );\n\t}\n\n\t/**\n\t * Checks if this node has child nodes ( child elements )\n\t * \n\t * @return `true` if this node has child nodes, `false` else\n\t */\n\thasChildren(): boolean {\n\t\treturn this.native.children.length !== 0;\n\t}\n\n\t/**\n\t * Checks if the node has the given node as child node\n\t * \n\t * @return `true` if this node has the given node as child, `false` else\n\t */\n\thasChild( n: Node ): boolean {\n\t\treturn this.children().some( function ( node ) {\n\t\t\treturn node === n;\n\t\t});\n\t}\n\n\t/**\n\t * Removes specified child node and returns the node\n\t * \n\t * @param child Child node to remove from this node\n\t * \n\t * Throws an exception if child is actually is not a child\n\t * of this node\n\t */\n\tremoveChild( child: Node ) {\n\t\tthis.native.removeChild( child.native );\n\t}\n\n\t/**\n\t * removes this node from the parent\n\t */\n\tremove() {\n\t\tif ( this.native.parentElement ) {\n\t\t\tthis.native.parentElement.removeChild( this.native );\n\t\t}\n\t}\n\n\t/**\n\t * Removes all children\n\t */\n\tempty() {\n\t\tfor ( var i = this.native.children.length; i--; ) {\n\t\t\tthis.native.removeChild( this.native.children[i] );\n\t\t}\n\t}\n\n\t/**\n\t * Returns the parent HTMLElement from this node\n\t * \n\t * @return The parent node of this node\n\t * or null if this node has no parent\n\t */\n\tparent(): Node {\n\n\t\tvar p = this.native.parentElement;\n\t\tif ( p === null ) return null;\n\n\t\treturn Node.fromNative( p );\n\t}\n\n\t/**\n\t * Returns the bounding box of this node including values for top, bottom, left, right\n\t * @param relative A flag that specifies if the bounding box coordinates should be relative to the viewport.\n\t */\n\tbounds( relative: boolean = false ): { top: number; left: number; right: number; bottom: number; height: number; width: number  } {\n\n\t\tvar c = this.native.getBoundingClientRect();\n\t\tvar r = {  top: c.top, left: c.left, right: c.right, bottom: c.bottom, height: c.height, width: c.width };\n\n\t\tif ( !relative ) {\n\t\t\tr.top += document.body.scrollTop || document.documentElement.scrollTop;\n\t\t\tr.bottom += document.body.scrollTop || document.documentElement.scrollTop;\n\t\t\tr.left += document.body.scrollLeft || document.documentElement.scrollLeft;\n\t\t\tr.right += document.body.scrollLeft || document.documentElement.scrollLeft;\n\t\t}\n\n\t\tif ( r.height == undefined ) r.height = r.bottom - r.top;\n\t\tif ( r.width == undefined ) r.width = r.right - r.left;\n\n\t\treturn r;\n\t}\n\n\t/**\n\t * Returns the computed css style.\n\t */\n\tcss( property ) {\n\t\treturn ( this.native.currentStyle ) ? this.native.currentStyle[ property ] : document.defaultView.getComputedStyle( this.native, null ).getPropertyValue( property );\n\t}\n\n\t/**\n\t * Queries this node for the first child node by the specified selector\n\t * and returns it without removing it\n\t * \n\t * @param selector A CSS selector\n\t * @return The first node with the specified selector or `null` if a matching node was not found\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tone( selector: string ): Node {\n\n\t\tvar htmlElement = this.native.querySelector( selector );\n\n\t\tif ( htmlElement !== null ) {\n\t\t\treturn Node.fromNative( htmlElement );\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Queries this node for all the child nodes by the specified selector\n\t * and returns them as an array\n\t * \n\t * @param selector A CSS selector\n\t * @return Array of nodes (An empty array if there are no elements with\n\t * the specified selector )\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tall( selector: string ): Node[] {\n\t\treturn this.toNodes( this.native.querySelectorAll( selector ) );\n\t}\n\n\t/**\n\t * Helper function for getting nodes with a js attribute from within the node.\n\t * Gets only the first node\n\t * \n\t * @param key A key used to build the CSS selector [js=key] \n\t * @return The first node or `null` if no matching node was found\n\t */\n\tjs( key: string ): Node {\n\t\treturn this.one( '[js=' + key + ']' );\n\t}\n\n\t/**\n\t * Queries the document for the first node by the specified selector\n\t * and returns it\n\t * \n\t * @param selector A CSS selector\n\t * @return The first node with the specified selector or `null` if a matching node was not found\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tstatic one( selector: string ): Node {\n\t\tvar tempNode = new Node( document.body );\n\t\treturn tempNode.one( selector );\n\t}\n\n\t/**\n\t * Queries the document for all the nodes by the specified selector\n\t * and returns them as an array\n\t * \n\t * @param selector A CSS selector\n\t * @return Array of nodes or an emtpy array if there are no elements\n\t * with the specified selector\n\t * \n\t * Throws a SYNTAX_ERR exception if the specified selector is invalid.\n\t */\n\tstatic all( selector: string ): Node[] {\n\t\tvar tempNode = new Node( document.body );\n\t\treturn tempNode.all( selector );\n\t}\n\n\t/**\n\t * Helper function for getting nodes with a js attribute from the document.\n\t * Gets only the first occurence.\n\t * \n\t * @param key A key used to build the CSS selector [js-node=key] \n\t * @return The first node or `null` if no matching node was found\n\t */\n\tstatic js( key: string ): Node {\n\t\tvar tempNode = new Node( document.body );\n\t\treturn tempNode.js( key );\n\t}\n\n\tstatic get body():Node {\n\t\treturn Node.fromNative( document.body );\n\t}\n\n\t/**\n\t * Helper function that setups all signals properly\n\t */\n\tprivate setupSignals() {\n\t\tvar events = ['scroll',\n\t\t\t'mouseleave',\n\t\t\t'mouseenter',\n\t\t\t'mouseout',\n\t\t\t'mouseover',\n\t\t\t'mouseup',\n\t\t\t'mousedown',\n\t\t\t'mousemove',\n\t\t\t'click',\n\t\t\t'keypress',\n\t\t\t'keydown',\n\t\t\t'keyup',\n\t\t\t'focus',\n\t\t\t'blur',\n\t\t\t'change'];\n\n\t\tfor ( var i = 0; i < events.length; i++ ) {\n\t\t\tthis[events[i]] = new NodeSignal( this, events[i] );\n\t\t}\n\t}\n\n\t/**\n\t * Turns a string into a document fragment\n\t */\n\tpublic static fragment( html: string ): DocumentFragment {\n\n\t\t// browser check if its available\n\t\ttry {\n\t\t\treturn document.createRange().createContextualFragment( html );\n\t\t} catch ( e ) {\n\n\t\t\t// for older browsers make temp document and loop over nodes\n\t\t\tvar frag = document.createDocumentFragment(),\n\t\t\t\tbody = document.createElement( 'body' ), c;\n\t\t\tbody.innerHTML = html;\n\n\t\t\twhile ( c = body.firstElementChild ) frag.appendChild( c );\n\n\t\t\treturn frag;\n\t\t}\n\t}\n\n\t/**\n\t * Helper to turn a HTML Collection or any list into an array of nodes\n\t */\n\tprivate toNodes( collection: any ): Node[] {\n\n\t\tvar temp = [];\n\n\t\tfor ( var i = 0; i < collection.length; i++ ) {\n\t\t\tif ( collection[i] instanceof Element ) {\n\t\t\t\ttemp.push( Node.fromNative( collection[i] ) );\n\t\t\t}\n\t\t}\n\n\t\treturn temp;\n\t}\n}\n\nexport default Node;\n\n\n\n","\n/**\n * An interface for any source for the LookupDict\n */\nexport interface LookupSource {\n\tlookup( key:string ):any;\n}\n\n\n/**\n\t* This class provides a key:value lookup mechanism where multiple sources can be added.\n\t*/\nclass LookupDict {\n\t\n\tprivate sources:LookupSource[] = [];\n\t\n\t/**\n\t\t* Adds a new source to the dictionary to search for keys\n\t\t*/\n\tadd( source:LookupSource ) {\n\t\tthis.sources.push( source );\n\t}\n\t\n\t\n\t/**\n\t\t* Returns the value stored behind the given key in the first of all sources\n\t\t*/\n\tget( key:string, fallback?:string ):any {\n\t\t\n\t\tvar result, i, s = this.sources;\n\n\t\t// loop over sources to find key.\n\t\tfor( i = 0; i < s.length; i++ ) {\n\t\t\tresult = s[ i ].lookup( key );\n\t\t\tif( result != null ) return result;\n\t\t}\n\t\t\n\t\t// if fallback is provided show that\n\t\tif( fallback !== undefined ) return fallback;\n\t\t\n\t\t// not found\n\t\treturn '!{' + key + '}'; \n\t}\n}\n\n\n\nexport default LookupDict;","/**\n * A collection of js specific helper functions.\n */\n\ntype baseTypes = \"Array\" | \"Object\" | \"String\" | \"Date\" | \"RegExp\" | \"Function\" | \"Boolean\" | \"Number\" | \"Null\" | \"Undefined\";\n\n/**\n * Helper function that checks if the given input is any of the given types \n */\nexport function isType( input:any, checkOn:baseTypes | Array ):boolean {\n\n\t// catch single string input\n\tvar types:Array = ( !Array.isArray( checkOn ) ) ? [ checkOn ] : checkOn;\n\n\t// test on object.\n\tif( types.indexOf( \"Object\" ) >= 0 ) {\n\t\tvar match = input && input.constructor == Object;\n\t\tif( match ) return match;\n\n\t\ttypes.splice( types.indexOf( \"Object\" ), 1 );\n\t}\n\n\t// test anything else\n\tvar inputType = Object.prototype.toString.call( input ).slice(8, -1);\n\treturn types.indexOf( inputType ) >= 0;\n}\n\n\n/**\n * This function applies multiple mixins to the given extendedClass.\n */\nexport function mixin( extendedClass: any, mixins: any[] ) {\n mixins.forEach( baseCtor => {\n Object.getOwnPropertyNames( baseCtor.prototype ).forEach( name => {\n extendedClass.prototype[name] = baseCtor.prototype[name];\n });\n }); \n}\n\n/**\n * Merges multiple objects into one. No deep clone.\n * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\n */\nexport function assign( target:{} = {}, ...args:Object[] ) {\n\n\tvar copy = Object( target );\n\n\targs.forEach( ( source ) => {\n\t\tif( source ) {\n\t\t\tfor( var nextKey in source ) {\n\t\t\t\tif( source.hasOwnProperty( nextKey ) ) copy[ nextKey ] = source[ nextKey ];\n\t\t\t}\n\t\t}\n\t});\n\n\treturn copy;\n}\n\n\n/**\n * Adapted from: http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array\n */\nexport function shuffle(array) {\n var i = array.length, t, randomIndex;\n // While there remain elements to shuffle...\n while (0 !== i) {\n // Pick a remaining element...\n randomIndex = Math.floor(Math.random() * i);\n i -= 1;\n // And swap it with the current element.\n t = array[i];\n array[i] = array[randomIndex];\n array[randomIndex] = t;\n }\n return array;\n}","import { LookupSource } from './LookupDict';\nimport { isType } from '../js';\n\n/**\n\t* A lookup dictionary source that searches on an object for given keys.\n\t* It allows to search in nested objects too.\n\t*/\nclass LookupObject implements LookupSource {\n\t\n\tprivate obj:Object;\n\n\tconstructor( obj:Object ) {\n\t\tthis.obj = ( isType( obj, 'Object' ) ) ? obj : {};\n\t}\n\t\n\t/**\n\t\t* lookups in the object for the given key.\n\t\t*/\n\tlookup( key:string ):any {\n\t\tvar keys = key.split( '.' );\n\t\treturn this.search( this.obj, keys );\n\t}\n\t\n\t/**\n\t\t* Adds a prefix to the search keys on this object\n\t\t*/\n\tprefix( name:string ):void {\n\t\tvar temp = {};\n\t\ttemp[ name ] = this.obj;\n\t\tthis.obj = temp;\n\t}\n\t\n\t/**\n\t\t* Searches in the given object for the first key in the keys array.\n\t\t* If there are sub keys it digs deeper into object hierarchy.\n\t\t*/\n\tprivate search = function( current:Object, keys:string[] ) {\n\t\t\n\t\tvar top = keys.shift();\n\t\t\n\t\t// there are sub keys to search\n\t\tif( keys.length > 0 ) {\n\t\t\treturn ( current[ top ] != undefined ) ? this.search( current[ top ], keys ) : undefined;\n\t\t} else {\n\t\t\treturn current[ top ];\n\t\t}\n\t};\n}\n\nexport default LookupObject;","\n/**\n* An interface of the complied template function containing also its source\n*/\ninterface CompliedTemplate {\n ( data:any ):string;\n source:string;\n}\n\ninterface TemplatePass {\n regex:RegExp;\n mapper:( match:string, inner:string ) => string;\n}\n\n\n/**\n* This class adapts the template implementation of the underscore library:\n* https://github.com/jashkenas/underscore\n*/\nclass TemplateRenderer {\n \n public passes:TemplatePass[] = [];\n public context:any = { };\n \n \n constructor() {\n \n // inject escape function\n this.context.esc = this.escape;\n this.context.empty = this.empty;\n this.context.url = encodeURIComponent;\n\n this.passes = this.defaultPasses();\n }\n \n /**\n * Renders the given data into the given template string\n */\n render( template:string, data:any = {} ):string {\n return this.compile( template )( data );\n }\n \n /**\n * Compiles a template string into a template function.\n */\n compile( templateString:string ):CompliedTemplate {\n \n var source:string = this.parse( templateString );\n var render = new Function( 'data', source );\n var contx = this.context;\n \n // wrap function to adjust context\n var template = function( data ):string {\n return render.call( contx, data );\n }\n \n template.source = source;\n return template;\n }\n \n /**\n * Turns the given template into a function body code.\n */\n parse( template:string ):string {\n \n template = this.sanitize( template );\n \n // loop over all passes\n this.passes.forEach( function( pass ) {\n template = template.replace( pass.regex, pass.mapper );\n });\n \n // finalize full template instructions\n template = \"__t='\" + template + \"';\\n\";\n \n // adjust scope with 'with'\n template = 'with( data ) {\\n' + template + '}\\n';\n \n // add opt variable for optional access\n return 'var __t;\\nvar opt = data;\\nvar __c = this;' + template + 'return __t;'\n }\n \n /**\n * Escapes the given string with html entities\n */\n escape( text:string ):string {\n var reg:RegExp = /[&<>\"'\\/`]/g;\n var lookup = { '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''', '/': '/', '`': '`' };\n text = text + ''; // make string.\n return text.replace( reg, function( match:string ) {\n return lookup[ match ];\n });\n }\n \n /**\n * Tests if the value of the template is empty or not\n */\n empty( data:any ):string {\n return data || data === 0 ? data : '';\n }\n \n /**\n * Sanitizes the given string to avoid escaping of the source\n */\n sanitize( text:string ):string {\n var reg:RegExp = /'|\\\\|\\r|\\n|\\u2028|\\u2029/g;\n var lookup = { \"'\": \"\\\\'\", '\\\\': '\\\\\\\\', '\\r': '\\\\r', '\\n': '\\\\n', '\\u2028': '\\\\u2028', '\\u2029': '\\\\u2029' };\n return text.replace( reg, function( match:string ) {\n return lookup[ match ];\n });\n }\n \n /**\n * Reverts the sanitizes escapes\n */\n unsanitize( text:string ):string {\n var reg:RegExp = /\\\\\\\\|\\\\'|\\\\r|\\\\n|\\\\u2028|\\\\u2029/g;\n var lookup = { '\\\\\\\\': '\\\\', \"\\\\'\": \"'\", '\\\\r': '\\r', '\\\\n': '\\n', '\\\\u2028': '\\u2028', '\\\\u2029': '\\u2029' };\n return text.replace( reg, function( match:string ) {\n return lookup[ match ];\n });\n }\n \n /**\n * Returns the array of default template render passes\n * Most specific match has to be the first.\n */\n defaultPasses():TemplatePass[] {\n \n var revert = this.unsanitize;\n \n return [\n {\n regex:/\\[\\[=([\\s\\S]+?)\\]\\]/g,\n mapper: function( match, inner ) {\n return \"' + __c.empty( \" + revert( inner ) + \" ) + '\";\n }\n },\n {\n regex:/\\[\\[([\\s\\S]+?)\\]\\]/g,\n mapper: function( match, inner ) {\n return \"' + __c.esc( __c.empty( \" + revert( inner ) + \" ) ) + '\";\n }\n },\n {\n regex:/\\[%([\\s\\S]+?)%\\]/g,\n mapper: function( match, inner ) {\n return \"';\\n\" + revert( inner ) + \"\\n__t+='\";\n }\n }\n ]\n }\n}\n\nexport default TemplateRenderer;","import LookupDict from '../lookup/LookupDict';\nimport LookupObject from '../lookup/LookupObject';\nimport TemplateRenderer from './TemplateRenderer';\nimport Lang from '../lang/Lang';\nimport Node from '../node/Node';\n\n/**\n\t* This class manages template from different sources.\n\t* It will look up for templates in the stored sources and renders them with a TemplateRenderer.\n\t* For performance the manager keeps cached versions of looked up templates.\n\t*/\nclass TemplateManager {\n\t\n\tprivate cache:any = {};\n\tprivate lookup:LookupDict;\n\tpublic renderer:TemplateRenderer;\t\n\n\tconstructor() {\n\t\tthis.renderer = new TemplateRenderer();\n\t\tthis.lookup = new LookupDict();\n\t}\n\t\n\t/**\n\t* Renders the template with the given key with the given data into a string. \n\t*/\n\tpublic render( keyOrTemplate:string, data:any = {} ):string {\n\t\t\n\t\tif( this.cache[ keyOrTemplate ] == undefined ) {\n\t\t\tthis.cache[ keyOrTemplate ] = this.renderer.compile( this.lookup.get( keyOrTemplate, keyOrTemplate ) );\n\t\t}\n\t\t\n\t\treturn this.cache[ keyOrTemplate ]( data );\n\t}\n\n\n\t/**\n\t * Renders the template into a document fragment\n\t */\n\tpublic document( key:string, data:any = {} ):DocumentFragment {\n\t\treturn Node.fragment( this.render( key, data ) );\n\t}\n\t\n\tpublic get context():any {\n\t\treturn this.renderer.context;\n\t}\n\n\t/**\n\t * Adds a template lookup object\n\t * @param obj \n\t */\n\tpublic add( obj:Object ) {\n\t\tthis.lookup.add( new LookupObject( obj ) );\n\t}\n}\n\n// create singleton to export.\nvar singleton = new TemplateManager();\n\n/**\n * Add import function to TemplateRenderer context\n */\nsingleton.renderer.context[\"import\"] = function( key:string, data?:any ) {\n\treturn singleton.render( key, data );\n}\n\n\nexport default singleton;","import 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, asc:boolean = true ) {\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\tif( asc ) return a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0;\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 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","/**\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 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\tenumerable: true,\n\t\t\t\tconfigurable: true\n\t\t\t});\n\t\t}\n\n\t\tfor( var attr in this._obj ){\n\t\t\tif( !( attr in this ) )\tcreateProperty( attr );\n\t\t}\n\t}\n}\n\nexport default Model;","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 for simpler usage with a render function.\r\n * \r\n * @example\r\n * ```typescript\r\n * \r\n * // Example 1\r\n * // Render a list of strings\r\n * \r\n * // Instantiate the ListRenderer and provide a node the listRenedrer is rendered to\r\n * var listRenderer = new ListRenderer( Node.js( 'targetNode' ) );\r\n * \r\n * // Define the closure a list item is rendererd with\r\n * listRenderer.defaultRender( function( item:string ) {\r\n * \tvar myView = new MyView( item ).render();\r\n * \treturn { node: myView.node }\r\n * });\r\n * \r\n * // Fill in the source with items. This invokes the render function of the listRenderer\r\n * listRenderer.source.fill( [ 'one', 'two', 'three' ] );\r\n * \r\n * // Append a new element. The list is rerenderer after all list manipulations\r\n * listRenderer.source.append( 'four' );\r\n * \r\n * \r\n * \r\n * // Example 2\r\n * // Render models \r\n * \r\n * // Instantiate a ListRenderer with type \"Model\"\r\n * var listRenderer = new ListRenderer( Node.js( 'targetNode' ) );\r\n * \r\n * // Define how a Title model is rendered\r\n * listRenderer.renderer.ioc.add( 'Title', function( model:TitleModel ) {\r\n * \tvar titleView = new TitleView( model ).render();\r\n * \treturn { node: titleView.node }\r\n * });\r\n *\r\n * // Define how a Paragraph model is rendered\r\n * listRenderer.renderer.ioc.add( 'Paragraph', function( model:ParagraphModel ) {\r\n * \tvar paragraphView = new ParagraphView( model ).render();\r\n * \treturn { node: paragraphView.node }\r\n * });\r\n * \r\n * // Define how to distinguish between the models\r\n * // This step is optional in this case. The selectorFunction is predefined\r\n * listRenderer.selectorFunction = function( model:Model ) {\r\n * \treturn model.get( 'modelName' );\r\n * }\r\n *\r\n * // Fill the source of the ListRenderer with models. \r\n * listRenderer.source.fill( [ new TitleModel(), new ParagraphModel(), new ParagraphModel() ] )\r\n *\r\n * \r\n * \r\n * // Example 3\r\n * // Use a ModifiableList instead of a List as source.\r\n * // A source must implement the ListRendererSource interface.\r\n * // Fallback source is a List\r\n * \r\n * // Instantiate a ListRenderer with type string, containig a source with type ModifiableList\r\n * var listRenderer = new ListRenderer>( Node.js( \"content\" ) );\r\n * \r\n * // Define the closure a list item is rendererd with\r\n * listRenderer.defaultRender( function( item:string ) {\r\n *\t\tvar node = Node.fromTag( 'div' );\r\n *\t\tnode.html = item;\r\n *\t\treturn { node: node };\r\n *\t});\r\n *\r\n * // Instantiate a ModifiableList as new source\r\n * listRenderer.source = new ModifiableList();\r\n *\r\n * // Fill in the source \r\n * listRenderer.source.fill( [ \"eins\", \"zwei\", \"drei\", \"vier\", \"fünf\", \"sechs\" ] );\r\n * \r\n * // Define a modifier for the ModifiableList\r\n * // Filter the list in order to display only items with less than 5 letters\r\n * // Appending a modifier applies the modifier and rerenders the list\r\n * listRenderer.source.modifiers.append( function( items ) {\r\n * \treturn items.filter( ( item ) => {\r\n * \t\treturn item.length < 5;\r\n * \t});\r\n * });\r\n * \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 Node from 'ln/node/Node'; \nimport LinkedList from'ln/linkedlist/LinkedList'; \nimport Template from'ln/template/TemplateManager';\nimport Window from 'ln/node/Window';\nimport ModifiableList from 'ln/list/ModifiableList';\nimport ListRenderer from 'ln/list/ListRenderer';\nimport ChapterModel from'lb/models/ChapterModel';\n//import BookModel from'lb/models/BookModel';\n\nclass ChapterFilter {\n\n filterClass:string;\n data:{ books: [ {} ]};\n chapters: ChapterModel[];\n modifiableChapterList:ModifiableList;\n chapterList:ListRenderer;\n filterButtons: Node[];\n\n constructor( data ) {\n this.data = data;\n this.chapters = this.getChapters( data );\n this.modifiableChapterList = new ModifiableList([]);\n this.filterClass = \"\";\n this.init();\n }\n \n getChapters( data ) {\n var chapters= [];\n data.books.forEach( (book)=>{\n book.chapters.forEach( (chapter) => {\n if (chapter.hidden != true) {\n chapters.push(chapter);\n chapter.book = book;\n }\n })\n });\n return chapters;\n }\n\n\n filterChapters(chapters: ChapterModel[]) {\n if ( !this.filterClass ) return chapters;\n return chapters.filter( ( chapter )=>{\n return ( chapter.book.slug === this.filterClass );\n });\n }\n\n init( ){ \n\n\t this.chapterList = new ListRenderer( Node.js( 'chapter-overview' ) );\n\n this.chapterList.defaultRender( function( chapter:ChapterModel ) {\n var node = Node.fromHTML( Template.render('lb.box-chapter', chapter ));\n return { node:node };\n });\n\n this.modifiableChapterList.fill( this.chapters );\n\t this.chapterList.source = this.modifiableChapterList;\n \n this.modifiableChapterList.modifiers.append( this.filterChapters.bind(this) );\n this.filterButtons = [];\n this.data.books.forEach( function(book) {\n if( book.hidden !== true ) {\n var node = Node.fromHTML( Template.render('lb.button-filter', book ));\n Node.one( '.filter-container' ).append( node );\n this.filterButtons.push(node);\n node.click.add( ( filter )=> {\n filter.toggleClass('-active');\n window.scroll(0, Node.one('#chapter-overview').bounds().top -195 );\n\n //remove active class from sibling(s) ( TODO.... das sollte einfacher gehen...)\n Node.all('.button-filter:not(.-toggle)').forEach( ( button )=> {\n if (button.getAttribute('data-slug') !== filter.getAttribute('data-slug')) {\n button.removeClass('-active');\n }\n });\n this.filterClass = ( filter.hasClass('-active') ) ? filter.getAttribute('data-slug'): '';\n this.modifiableChapterList.update();\n });\n }\n }.bind(this));\n\n var toggleButton = Node.fromHTML(Template.render('lb.button-filter', {'title': 'Filtern nach Stufe', 'slug': 'none'} ));\n toggleButton.addClass('-toggle');\n toggleButton.removeClass('-hidden');\n toggleButton.click.add( ()=> {\n toggleButton.toggleClass('-active');\n this.filterButtons.forEach( (button)=> {\n button.toggleClass('-hidden');\n })\n })\n Node.one( '.filter-container' ).append( toggleButton );\n }\n\n}\n\nexport default ChapterFilter;","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 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;","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","/*!\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 4.1.1\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\nvar _isArray = undefined;\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 = undefined;\nvar customSchedulerFn = undefined;\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 r = require;\n var vertx = r('vertx');\n vertxNext = vertx.runOnLoop || vertx.runOnContext;\n return useVertxTimer();\n } catch (e) {\n return useSetTimeout();\n }\n}\n\nvar scheduleFlush = undefined;\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 _arguments = arguments;\n\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 if (_state) {\n (function () {\n var callback = _arguments[_state - 1];\n asap(function () {\n return invokeCallback(_state, child, callback, parent._result);\n });\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(16);\n\nfunction noop() {}\n\nvar PENDING = void 0;\nvar FULFILLED = 1;\nvar REJECTED = 2;\n\nvar GET_THEN_ERROR = new ErrorObject();\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 GET_THEN_ERROR.error = error;\n return GET_THEN_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 === GET_THEN_ERROR) {\n reject(promise, GET_THEN_ERROR.error);\n GET_THEN_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 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 = undefined,\n callback = undefined,\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 ErrorObject() {\n this.error = null;\n}\n\nvar TRY_CATCH_ERROR = new ErrorObject();\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 = undefined,\n error = undefined,\n succeeded = undefined,\n failed = undefined;\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 Enumerator$1(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\nfunction validationError() {\n return new Error('Array Methods must be provided an Array');\n}\n\nEnumerator$1.prototype._enumerate = function (input) {\n for (var i = 0; this._state === PENDING && i < input.length; i++) {\n this._eachEntry(input[i], i);\n }\n};\n\nEnumerator$1.prototype._eachEntry = function (entry, i) {\n var c = this._instanceConstructor;\n var resolve$$1 = c.resolve;\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$2) {\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\nEnumerator$1.prototype._settledAt = function (state, i, value) {\n var promise = this.promise;\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\nEnumerator$1.prototype._willSettleAt = function (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/**\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$1(entries) {\n return new Enumerator$1(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$1(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*/\nfunction Promise$2(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$2 ? initializePromise(this, resolver) : needsNew();\n }\n}\n\nPromise$2.all = all$1;\nPromise$2.race = race$1;\nPromise$2.resolve = resolve$1;\nPromise$2.reject = reject$1;\nPromise$2._setScheduler = setScheduler;\nPromise$2._setAsap = setAsap;\nPromise$2._asap = asap;\n\nPromise$2.prototype = {\n constructor: Promise$2,\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 \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 \n Chaining\n --------\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 \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 \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 \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 \n Assimilation\n ------------\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 \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 \n If the assimliated promise rejects, then the downstream promise will also reject.\n \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 \n Simple Example\n --------------\n \n Synchronous Example\n \n ```javascript\n let result;\n \n try {\n result = findResult();\n // success\n } catch(reason) {\n // failure\n }\n ```\n \n Errback Example\n \n ```js\n findResult(function(result, err){\n if (err) {\n // failure\n } else {\n // success\n }\n });\n ```\n \n Promise Example;\n \n ```javascript\n findResult().then(function(result){\n // success\n }, function(reason){\n // failure\n });\n ```\n \n Advanced Example\n --------------\n \n Synchronous Example\n \n ```javascript\n let author, books;\n \n try {\n author = findAuthor();\n books = findBooksByAuthor(author);\n // success\n } catch(reason) {\n // failure\n }\n ```\n \n Errback Example\n \n ```js\n \n function foundBooks(books) {\n \n }\n \n function failure(reason) {\n \n }\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 \n Promise Example;\n \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 \n @method then\n @param {Function} onFulfilled\n @param {Function} onRejected\n Useful for tooling.\n @return {Promise}\n */\n then: then,\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 \n ```js\n function findAuthor(){\n throw new Error('couldn't find that author');\n }\n \n // synchronous\n try {\n findAuthor();\n } catch(reason) {\n // something went wrong\n }\n \n // async with promises\n findAuthor().catch(function(reason){\n // something went wrong\n });\n ```\n \n @method catch\n @param {Function} onRejection\n Useful for tooling.\n @return {Promise}\n */\n 'catch': function _catch(onRejection) {\n return this.then(null, onRejection);\n }\n};\n\n/*global self*/\nfunction polyfill$1() {\n var local = undefined;\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$2;\n}\n\n// Strange compat..\nPromise$2.polyfill = polyfill$1;\nPromise$2.Promise = Promise$2;\n\nreturn Promise$2;\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\t// added 'undefined' and '#' because IE11 has a problem with them\n\t\tif( window.location.hash == '' || typeof window.location.hash == 'undefined' || 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 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 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 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\n\t// signal for chapter change event\n\tpublic chapterChanged = new Signal();\n\tpublic elementChanged = new Signal();\n\n\tconstructor( data:any ) {\n\t\t\n\t\tsuper( { template:'lb.lernbuch' } );\n\n\t\tthis.scrollMonitor = new ScrollMonitor();\n\t\tthis.book = mapper.model( data ) as BookModel;\n\t\t\n\t}\n\t\n\tinit() {\n\t\tscanner.scan( this.node, this, [ 'search-input', 'search-output' ] );\n\t}\n\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} else {\n\n\t\t\t// no chapter change only element change\n\t\t\tthis.elementChanged.dispatch( this.chapter );\n\t\t}\n\n\t\tthis.updateURL( chapter, element );\n\t\tthis.scrollMonitor.scrollToElementHash();\n\t}\n\n\t/**\n\t * This function resolves the chapter / element model based on the given slug and uid and passes them to the show function\n\t * @param chapterSlug The slug of the chapter to show\n\t * @param elementUid The optional element uid to show\n\t */\n\tpublic showSlug( chapterSlug:string, elementUid:string = window.location.hash.slice( 1 ) ) {\n\n\t\t// solve chapter based on slug\n\t\tvar currentChapter = this.book.chapterOf( chapterSlug );\n\t\t// fallback to first chapter if its not found\n\t\tif( !currentChapter ) currentChapter = this.book.chapters[0];\n\n\t\tvar element = currentChapter.getElement( elementUid );\n\n\t\tthis.show( currentChapter, element );\n\t}\n\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\twindow.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', this.node ) as ChapterNavigation;\t\n\t}\n\n\tget outlineNavigation():OutlineNavigation {\n\t\treturn scanner.first( 'outline-navigation', this.node ) as OutlineNavigation;\n\t}\n\t\n\tget searchInput():SearchInput {\n\t\treturn scanner.first( 'search-input', this.node ) as SearchInput;\n\t}\n\t\n\tget searchOutput():SearchOutput {\n\t\treturn scanner.first( 'search-output', this.node ) as SearchOutput;\n\t}\n}\n\nexport default LernBuch;","\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;","// 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( model );\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 lib\nimport Node from 'ln/node/Node';\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport template from 'ln/template/TemplateManager';\n\n// import project\nimport Element from './Element';\nimport LernBuch from '../LernBuch';\nimport GalleryModel from '../models/GalleryModel';\nimport GalleryImageModel from '../models/GalleryImageModel' \n\n/**\n * An element that renders a gallery\n */\nclass Gallery extends Element {\n\n public model:GalleryModel;\n private currentImage:LinkedList;\n\n constructor( model:GalleryModel ) {\n super( model );\n this.currentImage = LinkedList.fromArray( model.images );\n this.defaultTemplate = this.model.get( 'template', 'lb.gallery-element' );\n }\n\n\t/**\n\t * gallery specific rendering\n\t */\n public init() {\n\t\tsuper.init();\n\n // register click listener on the buttons\n this.node.js( 'next' ).click.add( this.goToNextImage, this );\n this.node.js( 'prev' ).click.add( this.goToPrevImage, this );\n\n this.node.mouseover.add( this.showNavigation, this );\n this.node.mouseout.add( this.hideNavigation, this );\n\n // render elements\n this.renderImageNodes();\n \n // show the first image\n this.showImage();\n }\n\n /**\n * render the image nodes\n */\n private renderImageNodes() {\n\t\t\n\t\tvar images = this.model.images;\n\t\tvar imageContainer = this.node.js( 'images' );\n\n // loop over the images. append them to the image container\n for (var i = 0; i < images.length; i++) {\n\n\t // image-node erstellen\n\t var node = Node.fromHTML( template.render( 'lb.gallery-image-element', images[i] ) );\n\t node.addClass( \"gallery-image\", \"-hidden\" );\n\t\n\t imageContainer.append(node);\n }\n }\n\n /**\n * function to go to the next image within the gallery\n */\n private goToNextImage() {\n this.currentImage = this.currentImage.next || this.currentImage.getFirst();\n this.showImage();\n }\n\n /**\n * function to go to the previous image within the gallery\n */\n private goToPrevImage() {\n this.currentImage = this.currentImage.previous || this.currentImage.getLast();\n this.showImage();\n }\n\n /**\n * go to the current image\n */\n private showImage() {\n\n if( !this.node.js( \"images\" ) ) {\n console.log(\" js attribute \\\"images\\\" missing in template of gallery\" ); \n return;\n }\n\n var images = this.model.images;\n var nodes = this.node.js( \"images\" ).all( \".gallery-image\" );\n \n nodes.forEach( ( node, index ) => {\n // toggle class on the images. hide all the images except the active one \n node.toggleClass( '-hidden', images[index] != this.currentImage );\n // animation: fade-in the currentImage\n // node.toggleClass( 'fade-in', images[index] == this.currentImage );\n });\n\n }\n\n private showNavigation() {\n this.node.js( 'next' ).removeClass( 'hidden' );\n this.node.js( 'prev' ).removeClass( 'hidden' );\n }\n\n private hideNavigation() {\n this.node.js( 'next' ).addClass( 'hidden' );\n this.node.js( 'prev' ).addClass( 'hidden' );\n }\n\n}\n\nexport default Gallery;\n","import View from 'ln/view/View';\nimport Node from 'ln/node/Node';\nimport ListRenderer from 'ln/list/ListRenderer';\nimport List from 'ln/list/List';\nimport IoC from 'ln/ioc/IoC';\nimport { IoCFactoryFunction } from 'ln/list/ListRenderer';\nimport LernBuch from '../../../LernBuch';\nimport TreeNode from './TreeNode';\n\n\nclass TreeView extends View {\n\n\tpublic viewList:ListRenderer;\n\tpublic parent:TreeView = null;\n\tpublic lernbuch:LernBuch;\n\n\tconstructor( public treeNode:TreeNode = null ) {\n\t\t\n\t\tsuper();\n\t\tthis.viewList = new ListRenderer();\n\n\t}\n \n public renderChildren( nodes:TreeNode[] ){\n \n\t\tthis.viewList.selectorFunction = function( item:TreeNode ) {\n\t\t\treturn item.element.get( 'modelName' );\n\t\t}\n\n\t\tthis.viewList.source.fill( nodes );\n\n\t\tthis.viewList.links.all().forEach( link => {\n\t\t\tlink.parent = this;\n\t\t})\n\t}\n\t\n\n\t/**\n\t * Get root of this treeview\n\t */\n\tget root():TreeView{\n\t\treturn ( this.parent == null ) ? this : this.parent.root;\n\t}\n\n\tprotected renderData():any{\n\t\tif( this.treeNode ) return this.treeNode.element;\n\t}\n\n\t/**\n\t * Activate all nodes on this treeview according to\n\t * given path and index\n\t * @param path \n\t * @param index \n\t */\n\tpublic activateCurrentPath( path: TreeNode[], index:number = 0 ){\n\t\tif( path.length > 0 ){\n\t\t\tif( this.treeNode == path[index] ){\n\t\t\t\tthis.node.addClass( '-current' );\n\n\t\t\t\tif( this.viewList.source ){\n\t\t\t\t\tvar link = this.viewList.linkOf( path[index + 1] );\n\t\t\t\t\tif( link ) link.activateCurrentPath( path, index + 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic deactivateCurrentPath(){\n\t\tthis.node.removeClass( '-current' );\n\t\tif( this.viewList.source ){\n\t\t\tthis.viewList.links.all().forEach( link => {\n\t\t\t\tlink.deactivateCurrentPath();\n\t\t\t});\n\t\t}\n\t}\n}\n\n\n\nexport default TreeView;\n","import View from 'ln/view/View';\r\nimport Node from 'ln/node/Node';\r\nimport Signal from 'ln/signal/Signal';\r\nimport ListRenderer from 'ln/list/ListRenderer';\r\nimport List from 'ln/list/List';\r\nimport setup from 'ln/setup/setup';\r\nimport LernBuch from '../../LernBuch';\r\nimport ChapterModel from '../../models/ChapterModel';\r\nimport TreeNode from './tree/TreeNode';\r\nimport TreeView from './tree/TreeView';\r\nimport { TreeNodeCallback } from './tree/TreeNode';\r\nimport Navigation from './Navigation';\r\n\r\nexport interface ChapterElementCallback{\r\n\t( element:ChapterElement)\r\n}\r\n\r\n/**\r\n * Renders a chapter navigation item\r\n */\r\nclass ChapterElement extends TreeView {\r\n\r\n\r\n\tconstructor( public treeNode:TreeNode ) {\r\n\t\t\r\n\t\tsuper( treeNode );\r\n\t\tthis.defaultTemplate = 'lb.navigation-chapter-element';\r\n\r\n\t}\r\n\r\n\tpublic init(){\r\n\t\t\r\n\t\tthis.node.click.add( this.onSelected, this );\r\n\r\n\t\tthis.viewList.container = this.node.js( 'elements' );\r\n\t\tthis.viewList.defaultRender( function( node:TreeNode ) {\r\n\t\t\treturn new ChapterElement( node ).render();\r\n\t\t});\r\n\t\tthis.renderChildren( this.treeNode.children );\r\n\t}\r\n\r\n\tprotected renderData():any{\r\n\t\treturn this.treeNode.element.object();\r\n\t}\r\n\r\n\tprivate onSelected( n:Node, e:MouseEvent ){\r\n\t\t\r\n\t\te.stopPropagation();\r\n\r\n\t\tlet node = this.treeNode.root() as TreeNode;\r\n\t\tlet chapter = node.element as ChapterModel;\r\n\r\n\t\tthis.root.lernbuch.show( chapter, this.treeNode.element );\r\n\r\n\t\t/*\r\n\t\tvar navigation = this.root as Navigation;\r\n\t\tnavigation.toggleNavigation();\r\n\t\t*/\r\n\r\n\t}\r\n\r\n}\r\n\r\nexport default ChapterElement;","import Signal from 'ln/signal/Signal';\r\nimport Node from 'ln/node/Node';\r\nimport ListRenderer from 'ln/list/ListRenderer';\r\nimport List from 'ln/list/List';\r\nimport template from 'ln/template/TemplateManager';\r\nimport setup from 'ln/setup/setup';\r\nimport ChapterModel from '../../models/ChapterModel';\r\nimport LernBuch from '../../LernBuch';\r\nimport ChapterElement from './ChapterElement';\r\nimport TreeNode from './tree/TreeNode';\r\nimport TreeView from './tree/TreeView';\r\nimport Navigation from './Navigation';\r\nimport Element from '../../elements/Element';\r\n\r\nexport interface ChapterCallback{\r\n\t( chapter:Chapter )\r\n}\r\n\r\n/**\r\n * Renders a list chapters used for navigation between chapters\r\n */\r\nclass Chapter extends TreeView{\r\n\r\n\tpublic onSelected:Signal;\r\n\tprivate currentPath:TreeNode[];\r\n\r\n\tconstructor( public treeNode:TreeNode ) {\r\n\t\t\r\n\t\tsuper( treeNode );\r\n\t\tthis.defaultTemplate = 'lb.navigation-chapter';\r\n\t\tthis.onSelected = new Signal();\r\n\t\tthis.currentPath = [];\r\n\t}\r\n\t\r\n\t/**\r\n\t * Render the template for the chapters\r\n\t */\r\n\tpublic init() {\r\n\t\t\r\n\t\tthis.viewList.container = this.node.js( 'elements' );\r\n\t\tthis.viewList.defaultRender( function( node:TreeNode ) {\r\n\t\t\treturn new ChapterElement( node ).render();\r\n\t\t});\r\n\t\t\r\n\t\tif( this.treeNode.children.length > 0 ) {\r\n\t\t\tthis.renderChildren( this.treeNode.children );\r\n\t\t\tthis.node.js( 'toggle' ).click.add( this.onToggle, this );\r\n\t\t} else {\r\n\t\t\tthis.node.js( 'toggle' ).style.visibility = 'hidden';\r\n\t\t}\r\n\r\n\t\tthis.node.js( 'title' ).click.add( this.onChapter, this );\r\n\t\t\r\n\t}\r\n\r\n\t/**\r\n\t * Close the elements of this navigation chapter\r\n\t */\r\n\tpublic closeElements(){\r\n\t\tthis.node.removeClass( '-open' );\r\n\t}\r\n\r\n\t/**\r\n\t * Close the elements of this navigation chapter\r\n\t */\r\n\tpublic openElements(){\r\n\t\tthis.node.addClass( '-open' );\r\n\t}\r\n\r\n\t\r\n\tprivate onChapter() {\r\n\t\tthis.onSelected.dispatch( this );\r\n\t\twindow.scrollTo( 0, 0 );\r\n\t}\r\n\r\n\t/**\r\n\t * Toggle elements of this chapter\r\n\t * @param n \r\n\t * @param e \r\n\t */\r\n\tprivate onToggle( n:Node, e:MouseEvent ) {\r\n\t\tif( !this.node.hasClass( '-open' ) ) ( this.root as Navigation ).hideAllNavigationChapters();\r\n\t\tthis.node.toggleClass( '-open' );\r\n\t}\r\n\r\n\t/**\r\n\t * Check if path to one of the visible elements exists\r\n\t * @param visibleElements \r\n\t */\r\n\tpublic checkPathToElement( visibleElements:Element[] ):TreeNode[]{\r\n\r\n\t\tvar path = [];\r\n\t\tvisibleElements.some( visibleElement => {\r\n\t\t\tlet p = this.treeNode.getPath( visibleElement.model );\r\n\t\t\tif( p.length > 0){\r\n\t\t\t\tpath = p;\r\n\t\t\t\treturn true\r\n\t\t\t}\r\n\t\t\treturn false;\r\n\t\t});\r\n\r\n\t\treturn ( path.length == 0 ) ? undefined : path;\r\n\t}\r\n}\r\n\r\nexport default Chapter;\r\n","\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 BaseTreeNode from \"ln/tree/TreeNode\";\nimport ElementModel from \"../../../models/ElementModel\";\n\nexport interface TreeNodeCallback{\n ():TreeNode;\n}\n\nclass TreeNode extends BaseTreeNode {\n\n private _element:ElementModel;\n private _children:TreeNode[] = [];\n\n constructor( el:ElementModel ){\n super();\n this.element = el;\n }\n\n get element():ElementModel{\n return this._element;\n }\n\n set element( e:ElementModel ){\n this._element = e;\n }\n\n public addChild( n:TreeNode ){\n this.children.push( n );\n }\n\n get children():TreeNode[]{\n return this._children;\n }\n\n public getLevel():number{\n return this._element.get( 'level', undefined );\n }\n\n public flattenChildren():TreeNode[]{\n var nodes:TreeNode[] = [];\n if( this.children && this.children.length > 0 ){\n this.children.forEach( child => {\n nodes.push( child );\n nodes = nodes.concat( child.flattenChildren() );\n });\n }\n return nodes;\n }\n\n public getPath( element:ElementModel ):TreeNode[]{\n var path:TreeNode[] = [];\n if( element == this.element ){\n path.push( this );\n } else {\n this.children.some( child => {\n let tmpPath = child.getPath( element );\n if( tmpPath.length > 0 ){\n path = path.concat( tmpPath );\n path.unshift( this );\n return true;\n }\n return false;\n });\n }\n return path;\n }\n\n}\n\nexport default TreeNode;","import ChapterModel from '../../../models/ChapterModel';\nimport TreeNode from './TreeNode';\nimport ElemetModel from '../../../models/ElementModel';\n\nexport interface IFilteredChapters{\n root:TreeNode,\n children: TreeNode[]\n}\n\nexport interface IChapterFilter{\n modelNames:string[],\n level:number\n}\n\nclass TreeBuilder {\n\n private startLevel = 1;\n private filteredChapters\n\n public convert( chapters:ChapterModel[], options:IChapterFilter ){\n this.filteredChapters = this.filter( chapters, options );\n\n var hierarchyChapters:TreeNode[] = [];\n this.filteredChapters.forEach( chapter => {\n var result = this.build( chapter.root, chapter.children );\n hierarchyChapters.push( result );\n });\n\n return hierarchyChapters;\n\n }\n \n private filter( chapters:ChapterModel[], options:IChapterFilter ){\n \n var filteredChapters:IFilteredChapters[] = [];\n\n chapters.forEach( chapter => {\n var root = new TreeNode( chapter );\n var children = [];\n chapter.elements.all().forEach( element => {\n if( options.modelNames.indexOf( element.modelName ) > -1 && element.get( 'level', 0 ) <= options.level ){\n children.push( new TreeNode( element ))\n }\n });\n filteredChapters.push( { root:root, children: children } );\n });\n\n return filteredChapters;\n }\n\n private buildHierarchy( current:TreeNode, elements:TreeNode[], level:number ){\n // if we are still on the startlevel and have no level specified - just return current \n if( current.getLevel() == undefined && level == this.startLevel ){\n return current;\n } \n\n while( elements.length > 0 ){\n var next = elements.shift();\n if( next.getLevel() > level ){\n \n // add first to current\n current.addChild( next );\n next = this.buildHierarchy( next, elements, next.getLevel() );\n\n } else if ( next.getLevel() == undefined ){\n \n current.addChild( next );\n\n } else {\n \n // put element back on stack and go level back\n elements.unshift( next );\n break;\n\n }\n\n }\n\n current.assignParent();\n\n return current;\n\n }\n\n private build( chapter:TreeNode, elements:TreeNode[] ){\n while( elements.length > 0 ){\n chapter.addChild( this.buildHierarchy( elements.shift(), elements, this.startLevel ) );\n chapter.assignParent();\n }\n return chapter;\n }\n\n}\n\nexport default TreeBuilder;","import View from 'ln/view/View';\nimport Signal from 'ln/signal/Signal';\nimport Node from 'ln/node/Node';\nimport template from 'ln/template/TemplateManager';\nimport ListRenderer from 'ln/list/ListRenderer';\nimport List from 'ln/list/List';\nimport Model from 'ln/model/Model';\nimport IoC from 'ln/ioc/IoC';\nimport ChapterModel from '../../models/ChapterModel';\nimport Chapter from '../../elements/Chapter';\nimport LernBuch from '../../LernBuch';\nimport NaviChapter from './Chapter';\nimport NaviChapterElement from './ChapterElement';\nimport TreeBuilder from './tree/TreeBuilder';\nimport { IChapterFilter } from './tree/TreeBuilder';\nimport TreeNode from './tree/TreeNode';\nimport TreeView from './tree/TreeView';\nimport Element from '../../elements/Element';\n\n/**\n * Renders a list chapters used for navigation between chapters\n */\nclass Navigation extends TreeView {\n\n\tprivate treeBuilder:TreeBuilder;\n\tprivate treeChapters:TreeNode[];\n\tprivate currentChapter:NaviChapter = null;\n\n\n\tconstructor( public lernbuch:LernBuch ) {\n\t\t\n\t\tsuper();\n\t\tthis.treeBuilder = new TreeBuilder();\n\t\tthis.defaultTemplate = 'lb.chapter-navigation'\n\t}\n\n\t/**\n\t * Render the template for the chapters\n\t */\n\tpublic init() {\n\t\t\n\t\tthis.viewList = new ListRenderer( this.node.js( 'chapters' ) );\n\t\tthis.viewList.defaultRender( ( model:TreeNode ) => {\n\t\t\tvar naviChapter = new NaviChapter( model ).render() as NaviChapter;\n\t\t\tnaviChapter.onSelected.add( this.selectChapter, this )\n\t\t\treturn naviChapter;\n\t\t});\n\n\t\tthis.lernbuch.scrollMonitor.change.add( this.onScrolled, this );\n\t\tthis.lernbuch.chapterChanged.add( this.onChapterChanged, this );\n\n\t}\n\n\n\t/**\n\t * Render all chapters\n\t */\n\tpublic renderChapters( chapters:Array, options:IChapterFilter ) {\n\n\t\tthis.treeChapters = this.treeBuilder.convert( chapters, options );\n\n\t\tthis.renderChildren( this.treeChapters );\n\t}\n\n\t/**\n\t * Hide all navigations chapters\n\t */\n\tpublic hideAllNavigationChapters(){\n\t\tthis.viewList.links.all().forEach( link => {\n\t\t\tlink.closeElements();\n\t\t});\n\t}\n\n\t/**\n\t * Select the given chapter on the lernbuch\n\t * @param chapter \n\t */\n\tpublic selectChapter( chapter:NaviChapter ){\n\t\tthis.lernbuch.show( chapter.treeNode.element as ChapterModel );\n\t}\n\n\t/**\n\t * Scrollhandler on the lernbuch\n\t * @param visibleElements \n\t */\n\tprivate onScrolled( visibleElements:Element[] ){\n\t\t\n\t\t// update path on current chapter\n\t\tvar currentChapter = this.currentChapter;\n\t\tvar path = this.currentChapter.checkPathToElement( visibleElements );\n\t\tif( !path ) return;\n\t\tthis.currentChapter.deactivateCurrentPath();\n\t\tthis.currentChapter.activateCurrentPath( path );\n\n\t}\n\n\n\tprivate onChapterChanged( chapter:Chapter ){\n\t\t\n\t\tif( this.currentChapter ) this.currentChapter.deactivateCurrentPath();\n\t\t\n\t\tvar current = this.viewList.links.all().filter( link => {\n\t\t\treturn link.treeNode.element == chapter.model;\n\t\t});\n\n\t\tthis.currentChapter = current[0];\n\t}\n\t\n}\n\nexport default Navigation;\n","import View from 'ln/view/View';\nimport Node from 'ln/node/Node';\nimport Template from 'ln/template/TemplateManager';\nimport Window from 'ln/node/Window';\nimport LernBuch from '../../LernBuch';\nimport Element from '../../elements/Element';\nimport Chapter from '../../elements/Chapter';\nimport BookModel from '../../models/BookModel';\nimport ChapterModel from '../../models/ChapterModel';\n\n\n/**\n * renders all elements of all chapters into a line to quick navigation through them\n */\nclass OutlineNavigation extends View {\n\t\n\tprivate elements:Array = [];\n\tprivate lernbuch:LernBuch;\n\n\tconstructor( lernbuch:LernBuch, template:string = 'lb.outline-navigation' ) {\n\t\tsuper();\n\t\t\n\t\tthis.lernbuch = lernbuch;\n\t\tthis.defaultTemplate = template;\n\n\t}\n\n\tpublic init(){\n\n\t\t// replace the outlineNavigation\n\t\tthis.lernbuch.node.one( '.outline-navigation' ).replace( this.node );\n\n\t\t// register on change event of scrollMonitor\n\t\tthis.lernbuch.scrollMonitor.change.add( this.update, this );\n\n\t\t// listen on chapter changed to proper update even if no element is visible\n\t\tthis.lernbuch.chapterChanged.add( this.onChapterChange, this );\n\n\t}\n\n\t/**\n\t * Chapter change handler\n\t * @param chapter \n\t */\n\tprivate onChapterChange( chapter:Chapter ) {\n\t\tif (chapter.elements.links.first() != null) {\n\t\t\tthis.update( [ ( chapter.elements.links.first() as any ) ] ) ;\n\t\t}\n\t} \n\t\n\t/**\n\t * Updates the outline navigation based on the visible element of the lernbuch\n\t */\n\tprivate update( visibleElements:Array ) {\n\t\t\n\t\t// check for proper visible elements\n\t\tif( visibleElements && visibleElements.length > 0 ) {\n\t\t\t\n\t\t\t// now get the ration of the vertical scrollbar to select visible element based on scrollbar position\n\t\t\tvar scrollInfo = Window.scrollInfo();\n\t\t\t// check for division by zero\n\t\t\tvar ratio = ( scrollInfo.height != 0 ) ? ( scrollInfo.top / scrollInfo.height ) : 0;\n\t\t\t\n\t\t\t// clamped index based on scrollbar ratio\n\t\t\tvar index = Math.min( Math.max( Math.round( ratio * ( visibleElements.length - 1 ) ), 0 ), visibleElements.length - 1 );\n\t\t\tthis.selectElement( visibleElements[ index ] );\n\t\t}\n\t}\n\t\n\t/**\n\t * Selects only the outline element based on the given element\n\t */\n\tpublic selectElement( element:Element ) {\n\t\t// search for the elment with the given model\n\t\tvar found = this.elements.filter( ( current ) => {\n\t\t\treturn current.getAttribute( 'name' ) == element.model.uid;\n\t\t});\n\t\t\n\t\t// remove active class on all elements\n\t\tthis.node.all( '.element' ).forEach( ( node:Node ) => {\n\t\t\tnode.removeClass( '-active' );\n\t\t});\n\t\t\n\t\t// add active class to element\n\t\tif( found && found.length > 0 ) found[0].addClass( '-active' );\n\t}\n\t\n\t\n\t/**\n\t * Renders all chapters and elements\n\t */\n\tpublic renderBook( book:BookModel ):void {\n\t\t\n\t\t// loop over chapters and elements\n\t\tbook.chapters.forEach( ( chapter:ChapterModel ) => {\n\t\t\t\n\t\t\tvar chapterNode = Node.fromHTML( Template.render( 'lb.outline-chapter', chapter ) );\n\t\t\t\n\t\t\tchapter.elements.all().forEach( ( element ) => {\n\t\t\t\t\n\t\t\t\tvar elementNode = Node.fromHTML( Template.render( 'lb.outline-element', element ) );\n\t\t\t\telementNode.setAttribute( 'name', element.uid );\n\t\t\t\tthis.elements.push( elementNode );\n\t\t\t\t\n\t\t\t\tchapterNode.append( elementNode );\n\t\t\t});\n\t\t\t\n\t\t\tthis.node.append( chapterNode );\n\t\t});\n\t}\n}\n\nexport default OutlineNavigation;","import View from 'ln/view/View';\r\nimport Node from 'ln/node/Node';\r\nimport Window from 'ln/node/Window';\r\nimport LernBuch from '../../LernBuch';\r\nimport ChapterModel from '../../models/ChapterModel';\r\n\t\r\n/**\r\n * Renders a link to the previous chapter at the top of the chapter and a link to the next chapter at to bottom of the chapter\r\n */\r\nclass PagingNavigation extends View {\r\n\r\n\tconstructor( public lernbuch:LernBuch, template:string = 'lb.paging-navigation' ) {\r\n\t\t\r\n\t\tsuper();\r\n\t\t\r\n\t\tthis.defaultTemplate = template;\r\n\r\n\t\t// register on window events\r\n\t\tWindow.resize.add( this.updateVisibility, this );\r\n\t\tWindow.scroll.add( this.updateVisibility, this );\r\n\r\n\t\t// listen to chapter changes\r\n\t\tthis.lernbuch.chapterChanged.add( this.updateLink, this );\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Define things after render\r\n\t */\r\n\tpublic init(){\r\n\r\n\t\t// register on the click for the next chapter\r\n\t\tthis.node.click.add( this.onClick, this ); \r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Define data to render\r\n\t */\r\n\tprotected renderData(){\r\n\t\treturn { title: 'no next chapter yet' };\r\n\t}\r\n\t\r\n\tprivate updateLink() {\r\n\t\t\r\n\t\t// get the model of the current chapter\r\n\t\tvar currentChapter = this.lernbuch.chapter.model;\r\n\t\t\r\n\t\t// get the next chapter if existing\r\n\t\tvar nextChapter:ChapterModel = currentChapter.next as ChapterModel;\r\n\r\n\t\tif( nextChapter != undefined ) {\r\n\t\t\t\r\n\t\t\t// set the title of the next chapter\r\n\t\t\tthis.node.js( 'chaptertitle' ).html = nextChapter.title;\r\n\t\t\tthis.node.removeClass( 'hidden' );\r\n\r\n\t\t} else {\r\n\t\t\t\r\n\t\t\tthis.node.addClass( 'hidden' );\r\n\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Update visibility according to scroll position of window\r\n\t */\r\n\tprivate updateVisibility() {\r\n\t\t\r\n\t\tvar scrollInfo = Window.scrollInfo();\r\n\r\n\t\t// hide or show the buttons\r\n\t\tif( scrollInfo.height <= scrollInfo.top ) {\r\n\t\t\tthis.node.addClass( '-show' );\r\n\t\t}else {\r\n\t\t\tthis.node.removeClass( '-show' );\r\n\t\t}\r\n\t}\r\n\t\r\n\t/**\r\n\t * Callback when one of the navigation items was clicked.\r\n\t */\r\n\tprivate onClick( node:Node, event:Event ) {\r\n\t\t\r\n\t\tvar nextChapter = this.lernbuch.chapter.model.next as ChapterModel;\r\n\t\tthis.lernbuch.show( nextChapter );\r\n\r\n\t}\r\n\t\r\n}\r\n\r\nexport default PagingNavigation;\r\n\t","import template from 'ln/template/TemplateManager';\nimport BookModel from '../models/BookModel';\nimport ChapterModel from '../models/ChapterModel';\nimport ElementModel from '../models/ElementModel';\n\n\n/**\n * SearchResult Class.\n */\nclass SearchResult {\n\n\tpublic excerpts:Array;\n\tpublic book:BookModel;\n\tpublic chapter:ChapterModel;\n\tpublic element:ElementModel;\n\t\n\t/**\n\t * Instantiates a SearchResult\n\t */\n\tconstructor( element:ElementModel ) {\n\t\tthis.element = element;\n\t\tthis.excerpts = [];\n\t}\n\t\n\tpublic makeExcerpt( term:string, text:string ) {\n\t\t// limit defines how many chracters are included before and after the found term\n\t\tvar limit = 35;\n\t\t\t\t\t\n\t\tvar indices = [];\n\t\t// get the indices within the text (positions of the matches within the text)\n\t\ttext.replace( new RegExp( term, 'g' ), function( match, index ) {\n\t\t\tindices.push( index );\n\t\t\treturn match;\n\t\t});\n\t\t\n\t\t// iterate over the indices\n\t\tfor (var i = 0; i < indices.length; i++) {\n\t\t\t// define start and stop position\n\t\t\tvar start = Math.max( 0, indices[i] - limit );\n\t\t\tvar stop = Math.min( text.length, indices[i] + term.length + limit );\n\t\t\t// make the excerpt\n\t\t\tvar excerpt = text.substring( start, stop );\n\t\t\t// wrap the term with the search-result template\n\t\t\texcerpt = excerpt.replace( new RegExp( term, 'g' ), function( match, index ) {\n\t\t\t\treturn template.render( 'lb.search-result', { term:term } );\n\t\t\t});\n\t\t\t\n\t\t\tthis.excerpts.push(excerpt);\n\t\t}\n\t}\n\t\n\tpublic toJSON() {\n\t\treturn { excerpts: this.excerpts, element:this.element.object(), book:this.book.object(), chapter:this.chapter.object() }\t\n\t}\n}\n\nexport default SearchResult;","import ElementModel from '../models/ElementModel';\nimport SearchResult from './SearchResult';\n\n/**\n * SearchPattern Class.\n */\nclass SearchPattern {\n\n\t\n\tpublic search( term:string, element:ElementModel ):SearchResult {\n\t\tvar result:SearchResult = new SearchResult( element );\n\t\treturn null;\n\t}\n}\n\nexport default SearchPattern;","import SearchPattern from './SearchPattern';\nimport SearchResult from './SearchResult';\nimport ElementModel from '../models/ElementModel';\n\n/**\n * AtrributeSearch Class.\n */\nclass AttributeSearch extends SearchPattern {\n\n\tpublic attributes:Array;\n\n\t/**\n\t * Instantiates an AttributeSearch\n\t */\n\tconstructor( attributes ) {\n\t\tsuper();\n\t\tthis.attributes = attributes;\n\t}\n\t\n\tpublic search( term:string, element:ElementModel ):SearchResult {\n\t\tvar result = new SearchResult( element );\n\t\t// iterate over all the attributes\n\t\tthis.attributes.forEach( ( attribute ) => {\n\t\t\t// if there is an attribute, search on the attribute\n\t\t\tif (element.get(attribute)) {\n\t\t\t\tvar text = this.stripHTML( element.get(attribute).toString() );\n\t\t\t\tvar matchFound = text.indexOf(term);\n\t\t\t\tif (matchFound > -1) {\n\t\t\t\t\tresult.makeExcerpt( term, text );\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\t\n\t\t// return the result only when there is any excerpt\n\t\treturn ( result.excerpts.length > 0 ) ? result : null;\n\t}\n\n\tprivate stripHTML( text:string ){\n\t\tvar temp = document.createElement( 'div' );\n\t\ttemp.innerHTML = text;\n\t\treturn temp.textContent || temp.innerText;\n\t}\n}\n\nexport default AttributeSearch;","import SearchPattern from './SearchPattern';\nimport SearchResult from './SearchResult';\nimport ElementModel from '../models/ElementModel';\n\n\n/**\n * ArraySearch Class.\n */\nclass ArrayModelSearch extends SearchPattern {\n\n\tpublic attributes:Array;\n\tpublic elementType:string;\n\n\t/**\n\t * Instantiates an ArraySearch\n\t */\n\tconstructor( elementType, attributes ) {\n\t\tsuper();\n\t\tthis.elementType = elementType;\n\t\tthis.attributes = attributes;\n\t}\n\t\n\t\n\tpublic search( term:string, element:ElementModel ):SearchResult {\n\t\t// get the sub-elements\n\t\tvar elements = element.get(this.elementType);\n\t\t// create a new SearchResult\n\t\tvar result = new SearchResult( element );\n\t\t// iterate over the sub-elements\n\t\tfor (var key in elements) {\n\t\t\t// iterate over the attributes\n\t\t\tthis.attributes.forEach( ( attribute ) => {\n\t\t\t\t// if there is an element with the attribute, search on the attribute\n\t\t\t\tif (elements[key][attribute]) {\n\t\t\t\t\tvar text = elements[key][attribute].toString();\n\t\t\t\t\tvar matchFound = text.indexOf(term);\n\t\t\t\t\tif (matchFound > -1) {\n\t\t\t\t\t\tresult.makeExcerpt( term, text );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\t// return the result only when there is any excerpt\n\t\treturn ( result.excerpts.length > 0 ) ? result : null;\n\t}\n}\n\nexport default ArrayModelSearch;","import SearchPattern from './SearchPattern';\nimport SearchResult from './SearchResult';\nimport ElementModel from '../models/ElementModel';\n\n/**\n * ArraySearch Class.\n */\nclass ArraySearch extends SearchPattern {\n\n\tpublic attributes:Array;\n\n\t/**\n\t * Instantiates an ArraySearch\n\t */\n\tconstructor( attributes ) {\n\t\tsuper();\n\t\tthis.attributes = attributes;\n\t}\n\t\n\t\n\tpublic search( term:string, element:ElementModel ):SearchResult {\n\t\tvar result = new SearchResult( element );\n\t\t// iterate over all the attributes\n\t\tthis.attributes.forEach( ( attribute ) => {\n\t\t\t// if there is an attribute, search on the attribute\n\t\t\tif (element.get(attribute)) {\n\t\t\t\tvar attributeArray = element.get(attribute);\n\t\t\t\t// iterate over all the elements of the array\n\t\t\t\tfor (var key in attributeArray) {\n\t\t\t\t\tvar text = attributeArray[key].toString();\n\t\t\t\t\tvar matchFound = text.indexOf(term);\n\t\t\t\t\tif (matchFound > -1) {\n\t\t\t\t\t\tresult.makeExcerpt( term, text );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\t\n\t\t// return the result only when there is any excerpt\n\t\treturn ( result.excerpts.length > 0 ) ? result : null;\n\t}\n}\n\nexport default ArraySearch;","import BookModel from '../models/BookModel';\nimport SearchPattern from './SearchPattern';\nimport SearchResult from './SearchResult';\nimport AttributeSearch from './AttributeSearch';\nimport ArrayModelSearch from './ArrayModelSearch';\nimport ArraySearch from './ArraySearch';\n\n/**\n * Search class\n */\nclass Search {\n\n\tpublic book:BookModel;\n\tprivate patterns:{ [index:string]: SearchPattern };\n\n\t/**\n\t * Instantiates a Search\n\t */\n\tconstructor( book:BookModel ) {\n\t\tthis.book = book;\n\t\tthis.patterns = {};\n\t\tthis.setDefaultPatterns();\n\t}\n\t\n\t/**\n\t * Set the default Patterns\n\t */\t\n\tprivate setDefaultPatterns() {\n\t\tthis.setPattern( 'Title', new AttributeSearch( [ \"title\" ] ) );\n\t\tthis.setPattern( 'App\\\\Title', new AttributeSearch( [ \"title\" ] ) );\n\t\tthis.setPattern( 'Paragraph', new AttributeSearch( [ \"text\" ] ) );\n\t\tthis.setPattern( 'App\\\\Paragraph', new AttributeSearch( [ \"text\" ] ) );\n\t\tthis.setPattern( 'Image', new AttributeSearch( [ \"caption\" ] ) );\n\t\tthis.setPattern( 'App\\\\Image', new AttributeSearch( [ \"caption\" ] ) );\n\t\tthis.setPattern( 'IFrame', new AttributeSearch( [ \"caption\" ] ) );\n\t\tthis.setPattern( 'App\\\\IFrame', new AttributeSearch( [ \"caption\" ] ) );\n\t\tthis.setPattern( 'Quote', new AttributeSearch( [ \"text\", \"caption\" ] ) );\n\t\tthis.setPattern( 'App\\\\Quote', new AttributeSearch( [ \"text\", \"caption\" ] ) );\n\t\tthis.setPattern( 'Gallery', new ArrayModelSearch( \"images\", [ \"caption\" ] ) );\n\t\tthis.setPattern( 'App\\\\Gallery', new ArrayModelSearch( \"images\", [ \"caption\" ] ) );\n\t\tthis.setPattern( 'Listing', new ArraySearch( [ \"items\" ] ) );\n\t\tthis.setPattern( 'App\\\\Listing', new ArraySearch( [ \"items\" ] ) );\n\t}\n\t\n\t/**\n\t * Add a Search-Pattern\n\t */\t\t\n\tpublic setPattern( elementType:string, searchType:SearchPattern ) {\n\t\tthis.patterns[ elementType ] = searchType;\n\t}\n\t\n\t/**\n\t * Search function gets the term to search for\n\t */\n\tpublic forString( term:string ):Array {\n\t\tvar results:Array = [];\n\t\t// iterate over all the chapters of a book\n\t\tthis.book.chapters.forEach( ( chapter ) => {\n\t\t\t// iterate over all the elements of a chapter\n\t\t\tchapter.elements.all().forEach( ( element ) => {\n\t\t\t\tvar modelName = element.modelName;\n\t\t\t\t// add the pattern of the elements modelName\n\t\t\t\tvar pattern = this.patterns[ modelName ];\n\t\t\t\tif (pattern) {\n\t\t\t\t\t// search on the pattern\n\t\t\t\t\tvar result = pattern.search(term, element);\n\t\t\t\t\t// if there is a result, push the result to the results array. \n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tresults.push(result);\n\t\t\t\t\t\tresult.chapter = chapter;\n\t\t\t\t\t\tresult.book = this.book;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\treturn results;\t\t\t \n\t}\n}\n\nexport default Search;","import Signal from 'ln/signal/Signal';\nimport Node from 'ln/node/Node';\nimport View from 'ln/view/View';\nimport template from 'ln/template/TemplateManager';\nimport SearchResult from './SearchResult';\nimport Search from './Search';\nimport BookModel from '../models/BookModel';\n\n\n/**\n * Interface for all listener function that listen on the top signal\n */\ninterface SearchResultListener {\n ( term:string, results:Array, ...args:any[] );\n}\n\n/**\n * SearchInput Class.\n */\nclass SearchInput extends View {\n\t\n\tpublic search:Search;\n\tpublic minChars = 3;\n\tpublic results:Signal = new Signal();\n\t\n\tconstructor( book:BookModel, template:string = 'lb.search-input' ) {\n\t\t\n\t\tsuper();\n\t\tthis.defaultTemplate = template;\n\t\tthis.search = new Search( book );\n\t}\n\t\n\tpublic init() {\n\n\t\tvar input = this.node.js( 'input' );\n\t\tinput.keyup.add( ()=> {\n\t\t\tif( input.value.length >= this.minChars ) {\n\t\t\t\tvar searchResults = this.search.forString( input.value );\n\t\t\t}\n\t\t\tthis.results.dispatch( input.value, searchResults );\n\t\t});\n\t\t\n\t\tinput.click.add( ()=> {\n\t\t\tif ( input.value == \"Suchen\" ) {\n\t\t\t\tinput.value = \"\";\n\t\t\t}\n\t\t});\n\t\t\n\t\tinput.blur.add( ()=> {\n\t\t\tif ( input.value == \"\" ) {\n\t\t\t\tinput.value = \"Suchen\";\n\t\t\t}\n\t\t});\n\t}\n}\n\nexport default SearchInput;","import Node from 'ln/node/Node';\nimport View from 'ln/view/View';\nimport Signal from 'ln/signal/Signal';\nimport template from 'ln/template/TemplateManager';\nimport setup from 'ln/setup/setup';\nimport SearchInput from './SearchInput';\nimport SearchResult from './SearchResult';\nimport LernBuch from '../LernBuch';\n\n/**\n * SearchOutput Class.\n */\nclass SearchOutput extends View{\n\n\tpublic resultTemplate:string;\n\t\n\t/**\n\t * Instantiates a SearchOutput\n\t */\n\tconstructor( public lernbuch:LernBuch, template:string = \"lb.search-output\", resultTemplate:string = 'lb.search-output-result' ) {\n\t\tsuper();\n\t\tthis.defaultTemplate = template;\n\t\tthis.resultTemplate = resultTemplate;\n\t}\n\n\tpublic listenTo( input:SearchInput ){\n\t\tinput.results.add( function( term:string, results:SearchResult[] ){\n if( results ){\n this.node.addClass( '-open' );\n this.renderResults( term, results )\n } else {\n this.node.removeClass( '-open' );\n\t\t\t}\n\t\t}.bind( this ));\n\t}\n\t\n\tpublic renderResults( term:string, searchResults:Array ){\n\t\tvar outputList = this.node.js( 'list' );\n\t\toutputList.empty();\n\t\tfor( var i = 0; i < searchResults.length; i++ ) {\n\t\t\tvar result = new SearchOutputResult( searchResults[i] ).render() as SearchOutputResult;\n\t\t\tresult.selected.add( this.onResultSelected, this );\n\t\t\toutputList.append( result.node );\n\t\t}\n\t}\n\n\tprivate onResultSelected( result:SearchResult ){\n\t\tthis.node.removeClass( '-open' );\n\t\tthis.lernbuch.show( result.chapter, result.element );\n\t}\n}\n\nexport default SearchOutput;\n\ninterface SearchOutputCallback {\n\t( result:SearchResult );\n}\n\nclass SearchOutputResult extends View {\n\n\tpublic selected:Signal;\n\n\tconstructor( public result:SearchResult, template:string = 'lb.search-output-result'){\n\t\tsuper();\n\t\tthis.defaultTemplate = template;\n\n\t\tthis.selected = new Signal();\n\n\t}\n\n\tpublic init(){\n\t\tthis.node.click.add( this.onSelected, this );\n\t}\n\t\n\tprotected renderData(){\n\t\treturn this.result;\n\t}\n\t\n\tprivate onSelected(){\n\t\tthis.selected.dispatch( this.result );\n\t}\n}\n","import View from 'ln/view/View';\nimport Node from 'ln/node/Node';\nimport Window from 'ln/node/Window';\nimport Template from 'ln/template/TemplateManager';\nimport LernBuch from '../LernBuch';\nimport Element from '../elements/Element';\n\nexport interface ShareConfig{\n\tfacebook:string,\n\ttwitter:string,\n\tmail:string\n}\n\n/**\n * Dialogbox for sharing content of the lb\n */\nclass ShareDialog extends View {\n\n\tprivate config:ShareConfig = {\n\t\ttwitter: \"https://twitter.com/home?status=\",\n\t\tfacebook: \"https://www.facebook.com/sharer/sharer.php?u=\",\n\t\tmail: \"mailto:?body=\"\n\t};\n\t\n\t\n\tconstructor( public lernbuch:LernBuch, template:string = 'lb.share-dialog' ) {\n\t\tsuper();\n\n\t\tthis.lernbuch.scrollMonitor.change.add( this.updateLinks, this );\n\t\tthis.defaultTemplate = template;\n\t}\n\n\t/**\n\t * Set the config for the share dialog\n\t * @param config \n\t */\n\tpublic setConfig( config:ShareConfig ){\n\t\tthis.config = config;\n\t}\n\t\n\t/**\n\t * Renders the template for the sharedialog\n\t */\n\tpublic init() {\n\t\tthis.node.js( 'toggle' ).click.add( this.toggleShare, this );\n\t\tthis.setLinkNodes();\n\t}\n\n\tprivate setLinkNodes(){\n\t\tfor( var key in this.config ){\n\t\t\tthis.node.js( 'links' ).append( Node.fromHTML( Template.render( 'lb.share-link', { key: key } ) ) );\n\t\t}\n\t}\n\t\n\t/**\n\t * Toggles the visibility of the share dialog\n\t */\n\tprivate toggleShare():void {\n\t\tthis.node.toggleClass( '-open' );\n\t}\n\t\n\t/**\n\t * Update the share links\n\t */\n\tprivate updateLinks( visibleElements:Element[]):void {\n\t\tvar input = this.node.js( 'input' );\n\t\tif( visibleElements.length == 0 ){\n\t\t\tinput.value = window.location.toString();\n\t\t\tthis.setShareLinks( window.location.toString() );\n\t\t} else {\n\t\t\tinput.value = this.setHash( visibleElements[0] );\n\t\t\tthis.setShareLinks( this.setHash( visibleElements[0] ) );\n\t\t}\n\t}\n\t\n\t/**\n\t * Set the share links\n\t */\n\tprivate setShareLinks( url: string ) {\n\t\t\n\t\tfor( var key in this.config ){\n\t\t\tvar link = this.node.js( key );\n\t\t\tif( key == 'mail' ){\n\t\t\t\tlink.setAttribute( 'href', this.config[key] + this.setMailBody( url ) );\n\t\t\t} else {\n\t\t\t\tlink.setAttribute( 'href', this.config[key] + encodeURIComponent( url ) );\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * Set the body of the mail\n\t */\n\tprivate setMailBody( url: string ) {\n\t\tvar body = this.lernbuch.book.get( 'title' ) + ' - ' + this.lernbuch.chapter.model.get( 'title' );\n\t\treturn encodeURIComponent( body ) + '%0D%0A%0D%0A'\n\t\t\t+ encodeURIComponent( url );\n\t}\n\n\t/**\n\t * Set the hash of the url to the first visible element\n\t */\n\tprivate setHash( visibleElement:Element ): string {\n\t\tvar hash = window.location.hash;\n\t\tvar newHash = visibleElement.model.uid;\n\t\treturn ( hash == '' ) ? window.location.toString() + newHash : window.location.href.replace( hash, newHash );\n\t}\n}\n\nexport default ShareDialog;","// import lib\nimport Model from 'ln/model/Model';\nimport LinkedList from 'ln/linkedlist/LinkedList';\n\n// import project\nimport ChapterModel from './ChapterModel';\nimport ElementModel from './ElementModel';\n\nclass BookModel extends Model {\n\t\n\tget title():string {\n\t\treturn this.get( 'title' );\n\t}\n\t\n\tget slug():string {\n\t\treturn this.get( 'slug' );\n\t}\n\t\n\tget chapters():Array {\n\t\treturn this.get>( 'chapters' );\n\t}\n\t\n\tset chapters( chapters: Array ){\n\t\tthis.set( 'chapters', chapters );\n\t}\n\t\n\t\n\t/**\n\t\t* Returns the chapter model for the book with the given slug\n\t\t*/\n\tpublic chapterOf( slug:string ):ChapterModel {\n\n\t\t// find the chapter with the given id.\n\t\tvar found = this.chapters.filter( function( chapter:ChapterModel ) {\n\t\t\treturn chapter.slug === slug;\n\t\t});\n\t\t\n\t\treturn found[0];\n\t}\n}\n\nexport default BookModel;","import Model from 'ln/model/Model';\nimport ChapterModel from 'lb/models/ChapterModel';\n\n/**\n * This class generates a slug based on the id and model name\n */\nclass ElementModel extends Model {\n\t\n\tconstructor( json:Object ){\n\t\tsuper( json );\n\t}\n\t\n\tget modelName():string {\n\t\treturn this.get( 'modelName' );\n\t}\n\t\n\tget id():number {\n\t\treturn this.get( 'id' );\n\t}\n\t\n\tget uid():string {\n\t\tif( super.object().uid ) return super.object().uid + this.id;\n\t\tvar prefix = this.modelName.substring( 0, 2 );\n\t\treturn prefix + this.id;\n\t}\n\n\tget chapter():ChapterModel {\n\t\treturn this.get( 'chapter' );\n\t}\n\t\n\tobject() {\n\t\tvar obj:any = super.object();\n\t\tif( !obj.uid ) obj.uid = this.uid;\n\t\t\n\t\treturn obj;\n\t}\n}\n\nexport default ElementModel;","// import lib\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport List from 'ln/list/List';\nimport { mixin } from 'ln/js';\n\n// import project\nimport ElementModel from './ElementModel';\nimport BookModel from 'lb/models/BookModel';\n\nclass ChapterModel extends ElementModel implements LinkedList {\n \n next:LinkedList;\n previous:LinkedList;\n \n /**\n * returns an element with the given uid of this chapter\n */\n getElement( uid ):ElementModel {\n \n // find the element with the given id on the chapter config.\n var found = this.elements.all().filter( function( element:ElementModel ) {\n return element.uid == uid;\n });\n \n return found[0];\n }\n\n \n get title():string {\n return this.get( 'title' );\n }\n\n get style():string {\n return this.get( 'style' );\n }\n\n get subtitle():string {\n return this.get( 'subtitle' );\n }\n \n get elements():List {\n return this.get>( 'elements' );\n }\n\t\n\t\tset elements( el: List ){\n\t\t\tthis.set( 'elements', el );\n\t\t}\n\t\t\n get slug():string {\n return this.get( 'slug' );\n }\n\n get book():BookModel {\n return this.get( 'parent' );\n }\n \n /**\n * Add mixins functions\n */\n getFirst: () => LinkedList;\n getLast: () => LinkedList;\n addNext: ( node:LinkedList ) => void;\n addPrevious: ( node:LinkedList ) => void;\n toArray: () => LinkedList[];\n}\n\nmixin( ChapterModel, [ LinkedList ] );\n\nexport default ChapterModel;","import LinkedList from 'ln/linkedlist/LinkedList';\nimport { mixin } from 'ln/js';\nimport ElementModel from './ElementModel';\n\nclass GalleryImageModel extends ElementModel implements LinkedList {\n\t\n\tnext:LinkedList;\n\tprevious:LinkedList;\n\t\n\tget caption():string{\n\t\treturn this.get( 'caption' );\n\t}\n\t\n\tget image(){\n\t\treturn this.get( 'image' );\n\t}\n\t\n\t/**\n * Add mixins functions\n */\n getFirst: () => LinkedList;\n getLast: () => LinkedList;\n addNext: ( node:LinkedList ) => void;\n addPrevious: ( node:LinkedList ) => void;\n toArray: () => LinkedList[];\n}\n\nmixin( GalleryImageModel, [ LinkedList ] );\n\nexport default GalleryImageModel;","import ElementModel from './ElementModel';\nimport GalleryImageModel from './GalleryImageModel';\n\nclass GalleryModel extends ElementModel {\n\t\n\tget images(){\n\t\treturn this.get>( 'images' );\n\t}\n\t\n\tset images( images:GalleryImageModel[] ){\n\t\tthis.set( 'images', images );\n\t}\n}\n\nexport default GalleryModel;","import ElementModel from '../models/ElementModel';\nimport Element from './Element';\nimport ListRenderer from 'ln/list/ListRenderer';\nimport { ioc } from './Chapter';\n\n/**\n * A class that renders other elements within its content\n */\nclass Section extends Element {\n\n\tdata:ElementModel;\n\t/**\n\t * Sets the configuration for an element\n\t * @param model ElementModel\n\t */\n\tconstructor( model:ElementModel ) {\n\t\tsuper( model );\n\t}\n\t\n\tpublic init() {\n\t\t\n\t\tvar elements = new ListRenderer( this.node.js( 'elements' ) );\n\t\telements.ioc = ioc;\n\t\telements.source.fill( this.data.get( 'content', [] ) );\n\t}\n\n}\n\nexport default Section;","// import project\nimport Node from 'ln/node/Node';\nimport Element from './Element';\nimport LernBuch from '../LernBuch';\nimport GalleryModel from '../models/GalleryModel';\nimport GalleryImageModel from '../models/GalleryImageModel' \nimport ElementModel from '../models/ElementModel';\nimport TreeBuilder from '../navigation/chapter/tree/TreeBuilder';\nimport TreeNode from 'ln/tree/TreeNode';\nimport TemplateRenderer from 'ln/template/TemplateRenderer';\nimport TemplateManager from 'ln/template/TemplateManager';\nimport View from 'ln/view/View';\nimport ChapterModel from 'lb/models/ChapterModel';\n\ninterface ToCModel extends ElementModel {\n\tlevel?:number,\n\tmodelNames?:string[],\n\titemTemplate?:string,\n}\n\n/**\n * An element that renders a gallery\n */\nclass ToC extends Element {\n\n\tpublic model:ToCModel;\n\n constructor( model:ToCModel ) {\n\t\tsuper( model );\n\t\t\n\t\tthis.defaultTemplate = this.model.get( 'template', 'lb.toc-element' );\n }\n\n\t/**\n\t * gallery specific rendering\n\t */\n public init() {\n\t\tsuper.init();\n\n\t\tvar builder = new TreeBuilder();\n\t\tvar tree = builder.convert( this.model.chapter.book.chapters, { level: parseInt( this.model.get( 'level', 2 ) ), modelNames: this.model.get( 'modelNames', ['App\\\\Title'] ) } );\n\t\tthis.renderLevel( tree, this.node.js( 'entries' ), 0 );\n\t}\n\n\tprotected onToCItem( element:ElementModel|ChapterModel ) {\n\t\tif( element instanceof ChapterModel ) {\n\t\t\tthis.lernbuch.show( element );\n\t\t} else {\n\t\t\tthis.lernbuch.show( element.chapter, element );\n\t\t}\n\t}\n\t\n\tprotected renderLevel( trees:TreeNode[], target:Node, level ) {\n\n\t\ttrees.forEach( function( item ) {\n\n\t\t\tvar tocitem = new ToCItem( { element: item.element, level: level }, this.model.get( 'itemTemplate', 'lb.toc-element-item' ) );\n\t\t\ttocitem.render();\n\t\t\ttocitem.registerOn( this.onToCItem.bind( this ) );\n\t\t\ttarget.append( tocitem.node );\n\n\t\t\t// recursion\n\t\t\tif( item.children.length > 0 ) this.renderLevel( item.children, tocitem.node.js( 'children' ), level + 1 );\n\t\t}.bind( this ));\n\t}\n}\n\nexport default ToC;\n\n\nclass ToCItem extends View {\n\n\tpublic data:{element:ElementModel|ChapterModel, level:number };\n\n\tconstructor( data:{element:ElementModel|ChapterModel, level:number }, template:string ) {\n\t\tsuper( data );\n\t\tthis.defaultTemplate = template;\n\t}\n\n\trenderData() {\n\t\treturn { element: this.data.element.object(), level: this.data.level };\n\t}\n\n\tregisterOn( func ) {\n\t\tthis.node.js( \"click\" ).click.add( () => {\n\t\t\tfunc( this.data.element );\n\t\t});\n\t}\n}\n\n","import Node from 'ln/node/Node';\nimport List from 'ln/list/List';\nimport LinkedList from 'ln/linkedlist/LinkedList';\nimport { scanner } from 'ln/view/Scanner';\nimport { mapper } from '../LernBuch';\nimport { ioc as chapterIoC } from '../elements/Chapter';\nimport LernBuch from '../LernBuch';\nimport Element from '../elements/Element';\nimport Gallery from '../elements/Gallery';\nimport ChapterNavigation from '../navigation/chapter/Navigation';\nimport OutlineNavigation from '../navigation/outline/OutlineNavigation';\nimport PagingNavigation from '../navigation/paging/PagingNavigation';\nimport SearchInput from '../search/SearchInput';\nimport SearchOutput from '../search/SearchOutput';\nimport ShareDialog from '../share/ShareDialog';\nimport BookModel from '../models/BookModel';\nimport ChapterModel from '../models/ChapterModel';\nimport ElementModel from '../models/ElementModel';\nimport GalleryImageModel from '../models/GalleryImageModel';\nimport GalleryModel from '../models/GalleryModel';\nimport Section from '../elements/Section';\nimport ToC from '../elements/ToC';\n\nexport function defaultsetup(){\n\n models();\n lernbuch();\n chapter();\n\n};\n\nexport function models(){\n\n // Book\n mapper.toModel.add( 'Book', function ( json ) {\n var book = new BookModel( json );\n\n book.chapters.forEach( function( chapter:ChapterModel ){\n chapter.set( 'parent', book );\n }, this );\n\n LinkedList.fromArray( book.chapters );\n \n return book\n\t});\n mapper.toModel.alias( 'App\\\\Book', 'Book' );\n\n // Chapter\n mapper.toModel.add( 'Chapter', function( json:{ elements:any} ){\n var chapter = new ChapterModel( json );\n \n chapter.elements = new List( json.elements );\n chapter.elements.all().forEach( function( element ){\n element.set( 'chapter', chapter );\n }, this );\n \n return chapter;\n });\n\n mapper.toModel.alias( 'App\\\\Chapter', 'Chapter' );\n\n // Gallery\n mapper.toModel.add( 'Gallery', function( json ) {\n return new GalleryModel( json );\n });\n mapper.toModel.alias( 'App\\\\Gallery', 'Gallery' );\n\n // GalleryImage\n mapper.toModel.add( 'GalleryImage', function( json ) {\n return new GalleryImageModel( json );\n });\n mapper.toModel.alias( 'App\\\\GalleryImage', 'GalleryImage' );\n\n // Image\n mapper.toModel.add( 'Image', function( json ) {\n return new ElementModel( json );\n });\n mapper.toModel.alias( 'App\\\\Image', 'Image' );\n\n // default\n mapper.toModel.add( 'default', function( json ) {\n var element = new ElementModel( json );\n return element;\n });\n\n}\n\nexport function lernbuch(){\n\n // Navigation\n scanner.ioc.add( 'navigation', function( node:Node, lernbuch:LernBuch ) {\n var navi = new ChapterNavigation( lernbuch ).render( node ) as ChapterNavigation;\n navi.renderChapters( lernbuch.book.chapters, { modelNames: [ 'Title', 'Image', 'Gallery' ], level: 3 } );\n return navi;\n });\n \n // Outline Navigation\n scanner.ioc.add( 'outline-navigation', function( node:Node, lernbuch:LernBuch ) {\n\t\treturn new OutlineNavigation( lernbuch ).render( node );\n });\n \n // Next-Previous-Navigation\n scanner.ioc.add( 'paging-navigation', function( node:Node, lernbuch:LernBuch ) {\n\t\treturn new PagingNavigation( lernbuch ).render( node );\n });\n \n // Search\n scanner.ioc.add( 'search-input', function( node:Node, lernbuch:LernBuch ) {\n return new SearchInput( lernbuch.book ).render( node );\n });\n\n // Search\n scanner.ioc.add( 'search-output', function( node:Node, lernbuch:LernBuch ) {\n var output = new SearchOutput( lernbuch ).render( node ) as SearchOutput;\n output.listenTo( lernbuch.searchInput );\n return output;\n });\n\n // Share Dialog\n scanner.ioc.add( 'share-dialog', function( node:Node, lernbuch:LernBuch ){\n return new ShareDialog( lernbuch ).render( node );\n })\n\n}\n\nexport function chapter(){\n \n // default\n chapterIoC.add( 'default', function ( model:ElementModel ) {\n return new Element( model ).render() as Element;\n });\n \n // Gallery\n chapterIoC.add( 'Gallery', function( model:ElementModel ) {\n return new Gallery( model ).render() as Element;\n });\n chapterIoC.alias( 'App\\\\Gallery', 'Gallery' );\n \n // Section\n chapterIoC.add( 'Section', function( model:ElementModel ) {\n return new Section( model ).render() as Element;\n });\n chapterIoC.alias( 'App\\\\Section', 'Section' );\n\n // ToC\n chapterIoC.add( 'ToC', function( model:ElementModel ) {\n return new ToC( model ).render() as Element;\n });\n chapterIoC.alias( 'App\\\\ToC', 'ToC' );\n}\n","import IoC from 'ln/ioc/IoC';\nimport Node from 'ln/node/Node';\nimport Template from 'ln/template/TemplateManager';\n\nimport setup, { SetupConfig } from 'ln/setup/setup';\nimport View from 'ln/view/View';\nimport ListRenderer from 'ln/list/ListRenderer';\nimport List from 'ln/list/List';\nimport SlideModel from 'lf/slides/Slide/SlideModel';\n\n\nimport Element from 'lb/elements/Element'\n\nexport class ImageView extends Element {\n\n readonly model;\n scrollPosition: number;\n\n constructor(model) {\n super( model );\n \n }\n\n init() {\n this.scrollPosition = 0;\n\n if (this.node.js('lightbox')) {\n this.node.js('open').click.add( function() {\n this.scrollPosition = window.scrollY;\n this.node.one('.lightbox').addClass('-open');\n document.body.style.position = 'fixed';\n //this.node.one('.image').setAttribute('src', Template.context.imagePath(this.data.image.id, 'large') )\n }.bind(this));\n\n this.node.js('close').click.add( function() {\n this.node.one('.lightbox').removeClass('-open');\n document.body.style.position = 'static';\n window.scroll(0, this.scrollPosition);\n //this.node.one('.image').setAttribute('src', Template.context.imagePath(this.data.image.id, 'medium') )\n }.bind(this));\n\n }\n }\n\n}","import IoC from 'ln/ioc/IoC';\nimport Node from 'ln/node/Node';\nimport Template from 'ln/template/TemplateManager';\n\nimport setup, { SetupConfig } from 'ln/setup/setup';\nimport View from 'ln/view/View';\nimport ListRenderer from 'ln/list/ListRenderer';\nimport List from 'ln/list/List';\nimport SlideModel from 'lf/slides/Slide/SlideModel';\n\nimport {ImageView} from './ImageView';\n\nimport Element from 'lb/elements/Element';\n\nexport class CarouselView extends Element {\n\n readonly model;\n scrollPosition: number;\n lightboxes:Node[];\n position: number\n step: number;\n\n constructor(model) {\n super( model );\n }\n\n init() {\n this.data.scrollPosition = 0;\n this.data.position = 0;\n this.onImageLoaded();\n this.node.js('right').click.add( this.right.bind(this));\n this.node.js('left').click.add( this.left.bind(this) );\n\n this.node.js('close').click.add( function() {\n this.node.removeClass('-lightbox')\n document.body.style.position = 'static';\n window.scroll(0, this.data.scrollPosition);\n }.bind(this));\n\n }\n\n registerLightBox(element) {\n element.js('open').click.add( function(e) {\n this.data.scrollPosition = window.scrollY;\n document.body.style.position = 'fixed';\n this.node.addClass('-lightbox')\n }.bind(this));\n\n\n }\n\n onImageLoaded() {\n let all = this.node.all('img');\n let counter = all.length;\n\n all.forEach( function (image) {\n image.native.addEventListener('load', (e)=> {\n counter--;\n if (counter === 0 ) {\n this.data.lightboxes = this.node.all('.lightbox');\n \n\n // window.addEventListener('resize', ()=> {\n // // this.initCarouselSize();\n // });\n\n // //todo: this is probably not the best idea..\n // Node.one('.button-teacher').click.add( ( node )=>{\n // // this.initCarouselSize();\n // })\n\n this.data.lightboxes.forEach( function(element) {\n if (element.parent().js('lightbox') ) {\n this.registerLightBox( element );\n }\n }.bind(this))\n\n }\n })\n }.bind(this))\n }\n\n checkNavigation() {\n this.node.js('left').parent().style.display = this.data.position > 0 ? 'block' : 'none';\n this.node.js('right').parent().style.display = this.data.lightboxes.length > ( this.data.position + 1) ? 'block' : 'none';\n }\n\n left() {\n this.data.position = this.data.position - 1;\n this.updatePosition();\n }\n\n right() {\n this.data.position = this.data.position + 1;\n this.updatePosition();\n }\n\n // initLightboxSize() {\n // this.data.step = 100;\n // this.updatePosition();\n // }\n\n // initCarouselSize() {\n // //this.data.step = this.node.one('.lightbox').native.getBoundingClientRect().width / Math.min(document.documentElement.clientWidth || 0, window.innerWidth || 0) * 100;\n // //this.data.step = this.data.position*100;\n // this.updatePosition();\n // }\n \n updatePosition() {\n this.node.one('.inner-stage').style.left = -( this.data.position * 100) + '%';\n this.checkNavigation();\n }\n\n\n}","import Node from 'ln/node/Node';\nimport Element from 'lb/elements/Element';\nimport Model from 'ln/model/Model';\n\nexport class ParagraphModel extends Model {\n \n readonly model;\n text: string;\n glossaries: any;\n\n constructor(json) {\n super(json)\n\n this.glossaries.forEach( (glossary) => {\n if (this.text.indexOf( glossary.term) > -1) {\n let replacement = '' + glossary.term +'';\n this.text = this.text.replace(glossary.term, replacement)\n }\n })\n }\n}\n\nexport class ParagraphView extends Element {\n\n constructor(model) {\n super( model );\n } \n\n init() {\n this.node.all('.abbreviation').forEach( (node, index) => {\n \n let currentGlossar = this.data.glossaries.find( (e) => e.term === node.native.outerText )\n let tooltip = Node.fromHTML('
'+ currentGlossar.definition +'
' )\n\n // calculate the text-width (probably better calculate the number of letters...)\n let textWidth = this.getTextWidth(currentGlossar.definition.replace(/(<([^>]+)>)/gi, \"\") , \"300 14px futura-pt\"); // close to 86\n \n node.click.add( (e) => {\n //disable tooltip-click on desktop\n if (document.documentElement.clientWidth >= 1024) {\n return\n }\n\n e.toggleClass('-open')\n });\n node.append(tooltip)\n\n //let tooltipBounds = tooltip.native.getBoundingClientRect();\n\n //position it where most space is avaiable\n node.native.addEventListener('mouseenter', (e)=> {\n node.native.className = 'abbreviation';\n //check if sidebar is open\n let sidebar = document.querySelector('.content.-sidebar .sidebar-teacher');\n\n let space = sidebar ? (document.documentElement.clientWidth - sidebar.clientWidth) / 2 : document.documentElement.clientWidth / 2;\n \n let position = '';\n\n if (space < textWidth && !sidebar ) {\n position += ' -big';\n }\n\n if (space < textWidth && sidebar ) {\n position += ' -sidebar';\n }\n\n\n if (space > e.y) {\n position += ' -bottom';\n }\n else {\n position += ' -top';\n }\n if (space > e.x) {\n position += ' -right'\n }\n else {\n position += ' -left'\n }\n\n node.addClass(position);\n });\n })\n\n //close tooltip when clicked outside\n window.addEventListener('click', (e)=> {\n if (this.node.native.contains(e.target) ) {\n return\n } \n\n this.node.all('.abbreviation').forEach( (node, index) => {\n node.removeClass('-open')\n });\n })\n }\n\n /**\n * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.\n * \n * @param {String} text The text to be rendered.\n * @param {String} font The css font descriptor that text is to be rendered with (e.g. \"bold 14px verdana\").\n * \n * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393\n */\n getTextWidth(text, font) {\n // re-use canvas object for better performance\n const canvas = this.data.canvas || (this.data.canvas = document.createElement(\"canvas\"));\n const context = canvas.getContext(\"2d\");\n context.font = font;\n const metrics = context.measureText(text);\n return metrics.width;\n }\n \n getCssStyle(element, prop) {\n return window.getComputedStyle(element, null).getPropertyValue(prop);\n }\n \n getCanvasFontSize(el = document.body) {\n const fontWeight = this.getCssStyle(el, 'font-weight') || 'normal';\n const fontSize = this.getCssStyle(el, 'font-size') || '16px';\n const fontFamily = this.getCssStyle(el, 'font-family') || 'Times New Roman';\n \n return `${fontWeight} ${fontSize} ${fontFamily}`;\n }\n \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\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