overhaul of JS and form handling code, not based on jQuery

This commit is contained in:
KeyserSosa
2009-01-26 15:11:23 -08:00
parent 7742fbfaab
commit 4440ccfc6e
128 changed files with 4857 additions and 3631 deletions

10
.gitignore vendored
View File

@@ -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
View 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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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):

View File

@@ -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()

View File

@@ -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

View File

@@ -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="/")))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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):

View File

@@ -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"

View File

@@ -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 ""

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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()

View 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' );
}

View File

@@ -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; }

View File

@@ -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

File diff suppressed because one or more lines are too long

1
r2/r2/public/static/js/jquery.js vendored Symbolic link
View File

@@ -0,0 +1 @@
jquery-1.2.6.js

View 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);

View File

@@ -0,0 +1 @@
jquery.json-1.3.js

View 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(/&gt;/g, ">")
.replace(/&lt;/g, "<").replace(/&amp;/g, "&")
.replace(/&quot;/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);

View 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");
});

View File

@@ -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);
};

View File

@@ -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>

View File

@@ -0,0 +1 @@
.rembeddit { border: 2px dashed gray; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View 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>
&nbsp;/&nbsp;
<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">&raquo;</b>
%else:
&nbsp;
%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>

View 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>

View File

@@ -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()}

View File

@@ -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(" ", "&#32;");
%>
${unsafe(label % dict(site = link))}
@@ -62,6 +67,7 @@
</p>
%endif
</div>
%endif
<div class="rembeddit-content" ${optionalstyle("padding:5px;")}>
${next.body()}
</div>

View File

@@ -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>

View File

@@ -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(" ", "&nbsp;")
%>
${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";

View File

@@ -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:
&nbsp;<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:
&nbsp;<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:
&nbsp;<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

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>&#32;
%endif
${unsafe(self.author(friend=thing.friend, gray = collapse))}&#32;
%else:
<em>${_("comment deleted")}</em>&#32;
<em>${_("[deleted]")}</em>&#32;
%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))}&#32;
%if show:
${unsafe(self.score(thing, likes = thing.likes))}&#32;
%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>

View File

@@ -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>

View File

@@ -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>
&nbsp;|<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>

View File

@@ -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 ''}

View File

@@ -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>

View File

@@ -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

View File

@@ -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)}
&#32;
<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>
&#32;
<span id="status" class="error"></span>
<span class="status error"></span>
${error_field("RATELIMIT", "span")}
</div>
</div>

View File

@@ -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

View File

@@ -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

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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);
};

View File

@@ -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">&bull;</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>

View File

@@ -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)}
|&#32;
${score(thing, thing.likes, inline = True)}
&#32;|&#32;
%endif
<a class="reddit-comment-link"
${optionalstyle("color:gray")}

View File

@@ -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)}

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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')}: &nbsp;<i>(${_('optional')})</i></label>
<input value="" name="email_${op}" id="email_${op}"
<label for="email_${op}">
${_('email')}: &nbsp;<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>

View File

@@ -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>

View File

@@ -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 '')}>

View File

@@ -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>

View File

@@ -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">&nbsp;(${thing.count} ${ungettext("reply", "replies", thing.count)})</span>
</a>
</span>

View File

@@ -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}:&#32;</span>
%endif
<ul class="flat-list hover"
${"id='%s'" % thing._id if thing._id else ""}>
%for i, option in enumerate(thing):

View File

@@ -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>

View File

@@ -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(" <", "&#32;<").replace("> ", ">&#32;"))
%>
${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>

View File

@@ -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