/*
RSSController.m

Author: Makoto Kinoshita

Copyright 2004-2006 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "SRSearchEngine.h"

#import "SRAppController.h"
#import "SRBrowserController.h"
#import "SRSearchFieldController.h"
#import "SRPageController.h"

#import "SRWebViewContextMenu.h"

#import "SRPrefDefaultKeys.h"

#import "RSSFeedParser.h"
#import "RSSController.h"
#import "RSSDefaultKeys.h"
#import "RSSManager.h"
#import "RSSPersistentStack.h"

NSString*   RSSArticlesRead = @"RSSArticlesRead";

static NSArray* _templateKeys = nil;

@interface RSSController (private)

// Contents HTML
- (NSString*)_createHTMLWithItem:(id)item;
- (NSString*)_createHTMLWithFeed:(id)feed;

@end

@implementation RSSController

//--------------------------------------------------------------//
#pragma mark -- Initialize --
//--------------------------------------------------------------//

- (id)init
{
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // Load nib
    if (![NSBundle loadNibNamed:@"RSSView" owner:self]) {
        // Error
        NSLog(@"Can't load RSSView.nib");
        return nil;
    }
    
    // Create template keys
    if (!_templateKeys) {
        _templateKeys = [[NSArray arrayWithObjects:
                @"$ENTRY_TITLE$", @"$ENTRY_LINK$", @"$ENTRY_DATE$", @"$ENTRY_CONTENT$", nil] retain];
    }
        
    // Register observer
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(rssPersistentStackRefreshed:) 
            name:RSSPersistentStackRefreshed object:[RSSPersistentStack sharedInstance]];
    
    return self;
}

- (void)awakeFromNib
{
    NSRect  frame;
    
    // Get bar frame
    NSRect  barFrame;
    barFrame = [_rssBar frame];
    
    // Create buttons
    frame.origin.x = barFrame.size.width - 56;
    frame.origin.y = 4;
    frame.size = NSMakeSize(25, 19);
    _tableButton = [[NSClassFromString(@"HMButton") alloc] initWithFrame:frame];
    [_tableButton setButtonType:NSMomentaryChangeButton];
    [_tableButton setBezelStyle:NSRegularSquareBezelStyle];
    [_tableButton setAutoresizingMask:NSViewMinXMargin];
    [_tableButton setBordered:NO];
    [_tableButton setImage:[NSImage imageNamed:@"shelfListBrowser"]];
    [_tableButton setSelectedImage:[NSImage imageNamed:@"shelfPressedBackground"]];
    [_tableButton setTarget:self];
    [_tableButton setAction:@selector(selectDisplayAction:)];
    [_rssBar addSubview:_tableButton];
    
    frame.origin.x = barFrame.size.width - 29;
    frame.origin.y = 4;
    frame.size = NSMakeSize(25, 19);
    _listButton = [[NSClassFromString(@"HMButton") alloc] initWithFrame:frame];
    [_listButton setButtonType:NSMomentaryChangeButton];
    [_listButton setBezelStyle:NSRegularSquareBezelStyle];
    [_listButton setAutoresizingMask:NSViewMinXMargin];
    [_listButton setBordered:NO];
    [_listButton setImage:[NSImage imageNamed:@"shelfTableView"]];
    [_listButton setSelectedImage:[NSImage imageNamed:@"shelfPressedBackground"]];
    [_listButton setTarget:self];
    [_listButton setAction:@selector(selectDisplayAction:)];
    [_rssBar addSubview:_listButton];
    
    // Configure list view
    [_listSplitView setSplitType:HMSplitWide];
    
    // Configure array controller
    NSSortDescriptor*   sortDescriptor;
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
    [_itemArrayController setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];
    
    // Configure table view
    NSTableColumn*          column;
    NSCell*                 oldCell;
    HMImageTextFieldCell*   cell;
    column = [_listTableView tableColumnWithIdentifier:@"title"];
    oldCell = [column dataCell];
    cell = [[HMImageTextFieldCell alloc] init];
    [cell setFont:[oldCell font]];
    [column setDataCell:cell];
    [cell release];
    
    column = [_listTableView tableColumnWithIdentifier:@"dateAndTime"];
    oldCell = [column dataCell];
    cell = [[HMImageTextFieldCell alloc] init];
    [cell setFont:[oldCell font]];
    [column setDataCell:cell];
    [cell release];
    
    // Set display style
    [self setDisplayStyle:
            [[NSUserDefaults standardUserDefaults] integerForKey:RSSDisplayStyle]];
}

- (void)dealloc
{
    [_urlString release], _urlString = nil;
    [_feed release], _feed = nil;
    [_listButton release], _listButton = nil;
    [_tableButton release], _tableButton = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- View --
//--------------------------------------------------------------//

- (NSView*)view
{
    return _view;
}

//--------------------------------------------------------------//
#pragma mark -- Display style --
//--------------------------------------------------------------//

- (void)_updateDisplayStyle
{
    // Get view frame
    NSRect  frame;
    frame = [_lowerView bounds];
    
    // Decide display style
    int displayStyle = RSSListStyle;
    if ([_listButton isSelected]) {
        displayStyle = RSSListStyle;
    }
    else if ([_tableButton isSelected]) {
        displayStyle = RSSTableStyle;
    }
    
    // For list
    if (displayStyle == RSSListStyle) {
        if ([_tableView superview] == _lowerView) {
            [_tableView removeFromSuperview];
        }
        
        [_listView setFrame:frame];
        [_lowerView addSubview:_listView];
    }
    // For table
    if (displayStyle == RSSTableStyle) {
        if ([_listView superview] == _lowerView) {
            [_listView removeFromSuperview];
        }
        
        [_tableView setFrame:frame];
        [_lowerView addSubview:_tableView];
        
        // Create HTML
        NSString*   html;
        html = [self _createHTMLWithFeed:_feed];
        if (!html) {
            html = @"";
        }
        
        // Load HTML
        NSString*   path;
        path = [HMApplicationSupportFolder(@"Shiira") 
                stringByAppendingPathComponent:@"template/tmp.html"];
        [[html dataUsingEncoding:NSUTF8StringEncoding] writeToFile:path atomically:YES];
        [[_tableWebView mainFrame] loadRequest:
                [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
        
        // Make feed articles previewed
        [[RSSManager sharedInstance] makeItemsPreviewedWithFeed:_feed];
    }
}

- (void)setDisplayStyle:(int)displayStyle
{
    // Select button
    switch (displayStyle) {
    case RSSListStyle: {
        [_listButton setSelected:YES];
        [_tableButton setSelected:NO];
        break;
    }
    case RSSTableStyle: {
        [_listButton setSelected:NO];
        [_tableButton setSelected:YES];
        break;
    }
    }
    
    // Update display style
    [self _updateDisplayStyle];
}

//--------------------------------------------------------------//
#pragma mark -- WebDataSource loading --
//--------------------------------------------------------------//

- (void)_loadFeedWithData:(NSData*)data URLString:(NSString*)urlString
{
    // Create XML document
    NSXMLDocument*  document;
    document = [[NSXMLDocument alloc] initWithData:data options:0 error:NULL];
    
    // Parse it
    RSSFeedParser*  parser;
    parser = [[RSSFeedParser alloc] 
            initWithXMLNode:[document rootElement] URLString:urlString];
    _feed = [parser parseWithManagedObjectContext:nil];
    [_feed retain];
    [parser release];
}

- (void)_loadFeedFromContextWithURLString:(NSString*)urlString
{
    // Swap shceme
    NSString*   feedURL = urlString;
    if ([feedURL hasPrefix:@"rss://"]) {
        feedURL = [NSString stringWithFormat:@"http://%@", 
                [feedURL substringFromIndex:6]];
    }
    
    // Get managed object context
    NSManagedObjectContext* context;
    context = [self managedObjectContext];
    
    // Get feed
    NSFetchRequest* request;
    request = [[NSFetchRequest alloc] init];
    [request autorelease];
    [request setEntity:
            [NSEntityDescription entityForName:@"RSSFeed" inManagedObjectContext:context]];
    [request setPredicate:
            [NSPredicate predicateWithFormat:@"feedURL == %@", feedURL]];
    
    NSArray*    feeds;
    feeds = [context executeFetchRequest:request error:NULL];
    if ([feeds count] > 0) {
        _feed = [feeds objectAtIndex:0];
        [_feed retain];
    }
}

- (void)finishedLoadingWithDataSource:(WebDataSource*)dataSource
{
    // Get URL
    [_urlString release], _urlString = nil;
    _urlString = [[[[dataSource request] URL] absoluteString] copy];
    
    // For http scheme
    if ([_urlString hasPrefix:@"http://"]) {
        // Get RSS data
        NSData* data;
        data = [dataSource data];
        
        // Load feed
        [self _loadFeedWithData:data URLString:_urlString];
    }
    // For rss scheme
    if ([_urlString hasPrefix:@"rss://"]) {
        // Load feed with RSS URL
        [self _loadFeedFromContextWithURLString:_urlString];
    }
    
    // Set title
    [_rssBar setTitle:[self title]];
    
    // Set contents
    [_itemArrayController setContent:[_feed valueForKey:@"items"]];
    
    // Update appearance
    [self _updateDisplayStyle];
}

//--------------------------------------------------------------//
#pragma mark -- Feed --
//--------------------------------------------------------------//

- (NSString*)title
{
    NSString*   title;
    title = [_feed valueForKey:@"title"];
    if ([title length] == 0) {
        title = NSLocalizedString(@"(Untitled)", nil);
    }
    
    return title;
}

- (NSString*)link
{
    return [_feed valueForKey:@"link"];
}

//--------------------------------------------------------------//
#pragma mark -- Persistent stack --
//--------------------------------------------------------------//

- (NSManagedObjectContext*)managedObjectContext
{
    return [[RSSPersistentStack sharedInstance] managedObjectContext];
}

//--------------------------------------------------------------//
#pragma mark -- Contents HTML --
//--------------------------------------------------------------//

- (NSString*)_valueForTemplateKey:(NSString*)key item:(id)item
{
    // title
    if ([key isEqualToString:@"$ENTRY_TITLE$"]) {
        return [item valueForKey:@"title"];
    }
    // link
    if ([key isEqualToString:@"$ENTRY_LINK$"]) {
        return [item valueForKey:@"link"];
    }
    // date
    if ([key isEqualToString:@"$ENTRY_DATE$"]) {
        return [[item valueForKey:@"date"] 
                descriptionWithCalendarFormat:@"%y/%m/%d %H:%M" 
                timeZone:nil 
                locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]];
    }
    // date
    if ([key isEqualToString:@"$ENTRY_DATE$"]) {
        return [[item valueForKey:@"date"] description];
    }
    // content
    if ([key isEqualToString:@"$ENTRY_CONTENT$"]) {
        return [item valueForKey:@"content"];
    }
    
    return nil;
}

- (NSString*)_createHTMLWithItem:(id)item
{
    // Check arguments
    if (!item) {
        return nil;
    }
    
    // Load template
    NSString*   resPath;
    NSString*   path;
    NSString*   entryTemplate;
    resPath = [[NSBundle mainBundle] resourcePath];
    path = [resPath stringByAppendingPathComponent:@"template/EntryTemplate.html"];
    entryTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
    if (!entryTemplate) {
        // Error
        NSLog(@"Can't load EntryTemplate.html");
        return nil;
    }
    
    // Create buffer
    NSMutableString*    html;
    html = [NSMutableString stringWithString:entryTemplate];
    
    // Replace keys
    NSEnumerator*   enumerator;
    NSString*       templateKey;
    enumerator = [_templateKeys objectEnumerator];
    while (templateKey = [enumerator nextObject]) {
        // Find in buffer
        if ([html rangeOfString:templateKey].location == NSNotFound) {
            continue;
        }
        
        // Get value for template key
        NSString*   value;
        value = [self _valueForTemplateKey:templateKey item:item];
        if (!value) {
            value = @"";
        }
        
        // Replace it
        [html replaceOccurrencesOfString:templateKey 
                withString:value options:0 range:NSMakeRange(0, [html length])];
    }
    
    return html;
}

- (NSString*)_createHTMLWithFeed:(id)feed
{
    // Check arguments
    if (!feed) {
        return nil;
    }
    
    // Load templates
    NSString*   resPath;
    NSString*   path;
    NSString*   feedTemplate;
    NSString*   feedEntryTemplate;
    NSString*   feedEntryAltTemplate;
    resPath = [[NSBundle mainBundle] resourcePath];
    path = [resPath stringByAppendingPathComponent:@"template/FeedTemplate.html"];
    feedTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
    if (!feedTemplate) {
        // Error
        NSLog(@"Can't load EntryTemplate.html");
        return nil;
    }
    
    path = [resPath stringByAppendingPathComponent:@"template/FeedEntryTemplate.html"];
    feedEntryTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
    if (!feedEntryTemplate) {
        feedEntryTemplate = @"";
    }
    
    path = [resPath stringByAppendingPathComponent:@"template/FeedEntryAltTemplate.html"];
    feedEntryAltTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
    if (!feedEntryAltTemplate) {
        feedEntryAltTemplate = @"";
    }
    
    // Create buffer
    NSMutableString*    html;
    html = [NSMutableString stringWithString:feedTemplate];
    
    //
    // Replace feed keys
    //
    NSEnumerator*   enumerator;
    NSString*       templateKey;
    enumerator = [_templateKeys objectEnumerator];
    while (templateKey = [enumerator nextObject]) {
        // Find in buffer
        if ([html rangeOfString:templateKey].location == NSNotFound) {
            continue;
        }
        
        // Get value for template key
        NSString*   value;
        value = [self _valueForTemplateKey:templateKey item:feed];
        if (!value) {
            value = @"";
        }
        
        // Replace it
        [html replaceOccurrencesOfString:templateKey 
                withString:value options:0 range:NSMakeRange(0, [html length])];
    }
    
    //
    // Create entries
    //
    
    // Get sorted items
    NSArray*            items;
    NSSortDescriptor*   sortDescriptor;
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
    items = [[feed valueForKey:@"items"] allObjects];
    items = [items sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    [sortDescriptor release];
    
    // Enumerate items
    NSMutableString*    entries;
    NSEnumerator*       itemEnumerator;
    id                  item;
    int                 count = 0;
    entries = [NSMutableString string];
    itemEnumerator = [items objectEnumerator];
    while (item = [itemEnumerator nextObject]) {
        // Copy template
        NSMutableString*    entry;
        if (count % 2 == 0) {
            entry = [NSMutableString stringWithString:feedEntryTemplate];
        }
        else {
            entry = [NSMutableString stringWithString:feedEntryAltTemplate];
        }
        count++;
        
        // Replace keys
        enumerator = [_templateKeys objectEnumerator];
        while (templateKey = [enumerator nextObject]) {
            // Find in buffer
            if ([entry rangeOfString:templateKey].location == NSNotFound) {
                continue;
            }
            
            // Get value for template key
            NSString*   value;
            value = [self _valueForTemplateKey:templateKey item:item];
            if (!value) {
                value = @"";
            }
            
            // Replace it
            [entry replaceOccurrencesOfString:templateKey 
                    withString:value options:0 range:NSMakeRange(0, [entry length])];
        }
        
        // Append entry
        [entries appendString:entry];
    }
    
    // Replace entry
    [html replaceOccurrencesOfString:@"$ENTRIES$" 
            withString:entries options:0 range:NSMakeRange(0, [html length])];
    
    return html;
}

//--------------------------------------------------------------//
#pragma mark -- Action --
//--------------------------------------------------------------//

- (void)selectDisplayAction:(id)sender
{
    // Set display style
    if (sender == _listButton) {
        [self setDisplayStyle:RSSListStyle];
        [[NSUserDefaults standardUserDefaults] 
                setInteger:RSSListStyle forKey:RSSDisplayStyle];
    }
    if (sender == _tableButton) {
        [self setDisplayStyle:RSSTableStyle];
        [[NSUserDefaults standardUserDefaults] 
                setInteger:RSSTableStyle forKey:RSSDisplayStyle];
    }
}

//--------------------------------------------------------------//
#pragma mark -- WebFrameLoadDelegate --
//--------------------------------------------------------------//

- (void)webView:(WebView*)webView 
        decidePolicyForNavigationAction:(NSDictionary*)info 
        request:(NSURLRequest*)request 
        frame:(WebFrame*)frame 
        decisionListener:(id<WebPolicyDecisionListener>)listener
{
    // Get navigation type
    WebNavigationType   navigationType;
    navigationType = [[info objectForKey:WebActionNavigationTypeKey] intValue];
    
    // For link click
    if (navigationType == WebNavigationTypeLinkClicked) {
        // Open in new tab by borwser controller
        SRBrowserController*    browserController;
        browserController = (SRBrowserController*)[[_view window] windowController];
        [browserController openInNewTabURL:[request URL]];
        
        [listener ignore];
        return;
    }
    
    // Use it
    [listener use];
}

//--------------------------------------------------------------//
#pragma mark -- WebUIDelegate --
//--------------------------------------------------------------//

- (NSResponder*)webViewFirstResponder:(WebView*)webView
{
    return [[webView window] firstResponder];
}

- (void)webView:(WebView*)webView 
        makeFirstResponder:(NSResponder*)responder
{
    // Make first responder
    [[webView window] makeFirstResponder:responder];
}

- (void)webView:(WebView*)webView 
        mouseDidMoveOverElement:(NSDictionary*)info 
        modifierFlags:(unsigned int)flags
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get browser controller
    SRBrowserController*    browserController;
    browserController = [[webView window] windowController];
    if (![browserController isKindOfClass:[SRBrowserController class]]) {
        return;
    }
    
    NSString*   status = nil;
    
    // Get URL string
    NSString*   URLString;
    URLString = [[info objectForKey:WebElementLinkURLKey] _web_userVisibleString];
    
    if (URLString) {
        // Modifier flag is specified
        if (flags) {
            // Check key mask
            unsigned int    cmdFlag, optionFlag, shiftFlag;
            cmdFlag = flags & NSCommandKeyMask;
            optionFlag = flags & NSAlternateKeyMask;
            shiftFlag = flags & NSShiftKeyMask;
            
            // Check tab browsing
            BOOL    selectNewTabs;
            selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
            
            // If select new tabs
            if (selectNewTabs) {
                // Open in new tabs and select it
                if (cmdFlag && !optionFlag && !shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new tab"), nil);
                    goto finalize;
                }
                // Open in new tabs
                else if (cmdFlag && !optionFlag && shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new background tab"), nil);
                    goto finalize;
                }
                // Open in new window
                else if (cmdFlag && optionFlag && !shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new window"), nil);
                    goto finalize;
                }
                // Open in new window in background
                else if (cmdFlag && optionFlag && shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new background window"), nil);
                    goto finalize;
                }
            }
            // If not select new tabs
            else {
                // Open in new tabs
                if (cmdFlag && !optionFlag && !shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new background tab"), nil);
                    goto finalize;
                }
                // Open in new tabs and select it
                else if (cmdFlag && !optionFlag && shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new tab"), nil);
                    goto finalize;
                }
                // Open in new window in background
                else if (cmdFlag && optionFlag && !shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new background window"), nil);
                    goto finalize;
                }
                // Open in new window
                else if (cmdFlag && optionFlag && shiftFlag) {
                    status = NSLocalizedString(UTF8STR("Open “%@” in a new window"), nil);
                    goto finalize;
                }
            }
        }
        
        status = NSLocalizedString(UTF8STR("Open “%@” in a new tab"), nil);
    }
        
finalize:
    if (status) {
        [[browserController browserContent] setValue:[NSString stringWithFormat:status, URLString]
                forKey:@"status"];
    }
    else {
        [[browserController browserContent] setValue:@"" forKey:@"status"];
    }
}

- (NSArray*)webView:(WebView*)webView 
        contextMenuItemsForElement:(NSDictionary*)element 
        defaultMenuItems:(NSArray*)defaultMenuItems
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Create array
    NSMutableArray* items;
    items = [NSMutableArray array];
    
    // Get browser controller
    SRBrowserController*    browserController;
    browserController = [[webView window] windowController];
    if (![browserController isKindOfClass:[SRBrowserController class]]) {
        return items;
    }
    
    // Get URL string
    NSString*   urlString;
    urlString = [[[[[webView mainFrame] dataSource] request] URL] absoluteString];
    
    // Get element info
    NSURL*      linkURL;
    NSURL*      imageURL;
    WebFrame*   webFrame;
    BOOL        isSelected;
    linkURL = [element objectForKey:WebElementLinkURLKey];
    imageURL = [element objectForKey:WebElementImageURLKey];
    webFrame = [element objectForKey:WebElementFrameKey];
    isSelected = [[element objectForKey:WebElementIsSelectedKey] boolValue];
    
    // Check tab browsing
    BOOL    selectNewTabs;
    selectNewTabs = [defaults boolForKey:SRTabSelectNewTabs];
    
    // Check full screen mode
    BOOL    isFullScreen;
    isFullScreen = [SRBrowserController isFullScreen];
    
    // Get web view context menu
    NSMenu*     menu;
    NSMenuItem* menuItem;
    NSMenuItem* altMenuItem;
    menu = [SRWebViewContextMenu contextMenu];
    
    // Get selected string
    NSString*   selectedString = nil;
    if (isSelected) {
        NSView* documentView;
        documentView = [[[element objectForKey:WebElementFrameKey] frameView] documentView];
        if ([documentView respondsToSelector:@selector(selectedString)]) {
            selectedString = [documentView performSelector:@selector(selectedString)];
        }
    }
    
    // Get selected links
    DOMRange*       range;
    DOMNode*        startContainer = nil;
    NSMutableArray* linkURLStrings;
    range = [webView selectedDOMRange];
    startContainer = [range startContainer];
    linkURLStrings = [NSMutableArray array];
    if (startContainer) {
        HMDOMRangeFilter*   filter;
        DOMNodeIterator*    iterator;
        DOMNode*            node;
        filter = [[HMDOMRangeFilter alloc] initWithDOMRange:range nodeName:@"A"];
        iterator = [[[webView mainFrame] DOMDocument] createNodeIterator:startContainer :DOM_SHOW_ALL :filter :NO];
        while (node = [iterator nextNode]) {
            // Get URL string
            NSString*   URLString = nil;
            if ([node respondsToSelector:@selector(href)]) {
                URLString = [(DOMHTMLLinkElement*)node href];
            }
            if (URLString) {
                // Check scheme
                NSString*   scheme;
                scheme = [[NSURL URLWithString:URLString] scheme];
                if (![scheme isEqualToString:@"javascript"]) {
                    [linkURLStrings addObject:URLString];
                }
            }
        }
        [filter release];
    }
    
    // When link is contained
    if (linkURL) {
        if ([WebView _canHandleRequest:[NSURLRequest requestWithURL:linkURL]] && !isFullScreen) {
            // 'Open Link in New Tab' and 'Open Link in Background Tab'
            if (selectNewTabs) {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewTabTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundTabTag, browserController);
            }
            else {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundTabTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewTabTag, browserController);
            }
            [menuItem setRepresentedObject:linkURL];
            [items addObject:menuItem];
            
            [altMenuItem setRepresentedObject:linkURL];
            [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
            [altMenuItem setAlternate:YES];
            [items addObject:altMenuItem];
            
            // 'Open Link in New Window' and 'Open Link in Background Window'
            if (selectNewTabs) {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewWindowTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundWindowTag, browserController);
            }
            else {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewBackgroundWindowTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenLinkInNewWindowTag, browserController);
            }
            [menuItem setRepresentedObject:linkURL];
            [items addObject:menuItem];
            
            [altMenuItem setRepresentedObject:linkURL];
            [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
            [altMenuItem setAlternate:YES];
            [items addObject:altMenuItem];
        }
    }
    
    // 'Open All Links in New Tabs' and 'Open All Links in Background Tabs'
    if ([linkURLStrings count] > 1 && !isFullScreen) {
        if (selectNewTabs) {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewTabsTag, browserController);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewBackgroundTabsTag, browserController);
        }
        else {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewBackgroundTabsTag, browserController);
            altMenuItem = HMCopyMenuItemWithTag(menu, SROpenAllLinksInNewTabsTag, browserController);
        }
        
        [menuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
        [menuItem setRepresentedObject:linkURLStrings];
        [items addObject:menuItem];
        
        [altMenuItem setTitle:[NSString stringWithFormat:[menuItem title], [linkURLStrings count]]];
        [altMenuItem setRepresentedObject:linkURLStrings];
        [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
        [altMenuItem setAlternate:YES];
        [items addObject:altMenuItem];
    }
    
    // When link is contained
    if (linkURL) {
        // 'Download Linked File' and 'Download Link File to Location'
        menuItem = HMCopyMenuItemWithTag(menu, SRDownloadLinkedFileTag, browserController);
        [menuItem setRepresentedObject:linkURL];
        [items addObject:menuItem];
        //menuItem = HMCopyMenuItemWithTag(menu, SRDownloadLinkedFileToLocationTag, browserController);
        //[menuItem setRepresentedObject:linkURL];
        //[items addObject:menuItem];
        
        // 'Add Link to Bookmarks'
        if (!isFullScreen) {
            menuItem = HMCopyMenuItemWithTag(menu, SRAddLinkToBookmarksTag, browserController);
            [menuItem setRepresentedObject:element];
            [items addObject:menuItem];
        }
        
        // 'Copy Link'
        menuItem = HMCopyMenuItemWithTag(menu, SRCopyLinkTag, browserController);
        [menuItem setRepresentedObject:element];
        [items addObject:menuItem];
    }
    
    // When image is contained
    if (imageURL) {
        // Add separator
        if ([items count] > 0) {
            [items addObject:[NSMenuItem separatorItem]];
        }
        
        if (!isFullScreen) {
            // 'Open Image in New Tab' and 'Open Image in Background Tab'
            if (selectNewTabs) {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewTabTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundTabTag, browserController);
            }
            else {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundTabTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewTabTag, browserController);
            }
            [menuItem setRepresentedObject:imageURL];
            [items addObject:menuItem];
            
            [altMenuItem setRepresentedObject:imageURL];
            [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
            [altMenuItem setAlternate:YES];
            [items addObject:altMenuItem];
            
            // 'Open Image in New Window' and 'Open Image in Background Window'
            if (selectNewTabs) {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewWindowTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundWindowTag, browserController);
            }
            else {
                menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewBackgroundWindowTag, browserController);
                altMenuItem = HMCopyMenuItemWithTag(menu, SROpenImageInNewWindowTag, browserController);
            }
            [menuItem setRepresentedObject:imageURL];
            [items addObject:menuItem];
            
            [altMenuItem setRepresentedObject:imageURL];
            [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
            [altMenuItem setAlternate:YES];
            [items addObject:altMenuItem];
        }
        
        // 'Download Image' and 'Download Image to Location'
        menuItem = HMCopyMenuItemWithTag(menu, SRDownloadImageTag, browserController);
        [menuItem setRepresentedObject:imageURL];
        [items addObject:menuItem];
        //menuItem = HMCopyMenuItemWithTag(menu, SRDownloadImageToLocationTag, nil);
        //[items addObject:menuItem];
        
        // 'Copy Image to Clipboard' and 'Copy Image Location to Clipboard'
        menuItem = HMCopyMenuItemWithTag(menu, SRCopyImageTag, browserController);
        [menuItem setRepresentedObject:element];
        [items addObject:menuItem];
        menuItem = HMCopyMenuItemWithTag(menu, SRCopyImageURLTag, browserController);
        [menuItem setRepresentedObject:element];
        [items addObject:menuItem];
        
        // 'Open Image in Full Screen'
        if (!isFullScreen) {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenImageInFullScreenTag, browserController);
            [menuItem setRepresentedObject:element];
            [items addObject:menuItem];
        }
    }
    
    // When text is selected
    if (isSelected) {
        // Add separator
        if ([items count] > 0) {
            [items addObject:[NSMenuItem separatorItem]];
        }
        
        // 'Copy'
        menuItem = HMCopyMenuItemWithTag(menu, SRWebTextCopyTag, browserController);
        [items addObject:menuItem];
        
        // 'Open Text As URL'
        if (selectedString) {
            menuItem = HMCopyMenuItemWithTag(menu, SROpenTextAsURLTag, browserController);
            [menuItem setRepresentedObject:selectedString];
            [items addObject:menuItem];
        }
    }
    
    // When text is selected
    if (isSelected) {
        // Add separator
        if ([items count] > 0) {
            [items addObject:[NSMenuItem separatorItem]];
        }
        
        // 'Search'
        menuItem = HMCopyMenuItemWithTag(menu, SRFindBySearchEngineTag, browserController);
        [items addObject:menuItem];
        
        // 'Search By Other'
        menuItem = HMCopyMenuItemWithTag(menu, SRFindByOtherSearchEngineTag, browserController);
        [items addObject:menuItem];
    }
    
    // When text is selcted
    if (selectedString) {
        // Get current search engine
        SRSearchEngine* currentEngine;
        currentEngine = [[browserController searchFieldController] currentSearchEngine];
        
        if (currentEngine) {
            // Add separator
            if ([items count] > 0) {
                [items addObject:[NSMenuItem separatorItem]];
            }
            
#if 0
            // Add 'Search by serach engine'
            NSMenuItem* searchMenuItem;
            searchMenuItem = [self _createSerachMenuItemByEngine:currentEngine 
                    searchString:selectedString];
            [items addObject:searchMenuItem];
#endif
            
            // Find other search engines
            NSMutableArray* otherEngines;
            NSArray*        engines;
            NSEnumerator*   enumerator;
            SRSearchEngine* engine;
            otherEngines = [NSMutableArray array];
            engines = [[SRAppController sharedInstance] searchEngines];
            enumerator = [engines objectEnumerator];
            while (engine = [enumerator nextObject]) {
                if ([[engine valueForKey:@"isUsing"] boolValue] && 
                    ![[engine valueForKey:@"title"] isEqualToString:[currentEngine valueForKey:@"title"]])
                {
                    [otherEngines addObject:engine];
                }
            }
            
            // Add search by other engines menu
            if ([otherEngines count] > 0) {
                // Add 'Search by other'
                menuItem = HMCopyMenuItemWithTag(menu, SRFindByOtherSearchEngineTag, browserController);
                [items addObject:menuItem];
                
#if 0
                // Create submenu
                NSMenu* otherEnginesSubmenu;
                otherEnginesSubmenu = [[NSMenu alloc] initWithTitle:@"Other engine"];
                [menuItem setSubmenu:otherEnginesSubmenu];
                
                enumerator = [otherEngines objectEnumerator];
                while (engine = [enumerator nextObject]) {
                    // Add 'Search by serach engine'
                    NSMenuItem* searchMenuItem;
                    searchMenuItem = [self _createSerachMenuItemByEngine:engine 
                            searchString:selectedString];
                    [otherEnginesSubmenu addItem:searchMenuItem];
                }
#endif
            }
        }
    }
                
    // When nothing is selected
    if (!isSelected && !linkURL && !imageURL) {
        // 'Go Back'
        if ([webView canGoBack]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewGoBackTag, browserController);
            [items addObject:menuItem];
        }
        // 'Go Forward'
        if ([webView canGoForward]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewGoForwardTag, browserController);
            [items addObject:menuItem];
        }
        // 'Reload Page'
        if (![webView isLoading]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewReloadPageTag, browserController);
            [items addObject:menuItem];
        }
        // 'Stop Loading'
        if ([webView isLoading]) {
            menuItem = HMCopyMenuItemWithTag(menu, SRWebViewStopLoadingTag, browserController);
            [items addObject:menuItem];
        }
        
        // Frame and sub frame
        if ([[webView mainFrame] dataSource] || webFrame != [webView mainFrame]) {
            // Add separator
            if ([items count] > 0) {
                [items addObject:[NSMenuItem separatorItem]];
            }
            
            // When in sub frame
            if (webFrame != [webView mainFrame] && !isFullScreen) {
                // Get frame URL
                WebDataSource*  dataSource;
                NSURL*          frameURL;
                dataSource = [[element objectForKey:WebElementFrameKey] dataSource];
                frameURL = [dataSource unreachableURL];
                if (!frameURL) {
                    frameURL = [[dataSource request] URL];
                }
                
                // 'Open Frame in New Tab' and 'Open Frame in Background Tab'
                if (selectNewTabs) {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewTabTag, browserController);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundTabTag, browserController);
                }
                else {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundTabTag, browserController);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewTabTag, browserController);
                }
                [menuItem setRepresentedObject:frameURL];
                [items addObject:menuItem];
                
                [altMenuItem setRepresentedObject:frameURL];
                [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
                [altMenuItem setAlternate:YES];
                [items addObject:altMenuItem];
                
                // 'Open Frame in New Window' and 'Open Frame in Background Window'
                if (selectNewTabs) {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewWindowTag, browserController);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundWindowTag, browserController);
                }
                else {
                    menuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewBackgroundWindowTag, browserController);
                    altMenuItem = HMCopyMenuItemWithTag(menu, SROpenFrameInNewWindowTag, browserController);
                }
                [menuItem setRepresentedObject:frameURL];
                [items addObject:menuItem];
                
                [altMenuItem setRepresentedObject:frameURL];
                [altMenuItem setKeyEquivalentModifierMask:NSShiftKeyMask];
                [altMenuItem setAlternate:YES];
                [items addObject:altMenuItem];
            }
            
            // 'View Source'
            if ([[webView mainFrame] dataSource] && !isFullScreen) {
                menuItem = HMCopyMenuItemWithTag(menu, SRViewSourceTag, browserController);
                [items addObject:menuItem];
            }
            
            // 'View Frame Source'
            if (webFrame != [webView mainFrame] && !isFullScreen) {
                menuItem = HMCopyMenuItemWithTag(menu, SRViewFrameSourceTag, browserController);
                [items addObject:menuItem];
            }
        }
    }
    
    // Remove last separator
    if ([[items lastObject] isSeparatorItem]) {
        [items removeLastObject];
    }
    
    return items;
}

//--------------------------------------------------------------//
#pragma mark -- NSTableView delegate --
//--------------------------------------------------------------//

- (void)tableViewSelectionDidChange:(NSNotification*)notification
{
    // Get selected item
    NSArray*    items;
    id          item;
    items = [_itemArrayController selectedObjects];
    if ([items count] == 0) {
        return;
    }
    item = [items objectAtIndex:0];
    
    // Create HTML
    NSString*   html;
    html = [self _createHTMLWithItem:item];
    if (!html) {
        html = @"";
    }
    
    // Load HTML
    NSString*   path;
    path = [HMApplicationSupportFolder(@"Shiira") stringByAppendingPathComponent:@"template/tmp.html"];
    [[html dataUsingEncoding:NSUTF8StringEncoding] writeToFile:path atomically:YES];
    [[_listWebView mainFrame] loadRequest:
            [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
    
    // Make selected item read
    [[RSSManager sharedInstance] makeItemPreviewed:item];
}

//--------------------------------------------------------------//
#pragma mark -- RSSPersistentStack notification --
//--------------------------------------------------------------//

- (void)rssPersistentStackRefreshed:(NSNotification*)notification
{
    // Load feed with RSS URL
    [self _loadFeedFromContextWithURLString:_urlString];
    
    // Set contents
    [_itemArrayController setContent:[_feed valueForKey:@"items"]];
}

@end
