Merge branch 'master' into animations

This commit is contained in:
Samuel Clay 2011-07-12 08:53:15 -07:00
commit 38accaa3c0
52 changed files with 28930 additions and 9488 deletions

View file

@ -32,7 +32,7 @@ class MClassifierTitle(mongo.Document):
meta = { meta = {
'collection': 'classifier_title', 'collection': 'classifier_title',
'indexes': ['feed_id', 'user_id', ('user_id', 'feed_id')], 'indexes': [('user_id', 'feed_id')],
'allow_inheritance': False, 'allow_inheritance': False,
} }
@ -45,7 +45,7 @@ class MClassifierAuthor(mongo.Document):
meta = { meta = {
'collection': 'classifier_author', 'collection': 'classifier_author',
'indexes': ['feed_id', 'user_id', ('user_id', 'feed_id')], 'indexes': [('user_id', 'feed_id')],
'allow_inheritance': False, 'allow_inheritance': False,
} }
@ -58,7 +58,7 @@ class MClassifierFeed(mongo.Document):
meta = { meta = {
'collection': 'classifier_feed', 'collection': 'classifier_feed',
'indexes': ['feed_id', 'user_id', ('user_id', 'feed_id')], 'indexes': [('user_id', 'feed_id')],
'allow_inheritance': False, 'allow_inheritance': False,
} }
@ -72,7 +72,7 @@ class MClassifierTag(mongo.Document):
meta = { meta = {
'collection': 'classifier_tag', 'collection': 'classifier_tag',
'indexes': ['feed_id', 'user_id', ('user_id', 'feed_id')], 'indexes': [('user_id', 'feed_id')],
'allow_inheritance': False, 'allow_inheritance': False,
} }
@ -114,17 +114,13 @@ def apply_classifier_tags(classifiers, story):
def get_classifiers_for_user(user, feed_id, classifier_feeds=None, classifier_authors=None, classifier_titles=None, classifier_tags=None): def get_classifiers_for_user(user, feed_id, classifier_feeds=None, classifier_authors=None, classifier_titles=None, classifier_tags=None):
if classifier_feeds is None: if classifier_feeds is None:
classifier_feeds = MClassifierFeed.objects(user_id=user.pk, feed_id=feed_id) classifier_feeds = list(MClassifierFeed.objects(user_id=user.pk, feed_id=feed_id))
else: classifier_feeds.rewind()
if classifier_authors is None: if classifier_authors is None:
classifier_authors = MClassifierAuthor.objects(user_id=user.pk, feed_id=feed_id) classifier_authors = list(MClassifierAuthor.objects(user_id=user.pk, feed_id=feed_id))
else: classifier_authors.rewind()
if classifier_titles is None: if classifier_titles is None:
classifier_titles = MClassifierTitle.objects(user_id=user.pk, feed_id=feed_id) classifier_titles = list(MClassifierTitle.objects(user_id=user.pk, feed_id=feed_id))
else: classifier_titles.rewind()
if classifier_tags is None: if classifier_tags is None:
classifier_tags = MClassifierTag.objects(user_id=user.pk, feed_id=feed_id) classifier_tags = list(MClassifierTag.objects(user_id=user.pk, feed_id=feed_id))
else: classifier_tags.rewind()
payload = { payload = {
'feeds': dict([(f.feed_id, f.score) for f in classifier_feeds]), 'feeds': dict([(f.feed_id, f.score) for f in classifier_feeds]),

View file

@ -112,7 +112,7 @@ class OPMLImporter(Importer):
def process_outline(self, outline): def process_outline(self, outline):
folders = [] folders = []
for item in outline: for item in outline:
if not hasattr(item, 'xmlUrl'): if not hasattr(item, 'xmlUrl') and hasattr(item, 'text'):
folder = item folder = item
# if hasattr(folder, 'text'): # if hasattr(folder, 'text'):
# logging.info(' ---> [%s] ~FRNew Folder: %s' % (self.user, folder.text)) # logging.info(' ---> [%s] ~FRNew Folder: %s' % (self.user, folder.text))

0
apps/mobile/__init__.py Normal file
View file

3
apps/mobile/models.py Normal file
View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

23
apps/mobile/tests.py Normal file
View file

@ -0,0 +1,23 @@
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".
Replace these with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}

6
apps/mobile/urls.py Normal file
View file

@ -0,0 +1,6 @@
from django.conf.urls.defaults import *
from apps.mobile import views
urlpatterns = patterns('apps.mobile.views',
url(r'^$', views.index, name='mobile-index'),
)

15
apps/mobile/views.py Normal file
View file

@ -0,0 +1,15 @@
import os
import base64
import yaml
from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
from apps.profile.models import Profile
from apps.reader.models import UserSubscription, UserSubscriptionFolders
from utils import json_functions as json
from utils import log as logging
def index(request):
return render_to_response('mobile/mobile_workspace.xhtml', {},
context_instance=RequestContext(request))

View file

@ -7,7 +7,9 @@ class UserSubscriptionManager(models.Manager):
try: try:
return super(UserSubscriptionManager, self).get(*args, **kwargs) return super(UserSubscriptionManager, self).get(*args, **kwargs)
except: except:
if 'feed' in kwargs: if isinstance(kwargs.get('feed'), int):
feed_id = kwargs.get('feed')
elif 'feed' in kwargs:
feed_id = kwargs['feed'].pk feed_id = kwargs['feed'].pk
elif 'feed__pk' in kwargs: elif 'feed__pk' in kwargs:
feed_id = kwargs['feed__pk'] feed_id = kwargs['feed__pk']

View file

@ -191,10 +191,10 @@ class UserSubscription(models.Model):
# if not silent: # if not silent:
# logging.info(' ---> [%s] Format stories: %s' % (self.user, datetime.datetime.now() - now)) # logging.info(' ---> [%s] Format stories: %s' % (self.user, datetime.datetime.now() - now))
classifier_feeds = MClassifierFeed.objects(user_id=self.user.pk, feed_id=self.feed.pk) classifier_feeds = list(MClassifierFeed.objects(user_id=self.user.pk, feed_id=self.feed.pk))
classifier_authors = MClassifierAuthor.objects(user_id=self.user.pk, feed_id=self.feed.pk) classifier_authors = list(MClassifierAuthor.objects(user_id=self.user.pk, feed_id=self.feed.pk))
classifier_titles = MClassifierTitle.objects(user_id=self.user.pk, feed_id=self.feed.pk) classifier_titles = list(MClassifierTitle.objects(user_id=self.user.pk, feed_id=self.feed.pk))
classifier_tags = MClassifierTag.objects(user_id=self.user.pk, feed_id=self.feed.pk) classifier_tags = list(MClassifierTag.objects(user_id=self.user.pk, feed_id=self.feed.pk))
# if not silent: # if not silent:
# logging.info(' ---> [%s] Classifiers: %s (%s)' % (self.user, datetime.datetime.now() - now, classifier_feeds.count() + classifier_authors.count() + classifier_tags.count() + classifier_titles.count())) # logging.info(' ---> [%s] Classifiers: %s (%s)' % (self.user, datetime.datetime.now() - now, classifier_feeds.count() + classifier_authors.count() + classifier_tags.count() + classifier_titles.count()))
@ -204,13 +204,10 @@ class UserSubscription(models.Model):
} }
for story in stories: for story in stories:
classifier_authors.rewind()
classifier_tags.rewind()
classifier_titles.rewind()
scores.update({ scores.update({
'author': apply_classifier_authors(classifier_authors, story), 'author' : apply_classifier_authors(classifier_authors, story),
'tags': apply_classifier_tags(classifier_tags, story), 'tags' : apply_classifier_tags(classifier_tags, story),
'title': apply_classifier_titles(classifier_titles, story), 'title' : apply_classifier_titles(classifier_titles, story),
}) })
max_score = max(scores['author'], scores['tags'], scores['title']) max_score = max(scores['author'], scores['tags'], scores['title'])

View file

@ -63,6 +63,7 @@ def index(request):
active_count = UserSubscription.objects.filter(user=request.user, active=True).count() if authed else 0 active_count = UserSubscription.objects.filter(user=request.user, active=True).count() if authed else 0
train_count = UserSubscription.objects.filter(user=request.user, active=True, is_trained=False, feed__stories_last_month__gte=1).count() if authed else 0 train_count = UserSubscription.objects.filter(user=request.user, active=True, is_trained=False, feed__stories_last_month__gte=1).count() if authed else 0
recommended_feeds = RecommendedFeed.objects.filter(is_public=True, approved_date__lte=datetime.datetime.now()).select_related('feed')[:2] recommended_feeds = RecommendedFeed.objects.filter(is_public=True, approved_date__lte=datetime.datetime.now()).select_related('feed')[:2]
unmoderated_feeds = RecommendedFeed.objects.filter(is_public=False, declined_date__isnull=True).select_related('feed')[:2]
statistics = MStatistics.all() statistics = MStatistics.all()
feedbacks = MFeedback.all() feedbacks = MFeedback.all()
@ -77,6 +78,7 @@ def index(request):
'train_count' : active_count - train_count, 'train_count' : active_count - train_count,
'account_images' : range(1, 4), 'account_images' : range(1, 4),
'recommended_feeds' : recommended_feeds, 'recommended_feeds' : recommended_feeds,
'unmoderated_feeds' : unmoderated_feeds,
'statistics' : statistics, 'statistics' : statistics,
'feedbacks' : feedbacks, 'feedbacks' : feedbacks,
'start_import_from_google_reader': request.session.get('import_from_google_reader', False), 'start_import_from_google_reader': request.session.get('import_from_google_reader', False),
@ -184,6 +186,7 @@ def load_feed_favicons(request):
def load_feeds_flat(request): def load_feeds_flat(request):
user = get_user(request) user = get_user(request)
include_favicons = request.REQUEST.get('include_favicons', False)
feeds = {} feeds = {}
try: try:
@ -197,14 +200,7 @@ def load_feeds_flat(request):
for sub in user_subs: for sub in user_subs:
if sub.needs_unread_recalc: if sub.needs_unread_recalc:
sub.calculate_feed_scores(silent=True) sub.calculate_feed_scores(silent=True)
feeds[sub.feed.pk] = { feeds[sub.feed.pk] = sub.canonical(include_favicon=include_favicons)
'id': sub.feed.pk,
'feed_title': sub.user_title or sub.feed.feed_title,
'feed_link': sub.feed.feed_link,
'ps': sub.unread_count_positive,
'nt': sub.unread_count_neutral,
'ng': sub.unread_count_negative,
}
folders = json.decode(folders.folders) folders = json.decode(folders.folders)
flat_folders = {} flat_folders = {}
@ -212,25 +208,24 @@ def load_feeds_flat(request):
def make_feeds_folder(items, parent_folder="", depth=0): def make_feeds_folder(items, parent_folder="", depth=0):
for item in items: for item in items:
if isinstance(item, int) and item in feeds: if isinstance(item, int) and item in feeds:
feed = feeds[item]
if not parent_folder: if not parent_folder:
parent_folder = ' ' parent_folder = ' '
if parent_folder in flat_folders: if parent_folder in flat_folders:
flat_folders[parent_folder].append(feed) flat_folders[parent_folder].append(item)
else: else:
flat_folders[parent_folder] = [feed] flat_folders[parent_folder] = [item]
elif isinstance(item, dict): elif isinstance(item, dict):
for folder_name in item: for folder_name in item:
folder = item[folder_name] folder = item[folder_name]
flat_folder_name = "%s%s%s" % ( flat_folder_name = "%s%s%s" % (
parent_folder, parent_folder,
" - " if parent_folder else "", " - " if parent_folder and parent_folder != ' ' else "",
folder_name folder_name
) )
make_feeds_folder(folder, flat_folder_name, depth+1) make_feeds_folder(folder, flat_folder_name, depth+1)
make_feeds_folder(folders) make_feeds_folder(folders)
data = dict(flat_folders=flat_folders, user=user.username) data = dict(flat_folders=flat_folders, feeds=feeds, user=user.username)
return data return data
@json.json_view @json.json_view
@ -278,16 +273,16 @@ def refresh_feeds(request):
@json.json_view @json.json_view
def load_single_feed(request, feed_id): def load_single_feed(request, feed_id):
start = datetime.datetime.utcnow() start = time.time()
user = get_user(request) user = get_user(request)
offset = int(request.REQUEST.get('offset', 0)) offset = int(request.REQUEST.get('offset', 0))
limit = int(request.REQUEST.get('limit', 12)) limit = int(request.REQUEST.get('limit', 12))
page = int(request.REQUEST.get('page', 1)) page = int(request.REQUEST.get('page', 1))
if page:
offset = limit * (page-1)
dupe_feed_id = None dupe_feed_id = None
if not feed_id: userstories_db = None
raise Http404
if page: offset = limit * (page-1)
if not feed_id: raise Http404
try: try:
feed = Feed.objects.get(id=feed_id) feed = Feed.objects.get(id=feed_id)
@ -303,10 +298,12 @@ def load_single_feed(request, feed_id):
stories = feed.get_stories(offset, limit) stories = feed.get_stories(offset, limit)
# Get intelligence classifier for user # Get intelligence classifier for user
classifier_feeds = MClassifierFeed.objects(user_id=user.pk, feed_id=feed_id) classifier_feeds = list(MClassifierFeed.objects(user_id=user.pk, feed_id=feed_id))
classifier_authors = MClassifierAuthor.objects(user_id=user.pk, feed_id=feed_id) classifier_authors = list(MClassifierAuthor.objects(user_id=user.pk, feed_id=feed_id))
classifier_titles = MClassifierTitle.objects(user_id=user.pk, feed_id=feed_id) classifier_titles = list(MClassifierTitle.objects(user_id=user.pk, feed_id=feed_id))
classifier_tags = MClassifierTag.objects(user_id=user.pk, feed_id=feed_id) classifier_tags = list(MClassifierTag.objects(user_id=user.pk, feed_id=feed_id))
checkpoint1 = time.time()
usersub = UserSubscription.objects.get(user=user, feed=feed) usersub = UserSubscription.objects.get(user=user, feed=feed)
userstories = [] userstories = []
@ -323,8 +320,9 @@ def load_single_feed(request, feed_id):
elif hasattr(us.story, 'id') and isinstance(us.story.id, unicode): elif hasattr(us.story, 'id') and isinstance(us.story.id, unicode):
userstories.append(us.story.id) # TODO: Remove me after migration from story.id->guid userstories.append(us.story.id) # TODO: Remove me after migration from story.id->guid
checkpoint2 = time.time()
for story in stories: for story in stories:
[x.rewind() for x in [classifier_feeds, classifier_authors, classifier_tags, classifier_titles]]
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone) story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
now = localtime_for_timezone(datetime.datetime.now(), user.profile.timezone) now = localtime_for_timezone(datetime.datetime.now(), user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now) story['short_parsed_date'] = format_story_link_date__short(story_date, now)
@ -349,6 +347,8 @@ def load_single_feed(request, feed_id):
'title': apply_classifier_titles(classifier_titles, story), 'title': apply_classifier_titles(classifier_titles, story),
} }
checkpoint3 = time.time()
# Intelligence # Intelligence
feed_tags = json.decode(feed.data.popular_tags) if feed.data.popular_tags else [] feed_tags = json.decode(feed.data.popular_tags) if feed.data.popular_tags else []
feed_authors = json.decode(feed.data.popular_authors) if feed.data.popular_authors else [] feed_authors = json.decode(feed.data.popular_authors) if feed.data.popular_authors else []
@ -358,14 +358,19 @@ def load_single_feed(request, feed_id):
if usersub: if usersub:
usersub.feed_opens += 1 usersub.feed_opens += 1
usersub.save() usersub.save()
timediff = time.time()-start
diff = datetime.datetime.utcnow()-start
timediff = float("%s.%.2s" % (diff.seconds, (diff.microseconds / 1000)))
last_update = relative_timesince(feed.last_update) last_update = relative_timesince(feed.last_update)
logging.user(request.user, "~FYLoading feed: ~SB%s%s ~SN(%s seconds)" % ( logging.user(request.user, "~FYLoading feed: ~SB%s%s ~SN(%.4s seconds)" % (
feed, ('~SN/p%s' % page) if page > 1 else '', timediff)) feed, ('~SN/p%s' % page) if page > 1 else '', timediff))
FeedLoadtime.objects.create(feed=feed, loadtime=timediff) FeedLoadtime.objects.create(feed=feed, loadtime=timediff)
if timediff >= 1:
diff1 = checkpoint1-start
diff2 = checkpoint2-start
diff3 = checkpoint3-start
logging.user(request.user, "~FYSlow feed load: ~SB%.4s/%.4s(%s)/%.4s" % (
diff1, diff2, userstories_db and userstories_db.count(), diff3))
data = dict(stories=stories, data = dict(stories=stories,
feed_tags=feed_tags, feed_tags=feed_tags,
feed_authors=feed_authors, feed_authors=feed_authors,
@ -429,7 +434,7 @@ def load_river_stories(request):
user = get_user(request) user = get_user(request)
feed_ids = [int(feed_id) for feed_id in request.REQUEST.getlist('feeds') if feed_id] feed_ids = [int(feed_id) for feed_id in request.REQUEST.getlist('feeds') if feed_id]
original_feed_ids = list(feed_ids) original_feed_ids = list(feed_ids)
page = int(request.REQUEST.get('page', 0))+1 page = int(request.REQUEST.get('page', 1))
read_stories_count = int(request.REQUEST.get('read_stories_count', 0)) read_stories_count = int(request.REQUEST.get('read_stories_count', 0))
bottom_delta = datetime.timedelta(days=settings.DAYS_OF_UNREAD) bottom_delta = datetime.timedelta(days=settings.DAYS_OF_UNREAD)

View file

@ -1,3 +1,4 @@
import datetime
from django import template from django import template
from apps.reader.models import UserSubscription from apps.reader.models import UserSubscription
from utils.user_functions import get_user from utils.user_functions import get_user
@ -7,7 +8,7 @@ from apps.rss_feeds.models import MFeedIcon
register = template.Library() register = template.Library()
@register.inclusion_tag('recommendations/render_recommended_feed.xhtml', takes_context=True) @register.inclusion_tag('recommendations/render_recommended_feed.xhtml', takes_context=True)
def render_recommended_feed(context, recommended_feeds): def render_recommended_feed(context, recommended_feeds, unmoderated=False):
user = get_user(context['user']) user = get_user(context['user'])
usersub = None usersub = None
@ -18,11 +19,13 @@ def render_recommended_feed(context, recommended_feeds):
if recommended_feed: if recommended_feed:
return { return {
'recommended_feed': recommended_feed, 'recommended_feed' : recommended_feed,
'description': recommended_feed.description or recommended_feed.feed.data.feed_tagline, 'description' : recommended_feed.description or recommended_feed.feed.data.feed_tagline,
'usersub': usersub, 'usersub' : usersub,
'feed_icon': feed_icon and feed_icon[0], 'feed_icon' : feed_icon and feed_icon[0],
'user': context['user'], 'user' : context['user'],
'has_next_page': len(recommended_feeds) > 1 'has_next_page' : len(recommended_feeds) > 1,
'unmoderated' : unmoderated,
'today' : datetime.datetime.now(),
} }

View file

@ -4,5 +4,7 @@ from apps.recommendations import views
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^load_recommended_feed', views.load_recommended_feed, name='load-recommended-feed'), url(r'^load_recommended_feed', views.load_recommended_feed, name='load-recommended-feed'),
url(r'^save_recommended_feed', views.save_recommended_feed, name='save-recommended-feed'), url(r'^save_recommended_feed', views.save_recommended_feed, name='save-recommended-feed'),
url(r'^approve_feed', views.approve_feed, name='approve-recommended-feed'),
url(r'^decline_feed', views.decline_feed, name='decline-recommended-feed'),
url(r'^load_feed_info/(?P<feed_id>\d+)', views.load_feed_info, name='load-recommended-feed-info'), url(r'^load_feed_info/(?P<feed_id>\d+)', views.load_feed_info, name='load-recommended-feed-info'),
) )

