mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-04-05 03:00:15 -04:00
overhaul of JS and form handling code, not based on jQuery
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -15,11 +15,13 @@
|
||||
lighttpd.**
|
||||
development.ini
|
||||
development_*.ini
|
||||
staging.ini
|
||||
production.ini
|
||||
r2/r2/public/static/frame.js
|
||||
r2/r2/public/static/reddit.js
|
||||
r2/r2/public/static/vote.js
|
||||
r2/r2/public/static/reddit_rtl.css
|
||||
*.update
|
||||
r2/r2/public/static/*.js
|
||||
r2/r2/public/static/*.css
|
||||
r2/r2/**/defunct/**
|
||||
r2/r2/**/**/defunct/**
|
||||
r2/r2admin
|
||||
r2/reddit_i18n
|
||||
r2/data/*
|
||||
|
||||
93
r2/Makefile
Normal file
93
r2/Makefile
Normal file
@@ -0,0 +1,93 @@
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
# software over a computer network and provide for limited attribution for the
|
||||
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
# with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Reddit.
|
||||
#
|
||||
# The Original Developer is the Initial Developer. The Initial Developer of the
|
||||
# Original Code is CondeNet, Inc.
|
||||
#
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
# Jacascript files to be compressified
|
||||
js_targets = jquery.js jquery.json.js jquery.reddit.js reddit.js
|
||||
# CSS targets
|
||||
css_targets = reddit.css reddit-ie6-hax.css
|
||||
|
||||
SED=sed
|
||||
|
||||
package = r2
|
||||
static_dir = $(package)/public/static
|
||||
contrib = $(package)/lib/contrib
|
||||
|
||||
JSCOMPRESS = $(contrib)/jsmin.py
|
||||
|
||||
# If admin codebase is install, get its path so that we can build ini
|
||||
# files against the primary production.ini
|
||||
PRIVATEREPOS = $(shell python -c 'exec "try: import r2admin; print r2admin.__path__[0]\nexcept:pass"')
|
||||
|
||||
#------
|
||||
|
||||
JSTARGETS := $(foreach js, $(js_targets), $(static_dir)/$(js))
|
||||
CSSTARGETS := $(foreach css, $(css_targets), $(static_dir)/$(css))
|
||||
RTLCSS = $(CSSTARGETS:.css=-rtl.css)
|
||||
|
||||
|
||||
MD5S = $(JSTARGETS:=.md5) $(CSSTARGETS:=.md5)
|
||||
|
||||
ifdef PRIVATEREPOS
|
||||
INIUPDATE = $(wildcard *.update)
|
||||
INIS = $(INIUPDATE:.update=.ini)
|
||||
|
||||
%.ini: %.update
|
||||
ln -sf `pwd`/$< $(PRIVATEREPOS)/..
|
||||
make -C $(PRIVATEREPOS)/.. $@
|
||||
ln -sf $(PRIVATEREPOS)/../$@ .
|
||||
endif
|
||||
|
||||
all: $(JSTARGETS) $(CSSTARGETS) $(MD5S) $(RTLCSS) $(INIS)
|
||||
|
||||
.PHONY: js css md5 rtl clean all
|
||||
|
||||
$(MD5S): %.md5 : %
|
||||
cat $< | openssl md5 > $@
|
||||
|
||||
$(JSTARGETS): $(static_dir)/%.js : $(static_dir)/js/%.js
|
||||
$(JSCOMPRESS) < $< > $@
|
||||
|
||||
$(CSSTARGETS): $(static_dir)/%.css : $(static_dir)/css/%.css
|
||||
$(SED) -e 's/ \+/ /' \
|
||||
-e 's/\/\*.*\*\///g' \
|
||||
-e 's/: /:/' \
|
||||
$< | grep -v "^ *$$" > $@
|
||||
|
||||
$(RTLCSS): %-rtl.css : %.css
|
||||
$(SED) -e "s/left/>####</g" \
|
||||
-e "s/right/left/g" \
|
||||
-e "s/>####</right/g" \
|
||||
-e "s/\(margin\|padding\):\s*\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)/\1:\2 \5 \4 \3/g" $< > $@
|
||||
|
||||
|
||||
|
||||
|
||||
js: $(JSTARGETS)
|
||||
|
||||
css: $(CSSTARGETS)
|
||||
|
||||
md5: $(MD5S)
|
||||
|
||||
rtl: $(RTLCSS)
|
||||
|
||||
clean:
|
||||
rm $(JSTARGETS) $(CSSTARGETS) $(MD5S) $(INIS)
|
||||
@@ -1,71 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
# software over a computer network and provide for limited attribution for the
|
||||
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
# with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Reddit.
|
||||
#
|
||||
# The Original Developer is the Initial Developer. The Initial Developer of the
|
||||
# Original Code is CondeNet, Inc.
|
||||
#
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
files=( psrs.js utils.js animate.js link.js comments.js subreddit.js vote_piece.js reddit_piece.js organic.js )
|
||||
|
||||
wd=`pwd`
|
||||
redditjs='reddit.js'
|
||||
framejs='frame.js'
|
||||
votejs='vote.js'
|
||||
compressor=" $wd/r2/lib/contrib/jsjam -g -i"
|
||||
|
||||
|
||||
echo "generating rtl style sheet"
|
||||
|
||||
./rtl.sh
|
||||
|
||||
|
||||
echo "Generating reddit.js..."
|
||||
|
||||
cd r2/public/static
|
||||
[ -e $redditjs ] && rm $redditjs
|
||||
[ -e $redditjs-big ] && rm $redditjs-big
|
||||
|
||||
cat json.js > $redditjs.tmp
|
||||
for f in "${files[@]}"
|
||||
do
|
||||
$compressor $f >> $redditjs.tmp
|
||||
done;
|
||||
sed 's/\$/ \$/g' $redditjs.tmp > $redditjs
|
||||
|
||||
|
||||
echo "Generating vote.js..."
|
||||
# compress the votes alone (for buttons)
|
||||
cat psrs.js | $compressor | sed 's/\$/ \$/g' > $votejs
|
||||
cat utils.js vote_piece.js | $compressor >> $votejs
|
||||
|
||||
echo "Generating frame.js..."
|
||||
# compress frame alone (for the toolbar)
|
||||
cat psrs.js > $framejs
|
||||
cat vote_piece.js utils.js frame_piece.js | $compressor >> $framejs
|
||||
|
||||
echo "droppping md5s..."
|
||||
for file in *.js
|
||||
do
|
||||
cat $file | openssl md5 > $file.md5
|
||||
done
|
||||
for file in *.css
|
||||
do
|
||||
cat $file | openssl md5 > $file.md5
|
||||
done
|
||||
@@ -200,7 +200,6 @@ class DomainMiddleware(object):
|
||||
#this is a cname, and redirect to the frame controller.
|
||||
#Ignore localhost so paster shell still works.
|
||||
#If this is an error, don't redirect
|
||||
|
||||
if (not sub_domains.endswith(base_domain)
|
||||
and (not sub_domains == 'localhost')):
|
||||
environ['sub_domain'] = sub_domains
|
||||
@@ -210,11 +209,11 @@ class DomainMiddleware(object):
|
||||
elif self.is_auth_cname(sub_domains):
|
||||
environ['frameless_cname'] = True
|
||||
environ['authorized_cname'] = True
|
||||
elif ("redditSession" in environ.get('HTTP_COOKIE', '')
|
||||
elif ("redditSession=cname" in environ.get('HTTP_COOKIE', '')
|
||||
and environ['REQUEST_METHOD'] != 'POST'
|
||||
and not environ['PATH_INFO'].startswith('/error')):
|
||||
environ['original_path'] = environ['PATH_INFO']
|
||||
environ['PATH_INFO'] = '/frame'
|
||||
environ['FULLPATH'] = environ['PATH_INFO'] = '/frame'
|
||||
else:
|
||||
environ['frameless_cname'] = True
|
||||
return self.app(environ, start_response)
|
||||
|
||||
@@ -72,11 +72,12 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/feedback', controller='feedback', action='feedback')
|
||||
mc('/ad_inq', controller='feedback', action='ad_inq')
|
||||
|
||||
mc('/admin/:action', controller='admin')
|
||||
|
||||
mc('/admin/i18n', controller='i18n', action='list')
|
||||
mc('/admin/i18n/:action', controller='i18n')
|
||||
mc('/admin/i18n/:action/:lang', controller='i18n')
|
||||
|
||||
mc('/admin/:action', controller='admin')
|
||||
|
||||
mc('/user/:username/:where', controller='user', action='listing',
|
||||
where='overview')
|
||||
@@ -100,6 +101,9 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/mail/optin', controller='front', action = 'optin')
|
||||
mc('/stylesheet', controller = 'front', action = 'stylesheet')
|
||||
mc('/frame', controller='front', action = 'frame')
|
||||
mc('/framebuster/:blah', controller='front', action = 'framebuster')
|
||||
mc('/framebuster/:what/:blah',
|
||||
controller='front', action = 'framebuster')
|
||||
|
||||
mc('/promote/edit_promo/:link', controller='promote', action = 'edit_promo')
|
||||
mc('/promote/:action', controller='promote')
|
||||
@@ -152,6 +156,7 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/mobile', controller='redirect', action='redirect',
|
||||
dest='http://m.reddit.com/')
|
||||
|
||||
mc('/authorize_embed', controller = 'front', action = 'authorize_embed')
|
||||
|
||||
# This route handles displaying the error page and
|
||||
# graphics used in the 404/500
|
||||
@@ -161,6 +166,6 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/error/document/:id', controller='error', action="document")
|
||||
|
||||
mc("/*url", controller='front', action='catchall')
|
||||
|
||||
|
||||
return map
|
||||
|
||||
|
||||
@@ -41,5 +41,6 @@ api('morechildren', MoreCommentJsonTemplate)
|
||||
api('reddit', RedditJsonTemplate)
|
||||
api('panestack', PanestackJsonTemplate)
|
||||
api('listing', ListingJsonTemplate)
|
||||
api('usertableitem', UserItemJsonTemplate)
|
||||
|
||||
api('organiclisting', OrganicListingJsonTemplate)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,10 @@ class ButtonsController(RedditController):
|
||||
def get_link(self, url):
|
||||
try:
|
||||
sr = None if isinstance(c.site, FakeSubreddit) else c.site
|
||||
links = tup(Link._by_url(url, sr))
|
||||
try:
|
||||
links = tup(Link._by_url(url, sr))
|
||||
except NotFound:
|
||||
return None
|
||||
#find the one with the highest score
|
||||
return max(links, key = lambda x: x._score)
|
||||
except:
|
||||
@@ -71,6 +74,11 @@ class ButtonsController(RedditController):
|
||||
width = VInt('width', 0, 800),
|
||||
link = VByName('id'))
|
||||
def GET_button_content(self, url, title, css, vote, newwindow, width, link):
|
||||
|
||||
# no buttons on domain listings
|
||||
if isinstance(c.site, DomainSR):
|
||||
return self.abort404()
|
||||
|
||||
l = self.wrap_link(link or self.get_link(url))
|
||||
if l: url = l.url
|
||||
|
||||
@@ -107,6 +115,10 @@ class ButtonsController(RedditController):
|
||||
_height = VInt('height', 0, 300),
|
||||
_width = VInt('width', 0, 800))
|
||||
def GET_button_embed(self, buttontype, _height, _width, url):
|
||||
# no buttons on domain listings
|
||||
if isinstance(c.site, DomainSR):
|
||||
return self.abort404()
|
||||
|
||||
c.render_style = 'js'
|
||||
c.response_content_type = 'text/javascript; charset=UTF-8'
|
||||
|
||||
@@ -150,6 +162,9 @@ class ButtonsController(RedditController):
|
||||
|
||||
|
||||
def GET_button_demo_page(self):
|
||||
# no buttons for domain listings -> redirect to top level
|
||||
if isinstance(c.site, DomainSR):
|
||||
return self.redirect('/buttons')
|
||||
return BoringPage(_("reddit buttons"),
|
||||
show_sidebar = False,
|
||||
content=ButtonDemoPanel()).render()
|
||||
|
||||
@@ -151,6 +151,8 @@ class ErrorController(RedditController):
|
||||
except:
|
||||
return handle_awful_failure("something really bad just happened.")
|
||||
|
||||
POST_document = GET_document
|
||||
|
||||
def handle_awful_failure(fail_text):
|
||||
"""
|
||||
Makes sure that no errors generated in the error handler percolate
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from r2.lib.utils import Storage
|
||||
from r2.lib.utils import Storage, tup
|
||||
from pylons.i18n import _
|
||||
from copy import copy
|
||||
|
||||
@@ -35,6 +35,8 @@ error_list = dict((
|
||||
('NO_THING_ID', _('id not specified')),
|
||||
('NOT_AUTHOR', _("you can't do that")),
|
||||
('BAD_COMMENT', _('please enter a comment')),
|
||||
('DELETED_COMMENT', _('that comment has been deleted')),
|
||||
('DELETED_THING', _('that element has been deleted.')),
|
||||
('BAD_PASSWORD', _('invalid password')),
|
||||
('WRONG_PASSWORD', _('invalid password')),
|
||||
('BAD_PASSWORD_MATCH', _('passwords do not match')),
|
||||
@@ -70,11 +72,13 @@ error_list = dict((
|
||||
errors = Storage([(e, e) for e in error_list.keys()])
|
||||
|
||||
class Error(object):
|
||||
#__slots__ = ('name', 'message')
|
||||
def __init__(self, name, i18n_message, msg_params):
|
||||
|
||||
def __init__(self, name, i18n_message, msg_params, field = None):
|
||||
self.name = name
|
||||
self.i18n_message = i18n_message
|
||||
self.msg_params = msg_params
|
||||
# list of fields in the original form that caused the error
|
||||
self.fields = tup(field) if field else []
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
@@ -105,12 +109,13 @@ class ErrorSet(object):
|
||||
for x in self.errors:
|
||||
yield x
|
||||
|
||||
def _add(self, error_name, msg, msg_params = {}):
|
||||
self.errors[error_name] = Error(error_name, msg, msg_params)
|
||||
def _add(self, error_name, msg, msg_params = {}, field = None):
|
||||
self.errors[error_name] = Error(error_name, msg, msg_params,
|
||||
field = field)
|
||||
|
||||
def add(self, error_name, msg_params = {}):
|
||||
def add(self, error_name, msg_params = {}, field = None):
|
||||
msg = error_list[error_name]
|
||||
self._add(error_name, msg, msg_params = msg_params)
|
||||
self._add(error_name, msg, msg_params = msg_params, field = field)
|
||||
|
||||
def remove(self, error_name):
|
||||
if self.errors.has_key(error_name):
|
||||
|
||||
@@ -165,11 +165,13 @@ class FrontController(RedditController):
|
||||
|
||||
# insert reply box only for logged in user
|
||||
if c.user_is_loggedin and article.subreddit_slow.can_comment(c.user):
|
||||
displayPane.append(CommentReplyBox())
|
||||
#no comment box for permalinks
|
||||
if not comment:
|
||||
displayPane.append(CommentReplyBox(link_name =
|
||||
article._fullname))
|
||||
else:
|
||||
displayPane.append(CommentReplyBox())
|
||||
|
||||
# finally add the comment listing
|
||||
displayPane.append(listing.listing())
|
||||
|
||||
@@ -410,9 +412,18 @@ class FrontController(RedditController):
|
||||
|
||||
# dest is the location to redirect to upon completion
|
||||
dest = request.get.get('dest','') or request.referer or '/'
|
||||
if (c.user_is_loggedin and
|
||||
not request.environ.get('extension') == 'embed'):
|
||||
return self.redirect(dest)
|
||||
return LoginPage(dest = dest).render()
|
||||
|
||||
def GET_logout(self):
|
||||
dest = request.referer or '/'
|
||||
return self.redirect(dest)
|
||||
|
||||
@validate(VUser(),
|
||||
VModhash())
|
||||
def POST_logout(self):
|
||||
"""wipe login cookie and redirect to referer."""
|
||||
self.logout()
|
||||
dest = request.referer or '/'
|
||||
@@ -450,7 +461,7 @@ class FrontController(RedditController):
|
||||
return ''
|
||||
|
||||
|
||||
@validate(VUser(),
|
||||
@validate(VUser(),
|
||||
VSRSubmitPage(),
|
||||
url = VRequired('url', None),
|
||||
title = VRequired('title', None))
|
||||
@@ -466,20 +477,12 @@ class FrontController(RedditController):
|
||||
redirect_link = listing.things[0]
|
||||
# if there is more than one, check the users' subscriptions
|
||||
else:
|
||||
subscribed = [l for l in listing.things
|
||||
if c.user_is_loggedin
|
||||
and l.subreddit.is_subscriber_defaults(c.user)]
|
||||
|
||||
#if there is only 1 link to be displayed, just go there
|
||||
if len(subscribed) == 1:
|
||||
redirect_link = subscribed[0]
|
||||
else:
|
||||
infotext = strings.multiple_submitted % \
|
||||
listing.things[0].resubmit_link()
|
||||
res = BoringPage(_("seen it"),
|
||||
content = listing,
|
||||
infotext = infotext).render()
|
||||
return res
|
||||
infotext = strings.multiple_submitted % \
|
||||
listing.things[0].resubmit_link()
|
||||
res = BoringPage(_("seen it"),
|
||||
content = listing,
|
||||
infotext = infotext).render()
|
||||
return res
|
||||
|
||||
# we've found a link already. Redirect to its permalink page
|
||||
if redirect_link:
|
||||
@@ -530,14 +533,35 @@ class FrontController(RedditController):
|
||||
return Cnameframe(original_path, sr, sub_domain).render()
|
||||
|
||||
|
||||
def GET_framebuster(self):
|
||||
if c.site.domain and c.user_is_loggedin:
|
||||
u = UrlParser(c.site.path + "/frame")
|
||||
u.put_in_frame()
|
||||
c.cname = True
|
||||
def GET_framebuster(self, what = None, blah = None):
|
||||
"""
|
||||
renders the contents of the iframe which, on a cname, checks
|
||||
if the user is currently logged into reddit.
|
||||
|
||||
if this page is hit from the primary domain, redirects to the
|
||||
cnamed domain version of the site. If the user is logged in,
|
||||
this cnamed version will drop a boolean session cookie on that
|
||||
domain so that subsequent page reloads will be caught in
|
||||
middleware and a frame will be inserted around the content.
|
||||
|
||||
If the user is not logged in, previous session cookies will be
|
||||
emptied so that subsequent refreshes will not be rendered in
|
||||
that pesky frame.
|
||||
"""
|
||||
if not c.site.domain:
|
||||
return ""
|
||||
elif c.cname:
|
||||
return FrameBuster(login = (what == "login")).render()
|
||||
else:
|
||||
path = "/framebuster/"
|
||||
if c.user_is_loggedin:
|
||||
path += "login/"
|
||||
u = UrlParser(path + str(random.random()))
|
||||
u.mk_cname(require_frame = False, subreddit = c.site,
|
||||
port = request.port)
|
||||
return self.redirect(u.unparse())
|
||||
|
||||
return "fail"
|
||||
# the user is not logged in or there is no cname.
|
||||
return FrameBuster(login = False).render()
|
||||
|
||||
def GET_catchall(self):
|
||||
return self.abort404()
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
################################################################################
|
||||
from pylons import request, g
|
||||
from reddit_base import RedditController
|
||||
from api import Json
|
||||
from r2.lib.pages import UnfoundPage, AdminTranslations, AdminPage
|
||||
|
||||
from r2.lib.translation import Translator, TranslatorTemplate, get_translator
|
||||
|
||||
@@ -155,10 +155,11 @@ class PostController(ApiController):
|
||||
|
||||
|
||||
def POST_login(self, *a, **kw):
|
||||
res = ApiController.POST_login(self, *a, **kw)
|
||||
ApiController.POST_login(self, *a, **kw)
|
||||
c.render_style = "html"
|
||||
c.response_content_type = ""
|
||||
|
||||
dest = request.post.get('dest', request.referer or '/')
|
||||
errors = list(c.errors)
|
||||
if errors:
|
||||
for e in errors:
|
||||
@@ -168,16 +169,17 @@ class PostController(ApiController):
|
||||
c.errors._add(e + "_login", msg)
|
||||
|
||||
dest = request.post.get('dest', request.referer or '/')
|
||||
return LoginPage(user_login = request.post.get('user_login'),
|
||||
return LoginPage(user_login = request.post.get('user'),
|
||||
dest = dest).render()
|
||||
|
||||
return self.redirect(res.redirect)
|
||||
return self.redirect(dest)
|
||||
|
||||
def POST_reg(self, *a, **kw):
|
||||
res = ApiController.POST_register(self, *a, **kw)
|
||||
ApiController.POST_register(self, *a, **kw)
|
||||
c.render_style = "html"
|
||||
c.response_content_type = ""
|
||||
|
||||
dest = request.post.get('dest', request.referer or '/')
|
||||
errors = list(c.errors)
|
||||
if errors:
|
||||
for e in errors:
|
||||
@@ -185,12 +187,10 @@ class PostController(ApiController):
|
||||
msg = c.errors[e].message
|
||||
c.errors.remove(e)
|
||||
c.errors._add(e + "_reg", msg)
|
||||
|
||||
dest = request.post.get('dest', request.referer or '/')
|
||||
return LoginPage(user_reg = request.post.get('user_reg'),
|
||||
return LoginPage(user_reg = request.post.get('user'),
|
||||
dest = dest).render()
|
||||
|
||||
return self.redirect(res.redirect)
|
||||
return self.redirect(dest)
|
||||
|
||||
def GET_login(self, *a, **kw):
|
||||
return self.redirect('/login' + query_string(dict(dest="/")))
|
||||
|
||||
@@ -31,7 +31,6 @@ from r2.lib.cache import LocalCache
|
||||
import random as rand
|
||||
from r2.models.account import valid_cookie, FakeAccount
|
||||
from r2.models.subreddit import Subreddit
|
||||
import r2.config as config
|
||||
from r2.models import *
|
||||
from errors import ErrorSet
|
||||
from validator import *
|
||||
@@ -41,7 +40,7 @@ from r2.lib.jsontemplates import api_type
|
||||
from copy import copy
|
||||
from Cookie import CookieError
|
||||
from datetime import datetime
|
||||
import sha, inspect, simplejson
|
||||
import sha, simplejson
|
||||
from urllib import quote, unquote
|
||||
|
||||
from r2.lib.tracking import encrypt, decrypt
|
||||
@@ -61,8 +60,8 @@ class Cookie(object):
|
||||
self.dirty = dirty
|
||||
if domain:
|
||||
self.domain = domain
|
||||
elif c.authorized_cname:
|
||||
self.domain = c.site.domain
|
||||
elif c.authorized_cname and not c.default_sr:
|
||||
self.domain = utils.common_subdomain(request.host, c.site.domain)
|
||||
else:
|
||||
self.domain = g.domain
|
||||
|
||||
@@ -259,11 +258,14 @@ def set_content_type():
|
||||
c.response_content_type = e['content_type']
|
||||
|
||||
if e.has_key('extension'):
|
||||
ext = e['extension']
|
||||
c.extension = ext = e['extension']
|
||||
if ext == 'api' or ext.startswith('json'):
|
||||
c.response_access_control = 'allow <*>'
|
||||
if ext in ('embed', 'wired'):
|
||||
c.response_wrappers.append(utils.to_js)
|
||||
if ext in ('embed', 'wired', 'widget'):
|
||||
def to_js(content):
|
||||
return utils.to_js(content,callback = request.params.get(
|
||||
"callback", "document.write"))
|
||||
c.response_wrappers.append(to_js)
|
||||
|
||||
def get_browser_langs():
|
||||
browser_langs = []
|
||||
@@ -351,19 +353,22 @@ def set_recent_reddits():
|
||||
names = read_user_cookie('recent_reddits')
|
||||
c.recent_reddits = []
|
||||
if names:
|
||||
names = filter(None, names.split(','))
|
||||
c.recent_reddits = Subreddit._by_fullname(names, data = True,
|
||||
return_dict = False)
|
||||
names = filter(None, names.strip('[]').split(','))
|
||||
try:
|
||||
c.recent_reddits = Subreddit._by_fullname(names, data = True,
|
||||
return_dict = False)
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
def ratelimit_agents():
|
||||
user_agent = request.user_agent
|
||||
for s in g.agents:
|
||||
if s and user_agent and s in user_agent.lower():
|
||||
key = 'rate_agent_' + s
|
||||
if cache.get(s):
|
||||
if g.cache.get(s):
|
||||
abort(503, 'service temporarily unavailable')
|
||||
else:
|
||||
cache.set(s, 't', time = 1)
|
||||
g.cache.set(s, 't', time = 1)
|
||||
|
||||
#TODO i want to get rid of this function. once the listings in front.py are
|
||||
#moved into listingcontroller, we shouldn't have a need for this
|
||||
@@ -374,7 +379,7 @@ def base_listing(fn):
|
||||
before = VByName('before'),
|
||||
count = VCount('count'))
|
||||
def new_fn(self, before, **env):
|
||||
kw = self.build_arg_list(fn, env)
|
||||
kw = build_arg_list(fn, env)
|
||||
|
||||
#turn before into after/reverse
|
||||
kw['reverse'] = False
|
||||
@@ -387,26 +392,6 @@ def base_listing(fn):
|
||||
|
||||
class RedditController(BaseController):
|
||||
|
||||
@staticmethod
|
||||
def build_arg_list(fn, env):
|
||||
"""given a fn and and environment the builds a keyword argument list
|
||||
for fn"""
|
||||
kw = {}
|
||||
argspec = inspect.getargspec(fn)
|
||||
|
||||
# if there is a **kw argument in the fn definition,
|
||||
# just pass along the environment
|
||||
if argspec[2]:
|
||||
kw = env
|
||||
#else for each entry in the arglist set the value from the environment
|
||||
else:
|
||||
#skip self
|
||||
argnames = argspec[0][1:]
|
||||
for name in argnames:
|
||||
if name in env:
|
||||
kw[name] = env[name]
|
||||
return kw
|
||||
|
||||
def request_key(self):
|
||||
# note that this references the cookie at request time, not
|
||||
# the current value of it
|
||||
@@ -481,7 +466,6 @@ class RedditController(BaseController):
|
||||
if hasattr(c.user, 'msgtime') and c.user.msgtime:
|
||||
c.have_messages = c.user.msgtime
|
||||
c.user_is_admin = maybe_admin and c.user.name in g.admins
|
||||
|
||||
c.user_is_sponsor = c.user_is_admin or c.user.name in g.sponsors
|
||||
|
||||
c.over18 = over18()
|
||||
@@ -521,7 +505,7 @@ class RedditController(BaseController):
|
||||
|
||||
#check content cache
|
||||
if not c.user_is_loggedin:
|
||||
r = cache.get(self.request_key())
|
||||
r = g.rendercache.get(self.request_key())
|
||||
if r and request.method == 'GET':
|
||||
response = c.response
|
||||
response.headers = r.headers
|
||||
@@ -545,7 +529,7 @@ class RedditController(BaseController):
|
||||
c.used_cache = True
|
||||
# response wrappers have already been applied before cache write
|
||||
c.response_wrappers = []
|
||||
|
||||
|
||||
def post(self):
|
||||
response = c.response
|
||||
content = response.content
|
||||
@@ -564,7 +548,7 @@ class RedditController(BaseController):
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
|
||||
# send cookies
|
||||
if not c.used_cache:
|
||||
if not c.used_cache and c.cookies:
|
||||
# if we used the cache, these cookies should be set by the
|
||||
# cached response object instead
|
||||
for k,v in c.cookies.iteritems():
|
||||
@@ -581,9 +565,9 @@ class RedditController(BaseController):
|
||||
and not c.user_is_loggedin
|
||||
and not c.used_cache
|
||||
and response.content and response.content[0]):
|
||||
config.cache.set(self.request_key(),
|
||||
response,
|
||||
g.page_cache_time)
|
||||
g.rendercache.set(self.request_key(),
|
||||
response,
|
||||
g.page_cache_time)
|
||||
|
||||
def check_modified(self, thing, action):
|
||||
if c.user_is_loggedin:
|
||||
@@ -615,3 +599,4 @@ class RedditController(BaseController):
|
||||
return request.path + utils.query_string(merged)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ from pylons.controllers.util import abort
|
||||
from r2.lib import utils, captcha
|
||||
from r2.lib.filters import unkeep_space, websafe, _force_unicode
|
||||
from r2.lib.db.operators import asc, desc
|
||||
from r2.config import cache
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.jsonresponse import json_respond
|
||||
from r2.lib.jsonresponse import json_respond, JQueryResponse
|
||||
from r2.lib.jsontemplates import api_type
|
||||
|
||||
from r2.models import *
|
||||
|
||||
@@ -35,7 +35,7 @@ from r2.controllers.errors import errors, UserRequiredException
|
||||
|
||||
from copy import copy
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
import re, inspect
|
||||
|
||||
class Validator(object):
|
||||
default_param = None
|
||||
@@ -48,6 +48,13 @@ class Validator(object):
|
||||
self.default = default
|
||||
self.post, self.get, self.url = post, get, url
|
||||
|
||||
def set_error(self, error, msg_params = {}):
|
||||
"""
|
||||
Adds the provided error to c.errors and flags that it is come
|
||||
from the validator's param
|
||||
"""
|
||||
c.errors.add(error, msg_params = msg_params, field = self.param)
|
||||
|
||||
def __call__(self, url):
|
||||
a = []
|
||||
if self.param:
|
||||
@@ -63,27 +70,103 @@ class Validator(object):
|
||||
a.append(val)
|
||||
return self.run(*a)
|
||||
|
||||
|
||||
def build_arg_list(fn, env):
|
||||
"""given a fn and and environment the builds a keyword argument list
|
||||
for fn"""
|
||||
kw = {}
|
||||
argspec = inspect.getargspec(fn)
|
||||
|
||||
# if there is a **kw argument in the fn definition,
|
||||
# just pass along the environment
|
||||
if argspec[2]:
|
||||
kw = env
|
||||
#else for each entry in the arglist set the value from the environment
|
||||
else:
|
||||
#skip self
|
||||
argnames = argspec[0][1:]
|
||||
for name in argnames:
|
||||
if name in env:
|
||||
kw[name] = env[name]
|
||||
return kw
|
||||
|
||||
def _make_validated_kw(fn, simple_vals, param_vals, env):
|
||||
for validator in simple_vals:
|
||||
validator(env)
|
||||
kw = build_arg_list(fn, env)
|
||||
for var, validator in param_vals.iteritems():
|
||||
kw[var] = validator(env)
|
||||
return kw
|
||||
|
||||
|
||||
def validate(*simple_vals, **param_vals):
|
||||
def val(fn):
|
||||
def newfn(self, *a, **env):
|
||||
try:
|
||||
for validator in simple_vals:
|
||||
validator(env)
|
||||
|
||||
kw = self.build_arg_list(fn, env)
|
||||
for var, validator in param_vals.iteritems():
|
||||
kw[var] = validator(env)
|
||||
|
||||
kw = _make_validated_kw(fn, simple_vals, param_vals, env)
|
||||
return fn(self, *a, **kw)
|
||||
|
||||
except UserRequiredException:
|
||||
if request.method == "POST" and hasattr(self, "ajax_login_redirect"):
|
||||
# ajax failure, so redirect accordingly
|
||||
return self.ajax_login_redirect("/")
|
||||
return self.intermediate_redirect('/login')
|
||||
return newfn
|
||||
return val
|
||||
|
||||
def noresponse(*simple_vals, **param_vals):
|
||||
"""
|
||||
AJAXy decorator which takes the place of validate when no response
|
||||
is expected from the controller method.
|
||||
"""
|
||||
def val(fn):
|
||||
def newfn(self, *a, **env):
|
||||
c.render_style = api_type('html')
|
||||
c.response_content_type = 'application/json; charset=UTF-8'
|
||||
jquery = JQueryResponse()
|
||||
|
||||
validate(*simple_vals, **param_vals)(fn)(self, *a, **env)
|
||||
|
||||
return self.response_func()
|
||||
return newfn
|
||||
return val
|
||||
|
||||
|
||||
def validatedForm(*simple_vals, **param_vals):
|
||||
"""
|
||||
AJAX response validator for general form handling. In addition to
|
||||
validating simple_vals and param_vals in the same way as validate,
|
||||
a jquery object and a jquery form object are allocated and passed
|
||||
into the method which is decorated.
|
||||
"""
|
||||
def val(fn):
|
||||
def newfn(self, *a, **env):
|
||||
# set the content type for the response
|
||||
c.render_style = api_type('html')
|
||||
c.response_content_type = 'application/json; charset=UTF-8'
|
||||
|
||||
# generate a response object
|
||||
jquery = JQueryResponse()
|
||||
# generate a form object
|
||||
form = jquery(request.POST.get('id', "body"))
|
||||
# clear out the status line as a courtesy
|
||||
form.set_html(".status", "")
|
||||
|
||||
try:
|
||||
|
||||
kw = _make_validated_kw(fn, simple_vals, param_vals, env)
|
||||
val = fn(self, form, jquery, *a, **kw)
|
||||
|
||||
# auto-refresh the captcha if there are errors.
|
||||
if (c.errors.errors and
|
||||
any(isinstance(v, VCaptcha) for v in simple_vals)):
|
||||
form.new_captcha()
|
||||
|
||||
if val: return val
|
||||
return self.response_func(**dict(list(jquery)))
|
||||
|
||||
except UserRequiredException:
|
||||
return self.ajax_login_redirect("/")
|
||||
return newfn
|
||||
return val
|
||||
|
||||
|
||||
|
||||
#### validators ####
|
||||
class nop(Validator):
|
||||
@@ -107,7 +190,7 @@ class VRequired(Validator):
|
||||
def error(self, e = None):
|
||||
if not e: e = self._error
|
||||
if e:
|
||||
c.errors.add(e)
|
||||
self.set_error(e)
|
||||
|
||||
def run(self, item):
|
||||
if not item:
|
||||
@@ -206,16 +289,16 @@ class VLength(Validator):
|
||||
|
||||
def run(self, title):
|
||||
if not title:
|
||||
c.errors.add(self.emp_error)
|
||||
self.set_error(self.emp_error)
|
||||
elif len(title) > self.length:
|
||||
c.errors.add(self.len_error)
|
||||
self.set_error(self.len_error)
|
||||
else:
|
||||
return title
|
||||
|
||||
class VTitle(VLength):
|
||||
only_whitespace = re.compile(r"^\s*$", re.UNICODE)
|
||||
|
||||
def __init__(self, item, length = 200, **kw):
|
||||
def __init__(self, item, length = 300, **kw):
|
||||
VLength.__init__(self, item, length = length,
|
||||
empty_error = errors.NO_TITLE,
|
||||
length_error = errors.TITLE_TOO_LONG, **kw)
|
||||
@@ -223,7 +306,7 @@ class VTitle(VLength):
|
||||
def run(self, title):
|
||||
title = VLength.run(self, title)
|
||||
if title and self.only_whitespace.match(title):
|
||||
c.errors.add(errors.NO_TITLE)
|
||||
self.set_error(errors.NO_TITLE)
|
||||
else:
|
||||
return title
|
||||
|
||||
@@ -256,16 +339,16 @@ class VSubredditName(VRequired):
|
||||
class VSubredditTitle(Validator):
|
||||
def run(self, title):
|
||||
if not title:
|
||||
c.errors.add(errors.NO_TITLE)
|
||||
self.set_error(errors.NO_TITLE)
|
||||
elif len(title) > 100:
|
||||
c.errors.add(errors.TITLE_TOO_LONG)
|
||||
self.set_error(errors.TITLE_TOO_LONG)
|
||||
else:
|
||||
return title
|
||||
|
||||
class VSubredditDesc(Validator):
|
||||
def run(self, description):
|
||||
if description and len(description) > 500:
|
||||
c.errors.add(errors.DESC_TOO_LONG)
|
||||
self.set_error(errors.DESC_TOO_LONG)
|
||||
return unkeep_space(description or '')
|
||||
|
||||
class VAccountByName(VRequired):
|
||||
@@ -307,7 +390,7 @@ class VCaptcha(Validator):
|
||||
def run(self, iden, solution):
|
||||
if (not c.user_is_loggedin or c.user.needs_captcha()):
|
||||
if not captcha.valid_solution(iden, solution):
|
||||
c.errors.add(errors.BAD_CAPTCHA)
|
||||
self.set_error(errors.BAD_CAPTCHA)
|
||||
|
||||
class VUser(Validator):
|
||||
def run(self, password = None):
|
||||
@@ -315,7 +398,7 @@ class VUser(Validator):
|
||||
raise UserRequiredException
|
||||
|
||||
if (password is not None) and not valid_password(c.user, password):
|
||||
c.errors.add(errors.WRONG_PASSWORD)
|
||||
self.set_error(errors.WRONG_PASSWORD)
|
||||
|
||||
class VModhash(Validator):
|
||||
default_param = 'uh'
|
||||
@@ -380,6 +463,8 @@ class VSubmitParent(Validator):
|
||||
def run(self, fullname):
|
||||
if fullname:
|
||||
parent = Thing._by_fullname(fullname, False, data=True)
|
||||
if parent and parent._deleted:
|
||||
self.set_error(errors.DELETED_COMMENT)
|
||||
if isinstance(parent, Message):
|
||||
return parent
|
||||
else:
|
||||
@@ -393,8 +478,8 @@ class VSubmitSR(Validator):
|
||||
def run(self, sr_name):
|
||||
try:
|
||||
sr = Subreddit._by_name(sr_name)
|
||||
except NotFound:
|
||||
c.errors.add(errors.SUBREDDIT_NOEXIST)
|
||||
except (NotFound, AttributeError):
|
||||
self.set_error(errors.SUBREDDIT_NOEXIST)
|
||||
sr = None
|
||||
|
||||
if sr and not (c.user_is_loggedin and sr.can_submit(c.user)):
|
||||
@@ -461,7 +546,8 @@ class VSanitizedUrl(Validator):
|
||||
return utils.sanitize_url(url)
|
||||
|
||||
class VUrl(VRequired):
|
||||
def __init__(self, item, *a, **kw):
|
||||
def __init__(self, item, allow_self = True, *a, **kw):
|
||||
self.allow_self = allow_self
|
||||
VRequired.__init__(self, item, errors.NO_URL, *a, **kw)
|
||||
|
||||
def run(self, url, sr = None):
|
||||
@@ -471,7 +557,7 @@ class VUrl(VRequired):
|
||||
try:
|
||||
sr = Subreddit._by_name(sr)
|
||||
except NotFound:
|
||||
c.errors.add(errors.SUBREDDIT_NOEXIST)
|
||||
self.set_error(errors.SUBREDDIT_NOEXIST)
|
||||
sr = None
|
||||
else:
|
||||
sr = None
|
||||
@@ -480,7 +566,8 @@ class VUrl(VRequired):
|
||||
return self.error(errors.NO_URL)
|
||||
url = utils.sanitize_url(url)
|
||||
if url == 'self':
|
||||
return url
|
||||
if self.allow_self:
|
||||
return url
|
||||
elif url:
|
||||
try:
|
||||
l = Link._by_url(url, sr)
|
||||
@@ -532,7 +619,7 @@ class VInt(Validator):
|
||||
val = self.max
|
||||
return val
|
||||
except ValueError:
|
||||
c.errors.add(errors.BAD_NUMBER)
|
||||
self.set_error(errors.BAD_NUMBER)
|
||||
|
||||
class VCssName(Validator):
|
||||
"""
|
||||
@@ -591,11 +678,11 @@ class VRatelimit(Validator):
|
||||
if self.rate_ip:
|
||||
to_check.append('ip' + str(request.ip))
|
||||
|
||||
r = cache.get_multi(to_check, self.prefix)
|
||||
r = g.cache.get_multi(to_check, self.prefix)
|
||||
if r:
|
||||
expire_time = max(r.values())
|
||||
time = utils.timeuntil(expire_time)
|
||||
c.errors.add(errors.RATELIMIT, {'time': time})
|
||||
self.set_error(errors.RATELIMIT, {'time': time})
|
||||
|
||||
@classmethod
|
||||
def ratelimit(self, rate_user = False, rate_ip = False, prefix = "rate_"):
|
||||
@@ -607,7 +694,7 @@ class VRatelimit(Validator):
|
||||
if rate_ip:
|
||||
to_set['ip' + str(request.ip)] = expire_time
|
||||
|
||||
cache.set_multi(to_set, prefix, time = seconds)
|
||||
g.cache.set_multi(to_set, prefix, time = seconds)
|
||||
|
||||
class VCommentIDs(Validator):
|
||||
#id_str is a comma separated list of id36's
|
||||
@@ -635,16 +722,18 @@ class VCacheKey(Validator):
|
||||
|
||||
def run(self, key, name):
|
||||
if key:
|
||||
uid = cache.get(str(self.cache_prefix + "_" + key))
|
||||
uid = g.cache.get(str(self.cache_prefix + "_" + key))
|
||||
try:
|
||||
a = Account._byID(uid, data = True)
|
||||
g.cache.delete(str(self.cache_prefix + "_" + key))
|
||||
except NotFound:
|
||||
return None
|
||||
if name and a.name.lower() != name.lower():
|
||||
c.errors.add(errors.BAD_USERNAME)
|
||||
self.set_error(errors.BAD_USERNAME)
|
||||
if a:
|
||||
return a
|
||||
c.errors.add(errors.EXPIRED)
|
||||
|
||||
self.set_error(errors.EXPIRED)
|
||||
|
||||
class VOneOf(Validator):
|
||||
def __init__(self, param, options = (), *a, **kw):
|
||||
@@ -653,7 +742,7 @@ class VOneOf(Validator):
|
||||
|
||||
def run(self, val):
|
||||
if self.options and val not in self.options:
|
||||
c.errors.add(errors.INVALID_OPTION)
|
||||
self.set_error(errors.INVALID_OPTION)
|
||||
return self.default
|
||||
else:
|
||||
return val
|
||||
@@ -713,51 +802,55 @@ class ValidEmails(Validator):
|
||||
# special case for 1: there should be no delineators at all, so
|
||||
# send back original string to the user
|
||||
if self.num == 1:
|
||||
c.errors.add(errors.BAD_EMAILS,
|
||||
self.set_error(errors.BAD_EMAILS,
|
||||
{'emails': '"%s"' % emails0})
|
||||
# else report the number expected
|
||||
else:
|
||||
c.errors.add(errors.TOO_MANY_EMAILS,
|
||||
self.set_error(errors.TOO_MANY_EMAILS,
|
||||
{'num': self.num})
|
||||
# correct number, but invalid formatting
|
||||
elif failures:
|
||||
c.errors.add(errors.BAD_EMAILS,
|
||||
self.set_error(errors.BAD_EMAILS,
|
||||
{'emails': ', '.join(failures)})
|
||||
# no emails
|
||||
elif not emails:
|
||||
c.errors.add(errors.NO_EMAILS)
|
||||
self.set_error(errors.NO_EMAILS)
|
||||
else:
|
||||
# return single email if one is expected, list otherwise
|
||||
return list(emails)[0] if self.num == 1 else emails
|
||||
|
||||
|
||||
class VCnameDomain(Validator):
|
||||
domain_re = re.compile(r'^([\w]+\.)+[\w]+$')
|
||||
domain_re = re.compile(r'^([\w\-_]+\.)+[\w]+$')
|
||||
|
||||
def run(self, domain):
|
||||
if (domain
|
||||
and (not self.domain_re.match(domain)
|
||||
or domain.endswith('.reddit.com')
|
||||
or len(domain) > 300)):
|
||||
c.errors.add(errors.BAD_CNAME)
|
||||
self.set_error(errors.BAD_CNAME)
|
||||
elif domain:
|
||||
try:
|
||||
return str(domain).lower()
|
||||
except UnicodeEncodeError:
|
||||
c.errors.add(errors.BAD_CNAME)
|
||||
|
||||
|
||||
self.set_error(errors.BAD_CNAME)
|
||||
|
||||
class VTranslation(Validator):
|
||||
def run(self):
|
||||
from r2.lib.translation import Translator
|
||||
if Translator.exists(self.param):
|
||||
return Translator(locale = self.param)
|
||||
|
||||
# NOTE: make sure *never* to have res check these are present
|
||||
# otherwise, the response could contain reference to these errors...!
|
||||
class ValidIP(Validator):
|
||||
def run(self):
|
||||
if is_banned_IP(request.ip):
|
||||
c.errors.add(errors.BANNED_IP)
|
||||
self.set_error(errors.BANNED_IP)
|
||||
return request.ip
|
||||
|
||||
class ValidDomain(Validator):
|
||||
def run(self, url):
|
||||
if url and is_banned_domain(url):
|
||||
c.errors.add(errors.BANNED_DOMAIN)
|
||||
self.set_error(errors.BANNED_DOMAIN)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
from pylons import Response, c, g, cache, request, session, config
|
||||
from pylons import Response, c, g, request, session, config
|
||||
from pylons.controllers import WSGIController, Controller
|
||||
from pylons.i18n import N_, _, ungettext, get_lang
|
||||
import r2.lib.helpers as h
|
||||
@@ -114,7 +114,8 @@ class BaseController(WSGIController):
|
||||
if not kw.has_key('port'):
|
||||
kw['port'] = request.port
|
||||
|
||||
# disentagle the cname (for urls that would have cnameframe=1 in them)
|
||||
# disentagle the cname (for urls that would have
|
||||
# cnameframe=1 in them)
|
||||
u.mk_cname(**kw)
|
||||
|
||||
# make sure the extensions agree with the current page
|
||||
@@ -134,8 +135,12 @@ class BaseController(WSGIController):
|
||||
and added as the "dest" parameter of the new url.
|
||||
"""
|
||||
from r2.lib.template_helpers import add_sr
|
||||
dest = cls.format_output_url(request.fullpath)
|
||||
path = add_sr(form_path + query_string({"dest": dest}))
|
||||
params = dict(dest = cls.format_output_url(request.fullpath))
|
||||
if c.extension == "widget" and request.GET.get("callback"):
|
||||
params['callback'] = request.GET.get("callback")
|
||||
|
||||
path = add_sr(cls.format_output_url(form_path) +
|
||||
query_string(params))
|
||||
return cls.redirect(path)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
import random, string
|
||||
#TODO find a better way to cache the captchas
|
||||
from r2.config import cache
|
||||
from pylons import g
|
||||
from Captcha.Base import randomIdentifier
|
||||
from Captcha.Visual import Text, Backgrounds, Distortions, ImageCaptcha
|
||||
|
||||
@@ -48,22 +47,21 @@ def make_solution():
|
||||
return randomIdentifier(alphabet=string.ascii_letters, length = SOL_LENGTH).upper()
|
||||
|
||||
def get_image(iden):
|
||||
solution = cache.get(str(iden))
|
||||
solution = g.rendercache.get(str(iden))
|
||||
if not solution:
|
||||
solution = make_solution()
|
||||
cache.set(str(iden), solution, time = 300)
|
||||
g = RandCaptcha(solution=solution)
|
||||
return g.render()
|
||||
g.rendercache.set(str(iden), solution, time = 300)
|
||||
return RandCaptcha(solution=solution).render()
|
||||
|
||||
def valid_solution(iden, solution):
|
||||
if (not iden
|
||||
or not solution
|
||||
or len(iden) != IDEN_LENGTH
|
||||
or len(solution) != SOL_LENGTH
|
||||
or solution.upper() != cache.get(str(iden))):
|
||||
or solution.upper() != g.rendercache.get(str(iden))):
|
||||
solution = make_solution()
|
||||
cache.set(str(iden), solution, time = 300)
|
||||
g.rendercache.set(str(iden), solution, time = 300)
|
||||
return False
|
||||
else:
|
||||
cache.delete(str(iden))
|
||||
g.rendercache.delete(str(iden))
|
||||
return True
|
||||
|
||||
@@ -1,476 +0,0 @@
|
||||
#!/usr/bin/perl -wT
|
||||
use strict;
|
||||
#
|
||||
# Copyright 1998-2005 Eric Hammond <ehammond@thinksome.com>
|
||||
#
|
||||
|
||||
#---- Setup
|
||||
|
||||
BEGIN { # Set envariables for -T tainting.
|
||||
$ENV{'PATH'} = '/bin:/usr/bin';
|
||||
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
||||
}
|
||||
BEGIN { # Extract path and program name.
|
||||
use vars qw($path $prog);
|
||||
$0 =~ m%(.*)[/\\]([^/\\]*)%;
|
||||
($path, $prog) = ($1 || '.', $2 || $0);
|
||||
}
|
||||
|
||||
#---- Packages
|
||||
|
||||
use Getopt::Long;
|
||||
use sigtrap qw(die normal-signals);
|
||||
use IO::File;
|
||||
#use POSIX;
|
||||
|
||||
#---- Constants
|
||||
|
||||
# Version is extracted from CVS/RCS revision.
|
||||
my $REVISION = '$Revision: 1.14 $';
|
||||
use vars qw($VERSION);
|
||||
($VERSION = $REVISION) =~ s%^\$.evision: (.*?) \$$%$1%;
|
||||
|
||||
# Reserved keywords and other identifiers as specified in
|
||||
# "JavaScript: The Definitive Guide" by David Flanagan
|
||||
# Plus a few additional things like standard object/method names.
|
||||
my %RESERVED = map { $_ => 1 }
|
||||
qw(
|
||||
anchor area array boolean button checkbox date document element
|
||||
fileupload form frame function hidden history image javaarray
|
||||
javaclass javaobject javapackage link location math mimetype
|
||||
navigator number object option packages password plugin radio
|
||||
reset select string submit text textarea window abstract alert
|
||||
assign blur boolean break byte case catch char class cleartimeout
|
||||
close closed confirm const continue default defaultstatus delete
|
||||
do document double else escape eval extends false final finally
|
||||
float focus for frames function getclass goto history if
|
||||
implements import in instanceof int interface isnan java length
|
||||
location long name native navigate navigator netscape new null
|
||||
onblur onerror onfocus onload onunload open opener package parent
|
||||
parsefloat parseint private prompt protected prototype public ref
|
||||
return scroll self settimeout short static status sun super
|
||||
switch synchronized taint this throw throws tostring top
|
||||
transient true try typeof unescape untaint valueof var void while
|
||||
window with
|
||||
|
||||
anchor applet area array boolean button checkbox date document e
|
||||
element embed fileupload form frame function hidden history image
|
||||
jsobject javaarray javaclass javamethod javaobject javapackage
|
||||
ln10 ln2 log10e log2e link location max_value min_value math
|
||||
mimetype negative_infinity nan navigator number object option pi
|
||||
positive_infinity packages password plugin radio reset sqrt1_2
|
||||
sqrt2 select string submit text textarea url utc window abs acos
|
||||
action alert alinkcolor anchor anchors appcodename appname
|
||||
appversion applets arguments asin assign atan atan2 back bgcolor
|
||||
big blink blur bold border call caller ceil charat checked clear
|
||||
cleartimeout click close closed complete confirm constructor
|
||||
cookie cos current defaultchecked defaultselected defaultstatus
|
||||
defaultvalue description document domain elements embeds
|
||||
enabledplugin encoding escape eval exp fgcolor filename fixed
|
||||
floor focus fontcolor fontsize form forms forward frames getclass
|
||||
getdate getday gethours getmember getminutes getmonth getseconds
|
||||
getslot gettime gettimezoneoffset getwindow getyear go hash
|
||||
height history host hostname href hspace images index indexof
|
||||
isnan italics java javaenabled join lastindexof lastmodified
|
||||
length link linkcolor links location log lowsrc max method
|
||||
mimetypes min name navigate navigator netscape next onabort
|
||||
onblur onchange onclick onerror onfocus onload onmouseout
|
||||
onmouseover onreset onsubmit onunload open opener options parent
|
||||
parse parsefloat parseint pathname plugins port pow previous
|
||||
prompt protocol prototype random referrer refresh reload
|
||||
removemember replace reset reverse round scroll search select
|
||||
selected selectedindex self setdate sethours setmember setminutes
|
||||
setmonth setseconds setslot settime settimeout setyear sin small
|
||||
sort split sqrt src status strike sub submit substring suffixes
|
||||
sun sup taint taintenabled tan target text title togmtstring
|
||||
tolocalestring tolowercase tostring touppercase top type unescape
|
||||
untaint useragent value valueof vlinkcolor vspace width window
|
||||
write writeln
|
||||
|
||||
fromcharcode all screen classname innertext
|
||||
);
|
||||
|
||||
#---- Options
|
||||
|
||||
use vars qw($debug);
|
||||
$debug = 0;
|
||||
my $help = 0;
|
||||
use vars qw($quiet);
|
||||
$quiet = 0;
|
||||
my $version = 0;
|
||||
my $keep_identifiers = 0;
|
||||
my $keep_globals = 0;
|
||||
my $keep_whitespace = 0;
|
||||
my $keep_newlines = 0;
|
||||
my $keep_comments = 0;
|
||||
my $add_note = undef;
|
||||
|
||||
Getopt::Long::config('no_ignore_case');
|
||||
GetOptions(
|
||||
'debug' => \$debug,
|
||||
'help' => \$help,
|
||||
'quiet' => \$quiet,
|
||||
'version' => \$version,
|
||||
|
||||
'keep-identifiers' => \$keep_identifiers,
|
||||
'i' => \$keep_identifiers,
|
||||
|
||||
'keep-globals' => \$keep_globals,
|
||||
'g' => \$keep_globals,
|
||||
|
||||
'keep-whitespace' => \$keep_whitespace,
|
||||
'w' => \$keep_whitespace,
|
||||
|
||||
'keep-newlines' => \$keep_newlines,
|
||||
'n' => \$keep_newlines,
|
||||
|
||||
'keep-comments' => \$keep_comments,
|
||||
'c' => \$keep_comments,
|
||||
|
||||
'add-note:s' => \$add_note,
|
||||
'a:s' => \$add_note,
|
||||
)
|
||||
or die_usage();
|
||||
|
||||
#---- Initialization
|
||||
|
||||
# Don't buffer output.
|
||||
STDOUT->autoflush(1);
|
||||
STDERR->autoflush(1);
|
||||
|
||||
#---- Main
|
||||
|
||||
$quiet or warn "$prog v${VERSION}a\n";
|
||||
die_usage() if $help;
|
||||
exit 0 if $version;
|
||||
|
||||
# Can't keep comments without keeping newlines
|
||||
$keep_newlines = 1 if $keep_comments;
|
||||
|
||||
# Use stdin if no files specified.
|
||||
unshift(@ARGV, '-') unless scalar @ARGV;
|
||||
|
||||
# For each file...
|
||||
my $filename;
|
||||
while ( $filename = shift ) {
|
||||
$debug and warn "$main::prog: Processing $filename\n";
|
||||
|
||||
local($/) = undef;
|
||||
open(FILE, "< $filename")
|
||||
or die "$main::prog: Unable to open: $filename: $!";
|
||||
my $contents = <FILE>;
|
||||
close(FILE);
|
||||
|
||||
$contents = jam($contents);
|
||||
|
||||
print $contents;
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
#---- Functions
|
||||
|
||||
#
|
||||
# jam - compress the code.
|
||||
#
|
||||
sub jam {
|
||||
my ($contents) = @_;
|
||||
|
||||
# Identifiers which should not be shortened are indicated in the code using:
|
||||
# //jsjam-keep:identifier
|
||||
# where "identifier" is the identifier which should not be shortened.
|
||||
while ( $contents =~ s%//\s*jsjam-keep\s*:\s*(\w+).*\n%\n% ) {
|
||||
my $word = "\L$1\E";
|
||||
++ $RESERVED{$word};
|
||||
}
|
||||
|
||||
# Regexp for non-greedy stuff, counting quoted strings as opaque chunks.
|
||||
# Also count /.*/ regular expressions when they follow one of: = ( ,
|
||||
my $stuff_with_strings = <<'EOM';
|
||||
(?:
|
||||
<!-- .*? -->
|
||||
|
|
||||
[=(,] \s* / (?: \\/ | [^/] )* / \w*
|
||||
|
|
||||
[^"']+?
|
||||
|
|
||||
" (?: \\" | [^"] )* "
|
||||
|
|
||||
' (?: \\' | [^'] )* '
|
||||
)*?
|
||||
EOM
|
||||
|
||||
my $starts_with_comment = $1 if $contents =~ s%^(<!--.*?\n)%%;
|
||||
|
||||
if ( not $keep_comments ) {
|
||||
# Remove comments.
|
||||
$debug and warn "$main::prog: Removing comments\n";
|
||||
# Remove comments as long as the comments are not quoted.
|
||||
# $source = the remainder of the document to process
|
||||
my $source=$contents;
|
||||
my $result="";
|
||||
|
||||
while(length($source)>1){
|
||||
# match \" or \' or "" or ''
|
||||
# and copy to result
|
||||
$source =~ s/^(\\"|\\'|""|'')// && do {
|
||||
$result.=$1;
|
||||
next;
|
||||
};
|
||||
|
||||
# match ".." or '..'
|
||||
# and copy quoted text to result
|
||||
$source =~ s/^(".*?[^\\]"|'.*?[^\\]')// && do {
|
||||
$result.=$1;
|
||||
next;
|
||||
};
|
||||
|
||||
# match //
|
||||
# remove text to end of line
|
||||
$source =~ s/^\/\/.*?\n// && do {
|
||||
next;
|
||||
};
|
||||
|
||||
# match /*
|
||||
# remove text to */
|
||||
$source =~ s/^\/\*.*?\*\///s && do {
|
||||
next;
|
||||
};
|
||||
|
||||
# match string before /* or // or \" or \' or " or '
|
||||
$source =~ s/^(.+?)(\/\*|\/\/|\\"|\\'|"|')/$2/s && do {
|
||||
$result.=$1;
|
||||
next;
|
||||
};
|
||||
|
||||
# Copy remainder of input.
|
||||
$result.=$source;
|
||||
last;
|
||||
}
|
||||
$contents=$result;
|
||||
}
|
||||
|
||||
if ( not $keep_identifiers ) {
|
||||
my $new_contents = '';
|
||||
|
||||
if ( not $keep_globals ) {
|
||||
# Shorten all identifiers.
|
||||
$debug and warn "$main::prog: Shortening all identifiers\n";
|
||||
while ( $contents =~ s%^($stuff_with_strings)\b([_A-Za-z]\w*)%%sx ) {
|
||||
$new_contents .= $1.word($2);
|
||||
}
|
||||
|
||||
} else {
|
||||
# Shorten var identifiers only (TBD: may conflict with globals).
|
||||
$debug and warn "$main::prog: Shortening 'var' identifiers\n";
|
||||
while ($contents =~ s%^($stuff_with_strings\bvar\s+)([_A-Za-z]\w*)%%sx) {
|
||||
$new_contents .= $1.word($2);
|
||||
}
|
||||
}
|
||||
$contents = $new_contents . $contents;
|
||||
}
|
||||
|
||||
if ( not $keep_whitespace ) {
|
||||
# Remove blank lines.
|
||||
$debug and warn "$main::prog: Removing blank lines\n";
|
||||
$contents =~ s%^(\s*\n)%%gm;
|
||||
|
||||
# Compress whitespace.
|
||||
my $new_contents = '';
|
||||
|
||||
if ( not $keep_newlines ) {
|
||||
$debug and warn "$main::prog: Compressing whitespace\n";
|
||||
while ( $contents =~ s%^($stuff_with_strings)\s+%%sx ) {
|
||||
$new_contents .= $1.' ';
|
||||
}
|
||||
|
||||
} else {
|
||||
$debug and warn "$main::prog: Compressing non-newline whitespace\n";
|
||||
while ( $contents =~ s%^($stuff_with_strings)([\ \t]|$)+%%sx ) {
|
||||
$new_contents .= $1.' ';
|
||||
last if $contents =~ m%^\s*$%;
|
||||
}
|
||||
|
||||
}
|
||||
$contents = $new_contents . $contents;
|
||||
|
||||
# Remove whitespace which has punctuation on one side.
|
||||
$new_contents = '';
|
||||
$debug and warn "$main::prog: Removing unneeded whitespace\n";
|
||||
|
||||
my $re_space = $keep_newlines ? '[ \t]' : '\s';
|
||||
while ( $contents =~ s%^($stuff_with_strings)$re_space(\S|$)%$2%sx ) {
|
||||
my ($stuff, $after) = ($1, $2);
|
||||
my $before = $1 if $stuff =~ m%(.)$%;
|
||||
next unless defined $before;
|
||||
$new_contents .= $stuff;
|
||||
if ( $before =~ m%[\w'"@]% and
|
||||
$after =~ m%[\w'"@]% ) {
|
||||
$new_contents .= ' ';
|
||||
}
|
||||
}
|
||||
$contents = $new_contents . $contents;
|
||||
|
||||
# Remove leading/trailing whitespace.
|
||||
$debug and warn "$main::prog: Removing leading/trailing whitespace\n";
|
||||
$contents =~ s%^\s+%%gm;
|
||||
$contents =~ s%\s*$%\n%;
|
||||
|
||||
# Fixup HTML comments.
|
||||
# Unfortunately this breaks the JavaScript: line="<!--"+comment+"-->"
|
||||
# $contents =~ s%(<!--.*-->)%\n$1\n%g;
|
||||
|
||||
if ( $starts_with_comment ) {
|
||||
$contents = $starts_with_comment . $contents . "\n// -->\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Add note if desired.
|
||||
if ( defined $add_note ) {
|
||||
my $time_string = strftime("%Y/%M/%d %H:%M:%S",localtime);
|
||||
$contents .= <<"EOM";
|
||||
// Compressed by jsjam <www.jsjam.com> $time_string $add_note
|
||||
EOM
|
||||
}
|
||||
|
||||
$contents;
|
||||
}
|
||||
|
||||
#
|
||||
# word - lookup/create mapping for a potential identifier.
|
||||
#
|
||||
BEGIN {
|
||||
use vars qw(%map $next_short);
|
||||
%map = ();
|
||||
|
||||
$next_short = 'a';
|
||||
}
|
||||
sub word {
|
||||
my ($word) = @_;
|
||||
|
||||
return $word if defined $RESERVED{"\L$word\E"};
|
||||
|
||||
my $short = $map{$word};
|
||||
if ( not defined $short ) {
|
||||
while ( $RESERVED{$next_short} ) {
|
||||
++ $next_short;
|
||||
}
|
||||
$short = $next_short ++;
|
||||
$map{$word} = $short;
|
||||
$debug and warn "$main::prog: Mapping: $word => $short\n";
|
||||
}
|
||||
|
||||
$short;
|
||||
}
|
||||
|
||||
#
|
||||
# die_usage - Print usage string from manpage at end of file and die
|
||||
#
|
||||
sub die_usage {
|
||||
my $usage;
|
||||
open(PROG, "< $0")
|
||||
or die "$prog: Unable to open $0 to print usage";
|
||||
local($/) = undef;
|
||||
$usage = <PROG>;
|
||||
close(PROG);
|
||||
$usage =~ s%^.*?
|
||||
=head1\sSYNOPSIS\s+
|
||||
(.*?)\s+
|
||||
=head1\sOPTIONS\s*\n
|
||||
(.*?)\s*
|
||||
=head1.*$
|
||||
%Usage: $1\n$2\n%xs;
|
||||
die $usage;
|
||||
}
|
||||
|
||||
=head1 NAME
|
||||
|
||||
jsjam - Compress JavaScript code.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
jsjam [opts] file...
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-d --debug Debug mode.
|
||||
-h --help Print help and exit.
|
||||
-q --quiet Quiet mode.
|
||||
-v --version Print version and exit.
|
||||
|
||||
-i --keep-identifiers Do not shorten identifiers.
|
||||
-g --keep-globals Do not shorten non-"var" identifiers.
|
||||
-w --keep-whitespace Do not compress whitespace.
|
||||
-n --keep-newlines Do not remove newlines following stuff.
|
||||
-c --keep-comments Do not compress comments (implies -n).
|
||||
|
||||
-a --add-note N Adds note "N" to the end of the compressed
|
||||
output (in a // comment).
|
||||
|
||||
The options --keep-identifiers and --keep-globals are not compatible.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
file One or more JavaScript files to compress. If no files
|
||||
are specified, stdin is used.
|
||||
|
||||
=head1 DOWNLOAD
|
||||
|
||||
The jsjam Perl script is available here:
|
||||
|
||||
http://www.anvilon.com/software/download/jsjam
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This program attempts to compress JavaScript so that it downloads
|
||||
faster to the browser. If the identifier compression is kept on, it
|
||||
also has a side-effect of making the JavaScript fairly unreadable.
|
||||
|
||||
The compressed output for all input files is sent to stdout.
|
||||
|
||||
Compression methods include:
|
||||
|
||||
- Strip comments.
|
||||
|
||||
- Strip unnecessary whitespace.
|
||||
|
||||
- Shorten identifiers (variable names, function names, field names).
|
||||
|
||||
Identifiers which should not be shortened can be indicated in the
|
||||
JavaScript code using one line per identifier in the form:
|
||||
|
||||
//jsjam-keep:identifier
|
||||
|
||||
where "identifier" is the identifier which should not be shortened.
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
jsjam --debug mycode.js >mycode-jsjam.js
|
||||
|
||||
=head1 CAVEATS
|
||||
|
||||
This program was originally written to compress one particular set of
|
||||
JavaScript software, but many others have found it useful for their
|
||||
situations as well. If this happens to work or almost work for you,
|
||||
please drop a note to the author. Bug reports are welcomed and may
|
||||
even get fixed, especially if you can provide sample JavaScript code
|
||||
that illustrates the problem.
|
||||
|
||||
If you have any short global variable names (1-2 characters), the
|
||||
--keep-globals option will probably shorten local (var) variables so
|
||||
that they conflict with the global variables.
|
||||
|
||||
In addition to working well on Linux/Unix, this script has reportedly
|
||||
been able to run on Perl under Windows, though some Windows users
|
||||
report that it is better to remove the first line of this script file
|
||||
(#!/usr/bin/perl -wT)
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Original hack by
|
||||
Eric Hammond <http://www.anvilon.com>
|
||||
|
||||
Comment and quoted string processing rewritten by
|
||||
Cameron Shorter <http://cameron.shorter.net>
|
||||
|
||||
=cut
|
||||
218
r2/r2/lib/contrib/jsmin.py
Executable file
218
r2/r2/lib/contrib/jsmin.py
Executable file
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This code is original from jsmin by Douglas Crockford, it was translated to
|
||||
# Python by Baruch Even. The original code had the following copyright and
|
||||
# license.
|
||||
#
|
||||
# /* jsmin.c
|
||||
# 2007-05-22
|
||||
#
|
||||
# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||
# so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# The Software shall be used for Good, not Evil.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# */
|
||||
|
||||
from StringIO import StringIO
|
||||
|
||||
def jsmin(js):
|
||||
ins = StringIO(js)
|
||||
outs = StringIO()
|
||||
JavascriptMinify().minify(ins, outs)
|
||||
str = outs.getvalue()
|
||||
if len(str) > 0 and str[0] == '\n':
|
||||
str = str[1:]
|
||||
return str
|
||||
|
||||
def isAlphanum(c):
|
||||
"""return true if the character is a letter, digit, underscore,
|
||||
dollar sign, or non-ASCII character.
|
||||
"""
|
||||
return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
|
||||
(c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
|
||||
|
||||
class UnterminatedComment(Exception):
|
||||
pass
|
||||
|
||||
class UnterminatedStringLiteral(Exception):
|
||||
pass
|
||||
|
||||
class UnterminatedRegularExpression(Exception):
|
||||
pass
|
||||
|
||||
class JavascriptMinify(object):
|
||||
|
||||
def _outA(self):
|
||||
self.outstream.write(self.theA)
|
||||
def _outB(self):
|
||||
self.outstream.write(self.theB)
|
||||
|
||||
def _get(self):
|
||||
"""return the next character from stdin. Watch out for lookahead. If
|
||||
the character is a control character, translate it to a space or
|
||||
linefeed.
|
||||
"""
|
||||
c = self.theLookahead
|
||||
self.theLookahead = None
|
||||
if c == None:
|
||||
c = self.instream.read(1)
|
||||
if c >= ' ' or c == '\n':
|
||||
return c
|
||||
if c == '': # EOF
|
||||
return '\000'
|
||||
if c == '\r':
|
||||
return '\n'
|
||||
return ' '
|
||||
|
||||
def _peek(self):
|
||||
self.theLookahead = self._get()
|
||||
return self.theLookahead
|
||||
|
||||
def _next(self):
|
||||
"""get the next character, excluding comments. peek() is used to see
|
||||
if an unescaped '/' is followed by a '/' or '*'.
|
||||
"""
|
||||
c = self._get()
|
||||
if c == '/' and self.theA != '\\':
|
||||
p = self._peek()
|
||||
if p == '/':
|
||||
c = self._get()
|
||||
while c > '\n':
|
||||
c = self._get()
|
||||
return c
|
||||
if p == '*':
|
||||
c = self._get()
|
||||
while 1:
|
||||
c = self._get()
|
||||
if c == '*':
|
||||
if self._peek() == '/':
|
||||
self._get()
|
||||
return ' '
|
||||
if c == '\000':
|
||||
raise UnterminatedComment()
|
||||
|
||||
return c
|
||||
|
||||
def _action(self, action):
|
||||
"""do something! What you do is determined by the argument:
|
||||
1 Output A. Copy B to A. Get the next B.
|
||||
2 Copy B to A. Get the next B. (Delete A).
|
||||
3 Get the next B. (Delete B).
|
||||
action treats a string as a single character. Wow!
|
||||
action recognizes a regular expression if it is preceded by ( or , or =.
|
||||
"""
|
||||
if action <= 1:
|
||||
self._outA()
|
||||
|
||||
if action <= 2:
|
||||
self.theA = self.theB
|
||||
if self.theA == "'" or self.theA == '"':
|
||||
while 1:
|
||||
self._outA()
|
||||
self.theA = self._get()
|
||||
if self.theA == self.theB:
|
||||
break
|
||||
if self.theA <= '\n':
|
||||
raise UnterminatedStringLiteral()
|
||||
if self.theA == '\\':
|
||||
self._outA()
|
||||
self.theA = self._get()
|
||||
|
||||
|
||||
if action <= 3:
|
||||
self.theB = self._next()
|
||||
if self.theB == '/' and (self.theA == '(' or self.theA == ',' or
|
||||
self.theA == '=' or self.theA == ':' or
|
||||
self.theA == '[' or self.theA == '?' or
|
||||
self.theA == '!' or self.theA == '&' or
|
||||
self.theA == '|' or self.theA == ';' or
|
||||
self.theA == '{' or self.theA == '}' or
|
||||
self.theA == '\n'):
|
||||
self._outA()
|
||||
self._outB()
|
||||
while 1:
|
||||
self.theA = self._get()
|
||||
if self.theA == '/':
|
||||
break
|
||||
elif self.theA == '\\':
|
||||
self._outA()
|
||||
self.theA = self._get()
|
||||
elif self.theA <= '\n':
|
||||
raise UnterminatedRegularExpression()
|
||||
self._outA()
|
||||
self.theB = self._next()
|
||||
|
||||
|
||||
def _jsmin(self):
|
||||
"""Copy the input to the output, deleting the characters which are
|
||||
insignificant to JavaScript. Comments will be removed. Tabs will be
|
||||
replaced with spaces. Carriage returns will be replaced with linefeeds.
|
||||
Most spaces and linefeeds will be removed.
|
||||
"""
|
||||
self.theA = '\n'
|
||||
self._action(3)
|
||||
|
||||
while self.theA != '\000':
|
||||
if self.theA == ' ':
|
||||
if isAlphanum(self.theB):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(2)
|
||||
elif self.theA == '\n':
|
||||
if self.theB in ['{', '[', '(', '+', '-']:
|
||||
self._action(1)
|
||||
elif self.theB == ' ':
|
||||
self._action(3)
|
||||
else:
|
||||
if isAlphanum(self.theB):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(2)
|
||||
else:
|
||||
if self.theB == ' ':
|
||||
if isAlphanum(self.theA):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(3)
|
||||
elif self.theB == '\n':
|
||||
if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
|
||||
self._action(1)
|
||||
else:
|
||||
if isAlphanum(self.theA):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(3)
|
||||
else:
|
||||
self._action(1)
|
||||
|
||||
def minify(self, instream, outstream):
|
||||
self.instream = instream
|
||||
self.outstream = outstream
|
||||
self.theA = '\n'
|
||||
self.theB = None
|
||||
self.theLookahead = None
|
||||
|
||||
self._jsmin()
|
||||
self.instream.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
jsm = JavascriptMinify()
|
||||
jsm.minify(sys.stdin, sys.stdout)
|
||||
@@ -328,7 +328,7 @@ def find_preview_links(sr):
|
||||
|
||||
return links
|
||||
|
||||
def rendered_link(id, res, links, media, compress):
|
||||
def rendered_link(links, media, compress):
|
||||
from pylons.controllers.util import abort
|
||||
from r2.controllers import ListingController
|
||||
|
||||
@@ -343,14 +343,12 @@ def rendered_link(id, res, links, media, compress):
|
||||
|
||||
b = IDBuilder([l._fullname for l in links],
|
||||
num = 1, wrap = ListingController.builder_wrapper)
|
||||
l = LinkListing(b, nextprev=False,
|
||||
show_nums=True).listing().render(style='html')
|
||||
res._update(id, innerHTML=l)
|
||||
|
||||
return LinkListing(b, nextprev=False,
|
||||
show_nums=True).listing().render(style='html')
|
||||
finally:
|
||||
c.render_style = render_style
|
||||
|
||||
def rendered_comment(id, res, comments):
|
||||
def rendered_comment(comments):
|
||||
try:
|
||||
render_style = c.render_style
|
||||
|
||||
@@ -358,10 +356,9 @@ def rendered_comment(id, res, comments):
|
||||
|
||||
b = IDBuilder([x._fullname for x in comments],
|
||||
num = 1)
|
||||
l = LinkListing(b, nextprev=False,
|
||||
return LinkListing(b, nextprev=False,
|
||||
show_nums=False).listing().render(style='html')
|
||||
res._update('preview_comment', innerHTML=l)
|
||||
|
||||
|
||||
finally:
|
||||
c.render_style = render_style
|
||||
|
||||
|
||||
@@ -104,7 +104,8 @@ def send_queued_mail():
|
||||
email.to_MIMEText().as_string())
|
||||
email.set_sent(rejected = False)
|
||||
# exception happens only for local recipient that doesn't exist
|
||||
except (smtplib.SMTPRecipientsRefused, smtplib.SMTPSenderRefused):
|
||||
except (smtplib.SMTPRecipientsRefused, smtplib.SMTPSenderRefused,
|
||||
UnicodeDecodeError):
|
||||
# handle error and print, but don't stall the rest of the queue
|
||||
print "Handled error sending mail (traceback to follow)"
|
||||
traceback.print_exc(file = sys.stdout)
|
||||
@@ -122,8 +123,11 @@ def send_queued_mail():
|
||||
msg_hash = email.msg_hash,
|
||||
link = email.thing,
|
||||
body = email.body).render(style = "email")
|
||||
email.subject = _("[reddit] %(user)s has shared a link with you") % \
|
||||
{"user": email.from_name()}
|
||||
try:
|
||||
email.subject = _("[reddit] %(user)s has shared a link with you") % \
|
||||
{"user": email.from_name()}
|
||||
except UnicodeDecodeError:
|
||||
email.subject = _("[reddit] a user has shared a link with you")
|
||||
sendmail(email)
|
||||
elif email.kind == Email.Kind.OPTOUT:
|
||||
email.body = Mail_Opt(msg_hash = email.msg_hash,
|
||||
|
||||
@@ -38,139 +38,147 @@ def json_respond(x):
|
||||
res = x or ''
|
||||
return websafe_json(simplejson.dumps(res))
|
||||
|
||||
|
||||
class JsonListingStub(object):
|
||||
"""used in JsonResponse._thing to set default listing behavior on
|
||||
things that are pre-wrapped"""
|
||||
_js_cls = "Listing"
|
||||
|
||||
class JsonResponse():
|
||||
# handled entried in the response object
|
||||
__slots__ = ['update', 'blur', 'focus', 'object', 'hide', 'show',
|
||||
'captcha', 'success', 'call']
|
||||
|
||||
def __init__(self):
|
||||
self.update = []
|
||||
self.hide = []
|
||||
self.show = []
|
||||
self.focus = None
|
||||
self.blur = None
|
||||
self.object = {}
|
||||
self.captcha = None
|
||||
self.error = None
|
||||
self.success = None
|
||||
self.redirect = None
|
||||
self.call = []
|
||||
|
||||
def _call(self, fn):
|
||||
self.call.append(fn)
|
||||
|
||||
def _success(self):
|
||||
self.success = 1
|
||||
|
||||
def _hide(self, name):
|
||||
self.hide.append(dict(name=name))
|
||||
|
||||
def _show(self, name):
|
||||
self.show.append(dict(name=name))
|
||||
|
||||
|
||||
def _focus(self, f):
|
||||
self.focus = f
|
||||
|
||||
def _blur(self, f):
|
||||
self.blur = f
|
||||
|
||||
def _redirect(self, red):
|
||||
from pylons import c, request
|
||||
if c.cname:
|
||||
red = BaseController.format_output_url(red, subreddit = c.site,
|
||||
require_frame = False)
|
||||
self.redirect = red
|
||||
|
||||
def _update(self, name, **kw):
|
||||
k = kw.copy()
|
||||
k['id'] = name
|
||||
self.update.append(k)
|
||||
|
||||
def _clear_error(self, error_name, err_on_thing = ''):
|
||||
errid = error_name + (('_' + err_on_thing) if err_on_thing else '')
|
||||
self._update(errid, innerHTML='')
|
||||
self._hide(errid)
|
||||
if self.error and self.error.name == error_name:
|
||||
self.error_thing_id = '';
|
||||
self.error = None
|
||||
|
||||
def _set_error(self, error, err_on_thing = ''):
|
||||
if not self.error:
|
||||
self.error = error
|
||||
self.error_thing_id = err_on_thing;
|
||||
|
||||
def _chk_error(self, error_name, err_on_thing = ''):
|
||||
from pylons import c
|
||||
if error_name in c.errors:
|
||||
error = c.errors[error_name]
|
||||
self._set_error(error, err_on_thing)
|
||||
return True
|
||||
class JQueryResponse(object):
|
||||
"""
|
||||
class which mimics the jQuery in javascript for allowing Dom
|
||||
manipulations on the client side.
|
||||
|
||||
An instantiated JQueryResponse acts just like the "$" function on
|
||||
the JS layer with the exception of the ability to run arbitrary
|
||||
code on the client. Selectors and method functions evaluate to
|
||||
new JQueryResponse objects, and the transformations are cataloged
|
||||
by the original object which can be iterated and sent across the
|
||||
wire.
|
||||
"""
|
||||
def __init__(self, factory = None):
|
||||
self._has_errors = set([])
|
||||
self._new_captcha = False
|
||||
if factory:
|
||||
self.factory = factory
|
||||
self.ops = None
|
||||
self.objs = None
|
||||
else:
|
||||
self._clear_error(error_name, err_on_thing)
|
||||
return False
|
||||
self.factory = self
|
||||
self.objs = {self: 0}
|
||||
self.ops = []
|
||||
|
||||
def __call__(self, *a):
|
||||
return self.factory.transform(self, "call", a)
|
||||
|
||||
def _chk_errors(self, errors, err_on_thing = ''):
|
||||
if errors:
|
||||
return reduce(lambda x, y: x or y,
|
||||
[self._chk_error(e, err_on_thing = err_on_thing) for e in errors])
|
||||
return False
|
||||
def __getattr__(self, key):
|
||||
if not key.startswith("__"):
|
||||
return self.factory.transform(self, "attr", key)
|
||||
|
||||
def _chk_captcha(self, err, err_on_thing = ''):
|
||||
if self._chk_error(err, err_on_thing):
|
||||
self.captcha = {'iden' : get_iden(), 'refresh' : True, 'id': err_on_thing}
|
||||
self._focus('captcha')
|
||||
return True
|
||||
return False
|
||||
def transform(self, obj, op, args):
|
||||
new = self.__class__(self)
|
||||
newi = self.objs[new] = len(self.objs)
|
||||
self.ops.append([self.objs[obj], newi, op, args])
|
||||
return new
|
||||
|
||||
@property
|
||||
def response(self):
|
||||
res = {}
|
||||
for k in self.__slots__:
|
||||
v = getattr(self, k)
|
||||
if v: res[k] = v
|
||||
return res
|
||||
def __iter__(self):
|
||||
yield ("jquery", self.ops)
|
||||
|
||||
def _thing(self, thing, action = None):
|
||||
d = replace_render(JsonListingStub(), thing)
|
||||
if action:
|
||||
d['action'] = action
|
||||
return d
|
||||
def has_error(self):
|
||||
return bool(self._has_errors)
|
||||
|
||||
def _send_things(self, things, action=None):
|
||||
from r2.models import IDBuilder
|
||||
# thing methods
|
||||
#--------------
|
||||
|
||||
def _things(self, things, action, *a, **kw):
|
||||
"""
|
||||
function for inserting/replacing things in listings.
|
||||
"""
|
||||
from r2.models import IDBuilder, Listing
|
||||
listing = None
|
||||
if isinstance(things, Listing):
|
||||
listing = things.listing()
|
||||
things = listing.things
|
||||
things = tup(things)
|
||||
if not all(isinstance(t, Wrapped) for t in things):
|
||||
b = IDBuilder([t._fullname for t in things])
|
||||
things = b.get_items()[0]
|
||||
self.object = [self._thing(thing, action=action) for thing in things]
|
||||
data = [replace_render(listing, t) for t in things]
|
||||
|
||||
def __iter__(self):
|
||||
if self.error:
|
||||
e = dict(self.error)
|
||||
if self.error_thing_id:
|
||||
e['id'] = self.error_thing_id
|
||||
yield 'error', e
|
||||
if self.response:
|
||||
yield 'response', self.response
|
||||
if self.redirect:
|
||||
yield 'redirect', self.redirect
|
||||
|
||||
def Json(func):
|
||||
def _Json(self, *a, **kw):
|
||||
if kw:
|
||||
for d in data:
|
||||
if d.has_key('data'):
|
||||
d['data'].update(kw)
|
||||
|
||||
new = self.__getattr__(action)
|
||||
return new(data, *a)
|
||||
|
||||
def insert_things(self, things, append = False, **kw):
|
||||
return self._things(things, "insert_things", append, **kw)
|
||||
|
||||
def replace_things(self, things, keep_children = False,
|
||||
reveal = False, stubs = False, **kw):
|
||||
return self._things(things, "replace_things",
|
||||
keep_children, reveal, stubs, **kw)
|
||||
|
||||
def insert_table_rows(self, rows, index = -1):
|
||||
new = self.__getattr__("insert_table_rows")
|
||||
return new([row.render() for row in tup(rows)], index)
|
||||
|
||||
|
||||
# convenience methods:
|
||||
# --------------------
|
||||
def has_errors(self, input, *errors, **kw):
|
||||
from pylons import c
|
||||
from jsontemplates import api_type
|
||||
c.render_style = api_type('html')
|
||||
c.response_content_type = 'application/json; charset=UTF-8'
|
||||
res = JsonResponse()
|
||||
val = func(self, res, *a, **kw)
|
||||
if val: return val
|
||||
return self.response_func(**dict(res))
|
||||
return _Json
|
||||
rval = False
|
||||
for e in errors:
|
||||
if e in c.errors:
|
||||
# get list of params checked to generate this error
|
||||
# if they exist, make sure they match input checked
|
||||
fields = c.errors[e].fields
|
||||
if input and fields and input not in fields:
|
||||
continue
|
||||
self._has_errors.add(e)
|
||||
rval = True
|
||||
self.find("." + e).show().html(c.errors[e].message).end()
|
||||
else:
|
||||
self.find("." + e).html("").end()
|
||||
if rval and input:
|
||||
self.focus_input(input)
|
||||
return rval
|
||||
|
||||
def clear_errors(self, *errors):
|
||||
from pylons import c
|
||||
for e in errors:
|
||||
if e in self._has_errors:
|
||||
self._has_errors.remove(e)
|
||||
self.find("." + e).hide().html("").end()
|
||||
|
||||
def new_captcha(self):
|
||||
if not self._new_captcha:
|
||||
self.captcha(get_iden())
|
||||
self._new_captcha = True
|
||||
|
||||
def chk_captcha(self, *errors):
|
||||
if self.has_errors(None, *errors):
|
||||
self.new_captcha()
|
||||
return True
|
||||
|
||||
def get_input(self, name):
|
||||
return self.find("*[name=%s]" % name)
|
||||
|
||||
def set_inputs(self, **kw):
|
||||
for k, v in kw.iteritems():
|
||||
self.get_input(k).set(value = v).end()
|
||||
return self
|
||||
|
||||
def focus_input(self, name):
|
||||
return self.get_input(name).focus().end()
|
||||
|
||||
def set_html(self, selector, value):
|
||||
if value:
|
||||
return self.find(selector).show().html(value).end()
|
||||
return self.find(selector).hide().html("").end()
|
||||
|
||||
|
||||
def set(self, **kw):
|
||||
obj = self
|
||||
for k, v in kw.iteritems():
|
||||
obj = obj.attr(k, v)
|
||||
return obj
|
||||
|
||||
|
||||
|
||||
@@ -52,10 +52,45 @@ class JsonTemplate(Template):
|
||||
def render(self, thing = None, *a, **kw):
|
||||
return {}
|
||||
|
||||
class TableRowTemplate(JsonTemplate):
|
||||
def cells(self, thing):
|
||||
raise NotImplementedError
|
||||
|
||||
def css_id(self, thing):
|
||||
return ""
|
||||
|
||||
def css_class(self, thing):
|
||||
return ""
|
||||
|
||||
def render(self, thing = None, *a, **kw):
|
||||
return {"id": self.css_id(thing),
|
||||
"css_class": self.css_class(thing),
|
||||
"cells": self.cells(thing)}
|
||||
|
||||
class UserItemJsonTemplate(TableRowTemplate):
|
||||
def cells(self, thing):
|
||||
from r2.lib.filters import spaceCompress
|
||||
cells = []
|
||||
for cell in thing.cells:
|
||||
r = Wrapped.part_render(thing, 'cell_type', cell)
|
||||
cells.append(spaceCompress(r))
|
||||
return cells
|
||||
|
||||
def css_id(self, thing):
|
||||
return thing.user._fullname
|
||||
|
||||
def css_class(self, thing):
|
||||
return "thing"
|
||||
|
||||
|
||||
class ThingJsonTemplate(JsonTemplate):
|
||||
__data_attrs__ = dict()
|
||||
|
||||
def points(self, wrapped):
|
||||
"""
|
||||
Generates the JS-style point triplet for votable elements
|
||||
(stored on the vl var on the JS side).
|
||||
"""
|
||||
score = wrapped.score
|
||||
likes = wrapped.likes
|
||||
base_score = score-1 if likes else score if likes is None else score+1
|
||||
@@ -64,20 +99,43 @@ class ThingJsonTemplate(JsonTemplate):
|
||||
|
||||
|
||||
def kind(self, wrapped):
|
||||
"""
|
||||
Returns a string literal which identifies the type of this
|
||||
thing. For subclasses of Thing, it will be 't's + kind_id.
|
||||
"""
|
||||
_thing = wrapped.lookups[0] if isinstance(wrapped, Wrapped) else wrapped
|
||||
return make_typename(_thing.__class__)
|
||||
|
||||
def rendered_data(self, thing):
|
||||
"""
|
||||
Called only when get_api_type is non-None (i.e., a JSON
|
||||
request has been made with partial rendering of the object to
|
||||
be returned)
|
||||
|
||||
Canonical Thing data representation for JS, which is currently
|
||||
a dictionary of three elements (translated into a JS Object
|
||||
when sent out). The elements are:
|
||||
|
||||
* id : Thing _fullname of thing.
|
||||
* vl : triplet of scores (up, none, down) from self.score
|
||||
* content : rendered representation of the thing by
|
||||
calling replace_render on it using the style of get_api_subtype().
|
||||
"""
|
||||
from r2.lib.filters import spaceCompress
|
||||
from r2.lib.template_helpers import replace_render
|
||||
from pylons import c
|
||||
listing = thing.listing if hasattr(thing, "listing") else None
|
||||
return dict(id = thing._fullname,
|
||||
vl = self.points(thing),
|
||||
content = spaceCompress(replace_render(listing, thing,
|
||||
style=get_api_subtype())))
|
||||
#vl = self.points(thing),
|
||||
content = spaceCompress(
|
||||
replace_render(listing, thing,
|
||||
style=get_api_subtype())))
|
||||
|
||||
def raw_data(self, thing):
|
||||
"""
|
||||
Complement to rendered_data. Called when a dictionary of
|
||||
thing data attributes is to be sent across the wire.
|
||||
"""
|
||||
def strip_data(x):
|
||||
if isinstance(x, dict):
|
||||
return dict((k, strip_data(v)) for k, v in x.iteritems())
|
||||
@@ -92,6 +150,12 @@ class ThingJsonTemplate(JsonTemplate):
|
||||
for k, v in self.__data_attrs__.iteritems())
|
||||
|
||||
def thing_attr(self, thing, attr):
|
||||
"""
|
||||
For the benefit of subclasses, to lookup attributes which may
|
||||
require more work than a simple getattr (for example, 'author'
|
||||
which has to be gotten from the author_id attribute on most
|
||||
things).
|
||||
"""
|
||||
import time
|
||||
if attr == "author":
|
||||
return thing.author.name
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
@@ -26,7 +27,6 @@ from strings import StringHandler, plurals
|
||||
from r2.lib.db import operators
|
||||
from r2.lib.filters import _force_unicode
|
||||
from pylons.i18n import _
|
||||
#from r2.config import cache
|
||||
|
||||
|
||||
class MenuHandler(StringHandler):
|
||||
@@ -427,7 +427,7 @@ class NewMenu(SimpleGetMenu):
|
||||
type = 'flatlist'
|
||||
|
||||
def __init__(self, **kw):
|
||||
kw['title'] = _("sort by")
|
||||
kw['title'] = ""
|
||||
SimpleGetMenu.__init__(self, **kw)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -33,7 +33,7 @@ from r2.lib.filters import spaceCompress, _force_unicode, _force_utf8
|
||||
from r2.lib.menus import NavButton, NamedButton, NavMenu, PageNameNav, JsButton
|
||||
from r2.lib.menus import SubredditButton, SubredditMenu, menu
|
||||
from r2.lib.strings import plurals, rand_strings, strings
|
||||
from r2.lib.utils import title_to_url, query_string, UrlParser
|
||||
from r2.lib.utils import title_to_url, query_string, UrlParser, to_js
|
||||
from r2.lib.template_helpers import add_sr, get_domain
|
||||
import sys
|
||||
|
||||
@@ -177,15 +177,6 @@ class Reddit(Wrapped):
|
||||
buttons += [JsButton(g.lang_name.get(lang, lang),
|
||||
onclick = "return showlang();",
|
||||
css_class = "pref-lang")]
|
||||
#buttons += [NamedButton("stats", False, nocname=True)]
|
||||
#buttons += [NamedButton("help", False, nocname=True),
|
||||
#NamedButton("blog", False, nocname=True)]
|
||||
|
||||
if c.user_is_loggedin:
|
||||
buttons += [NamedButton("logout", False,
|
||||
nocname=not c.authorized_cname,
|
||||
target = "_self")]
|
||||
|
||||
return NavMenu(buttons, base_path = "/", type = "flatlist")
|
||||
|
||||
def footer_nav(self):
|
||||
@@ -221,18 +212,19 @@ class Reddit(Wrapped):
|
||||
if c.user_is_loggedin:
|
||||
more_buttons.append(NamedButton('saved', False))
|
||||
more_buttons.append(NamedButton('recommended', False))
|
||||
|
||||
|
||||
if c.user_is_admin:
|
||||
more_buttons.append(NamedButton('admin'))
|
||||
elif c.site.is_moderator(c.user):
|
||||
more_buttons.append(NavButton(menu.admin, 'about/edit'))
|
||||
|
||||
|
||||
if c.user_is_sponsor:
|
||||
more_buttons.append(NamedButton('promote'))
|
||||
|
||||
|
||||
toolbar = [NavMenu(main_buttons, type='tabmenu')]
|
||||
if more_buttons:
|
||||
toolbar.append(NavMenu(more_buttons, title=menu.more, type='tabdrop'))
|
||||
|
||||
if c.site != Default and not c.cname:
|
||||
toolbar.insert(0, PageNameNav('subreddit'))
|
||||
|
||||
@@ -352,7 +344,8 @@ class BoringPage(Reddit):
|
||||
|
||||
def __init__(self, pagename, **context):
|
||||
self.pagename = pagename
|
||||
Reddit.__init__(self, title = "%s: %s" % (c.site.name, pagename),
|
||||
name = c.site.name or g.default_sr
|
||||
Reddit.__init__(self, title = "%s: %s" % (name, pagename),
|
||||
**context)
|
||||
|
||||
def build_toolbars(self):
|
||||
@@ -429,7 +422,11 @@ class LinkInfoPage(Reddit):
|
||||
|
||||
link_title = ((self.link.title) if hasattr(self.link, 'title') else '')
|
||||
if comment:
|
||||
author = Account._byID(comment.author_id, data=True).name
|
||||
if comment._deleted and not c.user_is_admin:
|
||||
author = _("[deleted]")
|
||||
else:
|
||||
author = Account._byID(comment.author_id, data=True).name
|
||||
|
||||
params = {'author' : author, 'title' : _force_unicode(link_title)}
|
||||
title = strings.permalink_title % params
|
||||
else:
|
||||
@@ -796,8 +793,7 @@ class PermalinkMessage(Wrapped):
|
||||
"""renders the box on comment pages that state 'you are viewing a
|
||||
single comment's thread'"""
|
||||
def __init__(self, comments_url):
|
||||
self.comments_url = comments_url
|
||||
|
||||
Wrapped.__init__(self, comments_url = comments_url)
|
||||
|
||||
class PaneStack(Wrapped):
|
||||
"""Utility class for storing and rendering a list of block elements."""
|
||||
@@ -995,31 +991,8 @@ class Page_down(Wrapped):
|
||||
|
||||
# Classes for dealing with friend/moderator/contributor/banned lists
|
||||
|
||||
# TODO: if there is time, we could roll these Ajaxed classes into the
|
||||
# JsonTemplates framework...
|
||||
class Ajaxed():
|
||||
"""Base class for allowing simple interaction of UserTableItem and
|
||||
UserItem classes to be edited via JS and AJax requests. In
|
||||
analogy with Wrapped, this class provides an interface for
|
||||
'rendering' dictionary representations of the data which can be
|
||||
passed to the client via JSON over AJAX"""
|
||||
__slots__ = ['kind', 'action', 'data']
|
||||
|
||||
def __init__(self, kind, action):
|
||||
self._ajax = dict(kind=kind,
|
||||
action = None,
|
||||
data = {})
|
||||
|
||||
def for_ajax(self, action = None):
|
||||
self._ajax['action'] = action
|
||||
self._ajax['data'] = self.ajax_render()
|
||||
return self._ajax
|
||||
|
||||
def ajax_render(self, style="html"):
|
||||
return {}
|
||||
|
||||
|
||||
class UserTableItem(Wrapped, Ajaxed):
|
||||
class UserTableItem(Wrapped):
|
||||
"""A single row in a UserList of type 'type' and of name
|
||||
'container_name' for a given user. The provided list of 'cells'
|
||||
will determine what order the different columns are rendered in.
|
||||
@@ -1028,20 +1001,8 @@ class UserTableItem(Wrapped, Ajaxed):
|
||||
def __init__(self, user, type, cellnames, container_name, editable):
|
||||
self.user, self.type, self.cells = user, type, cellnames
|
||||
self.container_name = container_name
|
||||
self.name = "tr_%s_%s" % (user.name, type)
|
||||
self.editable = editable
|
||||
Wrapped.__init__(self)
|
||||
Ajaxed.__init__(self, 'UserTable', 'add')
|
||||
|
||||
def ajax_render(self, style="html"):
|
||||
"""Generates a 'rendering' of this item suitable for
|
||||
processing by JS for insert or removal from an existing
|
||||
UserList"""
|
||||
cells = []
|
||||
for cell in self.cells:
|
||||
r = Wrapped.part_render(self, 'cell_type', cell)
|
||||
cells.append(spaceCompress(r))
|
||||
return dict(cells=cells, id=self.type, name=self.name)
|
||||
|
||||
def __repr__(self):
|
||||
return '<UserTableItem "%s">' % self.user.name
|
||||
@@ -1059,7 +1020,7 @@ class UserList(Wrapped):
|
||||
self.editable = editable
|
||||
Wrapped.__init__(self)
|
||||
|
||||
def ajax_user(self, user):
|
||||
def user_row(self, user):
|
||||
"""Convenience method for constructing a UserTableItem
|
||||
instance of the user with type, container_name, etc. of this
|
||||
UserList instance"""
|
||||
@@ -1073,9 +1034,9 @@ class UserList(Wrapped):
|
||||
uids = self.user_ids()
|
||||
if uids:
|
||||
users = Account._byID(uids, True, return_dict = False)
|
||||
return [self.ajax_user(u) for u in users]
|
||||
return [self.user_row(u) for u in users]
|
||||
else:
|
||||
return ()
|
||||
return []
|
||||
|
||||
def user_ids(self):
|
||||
"""virtual method for fetching the list of ids of the Accounts
|
||||
@@ -1174,6 +1135,9 @@ class Cnameframe(Wrapped):
|
||||
self.title = ""
|
||||
self.frame_target = None
|
||||
|
||||
class FrameBuster(Wrapped):
|
||||
pass
|
||||
|
||||
class PromotePage(Reddit):
|
||||
create_reddit_box = False
|
||||
submit_box = False
|
||||
@@ -1208,3 +1172,4 @@ class PromoteLinkForm(Wrapped):
|
||||
timedeltatext = timedeltatext,
|
||||
listing = listing,
|
||||
*a, **kw)
|
||||
|
||||
|
||||
@@ -104,11 +104,9 @@ string_dict = dict(
|
||||
invalid_property_list = _('invalid CSS property list "%(proplist)s"'),
|
||||
unknown_rule_type = _('unknown CSS rule type "%(ruletype)s"')
|
||||
),
|
||||
|
||||
submit_box_text = _('to anything interesting: news article, blog entry, video, picture...'),
|
||||
permalink_title = _("%(author)s comments on %(title)s"),
|
||||
link_info_title = _("%(title)s : %(site)s"),
|
||||
|
||||
)
|
||||
|
||||
class StringHandler(object):
|
||||
|
||||
@@ -27,16 +27,36 @@ from mako.filters import url_escape
|
||||
import simplejson
|
||||
import os.path
|
||||
from copy import copy
|
||||
|
||||
import random
|
||||
from pylons import i18n, g, c
|
||||
|
||||
def static(file):
|
||||
"""
|
||||
Simple static file maintainer which automatically paths and
|
||||
versions files being served out of static.
|
||||
|
||||
In the case of JS and CSS where g.uncompressedJS is set, the
|
||||
version of the file is set to be random to prevent caching and it
|
||||
mangles the path to point to the uncompressed versions.
|
||||
"""
|
||||
|
||||
# stip of "/static/" if already present
|
||||
fname = os.path.basename(file).split('?')[0]
|
||||
v = g.static_md5.get(fname, '')
|
||||
# if uncompressed, we are in devel mode so randomize the hash
|
||||
if g.uncompressedJS:
|
||||
v = str(random.random()).split(".")[-1]
|
||||
else:
|
||||
v = g.static_md5.get(fname, '')
|
||||
if v: v = "?v=" + v
|
||||
# don't mangle paths
|
||||
if os.path.dirname(file):
|
||||
return file + v
|
||||
|
||||
if g.uncompressedJS:
|
||||
extension = file.split(".")[1:]
|
||||
if extension and extension[-1] in ("js", "css"):
|
||||
return os.path.join(c.site.static_path, extension[-1], file) + v
|
||||
|
||||
return os.path.join(c.site.static_path, file) + v
|
||||
|
||||
def generateurl(context, path, **kw):
|
||||
@@ -106,7 +126,7 @@ def replace_render(listing, item, style = None, display = True):
|
||||
if hasattr(listing, "num_margin"):
|
||||
num_margin = listing.num_margin
|
||||
else:
|
||||
num_margin = "%5.2fex" % (len(str(listing.max_num))*1.1)
|
||||
num_margin = "%.2fex" % (len(str(listing.max_num))*1.1)
|
||||
else:
|
||||
num_str = ''
|
||||
num_margin = "0px"
|
||||
|
||||
@@ -1006,3 +1006,19 @@ def trace(fn):
|
||||
% (fn,a,kw,ret))
|
||||
return ret
|
||||
return new_fn
|
||||
|
||||
def common_subdomain(domain1, domain2):
|
||||
domain1 = domain1.split(":")[0]
|
||||
domain2 = domain2.split(":")[0]
|
||||
if len(domain1) > len(domain2):
|
||||
domain1, domain2 = domain2, domain1
|
||||
|
||||
if domain1 == domain2:
|
||||
return domain1
|
||||
else:
|
||||
dom = domain1.split(".")
|
||||
for i in range(len(dom), 1, -1):
|
||||
d = '.'.join(dom[-i:])
|
||||
if domain2.endswith(d):
|
||||
return d
|
||||
return ""
|
||||
|
||||
@@ -42,7 +42,7 @@ class Account(Thing):
|
||||
pref_newwindow = False,
|
||||
pref_public_votes = False,
|
||||
pref_hide_ups = False,
|
||||
pref_hide_downs = True,
|
||||
pref_hide_downs = False,
|
||||
pref_min_link_score = -4,
|
||||
pref_min_comment_score = -4,
|
||||
pref_num_comments = g.num_comments,
|
||||
@@ -294,3 +294,14 @@ def register(name, password):
|
||||
|
||||
class Friend(Relation(Account, Account)): pass
|
||||
Account.__bases__ += (UserRel('friend', Friend),)
|
||||
|
||||
class DeletedUser(FakeAccount):
|
||||
@property
|
||||
def name(self):
|
||||
return '[deleted]'
|
||||
|
||||
def _fullname(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _id(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
from r2.lib.db.thing import Thing, Relation, NotFound, MultiRelation, \
|
||||
CreationError
|
||||
from r2.lib.utils import base_url, tup, domain, worker, title_to_url
|
||||
from account import Account
|
||||
from account import Account, DeletedUser
|
||||
from subreddit import Subreddit
|
||||
from printable import Printable
|
||||
from r2.config import cache
|
||||
@@ -59,7 +59,11 @@ class Link(Thing, Printable):
|
||||
|
||||
@classmethod
|
||||
def by_url_key(cls, url):
|
||||
return str(base_url(url.lower()))
|
||||
b = base_url(url.lower())
|
||||
try:
|
||||
return b.encode('utf8')
|
||||
except UnicodeDecodeError:
|
||||
return str(b)
|
||||
|
||||
@classmethod
|
||||
def _by_url(cls, url, sr):
|
||||
@@ -234,17 +238,19 @@ class Link(Thing, Printable):
|
||||
def make_permalink(self, sr, force_domain = False):
|
||||
from r2.lib.template_helpers import get_domain
|
||||
p = "comments/%s/%s/" % (self._id36, title_to_url(self.title))
|
||||
if not c.cname:
|
||||
if not c.cname and not force_domain:
|
||||
res = "/r/%s/%s" % (sr.name, p)
|
||||
elif sr != c.site or force_domain:
|
||||
res = "http://%s/%s" % (get_domain(cname = (c.cname and sr == c.site),
|
||||
res = "http://%s/%s" % (get_domain(cname = (c.cname and
|
||||
sr == c.site),
|
||||
subreddit = not c.cname), p)
|
||||
else:
|
||||
res = "/%s" % p
|
||||
return res
|
||||
|
||||
def make_permalink_slow(self):
|
||||
return self.make_permalink(self.subreddit_slow)
|
||||
def make_permalink_slow(self, force_domain = False):
|
||||
return self.make_permalink(self.subreddit_slow,
|
||||
force_domain = force_domain)
|
||||
|
||||
@classmethod
|
||||
def add_props(cls, user, wrapped):
|
||||
@@ -442,6 +448,7 @@ class Comment(Thing, Printable):
|
||||
request.host,
|
||||
c.cname,
|
||||
wrapped.author == c.user,
|
||||
wrapped.editted,
|
||||
wrapped.likes,
|
||||
wrapped.friend,
|
||||
wrapped.collapsed,
|
||||
@@ -505,12 +512,16 @@ class Comment(Thing, Printable):
|
||||
item.author != c.user and
|
||||
not item.show_spam)))
|
||||
|
||||
if item._deleted and not c.user_is_admin:
|
||||
item.author = DeletedUser()
|
||||
item.body = '[deleted]'
|
||||
|
||||
# don't collapse for admins, on profile pages, or if deleted
|
||||
item.collapsed = ((item.score < min_score) and
|
||||
not (c.profilepage or
|
||||
item.deleted or
|
||||
c.user_is_admin))
|
||||
|
||||
|
||||
if not hasattr(item,'editted'):
|
||||
item.editted = False
|
||||
#will get updated in builder
|
||||
|
||||
@@ -74,7 +74,6 @@ class Listing(object):
|
||||
|
||||
rendered_items = sgm(g.rendercache, fullnames, render_items, 'render_',
|
||||
time = g.page_cache_time)
|
||||
|
||||
#replace the render function
|
||||
for k, v in rendered_items.iteritems():
|
||||
def make_fn(v):
|
||||
|
||||
@@ -385,22 +385,6 @@ class Report(MultiRelation('report',
|
||||
return accts
|
||||
|
||||
|
||||
# def karma_whack(author, cls, dir):
|
||||
# try:
|
||||
# field = 'comment_karma' if cls == Comment else 'link_karma'
|
||||
# # get karma scale (ignore negative) -> user karma times 10%
|
||||
# karma = max(getattr(author, field) * .1, 1)
|
||||
|
||||
# # set the scale by the number of times this guy has been marked as a spammer
|
||||
# scale = max(author.spammer+1, 1)
|
||||
|
||||
# # the actual hit is the min of the two
|
||||
# hit = min(karma, scale) * ( 1 if dir > 0 else -1 )
|
||||
|
||||
# author._incr(field, int(hit))
|
||||
# except AttributeError:
|
||||
# pass
|
||||
|
||||
|
||||
def unreport(things, correct=False, auto = False, banned_by = ''):
|
||||
things = tup(things)
|
||||
@@ -435,9 +419,6 @@ def unreport(things, correct=False, auto = False, banned_by = ''):
|
||||
if t._spam != correct and hasattr(t, 'author_id'):
|
||||
# tally the spamminess of the author
|
||||
spammer[t.author_id] = spammer.get(t.author_id,0) + amount
|
||||
#author = authors.get(t.author_id)
|
||||
#if author:
|
||||
# karma_whack(author, t.__class__, -amount)
|
||||
|
||||
#will be empty if the items didn't have authors
|
||||
for s, v in spammer.iteritems():
|
||||
@@ -466,9 +447,6 @@ def unreport_account(user, correct = True, types = (Link, Comment, Message),
|
||||
if user.spammer + count >= 0:
|
||||
user._incr('spammer', count)
|
||||
|
||||
#for i in xrange(count if count > 0 else -count):
|
||||
# karma_whack(user, typ, -count)
|
||||
|
||||
things= list(typ._query(typ.c.author_id == user._id,
|
||||
typ.c._spam == (not correct),
|
||||
data = False, limit=300))
|
||||
@@ -498,9 +476,3 @@ def unreport_account(user, correct = True, types = (Link, Comment, Message),
|
||||
reports = Report._by_author(user, amount = 0)
|
||||
for r in reports: Report.accept(r, correct)
|
||||
|
||||
def whack(user, correct = True, auto = False, ban_user = False, banned_by = ''):
|
||||
unreport_account(user, correct = correct,
|
||||
auto = auto, banned_by = banned_by)
|
||||
if ban_user:
|
||||
user._spam = True
|
||||
user._commit()
|
||||
|
||||
38
r2/r2/public/static/css/reddit-ie6-hax.css
Normal file
38
r2/r2/public/static/css/reddit-ie6-hax.css
Normal file
@@ -0,0 +1,38 @@
|
||||
.link { line-height: 1.1; }
|
||||
|
||||
div.dropdown.tabdrop { display: inline; }
|
||||
|
||||
div.drop-choices {
|
||||
position: absolute; top: 0px; display: none;
|
||||
}
|
||||
|
||||
div.drop-choices.inuse {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*.tabdrop { display: none; }*/
|
||||
|
||||
.promoted { height: 70px; }
|
||||
.organic-listing { height: 70px; }
|
||||
|
||||
.help .help-cover {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
#header { height: 0px; }
|
||||
|
||||
.md { overflow: show; }
|
||||
|
||||
#sr-header-area { width: 100%; }
|
||||
|
||||
div.cover {
|
||||
position: absolute;
|
||||
top: expression( ( ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
|
||||
|
||||
}
|
||||
|
||||
div.popup {
|
||||
position: absolute;
|
||||
top: expression( ( 40 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
|
||||
}
|
||||
@@ -22,8 +22,8 @@ body {
|
||||
html,body { height: 100%; }
|
||||
|
||||
/* IE dumbness patch. hidden input in a hidden block that is
|
||||
subsequently shown leads to the input to "show" and generate undesired
|
||||
padding. This makes it go away. */
|
||||
* subsequently shown leads to the input to "show" and generate undesired
|
||||
* )padding. This makes it go away. */
|
||||
input[type=hidden] { position: absolute; }
|
||||
|
||||
/* html element defaults */
|
||||
@@ -44,6 +44,22 @@ a:active { border: 0 none;}
|
||||
a:focus { -moz-outline-style: none; }
|
||||
*/
|
||||
|
||||
.rounded {
|
||||
-moz-border-radius-topleft: 7px;
|
||||
-moz-border-radius-topright: 7px;
|
||||
-moz-border-radius-bottomleft: 7px;
|
||||
-moz-border-radius-bottomright: 7px;
|
||||
-webkit-border-top-left-radius: 7px;
|
||||
-webkit-border-top-right-radius: 7px;
|
||||
-webkit-border-bottom-left-radius: 7px;
|
||||
-webkit-border-bottom-right-radius: 7px;
|
||||
}
|
||||
.rounded .morelink {
|
||||
-webkit-border-top-right-radius: 6px;
|
||||
-moz-border-radius-topright: 6px;
|
||||
}
|
||||
|
||||
|
||||
div.autosize { display: table; width: 1px}
|
||||
div.autosize > div { display: table-cell; }
|
||||
|
||||
@@ -90,7 +106,6 @@ input[type=checkbox], input[type=radio] { margin-top: .4em; }
|
||||
#header {
|
||||
border-bottom: 1px solid #5f99cf;
|
||||
position: relative;
|
||||
_height: 0px; /*ie6 hack*/
|
||||
background-color: #cee3f8;
|
||||
z-index: 99;
|
||||
}
|
||||
@@ -140,8 +155,7 @@ input[type=checkbox], input[type=radio] { margin-top: .4em; }
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-title {
|
||||
}
|
||||
.drop-choices.inuse { display: block; }
|
||||
|
||||
.drop-choices {
|
||||
position: absolute;
|
||||
@@ -152,8 +166,10 @@ input[type=checkbox], input[type=radio] { margin-top: .4em; }
|
||||
white-space: nowrap;
|
||||
line-height: normal;
|
||||
margin-top: 1px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.drop-choices a.choice {
|
||||
cursor: pointer;
|
||||
padding: 0px 3px 0px 3px;
|
||||
@@ -232,10 +248,8 @@ input[type=checkbox], input[type=radio] { margin-top: .4em; }
|
||||
|
||||
#search input[type=text] {
|
||||
border: 1px solid gray;
|
||||
height: 22px;
|
||||
font-size: 18px;
|
||||
width: 295px;
|
||||
color: gray;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
@@ -366,8 +380,7 @@ input[type=checkbox], input[type=radio] { margin-top: .4em; }
|
||||
.subreddit-info {padding-bottom: 3px }
|
||||
.subreddit-info .moderate { color: orangered; }
|
||||
|
||||
.subreddit-info #subscribe {
|
||||
display: inline;
|
||||
.subreddit-info .subscribe-button {
|
||||
margin-right: 5px;
|
||||
font-size: larger;
|
||||
}
|
||||
@@ -453,6 +466,12 @@ before enabling */
|
||||
|
||||
/* thing rendering */
|
||||
|
||||
.preload {
|
||||
position: absolute;
|
||||
top: -1000px;
|
||||
left: -1000px;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
margin: 2px 0px 0px 0px;
|
||||
width: 100%;
|
||||
@@ -491,57 +510,57 @@ before enabling */
|
||||
.tagline a.friend {color: orangered }
|
||||
.tagline a:hover { text-decoration: underline }
|
||||
|
||||
.watch-play {
|
||||
.media-button .option { color: red; }
|
||||
.media-button .option.active {
|
||||
background: transparent url(/static/reddit-button-play.gif) no-repeat scroll right center;
|
||||
padding-right: 15px;
|
||||
color: #336699;
|
||||
}
|
||||
.watch-stop {
|
||||
color: red;
|
||||
|
||||
.linkcompressed .media-button .option.active {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.embededmedia { margin-top: 5px }
|
||||
.embededmedia { margin-top: 5px; margin-left: 60px; }
|
||||
|
||||
.title { color: blue; padding: 0px; overflow: hidden; }
|
||||
.title:visited { color: #551a8b }
|
||||
.title.click { color: #551a8b }
|
||||
|
||||
.title.loggedin { color: blue }
|
||||
.title.loggedin:visited { color: #551a8b }
|
||||
.title.loggedin.click { color: #551a8b }
|
||||
.title.loggedin.click:visited { color: #551a8b }
|
||||
.thing .title { color: blue; padding: 0px; overflow: hidden; }
|
||||
.thing .title:visited { color: #551a8b }
|
||||
.thing .title.click { color: #551a8b }
|
||||
|
||||
.thing .title.loggedin { color: blue }
|
||||
.thing .title.loggedin:visited { color: #551a8b }
|
||||
.thing .title.loggedin.click { color: #551a8b }
|
||||
.thing .title.loggedin.click:visited { color: #551a8b }
|
||||
|
||||
.sitetable { list-style-type: none; }
|
||||
.ajaxhook { position: absolute; top: -1000px; left: 0px; }
|
||||
.nextprev { color: gray; font-size: larger; margin-top: 10px;}
|
||||
|
||||
/* corner help */
|
||||
.corner-help {
|
||||
margin: 0px 5px 5px 0;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.corner-help a {
|
||||
.help a {
|
||||
color: #808080;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.helpcover {
|
||||
z-index: 1000;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
.help a.open {
|
||||
margin: 0px 5px 5px 0;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
|
||||
.help.help-cover {
|
||||
background-color: #F8F8F8;
|
||||
border: 1px solid gray;
|
||||
font-size: 110%;
|
||||
_width: 100%;
|
||||
_height: 70px;
|
||||
display:none;
|
||||
padding: 5px 10px 10px 10px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.helpcover p, .helpcover form { margin: 5px; }
|
||||
.help p, .help form { margin: 5px; }
|
||||
.help form { display: inline; }
|
||||
|
||||
|
||||
/* organic listing */
|
||||
@@ -550,14 +569,13 @@ before enabling */
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
_height: 60px;
|
||||
_overflow: hidden;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.organic-listing .link {
|
||||
background-color: #F8F8F8;
|
||||
background-color: #F8F8F8;
|
||||
padding: 5px 7em 10px 0;
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.organic-listing .linkcompressed {
|
||||
@@ -586,9 +604,12 @@ before enabling */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.promoted-list {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.promoted-list { font-size: larger; }
|
||||
.promoted-list .unpromote-button { display: inline }
|
||||
.promoted-list .unpromote-button a { color: gray; }
|
||||
|
||||
|
||||
|
||||
.organic-listing .promoted {
|
||||
background-color: #EFF7FF;
|
||||
@@ -700,6 +721,13 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
|
||||
/* links */
|
||||
|
||||
.toggle .error { font-size: x-small; }
|
||||
.toggle .option { display: none; }
|
||||
.toggle .option.active { display: inline; }
|
||||
|
||||
.thing .stub { display: none; }
|
||||
.link.last-clicked { border: 1px dashed gray; }
|
||||
|
||||
.link { margin: 0; margin-bottom: 8px; padding-left: 3px; }
|
||||
.link .score {text-align: center; color: #c6c6c6;}
|
||||
.link .title {font-size:medium; font-weight: normal; margin-bottom: 1px;}
|
||||
@@ -768,6 +796,7 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
/* comments */
|
||||
|
||||
.comment { margin-left: 10px; }
|
||||
.comment .edit-body { display: none; visibility: hidden; }
|
||||
.comment .midcol { margin-left: 0px; }
|
||||
.comment .title { font-size: small; margin-top: 10px;}
|
||||
|
||||
@@ -803,6 +832,8 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
width: 40em;
|
||||
}
|
||||
|
||||
textarea.gray { color: gray; }
|
||||
|
||||
.commentreply textarea {
|
||||
border: 1px solid #369;
|
||||
width: 100%;
|
||||
@@ -872,13 +903,18 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
.subreddit .midcol { margin-right: 5px }
|
||||
|
||||
.sr-toggle-button {
|
||||
width: 54px;
|
||||
height: 18px;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.sr-toggle-button.add { background-image: url(/static/sr-add-button.png) }
|
||||
.sr-toggle-button.remove { background-image: url(/static/sr-remove-button.png)}
|
||||
.sr-toggle-button a.option.active {
|
||||
display: block;
|
||||
width: 54px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.sr-toggle-button .remove { background-image: url(/static/sr-remove-button.png)}
|
||||
.sr-toggle-button .add { background-image: url(/static/sr-add-button.png) }
|
||||
|
||||
.sr-type-img {
|
||||
margin-right: 2px;
|
||||
@@ -887,6 +923,11 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
.commentbody { }
|
||||
|
||||
.commentbody.border { background-color: #ffc; padding-left: 5px}
|
||||
.commentbody.grayed {
|
||||
color: gray;
|
||||
background-color: #E0E0E0;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.fixedwidth { float: left; width: 100px; height: 0px; }
|
||||
.clearleft { clear: left; height: 0px; }
|
||||
@@ -946,7 +987,7 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
|
||||
.server-status { width: 300px; }
|
||||
.server-status table { font-size: xx-small; margin-left: 5px; }
|
||||
.server-status td { padding-right: 2px; }
|
||||
.server-status td { padding-right: 2px; padding-left: 2px; }
|
||||
.server-status .bar { height: 5px; background-color: blue; }
|
||||
.server-status .load0 { background-color: #FFFFFF; }
|
||||
.server-status .load1 { background-color: #E2ECFF; }
|
||||
@@ -955,6 +996,11 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
.server-status .load4 { background-color: #FF9191; }
|
||||
.server-status .load5 { background-color: #FF0000; color: #FFFFFF }
|
||||
|
||||
.server-status th { font-weight: bold; padding-right: 2px; }
|
||||
.server-status tr.title-region { cursor: pointer; }
|
||||
.server-status tr.title-region:hover > td,
|
||||
.server-status tr.title-region:hover > th { text-decoration: underline; }
|
||||
|
||||
.server-status .pegged {
|
||||
background-color: red;
|
||||
font-weight: bold;
|
||||
@@ -983,7 +1029,7 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
|
||||
.orangered { color: orangered; }
|
||||
|
||||
|
||||
.logout { display: inline; }
|
||||
.login-form-side {
|
||||
border: 1px solid gray;
|
||||
}
|
||||
@@ -1025,6 +1071,7 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
}
|
||||
|
||||
|
||||
.status { color: red; }
|
||||
.error { color: red; font-size: small; margin: 5px; }
|
||||
|
||||
#noresults { margin-right: 310px; }
|
||||
@@ -1149,10 +1196,10 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
}
|
||||
|
||||
.popup {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
left: 10%;
|
||||
background-color: white;
|
||||
top: 100px;
|
||||
top: 40px;
|
||||
width: 80%;
|
||||
text-align: left;
|
||||
z-index: 1001;
|
||||
@@ -1330,20 +1377,30 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
.instructions pre { margin: 5px; margin-right: 10px; }
|
||||
.instructions iframe { margin: 5px 10px 5px 0px; }
|
||||
.instructions input, .instructions select { margin: 0 .5em }
|
||||
.instructions a.view-code { float: right; margin-bottom: 1em; }
|
||||
.instructions a:focus { -moz-outline-style: none; }
|
||||
.instructions strong { font-weight: bold; }
|
||||
.instructions .buttons { margin-left: 1em; max-width: 50em; }
|
||||
.instructions .buttons li { margin-top: 1em;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding-bottom: 1em;}
|
||||
.instructions code { display: block;
|
||||
font-family: monospace;
|
||||
font-size: small;
|
||||
margin: 5px;
|
||||
background-color: #FF9;
|
||||
padding: 10px;
|
||||
max-width: 50em;}
|
||||
.instructions code {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
font-size: small;
|
||||
margin: 5px;
|
||||
background-color: #FF9;
|
||||
padding: 10px;
|
||||
max-width: 50em;}
|
||||
|
||||
|
||||
.button-demo a.view-code,
|
||||
.button-demo a.hide-code { float: right; margin-bottom: 1em; }
|
||||
.button-demo a.hide-code { display: none; }
|
||||
.instructions .button-demo code { display: none; }
|
||||
|
||||
.button-demo.show-demo a.view-code { display: none; }
|
||||
.button-demo.show-demo a.hide-code { display: inline; }
|
||||
.button-demo.show-demo code { display: block; }
|
||||
|
||||
#preview { float: right; width: 30em; margin: 10px; }
|
||||
#preview span { color: lightgray; }
|
||||
@@ -1356,18 +1413,18 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
}
|
||||
|
||||
/* the toolbar */
|
||||
.toolbar { height: 30px; border-bottom: 1px solid black}
|
||||
.toolbar { height: 30px; border-bottom: 1px solid black;}
|
||||
.toolbar td { vertical-align: center; }
|
||||
.toolbar td#frame-left > a,
|
||||
.toolbar td#frame-left > form { margin-right: 20px; }
|
||||
.toolbar td#frame-right { text-align: right; }
|
||||
.toolbar td#frame-right > a,
|
||||
.toolbar td#frame-right > form { margin: 0 5px; }
|
||||
.toolbar .arrow {
|
||||
background-position: center left;
|
||||
padding-left: 18px;
|
||||
display: inline;
|
||||
}
|
||||
.toolbar span {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.toolbar #frame-right { text-align: right }
|
||||
.toolbar #frame-right a { margin-left: 10px; }
|
||||
|
||||
/* default form styles */
|
||||
.pretty-form {
|
||||
@@ -1432,11 +1489,16 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
font-weight: bold;
|
||||
vertical-align: top;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.preftable th label { display: block; }
|
||||
.sharetable.preftable th label { display: inline; }
|
||||
.preftable th span { display: block; }
|
||||
|
||||
.preftable td.prefright { padding: 8px 0;}
|
||||
|
||||
.preftable .spacer { margin-bottom: 5px; }
|
||||
.preftable .note { width: 100%; vertical-align: top; padding-top: 10px; }
|
||||
.save-button { margin-left: 5px; }
|
||||
|
||||
.over18 button { margin: 0 10px 0 10px; padding: 5px}
|
||||
|
||||
@@ -1473,11 +1535,7 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
.stylesheet-customize-container h2 { margin-top: 15px; margin-bottom: 10px; }
|
||||
|
||||
.image-upload .new-image { margin-left: 20px }
|
||||
.image-upload table { margin-left: 20px; }
|
||||
.image-upload td,
|
||||
.image-upload th { vertical-align: top; }
|
||||
.image-upload span { padding-left: 5px; }
|
||||
.image-upload { display: inline; }
|
||||
|
||||
|
||||
ul#image-preview-list {
|
||||
@@ -1672,6 +1730,8 @@ ul#image-preview-list .description pre {
|
||||
border-bottom: 1px dotted #369;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.srdrop .choice.bottom-option {
|
||||
font-style: italic;
|
||||
border-top: 1px dotted #369;
|
||||
@@ -1716,3 +1776,5 @@ ul#image-preview-list .description pre {
|
||||
}
|
||||
|
||||
.subscription-box h1{ text-align: center; }
|
||||
|
||||
.toggle.deltranslator-button { display: inline; }
|
||||
@@ -1,117 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>reddit email policy</title>
|
||||
<body>
|
||||
|
||||
<div style="width: 80ex">
|
||||
<p>
|
||||
Section 17538.45 of the California Business and Professional Code allows
|
||||
Electronic Mail Service Providers to restrict or prohibit use of their
|
||||
equipment for Unsolicited Electronic Mail Advertising (UEMA), when the
|
||||
equipment is located in the state of California. It further provides
|
||||
for liquidated damages of $50 per message received. reddit.com uses
|
||||
equipment located in California, and the reddit.com policy is that
|
||||
Unsolicited Electronic Mail Advertising is prohibited.
|
||||
</p>
|
||||
<hr>
|
||||
<p>Text of section 17538.45 of the Business and Professional Code of the
|
||||
State of California, copied on July 17th, 2008. Check
|
||||
with the <a HREF="http://www.leginfo.ca.gov/calaw.html">California
|
||||
Law</a> website for the current text (search for section 17538 or "unsolicited"
|
||||
in the Business and Professions Code).
|
||||
</p>
|
||||
</div>
|
||||
<pre>
|
||||
17538.45. (a) For purposes of this section, the following words
|
||||
have the following meanings:
|
||||
(1) "Electronic mail advertisement" means any electronic mail
|
||||
message, the principal purpose of which is to promote, directly or
|
||||
indirectly, the sale or other distribution of goods or services to
|
||||
the recipient.
|
||||
(2) "Unsolicited electronic mail advertisement" means any
|
||||
electronic mail advertisement that meets both of the following
|
||||
requirements:
|
||||
(A) It is addressed to a recipient with whom the initiator does
|
||||
not have an existing business or personal relationship.
|
||||
(B) It is not sent at the request of or with the express consent
|
||||
of the recipient.
|
||||
(3) "Electronic mail service provider" means any business or
|
||||
organization qualified to do business in California that provides
|
||||
registered users the ability to send or receive electronic mail
|
||||
through equipment located in this state and that is an intermediary
|
||||
in sending or receiving electronic mail.
|
||||
(4) "Initiation" of an unsolicited electronic mail advertisement
|
||||
refers to the action by the initial sender of the electronic mail
|
||||
advertisement. It does not refer to the actions of any intervening
|
||||
electronic mail service provider that may handle or retransmit the
|
||||
electronic message.
|
||||
(5) "Registered user" means any individual, corporation, or other
|
||||
entity that maintains an electronic mail address with an electronic
|
||||
mail service provider.
|
||||
(b) No registered user of an electronic mail service provider
|
||||
shall use or cause to be used that electronic mail service provider's
|
||||
equipment located in this state in violation of that electronic mail
|
||||
service provider's policy prohibiting or restricting the use of its
|
||||
service or equipment for the initiation of unsolicited electronic
|
||||
mail advertisements.
|
||||
(c) No individual, corporation, or other entity shall use or cause
|
||||
to be used, by initiating an unsolicited electronic mail
|
||||
advertisement, an electronic mail service provider's equipment
|
||||
located in this state in violation of that electronic mail service
|
||||
provider's policy prohibiting or restricting the use of its equipment
|
||||
to deliver unsolicited electronic mail advertisements to its
|
||||
registered users.
|
||||
(d) An electronic mail service provider shall not be required to
|
||||
create a policy prohibiting or restricting the use of its equipment
|
||||
for the initiation or delivery of unsolicited electronic mail
|
||||
advertisements.
|
||||
(e) Nothing in this section shall be construed to limit or
|
||||
restrict the rights of an electronic mail service provider under
|
||||
Section 230(c)(1) of Title 47 of the United States Code, any decision
|
||||
of an electronic mail service provider to permit or to restrict
|
||||
access to or use of its system, or any exercise of its editorial
|
||||
function.
|
||||
(f) (1) In addition to any other action available under law, any
|
||||
electronic mail service provider whose policy on unsolicited
|
||||
electronic mail advertisements is violated as provided in this
|
||||
section may bring a civil action to recover the actual monetary loss
|
||||
suffered by that provider by reason of that violation, or liquidated
|
||||
damages of fifty dollars ($50) for each electronic mail message
|
||||
initiated or delivered in violation of this section, up to a maximum
|
||||
of twenty-five thousand dollars ($25,000) per day, whichever amount
|
||||
is greater.
|
||||
(2) In any action brought pursuant to paragraph (1), the court may
|
||||
award reasonable attorney's fees to a prevailing party.
|
||||
(3) (A) In any action brought pursuant to paragraph (1), the
|
||||
electronic mail service provider shall be required to establish as an
|
||||
element of its cause of action that prior to the alleged violation,
|
||||
the defendant had actual notice of both of the following:
|
||||
(i) The electronic mail service provider's policy on unsolicited
|
||||
electronic mail advertising.
|
||||
(ii) The fact that the defendant's unsolicited electronic mail
|
||||
advertisements would use or cause to be used the electronic mail
|
||||
service provider's equipment located in this state.
|
||||
(B) In this regard, the Legislature finds that with rapid advances
|
||||
in Internet technology, and electronic mail technology in
|
||||
particular, Internet service providers are already experimenting with
|
||||
embedding policy statements directly into the software running on
|
||||
the computers used to provide electronic mail services in a manner
|
||||
that displays the policy statements every time an electronic mail
|
||||
delivery is requested. While the state of the technology does not
|
||||
support this finding at present, the Legislature believes that, in a
|
||||
given case at some future date, a showing that notice was supplied
|
||||
via electronic means between the sending and receiving computers
|
||||
could be held to constitute actual notice to the sender for purposes
|
||||
of this paragraph.
|
||||
(4) (A) An electronic mail service provider who has brought an
|
||||
action against a party for a violation under Section 17529.8 shall
|
||||
not bring an action against that party under this section for the
|
||||
same unsolicited commercial electronic mail advertisement.
|
||||
(B) An electronic mail service provider who has brought an action
|
||||
against a party for a violation of this section shall not bring an
|
||||
action against that party under Section 17529.8 for the same
|
||||
unsolicited commercial electronic mail advertisement.
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
11
r2/r2/public/static/js/jquery-1.2.6.js
vendored
Normal file
11
r2/r2/public/static/js/jquery-1.2.6.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
r2/r2/public/static/js/jquery.js
vendored
Symbolic link
1
r2/r2/public/static/js/jquery.js
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
jquery-1.2.6.js
|
||||
156
r2/r2/public/static/js/jquery.json-1.3.js
Normal file
156
r2/r2/public/static/js/jquery.json-1.3.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* jQuery JSON Plugin
|
||||
* version: 1.0 (2008-04-17)
|
||||
*
|
||||
* This document is licensed as free software under the terms of the
|
||||
* MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Brantley Harris technically wrote this plugin, but it is based somewhat
|
||||
* on the JSON.org website's http://www.json.org/json2.js, which proclaims:
|
||||
* "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
|
||||
* I uphold. I really just cleaned it up.
|
||||
*
|
||||
* It is also based heavily on MochiKit's serializeJSON, which is
|
||||
* copywrited 2005 by Bob Ippolito.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
function toIntegersAtLease(n)
|
||||
// Format integers to have at least two digits.
|
||||
{
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
Date.prototype.toJSON = function(date)
|
||||
// Yes, it polutes the Date namespace, but we'll allow it here, as
|
||||
// it's damned usefull.
|
||||
{
|
||||
return this.getUTCFullYear() + '-' +
|
||||
toIntegersAtLease(this.getUTCMonth()) + '-' +
|
||||
toIntegersAtLease(this.getUTCDate());
|
||||
};
|
||||
|
||||
var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
|
||||
var meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
|
||||
$.quoteString = function(string)
|
||||
// Places quotes around a string, inteligently.
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
{
|
||||
if (escapeable.test(string))
|
||||
{
|
||||
return '"' + string.replace(escapeable, function (a)
|
||||
{
|
||||
var c = meta[a];
|
||||
if (typeof c === 'string') {
|
||||
return c;
|
||||
}
|
||||
c = a.charCodeAt();
|
||||
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
|
||||
}) + '"';
|
||||
}
|
||||
return '"' + string + '"';
|
||||
};
|
||||
|
||||
$.toJSON = function(o, compact)
|
||||
{
|
||||
var type = typeof(o);
|
||||
|
||||
if (type == "undefined")
|
||||
return "undefined";
|
||||
else if (type == "number" || type == "boolean")
|
||||
return o + "";
|
||||
else if (o === null)
|
||||
return "null";
|
||||
|
||||
// Is it a string?
|
||||
if (type == "string")
|
||||
{
|
||||
return $.quoteString(o);
|
||||
}
|
||||
|
||||
// Does it have a .toJSON function?
|
||||
if (type == "object" && typeof o.toJSON == "function")
|
||||
return o.toJSON(compact);
|
||||
|
||||
// Is it an array?
|
||||
if (type != "function" && typeof(o.length) == "number")
|
||||
{
|
||||
var ret = [];
|
||||
for (var i = 0; i < o.length; i++) {
|
||||
ret.push( $.toJSON(o[i], compact) );
|
||||
}
|
||||
if (compact)
|
||||
return "[" + ret.join(",") + "]";
|
||||
else
|
||||
return "[" + ret.join(", ") + "]";
|
||||
}
|
||||
|
||||
// If it's a function, we have to warn somebody!
|
||||
if (type == "function") {
|
||||
throw new TypeError("Unable to convert object of type 'function' to json.");
|
||||
}
|
||||
|
||||
// It's probably an object, then.
|
||||
var ret = [];
|
||||
for (var k in o) {
|
||||
var name;
|
||||
type = typeof(k);
|
||||
|
||||
if (type == "number")
|
||||
name = '"' + k + '"';
|
||||
else if (type == "string")
|
||||
name = $.quoteString(k);
|
||||
else
|
||||
continue; //skip non-string or number keys
|
||||
|
||||
var val = $.toJSON(o[k], compact);
|
||||
if (typeof(val) != "string") {
|
||||
// skip non-serializable values
|
||||
continue;
|
||||
}
|
||||
|
||||
if (compact)
|
||||
ret.push(name + ":" + val);
|
||||
else
|
||||
ret.push(name + ": " + val);
|
||||
}
|
||||
return "{" + ret.join(", ") + "}";
|
||||
};
|
||||
|
||||
$.compactJSON = function(o)
|
||||
{
|
||||
return $.toJSON(o, true);
|
||||
};
|
||||
|
||||
$.evalJSON = function(src)
|
||||
// Evals JSON that we know to be safe.
|
||||
{
|
||||
return eval("(" + src + ")");
|
||||
};
|
||||
|
||||
$.secureEvalJSON = function(src)
|
||||
// Evals JSON in a way that is *more* secure.
|
||||
{
|
||||
var filtered = src;
|
||||
filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
|
||||
filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
|
||||
filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
|
||||
|
||||
if (/^[\],:{}\s]*$/.test(filtered))
|
||||
return eval("(" + src + ")");
|
||||
else
|
||||
throw new SyntaxError("Error parsing JSON, source is not valid.");
|
||||
};
|
||||
})(jQuery);
|
||||
1
r2/r2/public/static/js/jquery.json.js
Symbolic link
1
r2/r2/public/static/js/jquery.json.js
Symbolic link
@@ -0,0 +1 @@
|
||||
jquery.json-1.3.js
|
||||
619
r2/r2/public/static/js/jquery.reddit.js
Normal file
619
r2/r2/public/static/js/jquery.reddit.js
Normal file
@@ -0,0 +1,619 @@
|
||||
/* The reddit extension for jquery. This file is intended to store
|
||||
* "utils" type function declarations and to add functionality to "$"
|
||||
* or "jquery" lookups. See
|
||||
* http://docs.jquery.com/Plugins/Authoring
|
||||
* for the plug-in spec.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
/* utility functions */
|
||||
|
||||
$.log = function(message) {
|
||||
if (window.console)
|
||||
console.debug(message);
|
||||
else
|
||||
alert(message);
|
||||
};
|
||||
|
||||
$.debug = $.log;
|
||||
|
||||
$.redirect = function(dest) {
|
||||
window.location = dest;
|
||||
};
|
||||
|
||||
$.refresh = function() {
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
$.defined = function(value) {
|
||||
return (typeof(value) != "undefined");
|
||||
};
|
||||
|
||||
$.with_default = function(value, alt) {
|
||||
return $.defined(value) ? value : alt;
|
||||
};
|
||||
|
||||
$.unsafe = function(text) {
|
||||
/* inverts websafe filtering of reddit app. */
|
||||
if(typeof(text) == "string") {
|
||||
text = text.replace(/>/g, ">")
|
||||
.replace(/</g, "<").replace(/&/g, "&")
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
return (text || "");
|
||||
};
|
||||
|
||||
/* upgrade show and hide to trigger onshow/onhide events when fired. */
|
||||
(function(show, hide) {
|
||||
$.fn.show = function(speed, callback) {
|
||||
$(this).trigger("onshow");
|
||||
return show.call(this, speed, callback);
|
||||
}
|
||||
$.fn.hide = function(speed, callback) {
|
||||
$(this).trigger("onhide");
|
||||
return hide.call(this, speed, callback);
|
||||
}
|
||||
})($.fn.show, $.fn.hide);
|
||||
|
||||
/* customized requests (formerly redditRequest) */
|
||||
|
||||
var _ajax_locks = {};
|
||||
function acquire_ajax_lock(op) {
|
||||
if(_ajax_locks[op]) {
|
||||
return false;
|
||||
}
|
||||
_ajax_locks[op] = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
function release_ajax_lock(op) {
|
||||
delete _ajax_locks[op];
|
||||
};
|
||||
|
||||
function handleResponse(action) {
|
||||
return function(r) {
|
||||
if(r.jquery) {
|
||||
var objs = {};
|
||||
objs[0] = jQuery;
|
||||
$.map(r.jquery, function(q) {
|
||||
var old_i = q[0], new_i = q[1], op = q[2], args = q[3];
|
||||
for(var i = 0; args.length && i < args.length; i++)
|
||||
args[i] = $.unsafe(args[i]);
|
||||
if (op == "call")
|
||||
objs[new_i] = objs[old_i].apply(objs[old_i]._obj, args);
|
||||
else if (op == "attr") {
|
||||
objs[new_i] = objs[old_i][args];
|
||||
if(objs[new_i])
|
||||
objs[new_i]._obj = objs[old_i];
|
||||
else
|
||||
$.debug("unrecognized");
|
||||
}
|
||||
else {
|
||||
$.debug("unrecognized");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var api_loc = '/api/';
|
||||
$.request = function(op, parameters, worker_in, block) {
|
||||
/*
|
||||
Uniquitous reddit AJAX poster. Automatically addes
|
||||
handleResponse(action) worker to deal with the API result. The
|
||||
current subreddit (reddit.post_site), the user's modhash
|
||||
(reddit.modhash) and whether or not we are in a frame
|
||||
(reddit.cnameframe) are also automatically sent across.
|
||||
*/
|
||||
var action = op;
|
||||
var worker = worker_in;
|
||||
|
||||
/* we have a lock if we are not blocking or if we have gotten a lock */
|
||||
var have_lock = !$.with_default(block, false) || acquire_ajax_lock(action);
|
||||
|
||||
parameters = $.with_default(parameters, {});
|
||||
worker_in = $.with_default(worker_in, handleResponse(action));
|
||||
var worker = function(r) {
|
||||
release_ajax_lock(action);
|
||||
return worker_in(r);
|
||||
};
|
||||
|
||||
|
||||
/* set the subreddit name if there is one */
|
||||
if (reddit.post_site)
|
||||
parameters.r = reddit.post_site;
|
||||
|
||||
/* flag whether or not we are on a cname */
|
||||
if (reddit.cnameframe)
|
||||
parameters.cnameframe = 1;
|
||||
|
||||
/* add the modhash if the user is logged in */
|
||||
if (reddit.logged)
|
||||
parameters.uh = reddit.modhash;
|
||||
|
||||
if(have_lock) {
|
||||
op = api_loc + op;
|
||||
/*if( document.location.host == reddit.ajax_domain )
|
||||
/* normal AJAX post */
|
||||
$.post(op, parameters, worker, "json");
|
||||
/*else { /* cross domain it is... * /
|
||||
op = "http://" + reddit.ajax_domain + op + "?callback=?";
|
||||
$.getJSON(op, parameters, worker);
|
||||
} */
|
||||
}
|
||||
};
|
||||
|
||||
var up_cls = "up";
|
||||
var upmod_cls = "upmod";
|
||||
var down_cls = "down";
|
||||
var downmod_cls = "downmod";
|
||||
|
||||
$.fn.vote = function(vh, callback) {
|
||||
/* for vote to work, $(this) should be the clicked arrow */
|
||||
if($(this).hasClass("arrow")) {
|
||||
var dir = ( $(this).hasClass(up_cls) ? 1 :
|
||||
( $(this).hasClass(down_cls) ? -1 : 0) );
|
||||
var things = $(this).all_things_by_id();
|
||||
/* find all arrows of things on the page */
|
||||
var arrows = things.children().not(".child").find('.arrow');
|
||||
|
||||
/* set the new arrow states */
|
||||
var u_before = (dir == 1) ? up_cls : upmod_cls;
|
||||
var u_after = (dir == 1) ? upmod_cls : up_cls;
|
||||
arrows.filter("."+u_before).removeClass(u_before).addClass(u_after);
|
||||
|
||||
var d_before = (dir == -1) ? down_cls : downmod_cls;
|
||||
var d_after = (dir == -1) ? downmod_cls : down_cls;
|
||||
arrows.filter("."+d_before).removeClass(d_before).addClass(d_after);
|
||||
|
||||
/* let the user vote only if they are logged in */
|
||||
if(reddit.logged) {
|
||||
|
||||
/* set the score and update the class */
|
||||
things.each(function() {
|
||||
var score = $(this).children().not(".child").find(".score");
|
||||
var to_update = score.add($(this));
|
||||
var label = reddit && reddit.vl &&
|
||||
reddit.vl[$(this).thing_id()];
|
||||
if(label)
|
||||
score.html(label[dir+1]);
|
||||
if(dir > 0)
|
||||
to_update.addClass('likes').removeClass('dislikes');
|
||||
else if(dir < 0)
|
||||
to_update.removeClass('likes').addClass('dislikes');
|
||||
else
|
||||
to_update.removeClass('likes').removeClass('dislikes');
|
||||
});
|
||||
|
||||
$.request("vote", {id: things.filter(":first").thing_id(),
|
||||
dir : dir, vh : vh});
|
||||
}
|
||||
/* execute any callbacks passed in. */
|
||||
if(callback)
|
||||
callback(things, dir);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$.fn.thing = function() {
|
||||
/* Returns the first thing that is a parent of the current element */
|
||||
return this.parents(".thing:first");
|
||||
};
|
||||
|
||||
$.fn.all_things_by_id = function() {
|
||||
/* Returns the set of things that have the same ID as the current
|
||||
* element's thing (we make no guarantee about uniqueness of
|
||||
* things across multiple listings on the same page) */
|
||||
return this.thing().add( $.things(this.thing_id()) );
|
||||
};
|
||||
|
||||
$.fn.thing_id = function() {
|
||||
/* Returns the (reddit) ID of the current element's thing */
|
||||
var t = (this.hasClass("thing")) ? this : this.thing();
|
||||
if(t.length) {
|
||||
var id = $.grep(t.get(0).className.split(' '),
|
||||
function(i) { return i.match(/^id-/); });
|
||||
return (id.length) ? id[0].slice(3, id[0].length) : "";
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
$.things = function() {
|
||||
/*
|
||||
* accepts a list of thing_ids as the first argument and returns a
|
||||
* jquery object consisting of the union of all things on the page
|
||||
* that represent those things.
|
||||
*/
|
||||
var sel = $.map(arguments, function(x) { return ".thing.id-" + x; })
|
||||
.join(", ");
|
||||
return $(sel);
|
||||
};
|
||||
|
||||
$.listing = function(name) {
|
||||
/*
|
||||
* Given an element name (a sitetable ID or a thing ID, with
|
||||
* optional siteTable_ at the front), return or generate a listing
|
||||
* with the proper id for that name.
|
||||
*
|
||||
* In the case of a thing ID, this siteTable will be the listing
|
||||
* in the child div of that thing's container.
|
||||
*
|
||||
* In the case of a general ID, it will be the listing of that
|
||||
* name already present in the DOM.
|
||||
*
|
||||
* On failure, will return a JQuery object of zero length.
|
||||
*/
|
||||
name = name || "";
|
||||
var sitetable = "siteTable";
|
||||
/* we'll add the hash specifier in later */
|
||||
if (name.slice(0, 1) == "#" || name.slice(0, 1) == ".")
|
||||
name = name.slice(1, name.length);
|
||||
|
||||
/* lname should be the name of the actual listing (will always
|
||||
* start with sitetable) while name should be the element it is
|
||||
* named for (strip off sitetable if present) */
|
||||
var lname = name;
|
||||
if(name.slice(0, sitetable.length) != sitetable)
|
||||
lname = sitetable + ( (name) ? ("_" + name): "");
|
||||
else
|
||||
name = name.slice(sitetable.length + 1, name.length);
|
||||
|
||||
var listing = $("#" + lname).filter(":first");
|
||||
/* did the $ lookup match anything? */
|
||||
if (listing.length == 0) {
|
||||
listing = $.things(name).find(".child")
|
||||
.append(document.createElement('div'))
|
||||
.children(":last")
|
||||
.addClass("sitetable")
|
||||
.attr("id", lname);
|
||||
}
|
||||
return listing;
|
||||
};
|
||||
|
||||
|
||||
var thing_init_func = function() { };
|
||||
$.fn.set_thing_init = function(func) {
|
||||
thing_init_func = func;
|
||||
$(this).find(".thing").each(function() { func(this) });
|
||||
};
|
||||
|
||||
|
||||
$.fn.new_thing_child = function(what, use_listing) {
|
||||
var id = this.thing_id();
|
||||
var where = (use_listing) ? $.listing(id) :
|
||||
this.thing().find(".child:first");
|
||||
|
||||
if (typeof(what) == "string")
|
||||
return where.prepend(what).children(":first");
|
||||
else
|
||||
return what.hide()
|
||||
.prependTo(where)
|
||||
.show()
|
||||
.find('input[name=parent]').attr('value', id).end();
|
||||
|
||||
};
|
||||
|
||||
|
||||
$.replace_things = function(things, keep_children, reveal, stubs) {
|
||||
/* Given the api-html structured things, insert them into the DOM
|
||||
* in such a way as to remove any elements with the same thing_id.
|
||||
* "keep_children" is a boolean to determine whether or not any
|
||||
* existing child divs should be retained on the new thing (in the
|
||||
* case of a comment tree, flags whether or not the new thing has
|
||||
* the thread present) while "reveal" determines whether or not to
|
||||
* animate the transition from old to new. */
|
||||
var midcol = $(".midcol:visible:first").css("width");
|
||||
var numcol = $(".rank:visible:first").css("width");
|
||||
return $.map(things, function(thing) {
|
||||
var data = thing.data;
|
||||
var existing = $.things(data.id);
|
||||
if(stubs)
|
||||
existing = existing.filter(".stub");
|
||||
existing.after($.unsafe(data.content));
|
||||
var new_thing = existing.next();
|
||||
new_thing.find(".midcol").css("width", midcol).end()
|
||||
.find(".rank").css("width", midcol);
|
||||
|
||||
/* update the score lookups */
|
||||
if(data.vl)
|
||||
reddit.vl[data.id] = data.vl;
|
||||
|
||||
if(keep_children) {
|
||||
/* show the new thing */
|
||||
new_thing.show()
|
||||
/* but hide its new content */
|
||||
.children(".midcol, .entry").hide().end()
|
||||
.children(".child:first")
|
||||
/* slop over the children */
|
||||
.html(existing.children(".child:first")
|
||||
.remove().html())
|
||||
.end();
|
||||
/* hide the old entry and show the new one */
|
||||
if(reveal) {
|
||||
existing.hide();
|
||||
new_thing.children(".midcol, .entry").show();
|
||||
}
|
||||
new_thing.find(".rank:first")
|
||||
.html(existing.find(".rank:first").html());
|
||||
}
|
||||
|
||||
/* hide and remove old. add in new */
|
||||
if(reveal) {
|
||||
existing.hide();
|
||||
if(keep_children)
|
||||
new_thing.children(".midcol, .entry")
|
||||
.show();
|
||||
else
|
||||
new_thing.show();
|
||||
existing.remove();
|
||||
}
|
||||
else {
|
||||
new_thing.hide();
|
||||
existing.remove();
|
||||
}
|
||||
|
||||
/* lastly, set the event handlers for these new things */
|
||||
thing_init_func(new_thing);
|
||||
return new_thing;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
$.insert_things = function(things, append) {
|
||||
/* Insert new things into a listing.*/
|
||||
return $.map(things, function(thing) {
|
||||
var data = thing.data;
|
||||
var midcol = $(".midcol:visible:first").css("width");
|
||||
var numcol = $(".rank:visible:first").css("width");
|
||||
var s = $.listing(data.parent);
|
||||
if(data.vl)
|
||||
reddit.vl[data.id] = data.vl;
|
||||
if(append)
|
||||
s = s.append($.unsafe(data.content)).children(".thing:last");
|
||||
else
|
||||
s = s.prepend($.unsafe(data.content)).children(".thing:first");
|
||||
s.find(".midcol").css("width", midcol);
|
||||
s.find(".rank").css("width", midcol);
|
||||
thing_init_func(s.hide().show());
|
||||
return s;
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.delete_table_row = function() {
|
||||
var tr = this.parents("tr:first").get(0);
|
||||
var table = this.parents("table").get(0);
|
||||
$(tr).fadeOut(function() {
|
||||
table.deleteRow(tr.rowIndex);
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.insert_table_rows = function(rows, index) {
|
||||
/* find the subset of the current selection that is a table, or
|
||||
* the first parent of the current selection that is a table.*/
|
||||
var tables = ((this.is("table")) ? this.filter("table") :
|
||||
this.parents("table:first"));
|
||||
|
||||
$.map(tables.get(),
|
||||
function(table) {
|
||||
$.map(rows, function(thing) {
|
||||
var i = index;
|
||||
if(i < 0)
|
||||
i = Math.max(table.rows.length + i + 1, 0);
|
||||
i = Math.min(i, table.rows.length);
|
||||
/* create a new row and set its id and class*/
|
||||
var row = table.insertRow(i);
|
||||
$(row).hide().attr("id", thing.id)
|
||||
.addClass(thing.css_class);
|
||||
/* insert cells */
|
||||
$.map(thing.cells, function(cell) {
|
||||
$(row.insertCell(row.cells.length))
|
||||
.html($.unsafe(cell));
|
||||
});
|
||||
/* reveal! */
|
||||
$(row).fadeIn();
|
||||
});
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
$.set_tracker = function(id, show_track, click_track) {
|
||||
/* hook for api to pass back tracker information */
|
||||
reddit.trackers[id] = {show: show_track, click: click_track};
|
||||
$.things(id).filter(":visible").show();
|
||||
};
|
||||
|
||||
|
||||
$.fn.captcha = function(iden) {
|
||||
/* */
|
||||
var c = this.find(".capimage");
|
||||
if(iden) {
|
||||
c.attr("src", "http://" + reddit.ajax_domain
|
||||
+ "/captcha/" + iden + ".png")
|
||||
.parents("form").find("input[name=iden]").attr("value", iden);
|
||||
}
|
||||
return c;
|
||||
};
|
||||
|
||||
|
||||
/* Textarea handlers */
|
||||
$.fn.insertAtCursor = function(value) {
|
||||
/* "this" refers to current jquery selection and may contain many
|
||||
* non-textarea elements, so filter out and apply to each */
|
||||
return $(this).filter("textarea").each(function() {
|
||||
/* this should be rebound to one of the elements in the orig list.*/
|
||||
var textbox = $(this).get(0);
|
||||
var orig_pos = textbox.scrollTop;
|
||||
|
||||
if (document.selection) { /* IE */
|
||||
textbox.focus();
|
||||
var sel = document.selection.createRange();
|
||||
sel.text = value;
|
||||
}
|
||||
else if (textbox.selectionStart) {
|
||||
var prev_start = textbox.selectionStart;
|
||||
textbox.value =
|
||||
textbox.value.substring(0, textbox.selectionStart) +
|
||||
value +
|
||||
textbox.value.substring(textbox.selectionEnd,
|
||||
textbox.value.length);
|
||||
prev_start += value.length;
|
||||
textbox.setSelectionRange(prev_start, prev_start);
|
||||
} else {
|
||||
textbox.value += value;
|
||||
}
|
||||
|
||||
if(textbox.scrollHeight) {
|
||||
textbox.scrollTop = orig_pos;
|
||||
}
|
||||
|
||||
$(this).focus();
|
||||
})
|
||||
.end();
|
||||
};
|
||||
|
||||
$.fn.select_line = function(lineNo) {
|
||||
return $(this).filter("textarea").each(function() {
|
||||
var newline = '\n', newline_length = 1, caret_pos = 0;
|
||||
if ( $.browser.msie ) { /* IE hack */
|
||||
newline = '\r';
|
||||
newline_length = 0;
|
||||
caret_pos = 1;
|
||||
}
|
||||
|
||||
var lines = $(this).attr("value").split(newline);
|
||||
|
||||
for(var x=0; x<lineNo-1; x++)
|
||||
caret_pos += lines[x].length + newline_length;
|
||||
|
||||
var end_pos = caret_pos;
|
||||
if (lineNo <= lines.length)
|
||||
end_pos += lines[lineNo-1].length + newline_length;
|
||||
|
||||
$(this).focus();
|
||||
if(this.createTextRange) { /* IE */
|
||||
var start = this.createTextRange();
|
||||
start.move('character', caret_pos);
|
||||
var end = this.createTextRange();
|
||||
end.move('character', end_pos);
|
||||
start.setEndPoint("StartToEnd", end);
|
||||
start.select();
|
||||
} else if (this.selectionStart) {
|
||||
this.setSelectionRange(caret_pos, end_pos);
|
||||
}
|
||||
if(this.scrollHeight) {
|
||||
var avgLineHight = this.scrollHeight / lines.length;
|
||||
this.scrollTop = (lineNo-2) * avgLineHight;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$.apply_stylesheet = function(cssText) {
|
||||
|
||||
var sheet_title = $("head").children("link[title], style[title]")
|
||||
.filter(":first").attr("title") || "preferred stylesheet";
|
||||
|
||||
if(document.styleSheets[0].cssText) {
|
||||
/* of course IE has to do this differently from everyone else. */
|
||||
var sheets = document.styleSheets;
|
||||
for(var x=0; x < sheets.length; x++)
|
||||
if(sheets[x].title == sheet_title) {
|
||||
sheets[x].cssText = cssText;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* for everyone else, we walk <head> for the <link> or <style>
|
||||
* that has the old stylesheet, and delete it. Then we add a
|
||||
* <style> with the new one */
|
||||
$("head").children("*[title=" + sheet_title + "]").remove();
|
||||
$("head").append("<style type='text/css' media='screen' title='" +
|
||||
sheet_title + "'>" + cssText + "</style>");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* namespace globals for cookies -- default prefix and domain */
|
||||
var default_cookie_domain;
|
||||
$.default_cookie_domain = function(domain) {
|
||||
if($.defined(domain))
|
||||
default_cookie_domain = domain;
|
||||
return default_cookie_domain;
|
||||
};
|
||||
|
||||
var cookie_name_prefix = "";
|
||||
$.cookie_name_prefix = function(name) {
|
||||
if($.defined(name))
|
||||
cookie_name_prefix = name + "_";
|
||||
return cookie_name_prefix;
|
||||
};
|
||||
|
||||
|
||||
/* cookie functions */
|
||||
$.cookie_test = function() {
|
||||
/* tries to write a cookie and sees if it is allowed by making
|
||||
* sure it can read back what it wrote */
|
||||
var m = (Math.random() + "").split('.')[1];
|
||||
var name = "test";
|
||||
$.cookie_write({name: name, data: m})
|
||||
if ($.cookie_read(name).data == m) {
|
||||
$.cookie_erase(name);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
$.cookie_erase = function(data) {
|
||||
data.data = "";
|
||||
data.expires = -1;
|
||||
$.cookie_write(data);
|
||||
};
|
||||
|
||||
$.cookie_write = function(c) {
|
||||
if(c.name) {
|
||||
var data = $.with_default(c.data, "");
|
||||
data = (typeof(data) == 'string') ? data : $.toJSON(data);
|
||||
data = cookie_name_prefix + c.name+'='+ escape(data);
|
||||
if($.defined(c.expires)) {
|
||||
var expires = c.expires;
|
||||
/* interpret numbers as number of days */
|
||||
if(typeof(expires) == "number") {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(expires*24*60*60*1000));
|
||||
expires = date;
|
||||
}
|
||||
/* Dates will have a conversion function */
|
||||
if($.defined(expires.toGMTString))
|
||||
expires = expires.toGMTString();
|
||||
data += '; expires=' + expires;
|
||||
}
|
||||
var domain = $.with_default(c.domain, default_cookie_domain);
|
||||
if($.defined(domain))
|
||||
data += '; domain=' + domain;
|
||||
data += '; path=' + $.with_default(c.path, '/');
|
||||
document.cookie=data;
|
||||
}
|
||||
};
|
||||
|
||||
$.cookie_read = function(name) {
|
||||
var nameEQ = cookie_name_prefix + name + '=';
|
||||
var ca=document.cookie.split(';');
|
||||
/* walk the list backwards so we always get the last cookie in the
|
||||
list */
|
||||
var data = '';
|
||||
for(var i = ca.length-1; i >= 0; i--) {
|
||||
var c = ca[i];
|
||||
while(c.charAt(0)==' ') c=c.substring(1,c.length);
|
||||
if(c.indexOf(nameEQ)==0) {
|
||||
/* we can unescape even if it's not escaped */
|
||||
data = unescape(c.substring(nameEQ.length,c.length));
|
||||
try {
|
||||
data = $.secureEvalJSON(data);
|
||||
} catch(e) {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {name: name, data: data};
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
568
r2/r2/public/static/js/reddit.js
Normal file
568
r2/r2/public/static/js/reddit.js
Normal file
@@ -0,0 +1,568 @@
|
||||
function open_menu(menu) {
|
||||
$(menu).siblings(".drop-choices").not(".inuse")
|
||||
.css("top", menu.offsetHeight + 'px')
|
||||
.each(function(){
|
||||
$(this).css("left", $(menu).offset().left + "px")
|
||||
.css("top", ($(menu).height()+
|
||||
$(menu).offset().top) + "px");
|
||||
})
|
||||
.addClass("active inuse");
|
||||
};
|
||||
|
||||
function close_menus() {
|
||||
$(".drop-choices.inuse").not(".active")
|
||||
.removeClass("inuse");
|
||||
$(".drop-choices.active").removeClass("active");
|
||||
};
|
||||
|
||||
function hover_open_menu(menu) { };
|
||||
|
||||
function post_form(form, where, statusfunc, nametransformfunc, block) {
|
||||
try {
|
||||
if(statusfunc == null)
|
||||
statusfunc = function(x) {
|
||||
return reddit.status_msg.submitting;
|
||||
};
|
||||
/* set the submitted state */
|
||||
$(form).find(".error").not(".status").hide();
|
||||
$(form).find(".status").html(statusfunc(form)).show();
|
||||
return simple_post_form(form, where, {});
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function simple_post_form(form, where, fields) {
|
||||
fields = fields || {};
|
||||
/* consolidate the form's inputs for submission */
|
||||
$(form).find("select, input, textarea").not(".gray").each(function() {
|
||||
if (($(this).attr("type") != "radio" &&
|
||||
$(this).attr("type") != "checkbox") ||
|
||||
$(this).attr("checked"))
|
||||
fields[$(this).attr("name")] = $(this).attr("value");
|
||||
});
|
||||
if (fields.id == null) {
|
||||
fields.id = $(form).attr("id") ? ("#" + $(form).attr("id")) : "";
|
||||
}
|
||||
|
||||
$.request(where, fields);
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
function emptyInput(elem, msg) {
|
||||
if (! $(elem).attr("value") || $(elem).attr("value") == msg )
|
||||
$(elem).addClass("gray").attr("value", msg).attr("rows", 3);
|
||||
else
|
||||
$(elem).focus(function(){});
|
||||
};
|
||||
|
||||
|
||||
function showlang() {
|
||||
$(".lang-popup:first").show();
|
||||
return false;
|
||||
};
|
||||
|
||||
function showcover(warning, reason) {
|
||||
$.request("new_captcha");
|
||||
if (warning)
|
||||
$("#cover_disclaim, #cover_msg").show();
|
||||
else
|
||||
$("#cover_disclaim, #cover_msg").hide();
|
||||
$(".login-popup:first").show()
|
||||
.find("form input[name=reason]").attr("value", (reason || ""));
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
function hidecover(where) {
|
||||
$(where).parents(".cover-overlay").hide();
|
||||
return false;
|
||||
};
|
||||
|
||||
/* table handling */
|
||||
|
||||
function deleteRow(elem) {
|
||||
$(elem).delete_table_row();
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* general things */
|
||||
|
||||
function change_state(elem, op, callback) {
|
||||
var form = $(elem).parents("form");
|
||||
/* look to see if the form has an id specified */
|
||||
var id = form.find("input[name=id]");
|
||||
if (id.length)
|
||||
id = id.attr("value");
|
||||
else /* fallback on the parent thing */
|
||||
id = $(elem).thing_id();
|
||||
|
||||
simple_post_form(form, op, {id: id});
|
||||
/* call the callback first before we mangle anything */
|
||||
if (callback) callback(form, op);
|
||||
form.html(form.attr("executed").value);
|
||||
return false;
|
||||
};
|
||||
|
||||
function save_thing(elem) {
|
||||
$(elem).thing().addClass("saved");
|
||||
}
|
||||
|
||||
function unsave_thing(elem) {
|
||||
$(elem).thing().removeClass("saved");
|
||||
}
|
||||
|
||||
function hide_thing(elem) {
|
||||
var thing = $(elem).thing();
|
||||
thing.hide();
|
||||
if(thing.hasClass("hidden"))
|
||||
thing.removeClass("hidden");
|
||||
else
|
||||
thing.addClass("hidden");
|
||||
};
|
||||
|
||||
function toggle(elem, callback, cancelback) {
|
||||
var self = $(elem).parent().andSelf().filter(".option");
|
||||
var sibling = self.removeClass("active")
|
||||
.siblings().addClass("active").get(0);
|
||||
if(cancelback && !sibling.onclick) {
|
||||
sibling.onclick = function() {
|
||||
return toggle(sibling, cancelback, callback);
|
||||
}
|
||||
}
|
||||
if(callback) callback(elem);
|
||||
return false;
|
||||
};
|
||||
|
||||
function cancelToggleForm(elem, form_class, button_class, on_hide) {
|
||||
/* if there is a toggle button that triggered this, toggle it if
|
||||
* it is not already active.*/
|
||||
if(button_class && $(elem).filter("button").length) {
|
||||
var sel = $(elem).thing().find(button_class)
|
||||
.children(":visible").filter(":first");
|
||||
toggle(sel);
|
||||
}
|
||||
$(elem).thing().find(form_class)
|
||||
.each(function() {
|
||||
if(on_hide) on_hide($(this));
|
||||
$(this).hide().remove();
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
/* organic listing */
|
||||
|
||||
function get_organic(elem, next) {
|
||||
var listing = $(elem).parents(".organic-listing");
|
||||
var thing = listing.find(".thing:visible");
|
||||
if(listing.find(":animated").length)
|
||||
return false;
|
||||
|
||||
/* note we are looking for .thing.link while empty entries (if the
|
||||
* loader isn't working) will be .thing.stub -> no visual
|
||||
* glitches */
|
||||
var next_thing;
|
||||
if (next) {
|
||||
next_thing = thing.nextAll(".thing:not(.stub)").filter(":first");
|
||||
if (next_thing.length == 0)
|
||||
next_thing = thing.siblings(".thing:not(.stub)").filter(":first");
|
||||
}
|
||||
else {
|
||||
next_thing = thing.prevAll(".thing:not(.stub)").filter(":first");
|
||||
if (next_thing.length == 0)
|
||||
next_thing = thing.siblings(".thing:not(.stub)").filter(":last");
|
||||
}
|
||||
thing.fadeOut('fast', function() {
|
||||
if(next_thing.length)
|
||||
next_thing.fadeIn('fast', function() {
|
||||
|
||||
/* make sure the next n are loaded */
|
||||
var n = 5;
|
||||
var t = thing;
|
||||
var to_fetch = [];
|
||||
for(var i = 0; i < 2*n; i++) {
|
||||
t = (next) ? t.nextAll(".thing:first") :
|
||||
t.prevAll(".thing:first");
|
||||
if(t.length == 0)
|
||||
t = t.end().parent()
|
||||
.children( (next) ? ".thing:first" :
|
||||
".thing:last");
|
||||
if(t.filter(".stub").length)
|
||||
to_fetch.push(t.thing_id());
|
||||
if(i >= n && to_fetch.length == 0)
|
||||
break;
|
||||
}
|
||||
if(to_fetch.length) {
|
||||
$.request("fetch_links",
|
||||
{links: to_fetch.join(','),
|
||||
listing: listing.attr("id")});
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
/* links */
|
||||
|
||||
function linkstatus(form) {
|
||||
var title = $(form).find("#title").attr("value");
|
||||
if(title)
|
||||
return reddit.status_msg.submitting;
|
||||
return reddit.status_msg.fetching;
|
||||
};
|
||||
|
||||
|
||||
function subscribe(reddit_name) {
|
||||
return function() {
|
||||
$.request("subscribe", {sr: reddit_name, action: "sub"});
|
||||
};
|
||||
};
|
||||
|
||||
function unsubscribe(reddit_name) {
|
||||
return function() {
|
||||
$.request("subscribe", {sr: reddit_name, action: "unsub"});
|
||||
};
|
||||
};
|
||||
|
||||
function friend(user_name, container_name, type) {
|
||||
return function() {
|
||||
$.request("friend",
|
||||
{name: user_name, container: container_name, type: type});
|
||||
}
|
||||
};
|
||||
|
||||
function unfriend(user_name, container_name, type) {
|
||||
return function() {
|
||||
$.request("unfriend",
|
||||
{name: user_name, container: container_name, type: type});
|
||||
}
|
||||
};
|
||||
|
||||
function show_media(obj) {
|
||||
obj = $.unsafe(obj);
|
||||
return function(elem) {
|
||||
var where = $(elem).thing().find(".embededmedia");
|
||||
if (where.length)
|
||||
where.show().html(obj);
|
||||
else
|
||||
$(elem).new_thing_child('<div class="embededmedia">' + obj + '</div>');
|
||||
}
|
||||
};
|
||||
|
||||
function cancelMedia(elem) {
|
||||
return cancelToggleForm(elem, ".embededmedia", ".media-button");
|
||||
};
|
||||
|
||||
function share(elem) {
|
||||
$(elem).new_thing_child($(".sharelink:first").clone(true)
|
||||
.attr("id", "sharelink_" + $(elem).thing_id()),
|
||||
false);
|
||||
};
|
||||
|
||||
function cancelShare(elem) {
|
||||
return cancelToggleForm(elem, ".sharelink", ".share-button");
|
||||
};
|
||||
|
||||
|
||||
/* Comment generation */
|
||||
function helpon(elem) {
|
||||
$(elem).parents("form:first").children(".markhelp:first").show();
|
||||
};
|
||||
function helpoff(elem) {
|
||||
$(elem).parents("form:first").children(".markhelp:first").hide();
|
||||
};
|
||||
|
||||
|
||||
function chkcomment(form) {
|
||||
var entry = $(form).find("textarea");
|
||||
if( entry.hasClass("gray") || !entry.attr("value") ) {
|
||||
return false;
|
||||
} else if(form.replace.value)
|
||||
return post_form(form, 'editcomment', null, null, true);
|
||||
else
|
||||
return post_form(form, 'comment', null, null, true);
|
||||
};
|
||||
|
||||
function comment_edit(elem) {
|
||||
return $(".commentreply:first").clone(true)
|
||||
.find("button[name=cancel]").show().end()
|
||||
.attr("id", "commentreply_" + $(elem).thing_id());
|
||||
};
|
||||
|
||||
function reply(elem) {
|
||||
$(elem).new_thing_child(comment_edit(elem))
|
||||
.find('textarea:first').focus();
|
||||
};
|
||||
|
||||
function editcomment(elem) {
|
||||
var comment = $(elem).thing();
|
||||
var thing_name = comment.thing_id();
|
||||
var edit = comment_edit(elem);
|
||||
var content = comment.find(".edit-body:first").html();
|
||||
content = decodeURIComponent(content.replace(/\+/g, " "));
|
||||
edit.prependTo(comment)
|
||||
.hide()
|
||||
.find("button[name=comment]").hide().end()
|
||||
.find("button[name=edit]").show().end()
|
||||
.find("textarea")
|
||||
.attr("value", content)
|
||||
.removeClass("gray")
|
||||
edit.attr("parent").value = thing_name;
|
||||
edit.attr("replace").value = 1;
|
||||
|
||||
comment.children(".midcol, .entry").hide();
|
||||
edit.find("textarea:first").focus();
|
||||
edit.show();
|
||||
};
|
||||
|
||||
|
||||
function hidecomment(elem) {
|
||||
$(elem).thing().hide()
|
||||
.find(".noncollapsed:first, .midcol:first, .child:first").hide().end()
|
||||
.show().find(".entry:first .collapsed").show();
|
||||
return false;
|
||||
};
|
||||
|
||||
function showcomment(elem) {
|
||||
var comment = $(elem).thing();
|
||||
comment.find(".entry:first .collapsed").hide().end()
|
||||
.find(".noncollapsed:first, .midcol:first, .child:first").show().end()
|
||||
.show();
|
||||
return false;
|
||||
};
|
||||
|
||||
function cancelReply(elem) {
|
||||
var on_hide = function(form) {
|
||||
$.things($(form).attr("parent").value)
|
||||
.children(".midcol, .entry").show();
|
||||
};
|
||||
return cancelToggleForm(elem, ".commentreply", ".reply-button", on_hide);
|
||||
};
|
||||
|
||||
|
||||
function morechildren(form, link_id, children, depth) {
|
||||
$(form).html(reddit.status_msg.loading)
|
||||
.css("color", "red");
|
||||
var id = $(form).parents(".thing.morechildren:first").thing_id();
|
||||
$.request('morechildren', {link_id: link_id,
|
||||
children: children, depth: depth, id: id});
|
||||
return false;
|
||||
};
|
||||
|
||||
/* stylesheet and CSS stuff */
|
||||
|
||||
function update_reddit_count(site) {
|
||||
if (!site || !reddit.logged) return;
|
||||
|
||||
var decay_factor = .9; //precentage to keep
|
||||
var decay_period = 86400; //num of seconds between updates
|
||||
var num_recent = 10; //num of recent reddits to report
|
||||
var num_count = 100; //num of reddits to actually count
|
||||
|
||||
var date_key = '_date';
|
||||
var cur_date = new Date();
|
||||
var count_cookie = 'reddit_counts';
|
||||
var recent_cookie = 'recent_reddits';
|
||||
var reddit_counts = $.cookie_read(count_cookie).data;
|
||||
|
||||
//init the reddit_counts dict
|
||||
if (!$.defined(reddit_counts) ) {
|
||||
reddit_counts = {};
|
||||
reddit_counts[date_key] = cur_date.toString();
|
||||
}
|
||||
var last_reset = new Date(reddit_counts[date_key]);
|
||||
var decay = cur_date - last_reset > decay_period * 1000;
|
||||
|
||||
//incrmenet the count on the current reddit
|
||||
reddit_counts[site] = $.with_default(reddit_counts[site], 0) + 1;
|
||||
|
||||
//collect the reddit names (for sorting) and decay the view counts
|
||||
//if necessary
|
||||
var names = [];
|
||||
$.each(reddit_counts, function(sr_name, value) {
|
||||
if(sr_name != date_key) {
|
||||
if (decay && sr_name != site) {
|
||||
//compute the new count val
|
||||
var val = Math.floor(decay_factor * reddit_counts[sr_name]);
|
||||
if (val > 0)
|
||||
reddit_counts[sr_name] = val;
|
||||
else
|
||||
delete reddit_counts[sr_name];
|
||||
}
|
||||
if (reddit_counts[sr_name])
|
||||
names.push(sr_name);
|
||||
}
|
||||
});
|
||||
|
||||
//sort the names by the view counts
|
||||
names.sort(function(n1, n2) {
|
||||
return reddit_counts[n2] - reddit_counts[n1];
|
||||
});
|
||||
|
||||
//update the last decay date
|
||||
if (decay) reddit_counts[date_key] = cur_date.toString();
|
||||
|
||||
//build the list of names to report as "recent"
|
||||
var recent_reddits = "";
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
var sr_name = names[i];
|
||||
if (i < num_recent) {
|
||||
recent_reddits += names[i] + ',';
|
||||
} else if (i >= num_count && sr_name != site) {
|
||||
delete reddit_counts[sr_name];
|
||||
}
|
||||
}
|
||||
|
||||
//set the two cookies: one for the counts, one for the final
|
||||
//recent list
|
||||
$.cookie_write({name: count_cookie, data: reddit_counts});
|
||||
if (recent_reddits)
|
||||
$.cookie_write({name: recent_cookie, data: recent_reddits});
|
||||
};
|
||||
|
||||
|
||||
function add_thing_to_cookie(thing, cookie_name) {
|
||||
var id = $(thing).thing_id();
|
||||
var cookie = $.cookie_read(cookie_name);
|
||||
cookie.data += ":" + id;
|
||||
/* enforce a cookie max size of 1000 characters */
|
||||
while(cookie.data.length > 1000) {
|
||||
var i = cookie.data.indexOf(":");
|
||||
/* break on bad data in the cookie and whipe out the contents */
|
||||
if (i < 0) {
|
||||
cookie.data = "";
|
||||
break;
|
||||
}
|
||||
cookie.data = cookie.data.slice(i+1);
|
||||
}
|
||||
$.cookie_write(cookie);
|
||||
};
|
||||
|
||||
function updateEventHandlers(thing) {
|
||||
/* this function serves as a default callback every time a new
|
||||
* Thing is inserted into the DOM. It serves to rewrite a Thing's
|
||||
* event handlers depending on context (as in the case of an
|
||||
* organic listing) and to set the click behavior on links. */
|
||||
thing = $(thing);
|
||||
var listing = thing.parent();
|
||||
|
||||
$(thing).filter(".promotedlink").bind("onshow", function() {
|
||||
var id = $(this).thing_id();
|
||||
if($.inArray(id, reddit.tofetch) != -1) {
|
||||
$.request("onload", {ids: reddit.tofetch.join(",")});
|
||||
reddit.tofetch = [];
|
||||
}
|
||||
var tracker = reddit.trackers[id];
|
||||
if($.defined(tracker)) {
|
||||
$(this).find("a.title").attr("href", tracker.click).end()
|
||||
.find("img.promote-pixel")
|
||||
.attr("src", tracker.show);
|
||||
delete reddit.trackers[id];
|
||||
}
|
||||
})
|
||||
/* pre-trigger new event if already shown */
|
||||
.filter(":visible").trigger("onshow");
|
||||
|
||||
/* click on a title.. */
|
||||
$(thing).find("a.title").mousedown(function() {
|
||||
/* the site is either stored in the sr dict, or we are on
|
||||
* an sr and it is the current one */
|
||||
var sr = reddit.sr[$(this).thing_id()] || reddit.cur_site;
|
||||
update_reddit_count(sr);
|
||||
/* mark as clicked */
|
||||
$(this).addClass("click");
|
||||
/* set the click cookie. */
|
||||
add_thing_to_cookie(this, "click");
|
||||
/* remember this as the last thing clicked */
|
||||
last_click(thing);
|
||||
});
|
||||
|
||||
if (listing.filter(".organic-listing").length) {
|
||||
thing.find(".hide-button a, .del-button a.yes, .report-button a.yes")
|
||||
.each(function() { $(this).get(0).onclick = null });
|
||||
thing.find(".hide-button a")
|
||||
.click(function() {
|
||||
var a = $(this).get(0);
|
||||
change_state(a, 'hide',
|
||||
function() { get_organic(a, 1); });
|
||||
});
|
||||
thing.find(".del-button a.yes")
|
||||
.click(function() {
|
||||
var a = $(this).get(0);
|
||||
change_state(a, 'del', function() { get_organic(a, 1); });
|
||||
});
|
||||
thing.find(".report-button a.yes")
|
||||
.click(function() {
|
||||
var a = $(this).get(0);
|
||||
change_state(a, 'report',
|
||||
function() { get_organic(a, 1); });
|
||||
});
|
||||
|
||||
/*thing.find(".arrow.down")
|
||||
.one("click", function() {
|
||||
var a = $(this).get(0);
|
||||
get_organic($(this).get(0), 1);
|
||||
}); */
|
||||
}
|
||||
};
|
||||
|
||||
function last_click(thing) {
|
||||
var cookie = "last_thing";
|
||||
if(thing) {
|
||||
var data = {href: window.location.href,
|
||||
what: $(thing).thing_id()};
|
||||
$.cookie_write({name: cookie, data: data});
|
||||
}
|
||||
else {
|
||||
var current = $.cookie_read(cookie).data;
|
||||
if(current && current.href == window.location.href) {
|
||||
$.cookie_write({name: cookie, data: ""});
|
||||
return current.what;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function login(elem) {
|
||||
if(cnameframe)
|
||||
return true;
|
||||
return post_form(this, "login");
|
||||
};
|
||||
|
||||
function register(elem) {
|
||||
if(cnameframe)
|
||||
return true;
|
||||
return post_form(this, "register");
|
||||
};
|
||||
|
||||
/* The ready method */
|
||||
$(function() {
|
||||
/* set function to be called on thing creation/replacement,
|
||||
* and call it on all things currently rendered in the
|
||||
* page. */
|
||||
$("body").set_thing_init(updateEventHandlers);
|
||||
|
||||
/* Set up gray inputs and textareas to clear on focus */
|
||||
$("textarea.gray, input.gray")
|
||||
.focus( function() {
|
||||
$(this).attr("rows", 7)
|
||||
.filter(".gray").removeClass("gray").attr("value", "")
|
||||
});
|
||||
/* set cookies to be from this user if there is one */
|
||||
if(reddit.logged)
|
||||
$.cookie_name_prefix(reddit.logged);
|
||||
/* set up the cookie domain */
|
||||
$.default_cookie_domain(reddit.cur_domain.split(':')[0]);
|
||||
|
||||
/* Count the rendering of this reddit */
|
||||
if(reddit.cur_site)
|
||||
update_reddit_count(reddit.cur_site);
|
||||
|
||||
var last = last_click();
|
||||
if(last)
|
||||
$.things(last).addClass("last-clicked");
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,297 +0,0 @@
|
||||
/*
|
||||
json.js
|
||||
2007-03-20
|
||||
|
||||
Public Domain
|
||||
|
||||
This file adds these methods to JavaScript:
|
||||
|
||||
array.toJSONString()
|
||||
boolean.toJSONString()
|
||||
date.toJSONString()
|
||||
number.toJSONString()
|
||||
object.toJSONString()
|
||||
string.toJSONString()
|
||||
These methods produce a JSON text from a JavaScript value.
|
||||
It must not contain any cyclical references. Illegal values
|
||||
will be excluded.
|
||||
|
||||
The default conversion for dates is to an ISO string. You can
|
||||
add a toJSONString method to any date object to get a different
|
||||
representation.
|
||||
|
||||
string.parseJSON(filter)
|
||||
This method parses a JSON text to produce an object or
|
||||
array. It can throw a SyntaxError exception.
|
||||
|
||||
The optional filter parameter is a function which can filter and
|
||||
transform the results. It receives each of the keys and values, and
|
||||
its return value is used instead of the original value. If it
|
||||
returns what it received, then structure is not modified. If it
|
||||
returns undefined then the member is deleted.
|
||||
|
||||
Example:
|
||||
|
||||
// Parse the text. If a key contains the string 'date' then
|
||||
// convert the value to a date.
|
||||
|
||||
myData = text.parseJSON(function (key, value) {
|
||||
return key.indexOf('date') >= 0 ? new Date(value) : value;
|
||||
});
|
||||
|
||||
It is expected that these methods will formally become part of the
|
||||
JavaScript Programming Language in the Fourth Edition of the
|
||||
ECMAScript standard in 2008.
|
||||
|
||||
This file will break programs with improper for..in loops. See
|
||||
http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
|
||||
|
||||
This is a reference implementation. You are free to copy, modify, or
|
||||
redistribute.
|
||||
|
||||
Use your own copy. It is extremely unwise to load untrusted third party
|
||||
code into your pages.
|
||||
*/
|
||||
|
||||
// Augment the basic prototypes if they have not already been augmented.
|
||||
|
||||
if (!Object.prototype.toJSONString) {
|
||||
|
||||
Array.prototype.toJSONString = function () {
|
||||
var a = ['['], // The array holding the text fragments.
|
||||
b, // A boolean indicating that a comma is required.
|
||||
i, // Loop counter.
|
||||
l = this.length,
|
||||
v; // The value to be stringified.
|
||||
|
||||
function p(s) {
|
||||
|
||||
// p accumulates text fragments in an array. It inserts a comma before all
|
||||
// except the first fragment.
|
||||
|
||||
if (b) {
|
||||
a.push(',');
|
||||
}
|
||||
a.push(s);
|
||||
b = true;
|
||||
}
|
||||
|
||||
// For each value in this array...
|
||||
|
||||
for (i = 0; i < l; i += 1) {
|
||||
v = this[i];
|
||||
switch (typeof v) {
|
||||
|
||||
// Serialize a JavaScript object value. Ignore objects thats lack the
|
||||
// toJSONString method. Due to a specification error in ECMAScript,
|
||||
// typeof null is 'object', so watch out for that case.
|
||||
|
||||
case 'object':
|
||||
if (v) {
|
||||
if (typeof v.toJSONString === 'function') {
|
||||
p(v.toJSONString());
|
||||
}
|
||||
} else {
|
||||
p("null");
|
||||
}
|
||||
break;
|
||||
|
||||
// Otherwise, serialize the value.
|
||||
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
p(v.toJSONString());
|
||||
|
||||
// Values without a JSON representation are ignored.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the fragments together and return.
|
||||
|
||||
a.push(']');
|
||||
return a.join('');
|
||||
};
|
||||
|
||||
|
||||
Boolean.prototype.toJSONString = function () {
|
||||
return String(this);
|
||||
};
|
||||
|
||||
|
||||
Date.prototype.toJSONString = function () {
|
||||
|
||||
// Ultimately, this method will be equivalent to the date.toISOString method.
|
||||
|
||||
function f(n) {
|
||||
|
||||
// Format integers to have at least two digits.
|
||||
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
return '"' + this.getFullYear() + '-' +
|
||||
f(this.getMonth() + 1) + '-' +
|
||||
f(this.getDate()) + 'T' +
|
||||
f(this.getHours()) + ':' +
|
||||
f(this.getMinutes()) + ':' +
|
||||
f(this.getSeconds()) + '"';
|
||||
};
|
||||
|
||||
|
||||
Number.prototype.toJSONString = function () {
|
||||
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
||||
return isFinite(this) ? String(this) : "null";
|
||||
};
|
||||
|
||||
|
||||
Object.prototype.toJSONString = function () {
|
||||
var a = ['{'], // The array holding the text fragments.
|
||||
b, // A boolean indicating that a comma is required.
|
||||
k, // The current key.
|
||||
v; // The current value.
|
||||
|
||||
function p(s) {
|
||||
|
||||
// p accumulates text fragment pairs in an array. It inserts a comma before all
|
||||
// except the first fragment pair.
|
||||
|
||||
if (b) {
|
||||
a.push(',');
|
||||
}
|
||||
a.push(k.toJSONString(), ':', s);
|
||||
b = true;
|
||||
}
|
||||
|
||||
// Iterate through all of the keys in the object, ignoring the proto chain.
|
||||
|
||||
for (k in this) {
|
||||
if (this.hasOwnProperty(k)) {
|
||||
v = this[k];
|
||||
switch (typeof v) {
|
||||
|
||||
// Serialize a JavaScript object value. Ignore objects that lack the
|
||||
// toJSONString method. Due to a specification error in ECMAScript,
|
||||
// typeof null is 'object', so watch out for that case.
|
||||
|
||||
case 'object':
|
||||
if (v) {
|
||||
if (typeof v.toJSONString === 'function') {
|
||||
p(v.toJSONString());
|
||||
}
|
||||
} else {
|
||||
p("null");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
p(v.toJSONString());
|
||||
|
||||
// Values without a JSON representation are ignored.
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the fragments together and return.
|
||||
|
||||
a.push('}');
|
||||
return a.join('');
|
||||
};
|
||||
|
||||
|
||||
(function (s) {
|
||||
|
||||
// Augment String.prototype. We do this in an immediate anonymous function to
|
||||
// avoid defining global variables.
|
||||
|
||||
// m is a table of character substitutions.
|
||||
|
||||
var m = {
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
|
||||
|
||||
s.parseJSON = function (filter) {
|
||||
|
||||
// Parsing happens in three stages. In the first stage, we run the text against
|
||||
// a regular expression which looks for non-JSON characters. We are especially
|
||||
// concerned with '()' and 'new' because they can cause invocation, and '='
|
||||
// because it can cause mutation. But just to be safe, we will reject all
|
||||
// unexpected characters.
|
||||
|
||||
try {
|
||||
if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
|
||||
test(this)) {
|
||||
|
||||
// In the second stage we use the eval function to compile the text into a
|
||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
// in parens to eliminate the ambiguity.
|
||||
|
||||
var j = eval('(' + this + ')');
|
||||
|
||||
// In the optional third stage, we recursively walk the new structure, passing
|
||||
// each name/value pair to a filter function for possible transformation.
|
||||
|
||||
if (typeof filter === 'function') {
|
||||
|
||||
function walk(k, v) {
|
||||
if (v && typeof v === 'object') {
|
||||
for (var i in v) {
|
||||
if (v.hasOwnProperty(i)) {
|
||||
v[i] = walk(i, v[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filter(k, v);
|
||||
}
|
||||
|
||||
j = walk('', j);
|
||||
}
|
||||
return j;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
// Fall through if the regexp test fails.
|
||||
|
||||
}
|
||||
throw new SyntaxError("parseJSON");
|
||||
};
|
||||
|
||||
|
||||
s.toJSONString = function () {
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can simply slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe
|
||||
// sequences.
|
||||
|
||||
if (/["\\\x00-\x1f]/.test(this)) {
|
||||
return '"' + this.replace(/([\x00-\x1f\\"])/g, function(a, b) {
|
||||
var c = m[b];
|
||||
if (c) {
|
||||
return c;
|
||||
}
|
||||
c = b.charCodeAt();
|
||||
return '\\u00' +
|
||||
Math.floor(c / 16).toString(16) +
|
||||
(c % 16).toString(16);
|
||||
}) + '"';
|
||||
}
|
||||
return '"' + this + '"';
|
||||
};
|
||||
})(String.prototype);
|
||||
};
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html> <head>
|
||||
<title></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1></h1>
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<address></address>
|
||||
<!-- hhmts start --> Last modified: Sun Mar 9 20:39:50 PDT 2008 <!-- hhmts end -->
|
||||
</body> </html>
|
||||
1
r2/r2/public/static/reddit-embed.css
Normal file
1
r2/r2/public/static/reddit-embed.css
Normal file
@@ -0,0 +1 @@
|
||||
.rembeddit { border: 2px dashed gray; }
|
||||
BIN
r2/r2/public/static/submit-hope.png
Normal file
BIN
r2/r2/public/static/submit-hope.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -1,8 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head><title>sureroute test obect</title></head>
|
||||
<body>
|
||||
<p><strong>sureroute test object</strong></p>
|
||||
<p>SureRoute for Performance chooses the fastest path to the origin to ensure that your site is continuously accessible and that uncacheable content is delivered to end users with optimal performance</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -21,9 +21,9 @@
|
||||
################################################################################
|
||||
|
||||
<%
|
||||
from supervise_watcher import Service_Page
|
||||
from supervise_watcher import AppServiceMonitor
|
||||
%>
|
||||
|
||||
%if c.user_is_admin:
|
||||
${Service_Page().render()}
|
||||
${AppServiceMonitor().render()}
|
||||
%endif
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
################################################################################
|
||||
|
||||
<%namespace file="translation.html" import="statusbar"/>
|
||||
<%namespace file="printable.html" import="yes_no_button, state_button" />
|
||||
<%namespace file="printable.html" import="ynbutton, state_button" />
|
||||
<%
|
||||
from r2.lib.translation import Translator
|
||||
%>
|
||||
@@ -59,11 +59,13 @@
|
||||
<span class="little">
|
||||
%if Translator.in_use(t):
|
||||
%if Translator.is_enabled(t):
|
||||
${state_button("disable_lang", t, _("disable"), \
|
||||
"return change_state(this, 'disable_lang');", _("disabled"))}
|
||||
${state_button("disable_lang", _("disable"), \
|
||||
"return change_state(this, 'disable_lang');", _("disabled"),
|
||||
hidden_data = dict(id=t))}
|
||||
%else:
|
||||
${state_button("enable_lang", t, _("enable"), \
|
||||
"return change_state(this, 'enable_lang');", _("enabled"))}
|
||||
${state_button("enable_lang", _("enable"), \
|
||||
"return change_state(this, 'enable_lang');", _("enabled"),
|
||||
hidden_data = dict(id=t))}
|
||||
%endif
|
||||
%if t in g.languages:
|
||||
<span class="orangered">[in use]</span>
|
||||
@@ -86,14 +88,13 @@
|
||||
<span class="little oldbylink"><b>authors: </b>
|
||||
%if authors:
|
||||
%for a in authors:
|
||||
${yes_no_button(a, "%s_%s" % (t,a), a, \
|
||||
"return deletetoggle(this, 'deltranslator');", \
|
||||
"removed",)}
|
||||
${ynbutton(a, "removed", "deltranslator",
|
||||
hidden_data = dict(lang=t, user=a))}
|
||||
%endfor
|
||||
%endif
|
||||
<a class="bylink"
|
||||
href="javascript:void(0)"
|
||||
onclick="hide(this); show('add_authors_${t}'); return false;">
|
||||
onclick="$(this).hide(); $('#add_authors_${t}').show(); return false;">
|
||||
[add author]</a>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
182
r2/r2/templates/appservicemonitor.html
Normal file
182
r2/r2/templates/appservicemonitor.html
Normal file
@@ -0,0 +1,182 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
## with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is Reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%
|
||||
cpu_col = 75.
|
||||
mem_col = 40.
|
||||
hide_data = "style='display:none'" if len(thing.hostlogs) > 4 else ''
|
||||
%>
|
||||
|
||||
<div class="server-status">
|
||||
<h3>Rendered by PID ${g.reddit_pid} on ${g.reddit_host}</h3>
|
||||
%if any(h.database for h in thing.hostlogs):
|
||||
<table class="monitor-database">
|
||||
<tr class="title-region">
|
||||
<th>database</th>
|
||||
<th>connections</th>
|
||||
<th>
|
||||
<span style="color:green">1 min</span> /
|
||||
<span style="color:red">5 min</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
%for host in thing.hostlogs:
|
||||
%if host.database:
|
||||
<%
|
||||
host_id = host.host.replace('.', '-')
|
||||
load = host.load()
|
||||
load_level = min(max(int(load+0.5), 0),5)
|
||||
%>
|
||||
<tr class="load${load_level} title-region" id="${host_id}">
|
||||
<th>
|
||||
${host.host} load: ${load}
|
||||
</th>
|
||||
<th>
|
||||
%if host.database.vacuuming:
|
||||
<blink style="color:red">VACUUMING!</blink>
|
||||
%endif
|
||||
</th>
|
||||
<td>
|
||||
<span style="color:green">${host.database.connections()}</span>
|
||||
/
|
||||
<span style="color:red">${host.database.connections(300)}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ${hide_data} class="data load${load_level} machine-${host_id}">
|
||||
<th>by ip:</th>
|
||||
<td></td><td></td>
|
||||
</tr>
|
||||
%for ip, nconn in host.database.ip_conn.iteritems():
|
||||
<tr ${hide_data}
|
||||
class="data load${load_level} machine-${host_id} by-ip">
|
||||
<td>
|
||||
${ip}
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
${nconn()}
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
<tr ${hide_data} class="data load${load_level} machine-${host_id} by-ip">
|
||||
<th>by database:</th>
|
||||
<td></td><td></td>
|
||||
</tr>
|
||||
%for db, nconn in host.database.db_conn.iteritems():
|
||||
<tr ${hide_data} class="data load${load_level} machine-${host_id} by-db">
|
||||
<td>
|
||||
${db}
|
||||
</td>
|
||||
<td>
|
||||
%if db in host.database.vacuuming:
|
||||
<blink style="color:red">VACUUMING!</blink>
|
||||
%endif
|
||||
</td>
|
||||
<td>
|
||||
${nconn()}
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
%endif
|
||||
%endfor
|
||||
</table>
|
||||
%endif
|
||||
|
||||
|
||||
<table class="process-monitor">
|
||||
<tr>
|
||||
<th>process</th>
|
||||
<th>memory</th>
|
||||
<th>
|
||||
<span style="color:green">1 min</span> /
|
||||
<span style="color:red">5 min</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
%for host in thing.hostlogs:
|
||||
<%
|
||||
host_id = host.host.replace('.', '-')
|
||||
s = host.services
|
||||
load = host.load()
|
||||
load_level = min(max(int(load+0.5), 0),5)
|
||||
%>
|
||||
<tr class="load${load_level} title-region" id="${host_id}">
|
||||
<th>
|
||||
${host.host} load: ${load}
|
||||
</th>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
%for service in host:
|
||||
<%
|
||||
pegged = all([service.cpu(x) > 99 \
|
||||
for x in (0, 5, 60, 300)]) or \
|
||||
(service.mem() >= 20)
|
||||
pegged = "pegged" if pegged else ""
|
||||
|
||||
mem_wid = int(mem_col/25*min(25, int(service.mem())))
|
||||
cpu_60_wid = int(cpu_col/100*min(100,int(service.cpu(60))))
|
||||
cpu_300_wid = int(cpu_col/100*min(100,int(service.cpu(300))))
|
||||
%>
|
||||
|
||||
<tr ${hide_data} class="data load${load_level} ${pegged} machine-${host_id}">
|
||||
<td>
|
||||
%if g.reddit_host == host.host and g.reddit_pid == service.pid:
|
||||
<b style="color:orangered; font-size:larger">»</b>
|
||||
%else:
|
||||
|
||||
%endif
|
||||
${service.name}
|
||||
</td>
|
||||
<td>
|
||||
<div class="membar" style="width:${mem_wid}px;"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="cpu50" style="width:${cpu_60_wid}px;"></div>
|
||||
<div class="cpu300" style="width:${cpu_300_wid}px;"></div>
|
||||
</td>
|
||||
<td>
|
||||
<%
|
||||
age = service.age
|
||||
if isinstance(age, str):
|
||||
age = 0
|
||||
if age > 60:
|
||||
age = "%d hr" % int(age/60)
|
||||
else:
|
||||
age = "%d min" % age
|
||||
%>
|
||||
${age}
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
%endfor
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(".server-status tr").click(function() {
|
||||
if(!$(this).hasClass("data")) {
|
||||
$(this).siblings(".machine-"+ $(this).attr("id")).toggle();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
17
r2/r2/templates/authorizedembed.html
Normal file
17
r2/r2/templates/authorizedembed.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<%namespace file="utils.html" import="js_preamble"/>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
document.domain = "${thing.domain}";
|
||||
${js_preamble()}
|
||||
reddit.vl = parent.reddit.vl;
|
||||
parent.reddit = reddit;
|
||||
|
||||
/* embed shouldn't be able to JS-redirect the page. */
|
||||
parent.redirect = parent.refresh;
|
||||
this.frameElement.className = "rembeddit-frame";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -19,11 +19,12 @@
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.template_helpers import static, get_domain
|
||||
|
||||
from r2.lib.template_helpers import static
|
||||
from r2.models import Link, Comment, Subreddit
|
||||
%>
|
||||
<%namespace file="utils.html" import="js_preamble"/>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="${c.lang}"
|
||||
xml:lang="${c.lang}" ${c.lang_rtl and unsafe('dir="rtl"') or ''}>
|
||||
@@ -39,21 +40,7 @@
|
||||
##things that need are referenced by the reddit, the buttons, and
|
||||
##the frame should go here
|
||||
<script type="text/javascript">
|
||||
var a = new Image();
|
||||
a.src = "${static('aupmod.png')}";
|
||||
var b = new Image();
|
||||
b.src = "${static('adownmod.png')}";
|
||||
var vl = {};
|
||||
var sr = {};
|
||||
|
||||
var logged = ${c.user_is_loggedin and ("'%s'" % c.user.name) or "false"};
|
||||
var post_site = "${c.site.name if not c.default_sr else ''}";
|
||||
var cnameframe = ${"true" if (c.cname and not c.authorized_cname) else "false"};
|
||||
var modhash = ${"'%s'" % c.modhash or "false"};
|
||||
var cur_domain = "${get_domain(cname = True, subreddit = False) if c.frameless_cname else g.domain}";
|
||||
var ajax_domain = "${get_domain(cname = True, subreddit = False) if c.authorized_cname else g.domain}";
|
||||
|
||||
|
||||
${js_preamble()}
|
||||
</script>
|
||||
|
||||
${self.javascript()}
|
||||
|
||||
@@ -28,15 +28,19 @@
|
||||
%>
|
||||
|
||||
<div class="rembeddit"
|
||||
${optionalstyle("font-family:verdana,arial,helvetica,sans-serif;" + style_line(bgcolor="FFFFFF", bordercolor="336699"))}>
|
||||
|
||||
${optionalstyle("font-family:verdana,arial,helvetica,sans-serif;" +
|
||||
style_line(bgcolor="FFFFFF", bordercolor="336699"))}>
|
||||
%if not request.GET.get("nobranding"):
|
||||
<div class="reddit-header"
|
||||
${optionalstyle("padding: 5px; padding-bottom:1px;" + ("background-color:#CEE3F8" if not c.bgcolor else ""))}>
|
||||
${optionalstyle("padding: 5px; padding-bottom:1px;" +
|
||||
("background-color:#CEE3F8" if not c.bgcolor else ""))}>
|
||||
<h4 class="reddit-title"
|
||||
${optionalstyle("margin:0;" + ("padding-bottom:3px" if not c.cname else ""))}
|
||||
${optionalstyle("margin:0;" +
|
||||
("padding-bottom:3px" if not c.cname else ""))}
|
||||
>
|
||||
<a href="http://${get_domain()}/" ${optionalstyle("margin:5px;")}>
|
||||
<img src="http://${get_domain(subreddit=False)}/static/spreddit1.gif" alt=""
|
||||
<img src="http://${get_domain(subreddit=False)}/static/spreddit1.gif"
|
||||
alt=""
|
||||
${optionalstyle("border:none")} />
|
||||
</a>
|
||||
<%
|
||||
@@ -44,7 +48,8 @@
|
||||
name = c.site.name
|
||||
if not isinstance(c.site, FakeSubreddit):
|
||||
name += ".%s" % g.domain
|
||||
link = '<a %s href="http://%s/">%s</a></h3>' % (style, get_domain(), name)
|
||||
link = ('<a %s href="http://%s/">%s</a></h3>' %
|
||||
(style, get_domain(), name))
|
||||
label = _("links from %(site)s").replace(" ", " ");
|
||||
%>
|
||||
${unsafe(label % dict(site = link))}
|
||||
@@ -62,6 +67,7 @@
|
||||
</p>
|
||||
%endif
|
||||
</div>
|
||||
%endif
|
||||
<div class="rembeddit-content" ${optionalstyle("padding:5px;")}>
|
||||
${next.body()}
|
||||
</div>
|
||||
|
||||
@@ -26,13 +26,15 @@
|
||||
<%inherit file="reddit.html"/>
|
||||
<%namespace module="r2.lib.template_helpers" import="generateurl"/>
|
||||
<%namespace file="buttontypes.html" import="*" />
|
||||
<%namespace file="printable.html" import="thing_css_class" />
|
||||
|
||||
|
||||
|
||||
<%def name="javascript()">
|
||||
<script src="${static('vote.js')}" type="text/javascript"></script>
|
||||
<script src="${static('jquery.js')}" type="text/javascript"></script>
|
||||
<script src="${static('jquery.reddit.js')}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
function showcover() { show("cover"); }
|
||||
function showcover() { $("#cover").show(); }
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
@@ -43,7 +45,7 @@
|
||||
<%def name="bodyHTML()">
|
||||
<body style="background-color: ${'#%s' % (c.bgcolor or 'FFFFFF')}"
|
||||
class="button-body">
|
||||
<div class="button">
|
||||
<div class="button ${thing_css_class(thing.link) if thing.link else ''}">
|
||||
%if not c.user_is_loggedin:
|
||||
<div id="cover" style="display: none">
|
||||
<div class="cover"></div>
|
||||
@@ -52,18 +54,18 @@
|
||||
target="_parent">please login</a>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
%if thing.button == 1:
|
||||
${button1(thing)}
|
||||
%elif thing.button == 2:
|
||||
${button2(thing)}
|
||||
%elif thing.button == 3:
|
||||
${button3(thing)}
|
||||
%elif thing.button == 4:
|
||||
${button4(thing)}
|
||||
%elif thing.button == 5:
|
||||
${button5(thing)}
|
||||
%endif
|
||||
%endif
|
||||
%if thing.button == 1:
|
||||
${button1(thing)}
|
||||
%elif thing.button == 2:
|
||||
${button2(thing)}
|
||||
%elif thing.button == 3:
|
||||
${button3(thing)}
|
||||
%elif thing.button == 4:
|
||||
${button4(thing)}
|
||||
%elif thing.button == 5:
|
||||
${button5(thing)}
|
||||
%endif
|
||||
</div>
|
||||
</body>
|
||||
</%def>
|
||||
|
||||
@@ -35,18 +35,18 @@
|
||||
<p>${_('use one of these buttons to quickly add reddit links to your site, or \
|
||||
see below for more options.')}</p>
|
||||
<ul class="buttons">
|
||||
<li>${badgedemo(1)}</li>
|
||||
<li>${badgedemo(7)}</li>
|
||||
<li>${point_button_demo(0)}</li>
|
||||
<li>${point_button_demo(1)}</li>
|
||||
<li>${interactive_button_demo(1)}</li>
|
||||
${demo(capture(drawbadge, 1))}
|
||||
${demo(capture(drawbadge, 7))}
|
||||
${demo(capture(draw_point_button, 0))}
|
||||
${demo(capture(draw_point_button, 1))}
|
||||
${demo(capture(draw_interactive, 1))}
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>${_("buttons with points")}</h2>
|
||||
<ul class="buttons">
|
||||
%for x in xrange(0,6):
|
||||
<li>${point_button_demo(x)}</li>
|
||||
${demo(capture(draw_point_button, x))}
|
||||
%endfor
|
||||
</ul>
|
||||
<h2>${_("customizing the look of your buttons")}</h2>
|
||||
@@ -61,21 +61,25 @@
|
||||
</ul>
|
||||
<p>${_('Example:')}</p>
|
||||
<code>
|
||||
${capture(point_option_example)}
|
||||
${point_option_example()}
|
||||
</code>
|
||||
|
||||
|
||||
<h2>${_('simple interactive button')}</h2>
|
||||
<p>${_('put this code on your page:')}</p>
|
||||
<code>${draw_interactive(False)}</code>
|
||||
<code>
|
||||
${capture(draw_interactive,False)}
|
||||
</code>
|
||||
<p>${_("and you'll get something like this:")}</p>
|
||||
<span style="margin-left: 10px;">${unsafe(draw_interactive(False))}</span>
|
||||
<span style="margin-left: 10px;">
|
||||
${draw_interactive(False)}
|
||||
</span>
|
||||
|
||||
<h2>${_("more interactive buttons")}</h2>
|
||||
<ul class="buttons">
|
||||
%for x in xrange(1,4):
|
||||
<li>${interactive_button_demo(x)}</li>
|
||||
%endfor
|
||||
${demo(capture(draw_interactive, x))}
|
||||
%endfor
|
||||
</ul>
|
||||
|
||||
<h2>${_('interactive button advanced settings')}</h2>
|
||||
@@ -108,76 +112,79 @@
|
||||
<span style="margin-left: 10px;">${draw_interactive_example()}</span>
|
||||
<p>${_('use this code:')}</p>
|
||||
<code>
|
||||
${capture(draw_interactive_example)}
|
||||
<%
|
||||
ex = websafe(capture(draw_interactive_example))
|
||||
ex = ex.replace("\n", "<br/>").replace(" ", " ")
|
||||
%>
|
||||
${unsafe(ex)}
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<h2>${_("more badges and buttons")}</h2>
|
||||
<ul class="buttons">
|
||||
%for x in xrange(1,15):
|
||||
<li>${badgedemo(x)}</li>
|
||||
%endfor
|
||||
%for x in xrange(1,15):
|
||||
${demo(capture(drawbadge, x))}
|
||||
%endfor
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$(".view-code").click(function(evt) {
|
||||
$(this).parent().addClass("show-demo");
|
||||
});
|
||||
$(".hide-code").click(function(evt) {
|
||||
$(this).parent().removeClass("show-demo");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<%def name="drawbadge(image)">
|
||||
<a href="http://${domain}/submit"
|
||||
onclick="window.location = 'http://${domain}/submit?url=' + encodeURIComponent(window.location); return false">
|
||||
<img src="http://${get_domain(subreddit=False)}/static/spreddit${image}.gif" alt="submit to reddit" border="0" />
|
||||
</a>
|
||||
<a href="http://${domain}/submit"
|
||||
onclick="window.location = 'http://${domain}/submit?url=' + encodeURIComponent(window.location); return false">
|
||||
<img src="http://${get_domain(subreddit=False)}/static/spreddit${image}.gif"
|
||||
alt="submit to reddit" border="0" />
|
||||
</a>
|
||||
</%def>
|
||||
|
||||
<%def name="badgedemo(image)">
|
||||
<a class="view-code" href=""
|
||||
onclick='show_hide_child(this, "code", "code"); return false'>view code</a>
|
||||
${unsafe(drawbadge(image))}<br />
|
||||
<code style="display: none">
|
||||
${capture(drawbadge, image)}
|
||||
<%def name="demo(content)">
|
||||
<li class="button-demo">
|
||||
<a class="view-code" href="javascript:void(0)">${_("view code")}</a>
|
||||
<a class="hide-code" href="javascript:void(0)">${_("hide code")}</a>
|
||||
${unsafe(content)}
|
||||
<br />
|
||||
<code>
|
||||
${content}
|
||||
</code>
|
||||
</li>
|
||||
</%def>
|
||||
|
||||
<%def name="draw_point_button(image)" buffered="True">
|
||||
<script type="text/javascript" src="http://${domain}/buttonlite.js?i=${image}"></script>
|
||||
<%def name="draw_point_button(image)">
|
||||
<script type="text/javascript"
|
||||
src="http://${domain}/buttonlite.js?i=${image}"></script>
|
||||
</%def>
|
||||
|
||||
<%def name="point_option_example()">
|
||||
<script type="text/javascript" src="http://${domain}/buttonlite.js?i=1&styled=off&url=foo.com&newwindow=1"></script>
|
||||
<%def name="point_option_example()" buffered="True">
|
||||
<script type="text/javascript"
|
||||
src="http://${domain}/buttonlite.js?i=1&styled=off&url=foo.com&newwindow=1"></script>
|
||||
</%def>
|
||||
|
||||
<%def name="point_button_demo(image)">
|
||||
<a class="view-code" href=""
|
||||
onclick='show_hide_child(this, "code", "code"); return false'>view code</a>
|
||||
${unsafe(draw_point_button(image))}<br />
|
||||
<code style="display: none">
|
||||
${draw_point_button(image)}
|
||||
</code>
|
||||
</%def>
|
||||
|
||||
<%def name="draw_interactive(type)" buffered="True">
|
||||
<%def name="draw_interactive(type)">
|
||||
%if type:
|
||||
<script type="text/javascript" src="http://${domain}/button.js?t=${type}"></script>
|
||||
<script type="text/javascript"
|
||||
src="http://${domain}/button.js?t=${type}"></script>
|
||||
%else:
|
||||
<script type="text/javascript" src="http://${domain}/button.js"></script>
|
||||
<script type="text/javascript" src="http://${domain}/button.js"></script>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="interactive_button_demo(number)">
|
||||
<a class="view-code" href=""
|
||||
onclick='show_hide_child(this, "code", "code"); return false'>view code</a>
|
||||
${unsafe(draw_interactive(number))}<br />
|
||||
<code style="display: none">
|
||||
${draw_interactive(number)}
|
||||
</code>
|
||||
<%def name="drawoption(option, val)" buffered="True">
|
||||
<script type="text/javascript">reddit_${option}='${val}'</script>
|
||||
</%def>
|
||||
|
||||
<%def name="drawoption(option, val)">
|
||||
${"<script type='text/javascript'>reddit_%s='%s'</script>" % (option, val)}
|
||||
</%def>
|
||||
|
||||
<%def name="draw_interactive_example()">
|
||||
<script type="text/javascript">
|
||||
<%def name="draw_interactive_example()"><script type="text/javascript">
|
||||
reddit_url = "http://${domain}/buttons";
|
||||
reddit_title = "Buttons!";
|
||||
reddit_bgcolor = "FF3";
|
||||
|
||||
@@ -34,11 +34,11 @@
|
||||
</%def>
|
||||
|
||||
<%def name="class_def(class_number, width=None)">
|
||||
%if style_line(width):
|
||||
<div class="blog blog${class_number}" style="${style_line(width)}">
|
||||
%else:
|
||||
<div class="blog blog${class_number}">
|
||||
%endif
|
||||
<div class="blog blog${class_number}"
|
||||
%if style_line(width):
|
||||
style="${style_line(width)}"
|
||||
%endif
|
||||
>
|
||||
</%def>
|
||||
|
||||
<%def name="button1(thing)">
|
||||
@@ -68,7 +68,7 @@ ${class_def(1, width=choose_width(thing.link, thing.width))}
|
||||
%if thing.link:
|
||||
%if thing.vote:
|
||||
${arrow(thing.link, 1, thing.likes)}
|
||||
${score(thing.link, thing.likes, inline=False, label = False)}
|
||||
${score(thing.link, thing.likes, inline=False)}
|
||||
${arrow(thing.link, 0, thing.likes == False)}
|
||||
%else:
|
||||
<br />
|
||||
@@ -96,7 +96,7 @@ ${class_def(3)}
|
||||
%if thing.link:
|
||||
%if thing.vote:
|
||||
${arrow(thing.link, 1, thing.likes)}
|
||||
${score(thing.link, thing.likes, inline=False, label = False)}
|
||||
${score(thing.link, thing.likes, inline=False)}
|
||||
${arrow(thing.link, 0, thing.likes == False)}
|
||||
%else:
|
||||
<br />
|
||||
@@ -121,7 +121,7 @@ ${class_def(3)}
|
||||
%if thing.link:
|
||||
%if thing.vote:
|
||||
${arrow(thing.link, 1, thing.likes)}
|
||||
${score(thing.link, thing.likes, inline=False, label = False)}
|
||||
${score(thing.link, thing.likes, inline=False)}
|
||||
${arrow(thing.link, 0, thing.likes == False)}
|
||||
%else:
|
||||
<br />
|
||||
@@ -166,7 +166,6 @@ ${class_def(3)}
|
||||
votes = [other_votes + 1, other_votes, other_votes + 1]
|
||||
up_pct = [int( 100 * ups[i] / votes[i]) if votes[i] else "--" for i in range(len(votes))]
|
||||
down_pct = [int( 100 * downs[i] / votes[i]) if votes[i] else "--" for i in range(len(votes))]
|
||||
|
||||
def controversy_text(_up_pct):
|
||||
if isinstance(_up_pct, str):
|
||||
return ""
|
||||
@@ -184,11 +183,12 @@ ${class_def(3)}
|
||||
%>
|
||||
<script type="text/javascript">
|
||||
function set_score(id, dir) {
|
||||
id = $(id).filter(":first").thing_id();
|
||||
for(var i = 0; i < 3; i++) {
|
||||
var fn = (i == dir + 1) ? show: hide;
|
||||
fn("down" + i + "_" + id);
|
||||
fn("up" + i + "_" + id);
|
||||
fn("controversy" + i + "_" + id);
|
||||
var fn = (i == dir + 1) ? 'show': 'hide';
|
||||
$("#down" + i + "_" + id)[fn]();
|
||||
$("#up" + i + "_" + id)[fn]();
|
||||
$("#controversy" + i + "_" + id)[fn]();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -217,7 +217,7 @@ ${class_def(3)}
|
||||
<a class="arrow up${'mod' if dir==1 else ''}"
|
||||
id="up_${fullname}"
|
||||
%if c.user_is_loggedin and thing.link:
|
||||
href="#" onclick="mod('${fullname}', 1); return false; "
|
||||
href="#" onclick="$(this).vote('', set_score); return false; "
|
||||
%else:
|
||||
href="${submitlink}" target="_new"
|
||||
%endif
|
||||
@@ -230,7 +230,7 @@ ${class_def(3)}
|
||||
</a>
|
||||
<a class="arrow down${'mod' if dir==-1 else ''}" id="down_${fullname}"
|
||||
%if c.user_is_loggedin and thing.link:
|
||||
href="#" onclick="mod('${fullname}', 0); return false;"
|
||||
href="#" onclick="$(this).vote('', set_score); return false;"
|
||||
%else:
|
||||
href="${submitlink}" target="_new"
|
||||
%endif
|
||||
|
||||
@@ -34,8 +34,7 @@ ${captchagen(thing.iden, thing.error)}
|
||||
<td></td>
|
||||
<td>
|
||||
%endif
|
||||
<img id="capimage"
|
||||
class="capimage"
|
||||
<img class="capimage"
|
||||
alt="i wonder if these things even work"
|
||||
%if iden:
|
||||
src="/captcha/${iden}.png"
|
||||
@@ -58,11 +57,11 @@ ${captchagen(thing.iden, thing.error)}
|
||||
</td>
|
||||
<td>
|
||||
%endif
|
||||
<input id="capiden" name="iden" type="hidden" value="${iden}"/>
|
||||
<input id="captcha" name="captcha" type="text" size="${size}"
|
||||
class="cap-text" onfocus="clearTitle(this)"/>
|
||||
<input name="iden" type="hidden" value="${iden}"/>
|
||||
<input class="captcha cap-text"
|
||||
name="captcha" type="text" size="${size}" />
|
||||
<script type="text/javascript">
|
||||
setMessage($('captcha'), "${_('type the letters from the image above')}");
|
||||
emptyInput($('input.captcha'), "${_('type the letters from the image above')}");
|
||||
</script>
|
||||
%if tabular:
|
||||
</td>
|
||||
|
||||
@@ -38,19 +38,6 @@
|
||||
|
||||
</head>
|
||||
|
||||
<script type="text/javascript">
|
||||
function replace_self(url, child) {
|
||||
if(child != window) {
|
||||
window.location = url;
|
||||
}
|
||||
};
|
||||
window.onload = function() {
|
||||
try {
|
||||
document.cookie = "redditSession=true; domain=${c.site.domain}; path=/";
|
||||
parent.replace_self(window.location, window);
|
||||
} catch(e) {};
|
||||
}
|
||||
</script>
|
||||
%if thing.frame_target:
|
||||
<frameset>
|
||||
<frame src="${thing.frame_target}" name="reddit-embed" id="reddit-embed">
|
||||
|
||||
@@ -55,17 +55,26 @@ ${parent.collapsed()}
|
||||
</%def>
|
||||
|
||||
<%def name="tagline(collapse=False)">
|
||||
<% fullname = thing._fullname %>
|
||||
%if not thing.deleted:
|
||||
<%
|
||||
if c.user_is_admin:
|
||||
show = True
|
||||
else:
|
||||
show = not thing.deleted
|
||||
%>
|
||||
%if show:
|
||||
%if thing.deleted:
|
||||
<em>${_("deleted comment from")}</em> 
|
||||
%endif
|
||||
${unsafe(self.author(friend=thing.friend, gray = collapse))} 
|
||||
%else:
|
||||
<em>${_("comment deleted")}</em> 
|
||||
<em>${_("[deleted]")}</em> 
|
||||
%endif
|
||||
%if collapse and thing.collapsed and not thing.deleted:
|
||||
|
||||
%if collapse and thing.collapsed and show:
|
||||
${_("comment score below threshold")}
|
||||
%else:
|
||||
%if not thing.deleted:
|
||||
${unsafe(self.score(thing, likes = thing.likes, _id = not collapse))} 
|
||||
%if show:
|
||||
${unsafe(self.score(thing, likes = thing.likes))} 
|
||||
%endif
|
||||
${thing.timesince} ${_("ago")}
|
||||
%if thing.editted:
|
||||
@@ -74,9 +83,9 @@ ${parent.collapsed()}
|
||||
%endif
|
||||
<a href="#" class="expand"
|
||||
%if collapse:
|
||||
onclick="return showcomment('${fullname}')">
|
||||
onclick="return showcomment(this)">
|
||||
%else:
|
||||
onclick="return hidecomment('${fullname}')">
|
||||
onclick="return hidecomment(this)">
|
||||
%endif
|
||||
[${_("+") if collapse else _("-")}]
|
||||
%if collapse:
|
||||
@@ -92,8 +101,12 @@ ${parent.Child(not thing.collapsed)}
|
||||
</%def>
|
||||
|
||||
<%def name="commentBody()">
|
||||
%if not thing.deleted:
|
||||
%if c.user_is_admin or not thing.deleted:
|
||||
${unsafe(safemarkdown(thing.body))}
|
||||
%else:
|
||||
<div class="gray md">
|
||||
${_("[deleted]")}
|
||||
</div>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
@@ -109,12 +122,18 @@ ${parent.midcol()}
|
||||
|
||||
|
||||
<%def name="buttons()">
|
||||
%if not thing.deleted:
|
||||
<% fullname = thing._fullname %>
|
||||
<li class="first">
|
||||
${parent.comment_button("permalink", fullname, _("permalink"), 0,
|
||||
thing.permalink)}
|
||||
</li>
|
||||
<li class="first">
|
||||
${parent.comment_button("permalink", _("permalink"), 0,
|
||||
thing.permalink)}
|
||||
</li>
|
||||
|
||||
%if thing.deleted:
|
||||
%if thing.parent_permalink and not c.profilepage:
|
||||
<li>
|
||||
${bylink_button("parent", _("parent"), thing.parent_permalink)}
|
||||
</li>
|
||||
%endif
|
||||
%else:
|
||||
%if not c.profilepage:
|
||||
%if thing.parent_permalink:
|
||||
<li>
|
||||
@@ -123,7 +142,7 @@ ${parent.midcol()}
|
||||
%endif
|
||||
%if c.user_is_loggedin and thing.author.name == c.user.name:
|
||||
<li>
|
||||
${parent.simple_button("edit", fullname, _("edit"), "editcomment")}
|
||||
${parent.simple_button("edit", _("edit"), "editcomment")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
@@ -131,7 +150,9 @@ ${parent.midcol()}
|
||||
${parent.buttons()}
|
||||
%if not c.profilepage and thing.can_reply:
|
||||
<li>
|
||||
${parent.simple_button("reply", fullname, _("reply {verb}"), "reply")}
|
||||
${parent.toggle_button("reply-button", _("reply {verb}"),
|
||||
_("cancel"), "reply", "cancelReply",
|
||||
login_required = True)}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
@@ -140,6 +161,6 @@ ${parent.midcol()}
|
||||
|
||||
|
||||
<%def name="bylink_button(name, title, link)">
|
||||
<a id="${name}_${thing._fullname}" href="${link}" class="bylink">${title}</a>
|
||||
<a href="${link}" class="bylink">${title}</a>
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -35,6 +35,15 @@
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
|
||||
%if thing.deleted:
|
||||
|
||||
<small>
|
||||
<b>${_("[deleted]")}</b> ${thing.timesince} ${_("ago")}
|
||||
</small>
|
||||
|
||||
%else:
|
||||
|
||||
<small>
|
||||
<a href="http://${get_domain()}/user/${thing.author.name}">
|
||||
<b>${thing.author.name}</b></a>
|
||||
@@ -42,4 +51,7 @@
|
||||
${thing.timesince}
|
||||
</small><br/>
|
||||
${unsafe(safemarkdown(thing.body))}
|
||||
|
||||
%endif
|
||||
|
||||
</%def>
|
||||
|
||||
@@ -34,11 +34,22 @@
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
%if thing.deleted:
|
||||
|
||||
<p>
|
||||
${_("[deleted]")} ${thing.timesince} ${_("ago")}
|
||||
</p>
|
||||
|
||||
%else:
|
||||
|
||||
<p>
|
||||
<a href="/user/${thing.author.name}">
|
||||
<b>${thing.author.name}</b></a>
|
||||
|<span class="score">${thing.score} ${ungettext("point", "points", thing.score)} </span>
|
||||
written ${thing.timesince} ago
|
||||
${_("written")} ${thing.timesince} ${_("ago")}
|
||||
</p>
|
||||
${unsafe(safemarkdown(thing.body))}
|
||||
|
||||
%endif
|
||||
|
||||
</%def>
|
||||
|
||||
@@ -24,11 +24,21 @@
|
||||
from r2.lib.filters import safemarkdown
|
||||
%>
|
||||
|
||||
|
||||
<%
|
||||
if thing.deleted:
|
||||
author = _('[deleted]')
|
||||
body = _('[deleted]')
|
||||
else:
|
||||
author = thing.author.name
|
||||
body = thing.body
|
||||
%>
|
||||
|
||||
<item>
|
||||
<title>${thing.author.name} ${_("on")} ${thing.link.title}</title>
|
||||
<title>${author} ${_("on")} ${thing.link.title}</title>
|
||||
<link>${thing.permalink}</link>
|
||||
<dc:date>${thing._date.isoformat()}-0700</dc:date>
|
||||
<description>${thing.body|h}</description>
|
||||
<description>${body|h}</description>
|
||||
</item>
|
||||
|
||||
${hasattr(thing, "child") and thing.child.render() or ''}
|
||||
|
||||
@@ -45,23 +45,26 @@ ${self.tagline(True)}
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
<% fullname = thing._fullname %>
|
||||
<% collapse = thing.collapsed %>
|
||||
<div id="collapsed_${fullname}" class="collapsed"
|
||||
${(not collapse and "style='display:none'" or "")}>
|
||||
<div class="collapsed" ${(not collapse and "style='display:none'" or "")}>
|
||||
${self.collapsed()}
|
||||
</div>
|
||||
<div id="display_${fullname}"
|
||||
${(collapse and "style='display:none'" or "")}>
|
||||
<p id="tagline_${fullname}" class="tagline">
|
||||
<div class="noncollapsed" ${(collapse and "style='display:none'" or "")}>
|
||||
<p class="tagline">
|
||||
${self.tagline()}
|
||||
</p>
|
||||
<div id="body_${fullname}"
|
||||
class="commentbody ${(c.focal_comment and c.focal_comment == thing._id36) and 'border' or ''}">${self.commentBody()}
|
||||
</div>
|
||||
<div style="display: none;" \
|
||||
id="edit_body_${fullname}">${self.commentText()}\
|
||||
<%
|
||||
if c.user_is_admin and thing.deleted:
|
||||
colorclass = 'grayed'
|
||||
elif c.focal_comment and c.focal_comment == thing._id36:
|
||||
colorclass = 'border'
|
||||
else:
|
||||
colorclass = ''
|
||||
%>
|
||||
<div class="commentbody ${colorclass}">
|
||||
${self.commentBody()}
|
||||
</div>
|
||||
<div class="edit-body">${self.commentText()}</div>
|
||||
<ul class="flat-list buttons">
|
||||
${self.buttons()}
|
||||
</ul>
|
||||
|
||||
@@ -21,58 +21,51 @@
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
%if not thing.link_name:
|
||||
<div id="samplecomment_" style="display:none">
|
||||
%endif
|
||||
<form id="commentform_${thing.link_name}"
|
||||
onsubmit="return chkcomment(this);"
|
||||
action="/post/${thing.action}" method="post" class="commentreply">
|
||||
<form id="commentreply_${thing.link_name}"
|
||||
${'' if thing.link_name else "style='display:none'"}
|
||||
onsubmit="return chkcomment(this);" action="#"
|
||||
method="get" class="commentreply">
|
||||
%if thing.link_name:
|
||||
<input id="isroot_${thing.link_name}"
|
||||
type="hidden" name="isroot" value="1" />
|
||||
<input type="hidden" name="isroot" value="1" />
|
||||
%endif
|
||||
<input id="replace_${thing.link_name}"
|
||||
type="hidden" name="replace" value="" />
|
||||
${error_field("BAD_COMMENT_" + thing.link_name)}
|
||||
${error_field("COMMENT_TOO_LONG_" + thing.link_name)}
|
||||
${error_field("RATELIMIT_" + thing.link_name)}
|
||||
<input type="hidden" name="replace" value="" />
|
||||
<input type="hidden" name="parent" value="${thing.link_name}" />
|
||||
|
||||
${error_field("BAD_COMMENT")}
|
||||
${error_field("DELETED_COMMENT")}
|
||||
${error_field("COMMENT_TOO_LONG")}
|
||||
${error_field("RATELIMIT")}
|
||||
## I know we don't need this div. You know we don't need this div. IE
|
||||
## begs to differ.
|
||||
<div>
|
||||
<textarea id="comment_reply_${thing.link_name}" name="comment"
|
||||
rows="2" onfocus="clearTitle(this);" >
|
||||
<textarea name="comment" rows="7">
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button id="comment_submit_${thing.link_name}"
|
||||
type="submit" class="btn">${_("comment {verb}")}
|
||||
<button type="submit" class="btn"
|
||||
name="comment">${_("comment {verb}")}
|
||||
</button>
|
||||
<button id="comment_edit_${thing.link_name}" style="display:none;"
|
||||
<button style="display:none;" name="edit"
|
||||
type="submit" class="btn">${_( "edit")}</button>
|
||||
%if not thing.link_name:
|
||||
<button id="cancel_" class="btn"
|
||||
<button class="btn" name="cancel" style="display:none;"
|
||||
onclick="cancelReply(this);return false">
|
||||
${_("cancel")}</button>
|
||||
%endif
|
||||
<span id='status_${thing.link_name}' class='error'></span>
|
||||
<span class="status error"></span>
|
||||
</div>
|
||||
<div id="marktoggle_${thing.link_name}"
|
||||
class="help-toggle">
|
||||
<a id="marklink_${thing.link_name}" href='#'
|
||||
onclick='return helpon(this, "markhelp_", "${_('hide help')}");'>
|
||||
<div class="help-toggle toggle">
|
||||
<a href="#" class="option active"
|
||||
onclick='return toggle(this, helpon, helpoff)'>
|
||||
${_("help")}
|
||||
</a>
|
||||
<a href="#" class="option">${_("hide help")}</a>
|
||||
</div>
|
||||
<div class="clearleft"></div>
|
||||
<script type="text/javascript">
|
||||
var form = $("commentform_${thing.link_name}");
|
||||
if(form) {
|
||||
setMessage(form.comment_reply_${thing.link_name},
|
||||
"${_('enter a comment here')}")
|
||||
}
|
||||
var form = $("#commentreply_${thing.link_name}").find("textarea");
|
||||
emptyInput(form, "${_('enter a comment here')}");
|
||||
</script>
|
||||
|
||||
<div id="markhelp_${thing.link_name}" style="display: none">
|
||||
<div class="markhelp" style="display: none">
|
||||
<table class="help">
|
||||
<tr style="background-color: #ffff99; text-align: center">
|
||||
<td><em>${_( "you type:")}</em></td>
|
||||
@@ -94,7 +87,4 @@
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
%if not thing.link_name:
|
||||
</div>
|
||||
%endif
|
||||
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
<%namespace file="utils.html" import="image_upload"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
function update_title() {
|
||||
var name_field = $("name");
|
||||
var title_field = $("title");
|
||||
if (title_field.value == "") {
|
||||
title_field.value = name_field.value;
|
||||
}
|
||||
function update_title(elem) {
|
||||
var form = $(elem).parents("form");
|
||||
var title = $(form).find("input[name=title]");
|
||||
var name = $(form).find("input[name=name]");
|
||||
if (title.attr("value") == "")
|
||||
title.attr("value", name.attr("value"));
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -40,7 +40,7 @@ function update_title() {
|
||||
<% full_name = "type_" + name %>
|
||||
<tr>
|
||||
<td class="nowrap nopadding">
|
||||
<input name="type" id="${full_name}" type="radio"
|
||||
<input name="type" type="radio" id="${full_name}"
|
||||
value="${name}" class="nomargin"
|
||||
%if thing.site and name == thing.site.type \
|
||||
or not thing.site and name == "public":
|
||||
@@ -80,7 +80,7 @@ function update_title() {
|
||||
%else:
|
||||
<input type="text" name="name" id="name" class="text"
|
||||
value="${thing.name}"
|
||||
onchange="update_title()"/>
|
||||
onchange="update_title(this)"/>
|
||||
%endif
|
||||
</td>
|
||||
<td class="note" id="note_name">
|
||||
@@ -153,7 +153,8 @@ function update_title() {
|
||||
<td colspan="2" class="prefright">
|
||||
<table >
|
||||
${radio_type(_("public"), _("anyone can view and submit"))}
|
||||
${radio_type(_("restricted"), _("anyone can view, but only contributors can submit links"))}
|
||||
${radio_type(_("restricted"),
|
||||
_("anyone can view, but only contributors can submit links"))}
|
||||
${radio_type(_("private"), _("only contributors can view and submit"))}
|
||||
</table>
|
||||
</td>
|
||||
@@ -163,8 +164,10 @@ function update_title() {
|
||||
<th><label>${_("age")}</label></th>
|
||||
<td colspan="2" class="prefright">
|
||||
<input class="nomargin" type="checkbox" name="over_18" id="over_18"
|
||||
${thing.site and thing.site.over_18 and "checked='checked'" or ""}/>
|
||||
<label for="over_18">${_("viewers must be over eighteen years old")}</label>
|
||||
${thing.site and thing.site.over_18 and "checked='checked'" or ""}/>
|
||||
<label for="over_18">
|
||||
${_("viewers must be over eighteen years old")}
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -194,32 +197,33 @@ function update_title() {
|
||||
</div>
|
||||
</td>
|
||||
<td class="note" id="note_domain">
|
||||
<a id="example_domain" href="javascript:show('domainhelp'); hide('example_domain')">what's this?</a>
|
||||
<a id="example_domain" href="#"
|
||||
onclick="$('#domainhelp').slideDown(); $('#example_domain').fadeOut(); return false;">what's this?</a>
|
||||
${error_field("BAD_CNAME", "span")}
|
||||
${error_field("USED_CNAME", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<table class="preftable pretty-form">
|
||||
%if thing.site and thing.site.can_change_stylesheet(c.user) and not g.css_killswitch:
|
||||
<tr>
|
||||
<th>
|
||||
${_("look and feel")}
|
||||
</th>
|
||||
<td class="prefright">
|
||||
<div class="spacer">
|
||||
%if thing.site and thing.site.can_change_stylesheet(c.user) and not g.css_killswitch:
|
||||
<tr>
|
||||
<th>
|
||||
${_("look and feel")}
|
||||
</th>
|
||||
<td class="prefright">
|
||||
<div class="spacer">
|
||||
${plain_link(_("edit the stylesheet"),
|
||||
"/about/stylesheet",
|
||||
_sr_path = True)}
|
||||
 
|
||||
<span class="gray">(${_("leaves this page")})</span>
|
||||
</div>
|
||||
<div class="spacer">
|
||||
<label for="headerfile">${_("upload header image")}</label>
|
||||
<%call expr="image_upload('/api/upload_sr_img', thing.site.header)">
|
||||
</td>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<%call expr="image_upload('/api/upload_sr_img', thing.site.header,
|
||||
tabular=True,
|
||||
label = _('upload header image'))">
|
||||
<tr><td></td><td>
|
||||
<button id="delete-img"
|
||||
%if not thing.site.header:
|
||||
style="display: none;"
|
||||
@@ -231,12 +235,21 @@ function update_title() {
|
||||
<input type="hidden" name="uh" value="${c.modhash}" />
|
||||
<input type="hidden" name="r" value="${c.site.name}" />
|
||||
<input type="hidden" name="header" value="1" />
|
||||
</%call>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<script type="text/javascript">
|
||||
function on_image_success(img) {
|
||||
$("#header-img").attr("src", img.attr("src"));
|
||||
$('#delete-img').show();
|
||||
$('#submit-img').hide();
|
||||
}
|
||||
</script>
|
||||
</td>
|
||||
</tr>
|
||||
</%call>
|
||||
%else:
|
||||
</table>
|
||||
</form>
|
||||
%endif
|
||||
</table>
|
||||
|
||||
<div class="save-button">
|
||||
<%
|
||||
@@ -248,9 +261,9 @@ function update_title() {
|
||||
text = _("create")
|
||||
%>
|
||||
<button name="${name}" class="btn" type="button"
|
||||
onclick="$('sr_form').onsubmit()"
|
||||
onclick="$('#sr_form').get(0).onsubmit()"
|
||||
>${text}</button>
|
||||
 
|
||||
<span id="status" class="error"></span>
|
||||
<span class="status error"></span>
|
||||
${error_field("RATELIMIT", "span")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
<% error = thing.error %>
|
||||
<li>
|
||||
%if hasattr(error,'line'):
|
||||
<a href="javascript:gotoTextboxLine('stylesheet_contents',${error.line})">
|
||||
<a href="javascript:void(0)"
|
||||
onclick="$('#stylesheet_contents').select_line(${error.line}); return false;">
|
||||
[line ${error.line}]
|
||||
%endif
|
||||
|
||||
|
||||
@@ -67,20 +67,22 @@
|
||||
</th>
|
||||
<td>
|
||||
%if thing.replyto:
|
||||
<input id="replyto" name="Reply-to" type="text"
|
||||
value="${thing.email}" onfocus="clearTitle(this)"
|
||||
<input id="replyto" name="replyto" type="text"
|
||||
value="${thing.email}"
|
||||
size="30"/>
|
||||
%else:
|
||||
<input id="replyto" name="replyto" style="color: gray"
|
||||
<input id="replyto" name="replyto" class="gray"
|
||||
value="${_('optional')}" type="text"
|
||||
onfocus="clearTitle(this)" size="30"/>
|
||||
size="30"/>
|
||||
%endif
|
||||
${error_field("BAD_EMAILS", "span")}
|
||||
%else:
|
||||
%if thing.email:
|
||||
<input id="email" name="email" type="text"
|
||||
value="${thing.email}" onfocus="clearTitle(this)" size="30"/>
|
||||
<input id="email" name="email" type="text"
|
||||
value="${thing.email}" size="30"/>
|
||||
%else:
|
||||
<input id="email" type="text" name="email" onfocus="clearTitle(this)" size="30"/>
|
||||
<input id="email" type="text" name="email" size="30"
|
||||
value="${ _('put your email here')}" class="gray"/>
|
||||
%endif
|
||||
%endif
|
||||
<span class="little gray">${_("(only used to reply)")}</span>
|
||||
@@ -102,18 +104,9 @@
|
||||
</table>
|
||||
<div style="margin: 10px 0 20px 0">
|
||||
<button class="btn" type="submit">${_("send")}</button>
|
||||
<span id="status" class="error"></span>
|
||||
<span class="status"></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
%if not thing.email:
|
||||
<script type="text/javascript">
|
||||
var form = $('feedback');
|
||||
if(form) {
|
||||
setMessage(form.email,
|
||||
"${ _('put your email here')}")
|
||||
}
|
||||
</script>
|
||||
%endif
|
||||
%endif
|
||||
|
||||
50
r2/r2/templates/framebuster.html
Normal file
50
r2/r2/templates/framebuster.html
Normal file
@@ -0,0 +1,50 @@
|
||||
<%namespace file="reddit.html" import="javascript"/>
|
||||
<%namespace file="utils.html" import="js_preamble"/>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>escappit</title>
|
||||
<% from r2.lib.template_helpers import static %>
|
||||
<script src="${static('jquery.js')}" type="text/javascript"></script>
|
||||
<script src="${static('jquery.json.js')}" type="text/javascript"></script>
|
||||
<script src="${static('jquery.reddit.js')}" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
$.cookie_write({name: "reddit_first", data: {firsttime: true},
|
||||
domain: "${c.site.domain}" });
|
||||
%if thing.login:
|
||||
$.cookie_write({name: "redditSession", data: "cname",
|
||||
domain: "${c.site.domain}" });
|
||||
if(parent.$ && parent.$.framebuster)
|
||||
parent.$.framebuster();
|
||||
%else:
|
||||
$.cookie_write({name: "redditSession", data: "",
|
||||
domain: "${c.site.domain}"});
|
||||
%endif
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
<%def name="framebuster()">
|
||||
%if c.cname and (c.frameless_cname or not c.user_is_loggedin) and not c.authorized_cname:
|
||||
<%
|
||||
import random
|
||||
from r2.lib.template_helpers import get_domain
|
||||
%>
|
||||
<script type="text/javascript">
|
||||
(function($) {
|
||||
$.framebuster = function() {
|
||||
$(function() {
|
||||
$.refresh();
|
||||
});
|
||||
}
|
||||
})(jQuery);
|
||||
document.write('<iframe name="reddit-window" id="reddit-window"' +
|
||||
'src="http://${get_domain(cname=False, subreddit=True)}/framebuster/'
|
||||
+ Math.random() +
|
||||
'" width="1" height="1" style="visibility: hidden"></iframe>');
|
||||
</script>
|
||||
%endif
|
||||
</%def>
|
||||
@@ -24,20 +24,29 @@
|
||||
%>
|
||||
|
||||
<%inherit file="reddit.html"/>
|
||||
<%namespace file="utils.html" import="error_field, plain_link"/>
|
||||
<%namespace file="printable.html" import="state_button, comment_button" />
|
||||
|
||||
<%def name="javascript()">
|
||||
${parent.javascript()}
|
||||
<script src="${static('frame.js')}" type="text/javascript"></script>
|
||||
</%def>
|
||||
<%namespace file="utils.html" import="plain_link, logout"/>
|
||||
<%namespace file="printable.html" import="state_button, comment_button, thing_css_class" />
|
||||
|
||||
<%def name="bodyHTML()">
|
||||
<body>
|
||||
<% fullname = thing.link._fullname %>
|
||||
<% upstyle = "mod" if thing.link.likes else "" %>
|
||||
<% downstyle = "mod" if thing.link.likes is False else "" %>
|
||||
<table class="toolbar" width="100%">
|
||||
<% fullname = thing.link._fullname %>
|
||||
<% upstyle = "mod" if thing.link.likes else "" %>
|
||||
<% downstyle = "mod" if thing.link.likes is False else "" %>
|
||||
<table class="${thing_css_class(thing.link)} toolbar" width="100%">
|
||||
<script type="text/javascript">
|
||||
function kill(uh) {
|
||||
$("#main").hide();
|
||||
$("#killed").show();
|
||||
$.request('noframe');
|
||||
}
|
||||
|
||||
function unkill() {
|
||||
$("#killed").hide();
|
||||
$("#main").show();
|
||||
$.request('frame');
|
||||
}
|
||||
|
||||
</script>
|
||||
<tr id="killed" style="display: none" class="menu">
|
||||
<td nowrap="nowrap">
|
||||
after reloading, this frame will not be shown again. click
|
||||
@@ -45,26 +54,40 @@
|
||||
<td width="100%" />
|
||||
</tr>
|
||||
<tr id="main">
|
||||
<td id="frame_left">
|
||||
<td id="frame-left">
|
||||
<a target="_top" href="http://${get_domain(cname = c.cname)}/">
|
||||
<img style="border: none" src="${static('littlehead.png')}"
|
||||
alt="reddit.com" title="reddit.com" />
|
||||
</a>
|
||||
<span id="up_${fullname}" class="arrow up${upstyle}"
|
||||
onclick="javascript:frame_mod('${fullname}', 1)">
|
||||
<a id="up_${fullname}" class="arrow up${upstyle}"
|
||||
%if c.user_is_loggedin:
|
||||
href="javascript:void(0);"
|
||||
onclick="$(this).vote()"
|
||||
%else:
|
||||
href="/login"
|
||||
target="_top"
|
||||
%endif
|
||||
>
|
||||
${_("like")}
|
||||
</span>
|
||||
<span id="down_${fullname}" class="arrow down${downstyle}"
|
||||
onclick="javascript:frame_mod('${fullname}', 0)">
|
||||
</a>
|
||||
<a id="down_${fullname}" class="arrow down${downstyle}"
|
||||
%if c.user_is_loggedin:
|
||||
href="javascript:void(0);"
|
||||
onclick="$(this).vote()"
|
||||
%else:
|
||||
href="/login"
|
||||
target="_top"
|
||||
%endif
|
||||
>
|
||||
${_("dislike")}
|
||||
</span>
|
||||
</a>
|
||||
%if c.user_is_loggedin:
|
||||
%if thing.link.saved:
|
||||
${state_button("unsave", fullname, _("unsave"), \
|
||||
"return change_state(this, 'unsave');", _("unsaved"), a_class='')}
|
||||
${state_button("unsave", _("unsave"),
|
||||
"return change_state(this, 'unsave');", _("unsaved"))}
|
||||
%else:
|
||||
${state_button("save", fullname, _("save"), \
|
||||
"return change_state(this, 'save');", _("saved"), a_class = '')}
|
||||
${state_button("save", _("save"),
|
||||
"return change_state(this, 'save');", _("saved"))}
|
||||
%endif
|
||||
%endif
|
||||
<%
|
||||
@@ -76,21 +99,18 @@
|
||||
com_label = ungettext("comment", "comments", thing.link.num_comments)
|
||||
%>
|
||||
<span>
|
||||
${comment_button("comment", fullname, com_label,
|
||||
thing.link.num_comments,
|
||||
thing.link.permalink, a_class='')}
|
||||
${comment_button("comment", com_label,
|
||||
thing.link.num_comments,
|
||||
thing.link.permalink)}
|
||||
</span>
|
||||
</td>
|
||||
<td id="logerr" style="text-align: right" class="error" width="50%">
|
||||
${error_field("WRONG_PASSWORD_login", "span")}
|
||||
</td>
|
||||
<td id="frame_middle" nowrap="nowrap" style="display: none">
|
||||
<td id="frame-middle" nowrap="nowrap" style="display: none">
|
||||
</td>
|
||||
<td id="frame-right" class="menu" nowrap="nowrap">
|
||||
%if c.user_is_loggedin:
|
||||
<a href="http://${get_domain(cname = c.cname)}/user/${c.user.name}"
|
||||
target="_parent">${c.user.name}</a> (${c.user.safe_karma})
|
||||
<a href="http://${get_domain(cname = c.cname)}/logout">${_("logout")}</a>
|
||||
${logout()}
|
||||
%else:
|
||||
<a href="/login">${_("login")}</a>
|
||||
<a href="/login">${_("register")}</a>
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="translatedstring.html" import="text_input"/>
|
||||
|
||||
<%
|
||||
highlight = lambda x: ("<span class='orangered' style='padding: 0px 1ex 0px 1ex'>" + x + "</span>")
|
||||
singular = unsafe(thing._singular(highlight))
|
||||
@@ -43,34 +45,3 @@
|
||||
%endif
|
||||
%endfor
|
||||
%endif
|
||||
|
||||
|
||||
<%def name="text_input(text, valid,
|
||||
index = 0, len = 0)">
|
||||
<div id="trans_div_${thing.md5}_${index}">
|
||||
%if text and valid:
|
||||
<b style="padding-right: 5px" id="string_${thing.md5}_${index}">
|
||||
${text}
|
||||
</b>
|
||||
<span class="little bylink">
|
||||
<a href="javascript:void(0)"
|
||||
onclick="return edit_trans('${thing.md5}_${index}')">
|
||||
edit</a>
|
||||
</span>
|
||||
%elif len < 80:
|
||||
<input type="text" value="${text}" size="40"
|
||||
name="trans_${thing.md5}_${index}" id="trans_${thing.md5}_${index}"/>
|
||||
%else:
|
||||
<textarea rows="4" cols="40"
|
||||
name="trans_${thing.md5}_${index}"
|
||||
id="trans_${thing.md5}_${index}">
|
||||
${text}
|
||||
</textarea>
|
||||
%endif
|
||||
%if not valid:
|
||||
<span class="error">
|
||||
Invalid Translation
|
||||
</span>
|
||||
%endif
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
## with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is Reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="printable.html" import="state_button"/>
|
||||
|
||||
<%def name="help_or_hide(name, help)">
|
||||
<div class="corner-help">
|
||||
<a href="javascript:void(0)" onclick="show('${name}-help'); return false;">
|
||||
${_("what's this?")}
|
||||
</a>
|
||||
</div>
|
||||
<div id="${name}-help" style="display:none" class="helpcover" >
|
||||
<p>
|
||||
${help}
|
||||
</p>
|
||||
%if c.user_is_loggedin:
|
||||
${state_button("leave" + name, name, _("here"),
|
||||
"return deletetoggle(this, disable_ui);", "",
|
||||
fmt = _("Click %(here)s to disable this feature."),
|
||||
fmt_param = "here",
|
||||
question = _("are you sure?"),
|
||||
yes = _("yes"), no = _("no"),
|
||||
a_class="organic-help-button")}
|
||||
%endif
|
||||
${state_button("nvm" + name, name, _("here"),
|
||||
"hide('%s-help'); return false;" % name, "",
|
||||
fmt = _("Click %(here)s to close help."),
|
||||
fmt_param = "here",
|
||||
a_class="organic-help-button")}
|
||||
</div>
|
||||
<div id="${name}-gone" style="display:none" >
|
||||
<p style="margin-left:5px">
|
||||
${_("The new link area will no longer appear for you. To re-enable it, visit your preferences.")}
|
||||
</p>
|
||||
</div>
|
||||
</%def>
|
||||
13
r2/r2/templates/jquery.reddit.js
Normal file
13
r2/r2/templates/jquery.reddit.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/* The reddit extension for jquery. This file is intended to store
|
||||
* "utils" type function declarations and to add functionality to "$"
|
||||
* or "jquery" lookups. See
|
||||
* http://docs.jquery.com/Plugins/Authoring
|
||||
* for the plug-in spec.
|
||||
*/
|
||||
|
||||
jQuery.log = function(message) {
|
||||
if (window.console)
|
||||
console.debug(message);
|
||||
else
|
||||
alert(message);
|
||||
};
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
<%def name="numcol()">
|
||||
<% num = thing.num %>
|
||||
<span class="rank" style="width:$numcolmargin;"
|
||||
id="num_${thing._fullname}">
|
||||
<span class="rank" style="width:$numcolmargin;">
|
||||
%if thing.top_link:
|
||||
<a class="star" href="/toplinks">
|
||||
%endif
|
||||
@@ -43,11 +42,11 @@
|
||||
</%def>
|
||||
|
||||
<%def name="make_link(name, css_class)">
|
||||
<a id="${name}_${thing._fullname}"
|
||||
onmousedown="setClick(this, '${css_class}')"
|
||||
class="${css_class} ${ c.user_is_loggedin and 'loggedin' or ''} ${thing.clicked and 'click' or ''}"
|
||||
<a class="${css_class} ${ c.user_is_loggedin and 'loggedin' or ''} ${thing.clicked and 'click' or ''}"
|
||||
%if c.user.pref_frame:
|
||||
href="http://${get_domain(cname = c.cname, subreddit = False)}/goto?id=${thing._id36}"
|
||||
%elif thing.is_self:
|
||||
href="${thing.permalink}"
|
||||
%else:
|
||||
href="${thing.url}"
|
||||
%endif
|
||||
@@ -65,7 +64,7 @@
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
<p class="title" id="titlerow_${thing._fullname}">
|
||||
<p class="title">
|
||||
<%call expr="make_link('title', 'title')">
|
||||
${thing.title}
|
||||
</%call>
|
||||
@@ -79,7 +78,6 @@
|
||||
${self.buttons()}
|
||||
${self.admintagline()}
|
||||
</ul>
|
||||
${self.mediadiv()}
|
||||
</%def>
|
||||
|
||||
<%def name="subreddit()" buffered="True">
|
||||
@@ -91,23 +89,23 @@
|
||||
path = thing.subreddit.path
|
||||
endif
|
||||
%>
|
||||
<a id="subreddit_${thing._fullname}" href="${path}" class="hover"
|
||||
<a href="${path}" class="subreddit hover"
|
||||
${'target="_top"' if c.cname else ''} >
|
||||
${thing.subreddit.name}
|
||||
</a>
|
||||
<script type="text/javascript">
|
||||
sr['${thing._fullname}'] = '${thing.subreddit._fullname}';
|
||||
reddit.sr['${thing._fullname}'] = '${thing.subreddit._fullname}';
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="midcol(display=True)">
|
||||
<div id="arrows_${thing._fullname}" class="midcol" style="width:$midcolmargin;"
|
||||
<div class="midcol" style="width:$midcolmargin;"
|
||||
${not display and "style='display:none'" or ''}>
|
||||
${self.arrow(thing, 1, thing.likes)}
|
||||
%if thing.hide_score:
|
||||
<div class="score">•</div>
|
||||
%else:
|
||||
${self.score(thing, thing.likes, inline=False, label = False)}
|
||||
${self.score(thing, thing.likes, inline=False)}
|
||||
%endif
|
||||
${self.arrow(thing, 0, thing.likes == False)}
|
||||
</div>
|
||||
@@ -152,30 +150,31 @@
|
||||
com_label = ungettext("comment", "comments", thing.num_comments)
|
||||
%>
|
||||
<li class="first">
|
||||
${parent.comment_button("comment", fullname, com_label,
|
||||
${parent.comment_button("comment", com_label,
|
||||
thing.num_comments, thing.permalink,
|
||||
newwindow = c.user.pref_newwindow)}
|
||||
</li>
|
||||
%endif
|
||||
<li id="share_li_${fullname}">
|
||||
${parent.simple_button("share", fullname, _("share"), "share")}
|
||||
<li class="share">
|
||||
${parent.toggle_button("share-button", _("share"), _("cancel"),
|
||||
"share", "cancelShare", login_required = True)}
|
||||
</li>
|
||||
%if c.user_is_loggedin:
|
||||
<li>
|
||||
%if thing.saved:
|
||||
${parent.state_button("unsave", fullname, _("unsave"), \
|
||||
"return change_state(this, 'unsave');", _("unsaved"))}
|
||||
${parent.state_button("unsave", _("unsave"), \
|
||||
"return change_state(this, 'unsave', unsave_thing);", _("unsaved"))}
|
||||
%else:
|
||||
${parent.state_button("save", fullname, _("save"), \
|
||||
"return change_state(this, 'save');", _("saved"))}
|
||||
${parent.state_button("save", _("save"), \
|
||||
"return change_state(this, 'save', save_thing);", _("saved"))}
|
||||
%endif
|
||||
</li><li>
|
||||
%if thing.hidden:
|
||||
${parent.state_button("unhide", fullname, _("unhide"), \
|
||||
"return change_w_callback(this, $ListClass.unhide);", _("unhidden"))}
|
||||
${parent.state_button("unhide", _("unhide"), \
|
||||
"change_state(this, 'unhide', hide_thing);", _("unhidden"))}
|
||||
%else:
|
||||
${parent.state_button("hide", fullname, _("hide"), \
|
||||
"return change_w_callback(this, $ListClass.hide);", _("hidden"))}
|
||||
${parent.state_button("hide", _("hide"), \
|
||||
"change_state(this, 'hide', hide_thing);", _("hidden"))}
|
||||
%endif
|
||||
</li>
|
||||
%endif
|
||||
@@ -189,19 +188,9 @@
|
||||
<%def name="media_embed()">
|
||||
%if thing.media_object:
|
||||
<li>
|
||||
<a id="view_embeded_media_a_${thing._fullname}" class="" \
|
||||
href="javascript:view_embeded_media('${thing._fullname}', '${thing.media_object}')">\
|
||||
<span id="view_embeded_media_span_watch_${thing._fullname}"
|
||||
class="watch-play"
|
||||
style="display: inline">
|
||||
${_("watch")}
|
||||
</span>
|
||||
<span id="view_embeded_media_span_close_${thing._fullname}"
|
||||
class="watch-stop"
|
||||
style="display: none">
|
||||
${_("close")}
|
||||
</span>
|
||||
</a>
|
||||
${parent.toggle_button("media-button", _("watch"), _("cancel"),
|
||||
'show_media("%s")' % websafe(thing.media_object),
|
||||
"cancelMedia")}
|
||||
</li>
|
||||
%endif
|
||||
</%def>
|
||||
@@ -214,11 +203,3 @@
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="mediadiv()">
|
||||
%if thing.media_object:
|
||||
<div id="embeded_media_${thing._fullname}"
|
||||
class="embededmedia" style="display: none;">
|
||||
<p class="error">loading...</p>
|
||||
</div>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<%namespace file="utils.html" import="optionalstyle"/>
|
||||
<%namespace file="printable.html" import="score"/>
|
||||
|
||||
<%!
|
||||
from pylons.i18n import _, ungettext
|
||||
@@ -29,6 +30,7 @@
|
||||
|
||||
<%def name="entry()">
|
||||
<%
|
||||
from r2.lib.strings import Score
|
||||
if thing.num_comments:
|
||||
# generates "XX comments" as a noun
|
||||
com_label = "%d %s" % \
|
||||
@@ -40,39 +42,12 @@
|
||||
|
||||
domain = get_domain(subreddit=False)
|
||||
permalink = "http://%s%s" % (domain, thing.permalink)
|
||||
if thing.likes == False:
|
||||
arrow = "http://%s/static/widget_arrows_down.gif"
|
||||
elif thing.likes:
|
||||
arrow = "http://%s/static/widget_arrows_up.gif"
|
||||
else:
|
||||
arrow = "http://%s/static/widget_arrows.gif"
|
||||
arrow = arrow % domain
|
||||
expanded = request.get.get("expanded")
|
||||
two_col = request.get.has_key("twocolumn") if l else False
|
||||
|
||||
thing.score_fmt = Score.safepoints
|
||||
%>
|
||||
%if expanded:
|
||||
<div class="reddit-voting-arrows"
|
||||
${optionalstyle("float:left; margin: 1px;")}>
|
||||
<script type="text/javascript">
|
||||
var reddit_bordercolor="FFFFFF";
|
||||
</script>
|
||||
<%
|
||||
url = "http://%s/button_content?t=4&id=%s" % (get_domain(cname = c.cname, subreddit = True), thing._fullname)
|
||||
if c.bgcolor:
|
||||
url += "&bgcolor=%s&bordercolor=%s" % (c.bgcolor, c.bgcolor)
|
||||
else:
|
||||
url += "&bgcolor=FFFFFF&bordercolor=FFFFFF"
|
||||
%>
|
||||
<iframe src="${url}" height="55" width="51" scrolling="no" frameborder="0"
|
||||
${optionalstyle("margin:0px;")}>
|
||||
</iframe>
|
||||
</div>
|
||||
%else:
|
||||
<a href="${permalink}" class="reddit-voting-arrows" target="_blank"
|
||||
${optionalstyle("float:left; display:block;")}>
|
||||
<img src="${arrow}" alt="vote" ${optionalstyle("border:none;margin-top:3px;")}/>
|
||||
</a>
|
||||
%endif
|
||||
${self.arrows(thing)}
|
||||
<div class="reddit-entry"
|
||||
%if expanded:
|
||||
${optionalstyle("margin-left: 58px;")}
|
||||
@@ -95,8 +70,8 @@
|
||||
%endif
|
||||
>
|
||||
%if not expanded:
|
||||
${thing.score} ${ungettext("point", "points", thing.score)}
|
||||
| 
|
||||
${score(thing, thing.likes, inline = True)}
|
||||
 | 
|
||||
%endif
|
||||
<a class="reddit-comment-link"
|
||||
${optionalstyle("color:gray")}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</%def>
|
||||
|
||||
<%def name="midcol(display=True)">
|
||||
<div id="arrows_${thing._fullname}" class="midcol" style="width:&midcolmargin;"
|
||||
<div class="midcol" style="width:&midcolmargin;"
|
||||
${not display and "style='display:none'" or ''}>
|
||||
${self.arrow(thing, 1, thing.likes)}
|
||||
${self.arrow(thing, 0, thing.likes == False)}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<%namespace file="printable.html" import="state_button" />
|
||||
<%namespace file="printable.html" import="state_button, thing_css_class" />
|
||||
|
||||
<div class="raisedbox linkinfo">
|
||||
<div class="raisedbox linkinfo ${thing_css_class(thing.a)}">
|
||||
<table class="details">
|
||||
<tr><th>${_("submitted on")}</th>
|
||||
<td>${thing.a._date.strftime(thing.datefmt)}</td></tr>
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
<%!
|
||||
from datetime import datetime
|
||||
%>
|
||||
|
||||
<%namespace file="printable.html" import="yes_no_button" />
|
||||
<%namespace file="printable.html" import="ynbutton" />
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
%if thing.a.promoted:
|
||||
<tr>
|
||||
@@ -58,12 +58,4 @@
|
||||
% dict(subreddit = c.site.name))}</td>
|
||||
</tr>
|
||||
%endif
|
||||
%else:
|
||||
<tr>
|
||||
<th></th>
|
||||
<td>
|
||||
${yes_no_button("promote", thing.a._fullname, _("promote"), \
|
||||
"return deletetoggle(this,'promote');", _("promoted"))}
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
|
||||
@@ -25,11 +25,9 @@
|
||||
|
||||
<%
|
||||
_id = ("_%s" % thing.parent_name) if hasattr(thing, 'parent_name') else ''
|
||||
cls = thing.lookups[0].__class__.__name__.lower()
|
||||
%>
|
||||
<div id="siteTable${_id}" class="sitetable">
|
||||
<div id="ajaxHook${_id}" class="ajaxhook">
|
||||
</div>
|
||||
|
||||
<div id="siteTable${_id}" class="sitetable ${cls}">
|
||||
%for a in thing.things:
|
||||
${unsafe(replace_render(thing, a))}
|
||||
%endfor
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<%namespace file="utils.html" import="optionalstyle"/>
|
||||
<%namespace file="printable.html" import="thing_css_class"/>
|
||||
<div class="reddit-listing"
|
||||
${optionalstyle("margin-left:5px;margin-top:7px;")}>
|
||||
<%
|
||||
@@ -44,7 +45,7 @@
|
||||
%endif
|
||||
%endif
|
||||
|
||||
<div class="${cls}">
|
||||
<div class="${cls} ${thing_css_class(a)}">
|
||||
${a.render()}
|
||||
</div>
|
||||
%if two_col and i == l - 1:
|
||||
|
||||
@@ -33,18 +33,21 @@
|
||||
%if c.user_is_loggedin:
|
||||
<p class="error">${_("you are logged in. go use the site!")}</p>
|
||||
%else:
|
||||
${login_panel(user_reg = thing.user_reg, user_login = thing.user_login,
|
||||
${login_panel(login_form,
|
||||
user_reg = thing.user_reg, user_login = thing.user_login,
|
||||
dest=thing.dest)}
|
||||
<script type="text/javascript">
|
||||
new_captcha();
|
||||
$.request("new_captcha");
|
||||
</script>
|
||||
%endif
|
||||
|
||||
<%def name="login_form(register=False, user='', dest='')">
|
||||
<%def name="login_form(register=False, user='', dest='', include_tos=True)">
|
||||
<% op = "reg" if register else "login" %>
|
||||
<form id="login_${op}" method="post"
|
||||
action="${add_sr('/post/' + op, nocname = True)}"
|
||||
onsubmit="return chklogin(this);"
|
||||
%if not c.frameless_cname or c.authorized_cname:
|
||||
onsubmit="return post_form(this, '${"register" if register else "login"}');"
|
||||
%endif
|
||||
target="_top">
|
||||
%if c.cname:
|
||||
<input type="hidden" name="${UrlParser.cname_get}"
|
||||
@@ -59,73 +62,73 @@
|
||||
<ul>
|
||||
<li>
|
||||
<label for="user_${op}">${_('username')}:</label>
|
||||
<input value="${user}" name="user_${op}" id="user_${op}"
|
||||
<input value="${user}" name="user" id="user_${op}"
|
||||
type="text" maxlength="20"/>
|
||||
%if register:
|
||||
${error_field("BAD_USERNAME_" + op, kind="span")}
|
||||
${error_field("USERNAME_TAKEN_" + op, kind="span")}
|
||||
${error_field("BAD_USERNAME", kind="span")}
|
||||
${error_field("USERNAME_TAKEN", kind="span")}
|
||||
%endif
|
||||
</li>
|
||||
%if register:
|
||||
<li>
|
||||
<label for="email_${op}">${_('email')}: <i>(${_('optional')})</i></label>
|
||||
<input value="" name="email_${op}" id="email_${op}"
|
||||
<label for="email_${op}">
|
||||
${_('email')}: <i>(${_('optional')})
|
||||
</i></label>
|
||||
<input value="" name="email" id="email_${op}"
|
||||
type="text" maxlength="50"/>
|
||||
%if register:
|
||||
${error_field("BAD_EMAILS", kind="span")}
|
||||
%endif
|
||||
</li>
|
||||
%endif
|
||||
<li>
|
||||
<label for="passwd_${op}">${_('password')}:</label>
|
||||
<input id="passwd_${op}"
|
||||
name="passwd_${op}" type="password" maxlength="20"/>
|
||||
<input id="passwd_${op}" name="passwd" type="password"
|
||||
maxlength="20"/>
|
||||
%if register:
|
||||
${error_field("BAD_PASSWORD_" + op, kind="span")}
|
||||
${error_field("BAD_PASSWORD", kind="span")}
|
||||
%else:
|
||||
${error_field("WRONG_PASSWORD_" + op, kind="span")}
|
||||
${error_field("WRONG_PASSWORD", kind="span")}
|
||||
%endif
|
||||
</li>
|
||||
%if register:
|
||||
<li>
|
||||
<label for="passwd2_${op}">${_('verify password')}:</label>
|
||||
<input name="passwd2_${op}" id="passwd2_${op}"
|
||||
<input name="passwd2" id="passwd2_${op}"
|
||||
type="password" maxlength="20" />
|
||||
${error_field("BAD_PASSWORD_MATCH_" + op, kind="span")}
|
||||
${error_field("BAD_PASSWORD_MATCH", kind="span")}
|
||||
</li>
|
||||
<li>
|
||||
${captchagen('', tabulate=True, label=False, size=30)}
|
||||
${error_field("DRACONIAN_" + op, kind="span")}
|
||||
${error_field("RATELIMIT_" + op, kind="span")}
|
||||
${error_field("DRACONIAN", kind="span")}
|
||||
${error_field("RATELIMIT", kind="span")}
|
||||
</li>
|
||||
%endif
|
||||
<li>
|
||||
<input type="checkbox" name="rem" id="rem_${op}" />
|
||||
<label for="rem_${op}" class="remember">${_('remember me')}</label>
|
||||
<label for="rem_${op}" class="remember">
|
||||
${_('remember me')}
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
%if register:
|
||||
<p class="legal">
|
||||
${text_with_links(strings.legal,
|
||||
user_agreement = (_("User Agreement"), "http://reddit.com/help/useragreement"),
|
||||
privacy_policy = (_("Privacy Policy"), "http://reddit.com/help/privacypolicy"))}
|
||||
</p>
|
||||
%endif
|
||||
<p>
|
||||
<button type="submit">
|
||||
${register and _("create account") or _("login")}
|
||||
</button>
|
||||
<span id='status_${op}' class='error'></span>
|
||||
<span class="status"></span>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="login_panel(user_reg = '', user_login = '', dest='')">
|
||||
<%def name="login_panel(lf, user_reg = '', user_login = '', dest='')">
|
||||
<div class="loginform divide">
|
||||
<h3>${_("create a new account")}</h3>
|
||||
<p class="tagline">
|
||||
${_("all it takes is a username and password")}
|
||||
</p>
|
||||
${login_form(register=True, user=user_reg, dest=dest)}
|
||||
${lf(register=True, user=user_reg, dest=dest)}
|
||||
<p>
|
||||
<span class="orangered">
|
||||
${_("is it really that easy? only one way to find out...")}
|
||||
@@ -137,10 +140,10 @@
|
||||
<p class="tagline">
|
||||
${_("already have an account and just want to login?")}
|
||||
</p>
|
||||
${login_form(user = user_login, dest = dest)}
|
||||
<p>
|
||||
${plain_link(_("forgot your password?"), "/password")}
|
||||
</p>
|
||||
${lf(user = user_login, dest = dest)}
|
||||
##<p>
|
||||
## ${plain_link(_("forgot your password?"), "/password")}
|
||||
##</p>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</%def>
|
||||
|
||||
@@ -27,32 +27,28 @@
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
|
||||
<% op = "login-main" %>
|
||||
<form method="post"
|
||||
action="${add_sr('/post/login', nocname = True)}"
|
||||
onsubmit="return chklogin(this);"
|
||||
id="login_${op}" action="${add_sr('/post/login', nocname = True)}"
|
||||
%if not c.frameless_cname or c.authorized_cname:
|
||||
onsubmit="return post_form(this, 'login');"
|
||||
%endif
|
||||
class="login-form-side">
|
||||
<% op = "login-main" %>
|
||||
%if c.cname:
|
||||
<input type="hidden" name="${UrlParser.cname_get}"
|
||||
value="${random.random()}" />
|
||||
%endif
|
||||
<input type="hidden" name="op" value="${op}" />
|
||||
<input name="user_login"
|
||||
id="user-${op}"
|
||||
type="text" maxlength="20" tabindex="1"/>
|
||||
<input id="passwd-${op}"
|
||||
name="passwd_login"
|
||||
type="password" maxlength="20" tabindex="2"/>
|
||||
<input name="user" type="text" maxlength="20" tabindex="1"/>
|
||||
<input name="passwd" type="password" maxlength="20" tabindex="2"/>
|
||||
|
||||
${error_field("WRONG_PASSWORD_" + op, "div")}
|
||||
<div id="status_${op}" class="error"></div>
|
||||
${error_field("WRONG_PASSWORD", "div")}
|
||||
<div class="status error"></div>
|
||||
|
||||
<div id="remember-me">
|
||||
<button class="btn" type="submit" tabindex="4">${_("login")}</button>
|
||||
<input type="checkbox" name="rem" tabindex="3"
|
||||
id="rem-${op}" />
|
||||
<label for="rem-${op}">
|
||||
${_("remember me")}</label>
|
||||
<input type="checkbox" name="rem" tabindex="3" id="rem-${op}" />
|
||||
<label for="rem-${op}">${_("remember me")}</label>
|
||||
<a href="/password">${_("recover password")}</a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
@@ -73,7 +73,7 @@ ${unsafe(safemarkdown(thing.body))}
|
||||
<%def name="buttons()">
|
||||
%if hasattr(thing, "was_comment"):
|
||||
<li>
|
||||
${parent.comment_button("context", thing._fullname, _("context"), 0,
|
||||
${parent.comment_button("context", _("context"), 0,
|
||||
thing.permalink + "?context=3")}
|
||||
</li>
|
||||
${parent.delete_or_report_buttons(delete=False)}
|
||||
@@ -82,24 +82,21 @@ ${unsafe(safemarkdown(thing.body))}
|
||||
${parent.buttons()}
|
||||
%if c.user_is_loggedin:
|
||||
<li>
|
||||
${parent.simple_button("reply", thing._fullname, _("reply {verb}"), "reply")}
|
||||
${parent.simple_button("reply", _("reply {verb}"), "reply")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
<% fullname = thing._fullname %>
|
||||
<div id="display_${fullname}">
|
||||
<p id="tagline_${fullname}" class="tagline">
|
||||
<div class="uncollapsed">
|
||||
<p class="tagline">
|
||||
${self.tagline()}
|
||||
</p>
|
||||
<div id="body_${fullname}" \
|
||||
class="body">${self.commentBody()}
|
||||
<div class="body">${self.commentBody()}
|
||||
</div>
|
||||
%if thing.author == c.user:
|
||||
<div style="display: none;" \
|
||||
id="edit_body_${fullname}">${self.commentText()}\
|
||||
<div style="display: none;">${self.commentText()}\
|
||||
</div>
|
||||
%endif
|
||||
<ul class="flat-list buttons" ${(collapse and "style='display:none'" or '')}>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
|
||||
<h1>${_("send a message")}</h1>
|
||||
|
||||
${success_field(_("your message has been delivered"),
|
||||
successful=thing.success)}
|
||||
|
||||
<%call expr="submit_form(onsubmit='return post_form(this, \'compose\', null, null, true)',
|
||||
method='post',
|
||||
<%call expr="submit_form(
|
||||
onsubmit='return post_form(this, \'compose\', null, null, true)',
|
||||
method='post', _id = 'compose-message',
|
||||
action='/message/compose', _class='iform')">
|
||||
${success_field(_("your message has been delivered"), successful=thing.success)}
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
cids = [cm._id36 for cm in thing.children]
|
||||
%>
|
||||
<span class="morecomments">
|
||||
<a style="font-size: smaller; font-weight: bold" id="more_${thing._fullname}" href="javascript:void()" onclick="return morechildren(this, '${thing.link_name}', ${cids}, ${thing.depth})">
|
||||
<a style="font-size: smaller; font-weight: bold"
|
||||
id="more_${thing._fullname}" href="javascript:void()"
|
||||
onclick="return morechildren(this, '${thing.link_name}', '${",".join(cids)}', ${thing.depth})">
|
||||
load more comments <span class="gray"> (${thing.count} ${ungettext("reply", "replies", thing.count)})</span>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
%endif
|
||||
</div>
|
||||
|
||||
<div class="drop-choices ${css_class}" style="display:none">
|
||||
<div class="drop-choices ${css_class}">
|
||||
%for option in thing:
|
||||
%if option != thing.selected:
|
||||
${plain_link(option.title, option.path, _sr_path = option.sr_path,
|
||||
@@ -55,7 +55,12 @@
|
||||
|
||||
|
||||
<%def name="flatlist()">
|
||||
<% css_class = str(thing.css_class) if thing.css_class else "" %>
|
||||
%if thing:
|
||||
%if thing.title:
|
||||
<span class="flat-list title">${thing.title}: </span>
|
||||
%endif
|
||||
|
||||
<ul class="flat-list hover"
|
||||
${"id='%s'" % thing._id if thing._id else ""}>
|
||||
%for i, option in enumerate(thing):
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.strings import strings
|
||||
%>
|
||||
<%namespace file="utils.html" import="error_field, submit_form, plain_link, text_with_links"/>
|
||||
|
||||
%if thing.subreddits:
|
||||
@@ -39,8 +42,7 @@
|
||||
<label for="url">${_("url")}</label>
|
||||
</th>
|
||||
<td>
|
||||
<input id="url" name="url" type="text"
|
||||
value="${thing.url}" onfocus="clearTitle(this);"/>
|
||||
<input id="url" name="url" type="text" value="${thing.url}"/>
|
||||
${error_field("NO_URL", "span")}
|
||||
${error_field("BAD_URL", "span")}
|
||||
${error_field("ALREADY_SUB", "span")}
|
||||
@@ -50,8 +52,7 @@
|
||||
<th>
|
||||
<label for="title">${_("title")}</label>
|
||||
</th>
|
||||
<td><input id="title" name="title" value="${thing.title}"
|
||||
onfocus="clearTitle(this)" type="text" />
|
||||
<td><input id="title" name="title" value="${thing.title}" type="text" />
|
||||
${error_field("NO_TITLE", "span")}
|
||||
${error_field("TITLE_TOO_LONG", "span")}
|
||||
</td>
|
||||
@@ -84,18 +85,18 @@
|
||||
<td></td>
|
||||
<td>
|
||||
<button class="btn" type="submit">${_("submit")}</button>
|
||||
<span id="status" class="error"></span>
|
||||
<span class="status"></span>
|
||||
${error_field("RATELIMIT", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script type="text/javascript">
|
||||
var form = $('newlink');
|
||||
if(form) {
|
||||
setMessage(form.url,
|
||||
var form = $("#newlink");
|
||||
if(form.length) {
|
||||
emptyInput(form.find("#url"),
|
||||
'${unsafe(_('type "self" here to make this post refer to itself.'))}');
|
||||
setMessage(form.title,
|
||||
emptyInput(form.find("#title"),
|
||||
"${_('enter a title, or click submit to find one automatically.')}")
|
||||
}
|
||||
</script>
|
||||
@@ -105,3 +106,4 @@
|
||||
bookmarklets = (_('reddit bookmarklets'), '/bookmarklets'))}
|
||||
</p>
|
||||
</%call>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="help.html" import="help_or_hide"/>
|
||||
<%namespace file="printable.html" import="ynbutton"/>
|
||||
<%
|
||||
from r2.lib.template_helpers import replace_render, static
|
||||
from r2.lib.promote import get_promoted
|
||||
@@ -28,29 +28,83 @@
|
||||
|
||||
<div id="siteTable_organic" class="organic-listing">
|
||||
<%
|
||||
lookup = dict((t._fullname, t) for t in thing.things)
|
||||
seen = set([])
|
||||
promoted = set(get_promoted())
|
||||
promoted = [o for o in thing.org_links if o in promoted ]
|
||||
%>
|
||||
<div id="ajaxHook_organic" class="ajaxhook"> </div>
|
||||
<script type="text/javascript">
|
||||
## fortunately, python arrays print in JS-compatible form.
|
||||
var organic_links = ${thing.org_links};
|
||||
reddit_thing_info.fetch = ${promoted};
|
||||
/*reddit_thing_info.fetch = ${promoted};*/
|
||||
</script>
|
||||
%for a in thing.things:
|
||||
${unsafe(replace_render(thing, a,
|
||||
display = (thing.visible_link == a._fullname)))}
|
||||
%for name in thing.org_links:
|
||||
%if name in seen:
|
||||
<% pass %>
|
||||
%elif lookup.has_key(name):
|
||||
<% seen.add(name) %>
|
||||
${unsafe(replace_render(thing, lookup[name],
|
||||
display = (thing.visible_link == name)))}
|
||||
%else:
|
||||
<div class="thing id-${name} stub" style="display:none"></div>
|
||||
%endif
|
||||
%endfor
|
||||
|
||||
<div class="nextprev">
|
||||
<img src="${static('prev_organic.png')}" alt="prev"
|
||||
onclick="return get_organic(false)"/>
|
||||
onclick="return get_organic(this, false)"/>
|
||||
<img src="${static('next_organic.png')}" alt="next"
|
||||
onclick="return get_organic(true)" />
|
||||
onclick="return get_organic(this, true)" />
|
||||
</div>
|
||||
|
||||
<div class="help">
|
||||
<a class="open" href="javascript:void(0)">${_("what's this?")}</a>
|
||||
</div>
|
||||
|
||||
${help_or_hide("organic", _("This area shows new and upcoming links. Vote on links here to help them become popular, and click the forwards and backwards buttons to view more. "))}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="help help-cover">
|
||||
<p>
|
||||
${_("This area shows new and upcoming links. Vote on" +
|
||||
" links here to help them become popular, and click" +
|
||||
" the forwards and backwards buttons to view more. ")}
|
||||
</p>
|
||||
%if c.user_is_loggedin:
|
||||
${ynbutton(_("here"), _("This element has been disabled."),
|
||||
"disable_ui", callback = "disappear_help",
|
||||
format = _("Click %(here)s to disable this feature."),
|
||||
format_arg = "here",
|
||||
hidden_data = dict(id="organic"))}
|
||||
%else:
|
||||
<p>
|
||||
%endif
|
||||
<%
|
||||
fmt = _("Click %(here)s to close help.")
|
||||
title = _("here")
|
||||
link = '<a class="close" href="javascript:void(0)">%s</a>' % title
|
||||
link = (fmt % dict(here = link))
|
||||
link = unsafe(link.replace(" <", " <").replace("> ", "> "))
|
||||
%>
|
||||
${link}
|
||||
%if not c.user_is_loggedin:
|
||||
</p>
|
||||
%endif
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
function disappear_help(elem) {
|
||||
$(elem).parents(".help:first").parent().html(
|
||||
"${_('The new link area will no longer appear for you.' +
|
||||
' To re-enable it, visit your preferences.')}");
|
||||
}
|
||||
$(function() {
|
||||
$(".help")
|
||||
.find("a.open").click(function() {
|
||||
$(this).parents(".organic-listing").hide()
|
||||
.siblings(".help").show();
|
||||
}).end()
|
||||
.find("a.close").click(function() {
|
||||
$(this).parent().hide().siblings(".organic-listing").show();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
${thing.css_class and ("class='%s'" % str(thing.css_class)) or ""}>
|
||||
%endif
|
||||
|
||||
${t.render()}
|
||||
${t.render() if t else ''}
|
||||
|
||||
%if thing.div:
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user