 WizardBody = rclass
     displayName : 'WizardBody'
     propTypes:
-        actions  : rtypes.object
-        data     : rtypes.object
-        lang     : rtypes.string.isRequired
-        code     : rtypes.string
-        descr    : rtypes.string
-        cat0     : rtypes.string
-        cat1     : rtypes.string
-        cat2     : rtypes.number
-        catlist0 : rtypes.arrayOf(rtypes.string)
-        catlist1 : rtypes.arrayOf(rtypes.string)
-        catlist2 : rtypes.arrayOf(rtypes.string)
+        actions    : rtypes.object
+        data       : rtypes.object
+        lang       : rtypes.string.isRequired
+        code       : rtypes.string
+        descr      : rtypes.string
+        cat0       : rtypes.string
+        cat1       : rtypes.string
+        cat2       : rtypes.number
+        catlist0   : rtypes.arrayOf(rtypes.string)
+        catlist1   : rtypes.arrayOf(rtypes.string)
+        catlist2   : rtypes.arrayOf(rtypes.string)
+        search_str : rtypes.string
+        search_sel : rtypes.number
+        hits       : rtypes.arrayOf(rtypes.array)
+
+    componentWillMount: ->
+        @scrollTo0 = _.debounce (() -> $(@refs.list_0).find('.active').scrollintoview()), 50
+        @scrollTo1 = _.debounce (() -> $(@refs.list_1).find('.active').scrollintoview()), 50
+        @scrollTo2 = _.debounce (() -> $(@refs.list_2).find('.active').scrollintoview()), 50
+
+    componentDidUpdate: (props, state) ->
+        @scrollTo0()
+        @scrollTo1()
+        @scrollTo2()
+
     category_selection: (level, selected, idx) ->
         @props.actions.set_selected_category(level, selected, idx)
+
     category_list: (level) ->
         cat  = @props["cat#{level}"]
         list = @props["catlist#{level}"]
         if not list?
             list = []
         # don't use ListGroup & ListGroupItem with onClick, because then there are div/buttons (instead of ul/li) and layout is f'up
-        <ul className='list-group'>
+        <ul className='list-group' ref="list_#{level}">
             {list.map (name, idx) =>
-                click  = @category_selection.bind(this, level, name, idx)
+                click  = @category_selection.bind(@, level, name, idx)
                 # level 0 and 1 by name, level 2 by index
                 comp   = if level == 2 then idx else name
                 active = if comp == cat then 'active' else ''
-                <li className={"list-group-item " + active} onClick={click} key={idx} ref="cat_#{level}_#{idx}">{name}</li>
+                <li className={"list-group-item " + active} onClick={click} key={idx}>{name}</li>
             }
         </ul>
+
+    search_result_selection: (lvl1, lvl2, lvl3, idx) ->
+        @props.actions.search_selected(lvl1, lvl2, lvl3, idx)
+
+    render_search_results : ->
+        ss = @props.search_str
+        <ul className='list-group' ref="search_results">
+            {@props.hits.map (hit, idx) =>
+                [lvl1, lvl2, lvl3, title, descr, inDescr] = hit
+                click = @search_result_selection.bind(@, lvl1, lvl2, lvl3, idx)
+                title_hl = title.replace(new RegExp(ss, "gi"), "<span class='hl'>#{ss}</span>")
+                if inDescr != -1
+                    i = Math.max(0, inDescr-30)
+                    j = Math.min(descr.length, inDescr+30+ss.length)
+                    t = descr[inDescr...inDescr+ss.length]
+                    snippet = descr[i..j].replace(new RegExp(ss, "gi"), "<span class='hl'>#{t}</span>")
+                    if i > 0
+                        snippet = '...' + snippet
+                    if j < descr.length
+                        snippet = snippet + '...'
+                active = if @props.search_sel == idx then 'active' else ''
+                <li className={"list-group-item " + active} onClick={click} key={idx}>
+                    {lvl1} → {lvl2} → <span style={fontWeight: 'bold'} dangerouslySetInnerHTML={__html : title_hl}></span>
+                    {' '}{<span style={color:'#aaa'} dangerouslySetInnerHTML={__html : snippet}></span> if snippet?.length > 0}
+                </li>
+            }
+        </ul>
+    render_top : ->
+        searching = @props.search_str?.length > 0
+        if searching
+            <Row>
+                <Col sm={12}>{@render_search_results()}</Col>
+            </Row>
+        else
+            <Row>
+                <Col sm={3}>{@category_list(0)}</Col>
+                <Col sm={3}>{@category_list(1)}</Col>
+                <Col sm={6}>{@category_list(2)}</Col>
+            </Row>
     render : ->
         if @props.data?
             <Modal.Body className='modal-body'>
-                <Row>
-                    <Col sm={3}>{@category_list(0)}</Col>
-                    <Col sm={3}>{@category_list(1)}</Col>
-                    <Col sm={6}>{@category_list(2)}</Col>
-                </Row>
+                {@render_top()}
                 <Row>
                     <Col sm={6}>
                         <pre ref='code' className='code'>{@props.code}</pre>

                        catlist0   = {@props.catlist0}
                        catlist1   = {@props.catlist1}
                        catlist2   = {@props.catlist2}
                        data       = {@props.data} />

            <Modal.Footer>

                       type='text'
                       className='smc-wizard-search'
                       placeholder='Search'
                       value={@props.search_str}
                       onChange={@search}  />
            </Col>
        </Row>