View file

@ -1,3 +1,4 @@
import re
import datetime import datetime
from utils import log as logging from utils import log as logging
from django.http import HttpResponse from django.http import HttpResponse
@ -7,17 +8,21 @@ from apps.recommendations.models import RecommendedFeed
from apps.reader.models import UserSubscription from apps.reader.models import UserSubscription
from apps.rss_feeds.models import Feed, MFeedIcon from apps.rss_feeds.models import Feed, MFeedIcon
from utils import json_functions as json from utils import json_functions as json
from utils.user_functions import get_user, ajax_login_required from utils.user_functions import get_user, ajax_login_required, admin_only
def load_recommended_feed(request): def load_recommended_feed(request):
user = get_user(request) user = get_user(request)
page = int(request.REQUEST.get('page', 0)) page = int(request.REQUEST.get('page', 0))
usersub = None usersub = None
refresh = request.REQUEST.get('refresh') refresh = request.REQUEST.get('refresh')
now = datetime.datetime.now now = datetime.datetime.now
unmoderated = request.REQUEST.get('unmoderated', False) == 'true'
recommended_feeds = RecommendedFeed.objects.filter(is_public=True, approved_date__lte=now)[page:page+2] if unmoderated:
recommended_feeds = RecommendedFeed.objects.filter(is_public=False, declined_date__isnull=True)[page:page+2]
else:
recommended_feeds = RecommendedFeed.objects.filter(is_public=True, approved_date__lte=now)[page:page+2]
if recommended_feeds and request.user.is_authenticated(): if recommended_feeds and request.user.is_authenticated():
usersub = UserSubscription.objects.filter(user=user, feed=recommended_feeds[0].feed) usersub = UserSubscription.objects.filter(user=user, feed=recommended_feeds[0].feed)
if refresh != 'true' and page > 0: if refresh != 'true' and page > 0:
@ -34,6 +39,8 @@ def load_recommended_feed(request):
'feed_icon' : feed_icon and feed_icon[0], 'feed_icon' : feed_icon and feed_icon[0],
'has_next_page' : len(recommended_feeds) > 1, 'has_next_page' : len(recommended_feeds) > 1,
'has_previous_page' : page != 0, 'has_previous_page' : page != 0,
'unmoderated' : unmoderated,
'today' : datetime.datetime.now(),
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
else: else:
return HttpResponse("") return HttpResponse("")
@ -72,3 +79,32 @@ def save_recommended_feed(request):
) )
return dict(code=code if created else -1) return dict(code=code if created else -1)
@admin_only
@ajax_login_required
def approve_feed(request):
feed_id = request.POST['feed_id']
feed = get_object_or_404(Feed, pk=int(feed_id))
date = request.POST['date']
recommended_feed = RecommendedFeed.objects.filter(feed=feed)[0]
year, month, day = re.search(r'(\d{4})-(\d{1,2})-(\d{1,2})', date).groups()
recommended_feed.is_public = True
recommended_feed.approved_date = datetime.date(int(year), int(month), int(day))
recommended_feed.save()
return load_recommended_feed(request)
@admin_only
@ajax_login_required
def decline_feed(request):
feed_id = request.POST['feed_id']
feed = get_object_or_404(Feed, pk=int(feed_id))
recommended_feeds = RecommendedFeed.objects.filter(feed=feed)
for recommended_feed in recommended_feeds:
recommended_feed.is_public = False
recommended_feed.declined_date = datetime.datetime.now()
recommended_feed.save()
return load_recommended_feed(request)

View file

@ -737,7 +737,6 @@ class Feed(models.Model):
story_published_now = story.get('published_now', False) story_published_now = story.get('published_now', False)
start_date = story_pub_date - datetime.timedelta(hours=8) start_date = story_pub_date - datetime.timedelta(hours=8)
end_date = story_pub_date + datetime.timedelta(hours=8) end_date = story_pub_date + datetime.timedelta(hours=8)
existing_stories.rewind()
for existing_story in existing_stories: for existing_story in existing_stories:
content_ratio = 0 content_ratio = 0

View file

@ -117,11 +117,13 @@ class MFeedback(mongo.Document):
subject = mongo.StringField() subject = mongo.StringField()
url = mongo.StringField() url = mongo.StringField()
style = mongo.StringField() style = mongo.StringField()
order = mongo.IntField()
meta = { meta = {
'collection': 'feedback', 'collection': 'feedback',
'allow_inheritance': False, 'allow_inheritance': False,
'indexes': ['style'], 'indexes': ['style'],
'ordering': ['order'],
} }
def __unicode__(self): def __unicode__(self):
@ -131,13 +133,16 @@ class MFeedback(mongo.Document):
def collect_feedback(cls): def collect_feedback(cls):
data = urllib2.urlopen('https://getsatisfaction.com/newsblur/topics.widget').read() data = urllib2.urlopen('https://getsatisfaction.com/newsblur/topics.widget').read()
data = json.decode(data[1:-1]) data = json.decode(data[1:-1])
i = 0
if len(data): if len(data):
cls.objects.delete() cls.objects.delete()
for feedback in data: for feedback in data:
feedback['order'] = i
i += 1
for removal in ['about', 'less than']: for removal in ['about', 'less than']:
if removal in feedback['date']: if removal in feedback['date']:
feedback['date'] = feedback['date'].replace(removal, '') feedback['date'] = feedback['date'].replace(removal, '')
[MFeedback.objects.create(**feedback) for feedback in data] [cls.objects.create(**feedback) for feedback in data]
@classmethod @classmethod
def all(cls): def all(cls):

92
config/mongodb.dev.conf Normal file
View file

@ -0,0 +1,92 @@
# mongodb.conf
# Where to store the data.
# Note: if you run mongodb as a non-root user (recommended) you may
# need to create and set permissions for this directory manually,
# e.g., if the parent directory isn't mutable by the mongodb user.
dbpath=/Users/conesus/newsblur/data/db/unsharded
#where to log
logpath=/Users/conesus/newsblur/data/unsharded.log
logappend=false
#port = 27017
slowms=100
rest = true
profile = 2
# Enables periodic logging of CPU utilization and I/O wait
#cpu = true
# Turn on/off security. Off is currently the default
noauth = true
#auth = true
# Verbose logging output.
verbose = true
# Inspect all client data for validity on receipt (useful for
# developing drivers)
#objcheck = true
# Enable db quota management
#quota = true
# Set oplogging level where n is
# 0=off (default)
# 1=W
# 2=R
# 3=both
# 7=W+some reads
#diaglog = 0
# Diagnostic/debugging option
#nocursors = true
# Ignore query hints
#nohints = true
# Disable the HTTP interface (Defaults to localhost:27018).
#nohttpinterface = true
# Turns off server-side scripting. This will result in greatly limited
# functionality
#noscripting = true
# Turns off table scans. Any query that would do a table scan fails.
#notablescan = true
# Disable data file preallocation.
#noprealloc = true
# Specify .ns file size for new databases.
# nssize = <size>
# Accout token for Mongo monitoring server.
#mms-token = <token>
# Server name for Mongo monitoring server.
#mms-name = <server-name>
# Ping interval for Mongo monitoring server.
#mms-interval = <seconds>
# Replication Options
# in master/slave replicated mongo databases, specify here whether
# this is a slave or master
#slave = true
#source = master.example.com
# Slave only: specify a single database to replicate
#only = master.example.com
# or
#master = true
#source = slave.example.com
# in replica set configuration, specify the name of the replica set
# replSet = setname
journal = true

7
fabfile.py vendored
View file

@ -44,6 +44,7 @@ def deploy():
run('git pull') run('git pull')
run('kill -HUP `cat logs/gunicorn.pid`') run('kill -HUP `cat logs/gunicorn.pid`')
run('curl -s http://www.newsblur.com > /dev/null') run('curl -s http://www.newsblur.com > /dev/null')
run('curl -s http://www.newsblur.com/m/ > /dev/null')
run('curl -s http://www.newsblur.com/api/add_site_load_script/ABCDEF > /dev/null') run('curl -s http://www.newsblur.com/api/add_site_load_script/ABCDEF > /dev/null')
compress_media() compress_media()
@ -54,6 +55,7 @@ def deploy_full():
run('./manage.py migrate') run('./manage.py migrate')
run('sudo supervisorctl restart gunicorn') run('sudo supervisorctl restart gunicorn')
run('curl -s http://www.newsblur.com > /dev/null') run('curl -s http://www.newsblur.com > /dev/null')
run('curl -s http://www.newsblur.com/m/ > /dev/null')
compress_media() compress_media()
@roles('web') @roles('web')
@ -62,6 +64,7 @@ def staging():
run('git pull') run('git pull')
run('kill -HUP `cat logs/gunicorn.pid`') run('kill -HUP `cat logs/gunicorn.pid`')
run('curl -s http://dev.newsblur.com > /dev/null') run('curl -s http://dev.newsblur.com > /dev/null')
run('curl -s http://dev.newsblur.com/m/ > /dev/null')
compress_media() compress_media()
@roles('web') @roles('web')
@ -71,6 +74,7 @@ def staging_full():
run('./manage.py migrate') run('./manage.py migrate')
run('kill -HUP `cat logs/gunicorn.pid`') run('kill -HUP `cat logs/gunicorn.pid`')
run('curl -s http://dev.newsblur.com > /dev/null') run('curl -s http://dev.newsblur.com > /dev/null')
run('curl -s http://dev.newsblur.com/m/ > /dev/null')
compress_media() compress_media()
@roles('task') @roles('task')
@ -94,6 +98,9 @@ def compress_media():
with cd('media/js'): with cd('media/js'):
run('rm -f *.gz') run('rm -f *.gz')
run('for js in *-compressed-*.js; do gzip -9 $js -c > $js.gz; done;') run('for js in *-compressed-*.js; do gzip -9 $js -c > $js.gz; done;')
with cd('media/css/mobile'):
run('rm -f *.gz')
run('for css in *-compressed-*.css; do gzip -9 $css -c > $css.gz; done;')
with cd('media/css'): with cd('media/css'):
run('rm -f *.gz') run('rm -f *.gz')
run('for css in *-compressed-*.css; do gzip -9 $css -c > $css.gz; done;') run('for css in *-compressed-*.css; do gzip -9 $css -c > $css.gz; done;')

1
media/css/mobile/images Symbolic link
View file

@ -0,0 +1 @@
../../img/mobile

File diff suppressed because it is too large Load diff

297
media/css/mobile/mobile.css Normal file
View file

