# File raggle, line 4334
        def initialize(opts)
          @opts = opts

          # grab web UI root
          root = $config['web_ui_root_path'],
          # if it doesn't exist, then eset it to the data directory
          
          s = WEBrick::HTTPServer::new(
            :BindAddress  => @opts['bind_address'],
            :Port         => @opts['port'],
            :DocumentRoot => $config['web_ui_root_path'],
            :Logger       => WEBrick::Log::new($config['web_ui_log_path'])
          )

          # load templates
          puts "Loading templates from \"#{$config['web_ui_root_path']}/inc\"."
          @templates = Hash::new
          %w{feed feed_list item item_list desc feed_image quit}.each { |tmpl|
            path = $config['web_ui_root_path'] + "/inc/#{tmpl}.html"
            @templates[tmpl] = File::open(path).readlines.join if test(?e, path)
          }

          s.mount_proc('/') { |req, res| res['Location'] = '/index.html' }

          # set up mount points
          s.mount('/', WEBrick::HTTPServlet::FileHandler,
                  $config['web_ui_root_path'], true)

          # set up quit mount point
          s.mount_proc('/raggle/quit') { |req, res|
            quit_thread = Thread::new {
              $http_server.shutdown
              sleep opts['shutdown_sleep'];
              $feed_thread.kill if $feed_thread && $feed_thread.alive?
              $http_thread.kill if $http_thread && $http_thread.alive?
            }

            res['Content-Type'] = 'text/html'

            # handle quit template
            ret = @templates['quit'].dup
            { 'VERSION'   => $VERSION,
            }.each { |key, val|
              ret.gsub!(/__#{key}__/, val) if ret =~ /__#{key}__/
            }

            res.body = ret
          }

          # set up feed list mount point
          s.mount_proc('/raggle/feed') { |req, res|
            body = @templates['feed_list'].dup
            ret = ''

            mode = 'default'
            mode = $1 if req.query_string =~ /mode=([^&]+)/

            cat = 'all'
            cat = $1 if req.query_string =~ /category=([^&]+)/

            case mode
              when 'sort'
                Engine::sort_feeds
              when 'invalidate_all'
                Engine::invalidate_feed(-1)
                $feed_thread.run
              when 'add'
                url = $1 if req.query_string =~ /url=(.*)$/
                added = Engine::add_feed({ 'url' => url })
                $feed_thread.run if added and $config['update_after_add']
              when 'delete'
                if req.query_string =~ /id=(\d+)/
                  id = $1.to_i
                  Engine::delete_feed(id)
                end
              when 'invalidate'
                if req.query_string =~ /id=(\d+)/
                  id = $1.to_i
                  Engine::invalidate_feed(id)
                  $feed_thread.run 
                end
              when 'save_feed_list'
                Engine::save_feed_list
            end
              
            # build feed list title
            list_title = 'Feeds ' << ((cat && cat !~ /\ball\b/i) ? "(#{cat})" : '')

            # build each feed title
            $config['feeds'].category(cat).each_with_index { |feed, i|
              str = @templates['feed'].dup
              title = feed['title'] || $config['default_feed_title']
              
              # count unread items
              unread_count = 0
              feed['items'].each { |item| unread_count += 1 unless item['read?'] }

              # convert updated to minutes
              updated = (Time.now.to_i - feed['updated'] / 60).to_i

              # handle feed list item template
              { 'TITLE'         => title || 'Untitled Feed',
                'URL'           => feed['url'],
                'ITEM_COUNT'    => feed['items'].size.to_s,
                'UNREAD_COUNT'  => unread_count.to_s,
                'INDEX'         => i.to_s,
                'READ'          => ((unread_count == 0) ? 'read' : 'unread'),
                'EVEN'          => ((i % 2 == 0) ? 'even' : 'odd'),
                'UPDATED'       => updated,
                'CATEGORY'      => cat,
              }.each { |key, val|
                str.gsub!(/__#{key}__/, val) if str =~ /__#{key}__/
              }
              ret << str
            }

            # handle feed list template
            { 'FEEDS'           => ret,
              'REFRESH'         => opts['page_refresh'].to_s, 
              'VERSION'         => $VERSION,
              'CATEGORY'        => cat,
              'FEED_LIST_TITLE' => list_title,
              'CATEGORY_SELECT' => build_category_select(cat),
            }.each { |key, val|
              body.gsub!(/__#{key}__/, val) if body =~ /__#{key}__/
            }
            
            res['Content-Type'] = 'text/html'
            res.body = body
          }

          # set up feed item list mount point
          s.mount_proc('/raggle/item') { |req, res|
            body = @templates['item_list'].dup
            ret = ''

            feed_id = 0
            feed_id = $1.to_i if req.query_string =~ /feed_id=(\d+)/

            cat = 'all'
            cat = $1 if req.query_string =~ /category=([^&]+)/

            feed = $config['feeds'].category(cat)[feed_id]

            # get feed image
            feed_image = build_feed_image(feed)

            # convert updated to minutes
            updated = ((Time.now.to_i - feed['updated']) / 60).to_i

            # build each feed item title
            feed['items'].each_with_index { |item, i|
              str = @templates['item'].dup
              title = item['title']
              
              # handle item template
              { 'TITLE'         => title,
                'URL'           => item['url'],
                'DATE'          => item['date'].to_s,
                'EVEN'          => ((i % 2 == 0) ? 'even' : 'odd'),
                'READ'          => item['read?'] ? 'read' : 'unread',
                'FEED_ID'       => feed_id.to_s,
                'INDEX'         => i.to_s,
                'CATEGORY'      => cat,
              }.each { |key, val|
                str.gsub!(/__#{key}__/, val) if str =~ /__#{key}__/
              }
              ret << str
            }

            # handle item list template
            { 'ITEMS'           => ret,
              'FEED_ID'         => feed_id.to_s,
              'FEED_TITLE'      => feed['title'] || 'Untitled Feed',
              'FEED_SITE'       => feed['site'] || '',
              'FEED_URL'        => feed['url'],
              'FEED_DESC'       => feed['desc'] || '',
              'FEED_UPDATED'    => updated.to_s,
              'FEED_IMAGE'      => feed_image,
              'REFRESH'         => opts['page_refresh'].to_s,
              'VERSION'         => $VERSION,
              'CATEGORY'        => cat,
            }.each { |key, val|
              body.gsub!(/__#{key}__/, val) if body =~ /__#{key}__/
            }
            
            res['Content-Type'] = 'text/html'
            res.body = body
          }

          # set up desc window moutn point
          s.mount_proc('/raggle/desc') { |req, res|
            body = @templates['desc'].dup
            ret = ''

            feed_id = 0
            item_id = 0
            cat = 'all'
            feed_id = $1.to_i if req.query_string =~ /feed_id=([-\d]+)/
            item_id = $1.to_i if req.query_string =~ /item_id=([-\d]+)/
            cat = $1 if req.query_string =~ /category=([^&]+)/

            # get feed image
            feed = $config['feeds'].category(cat)[feed_id]
            feed_image = build_feed_image(feed)

            if item_id == -1
              title = feed['title']
              body = "description of feed #{title}"
            else 
              item = feed['items'][item_id] || opts['empty_item']
              title = item['title'] || 'Untitled Item'
              item['read?'] = true
              
              { 'TITLE'         => title,
                'DATE'          => item['date'].to_s,
                'READ'          => item['read?'] ? 'read' : 'unread',
                'URL'           => item['url'], 
                'FEED_IMAGE'    => feed_image,
                'DESC'          => item['desc'],
                'REFRESH'       => opts['refresh'], 
                'CATEGORY'      => cat,
              }.each { |key, val|
                body.gsub!(/__#{key}__/, val) if body =~ /__#{key}__/
              }
            end

            res['Content-Type'] = 'text/html'
            res.body = body
          }

          @server = s
        end