@ -0,0 +1,297 @@
/* ========== */
/* = Global = */
/* ========== */
body {
overflow-y: scroll !important;
}
.NB-favicon {
width: 16px;
height: 16px;
vertical-align: text-top;
}
.NB-story-tags {
overflow: hidden;
line-height: 12px;
height: 14px;
margin: 0 0 6px 0;
}
.NB-story-tag {
float: left;
font-weight: normal;
font-size: 9px;
padding: 0px 4px 1px;
margin: 0 2px 2px;
background-color: #E3DFD4;
color: #878787;
text-shadow: 0 1px 0 #E9E9E9;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
}
.NB-story-author {
color: #969696;
font-size: 10px;
text-transform: uppercase;
margin: 0 16px 8px 0;
text-shadow: 0 1px 0 #F9F9F9;
float: left;
}
.NB-story-date {
float: right;
font-size: 11px;
color: #252D6C;
}
.NB-story .NB-icon-score {
margin-top: 12px;
height: 16px;
width: 16px;
left: 4px;
top: 17px;
}
.NB-story.NB-read .NB-icon-score {
opacity: .2;
}
.NB-story.NB-score-positive .NB-icon-score {
background: transparent url('../../img/icons/silk/bullet_green.png') no-repeat 0 0;
}
.NB-story.NB-score-neutral .NB-icon-score {
background: transparent url('../../img/icons/silk/bullet_yellow.png') no-repeat 0 0;
}
.NB-story.NB-score-negative .NB-icon-score {
background: transparent url('../../img/icons/silk/bullet_red.png') no-repeat 0 0;
}
.NB-story .NB-story-title {
clear: left;
}
.ui-btn-up-c,
.ui-btn-hover-c {
border: 1px solid #E4E4E4;
color: #111;
background-color: #E0E0E0;
background-image: -moz-linear-gradient(top, #FCFCFC, #F3F3F3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#F3F3F3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #FCFCFC, #F3F3F3);
}
.ui-btn-active {
text-shadow: 0 -1px 1px #145072;
border: 1px solid #155678;
background-color: #4596ce;
background-image: -moz-linear-gradient(top, #85bae4, #5393c5); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#85bae4), to(#5393c5)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #85bae4, #5393c5);
}
/* ============= */
/* = Feed List = */
/* ============= */
#NB-feed-list a {
padding-left: 36px;
padding-right: 36px;
}
#NB-feed-list .ui-li-icon {
width: 16px;
height: 16px;
margin-top: -2px;
}
#NB-feed-list .ui-li-count {
position: static;
float: right;
margin: 1px 2px 0 0;
padding: 3px 4px 2px;
white-space: normal;
border: none;
border-radius: 5px;
text-shadow: none;
}
#NB-feed-list .ui-li-count.ui-li-count-positive {
color: white;
background-color: #559F4D;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#559F4D), to(#3B7613));
background-image: -moz-linear-gradient(center bottom, #3B7613, #559F4D);
}
#NB-feed-list .ui-li-count.ui-li-count-neutral {
background-color: #F9C72A;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F9C72A), to(#E4AB00));
background-image: -moz-linear-gradient(center bottom, #E4AB00, #F9C72A);
}
#NB-feed-list .ui-li-count.ui-li-count-negative {
color: white;
background-color: #CC2A2E;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#CC2A2E), to(#9B181B));
background-image: -moz-linear-gradient(center bottom, #9B181B, #CC2A2E);
}
/* ============== */
/* = Story List = */
/* ============== */
#NB-story-list .NB-story a {
padding-left: 24px;
padding-right: 36px;
}
#NB-story-list .NB-story-feed {
overflow: hidden;
text-shadow: 0 1px 0 #E9E9E9;
margin: 4px 0 0 0;
white-space: normal;
}
#NB-story-list .NB-story-feed .NB-story-feed-icon {
float: left;
margin: 0 4px 0 0;
}
#NB-story-list .NB-story-feed .NB-story-feed-icon img {
width: 16px;
height: 16px;
}
#NB-story-list .NB-story-feed .NB-story-feed-title {
float: left;
font-size: 12px;
margin: 2px 0 0 0;
color: #525252;
}
#NB-story-list .NB-read .NB-story-feed .NB-story-feed-title {
color: #8D8D8D;
}
#NB-story-list .NB-read .NB-story-feed img {
opacity: .6;
}
#NB-story-list .NB-story .NB-story-title {
text-shadow: 0 1px 0 #F3F3F3;
white-space: normal;
font-weight: bold;
}
#NB-story-list .NB-story.ui-btn-active .NB-story-title,
#NB-story-list .NB-story.ui-btn-active .NB-story-tags,
#NB-story-list .NB-story.ui-btn-active .NB-story-author,
#NB-story-list .NB-story.ui-btn-active .NB-story-date,
#NB-story-list .NB-story.ui-btn-active .NB-story-feed .NB-story-feed-title,
#NB-story-list .NB-read.ui-btn-active .NB-story-author,
#NB-story-list .NB-read.ui-btn-active .NB-story-date,
#NB-story-list .NB-read.ui-btn-active .NB-story-title,
#NB-story-list .NB-read.ui-btn-active .NB-story-feed .NB-story-feed-title {
text-shadow: inherit;
color: inherit;
}
#NB-story-list .NB-story.ui-btn-active .NB-story-feed .NB-story-feed-title,
#NB-story-list .NB-read.ui-btn-active .NB-story-feed .NB-story-feed-title {
text-shadow: 0 1px 0 #000;
}
#NB-story-list .NB-read .NB-story-title {
font-weight: normal;
color: #808080;
}
#NB-story-list .NB-read .NB-story-date {
color: #6D6D6D;
font-weight: normal;
}
#NB-story-list .NB-read .NB-story-author {
color: #B0B0B0;
}
#NB-story-list .NB-read .NB-story-tags {
opacity: .7;
}
#NB-story-list .NB-read.ui-btn-up-c,
#NB-story-list .NB-read.ui-btn-hover-c {
border: 1px solid #E4E4E4;
color: #111;
background-color: #E0E0E0;
background-image: -moz-linear-gradient(top, #FCFCFC, #F9F9F9); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#F9F9F9)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #FCFCFC, #F9F9F9);
}
/* ================ */
/* = Story Detail = */
/* ================ */
#NB-page-story .NB-story-feed-header {
margin: 8px 0;
}
#NB-page-story .ui-content {
padding: 0;
}
#NB-page-story .ui-header {
padding: 2px 0;
color: white;
font-size: 13px;
text-shadow: 1px 1px 0 #000;
}
#NB-page-story .ui-header.NB-inverse {
color: black;
text-shadow: 1px 1px 0 #E0E0E0;
}
#NB-page-story .ui-title {
text-overflow: ellipsis;
white-space: nowrap;
margin: 0 120px 0 106px;
}
#NB-page-story .ui-title .feed_title {
}
#NB-page-story .NB-favicon {
margin-right: 4px;
}
#NB-story-detail {
background-color: white;
}
#NB-story-detail .NB-story-header {
font-weight: bold;
font-size: 16px;
padding: 8px 24px 8px 24px;
background-color: #dadada;
background-image: -moz-linear-gradient(top, #EBEBEB, #CFCFCF); /* FF3.6 */
background-image: -ms-linear-gradient(top, #EBEBEB, #CFCFCF); /* IE10 */
background-image: -o-linear-gradient(top, #EBEBEB, #CFCFCF); /* Opera 11.10+ */
background-image: -webkit-gradient(linear, left top, left bottom, from(#EBEBEB), to(#CFCFCF)); /* Saf4+, Chrome */
background-image: -webkit-linear-gradient(top, #EBEBEB, #CFCFCF); /* Chrome 10+, Saf5.1+ */
background-image: linear-gradient(top, #EBEBEB, #CFCFCF);
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#EBEBEB', EndColorStr='#CFCFCF'); /* IE6IE9 */
border-bottom: 1px solid #ADADAD;
position: relative;
overflow: hidden;
}
#NB-story-detail .NB-story-header a {
text-decoration: none;
}
#NB-story-detail .NB-story-title {
color: #303030;
}
#NB-story-detail .NB-story-content {
text-shadow: none;
padding: 12px 24px;
color: #2b2b2b;
font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
font-size: 12px;
line-height: 1.5em;
}
#NB-story-detail .NB-story-content p {
clear: both;
}
#NB-story-detail .NB-story-content blockquote {
background-color: #F0F0F0;
border-left: 1px solid #9B9B9B;
padding: .5em 2em;
margin: 0px;
}
#NB-story-detail .NB-story-content img {
max-width: 100%;
}
#NB-page-story .NB-icon-score {
margin-top: 12px;
height: 16px;
width: 16px;
left: 4px;
top: 17px;
}
#NB-page-story.NB-score-positive .NB-icon-score {
background: transparent url('../../img/icons/silk/bullet_green.png') no-repeat 0 0;
}
#NB-page-story.NB-score-neutral .NB-icon-score {
background: transparent url('../../img/icons/silk/bullet_yellow.png') no-repeat 0 0;
}
#NB-page-story.NB-score-negative .NB-icon-score {
background: transparent url('../../img/icons/silk/bullet_red.png') no-repeat 0 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -2,17 +2,20 @@
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> <archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data> <data>
<int key="IBDocument.SystemTarget">1024</int> <int key="IBDocument.SystemTarget">1024</int>
<string key="IBDocument.SystemVersion">10H574</string> <string key="IBDocument.SystemVersion">10J869</string>
<string key="IBDocument.InterfaceBuilderVersion">804</string> <string key="IBDocument.InterfaceBuilderVersion">1305</string>
<string key="IBDocument.AppKitVersion">1038.35</string> <string key="IBDocument.AppKitVersion">1038.35</string>
<string key="IBDocument.HIToolboxVersion">461.00</string> <string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">123</string> <string key="NS.object.0">300</string>
</object> </object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<integer value="2"/> <string>IBUITableViewCell</string>
<string>IBUIImageView</string>
<string>IBUILabel</string>
<string>IBProxyObject</string>
</object> </object>
<object class="NSArray" key="IBDocument.PluginDependencies"> <object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@ -23,9 +26,7 @@
<object class="NSArray" key="dict.sortedKeys" id="0"> <object class="NSArray" key="dict.sortedKeys" id="0">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
</object> </object>
<object class="NSMutableArray" key="dict.values"> <reference key="dict.values" ref="0"/>
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object> </object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@ -50,8 +51,9 @@
<object class="IBUILabel" id="750430533"> <object class="IBUILabel" id="750430533">
<reference key="NSNextResponder" ref="994014136"/> <reference key="NSNextResponder" ref="994014136"/>
<int key="NSvFlags">292</int> <int key="NSvFlags">292</int>
<string key="NSFrame">{{133, 37}, {165, 15}}</string> <string key="NSFrame">{{153, 37}, {145, 15}}</string>
<reference key="NSSuperview" ref="994014136"/> <reference key="NSSuperview" ref="994014136"/>
<reference key="NSWindow"/>
<object class="NSColor" key="IBUIBackgroundColor" id="932003208"> <object class="NSColor" key="IBUIBackgroundColor" id="932003208">
<int key="NSColorSpace">1</int> <int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes> <bytes key="NSRGB">MSAxIDEAA</bytes>
@ -86,6 +88,7 @@
<int key="NSvFlags">292</int> <int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 4}, {280, 31}}</string> <string key="NSFrame">{{20, 4}, {280, 31}}</string>
<reference key="NSSuperview" ref="994014136"/> <reference key="NSSuperview" ref="994014136"/>
<reference key="NSWindow"/>
<reference key="IBUIBackgroundColor" ref="932003208"/> <reference key="IBUIBackgroundColor" ref="932003208"/>
<bool key="IBUIClipsSubviews">YES</bool> <bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int> <int key="IBUIContentMode">7</int>
@ -114,8 +117,9 @@
<object class="IBUILabel" id="194816084"> <object class="IBUILabel" id="194816084">
<reference key="NSNextResponder" ref="994014136"/> <reference key="NSNextResponder" ref="994014136"/>
<int key="NSvFlags">292</int> <int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 37}, {113, 15}}</string> <string key="NSFrame">{{20, 37}, {131, 15}}</string>
<reference key="NSSuperview" ref="994014136"/> <reference key="NSSuperview" ref="994014136"/>
<reference key="NSWindow"/>
<object class="NSColor" key="IBUIBackgroundColor"> <object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int> <int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEgMAA</bytes> <bytes key="NSRGB">MSAxIDEgMAA</bytes>
@ -147,6 +151,7 @@
<int key="NSvFlags">292</int> <int key="NSvFlags">292</int>
<string key="NSFrame">{{2, 12}, {16, 16}}</string> <string key="NSFrame">{{2, 12}, {16, 16}}</string>
<reference key="NSSuperview" ref="994014136"/> <reference key="NSSuperview" ref="994014136"/>
<reference key="NSWindow"/>
<bool key="IBUIAutoresizesSubviews">NO</bool> <bool key="IBUIAutoresizesSubviews">NO</bool>
<bool key="IBUIUserInteractionEnabled">NO</bool> <bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
@ -154,6 +159,7 @@
</object> </object>
<string key="NSFrameSize">{300, 55}</string> <string key="NSFrameSize">{300, 55}</string>
<reference key="NSSuperview" ref="749780469"/> <reference key="NSSuperview" ref="749780469"/>
<reference key="NSWindow"/>
<object class="NSColor" key="IBUIBackgroundColor"> <object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int> <int key="NSColorSpace">3</int>
<bytes key="NSWhite">MCAwAA</bytes> <bytes key="NSWhite">MCAwAA</bytes>
@ -167,6 +173,7 @@
</object> </object>
<string key="NSFrameSize">{320, 55}</string> <string key="NSFrameSize">{320, 55}</string>
<reference key="NSSuperview"/> <reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="IBUIBackgroundColor" ref="932003208"/> <reference key="IBUIBackgroundColor" ref="932003208"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUISeparatorStyle">1</int> <int key="IBUISeparatorStyle">1</int>
@ -297,17 +304,13 @@
<object class="NSMutableDictionary" key="unlocalizedProperties"> <object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/> <reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values"> <reference key="dict.values" ref="0"/>
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object> </object>
<nil key="activeLocalization"/> <nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations"> <object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/> <reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values"> <reference key="dict.values" ref="0"/>
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object> </object>
<nil key="sourceID"/> <nil key="sourceID"/>
<int key="maxID">23</int> <int key="maxID">23</int>
@ -366,173 +369,7 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/FeedDetailTableCell.h</string> <string key="minorKey">./Classes/FeedDetailTableCell.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Source/NSObject+SBJSON.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Source/SBJsonWriter.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">QuartzCore.framework/Headers/CAAnimation.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">QuartzCore.framework/Headers/CALayer.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="603407049">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIImageView</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIImageView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UILabel</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UILabel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIResponder</string>
<string key="superclassName">NSObject</string>
<reference key="sourceIdentifier" ref="603407049"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UITableViewCell</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITableViewCell.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
</object> </object>
</object> </object>
</object> </object>
@ -548,8 +385,7 @@
<integer value="3100" key="NS.object.0"/> <integer value="3100" key="NS.object.0"/>
</object> </object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../NewsBlur.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int> <int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">123</string> <string key="IBCocoaTouchPluginVersion">300</string>
</data> </data>
</archive> </archive>

View file

@ -69,7 +69,7 @@
- (void)fetchFeedDetail { - (void)fetchFeedDetail {
if ([appDelegate.activeFeed objectForKey:@"id"] != nil) { if ([appDelegate.activeFeed objectForKey:@"id"] != nil) {
NSString *theFeedDetailURL = [[NSString alloc] NSString *theFeedDetailURL = [[NSString alloc]
initWithFormat:@"http://nb.local.host:8000/reader/load_single_feed/?feed_id=%@", initWithFormat:@"http://nb.local.host:8000/reader/feed/%@",
[appDelegate.activeFeed objectForKey:@"id"]]; [appDelegate.activeFeed objectForKey:@"id"]];
//NSLog(@"Url: %@", theFeedDetailURL); //NSLog(@"Url: %@", theFeedDetailURL);
NSURL *urlFeedDetail = [NSURL URLWithString:theFeedDetailURL]; NSURL *urlFeedDetail = [NSURL URLWithString:theFeedDetailURL];
@ -150,12 +150,12 @@
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:indexPath.row]; NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:indexPath.row];
if ([[story objectForKey:@"story_authors"] class] != [NSNull class]) { if ([[story objectForKey:@"story_authors"] class] != [NSNull class]) {
cell.storyAuthor.text = [story objectForKey:@"story_authors"]; cell.storyAuthor.text = [[story objectForKey:@"story_authors"] uppercaseString];
} else { } else {
cell.storyAuthor.text = @""; cell.storyAuthor.text = @"";
} }
cell.storyTitle.text = [story objectForKey:@"story_title"]; cell.storyTitle.text = [story objectForKey:@"story_title"];
cell.storyDate.text = [story objectForKey:@"long_parsed_date"]; cell.storyDate.text = [story objectForKey:@"short_parsed_date"];
if ([[story objectForKey:@"read_status"] intValue] != 1) { if ([[story objectForKey:@"read_status"] intValue] != 1) {
// Unread story // Unread story

View file

@ -57,6 +57,9 @@
- (void)setTitle:(NSString *)title; - (void)setTitle:(NSString *)title;
- (void)showOriginalStory:(NSURL *)url; - (void)showOriginalStory:(NSURL *)url;
- (void)closeOriginalStory; - (void)closeOriginalStory;
- (int)indexOfNextStory;
- (int)indexOfPreviousStory;
- (int)indexOfActiveStory;
+ (int)computeStoryScore:(NSDictionary *)intelligence; + (int)computeStoryScore:(NSDictionary *)intelligence;
@end @end

View file

@ -119,6 +119,44 @@
[originalStoryViewController dismissModalViewControllerAnimated:YES]; [originalStoryViewController dismissModalViewControllerAnimated:YES];
} }
- (int)indexOfNextStory {
int activeIndex = [self indexOfActiveStory];
NSUInteger activeFeedStoriesCount = [activeFeedStories count];
NSLog(@"ActiveStory: %d", activeIndex);
NSLog(@"ActiveStory: %d", activeFeedStoriesCount);
for (int i=activeIndex+1; i < activeFeedStoriesCount; i++) {
NSDictionary *story = [activeFeedStories objectAtIndex:i];
NSDecimalNumber *readStatus = [story objectForKey:@"read_status"];
NSLog(@"readStatus: %@", readStatus);
if (readStatus == 0) {
NSLog(@"NextStory: %d", i);
return i;
}
}
return -1;
}
- (int)indexOfPreviousStory {
NSInteger activeIndex = [self indexOfActiveStory];
for (int i=activeIndex-1; i >= 0; i--) {
NSDictionary *story = [activeFeedStories objectAtIndex:i];
if ([story objectForKey:@"read_status"] == 1) {
return i;
}
}
return -1;
}
- (int)indexOfActiveStory {
for (int i=0; i < [activeFeedStories count]; i++) {
NSDictionary *story = [activeFeedStories objectAtIndex:i];
if ([activeStory objectForKey:@"id"] == [story objectForKey:@"id"]) {
return i;
}
}
return -1;
}
+ (int)computeStoryScore:(NSDictionary *)intelligence { + (int)computeStoryScore:(NSDictionary *)intelligence {
int score = 0; int score = 0;
// int score_max = 0; // int score_max = 0;

View file

@ -19,6 +19,7 @@
NSMutableData *responseData; NSMutableData *responseData;
NSMutableArray * feedTitleList; NSMutableArray * feedTitleList;
NSDictionary * dictFolders; NSDictionary * dictFolders;
NSDictionary * dictFeeds;
NSMutableArray * dictFoldersArray; NSMutableArray * dictFoldersArray;
IBOutlet UITableView * viewTableFeedTitles; IBOutlet UITableView * viewTableFeedTitles;
@ -40,6 +41,7 @@
@property (nonatomic, retain) NSMutableArray *feedTitleList; @property (nonatomic, retain) NSMutableArray *feedTitleList;
@property (nonatomic, retain) NSMutableArray *dictFoldersArray; @property (nonatomic, retain) NSMutableArray *dictFoldersArray;
@property (nonatomic, retain) NSDictionary *dictFolders; @property (nonatomic, retain) NSDictionary *dictFolders;
@property (nonatomic, retain) NSDictionary *dictFeeds;
@property (nonatomic, retain) NSMutableData *responseData; @property (nonatomic, retain) NSMutableData *responseData;
@end @end

View file

@ -22,6 +22,7 @@
@synthesize feedTitleList; @synthesize feedTitleList;
@synthesize dictFolders; @synthesize dictFolders;
@synthesize dictFeeds;
@synthesize dictFoldersArray; @synthesize dictFoldersArray;
#pragma mark - #pragma mark -
@ -38,6 +39,7 @@
- (void)viewDidLoad { - (void)viewDidLoad {
self.feedTitleList = [[NSMutableArray alloc] init]; self.feedTitleList = [[NSMutableArray alloc] init];
self.dictFolders = [[NSDictionary alloc] init]; self.dictFolders = [[NSDictionary alloc] init];
self.dictFeeds = [[NSDictionary alloc] init];
self.dictFoldersArray = [[NSMutableArray alloc] init]; self.dictFoldersArray = [[NSMutableArray alloc] init];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStylePlain target:self action:@selector(doLogoutButton)]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStylePlain target:self action:@selector(doLogoutButton)];
[appDelegate showNavigationBar:NO]; [appDelegate showNavigationBar:NO];
@ -84,6 +86,7 @@
- (void)dealloc { - (void)dealloc {
[feedTitleList release]; [feedTitleList release];
[dictFolders release]; [dictFolders release];
[dictFeeds release];
[dictFoldersArray release]; [dictFoldersArray release];
[appDelegate release]; [appDelegate release];
[super dealloc]; [super dealloc];
@ -129,6 +132,7 @@
appDelegate.activeUsername = [results objectForKey:@"user"]; appDelegate.activeUsername = [results objectForKey:@"user"];
[appDelegate setTitle:[results objectForKey:@"user"]]; [appDelegate setTitle:[results objectForKey:@"user"]];
self.dictFolders = [results objectForKey:@"flat_folders"]; self.dictFolders = [results objectForKey:@"flat_folders"];
self.dictFeeds = [results objectForKey:@"feeds"];
//NSLog(@"Received Feeds: %@", dictFolders); //NSLog(@"Received Feeds: %@", dictFolders);
NSSortDescriptor *sortDescriptor; NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"feed_title" sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"feed_title"
@ -139,19 +143,21 @@
for (id f in self.dictFolders) { for (id f in self.dictFolders) {
[self.dictFoldersArray addObject:f]; [self.dictFoldersArray addObject:f];
// NSArray *folder = [self.dictFolders objectForKey:f];
sortedArray = [[self.dictFolders objectForKey:f] sortedArrayUsingDescriptors:sortDescriptors]; // NSLog(@"F: %@", f);
[sortedFolders setValue:sortedArray forKey:f]; // NSLog(@"F: %@", folder);
// NSLog(@"F: %@", sortDescriptors);
// sortedArray = [folder sortedArrayUsingDescriptors:sortDescriptors];
// [sortedFolders setValue:sortedArray forKey:f];
} }
self.dictFolders = sortedFolders; // self.dictFolders = sortedFolders;
[self.dictFoldersArray sortUsingSelector:@selector(caseInsensitiveCompare:)]; [self.dictFoldersArray sortUsingSelector:@selector(caseInsensitiveCompare:)];
[[self viewTableFeedTitles] reloadData]; [[self viewTableFeedTitles] reloadData];
[sortedFolders release]; [sortedFolders release];
[results release]; [results release];
[jsonString release];
} }
[jsonString release]; [jsonString release];
} }
@ -219,8 +225,9 @@
// NSLog(@"Cell: %i: %@", section_index, f); // NSLog(@"Cell: %i: %@", section_index, f);
if (section_index == indexPath.section) { if (section_index == indexPath.section) {
NSArray *feeds = [self.dictFolders objectForKey:f]; NSArray *feeds = [self.dictFolders objectForKey:f];
// NSLog(@"Cell: %i: %@: %@", section_index, f, [feeds objectAtIndex:indexPath.row]); id feed_id = [feeds objectAtIndex:indexPath.row];
cell.textLabel.text = [[feeds objectAtIndex:indexPath.row] NSString *feed_id_str = [NSString stringWithFormat:@"%@",feed_id];
cell.textLabel.text = [[self.dictFeeds objectForKey:feed_id_str]
objectForKey:@"feed_title"]; objectForKey:@"feed_title"];
return cell; return cell;
} }
@ -233,10 +240,13 @@
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int section_index = 0; int section_index = 0;
for (id f in self.dictFoldersArray) { for (id f in self.dictFoldersArray) {
// NSLog(@"Cell: %i: %@", section_index, f); //NSLog(@"Cell: %i: %@", section_index, f);
if (section_index == indexPath.section) { if (section_index == indexPath.section) {
NSArray *feeds = [[NSArray alloc] initWithArray:[self.dictFolders objectForKey:f]]; NSArray *feeds = [[NSArray alloc] initWithArray:[self.dictFolders objectForKey:f]];
[appDelegate setActiveFeed:[feeds objectAtIndex:indexPath.row]]; id feed_id = [feeds objectAtIndex:indexPath.row];
NSString *feed_id_str = [NSString stringWithFormat:@"%@",feed_id];
[appDelegate setActiveFeed:[self.dictFeeds
objectForKey:feed_id_str]];
[feeds release]; [feeds release];
//NSLog(@"Active feed: %@", [appDelegate activeFeed]); //NSLog(@"Active feed: %@", [appDelegate activeFeed]);
break; break;

View file

@ -14,7 +14,6 @@
<UIScrollViewDelegate> { <UIScrollViewDelegate> {
NewsBlurAppDelegate *appDelegate; NewsBlurAppDelegate *appDelegate;
UIScrollView *scrollView;
UIWebView *webView; UIWebView *webView;
UIToolbar *toolbar; UIToolbar *toolbar;
UIBarButtonItem *buttonPrevious; UIBarButtonItem *buttonPrevious;
@ -22,7 +21,6 @@
} }
@property (nonatomic, retain) IBOutlet UIWebView *webView; @property (nonatomic, retain) IBOutlet UIWebView *webView;
@property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
@property (nonatomic, retain) IBOutlet UIToolbar *toolbar; @property (nonatomic, retain) IBOutlet UIToolbar *toolbar;
@property (nonatomic, retain) IBOutlet UIBarButtonItem *buttonPrevious; @property (nonatomic, retain) IBOutlet UIBarButtonItem *buttonPrevious;
@property (nonatomic, retain) IBOutlet UIBarButtonItem *buttonNext; @property (nonatomic, retain) IBOutlet UIBarButtonItem *buttonNext;
@ -32,5 +30,7 @@
- (void)showStory; - (void)showStory;
- (void)showOriginalSubview:(id)sender; - (void)showOriginalSubview:(id)sender;
- (void)resizeWebView; - (void)resizeWebView;
- (IBAction)doNextUnreadStory;
- (IBAction)doPreviousStory;
@end @end

View file

@ -16,7 +16,6 @@
@synthesize appDelegate; @synthesize appDelegate;
@synthesize webView; @synthesize webView;
@synthesize scrollView;
@synthesize toolbar; @synthesize toolbar;
@synthesize buttonNext; @synthesize buttonNext;
@synthesize buttonPrevious; @synthesize buttonPrevious;
@ -52,11 +51,11 @@
NSString *urlString = @"http://nb.local.host:8000/reader/mark_story_as_read"; NSString *urlString = @"http://nb.local.host:8000/reader/mark_story_as_read";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; // ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[appDelegate.activeStory objectForKey:@"id"] forKey:@"story_id"]; // [request setPostValue:[appDelegate.activeStory objectForKey:@"id"] forKey:@"story_id"];
[request setPostValue:[appDelegate.activeFeed objectForKey:@"id"] forKey:@"feed_id"]; // [request setPostValue:[appDelegate.activeFeed objectForKey:@"id"] forKey:@"feed_id"];
[request setDelegate:self]; // [request setDelegate:self];
[request startAsynchronous]; // [request startAsynchronous];
} }
@ -78,7 +77,7 @@
- (void)showStory { - (void)showStory {
NSLog(@"Loaded Story view: %@", [appDelegate.activeStory objectForKey:@"story_title"]); // NSLog(@"Loaded Story view: %@", appDelegate.activeStory);
NSString *imgCssString = [NSString stringWithFormat:@"<style>" NSString *imgCssString = [NSString stringWithFormat:@"<style>"
"body {" "body {"
" line-height: 18px;" " line-height: 18px;"
@ -103,16 +102,75 @@
" font-weight: bold;" " font-weight: bold;"
" background-color: #E0E0E0;" " background-color: #E0E0E0;"
" border-bottom: 1px solid #A0A0A0;" " border-bottom: 1px solid #A0A0A0;"
" padding: 12px 12px;" " padding: 12px 12px 8px;"
" text-shadow: 1px 1px 0 #EFEFEF;" " text-shadow: 1px 1px 0 #EFEFEF;"
"}" "}"
".NB-story {" ".NB-story {"
" margin: 12px;" " margin: 12px;"
"}" "}"
".NB-story-author {"
" color: #969696;"
" font-size: 10px;"
" text-transform: uppercase;"
" margin: 0 16px 4px 0;"
" text-shadow: 0 1px 0 #F9F9F9;"
" float: left;"
"}"
".NB-story-tags {"
" clear: both;"
" overflow: hidden;"
" line-height: 12px;"
" height: 14px;"
" margin: 6px 0 0 0;"
" text-transform: uppercase;"
"}"
".NB-story-tag {"
" float: left;"
" font-weight: normal;"
" font-size: 9px;"
" padding: 0px 4px 0px;"
" margin: 0 4px 2px 0;"
" background-color: #C6CBC3;"
" color: #505050;"
" text-shadow: 0 1px 0 #E7E7E7;"
" border-radius: 4px;"
" -moz-border-radius: 4px;"
" -webkit-border-radius: 4px;"
"}"
".NB-story-date {"
" float: right;"
" font-size: 11px;"
" color: #252D6C;"
"}"
".NB-story-title {"
" clear: left;"
"}"
"</style>"]; "</style>"];
NSString *story_author = @"";
if ([appDelegate.activeStory objectForKey:@"story_authors"]) {
NSString *author = [NSString stringWithFormat:@"%@",[appDelegate.activeStory objectForKey:@"story_authors"]];
if (author && ![author isEqualToString:@"<null>"]) {
story_author = [NSString stringWithFormat:@"<div class=\"NB-story-author\">%@</div>",author];
}
}
NSString *story_tags = @"";
if ([appDelegate.activeStory objectForKey:@"story_tags"]) {
NSArray *tag_array = [appDelegate.activeStory objectForKey:@"story_tags"];
if ([tag_array count] > 0) {
story_tags = [NSString stringWithFormat:@"<div class=\"NB-story-tags\"><div class=\"NB-story-tag\">%@</div></div>",
[tag_array componentsJoinedByString:@"</div><div class=\"NB-story-tag\">"]];
}
}
NSString *storyHeader = [NSString stringWithFormat:@"<div class=\"NB-header\">" NSString *storyHeader = [NSString stringWithFormat:@"<div class=\"NB-header\">"
"<div class=\"NB-story-date\">%@</div>"
"%@" "%@"
"</div>", [appDelegate.activeStory objectForKey:@"story_title"]]; "<div class=\"NB-story-title\">%@</div>"
"%@"
"</div>",
[story_tags length] ? [appDelegate.activeStory objectForKey:@"long_parsed_date"] : [appDelegate.activeStory objectForKey:@"short_parsed_date"],
story_author,
[appDelegate.activeStory objectForKey:@"story_title"],
story_tags];
NSString *htmlString = [NSString stringWithFormat:@"%@ %@ <div class=\"NB-story\">%@</div>", NSString *htmlString = [NSString stringWithFormat:@"%@ %@ <div class=\"NB-story\">%@</div>",
imgCssString, storyHeader, imgCssString, storyHeader,
[appDelegate.activeStory objectForKey:@"story_content"]]; [appDelegate.activeStory objectForKey:@"story_content"]];
@ -123,6 +181,38 @@
} }
- (IBAction)doNextUnreadStory {
int nextIndex = [appDelegate indexOfNextStory];
if (nextIndex == -1) {
} else {
[appDelegate setActiveStory:[[appDelegate activeFeedStories] objectAtIndex:nextIndex]];
[self showStory];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:.5];
[UIView setAnimationBeginsFromCurrentState:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:NO];
[UIView commitAnimations];
}
}
- (IBAction)doPreviousStory {
NSInteger nextIndex = [appDelegate indexOfPreviousStory];
if (nextIndex == -1) {
} else {
[appDelegate setActiveStory:[[appDelegate activeFeedStories] objectAtIndex:nextIndex]];
[self showStory];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:.5];
[UIView setAnimationBeginsFromCurrentState:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self.view cache:NO];
[UIView commitAnimations];
}
}
- (void)showOriginalSubview:(id)sender { - (void)showOriginalSubview:(id)sender {
NSURL *url = [NSURL URLWithString:[appDelegate.activeStory NSURL *url = [NSURL URLWithString:[appDelegate.activeStory
objectForKey:@"story_permalink"]]; objectForKey:@"story_permalink"]];
@ -157,29 +247,11 @@
} }
- (void)webViewDidStartLoad:(UIWebView *)webView { - (void)webViewDidStartLoad:(UIWebView *)webView {
[self resizeWebView];
} }
- (void)webViewDidFinishLoad:(UIWebView *)webView { - (void)webViewDidFinishLoad:(UIWebView *)webView {
[self resizeWebView];
} }
- (void)resizeWebView {
CGRect frame = webView.frame;
frame.size.height = 1;
webView.frame = frame;
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView.frame = frame;
NSLog(@"heights: %f / %f", frame.size.width, frame.size.height, toolbar.frame.size.height);
toolbar.frame = CGRectMake(0, webView.frame.size.height, toolbar.frame.size.width, toolbar.frame.size.height);
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
scrollView.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
}
- (void)dealloc { - (void)dealloc {
[appDelegate release]; [appDelegate release];
[webView release]; [webView release];

View file

@ -2,17 +2,22 @@
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10"> <archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data> <data>
<int key="IBDocument.SystemTarget">1024</int> <int key="IBDocument.SystemTarget">1024</int>
<string key="IBDocument.SystemVersion">10J567</string> <string key="IBDocument.SystemVersion">10J869</string>
<string key="IBDocument.InterfaceBuilderVersion">804</string> <string key="IBDocument.InterfaceBuilderVersion">1305</string>
<string key="IBDocument.AppKitVersion">1038.35</string> <string key="IBDocument.AppKitVersion">1038.35</string>
<string key="IBDocument.HIToolboxVersion">462.00</string> <string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">123</string> <string key="NS.object.0">300</string>
</object> </object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<integer value="14"/> <string>IBUIWebView</string>
<string>IBUIBarButtonItem</string>
<string>IBUIToolbar</string>
<string>IBUIProgressView</string>
<string>IBUIView</string>
<string>IBProxyObject</string>
</object> </object>
<object class="NSArray" key="IBDocument.PluginDependencies"> <object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@ -23,9 +28,7 @@
<object class="NSArray" key="dict.sortedKeys" id="0"> <object class="NSArray" key="dict.sortedKeys" id="0">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
</object> </object>
<object class="NSMutableArray" key="dict.values"> <reference key="dict.values" ref="0"/>
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object> </object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@ -42,67 +45,88 @@
<int key="NSvFlags">292</int> <int key="NSvFlags">292</int>
<object class="NSMutableArray" key="NSSubviews"> <object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIScrollView" id="55113995"> <object class="IBUIWebView" id="506862915">
<reference key="NSNextResponder" ref="191373211"/> <reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">268</int> <int key="NSvFlags">274</int>
<string key="NSFrameSize">{320, 416}</string>
<reference key="NSSuperview" ref="191373211"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="155973878"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIDataDetectorTypes">1</int>
<bool key="IBUIDetectsPhoneNumbers">YES</bool>
</object>
<object class="IBUIToolbar" id="155973878">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">266</int>
<object class="NSMutableArray" key="NSSubviews"> <object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIWebView" id="279038753"> <object class="IBUIProgressView" id="484194819">
<reference key="NSNextResponder" ref="55113995"/> <reference key="NSNextResponder" ref="155973878"/>
<int key="NSvFlags">274</int> <int key="NSvFlags">292</int>
<string key="NSFrameSize">{320, 466}</string> <string key="NSFrame">{{111, 17}, {78, 11}}</string>
<reference key="NSSuperview" ref="55113995"/> <reference key="NSSuperview" ref="155973878"/>
<object class="NSColor" key="IBUIBackgroundColor"> <reference key="NSWindow"/>
<int key="NSColorSpace">1</int> <bool key="IBUIOpaque">NO</bool>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<bool key="IBUIClipsSubviews">YES</bool>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIDataDetectorTypes">15</int> <float key="IBUIProgress">0.5</float>
<bool key="IBUIDetectsPhoneNumbers">YES</bool> <int key="IBUIProgressViewStyle">1</int>
</object>
<object class="IBUIToolbar" id="887718764">
<reference key="NSNextResponder" ref="55113995"/>
<int key="NSvFlags">298</int>
<string key="NSFrame">{{0, 415}, {320, 44}}</string>
<reference key="NSSuperview" ref="55113995"/>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<int key="IBUIContentMode">6</int>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableArray" key="IBUIItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIBarButtonItem" id="809166513">
<string key="IBUITitle">Item</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIStyle">1</int>
<reference key="IBUIToolbar" ref="887718764"/>
</object>
<object class="IBUIBarButtonItem" id="1040742603">
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="887718764"/>
<int key="IBUISystemItemIdentifier">5</int>
</object>
<object class="IBUIBarButtonItem" id="54550830">
<string key="IBUITitle">Item</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIStyle">1</int>
<reference key="IBUIToolbar" ref="887718764"/>
</object>
</object>
</object> </object>
</object> </object>
<string key="NSFrameSize">{320, 466}</string> <string key="NSFrame">{{0, 416}, {320, 44}}</string>
<reference key="NSSuperview" ref="191373211"/> <reference key="NSSuperview" ref="191373211"/>
<bool key="IBUIClipsSubviews">YES</bool> <reference key="NSWindow"/>
<bool key="IBUIMultipleTouchEnabled">YES</bool> <reference key="NSNextKeyView" ref="484194819"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIDelaysContentTouches">NO</bool> <object class="NSMutableArray" key="IBUIItems">
<bool key="IBUICanCancelContentTouches">NO</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIBarButtonItem" id="542821156">
<string key="IBUITitle">Previous</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIStyle">1</int>
<reference key="IBUIToolbar" ref="155973878"/>
</object>
<object class="IBUIBarButtonItem" id="936960182">
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="155973878"/>
<int key="IBUISystemItemIdentifier">5</int>
</object>
<object class="IBUIBarButtonItem" id="812131495">
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUICustomView" ref="484194819"/>
<reference key="IBUIToolbar" ref="155973878"/>
</object>
<object class="IBUIBarButtonItem" id="284060761">
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<reference key="IBUIToolbar" ref="155973878"/>
<int key="IBUISystemItemIdentifier">5</int>
</object>
<object class="IBUIBarButtonItem" id="1065495688">
<string key="IBUITitle">Next unread</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBUIStyle">1</int>
<reference key="IBUIToolbar" ref="155973878"/>
</object>
</object>
<object class="NSColor" key="IBUITintColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4yMjcwMjkxMjggMC4zNjIxMzU3NzY0IDAuNDU2NTIxNzM5MQA</bytes>
</object>
</object> </object>
</object> </object>
<string key="NSFrameSize">{320, 460}</string> <string key="NSFrame">{{0, 20}, {320, 460}}</string>
<reference key="NSSuperview"/> <reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="506862915"/>
<object class="NSColor" key="IBUIBackgroundColor"> <object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int> <int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes> <bytes key="NSWhite">MQA</bytes>
@ -117,69 +141,69 @@
<object class="IBObjectContainer" key="IBDocument.Objects"> <object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords"> <object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">webView</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="279038753"/>
</object>
<int key="connectionID">17</int>
</object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection"> <object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">buttonNext</string> <string key="label">buttonNext</string>
<reference key="source" ref="372490531"/> <reference key="source" ref="372490531"/>
<reference key="destination" ref="54550830"/> <reference key="destination" ref="1065495688"/>
</object> </object>
<int key="connectionID">32</int> <int key="connectionID">50</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">buttonPrevious</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="809166513"/>
</object>
<int key="connectionID">33</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="55113995"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">34</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">scrollView</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="55113995"/>
</object>
<int key="connectionID">35</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="279038753"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">36</int>
</object> </object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection"> <object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">toolbar</string> <string key="label">toolbar</string>
<reference key="source" ref="372490531"/> <reference key="source" ref="372490531"/>
<reference key="destination" ref="887718764"/> <reference key="destination" ref="155973878"/>
</object> </object>
<int key="connectionID">37</int> <int key="connectionID">51</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="506862915"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">52</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">webView</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="506862915"/>
</object>
<int key="connectionID">54</int>
</object> </object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection"> <object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string> <string key="label">view</string>
<reference key="source" ref="372490531"/> <reference key="source" ref="372490531"/>
<reference key="destination" ref="55113995"/> <reference key="destination" ref="191373211"/>
</object> </object>
<int key="connectionID">38</int> <int key="connectionID">55</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">buttonPrevious</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="542821156"/>
</object>
<int key="connectionID">56</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">doPreviousStory</string>
<reference key="source" ref="542821156"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">57</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">doNextUnreadStory</string>
<reference key="source" ref="1065495688"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">58</int>
</object> </object>
</object> </object>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
@ -196,7 +220,8 @@
<reference key="object" ref="191373211"/> <reference key="object" ref="191373211"/>
<object class="NSMutableArray" key="children"> <object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="55113995"/> <reference ref="506862915"/>
<reference ref="155973878"/>
</object> </object>
<reference key="parent" ref="0"/> <reference key="parent" ref="0"/>
</object> </object>
@ -212,45 +237,56 @@
<reference key="parent" ref="0"/> <reference key="parent" ref="0"/>
</object> </object>
<object class="IBObjectRecord"> <object class="IBObjectRecord">
<int key="objectID">19</int> <int key="objectID">39</int>
<reference key="object" ref="55113995"/> <reference key="object" ref="506862915"/>
<reference key="parent" ref="191373211"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">42</int>
<reference key="object" ref="155973878"/>
<object class="NSMutableArray" key="children"> <object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="279038753"/> <reference ref="542821156"/>
<reference ref="887718764"/> <reference ref="1065495688"/>
<reference ref="936960182"/>
<reference ref="812131495"/>
<reference ref="284060761"/>
</object> </object>
<reference key="parent" ref="191373211"/> <reference key="parent" ref="191373211"/>
</object> </object>
<object class="IBObjectRecord"> <object class="IBObjectRecord">
<int key="objectID">14</int> <int key="objectID">43</int>
<reference key="object" ref="279038753"/> <reference key="object" ref="542821156"/>
<reference key="parent" ref="55113995"/> <reference key="parent" ref="155973878"/>
</object> </object>
<object class="IBObjectRecord"> <object class="IBObjectRecord">
<int key="objectID">23</int> <int key="objectID">44</int>
<reference key="object" ref="887718764"/> <reference key="object" ref="1065495688"/>
<reference key="parent" ref="155973878"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">45</int>
<reference key="object" ref="936960182"/>
<reference key="parent" ref="155973878"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">47</int>
<reference key="object" ref="812131495"/>
<object class="NSMutableArray" key="children"> <object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="809166513"/> <reference ref="484194819"/>
<reference ref="54550830"/>
<reference ref="1040742603"/>
</object> </object>
<reference key="parent" ref="55113995"/> <reference key="parent" ref="155973878"/>
</object> </object>
<object class="IBObjectRecord"> <object class="IBObjectRecord">
<int key="objectID">24</int> <int key="objectID">46</int>
<reference key="object" ref="809166513"/> <reference key="object" ref="484194819"/>
<reference key="parent" ref="887718764"/> <reference key="parent" ref="812131495"/>
</object> </object>
<object class="IBObjectRecord"> <object class="IBObjectRecord">
<int key="objectID">25</int> <int key="objectID">48</int>
<reference key="object" ref="54550830"/> <reference key="object" ref="284060761"/>
<reference key="parent" ref="887718764"/> <reference key="parent" ref="155973878"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">26</int>
<reference key="object" ref="1040742603"/>
<reference key="parent" ref="887718764"/>
</object> </object>
</object> </object>
</object> </object>
@ -262,15 +298,13 @@
<string>-2.CustomClassName</string> <string>-2.CustomClassName</string>
<string>1.IBEditorWindowLastContentRect</string> <string>1.IBEditorWindowLastContentRect</string>
<string>1.IBPluginDependency</string> <string>1.IBPluginDependency</string>
<string>14.IBPluginDependency</string> <string>39.IBPluginDependency</string>
<string>14.IBViewBoundsToFrameTransform</string> <string>42.IBPluginDependency</string>
<string>19.IBPluginDependency</string> <string>43.IBPluginDependency</string>
<string>19.IBViewBoundsToFrameTransform</string> <string>44.IBPluginDependency</string>
<string>23.IBPluginDependency</string> <string>45.IBPluginDependency</string>
<string>23.IBViewBoundsToFrameTransform</string> <string>46.IBPluginDependency</string>
<string>24.IBPluginDependency</string> <string>48.IBPluginDependency</string>
<string>25.IBPluginDependency</string>
<string>26.IBPluginDependency</string>
</object> </object>
<object class="NSMutableArray" key="dict.values"> <object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@ -279,17 +313,9 @@
<string>{{751, 380}, {320, 480}}</string> <string>{{751, 380}, {320, 480}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABBoAAAw/IAAA</bytes>
</object>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAAAAAAAAw+UAAA</bytes>
</object>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform"> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<bytes key="NSTransformStruct">P4AAAL+AAAAAAAAAw8UAAA</bytes>
</object>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
@ -298,20 +324,16 @@
<object class="NSMutableDictionary" key="unlocalizedProperties"> <object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/> <reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values"> <reference key="dict.values" ref="0"/>
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object> </object>
<nil key="activeLocalization"/> <nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations"> <object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/> <reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values"> <reference key="dict.values" ref="0"/>
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object> </object>
<nil key="sourceID"/> <nil key="sourceID"/>
<int key="maxID">38</int> <int key="maxID">58</int>
</object> </object>
<object class="IBClassDescriber" key="IBDocument.Classes"> <object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions"> <object class="NSMutableArray" key="referencedPartialClassDescriptions">
@ -374,7 +396,7 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/FeedDetailViewController.h</string> <string key="minorKey">./Classes/FeedDetailViewController.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@ -421,7 +443,7 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/LoginViewController.h</string> <string key="minorKey">./Classes/LoginViewController.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@ -438,23 +460,9 @@
<string key="candidateClassName">NewsBlurAppDelegate</string> <string key="candidateClassName">NewsBlurAppDelegate</string>
</object> </object>
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="763602146">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/NewsBlurViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Source/NSObject+SBJSON.h</string> <string key="minorKey">./Classes/LogoutDelegate.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Source/SBJsonWriter.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@ -536,7 +544,7 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/NewsBlurAppDelegate.h</string> <string key="minorKey">./Classes/NewsBlurAppDelegate.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@ -606,7 +614,10 @@
</object> </object>
</object> </object>
</object> </object>
<reference key="sourceIdentifier" ref="763602146"/> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/NewsBlurViewController.h</string>
</object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">OriginalStoryViewController</string> <string key="className">OriginalStoryViewController</string>
@ -663,21 +674,42 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/OriginalStoryViewController.h</string> <string key="minorKey">./Classes/OriginalStoryViewController.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">StoryDetailViewController</string> <string key="className">StoryDetailViewController</string>
<string key="superclassName">UIViewController</string> <string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="actions"> <object class="NSMutableDictionary" key="actions">
<string key="NS.key.0">showOriginalSubview:</string> <bool key="EncodedWithXMLCoder">YES</bool>
<string key="NS.object.0">id</string> <object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>doNextUnreadStory</string>
<string>doPreviousStory</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
</object>
</object> </object>
<object class="NSMutableDictionary" key="actionInfosByName"> <object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">showOriginalSubview:</string> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo" key="NS.object.0"> <object class="NSArray" key="dict.sortedKeys">
<string key="name">showOriginalSubview:</string> <bool key="EncodedWithXMLCoder">YES</bool>
<string key="candidateClassName">id</string> <string>doNextUnreadStory</string>
<string>doPreviousStory</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo">
<string key="name">doNextUnreadStory</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">doPreviousStory</string>
<string key="candidateClassName">id</string>
</object>
</object> </object>
</object> </object>
<object class="NSMutableDictionary" key="outlets"> <object class="NSMutableDictionary" key="outlets">
@ -687,7 +719,6 @@
<string>appDelegate</string> <string>appDelegate</string>
<string>buttonNext</string> <string>buttonNext</string>
<string>buttonPrevious</string> <string>buttonPrevious</string>
<string>scrollView</string>
<string>toolbar</string> <string>toolbar</string>
<string>webView</string> <string>webView</string>
</object> </object>
@ -696,7 +727,6 @@
<string>NewsBlurAppDelegate</string> <string>NewsBlurAppDelegate</string>
<string>UIBarButtonItem</string> <string>UIBarButtonItem</string>
<string>UIBarButtonItem</string> <string>UIBarButtonItem</string>
<string>UIScrollView</string>
<string>UIToolbar</string> <string>UIToolbar</string>
<string>UIWebView</string> <string>UIWebView</string>
</object> </object>
@ -708,7 +738,6 @@
<string>appDelegate</string> <string>appDelegate</string>
<string>buttonNext</string> <string>buttonNext</string>
<string>buttonPrevious</string> <string>buttonPrevious</string>
<string>scrollView</string>
<string>toolbar</string> <string>toolbar</string>
<string>webView</string> <string>webView</string>
</object> </object>
@ -726,10 +755,6 @@
<string key="name">buttonPrevious</string> <string key="name">buttonPrevious</string>
<string key="candidateClassName">UIBarButtonItem</string> <string key="candidateClassName">UIBarButtonItem</string>
</object> </object>
<object class="IBToOneOutletInfo">
<string key="name">scrollView</string>
<string key="candidateClassName">UIScrollView</string>
</object>
<object class="IBToOneOutletInfo"> <object class="IBToOneOutletInfo">
<string key="name">toolbar</string> <string key="name">toolbar</string>
<string key="candidateClassName">UIToolbar</string> <string key="candidateClassName">UIToolbar</string>
@ -742,269 +767,7 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/StoryDetailViewController.h</string> <string key="minorKey">./Classes/StoryDetailViewController.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">QuartzCore.framework/Headers/CAAnimation.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">QuartzCore.framework/Headers/CALayer.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="354476810">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIBarButtonItem</string>
<string key="superclassName">UIBarItem</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIBarButtonItem.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIBarItem</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIBarItem.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIControl</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIControl.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UINavigationController</string>
<string key="superclassName">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="963649563">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIResponder</string>
<string key="superclassName">NSObject</string>
<reference key="sourceIdentifier" ref="354476810"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIScrollView</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIScrollView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchBar</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchDisplayController</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISlider</string>
<string key="superclassName">UIControl</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISlider.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UITableView</string>
<string key="superclassName">UIScrollView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITableView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UITextField</string>
<string key="superclassName">UIControl</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="117887094">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIToolbar</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIToolbar.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<reference key="sourceIdentifier" ref="117887094"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<reference key="sourceIdentifier" ref="963649563"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIWebView</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIWebView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIWindow</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIWindow.h</string>
</object> </object>
</object> </object>
</object> </object>
@ -1020,8 +783,7 @@
<integer value="3000" key="NS.object.0"/> <integer value="3000" key="NS.object.0"/>
</object> </object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../NewsBlur.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int> <int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">123</string> <string key="IBCocoaTouchPluginVersion">300</string>
</data> </data>
</archive> </archive>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "1.0">
</Bucket>

View file

@ -1,5 +1,5 @@
/*! /*!
* jQuery JavaScript Library v1.6.1pre Live From Git (8bb6e95b66413c484006288691a82c44ba50554e) * jQuery JavaScript Library v1.6.1
* http://jquery.com/ * http://jquery.com/
* *
* Copyright 2011, John Resig * Copyright 2011, John Resig
@ -11,7 +11,7 @@
* Copyright 2011, The Dojo Foundation * Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses. * Released under the MIT, BSD, and GPL Licenses.
* *
* Date: Sun May 8 01:20:01 UTC 2011 * Date: Thu May 12 15:04:36 2011 -0400
*/ */
(function( window, undefined ) { (function( window, undefined ) {
@ -204,7 +204,7 @@ jQuery.fn = jQuery.prototype = {
selector: "", selector: "",
// The current version of jQuery being used // The current version of jQuery being used
jquery: "1.6.1pre Live From Git (8bb6e95b66413c484006288691a82c44ba50554e)", jquery: "1.6.1",
// The default length of a jQuery object is 0 // The default length of a jQuery object is 0
length: 0, length: 0,
@ -1055,7 +1055,7 @@ jQuery.extend({
if ( jQuery.isFunction( fn ) ) { if ( jQuery.isFunction( fn ) ) {
deferred[ handler ](function() { deferred[ handler ](function() {
returned = fn.apply( this, arguments ); returned = fn.apply( this, arguments );
if ( jQuery.isFunction( returned.promise ) ) { if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise().then( newDefer.resolve, newDefer.reject ); returned.promise().then( newDefer.resolve, newDefer.reject );
} else { } else {
newDefer[ action ]( returned ); newDefer[ action ]( returned );
@ -1137,6 +1137,7 @@ jQuery.extend({
jQuery.support = (function() { jQuery.support = (function() {
var div = document.createElement( "div" ), var div = document.createElement( "div" ),
documentElement = document.documentElement,
all, all,
a, a,
select, select,
@ -1284,7 +1285,7 @@ jQuery.support = (function() {
body.style[ i ] = bodyStyle[ i ]; body.style[ i ] = bodyStyle[ i ];
} }
body.appendChild( div ); body.appendChild( div );
document.documentElement.appendChild( body ); documentElement.insertBefore( body, documentElement.firstChild );
// Check if a disconnected checkbox will retain its checked // Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7) // value of true after appended to the DOM (IE6/7)
@ -1339,12 +1340,12 @@ jQuery.support = (function() {
marginDiv.style.marginRight = "0"; marginDiv.style.marginRight = "0";
div.appendChild( marginDiv ); div.appendChild( marginDiv );
support.reliableMarginRight = support.reliableMarginRight =
( parseInt( document.defaultView.getComputedStyle( marginDiv, null ).marginRight, 10 ) || 0 ) === 0; ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
} }
// Remove the body element we added // Remove the body element we added
body.innerHTML = ""; body.innerHTML = "";
document.documentElement.removeChild( body ); documentElement.removeChild( body );
// Technique from Juriy Zaytsev // Technique from Juriy Zaytsev
// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
@ -1475,7 +1476,7 @@ jQuery.extend({
} }
if ( data !== undefined ) { if ( data !== undefined ) {
thisCache[ name ] = data; thisCache[ jQuery.camelCase( name ) ] = data;
} }
// TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
@ -1485,7 +1486,7 @@ jQuery.extend({
return thisCache[ internalKey ] && thisCache[ internalKey ].events; return thisCache[ internalKey ] && thisCache[ internalKey ].events;
} }
return getByName ? thisCache[ name ] : thisCache; return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache;
}, },
removeData: function( elem, name, pvt /* Internal Use Only */ ) { removeData: function( elem, name, pvt /* Internal Use Only */ ) {
@ -2176,6 +2177,11 @@ jQuery.extend({
return jQuery( elem )[ name ]( value ); return jQuery( elem )[ name ]( value );
} }
// Fallback to prop when attributes are not supported
if ( !("getAttribute" in elem) ) {
return jQuery.prop( elem, name, value );
}
var ret, hooks, var ret, hooks,
notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
@ -2255,7 +2261,7 @@ jQuery.extend({
// Setting the type on a radio button after the value resets the value in IE6-9 // Setting the type on a radio button after the value resets the value in IE6-9
// Reset value to it's default in case type is set after value // Reset value to it's default in case type is set after value
// This is for element creation // This is for element creation
var val = elem.getAttribute("value"); var val = elem.value;
elem.setAttribute( "type", value ); elem.setAttribute( "type", value );
if ( val ) { if ( val ) {
elem.value = val; elem.value = val;
@ -2359,6 +2365,24 @@ boolHook = {
} }
}; };
// Use the value property for back compat
// Use the formHook for button elements in IE6/7 (#1954)
jQuery.attrHooks.value = {
get: function( elem, name ) {
if ( formHook && jQuery.nodeName( elem, "button" ) ) {
return formHook.get( elem, name );
}
return elem.value;
},
set: function( elem, value, name ) {
if ( formHook && jQuery.nodeName( elem, "button" ) ) {
return formHook.set( elem, value, name );
}
// Does not return so that setAttribute is also used
elem.value = value;
}
};
// IE6/7 do not support getting/setting some attributes with get/setAttribute // IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !jQuery.support.getSetAttribute ) { if ( !jQuery.support.getSetAttribute ) {
@ -2366,12 +2390,9 @@ if ( !jQuery.support.getSetAttribute ) {
jQuery.attrFix = jQuery.propFix; jQuery.attrFix = jQuery.propFix;
// Use this for any attribute on a form in IE6/7 // Use this for any attribute on a form in IE6/7
formHook = jQuery.attrHooks.name = jQuery.attrHooks.value = jQuery.valHooks.button = { formHook = jQuery.attrHooks.name = jQuery.valHooks.button = {
get: function( elem, name ) { get: function( elem, name ) {
var ret; var ret;
if ( name === "value" && !jQuery.nodeName( elem, "button" ) ) {
return elem.getAttribute( name );
}
ret = elem.getAttributeNode( name ); ret = elem.getAttributeNode( name );
// Return undefined if nodeValue is empty string // Return undefined if nodeValue is empty string
return ret && ret.nodeValue !== "" ? return ret && ret.nodeValue !== "" ?
@ -3126,6 +3147,9 @@ var withinElement = function( event ) {
// Check if mouse(over|out) are still within the same parent element // Check if mouse(over|out) are still within the same parent element
var parent = event.relatedTarget; var parent = event.relatedTarget;
// set the correct event type
event.type = event.data;
// Firefox sometimes assigns relatedTarget a XUL element // Firefox sometimes assigns relatedTarget a XUL element
// which we cannot access the parentNode property of // which we cannot access the parentNode property of
try { try {
@ -3135,15 +3159,13 @@ var withinElement = function( event ) {
if ( parent && parent !== document && !parent.parentNode ) { if ( parent && parent !== document && !parent.parentNode ) {
return; return;
} }
// Traverse up the tree // Traverse up the tree
while ( parent && parent !== this ) { while ( parent && parent !== this ) {
parent = parent.parentNode; parent = parent.parentNode;
} }
if ( parent !== this ) { if ( parent !== this ) {
// set the correct event type
event.type = event.data;
// handle event if we actually just moused on to a non sub-element // handle event if we actually just moused on to a non sub-element
jQuery.event.handle.apply( this, arguments ); jQuery.event.handle.apply( this, arguments );
} }
@ -4331,7 +4353,8 @@ var Expr = Sizzle.selectors = {
}, },
reset: function( elem ) { reset: function( elem ) {
return elem.nodeName.toLowerCase() === "input" && "reset" === elem.type; var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && "reset" === elem.type;
}, },
button: function( elem ) { button: function( elem ) {
@ -4597,6 +4620,16 @@ if ( document.documentElement.compareDocumentPosition ) {
} else { } else {
sortOrder = function( a, b ) { sortOrder = function( a, b ) {
// The nodes are identical, we can exit early
if ( a === b ) {
hasDuplicate = true;
return 0;
// Fallback to using sourceIndex (in IE) if it's available on both nodes
} else if ( a.sourceIndex && b.sourceIndex ) {
return a.sourceIndex - b.sourceIndex;
}
var al, bl, var al, bl,
ap = [], ap = [],
bp = [], bp = [],
@ -4604,13 +4637,8 @@ if ( document.documentElement.compareDocumentPosition ) {
bup = b.parentNode, bup = b.parentNode,
cur = aup; cur = aup;
// The nodes are identical, we can exit early
if ( a === b ) {
hasDuplicate = true;
return 0;
// If the nodes are siblings (or identical) we can do a quick check // If the nodes are siblings (or identical) we can do a quick check
} else if ( aup === bup ) { if ( aup === bup ) {
return siblingCheck( a, b ); return siblingCheck( a, b );
// If no parents were found then the nodes are disconnected // If no parents were found then the nodes are disconnected
@ -5434,6 +5462,7 @@ var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
// checked="checked" or checked // checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rscriptType = /\/(java|ecma)script/i, rscriptType = /\/(java|ecma)script/i,
rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
wrapMap = { wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ], option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ], legend: [ 1, "<fieldset>", "</fieldset>" ],
@ -6162,7 +6191,7 @@ function evalScript( i, elem ) {
dataType: "script" dataType: "script"
}); });
} else { } else {
jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
} }
if ( elem.parentNode ) { if ( elem.parentNode ) {
@ -8039,6 +8068,9 @@ jQuery.fn.extend({
return this.each( optall.complete, [ false ] ); return this.each( optall.complete, [ false ] );
} }
// Do not change referenced properties as per-property easing will be lost
prop = jQuery.extend( {}, prop );
return this[ optall.queue === false ? "each" : "queue" ](function() { return this[ optall.queue === false ? "each" : "queue" ](function() {
// XXX 'this' does not always have a nodeName when running the // XXX 'this' does not always have a nodeName when running the
// test suite // test suite
@ -8071,7 +8103,7 @@ jQuery.fn.extend({
// easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
if ( jQuery.isArray( val ) ) { if ( jQuery.isArray( val ) ) {
opt.animatedProperties[ name ] = val[ 1 ]; opt.animatedProperties[ name ] = val[ 1 ];
val = val[ 0 ]; val = prop[ name ] = val[ 0 ];
} else { } else {
opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
} }
@ -8168,7 +8200,6 @@ jQuery.fn.extend({
if ( !gotoEnd ) { if ( !gotoEnd ) {
jQuery._unmark( true, this ); jQuery._unmark( true, this );
} }
// go in reverse order so anything added to the queue during the loop is ignored
while ( i-- ) { while ( i-- ) {
if ( timers[i].elem === this ) { if ( timers[i].elem === this ) {
if (gotoEnd) { if (gotoEnd) {
@ -8432,11 +8463,9 @@ jQuery.fx.prototype = {
jQuery.extend( jQuery.fx, { jQuery.extend( jQuery.fx, {
tick: function() { tick: function() {
var timers = jQuery.timers, for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) {
i = timers.length;
while ( i-- ) {
if ( !timers[i]() ) { if ( !timers[i]() ) {
timers.splice(i, 1); timers.splice(i--, 1);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,365 @@
(function($) {
NEWSBLUR.MobileReader = function() {
// ===========
// = Globals =
// ===========
this.model = NEWSBLUR.AssetModel.reader();
this.story_view = 'page';
this.pages = {
'feeds' : $('#NB-page-feeds'),
'stories' : $('#NB-page-stories'),
'story' : $('#NB-page-story')
};
this.$s = {
$body: $('body'),
$feed_list: $('#NB-feed-list'),
$story_list: $('#NB-story-list'),
$story_detail: $('#NB-story-detail')
};
this.flags = {
'feeds_loaded' : false,
'active_view' : null
};
this.locks = {};
this.counts = {};
this.cache = {};
this.constants = {};
$(document).bind('mobileinit', function() {
$.mobile.ajaxEnabled = false;
});
this.runner();
};
NEWSBLUR.MobileReader.prototype = {
runner: function() {
this.load_feeds();
this.bind_clicks();
this.bind_scroll();
},
// =============
// = Feed List =
// =============
load_feeds: function() {
this.flags.active_view = 'feeds';
$.mobile.showPageLoadingMsg();
this.pages.feeds.unbind('pagebeforeshow').bind('pagebeforeshow', _.bind(function(e) {
$('ul', this.$s.$feed_list).listview('refresh');
}, this));
this.pages.feeds.unbind('pageshow').bind('pageshow', _.bind(function(e) {
$('ul', this.$s.$story_list).remove();
}, this));
this.model.load_feeds_flat($.rescope(this.build_feed_list, this));
},
build_feed_list: function() {
this.flags.active_view = 'feeds';
var self = this;
var folders = this.model.folders;
var feeds = this.model.feeds;
var $feed_list = this.$s.$feed_list;
var $feeds = '';
_.each(folders, function(items, folder_name) {
$feeds += '<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b">';
if (folder_name && folder_name != ' ') {
$feeds += _.template('\
<li data-role="list-divider"><%= folder_name %></li>', {
folder_name : folder_name
});
}
_.each(items, function(item) {
$feeds += self.make_feed_title(item);
});
$feeds += '</ul>';
});
this.flags.feeds_loaded = true;
$feed_list.html($feeds);
$('ul', $feed_list).listview();
$.mobile.hidePageLoadingMsg();
},
make_feed_title: function(feed_id) {
var feed = this.model.get_feed(feed_id);
var unread_class = '';
var exception_class = '';
if (feed.ps) unread_class += ' unread_positive';
if (feed.nt) unread_class += ' unread_neutral';
if (feed.ng) unread_class += ' unread_negative';
if (!feed.active) exception_class += ' NB-feed-inactive';
if (feed.has_exception && feed.exception_type == 'feed') {
exception_class += ' NB-feed-exception';
}
if (feed.not_yet_fetched && !feed.has_exception) {
exception_class += ' NB-feed-unfetched';
}
var $feed = _.template('\
<li class="<%= unread_class %> <%= exception_class %>">\
<a href="#" data-feed-id="<%= feed.id %>">\
<% if (feed.ps) { %>\
<span class="ui-li-count ui-li-count-positive"><%= feed.ps %></span>\
<% } %>\
<% if (feed.nt) { %>\
<span class="ui-li-count ui-li-count-neutral"><%= feed.nt %></span>\
<% } %>\
<% if (feed.ng) { %>\
<span class="ui-li-count ui-li-count-negative"><%= feed.ng %></span>\
<% } %>\
<img src="<%= $.favicon(feed.favicon) %>" class="ui-li-icon">\
<%= feed.feed_title %>\
</a>\
</li>', {
feed : feed,
unread_class : unread_class,
exception_class : exception_class
});
return $feed;
},
// ===========
// = Stories =
// ===========
reset_feed: function() {
this.page = 1;
},
load_stories: function(feed_id) {
this.flags.active_view = 'stories';
$.mobile.showPageLoadingMsg();
this.active_feed = feed_id;
this.model.load_feed(feed_id, this.page, this.page == 1, _.bind(this.build_stories, this));
},
build_stories: function(data, first_load) {
this.flags.active_view = 'stories';
var self = this;
var $story_list = this.$s.$story_list;
var $stories = "";
var feed_id = data.feed_id;
if (this.active_feed != feed_id) return;
_.each(data.stories, function(story) {
$stories += self.make_story_title(story);
});
if (first_load) {
$stories = '<ul data-role="listview" data-inset="false" data-theme="c" data-dividertheme="b">' +
$stories +
'</ul>';
$story_list.html($stories);
} else {
$('ul', $story_list).append($stories);
$('ul', $story_list).listview('refresh');
}
$('ul', $story_list).listview();
$.mobile.hidePageLoadingMsg();
},
load_next_page_of_stories: function() {
this.page += 1;
this.load_stories(this.active_feed);
},
make_story_title: function(story) {
var feed = this.model.get_feed(this.active_feed);
var score_color = this.story_color(story);
return _.template('<li class="NB-story <%= story.read_status?"NB-read":"" %> NB-score-<%= score_color %>">\
<div class="ui-li-icon NB-icon-score"></div>\
<a href="#" data-story-id="<%= story.id %>">\
<div class="NB-story-date"><%= story.long_parsed_date %></div>\
<% if (story.story_authors) { %>\
<div class="NB-story-author"><%= story.story_authors %></div>\
<% } %>\
<% if (story.story_tags && story.story_tags.length) { %>\
<div class="NB-story-tags">\
<% _.each(story.story_tags, function(tag) { %>\
<div class="NB-story-tag"><%= tag %></div>\
<% }); %>\
</div>\
<% } %>\
<div class="NB-story-title"><%= story.story_title %></div>\
<div class="NB-story-feed">\
<div class="NB-story-feed-icon"><img src="<%= $.favicon(feed.favicon) %>"></div>\
<div class="NB-story-feed-title"><%= feed.feed_title %></div>\
</div>\
</a>\
</li>', {
story : story,
feed : feed,
score_color : score_color
});
},
scroll_story_list: function() {
var window_height = $(window).height();
var window_offset = $(window).scrollTop();
var story_list_height = this.pages.stories.height();
var fudge_factor = 18;
if (window_height + window_offset > story_list_height - fudge_factor) {
this.load_next_page_of_stories();
}
},
// ================
// = Story Detail =
// ================
load_story_detail: function(story_id) {
this.flags.active_view = 'story_detail';
$.mobile.showPageLoadingMsg();
var $story_detail_view = this.$s.$story_detail;
var story = this.model.get_story(story_id);
var score_color = this.story_color(story);
var $story = this.make_story_detail(story);
this.colorize_story_title(story);
$('.ul-li-right', this.pages.story).jqmData('icon', 'NB-'+score_color);
$story_detail_view.html($story);
$.mobile.hidePageLoadingMsg();
this.mark_story_as_read(story);
},
make_story_detail: function(story) {
var feed = this.model.get_feed(this.active_feed);
var score_color = this.story_color(story);
var $story = _.template('<div class="NB-story <%= story.read_status?"NB-read":"" %> NB-score-<%= score_color %>">\
<div class="NB-story-header">\
<div class="NB-story-header-feed-gradient"></div>\
<div class="ui-li-icon NB-icon-score"></div>\
<div class="NB-story-date"><%= story.long_parsed_date %></div>\
<% if (story.story_authors) { %>\
<div class="NB-story-author"><%= story.story_authors %></div>\
<% } %>\
<% if (story.story_tags && story.story_tags.length) { %>\
<div class="NB-story-tags">\
<% _.each(story.story_tags, function(tag) { %>\
<div class="NB-story-tag"><%= tag %></div>\
<% }); %>\
</div>\
<% } %>\
<a href="<%= story.story_permalink %>" data-story-id="<%= story.id %>">\
<div class="NB-story-title"><%= story.story_title %></div>\
</a>\
</div>\
<div class="NB-story-content"><%= story.story_content %></div>\
</div>', {
story : story,
feed : feed,
score_color : score_color
});
return $story;
},
colorize_story_title: function() {
var feed = this.model.get_feed(this.active_feed);
$('.ui-header', this.pages.story)
.css('background-image', NEWSBLUR.utils.generate_gradient(feed, 'webkit'))
.css('background-image', NEWSBLUR.utils.generate_gradient(feed, 'moz'))
.css('borderBottom', NEWSBLUR.utils.generate_gradient(feed, 'border'))
.css('borderTop', NEWSBLUR.utils.generate_gradient(feed, 'border'))
.toggleClass('NB-inverse', NEWSBLUR.utils.is_feed_floater_gradient_light(feed));
var $feed = _.template('<div class="NB-story-feed-header">\
<img class="NB-favicon" src="<%= $.favicon(feed.favicon) %>" />\
<span class="feed_title">\
<%= feed.feed_title %>\
</span>\
</div>', {
feed : feed
});
$('.ui-title', this.pages.story).html($feed);
},
// =====================
// = General Utilities =
// =====================
story_color: function(story) {
var score = NEWSBLUR.utils.compute_story_score(story);
var score_color = 'neutral';
if (score > 0) score_color = 'positive';
if (score < 0) score_color = 'negative';
return score_color;
},
mark_story_as_read: function(story) {
var story_id = story.id;
var feed_id = story.story_feed_id;
this.model.mark_story_as_read(story_id, feed_id, _.bind(function(read) {
this.update_read_count(story_id, feed_id);
}, this));
},
update_read_count: function(story_id, feed_id) {
},
// ==========
// = Events =
// ==========
bind_clicks: function() {
var self = this;
this.$s.$feed_list.delegate('li a', 'tap', function(e) {
e.preventDefault();
var feed_id = $(e.currentTarget).jqmData('feed-id');
$.mobile.changePage(self.pages.stories);
self.reset_feed();
self.load_stories(feed_id);
});
this.$s.$story_list.delegate('li a', 'tap', function(e) {
e.preventDefault();
var story_id = $(e.currentTarget).jqmData('story-id');
$.mobile.showPageLoadingMsg();
$.mobile.changePage(self.pages.story);
self.load_story_detail(story_id);
});
this.$s.$story_detail.delegate('li a', 'tap', function(e) {
e.preventDefault();
var story_id = $(e.currentTarget).jqmData('story-id');
$.mobile.showPageLoadingMsg();
$.mobile.changePage(self.pages.story);
self.load_story_detail(story_id);
});
this.pages.story.delegate('.NB-next', 'tap', function(e) {
});
this.pages.story.delegate('.NB-previous', 'tap', function(e) {
});
},
bind_scroll: function() {
$(window).bind('scroll', _.throttle(_.bind(function(e) {
if (this.flags.active_view == 'stories') {
this.scroll_story_list();
}
}, this), 500));
}
};
})(jQuery);

View file

@ -230,6 +230,46 @@ NEWSBLUR.AssetModel.Reader.prototype = {
this.make_request('/reader/feeds', data, pre_callback, error_callback, {request_type: 'GET'}); this.make_request('/reader/feeds', data, pre_callback, error_callback, {request_type: 'GET'});
}, },
load_feeds_flat: function(callback, error_callback) {
var self = this;
var data = {
flat: true,
include_favicons: true
};
var pre_callback = function(subscriptions) {
// NEWSBLUR.log(['subscriptions', subscriptions.flat_folders]);
var flat_feeds = function(feeds) {
var flattened = _.flatten(_.map(feeds, _.values));
return _.flatten(_.map(flattened, function(feed) {
if (!_.isNumber(feed) && feed) return flat_feeds(feed);
else return feed;
}));
};
var valid_feeds = flat_feeds({'root': subscriptions.flat_folders});
_.each(subscriptions.feeds, function(feed, feed_id) {
if (_.contains(valid_feeds, parseInt(feed_id, 10))) {
self.feeds[feed_id] = feed;
if (feed.favicon_fetching) self.flags['favicons_fetching'] = true;
}
});
self.folders = subscriptions.flat_folders;
self.starred_count = subscriptions.starred_count;
if (!_.isEqual(self.favicons, {})) {
_.each(self.feeds, function(feed) {
if (self.favicons[feed.id]) {
feed.favicon = self.favicons[feed.id];
}
});
}
callback();
};
this.make_request('/reader/feeds', data, pre_callback, error_callback, {request_type: 'GET'});
},
load_feed_favicons: function(callback, loaded_once, load_all) { load_feed_favicons: function(callback, loaded_once, load_all) {
var pre_callback = _.bind(function(favicons) { var pre_callback = _.bind(function(favicons) {
this.favicons = favicons; this.favicons = favicons;
@ -685,13 +725,29 @@ NEWSBLUR.AssetModel.Reader.prototype = {
this.make_request('/reader/features', {'page': page}, callback, callback, {request_type: 'GET'}); this.make_request('/reader/features', {'page': page}, callback, callback, {request_type: 'GET'});
}, },
load_recommended_feed: function(page, refresh, callback, error_callback) { load_recommended_feed: function(page, refresh, unmoderated, callback, error_callback) {
this.make_request('/recommendations/load_recommended_feed', { this.make_request('/recommendations/load_recommended_feed', {
'page': page, 'page' : page,
'refresh': refresh 'refresh' : refresh,
'unmoderated' : unmoderated
}, callback, error_callback, {request_type: 'GET'}); }, callback, error_callback, {request_type: 'GET'});
}, },
approve_feed_in_moderation_queue: function(feed_id, date, callback) {
this.make_request('/recommendations/approve_feed', {
'feed_id' : feed_id,
'date' : date,
'unmoderated' : true
}, callback, {request_type: 'GET'});
},
decline_feed_in_moderation_queue: function(feed_id, callback) {
this.make_request('/recommendations/decline_feed', {
'feed_id' : feed_id,
'unmoderated' : true
}, callback, {request_type: 'GET'});
},
load_dashboard_graphs: function(callback, error_callback) { load_dashboard_graphs: function(callback, error_callback) {
this.make_request('/statistics/dashboard_graphs', {}, callback, error_callback, {request_type: 'GET'}); this.make_request('/statistics/dashboard_graphs', {}, callback, error_callback, {request_type: 'GET'});
}, },

View file

@ -367,6 +367,10 @@
}, },
find_feed_in_feed_list: function(feed_id) { find_feed_in_feed_list: function(feed_id) {
if (_.contains(this.cache.$feed_in_feed_list, feed_id)) {
return this.cache.$feed_in_feed_list[feed_id];
}
var $feed_list = this.$s.$feed_list; var $feed_list = this.$s.$feed_list;
var $feeds = $([]); var $feeds = $([]);
@ -376,6 +380,8 @@
} }
}); });
this.cache.$feed_in_feed_list[feed_id] = $feeds;
return $feeds; return $feeds;
}, },
@ -1583,56 +1589,56 @@
this.active_feed = data.feed_id; this.active_feed = data.feed_id;
} }
if (this.active_feed == feed_id) { if (this.active_feed != feed_id) return;
// NEWSBLUR.log(['post_open_feed', data.stories, this.flags]);
this.flags['opening_feed'] = false; // NEWSBLUR.log(['post_open_feed', data.stories, this.flags]);
this.flags['feed_view_positions_calculated'] = false; this.flags['opening_feed'] = false;
this.story_titles_clear_loading_endbar(); this.flags['feed_view_positions_calculated'] = false;
this.create_story_titles(stories); this.story_titles_clear_loading_endbar();
this.make_story_feed_entries(stories, first_load); this.create_story_titles(stories);
this.show_feed_hidden_story_title_indicator(true); this.make_story_feed_entries(stories, first_load);
this.show_story_titles_above_intelligence_level({'animate': false}); this.show_feed_hidden_story_title_indicator(true);
this.fill_out_story_titles(); this.show_story_titles_above_intelligence_level({'animate': false});
$('.NB-feedbar-last-updated-date').text(data.last_update + ' ago'); this.fill_out_story_titles();
if (this.counts['find_next_unread_on_page_of_feed_stories_load']) { $('.NB-feedbar-last-updated-date').text(data.last_update + ' ago');
this.show_next_unread_story(true); if (this.counts['find_next_unread_on_page_of_feed_stories_load']) {
} this.show_next_unread_story(true);
this.flags['story_titles_loaded'] = true;
if (!first_load) {
var stories_count = this.cache['iframe_story_positions_keys'].length;
this.flags.iframe_story_locations_fetched = false;
var $iframe = this.$s.$feed_iframe.contents();
this.fetch_story_locations_in_story_frame(stories_count, false, $iframe);
if (this.story_view == 'feed') {
this.prefetch_story_locations_in_feed_view();
}
} else {
if (this.story_view == 'page') {
if (this.flags['iframe_view_loaded']) {
// NEWSBLUR.log(['Titles loaded, iframe loaded']);
var $iframe = this.$s.$feed_iframe.contents();
this.fetch_story_locations_in_story_frame(0, true, $iframe);
} else {
// NEWSBLUR.log(['Titles loaded, iframe NOT loaded -- prefetching now']);
_.delay(_.bind(function() {
this.prefetch_story_locations_in_story_frame();
}, this), 500);
}
} else if (this.story_view == 'feed') {
this.prefetch_story_locations_in_feed_view();
} else if (this.story_view == 'story') {
this.show_next_story(1);
}
}
if (this.flags['open_unread_stories_in_tabs']) {
_.defer(_.bind(this.open_unread_stories_in_tabs, this));
}
this.hide_stories_progress_bar();
if (this.flags['showing_feed_in_tryfeed_view']) {
this.show_tryfeed_add_button();
}
this.make_content_pane_feed_counter(feed_id);
} }
this.flags['story_titles_loaded'] = true;
if (!first_load) {
var stories_count = this.cache['iframe_story_positions_keys'].length;
this.flags.iframe_story_locations_fetched = false;
var $iframe = this.$s.$feed_iframe.contents();
this.fetch_story_locations_in_story_frame(stories_count, false, $iframe);
if (this.story_view == 'feed' || this.flags['page_view_showing_feed_view']) {
this.prefetch_story_locations_in_feed_view();
}
} else {
if (this.story_view == 'page') {
if (this.flags['iframe_view_loaded']) {
// NEWSBLUR.log(['Titles loaded, iframe loaded']);
var $iframe = this.$s.$feed_iframe.contents();
this.fetch_story_locations_in_story_frame(0, true, $iframe);
} else {
// NEWSBLUR.log(['Titles loaded, iframe NOT loaded -- prefetching now']);
_.delay(_.bind(function() {
this.prefetch_story_locations_in_story_frame();
}, this), 250);
}
} else if (this.story_view == 'feed') {
this.prefetch_story_locations_in_feed_view();
} else if (this.story_view == 'story') {
this.show_next_story(1);
}
}
if (this.flags['open_unread_stories_in_tabs']) {
_.defer(_.bind(this.open_unread_stories_in_tabs, this));
}
this.hide_stories_progress_bar();
if (this.flags['showing_feed_in_tryfeed_view']) {
this.show_tryfeed_add_button();
}
this.make_content_pane_feed_counter(feed_id);
}, },
setup_mousemove_on_views: function() { setup_mousemove_on_views: function() {
@ -1801,7 +1807,7 @@
var feeds = this.list_feeds_with_unreads_in_folder($folder, false, true); var feeds = this.list_feeds_with_unreads_in_folder($folder, false, true);
this.cache['river_feeds_with_unreads'] = feeds; this.cache['river_feeds_with_unreads'] = feeds;
this.show_stories_progress_bar(feeds.length); this.show_stories_progress_bar(feeds.length);
this.model.fetch_river_stories(this.active_feed, feeds, 0, this.model.fetch_river_stories(this.active_feed, feeds, 1,
_.bind(this.post_open_river_stories, this), true); _.bind(this.post_open_river_stories, this), true);
}, },
@ -1840,7 +1846,9 @@
var feeds = _.compact(_.map($('.feed:not(.NB-empty)', $folder), function(o) { var feeds = _.compact(_.map($('.feed:not(.NB-empty)', $folder), function(o) {
var feed_id = parseInt($(o).attr('data-id'), 10); var feed_id = parseInt($(o).attr('data-id'), 10);
var feed = model.get_feed(feed_id); var feed = model.get_feed(feed_id);
if (counts_only && !visible_only) { if (!feed) {
return;
} else if (counts_only && !visible_only) {
return feed.ps + feed.nt + feed.ng; return feed.ps + feed.nt + feed.ng;
} else if (counts_only && visible_only) { } else if (counts_only && visible_only) {
if (unread_view == 'positive') return feed.ps; if (unread_view == 'positive') return feed.ps;
@ -2079,7 +2087,7 @@
!$story.length || !$story.length ||
this.flags['iframe_fetching_story_locations'] || this.flags['iframe_fetching_story_locations'] ||
this.flags['iframe_story_locations_fetched'] || this.flags['iframe_story_locations_fetched'] ||
parseInt($story.offset().top, 10) > this.cache['prefetch_iteration']*4000) { parseInt($story.offset().top, 10) > this.cache['prefetch_iteration']*2000) {
if ($story && $story.length) { if ($story && $story.length) {
// NEWSBLUR.log(['Prefetch break on position too far', parseInt($story.offset().top, 10), this.cache['prefetch_iteration']*4000]); // NEWSBLUR.log(['Prefetch break on position too far', parseInt($story.offset().top, 10), this.cache['prefetch_iteration']*4000]);
break; break;
@ -2100,7 +2108,7 @@
&& !self.flags['iframe_story_locations_fetched']) { && !self.flags['iframe_story_locations_fetched']) {
self.prefetch_story_locations_in_story_frame(); self.prefetch_story_locations_in_story_frame();
} }
}, 2000); }, 1000);
} }
}, },
@ -2186,14 +2194,13 @@
var feed = this.model.get_feed(feed_id); var feed = this.model.get_feed(feed_id);
var $feed_list = this.$s.$feed_list; var $feed_list = this.$s.$feed_list;
var $feed = this.cache.$feed_in_feed_list[feed_id] || this.find_feed_in_feed_list(feed_id); var $feed = this.find_feed_in_feed_list(feed_id);
var $feed_counts = this.cache.$feed_counts_in_feed_list[feed_id] || $('.feed_counts_floater', $feed); var $feed_counts = this.cache.$feed_counts_in_feed_list[feed_id] || $('.feed_counts_floater', $feed);
var $story_title = this.find_story_in_story_titles(story_id); var $story_title = this.find_story_in_story_titles(story_id);
var $content_pane = this.$s.$content_pane; var $content_pane = this.$s.$content_pane;
var $floater = $('.feed_counts_floater', $content_pane); var $floater = $('.feed_counts_floater', $content_pane);
var unread_view = this.get_unread_view_name(); var unread_view = this.get_unread_view_name();
this.cache.$feed_in_feed_list[feed_id] = $feed;
this.cache.$feed_counts_in_feed_list[feed_id] = $feed_counts; this.cache.$feed_counts_in_feed_list[feed_id] = $feed_counts;
$story_title.toggleClass('read', !unread); $story_title.toggleClass('read', !unread);
@ -2509,7 +2516,7 @@
var read = story.read_status var read = story.read_status
? ' read ' ? ' read '
: ''; : '';
var score = this.compute_story_score(story); var score = NEWSBLUR.utils.compute_story_score(story);
var score_color = 'neutral'; var score_color = 'neutral';
var starred = story.starred ? ' NB-story-starred ' : ''; var starred = story.starred ? ' NB-story-starred ' : '';
if (options.river_stories) { if (options.river_stories) {
@ -2557,82 +2564,6 @@
return $story_title; return $story_title;
}, },
is_feed_floater_gradient_light: function(feed) {
if (!feed) return false;
var color = feed.favicon_color;
if (!color) return false;
var r = parseInt(color.substr(0, 2), 16) / 255.0;
var g = parseInt(color.substr(2, 2), 16) / 255.0;
var b = parseInt(color.substr(4, 2), 16) / 255.0;
return $.textColor({r: r, g: g, b: b}) != 'white';
},
generate_gradient: function(feed, type) {
if (!feed) return '';
var color = feed.favicon_color;
if (!color) return '';
var r = parseInt(color.substr(0, 2), 16);
var g = parseInt(color.substr(2, 2), 16);
var b = parseInt(color.substr(4, 2), 16);
if (type == 'border') {
return [
'1px solid rgb(',
[
parseInt(r*(6/8), 10),
parseInt(g*(6/8), 10),
parseInt(b*(6/8), 10)
].join(','),
')'
].join('');
} else if (type == 'webkit') {
return [
'-webkit-gradient(',
'linear,',
'left bottom,',
'left top,',
'color-stop(0.06, rgba(',
[
r,
g,
b,
255
].join(','),
')),',
'color-stop(0.84, rgba(',
[
r+35,
g+35,
b+35,
255
].join(','),
')))'
].join('');
} else if (type == 'moz') {
return [
'-moz-linear-gradient(',
'center bottom,',
'rgb(',
[
r,
g,
b
].join(','),
') 6%,',
'rgb(',
[
r+35,
g+35,
b+35
].join(','),
') 84%)'
].join('');
}
},
story_titles_clear_loading_endbar: function() { story_titles_clear_loading_endbar: function() {
var $story_titles = this.$s.$story_titles; var $story_titles = this.$s.$story_titles;
@ -2643,22 +2574,6 @@
} }
}, },
compute_story_score: function(story) {
var score = 0;
var score_max = Math.max(story.intelligence['title'],
story.intelligence['author'],
story.intelligence['tags']);
var score_min = Math.min(story.intelligence['title'],
story.intelligence['author'],
story.intelligence['tags']);
if (score_max > 0) score = score_max;
else if (score_min < 0) score = score_min;
if (score == 0) score = story.intelligence['feed'];
return score;
},
recalculate_story_scores: function(feed_id) { recalculate_story_scores: function(feed_id) {
feed_id = feed_id || this.active_feed; feed_id = feed_id || this.active_feed;
@ -2667,7 +2582,7 @@
var replace_stories = _.bind(function($story, story_id) { var replace_stories = _.bind(function($story, story_id) {
var story = this.model.get_story(story_id); var story = this.model.get_story(story_id);
if (story.story_feed_id != feed_id) return; if (story.story_feed_id != feed_id) return;
var score = this.compute_story_score(story); var score = NEWSBLUR.utils.compute_story_score(story);
$story.removeClass('NB-story-positive') $story.removeClass('NB-story-positive')
.removeClass('NB-story-neutral') .removeClass('NB-story-neutral')
.removeClass('NB-story-negative'); .removeClass('NB-story-negative');
@ -2972,7 +2887,7 @@
var unread_view_name = this.get_unread_view_name(); var unread_view_name = this.get_unread_view_name();
var $indicator = $('.NB-story-title-indicator', $story_titles); var $indicator = $('.NB-story-title-indicator', $story_titles);
var hidden_stories = _.any(this.model.stories, _.bind(function(story) { var hidden_stories = _.any(this.model.stories, _.bind(function(story) {
var score = this.compute_story_score(story); var score = NEWSBLUR.utils.compute_story_score(story);
if (unread_view_name == 'positive') return score <= 0; if (unread_view_name == 'positive') return score <= 0;
else if (unread_view_name == 'neutral') return score < 0; else if (unread_view_name == 'neutral') return score < 0;
@ -2982,6 +2897,7 @@
if ($indicator.length) { if ($indicator.length) {
var $counts = this.make_feed_counts_floater(feed.ps, feed.nt, feed.ng); var $counts = this.make_feed_counts_floater(feed.ps, feed.nt, feed.ng);
$('.feed_counts_floater', $indicator).replaceWith($counts); $('.feed_counts_floater', $indicator).replaceWith($counts);
this.cache.$feed_counts_in_feed_list[feed_id] = null;
$indicator.css({'opacity': 1}); $indicator.css({'opacity': 1});
} else if (feed) { } else if (feed) {
$indicator = $.make('div', { className: 'NB-story-title-indicator' }, [ $indicator = $.make('div', { className: 'NB-story-title-indicator' }, [
@ -3011,14 +2927,14 @@
'positive' : 'positive' :
'neutral'; 'neutral';
var hidden_stories_at_threshold = _.any(this.model.stories, _.bind(function(story) { var hidden_stories_at_threshold = _.any(this.model.stories, _.bind(function(story) {
var score = this.compute_story_score(story); var score = NEWSBLUR.utils.compute_story_score(story);
if (unread_view_name == 'positive') return score == 0; if (unread_view_name == 'positive') return score == 0;
else if (unread_view_name == 'neutral') return score < 0; else if (unread_view_name == 'neutral') return score < 0;
}, this)); }, this));
var hidden_stories_below_threshold = unread_view_name == 'positive' && var hidden_stories_below_threshold = unread_view_name == 'positive' &&
_.any(this.model.stories, _.bind(function(story) { _.any(this.model.stories, _.bind(function(story) {
var score = this.compute_story_score(story); var score = NEWSBLUR.utils.compute_story_score(story);
return score < 0; return score < 0;
}, this)); }, this));
@ -3164,7 +3080,7 @@
var read = story.read_status var read = story.read_status
? ' read ' ? ' read '
: ''; : '';
var score = this.compute_story_score(story); var score = NEWSBLUR.utils.compute_story_score(story);
var score_color = 'neutral'; var score_color = 'neutral';
var river_stories = options['river_stories'] var river_stories = options['river_stories']
? ' NB-river-story ' ? ' NB-river-story '
@ -3189,11 +3105,11 @@
$.make('span', { className: 'feed_title' }, feed.feed_title) $.make('span', { className: 'feed_title' }, feed.feed_title)
]) ])
) )
]).css('background-image', this.generate_gradient(feed, 'webkit')) ]).css('background-image', NEWSBLUR.utils.generate_gradient(feed, 'webkit'))
.css('background-image', this.generate_gradient(feed, 'moz')) .css('background-image', NEWSBLUR.utils.generate_gradient(feed, 'moz'))
.css('borderBottom', this.generate_gradient(feed, 'border')) .css('borderBottom', NEWSBLUR.utils.generate_gradient(feed, 'border'))
.css('borderTop', this.generate_gradient(feed, 'border')) .css('borderTop', NEWSBLUR.utils.generate_gradient(feed, 'border'))
.toggleClass('NB-inverse', this.is_feed_floater_gradient_light(feed)), .toggleClass('NB-inverse', NEWSBLUR.utils.is_feed_floater_gradient_light(feed)),
$.make('div', { className: 'NB-feed-story-header-info' }, [ $.make('div', { className: 'NB-feed-story-header-info' }, [
(story.story_authors && (story.story_authors &&
this.make_story_feed_author(story)), this.make_story_feed_author(story)),
@ -4599,6 +4515,7 @@
var $new_feed = $(self.make_feed_title_template(feeds[feed_id], 'feed')); var $new_feed = $(self.make_feed_title_template(feeds[feed_id], 'feed'));
if ($feed.hasClass('NB-toplevel')) $new_feed.addClass('NB-toplevel'); if ($feed.hasClass('NB-toplevel')) $new_feed.addClass('NB-toplevel');
$feed.replaceWith($new_feed); $feed.replaceWith($new_feed);
self.cache.$feed_in_feed_list[feed_id] = null;
self.hover_over_feed_titles($new_feed); self.hover_over_feed_titles($new_feed);
if (self.active_feed == feed_id) { if (self.active_feed == feed_id) {
self.open_feed(feed_id, true, $new_feed); self.open_feed(feed_id, true, $new_feed);
@ -4633,8 +4550,7 @@
var feed = this.model.get_feed(feed_id); var feed = this.model.get_feed(feed_id);
if (!feed) continue; if (!feed) continue;
var $feed = $(this.make_feed_title_template(feed, 'feed')); var $feed = $(this.make_feed_title_template(feed, 'feed'));
var $feed_on_page = this.cache.$feed_in_feed_list[feed_id] || var $feed_on_page = this.find_feed_in_feed_list(feed_id);
this.find_feed_in_feed_list(feed_id);
if (feed_id == this.active_feed) { if (feed_id == this.active_feed) {
NEWSBLUR.log(['UPDATING INLINE', feed.feed_title, $feed, $feed_on_page, replace_active_feed]); NEWSBLUR.log(['UPDATING INLINE', feed.feed_title, $feed, $feed_on_page, replace_active_feed]);
@ -4648,6 +4564,7 @@
} else { } else {
if ($feed_on_page.hasClass('NB-toplevel')) $feed.addClass('NB-toplevel'); if ($feed_on_page.hasClass('NB-toplevel')) $feed.addClass('NB-toplevel');
$feed_on_page.replaceWith($feed); $feed_on_page.replaceWith($feed);
this.cache.$feed_in_feed_list[feed_id] = null;
this.mark_feed_as_selected(this.active_feed, $feed); this.mark_feed_as_selected(this.active_feed, $feed);
if (!single_feed_id) this.recalculate_story_scores(feed_id); if (!single_feed_id) this.recalculate_story_scores(feed_id);
this.show_feed_hidden_story_title_indicator(); this.show_feed_hidden_story_title_indicator();
@ -4659,6 +4576,7 @@
} }
if ($feed_on_page.hasClass('NB-toplevel')) $feed.addClass('NB-toplevel'); if ($feed_on_page.hasClass('NB-toplevel')) $feed.addClass('NB-toplevel');
$feed_on_page.replaceWith($feed); $feed_on_page.replaceWith($feed);
this.cache.$feed_in_feed_list[feed_id] = null;
(function($feed) { (function($feed) {
$feed.css({'backgroundColor': '#D7DDE6'}); $feed.css({'backgroundColor': '#D7DDE6'});
$feed.animate({ $feed.animate({
@ -4701,7 +4619,7 @@
if ($story && $story.length) { if ($story && $story.length) {
// Just update intelligence // Just update intelligence
var score = this.compute_story_score(story); var score = NEWSBLUR.utils.compute_story_score(story);
$story.removeClass('NB-story-neutral') $story.removeClass('NB-story-neutral')
.removeClass('NB-story-negative') .removeClass('NB-story-negative')
.removeClass('NB-story-positive'); .removeClass('NB-story-positive');
@ -5134,13 +5052,44 @@
this.open_add_feed_modal({url: feed_address}); this.open_add_feed_modal({url: feed_address});
}, },
load_recommended_feed: function(direction, refresh) { approve_feed_in_moderation_queue: function(feed_id) {
var self = this; var self = this;
var $module = $('.NB-module-recommended'); var $module = $('.NB-module-recommended.NB-recommended-unmoderated');
$module.addClass('NB-loading');
var date = $('.NB-recommended-moderation-date').val();
this.model.approve_feed_in_moderation_queue(feed_id, date, function(resp) {
if (!resp) return;
$module.removeClass('NB-loading');
$module.replaceWith(resp);
self.load_javascript_elements_on_page();
});
},
decline_feed_in_moderation_queue: function(feed_id) {
var self = this;
var $module = $('.NB-module-recommended.NB-recommended-unmoderated');
$module.addClass('NB-loading');
this.model.decline_feed_in_moderation_queue(feed_id, function(resp) {
if (!resp) return;
$module.removeClass('NB-loading');
$module.replaceWith(resp);
self.load_javascript_elements_on_page();
});
},
load_recommended_feed: function(direction, refresh, unmoderated) {
var self = this;
var $module = unmoderated ?
$('.NB-module-recommended.NB-recommended-unmoderated') :
$('.NB-module-recommended:not(.NB-recommended-unmoderated)');
$module.addClass('NB-loading'); $module.addClass('NB-loading');
direction = direction || 0; direction = direction || 0;
this.model.load_recommended_feed(this.counts['recommended_feed_page']+direction, !!refresh, function(resp) { this.model.load_recommended_feed(this.counts['recommended_feed_page']+direction,
!!refresh, unmoderated, function(resp) {
if (!resp) return; if (!resp) return;
self.counts['recommended_feed_page'] += direction; self.counts['recommended_feed_page'] += direction;
@ -5653,17 +5602,33 @@
}); });
}); });
$.targetIs(e, { tagSelector: '.NB-recommended-decline' }, function($t, $p){
e.preventDefault();
var feed_id = $t.closest('.NB-recommended').attr('data-feed-id');
self.decline_feed_in_moderation_queue(feed_id);
});
$.targetIs(e, { tagSelector: '.NB-recommended-approve' }, function($t, $p){
e.preventDefault();
var feed_id = $t.closest('.NB-recommended').attr('data-feed-id');
self.approve_feed_in_moderation_queue(feed_id);
});
$.targetIs(e, { tagSelector: '.NB-module-recommended .NB-module-next-page' }, function($t, $p){ $.targetIs(e, { tagSelector: '.NB-module-recommended .NB-module-next-page' }, function($t, $p){
e.preventDefault(); e.preventDefault();
if (!$t.hasClass('NB-disabled')) { if (!$t.hasClass('NB-disabled')) {
self.load_recommended_feed(1); console.log(['parent', $t.closest('.NB-module-recommended'), $t.closest('.NB-module-recommended').hasClass('NB-recommended-unmoderated')]);
var unmoderated = $t.closest('.NB-module-recommended').hasClass('NB-recommended-unmoderated');
self.load_recommended_feed(1, false, unmoderated);
} }
}); });
$.targetIs(e, { tagSelector: '.NB-module-recommended .NB-module-previous-page' }, function($t, $p){ $.targetIs(e, { tagSelector: '.NB-module-recommended .NB-module-previous-page' }, function($t, $p){
e.preventDefault(); e.preventDefault();
if (!$t.hasClass('NB-disabled')) { if (!$t.hasClass('NB-disabled')) {
self.load_recommended_feed(-1); var unmoderated = $t.closest('.NB-module-recommended').hasClass('NB-recommended-unmoderated');
console.log(['parent', $t.closest('.NB-module-recommended')]);
self.load_recommended_feed(-1, false, unmoderated);
} }
}); });

View file

@ -0,0 +1,95 @@
NEWSBLUR.utils = {
compute_story_score: function(story) {
var score = 0;
var score_max = Math.max(story.intelligence['title'],
story.intelligence['author'],
story.intelligence['tags']);
var score_min = Math.min(story.intelligence['title'],
story.intelligence['author'],
story.intelligence['tags']);
if (score_max > 0) score = score_max;
else if (score_min < 0) score = score_min;
if (score == 0) score = story.intelligence['feed'];
return score;
},
generate_gradient: function(feed, type) {
if (!feed) return '';
var color = feed.favicon_color;
if (!color) return '';
var r = parseInt(color.substr(0, 2), 16);
var g = parseInt(color.substr(2, 2), 16);
var b = parseInt(color.substr(4, 2), 16);
if (type == 'border') {
return [
'1px solid rgb(',
[
parseInt(r*(6/8), 10),
parseInt(g*(6/8), 10),
parseInt(b*(6/8), 10)
].join(','),
')'
].join('');
} else if (type == 'webkit') {
return [
'-webkit-gradient(',
'linear,',
'left bottom,',
'left top,',
'color-stop(0, rgba(',
[
r,
g,
b,
255
].join(','),
')),',
'color-stop(1, rgba(',
[
r+35,
g+35,
b+35,
255
].join(','),
')))'
].join('');
} else if (type == 'moz') {
return [
'-moz-linear-gradient(',
'center bottom,',
'rgb(',
[
r,
g,
b
].join(','),
') 0%,',
'rgb(',
[
r+35,
g+35,
b+35
].join(','),
') 100%)'
].join('');
}
},
is_feed_floater_gradient_light: function(feed) {
if (!feed) return false;
var color = feed.favicon_color;
if (!color) return false;
var r = parseInt(color.substr(0, 2), 16) / 255.0;
var g = parseInt(color.substr(2, 2), 16) / 255.0;
var b = parseInt(color.substr(4, 2), 16) / 255.0;
return $.textColor({r: r, g: g, b: b}) != 'white';
}
};

View file

@ -155,7 +155,7 @@ LOGGING = {
COMPRESS_JS = { COMPRESS_JS = {
'all': { 'all': {
'source_filenames': ( 'source_filenames': (
'js/jquery-1.6.js', 'js/jquery-1.6.1.js',
'js/inflector.js', 'js/inflector.js',
'js/jquery.json.js', 'js/jquery.json.js',
'js/jquery.easing.js', 'js/jquery.easing.js',
@ -183,6 +183,7 @@ COMPRESS_JS = {
'js/jquery.flot.js', 'js/jquery.flot.js',
'js/jquery.tipsy.js', 'js/jquery.tipsy.js',
'js/underscore.js', 'js/underscore.js',
'js/newsblur/reader_utils.js',
'js/newsblur/assetmodel.js', 'js/newsblur/assetmodel.js',
'js/newsblur/reader.js', 'js/newsblur/reader.js',
'js/newsblur/generate_bookmarklet.js', 'js/newsblur/generate_bookmarklet.js',
@ -204,6 +205,22 @@ COMPRESS_JS = {
), ),
'output_filename': 'js/all-compressed-?.js' 'output_filename': 'js/all-compressed-?.js'
}, },
'mobile': {
'source_filenames': (
'js/jquery-1.6.1.js',
'js/mobile/jquery.mobile-1.0b1.js',
'js/jquery.ajaxmanager.3.js',
'js/underscore.js',
'js/inflector.js',
'js/jquery.json.js',
'js/jquery.easing.js',
'js/jquery.newsblur.js',
'js/newsblur/reader_utils.js',
'js/newsblur/assetmodel.js',
'js/mobile/newsblur/mobile_workspace.js',
),
'output_filename': 'js/mobile-compressed-?.js',
},
'paypal': { 'paypal': {
'source_filenames': ( 'source_filenames': (
'js/newsblur/paypal_return.js', 'js/newsblur/paypal_return.js',
@ -234,6 +251,13 @@ COMPRESS_CSS = {
), ),
'output_filename': 'css/all-compressed-?.css' 'output_filename': 'css/all-compressed-?.css'
}, },
'mobile': {
'source_filenames': (
'css/mobile/jquery.mobile-1.0b1.css',
'css/mobile/mobile.css',
),
'output_filename': 'css/mobile/mobile-compressed-?.css',
},
'paypal': { 'paypal': {
'source_filenames': ( 'source_filenames': (
'css/paypal_return.css', 'css/paypal_return.css',
@ -305,6 +329,8 @@ INSTALLED_APPS = (
'apps.profile', 'apps.profile',
'apps.recommendations', 'apps.recommendations',
'apps.statistics', 'apps.statistics',
'apps.static',
'apps.mobile',
'south', 'south',
'utils', 'utils',
'vendor', 'vendor',

View file

@ -0,0 +1,116 @@
{% load compressed utils_tags %}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}NewsBlur{% endblock %}</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<link rel="shortcut icon" HREF="/media/img/favicon.png">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="/media/img/logo_128.png"/>
<script type="text/javascript" charset="utf-8">
var NEWSBLUR = {};
NEWSBLUR.Globals = {
'is_authenticated' : {{ user.is_authenticated|yesno:"true,false" }},
'is_anonymous' : {{ user.is_anonymous|yesno:"true,false" }},
'is_premium' : {{ user.profile.is_premium|yesno:"true,false" }},
'secret_token' : "{{ user.profile.secret_token }}",
'username' : "{{ user.username|safe }}",
'email' : "{{ user.email|safe }}",
'google_favicon_url' : 'http://www.google.com/s2/favicons?domain_url=',
'MEDIA_URL' : "{{ MEDIA_URL }}"
};
NEWSBLUR.Flags = {
'start_import_from_google_reader': {{ start_import_from_google_reader|yesno:"true,false" }}
};
NEWSBLUR.URLs = {
'domain' : "{% current_domain %}"
};
</script>
{% compressed_css 'mobile' %}
{% block head_js %}
{% compressed_js 'mobile' %}
{% endblock head_js %}
{% block extra_head_js %}
{% endblock extra_head_js %}
{% if not debug %}
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-8371683-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
{% endif %}
</head>
<body>
<div data-role="page" data-url="feeds" id="NB-page-feeds">
<div data-role="header" data-backbtn="false">
<h1>{{ request.user.username }}</h1>
</div>
<div data-role="content">
<div id="NB-feed-list"></div>
</div>
<div data-role="footer">
<div class="NB-footer-site-count ui-li-count">12 sites</div>
</div>
</div>
<div data-role="page" data-url="stories" id="NB-page-stories">
<div data-role="header">
<a href="#" data-icon="arrow-l" data-back-btn-text="Sites" data-rel="back">Sites</a>
<h1>Stories</h1>
</div>
<div data-role="content">
<div id="NB-story-list"></div>
</div>
<div data-role="footer" class="ui-header"> 1
<div class="NB-footer-site-count ui-li-count">12 sites</div>
</div>
</div>
<div data-role="page" data-url="story" id="NB-page-story">
<div data-role="header">
<a href="#" data-icon="arrow-l" data-back-btn-text="Stories" data-rel="back">Stories</a>
<h1>Story</h1>
<a href="#" data-icon="arrow-d" class="ui-btn-right NB-next">Next Unread</a>
</div>
<div data-role="content">
<div id="NB-story-detail">
</div>
</div>
<div data-role="footer" class="ui-header">
<a href="#" data-icon="arrow-u" class="NB-previous">Previous</a>
<h1>...</h1>
<a href="#" data-icon="arrow-d" class="ui-btn-right NB-next">Next Unread</a>
</div>
</div>
<script>
$(document).bind('mobileinit', function() {
$.mobile.ajaxEnabled = false;
});
$(document).bind('ready', function() {
NEWSBLUR.mobile_reader = new NEWSBLUR.MobileReader();
});
</script>
</body>
</html>

View file

@ -84,6 +84,11 @@ $(document).ready(function() {
{% render_recommended_feed recommended_feeds %} {% render_recommended_feed recommended_feeds %}
{% endif %} {% endif %}
{% if user.is_staff and unmoderated_feeds %}
{% render_recommended_feed unmoderated_feeds 1 %}
{% endif %}
</div> </div>

View file

@ -1,6 +1,10 @@
<div class="NB-module-recommended NB-module"> <div class="NB-module-recommended NB-module {% if unmoderated %}NB-recommended-unmoderated{% endif %}">
<h5 class="NB-module-header"> <h5 class="NB-module-header">
Recommended Site {% if unmoderated %}
Moderation Queue
{% else %}
Recommended Site
{% endif %}
<div class="NB-module-header-right"> <div class="NB-module-header-right">
{% if has_next_page or has_previous_page %} {% if has_next_page or has_previous_page %}
<a href="#" class="NB-module-direction NB-module-next-page NB-javascript {% if not has_next_page %}NB-disabled{% endif %}"></a> <a href="#" class="NB-module-direction NB-module-next-page NB-javascript {% if not has_next_page %}NB-disabled{% endif %}"></a>
@ -8,9 +12,15 @@
{% endif %} {% endif %}
<div class="NB-spinner NB-left"></div> <div class="NB-spinner NB-left"></div>
<div class="NB-module-recommended-date"> <div class="NB-module-recommended-date">
{{ recommended_feed.approved_date|date:"M j" }} {% if unmoderated %}
<span>{{ recommended_feed.approved_date|date:"S" }}</span>, {{ recommended_feed.created_date|date:"M j" }}
{{ recommended_feed.approved_date|date:"Y" }} <span>{{ recommended_feed.created_date|date:"S" }}</span>,
{{ recommended_feed.created_date|date:"Y" }}
{% else %}
{{ recommended_feed.approved_date|date:"M j" }}
<span>{{ recommended_feed.approved_date|date:"S" }}</span>,
{{ recommended_feed.approved_date|date:"Y" }}
{% endif %}
</div> </div>
</div> </div>
</h5> </h5>
@ -34,6 +44,11 @@
<div class="NB-recommended-try NB-modal-submit-green NB-modal-submit-button NB-javascript">Try</div> <div class="NB-recommended-try NB-modal-submit-green NB-modal-submit-button NB-javascript">Try</div>
<div class="NB-recommended-add NB-modal-submit-close NB-modal-submit-button NB-javascript">Add</div> <div class="NB-recommended-add NB-modal-submit-close NB-modal-submit-button NB-javascript">Add</div>
{% endif %} {% endif %}
{% if unmoderated %}
<input type="text" class="NB-recommended-moderation-date" name="date" value="{{ today|date:"Y-m-d" }}" />
<div class="NB-recommended-approve NB-modal-submit-green NB-modal-submit-button NB-javascript">Approve</div>
<div class="NB-recommended-decline NB-modal-submit-close NB-modal-submit-button NB-javascript">Decline</div>
{% endif %}
</div> </div>
</div> </div>

View file

@ -13,6 +13,8 @@ urlpatterns = patterns('',
(r'^api/', include('apps.api.urls')), (r'^api/', include('apps.api.urls')),
(r'^recommendations/', include('apps.recommendations.urls')), (r'^recommendations/', include('apps.recommendations.urls')),
(r'^statistics/', include('apps.statistics.urls')), (r'^statistics/', include('apps.statistics.urls')),
(r'^mobile/', include('apps.mobile.urls')),
(r'^m/', include('apps.mobile.urls')),
url(r'^about/?', static_views.about, name='about'), url(r'^about/?', static_views.about, name='about'),
url(r'^faq/?', static_views.faq, name='faq'), url(r'^faq/?', static_views.faq, name='faq'),
url(r'^api/?', static_views.api, name='api'), url(r'^api/?', static_views.api, name='api'),

View file

@ -212,11 +212,11 @@ class ProcessFeed:
# if story.get('published') > end_date: # if story.get('published') > end_date:
# end_date = story.get('published') # end_date = story.get('published')
story_guids.append(story.get('guid') or story.get('link')) story_guids.append(story.get('guid') or story.get('link'))
existing_stories = MStory.objects( existing_stories = list(MStory.objects(
# story_guid__in=story_guids, # story_guid__in=story_guids,
story_date__gte=start_date, story_date__gte=start_date,
story_feed_id=self.feed.pk story_feed_id=self.feed.pk
).limit(len(story_guids)) ).limit(len(story_guids)))
# MStory.objects( # MStory.objects(
# (Q(story_date__gte=start_date) & Q(story_date__lte=end_date)) # (Q(story_date__gte=start_date) & Q(story_date__lte=end_date))

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,25 @@ def ajax_login_required(function=None):
else: else:
return _dec(function) return _dec(function)
def admin_only(function=None):
def _dec(view_func):
def _view(request, *args, **kwargs):
if not request.user.is_staff:
return HttpResponseForbidden()
else:
return view_func(request, *args, **kwargs)
_view.__name__ = view_func.__name__
_view.__dict__ = view_func.__dict__
_view.__doc__ = view_func.__doc__
return _view
if function is None:
return _dec
else:
return _dec(function)
def get_user(request): def get_user(request):
if not hasattr(request, 'user'): if not hasattr(request, 'user'):
user = request user = request

View file

@ -59,8 +59,8 @@ class Opml(object):
return self._outlines[index] return self._outlines[index]
def from_string(opml_text): def from_string(opml_text):
parser = lxml.etree.XMLParser(recover=True)
return Opml(lxml.etree.fromstring(opml_text)) return Opml(lxml.etree.fromstring(opml_text, parser))
def parse(opml_url): def parse(opml_url):