From bdf1cc4f9dd3f0ac06ba1af3c7d35c72cc358297 Mon Sep 17 00:00:00 2001
From: Evgeny Fadeev
Date: Mon, 8 Feb 2010 19:13:04 -0500
Subject: fixed a little bug that i have planted into fbconnect before, removed
unused files fixed email signup form validation
---
fbconnect/fb.py | 9 +-
fbconnect/urls.py | 5 +-
fbconnect/views.py | 33 +-
locale/en/LC_MESSAGES/django.mo | Bin 26619 -> 26986 bytes
locale/en/LC_MESSAGES/django.po | 714 ++++++++++++++++-----------------
session_messages/__init__.py | 37 --
session_messages/context_processors.py | 48 ---
session_messages/models.py | 3 -
templates/authopenid/complete.html | 3 +
templates/authopenid/signin.html | 2 +-
templates/authopenid/signup.html | 6 +-
templates/content/style/style.css | 3 +
templates/fbconnect/xd_receiver.html | 11 +-
13 files changed, 409 insertions(+), 465 deletions(-)
delete mode 100644 session_messages/__init__.py
delete mode 100644 session_messages/context_processors.py
delete mode 100644 session_messages/models.py
diff --git a/fbconnect/fb.py b/fbconnect/fb.py
index 99bc0b79..afcd8210 100755
--- a/fbconnect/fb.py
+++ b/fbconnect/fb.py
@@ -73,17 +73,24 @@ STATES = {
def get_user_state(request):
API_KEY = settings.FB_API_KEY
+ logging.debug('')
if API_KEY in request.COOKIES:
+ logging.debug('FB API key is in request cookies')
if check_cookies_signature(request.COOKIES):
+ logging.debug('FB cookie signature is fine')
if check_session_expiry(request.COOKIES):
+ logging.debug('FB session is not expired')
try:
uassoc = FBAssociation.objects.get(fbuid=request.COOKIES[API_KEY + '_user'])
+ logging.debug('found existing FB user association')
return (STATES['RETURNINGUSER'], uassoc.user)
except:
+ logging.debug('dont have FB association for this user')
return (STATES['FIRSTTIMER'], get_user_data(request.COOKIES))
else:
+ logging.debug('FB session expired')
return (STATES['SESSIONEXPIRED'], None)
+ logging.debug('FB state is INVALID')
return (STATES['INVALIDSTATE'], None)
-
diff --git a/fbconnect/urls.py b/fbconnect/urls.py
index 9b4ff0c9..bf2d4364 100755
--- a/fbconnect/urls.py
+++ b/fbconnect/urls.py
@@ -1,10 +1,13 @@
from django.conf.urls.defaults import *
from django.utils.translation import ugettext as _
from django.views.generic.simple import direct_to_template
+from django.conf import settings
from views import signin, register
urlpatterns = patterns('',
- url(r'^xd_receiver$', direct_to_template, {'template': 'fbconnect/xd_receiver.html'}, name='xd_receiver'),
+ url(r'^xd_receiver$', direct_to_template, {'template': 'fbconnect/xd_receiver.html',\
+ 'extra_context': {'APP_SHORT_NAME':settings.APP_SHORT_NAME}},\
+ name='xd_receiver'),
url(r'^%s$' % _('signin/'), signin, name="fb_signin"),
url(r'^%s%s$' % (_('signin/'), _('newquestion/')), signin, {'newquestion': True}, name="fb_signin_new_question"),
diff --git a/fbconnect/views.py b/fbconnect/views.py
index 5c308e45..1781f6bf 100755
--- a/fbconnect/views.py
+++ b/fbconnect/views.py
@@ -6,7 +6,7 @@ from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.contrib.auth import login, logout
from models import FBAssociation
-from forum.forms import EditUserEmailFeedsForm
+from forum.forms import SimpleEmailSubscribeForm
from django.conf import settings
import fb
@@ -15,9 +15,11 @@ import forms
import logging
def signin(request, newquestion = False, newanswer = False):
+ logging.debug('')
state, context = fb.get_user_state(request)
if state == fb.STATES['FIRSTTIMER']:
+ logging.debug('FB state = FIRSTTIMER')
if newquestion:
register_url = 'fb_user_register_new_question'
elif newanswer:
@@ -26,8 +28,10 @@ def signin(request, newquestion = False, newanswer = False):
register_url = 'fb_user_register'
return HttpResponseRedirect(reverse(register_url))
elif state == fb.STATES['RETURNINGUSER']:
+ logging.debug('FB state = RETURNINGUSER')
return login_and_forward(request, context, newquestion, newanswer)
elif state == fb.STATES['SESSIONEXPIRED']:
+ logging.debug('FB state = SESSIONEXPIRED')
response = logout(request, next_page=reverse('index'))
fb.delete_cookies(response)
return response
@@ -35,36 +39,41 @@ def signin(request, newquestion = False, newanswer = False):
return HttpResponseRedirect(reverse('index'))
def register(request, newquestion = False, newanswer = False):
+ logging.debug('')
state, context = fb.get_user_state(request)
if state == fb.STATES['FIRSTTIMER']:
-
- if 'bnewaccount' in request.POST.keys():
+ logging.debug('FB FIRSTTIMER - try to register locally')
+ logging.debug('request method is %s' % request.method)
+ if request.method == 'POST' and 'bnewaccount' in request.POST:
form1 = forms.FBConnectRegisterForm(request.POST)
- email_feeds_form = EditUserEmailFeedsForm(request.POST)
+ email_feeds_form = SimpleEmailSubscribeForm(request.POST)
if (form1.is_valid() and email_feeds_form.is_valid()):
tmp_pwd = User.objects.make_random_password()
user_ = User.objects.create_user(form1.cleaned_data['username'],
form1.cleaned_data['email'], tmp_pwd)
-
+
user_.set_unusable_password()
+ logging.debug('created new internal user %s' % form1.cleaned_data['username'])
uassoc = FBAssociation(user=user_, fbuid=context['uid'])
uassoc.save()
+ logging.debug('created new user association')
email_feeds_form.save(user_)
return login_and_forward(request, user_, newquestion, newanswer)
+ else:
+ logging.debug('form user input is invalid')
else:
form1 = forms.FBConnectRegisterForm(initial={
'next': '/',
'username': context['name'],
'email': '',
})
-
- email_feeds_form = EditUserEmailFeedsForm()
-
+ email_feeds_form = SimpleEmailSubscribeForm()
+
return render('authopenid/complete.html', {
'form1': form1,
'email_feeds_form': email_feeds_form,
@@ -73,25 +82,31 @@ def register(request, newquestion = False, newanswer = False):
'gravatar_faq_url':reverse('faq') + '#gravatar',
}, context_instance=RequestContext(request))
else:
+ logging.debug('not a FIRSTTIMER --> redirect to index view')
return HttpResponseRedirect(reverse('index'))
def login_and_forward(request, user, newquestion = False, newanswer = False):
old_session = request.session.session_key
user.backend = "django.contrib.auth.backends.ModelBackend"
+ logging.debug('attached auth.backends.ModelBackend to this FB user')
login(request, user)
+ logging.debug('user logged in!')
from forum.models import user_logged_in
user_logged_in.send(user=user,session_key=old_session,sender=None)
+ logging.debug('user_logged_in signal sent')
if (newquestion):
from forum.models import Question
question = Question.objects.filter(author=user).order_by('-added_at')[0]
+ logging.debug('redirecting to newly posted question')
return HttpResponseRedirect(question.get_absolute_url())
if (newanswer):
from forum.models import Answer
answer = Answer.objects.filter(author=user).order_by('-added_at')[0]
+ logging.debug('redirecting to newly posted answer')
return HttpResponseRedirect(answer.get_absolute_url())
+ logging.debug('redirecting to front page')
return HttpResponseRedirect('/')
-
diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo
index ef3007a0..38120e49 100644
Binary files a/locale/en/LC_MESSAGES/django.mo and b/locale/en/LC_MESSAGES/django.mo differ
diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po
index ee40fa36..bfec60c0 100644
--- a/locale/en/LC_MESSAGES/django.po
+++ b/locale/en/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-12-09 08:54-0800\n"
+"POT-Creation-Date: 2010-02-08 18:43-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -16,297 +16,228 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: django_authopenid/forms.py:70
-msgid "choose a username"
-msgstr "Choose screen name"
-
-#: django_authopenid/forms.py:76
-msgid "user name is required"
-msgstr ""
-
-#: django_authopenid/forms.py:77
-msgid "sorry, this name is taken, please choose another"
-msgstr ""
-
-#: django_authopenid/forms.py:78
-msgid "sorry, this name is not allowed, please choose another"
-msgstr ""
-
-#: django_authopenid/forms.py:79
-msgid "sorry, there is no user with this name"
-msgstr ""
-
-#: django_authopenid/forms.py:80
-msgid "sorry, we have a serious error - user name is taken by several users"
-msgstr ""
-
-#: django_authopenid/forms.py:81
-msgid "user name can only consist of letters, empty space and underscore"
-msgstr ""
-
-#: django_authopenid/forms.py:116
-msgid "your email address"
-msgstr "Your email (never shared)"
-
-#: django_authopenid/forms.py:117
-msgid "email address is required"
-msgstr ""
-
-#: django_authopenid/forms.py:118
-msgid "please enter a valid email address"
-msgstr ""
-
-#: django_authopenid/forms.py:119
-msgid "this email is already used by someone else, please choose another"
-msgstr ""
-
-#: django_authopenid/forms.py:163 django_authopenid/views.py:118
+#: django_authopenid/forms.py:71 django_authopenid/views.py:118
msgid "i-names are not supported"
msgstr ""
-#: django_authopenid/forms.py:219
+#: django_authopenid/forms.py:134
msgid "Account with this name already exists on the forum"
msgstr ""
-#: django_authopenid/forms.py:220
+#: django_authopenid/forms.py:135
msgid "can't have two logins to the same account yet, sorry."
msgstr ""
-#: django_authopenid/forms.py:242
+#: django_authopenid/forms.py:157
msgid "Please enter valid username and password (both are case-sensitive)."
msgstr ""
-#: django_authopenid/forms.py:245 django_authopenid/forms.py:295
+#: django_authopenid/forms.py:160 django_authopenid/forms.py:210
msgid "This account is inactive."
msgstr ""
-#: django_authopenid/forms.py:247
+#: django_authopenid/forms.py:162
msgid "Login failed."
msgstr ""
-#: django_authopenid/forms.py:249
+#: django_authopenid/forms.py:164
msgid "Please enter username and password"
msgstr ""
-#: django_authopenid/forms.py:251
+#: django_authopenid/forms.py:166
msgid "Please enter your password"
msgstr ""
-#: django_authopenid/forms.py:253
+#: django_authopenid/forms.py:168
msgid "Please enter user name"
msgstr ""
-#: django_authopenid/forms.py:291
+#: django_authopenid/forms.py:206
msgid ""
"Please enter a valid username and password. Note that "
"both fields are case-sensitive."
msgstr ""
-#: django_authopenid/forms.py:313
-msgid "choose password"
-msgstr "Password"
-
-#: django_authopenid/forms.py:314
-msgid "password is required"
-msgstr ""
-
-#: django_authopenid/forms.py:317
-msgid "retype password"
-msgstr "Password (please retype)"
-
-#: django_authopenid/forms.py:318
-msgid "please, retype your password"
-msgstr ""
-
-#: django_authopenid/forms.py:319
-msgid "sorry, entered passwords did not match, please try again"
-msgstr ""
-
-#: django_authopenid/forms.py:344
+#: django_authopenid/forms.py:229
msgid "Current password"
msgstr ""
-#: django_authopenid/forms.py:346
-msgid "New password"
-msgstr ""
-
-#: django_authopenid/forms.py:348
-msgid "Retype new password"
-msgstr ""
-
-#: django_authopenid/forms.py:359
+#: django_authopenid/forms.py:240
msgid ""
"Old password is incorrect. Please enter the correct "
"password."
msgstr ""
-#: django_authopenid/forms.py:371
-msgid "new passwords do not match"
-msgstr ""
-
-#: django_authopenid/forms.py:435
+#: django_authopenid/forms.py:305
msgid "Your user name (required)"
msgstr ""
-#: django_authopenid/forms.py:450
+#: django_authopenid/forms.py:320
msgid "Incorrect username."
msgstr "sorry, there is no such user name"
-#: django_authopenid/urls.py:9 django_authopenid/urls.py:10
-#: django_authopenid/urls.py:11 django_authopenid/urls.py:13 forum/urls.py:24
+#: django_authopenid/urls.py:23 django_authopenid/urls.py:24
+#: django_authopenid/urls.py:25 django_authopenid/urls.py:27
+#: fbconnect/urls.py:12 fbconnect/urls.py:13 fbconnect/urls.py:14
+#: forum/urls.py:29
msgid "signin/"
msgstr ""
-#: django_authopenid/urls.py:10
+#: django_authopenid/urls.py:24 fbconnect/urls.py:13 fbconnect/urls.py:17
msgid "newquestion/"
msgstr ""
-#: django_authopenid/urls.py:11
+#: django_authopenid/urls.py:25 fbconnect/urls.py:14 fbconnect/urls.py:18
msgid "newanswer/"
msgstr ""
-#: django_authopenid/urls.py:12
+#: django_authopenid/urls.py:26
msgid "signout/"
msgstr ""
-#: django_authopenid/urls.py:13
+#: django_authopenid/urls.py:27
msgid "complete/"
msgstr ""
-#: django_authopenid/urls.py:15
-msgid "external-login/"
-msgstr ""
-
-#: django_authopenid/urls.py:16
+#: django_authopenid/urls.py:29 fbconnect/urls.py:16 fbconnect/urls.py:17
+#: fbconnect/urls.py:18
msgid "register/"
msgstr ""
-#: django_authopenid/urls.py:17
+#: django_authopenid/urls.py:30
msgid "signup/"
msgstr ""
-#: django_authopenid/urls.py:19
+#: django_authopenid/urls.py:32
msgid "sendpw/"
msgstr ""
-#: django_authopenid/urls.py:20 django_authopenid/urls.py:24
+#: django_authopenid/urls.py:33 django_authopenid/urls.py:37
msgid "password/"
msgstr ""
-#: django_authopenid/urls.py:20
+#: django_authopenid/urls.py:33
msgid "confirm/"
msgstr ""
-#: django_authopenid/urls.py:23
+#: django_authopenid/urls.py:36
msgid "account_settings"
msgstr ""
-#: django_authopenid/urls.py:25 django_authopenid/urls.py:26
-#: django_authopenid/urls.py:27 django_authopenid/urls.py:28
+#: django_authopenid/urls.py:38 django_authopenid/urls.py:39
+#: django_authopenid/urls.py:40 django_authopenid/urls.py:41
msgid "email/"
msgstr ""
-#: django_authopenid/urls.py:25
+#: django_authopenid/urls.py:38
msgid "validate/"
msgstr ""
-#: django_authopenid/urls.py:26
+#: django_authopenid/urls.py:39
msgid "change/"
msgstr ""
-#: django_authopenid/urls.py:27
+#: django_authopenid/urls.py:40
msgid "sendkey/"
msgstr ""
-#: django_authopenid/urls.py:28
+#: django_authopenid/urls.py:41
msgid "verify/"
msgstr ""
-#: django_authopenid/urls.py:29
+#: django_authopenid/urls.py:42
msgid "openid/"
msgstr ""
-#: django_authopenid/urls.py:30 forum/urls.py:44 forum/urls.py:48
+#: django_authopenid/urls.py:43 forum/urls.py:49 forum/urls.py:53
msgid "delete/"
msgstr ""
-#: django_authopenid/views.py:124
+#: django_authopenid/urls.py:51
+msgid "external-login/forgot-password/"
+msgstr ""
+
+#: django_authopenid/urls.py:54
+msgid "external-login/signup/"
+msgstr ""
+
+#: django_authopenid/views.py:125
#, python-format
msgid "OpenID %(openid_url)s is invalid"
msgstr ""
-#: django_authopenid/views.py:532
+#: django_authopenid/views.py:593
msgid "Welcome email subject line"
msgstr "Welcome to the Q&A forum"
-#: django_authopenid/views.py:627
+#: django_authopenid/views.py:699
msgid "Password changed."
msgstr ""
-#: django_authopenid/views.py:639 django_authopenid/views.py:645
+#: django_authopenid/views.py:711 django_authopenid/views.py:717
#, python-format
msgid "your email needs to be validated see %(details_url)s"
msgstr ""
"Your email needs to be validated. Please see details here."
-#: django_authopenid/views.py:666
+#: django_authopenid/views.py:738
msgid "Email verification subject line"
msgstr "Verification Email from Q&A forum"
-#: django_authopenid/views.py:752
+#: django_authopenid/views.py:829
msgid "your email was not changed"
msgstr ""
-#: django_authopenid/views.py:799 django_authopenid/views.py:951
+#: django_authopenid/views.py:877 django_authopenid/views.py:1035
#, python-format
msgid "No OpenID %s found associated in our database"
msgstr ""
-#: django_authopenid/views.py:803 django_authopenid/views.py:958
+#: django_authopenid/views.py:881 django_authopenid/views.py:1042
#, python-format
msgid "The OpenID %s isn't associated to current user logged in"
msgstr ""
-#: django_authopenid/views.py:811
+#: django_authopenid/views.py:889
msgid "Email Changed."
msgstr ""
-#: django_authopenid/views.py:886
+#: django_authopenid/views.py:967
msgid "This OpenID is already associated with another account."
msgstr ""
-#: django_authopenid/views.py:891
+#: django_authopenid/views.py:972
#, python-format
msgid "OpenID %s is now associated with your account."
msgstr ""
-#: django_authopenid/views.py:961
+#: django_authopenid/views.py:1045
msgid "Account deleted."
msgstr ""
-#: django_authopenid/views.py:1004
+#: django_authopenid/views.py:1097
msgid "Request for new password"
msgstr ""
-#: django_authopenid/views.py:1017
+#: django_authopenid/views.py:1111
msgid "A new password and the activation link were sent to your email address."
msgstr ""
-#: django_authopenid/views.py:1047
+#: django_authopenid/views.py:1143
#, python-format
msgid ""
"Could not change password. Confirmation key '%s' is not "
"registered."
msgstr ""
-#: django_authopenid/views.py:1056
+#: django_authopenid/views.py:1153
msgid ""
"Can not change password. User don't exist anymore in our "
"database."
msgstr ""
-#: django_authopenid/views.py:1065
+#: django_authopenid/views.py:1163
#, python-format
msgid "Password changed for %s. You may now sign in."
msgstr ""
@@ -439,7 +370,7 @@ msgstr ""
msgid "[deleted]"
msgstr ""
-#: forum/const.py:87 forum/views.py:849 forum/views.py:868
+#: forum/const.py:87 forum/views.py:796 forum/views.py:815
msgid "initial version"
msgstr ""
@@ -452,7 +383,7 @@ msgid "exclude ignored tags"
msgstr ""
#: forum/const.py:92
-msgid "allow only interesting tags"
+msgid "allow only selected tags"
msgstr ""
#: forum/feed.py:18
@@ -463,373 +394,384 @@ msgstr ""
msgid "latest questions"
msgstr ""
-#: forum/feed.py:19 forum/urls.py:52
-msgid "question/"
-msgstr ""
-
-#: forum/forms.py:16 templates/answer_edit_tips.html:35
+#: forum/forms.py:18 templates/answer_edit_tips.html:35
#: templates/answer_edit_tips.html.py:39 templates/question_edit_tips.html:32
#: templates/question_edit_tips.html:37
msgid "title"
msgstr ""
-#: forum/forms.py:17
+#: forum/forms.py:19
msgid "please enter a descriptive title for your question"
msgstr ""
-#: forum/forms.py:22
+#: forum/forms.py:24
msgid "title must be > 10 characters"
msgstr ""
-#: forum/forms.py:31
+#: forum/forms.py:33
msgid "content"
msgstr ""
-#: forum/forms.py:37
+#: forum/forms.py:39
msgid "question content must be > 10 characters"
msgstr ""
-#: forum/forms.py:47 templates/header.html:28 templates/header.html.py:62
+#: forum/forms.py:49 templates/header.html:28 templates/header.html.py:56
msgid "tags"
msgstr ""
-#: forum/forms.py:49
+#: forum/forms.py:51
msgid ""
"Tags are short keywords, with no spaces within. Up to five tags can be used."
msgstr ""
-#: forum/forms.py:56 templates/question_retag.html:39
+#: forum/forms.py:58 templates/question_retag.html:39
msgid "tags are required"
msgstr ""
-#: forum/forms.py:62
+#: forum/forms.py:64
msgid "please use 5 tags or less"
msgstr ""
-#: forum/forms.py:65
+#: forum/forms.py:67
msgid "tags must be shorter than 20 characters"
msgstr ""
-#: forum/forms.py:69
+#: forum/forms.py:71
msgid ""
"please use following characters in tags: letters 'a-z', numbers, and "
"characters '.-_#'"
msgstr ""
-#: forum/forms.py:79 templates/index.html:61 templates/index.html.py:73
+#: forum/forms.py:81 templates/index.html:61 templates/index.html.py:73
#: templates/post_contributor_info.html:7
#: templates/question_summary_list_roll.html:26
#: templates/question_summary_list_roll.html:38 templates/questions.html:92
-#: templates/questions.html.py:104 templates/unanswered.html:51
-#: templates/unanswered.html.py:63
+#: templates/questions.html.py:104
msgid "community wiki"
msgstr ""
-#: forum/forms.py:80
+#: forum/forms.py:82
msgid ""
"if you choose community wiki option, the question and answer do not generate "
"points and name of author will not be shown"
msgstr ""
-#: forum/forms.py:96
+#: forum/forms.py:98
msgid "update summary:"
msgstr ""
-#: forum/forms.py:97
+#: forum/forms.py:99
msgid ""
"enter a brief summary of your revision (e.g. fixed spelling, grammar, "
"improved style, this field is optional)"
msgstr ""
-#: forum/forms.py:100
+#: forum/forms.py:102
msgid "Automatically accept user's contributions for the email updates"
msgstr ""
-#: forum/forms.py:113
+#: forum/forms.py:118
msgid "Your name:"
msgstr ""
-#: forum/forms.py:114
+#: forum/forms.py:119
msgid "Email (not shared with anyone):"
msgstr ""
-#: forum/forms.py:115
+#: forum/forms.py:120
msgid "Your message:"
msgstr ""
-#: forum/forms.py:197
+#: forum/forms.py:202
msgid "this email does not have to be linked to gravatar"
msgstr ""
-#: forum/forms.py:198
+#: forum/forms.py:204
msgid "Screen name"
msgstr ""
-#: forum/forms.py:199
+#: forum/forms.py:205
msgid "Real name"
msgstr ""
-#: forum/forms.py:200
+#: forum/forms.py:206
msgid "Website"
msgstr ""
-#: forum/forms.py:201
+#: forum/forms.py:207
msgid "Location"
msgstr ""
-#: forum/forms.py:202
+#: forum/forms.py:208
msgid "Date of birth"
msgstr ""
-#: forum/forms.py:202
+#: forum/forms.py:208
msgid "will not be shown, used to calculate age, format: YYYY-MM-DD"
msgstr ""
-#: forum/forms.py:203 templates/authopenid/settings.html:21
+#: forum/forms.py:209 templates/authopenid/settings.html:21
msgid "Profile"
msgstr ""
-#: forum/forms.py:231 forum/forms.py:232
+#: forum/forms.py:240 forum/forms.py:241
msgid "this email has already been registered, please use another one"
msgstr ""
-#: forum/forms.py:238
+#: forum/forms.py:247
msgid "Choose email tag filter"
msgstr ""
-#: forum/forms.py:245 forum/forms.py:246
+#: forum/forms.py:262 forum/forms.py:263
msgid "weekly"
msgstr ""
-#: forum/forms.py:245 forum/forms.py:246
+#: forum/forms.py:262 forum/forms.py:263
msgid "no email"
msgstr ""
-#: forum/forms.py:246
+#: forum/forms.py:263
msgid "daily"
msgstr ""
-#: forum/forms.py:261
+#: forum/forms.py:278
msgid "Asked by me"
msgstr ""
-#: forum/forms.py:264
+#: forum/forms.py:281
msgid "Answered by me"
msgstr ""
-#: forum/forms.py:267
+#: forum/forms.py:284
msgid "Individually selected"
msgstr ""
-#: forum/forms.py:270
+#: forum/forms.py:287
msgid "Entire forum (tag filtered)"
msgstr ""
-#: forum/models.py:51
-msgid "Entire forum"
+#: forum/forms.py:341
+msgid "okay, let's try!"
+msgstr ""
+
+#: forum/forms.py:342
+msgid "no OSQA community email please, thanks"
+msgstr ""
+
+#: forum/forms.py:345
+msgid "please choose one of the options above"
msgstr ""
#: forum/models.py:52
-msgid "Questions that I asked"
+msgid "Entire forum"
msgstr ""
#: forum/models.py:53
-msgid "Questions that I answered"
+msgid "Questions that I asked"
msgstr ""
#: forum/models.py:54
+msgid "Questions that I answered"
+msgstr ""
+
+#: forum/models.py:55
msgid "Individually selected questions"
msgstr ""
-#: forum/models.py:57
+#: forum/models.py:58
msgid "Weekly"
msgstr ""
-#: forum/models.py:58
+#: forum/models.py:59
msgid "Daily"
msgstr ""
-#: forum/models.py:59
+#: forum/models.py:60
msgid "No email"
msgstr ""
-#: forum/models.py:301
+#: forum/models.py:321
#, python-format
msgid "%(author)s modified the question"
msgstr ""
-#: forum/models.py:305
+#: forum/models.py:325
#, python-format
msgid "%(people)s posted %(new_answer_count)s new answers"
msgstr ""
-#: forum/models.py:310
+#: forum/models.py:330
#, python-format
msgid "%(people)s commented the question"
msgstr ""
-#: forum/models.py:315
+#: forum/models.py:335
#, python-format
msgid "%(people)s commented answers"
msgstr ""
-#: forum/models.py:317
+#: forum/models.py:337
#, python-format
msgid "%(people)s commented an answer"
msgstr ""
-#: forum/models.py:348
+#: forum/models.py:368
msgid "interesting"
msgstr ""
-#: forum/models.py:348
+#: forum/models.py:368
msgid "ignored"
msgstr ""
-#: forum/models.py:511 templates/badges.html:53
+#: forum/models.py:541 templates/badges.html:53
msgid "gold"
msgstr ""
-#: forum/models.py:512 templates/badges.html:61
+#: forum/models.py:542 templates/badges.html:61
msgid "silver"
msgstr ""
-#: forum/models.py:513 templates/badges.html:68
+#: forum/models.py:543 templates/badges.html:68
msgid "bronze"
msgstr ""
-#: forum/urls.py:21
+#: forum/urls.py:26
msgid "upfiles/"
msgstr ""
-#: forum/urls.py:25
+#: forum/urls.py:30
msgid "about/"
msgstr ""
-#: forum/urls.py:26
+#: forum/urls.py:31
msgid "faq/"
msgstr ""
-#: forum/urls.py:27
+#: forum/urls.py:32
msgid "privacy/"
msgstr ""
-#: forum/urls.py:28
+#: forum/urls.py:33
msgid "logout/"
msgstr ""
-#: forum/urls.py:29 forum/urls.py:30 forum/urls.py:31 forum/urls.py:48
+#: forum/urls.py:34 forum/urls.py:35 forum/urls.py:36 forum/urls.py:53
msgid "answers/"
msgstr ""
-#: forum/urls.py:29 forum/urls.py:41 forum/urls.py:44 forum/urls.py:48
+#: forum/urls.py:34 forum/urls.py:46 forum/urls.py:49 forum/urls.py:53
msgid "comments/"
msgstr ""
-#: forum/urls.py:30 forum/urls.py:35 forum/urls.py:70
+#: forum/urls.py:35 forum/urls.py:40 forum/urls.py:75
#: templates/user_info.html:45
msgid "edit/"
msgstr ""
-#: forum/urls.py:31 forum/urls.py:40
+#: forum/urls.py:36 forum/urls.py:45
msgid "revisions/"
msgstr ""
-#: forum/urls.py:32 forum/urls.py:33 forum/urls.py:34 forum/urls.py:35
-#: forum/urls.py:36 forum/urls.py:37 forum/urls.py:38 forum/urls.py:39
-#: forum/urls.py:40 forum/urls.py:41 forum/urls.py:44
+#: forum/urls.py:37 forum/urls.py:38 forum/urls.py:39 forum/urls.py:40
+#: forum/urls.py:41 forum/urls.py:42 forum/urls.py:43 forum/urls.py:44
+#: forum/urls.py:45 forum/urls.py:46 forum/urls.py:49
msgid "questions/"
msgstr ""
-#: forum/urls.py:33 forum/urls.py:80
+#: forum/urls.py:38 forum/urls.py:85
msgid "ask/"
msgstr ""
-#: forum/urls.py:34
+#: forum/urls.py:39
msgid "unanswered/"
msgstr ""
-#: forum/urls.py:36
+#: forum/urls.py:41
msgid "close/"
msgstr ""
-#: forum/urls.py:37
+#: forum/urls.py:42
msgid "reopen/"
msgstr ""
-#: forum/urls.py:38
+#: forum/urls.py:43
msgid "answer/"
msgstr ""
-#: forum/urls.py:39
+#: forum/urls.py:44
msgid "vote/"
msgstr ""
-#: forum/urls.py:42
+#: forum/urls.py:47
msgid "command/"
msgstr ""
-#: forum/urls.py:53 forum/urls.py:54
+#: forum/urls.py:57 forum/views.py:440
+msgid "question/"
+msgstr ""
+
+#: forum/urls.py:58 forum/urls.py:59
msgid "tags/"
msgstr ""
-#: forum/urls.py:56 forum/urls.py:60
+#: forum/urls.py:61 forum/urls.py:65
msgid "mark-tag/"
msgstr ""
-#: forum/urls.py:56
+#: forum/urls.py:61
msgid "interesting/"
msgstr ""
-#: forum/urls.py:60
+#: forum/urls.py:65
msgid "ignored/"
msgstr ""
-#: forum/urls.py:64
+#: forum/urls.py:69
msgid "unmark-tag/"
msgstr ""
-#: forum/urls.py:68 forum/urls.py:70 forum/urls.py:71
+#: forum/urls.py:73 forum/urls.py:75 forum/urls.py:76
msgid "users/"
msgstr ""
-#: forum/urls.py:69
+#: forum/urls.py:74
msgid "moderate-user/"
msgstr ""
-#: forum/urls.py:72 forum/urls.py:73
+#: forum/urls.py:77 forum/urls.py:78
msgid "badges/"
msgstr ""
-#: forum/urls.py:74
+#: forum/urls.py:79
msgid "messages/"
msgstr ""
-#: forum/urls.py:74
+#: forum/urls.py:79
msgid "markread/"
msgstr ""
-#: forum/urls.py:76
+#: forum/urls.py:81
msgid "nimda/"
msgstr ""
-#: forum/urls.py:78
+#: forum/urls.py:83
msgid "upload/"
msgstr ""
-#: forum/urls.py:79 forum/urls.py:80 forum/urls.py:81
+#: forum/urls.py:84 forum/urls.py:85 forum/urls.py:86
msgid "books/"
msgstr ""
-#: forum/urls.py:82
+#: forum/urls.py:87
msgid "search/"
msgstr ""
-#: forum/urls.py:83
+#: forum/urls.py:88
msgid "feedback/"
msgstr ""
-#: forum/urls.py:84
+#: forum/urls.py:89 forum/urls.py:90
msgid "account/"
msgstr ""
@@ -929,49 +871,49 @@ msgstr ""
msgid "We look forward to hearing your feedback! Please, give it next time :)"
msgstr ""
-#: forum/views.py:1150
+#: forum/views.py:1098
#, python-format
msgid "subscription saved, %(email)s needs validation, see %(details_url)s"
msgstr ""
"Your subscription is saved, but email address %(email)s needs to be "
"validated, please see more details here"
-#: forum/views.py:1158
+#: forum/views.py:1106
msgid "email update frequency has been set to daily"
msgstr ""
-#: forum/views.py:2032
+#: forum/views.py:1982 forum/views.py:1986
msgid "changes saved"
msgstr ""
-#: forum/views.py:2038
+#: forum/views.py:1992
msgid "email updates canceled"
msgstr ""
-#: forum/views.py:2207
+#: forum/views.py:2159
msgid "uploading images is limited to users with >60 reputation points"
msgstr "sorry, file uploading requires karma >60"
-#: forum/views.py:2209
+#: forum/views.py:2161
msgid "allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"
msgstr ""
-#: forum/views.py:2211
+#: forum/views.py:2163
#, python-format
msgid "maximum upload file size is %sK"
msgstr ""
-#: forum/views.py:2213
+#: forum/views.py:2165
#, python-format
msgid ""
"Error uploading file. Please contact the site administrator. Thank you. %s"
msgstr ""
-#: forum/management/commands/send_email_alerts.py:160
+#: forum/management/commands/send_email_alerts.py:156
msgid "email update message subject"
msgstr "news from Q&A forum"
-#: forum/management/commands/send_email_alerts.py:161
+#: forum/management/commands/send_email_alerts.py:158
#, python-format
msgid "%(name)s, this is an update message header for a question"
msgid_plural "%(name)s, this is an update message header for %(num)d questions"
@@ -982,11 +924,11 @@ msgstr[1] ""
"
Dear %(name)s,
The following %(num)d questions have been updated on "
"the Q&A forum:
"
-#: forum/management/commands/send_email_alerts.py:172
+#: forum/management/commands/send_email_alerts.py:169
msgid "new question"
msgstr ""
-#: forum/management/commands/send_email_alerts.py:182
+#: forum/management/commands/send_email_alerts.py:179
#, python-format
msgid "There is also one question which was recently "
msgid_plural ""
@@ -994,12 +936,12 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: forum/management/commands/send_email_alerts.py:187
+#: forum/management/commands/send_email_alerts.py:184
msgid ""
"Perhaps you could look up previously sent forum reminders in your mailbox."
msgstr ""
-#: forum/management/commands/send_email_alerts.py:191
+#: forum/management/commands/send_email_alerts.py:188
#, python-format
msgid ""
"go to %(link)s to change frequency of email updates or %(email)s "
@@ -1011,39 +953,31 @@ msgstr ""
"administrator at %(email)s.
Sincerely,
Your friendly Q&A forum "
"server.
"
-#: forum/templatetags/extra_tags.py:163 forum/templatetags/extra_tags.py:192
+#: forum/templatetags/extra_tags.py:164 forum/templatetags/extra_tags.py:193
#: templates/header.html:33
msgid "badges"
msgstr ""
-#: forum/templatetags/extra_tags.py:164 forum/templatetags/extra_tags.py:191
+#: forum/templatetags/extra_tags.py:165 forum/templatetags/extra_tags.py:192
msgid "reputation points"
msgstr "karma"
-#: forum/templatetags/extra_tags.py:247
-msgid "%b %d at %H:%M"
-msgstr ""
-
-#: forum/templatetags/extra_tags.py:249
-msgid "%b %d '%y at %H:%M"
-msgstr ""
-
-#: forum/templatetags/extra_tags.py:251
+#: forum/templatetags/extra_tags.py:252
msgid "2 days ago"
msgstr ""
-#: forum/templatetags/extra_tags.py:253
+#: forum/templatetags/extra_tags.py:254
msgid "yesterday"
msgstr ""
-#: forum/templatetags/extra_tags.py:255
+#: forum/templatetags/extra_tags.py:256
#, python-format
msgid "%(hr)d hour ago"
msgid_plural "%(hr)d hours ago"
msgstr[0] ""
msgstr[1] ""
-#: forum/templatetags/extra_tags.py:257
+#: forum/templatetags/extra_tags.py:258
#, python-format
msgid "%(min)d min ago"
msgid_plural "%(min)d mins ago"
@@ -1121,38 +1055,6 @@ msgstr ""
msgid "About"
msgstr ""
-#: templates/about.html:21
-msgid ""
-"CNPROG Q&A is a "
-"collaboratively edited question\n"
-" and answer site created for the CNPROG "
-"community.\n"
-" "
-msgstr ""
-
-#: templates/about.html:25
-msgid ""
-"Here you can ask and answer questions, "
-"comment\n"
-" and vote for the questions of others and their answers. "
-"Both questions and answers\n"
-" can be revised and improved. Questions can be "
-"tagged with\n"
-" the relevant keywords to simplify future access and organize the "
-"accumulated material."
-msgstr ""
-
-#: templates/about.html:31
-msgid ""
-"This Q&A site is moderated by its members, "
-"hopefully - including yourself!\n"
-" Moderation rights are gradually assigned to the site users based on the "
-"accumulated \"reputation\"\n"
-" points. These points are added to the users account when others vote for "
-"his/her questions or answers.\n"
-" These points (very) roughly reflect the level of trust of the community."
-msgstr ""
-
#: templates/answer_edit.html:5 templates/answer_edit.html.py:48
msgid "Edit answer"
msgstr ""
@@ -1436,7 +1338,7 @@ msgstr ""
#: templates/book.html:105 templates/index.html:49
#: templates/question_summary_list_roll.html:14 templates/questions.html:80
-#: templates/unanswered.html:39 templates/users_questions.html:32
+#: templates/users_questions.html:32
msgid "votes"
msgstr ""
@@ -1446,14 +1348,13 @@ msgstr ""
#: templates/book.html:115 templates/index.html:50
#: templates/question_summary_list_roll.html:15 templates/questions.html:81
-#: templates/unanswered.html:40 templates/users_questions.html:40
+#: templates/users_questions.html:40
msgid "views"
msgstr ""
#: templates/book.html:125 templates/index.html:105
#: templates/question.html:480 templates/question_summary_list_roll.html:52
#: templates/questions.html:136 templates/tags.html:49
-#: templates/unanswered.html:95 templates/unanswered.html.py:122
#: templates/users_questions.html:52
msgid "using tags"
msgstr ""
@@ -1699,7 +1600,7 @@ msgstr ""
"Please ask your question, help make our "
"community better!"
-#: templates/faq.html:128 templates/header.html:27 templates/header.html.py:61
+#: templates/faq.html:128 templates/header.html:27 templates/header.html.py:55
msgid "questions"
msgstr ""
@@ -1775,18 +1676,10 @@ msgid "faq"
msgstr ""
#: templates/footer.html:10
-msgid "blog"
-msgstr ""
-
-#: templates/footer.html:11
-msgid "contact us"
-msgstr ""
-
-#: templates/footer.html:12
msgid "privacy policy"
msgstr ""
-#: templates/footer.html:21
+#: templates/footer.html:19
msgid "give feedback"
msgstr ""
@@ -1802,7 +1695,7 @@ msgstr ""
msgid "back to home page"
msgstr ""
-#: templates/header.html:29 templates/header.html.py:63
+#: templates/header.html:29 templates/header.html.py:57
msgid "users"
msgstr ""
@@ -1814,15 +1707,11 @@ msgstr ""
msgid "unanswered questions"
msgstr "unanswered"
-#: templates/header.html:38
-msgid "my profile"
-msgstr ""
-
-#: templates/header.html:42
+#: templates/header.html:36
msgid "ask a question"
msgstr ""
-#: templates/header.html:57
+#: templates/header.html:51
msgid "search"
msgstr ""
@@ -1839,7 +1728,6 @@ msgid "last updated questions"
msgstr ""
#: templates/index.html:27 templates/questions.html:47
-#: templates/unanswered.html:21
msgid "newest"
msgstr ""
@@ -1864,27 +1752,23 @@ msgid "all questions"
msgstr ""
#: templates/index.html:48 templates/question_summary_list_roll.html:13
-#: templates/questions.html:79 templates/unanswered.html:38
-#: templates/users_questions.html:36
+#: templates/questions.html:79 templates/users_questions.html:36
msgid "answers"
msgstr ""
#: templates/index.html:80 templates/index.html.py:94
#: templates/questions.html:111 templates/questions.html.py:125
-#: templates/unanswered.html:70 templates/unanswered.html.py:84
msgid "Posted:"
msgstr ""
#: templates/index.html:83 templates/index.html.py:88
#: templates/questions.html:114 templates/questions.html.py:119
-#: templates/unanswered.html:73 templates/unanswered.html.py:78
msgid "Updated:"
msgstr ""
#: templates/index.html:105 templates/question.html:480
#: templates/question_summary_list_roll.html:52 templates/questions.html:136
-#: templates/tags.html:49 templates/unanswered.html:95
-#: templates/unanswered.html.py:122 templates/users_questions.html:52
+#: templates/tags.html:49 templates/users_questions.html:52
msgid "see questions tagged"
msgstr ""
@@ -1929,7 +1813,7 @@ msgstr ""
msgid "complete list of questions"
msgstr "list of all questions"
-#: templates/index.html:161 templates/authopenid/signup.html:18
+#: templates/index.html:161 templates/authopenid/signup.html:26
msgid "or"
msgstr ""
@@ -1958,6 +1842,14 @@ msgstr ""
msgid "Logout now"
msgstr "Logout Now"
+#: templates/notarobot.html:3
+msgid "Please prove that you are a Human Being"
+msgstr ""
+
+#: templates/notarobot.html:10
+msgid "I am a Human Being"
+msgstr ""
+
#: templates/pagesize.html:6
msgid "posts per page"
msgstr ""
@@ -2414,8 +2306,7 @@ msgstr ""
msgid "Found by title"
msgstr ""
-#: templates/questions.html:39 templates/unanswered.html:8
-#: templates/unanswered.html.py:19
+#: templates/questions.html:39
msgid "Unanswered questions"
msgstr ""
@@ -2423,7 +2314,7 @@ msgstr ""
msgid "All questions"
msgstr ""
-#: templates/questions.html:47 templates/unanswered.html:21
+#: templates/questions.html:47
msgid "most recently asked questions"
msgstr ""
@@ -2480,14 +2371,12 @@ msgid_plural ""
" "
msgstr[0] ""
"\n"
-"
You are here for the first time with your "
+"Facebook login. Please create your screen name "
+"and save your email address. Saved email address will let "
+"you subscribe for the updates on the most interesting "
+"questions and will be used to create and retrieve your unique avatar image - "
+"gravatar.
"
+
+#: templates/authopenid/complete.html:42
msgid "This account already exists, please use another."
msgstr ""
-#: templates/authopenid/complete.html:55
+#: templates/authopenid/complete.html:57
msgid "Sorry, looks like we have some errors:"
msgstr ""
-#: templates/authopenid/complete.html:76
+#: templates/authopenid/complete.html:82
msgid "Screen name label"
msgstr "Screen Name (will be shown to others)"
-#: templates/authopenid/complete.html:83
+#: templates/authopenid/complete.html:89
msgid "Email address label"
msgstr ""
"Email Address (will not be shared with "
"anyone, must be valid)"
-#: templates/authopenid/complete.html:89 templates/authopenid/signup.html:15
+#: templates/authopenid/complete.html:95 templates/authopenid/signup.html:18
msgid "receive updates motivational blurb"
msgstr ""
"Receive forum updates by email - this will help our "
@@ -3174,31 +3071,31 @@ msgstr ""
"week - only when there is anything new. If you like, please "
"adjust this now or any time later from your user account."
-#: templates/authopenid/complete.html:91
+#: templates/authopenid/complete.html:99
msgid "Tag filter tool will be your right panel, once you log in."
msgstr ""
-#: templates/authopenid/complete.html:92
+#: templates/authopenid/complete.html:100
msgid "create account"
msgstr "Signup"
-#: templates/authopenid/complete.html:101
+#: templates/authopenid/complete.html:109
msgid "Existing account"
msgstr ""
-#: templates/authopenid/complete.html:102
+#: templates/authopenid/complete.html:110
msgid "user name"
msgstr ""
-#: templates/authopenid/complete.html:103
+#: templates/authopenid/complete.html:111
msgid "password"
msgstr ""
-#: templates/authopenid/complete.html:108
+#: templates/authopenid/complete.html:118
msgid "Register"
msgstr ""
-#: templates/authopenid/complete.html:109 templates/authopenid/signin.html:140
+#: templates/authopenid/complete.html:119 templates/authopenid/signin.html:151
msgid "Forgot your password?"
msgstr ""
@@ -3292,7 +3189,10 @@ msgid "Traditional login information"
msgstr ""
#: templates/authopenid/external_legacy_login_info.html:12
-msgid "how to login with password through external login website"
+#, python-format
+msgid ""
+"how to login with password through external login website or use %"
+"(feedback_url)s"
msgstr ""
#: templates/authopenid/sendpw.html:4 templates/authopenid/sendpw.html.py:7
@@ -3397,14 +3297,14 @@ msgstr ""
"have to remember another one. CNPROG option requires your login name and "
"password entered here."
-#: templates/authopenid/signin.html:117
+#: templates/authopenid/signin.html:128
msgid "Enter your Provider user name"
msgstr ""
"Enter your Provider user name (or "
"select another login method above)"
-#: templates/authopenid/signin.html:124
+#: templates/authopenid/signin.html:135
msgid ""
"Enter your OpenID "
"web address"
@@ -3413,55 +3313,55 @@ msgstr ""
"openid.net\">OpenID web address (or choose "
"another login method above)"
-#: templates/authopenid/signin.html:126 templates/authopenid/signin.html:138
+#: templates/authopenid/signin.html:137 templates/authopenid/signin.html:149
msgid "Login"
msgstr ""
-#: templates/authopenid/signin.html:129
+#: templates/authopenid/signin.html:140
msgid "Enter your login name and password"
msgstr ""
"Enter your CNPROG login and password (or select your OpenID provider above)"
-#: templates/authopenid/signin.html:133
+#: templates/authopenid/signin.html:144
msgid "Login name"
msgstr ""
-#: templates/authopenid/signin.html:135
+#: templates/authopenid/signin.html:146
msgid "Password"
msgstr ""
-#: templates/authopenid/signin.html:139
+#: templates/authopenid/signin.html:150
msgid "Create account"
msgstr ""
-#: templates/authopenid/signin.html:149
+#: templates/authopenid/signin.html:160
msgid "Why use OpenID?"
msgstr ""
-#: templates/authopenid/signin.html:152
+#: templates/authopenid/signin.html:163
msgid "with openid it is easier"
msgstr "With the OpenID you don't need to create new username and password."
-#: templates/authopenid/signin.html:155
+#: templates/authopenid/signin.html:166
msgid "reuse openid"
msgstr "You can safely re-use the same login for all OpenID-enabled websites."
-#: templates/authopenid/signin.html:158
+#: templates/authopenid/signin.html:169
msgid "openid is widely adopted"
msgstr ""
"There are > 160,000,000 OpenID account in use. Over 10,000 sites are OpenID-"
"enabled."
-#: templates/authopenid/signin.html:161
+#: templates/authopenid/signin.html:172
msgid "openid is supported open standard"
msgstr "OpenID is based on an open standard, supported by many organizations."
-#: templates/authopenid/signin.html:166
+#: templates/authopenid/signin.html:177
msgid "Find out more"
msgstr ""
-#: templates/authopenid/signin.html:167
+#: templates/authopenid/signin.html:178
msgid "Get OpenID"
msgstr ""
@@ -3482,14 +3382,104 @@ msgstr ""
"simply reuse your external login (e.g. Gmail or AOL) without ever sharing "
"your login details with anyone and having to remember yet another password."
-#: templates/authopenid/signup.html:17
+#: templates/authopenid/signup.html:19
+msgid ""
+"Please select your preferred email update schedule for the following groups "
+"of questions:"
+msgstr ""
+
+#: templates/authopenid/signup.html:23
+msgid ""
+"Please read and type in the two words below to help us prevent automated "
+"account creation."
+msgstr ""
+
+#: templates/authopenid/signup.html:25
msgid "Create Account"
msgstr ""
-#: templates/authopenid/signup.html:19
+#: templates/authopenid/signup.html:27
msgid "return to OpenID login"
msgstr ""
+#: templates/fbconnect/xd_receiver.html:5
+#, python-format
+msgid "Connect to %(APP_SHORT_NAME)s with Facebook!"
+msgstr ""
+
+#: utils/forms.py:27
+msgid "this field is required"
+msgstr ""
+
+#: utils/forms.py:42
+msgid "choose a username"
+msgstr "Choose screen name"
+
+#: utils/forms.py:47
+msgid "user name is required"
+msgstr ""
+
+#: utils/forms.py:48
+msgid "sorry, this name is taken, please choose another"
+msgstr ""
+
+#: utils/forms.py:49
+msgid "sorry, this name is not allowed, please choose another"
+msgstr ""
+
+#: utils/forms.py:50
+msgid "sorry, there is no user with this name"
+msgstr ""
+
+#: utils/forms.py:51
+msgid "sorry, we have a serious error - user name is taken by several users"
+msgstr ""
+
+#: utils/forms.py:52
+msgid "user name can only consist of letters, empty space and underscore"
+msgstr ""
+
+#: utils/forms.py:100
+msgid "your email address"
+msgstr "Your email (never shared)"
+
+#: utils/forms.py:101
+msgid "email address is required"
+msgstr ""
+
+#: utils/forms.py:102
+msgid "please enter a valid email address"
+msgstr ""
+
+#: utils/forms.py:103
+msgid "this email is already used by someone else, please choose another"
+msgstr ""
+
+#: utils/forms.py:128
+msgid "choose password"
+msgstr "Password"
+
+#: utils/forms.py:129
+msgid "password is required"
+msgstr ""
+
+#: utils/forms.py:132
+msgid "retype password"
+msgstr "Password (please retype)"
+
+#: utils/forms.py:133
+msgid "please, retype your password"
+msgstr ""
+
+#: utils/forms.py:134
+msgid "sorry, entered passwords did not match, please try again"
+msgstr ""
+
+#~ msgid "have %(num_q)s unanswered questions"
+#~ msgstr ""
+#~ "
%(num_q)s
questions without "
+#~ "accepted answers"
+
#~ msgid ""
#~ "\n"
#~ "\t\t\t\thave total %(q_num)s questions\n"
diff --git a/session_messages/__init__.py b/session_messages/__init__.py
deleted file mode 100644
index 4dd10a6b..00000000
--- a/session_messages/__init__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""
-Lightweight session-based messaging system.
-
-Time-stamp: <2009-03-10 19:22:29 carljm __init__.py>
-
-"""
-VERSION = (0, 1, 'pre')
-
-def create_message (request, message):
- """
- Create a message in the current session.
-
- """
- assert hasattr(request, 'session'), "django-session-messages requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
-
- try:
- request.session['messages'].append(message)
- except KeyError:
- request.session['messages'] = [message]
-
-def get_and_delete_messages (request, include_auth=False):
- """
- Get and delete all messages for current session.
-
- Optionally also fetches user messages from django.contrib.auth.
-
- """
- assert hasattr(request, 'session'), "django-session-messages requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
-
- messages = request.session.pop('messages', [])
- import logging
-
- if include_auth and request.user.is_authenticated():
- messages.extend(request.user.get_and_delete_messages())
-
- return messages
-
diff --git a/session_messages/context_processors.py b/session_messages/context_processors.py
deleted file mode 100644
index df9840fd..00000000
--- a/session_messages/context_processors.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-Context processor for lightweight session messages.
-
-Time-stamp: <2008-07-19 23:16:19 carljm context_processors.py>
-
-"""
-from django.utils.encoding import StrAndUnicode
-
-from session_messages import get_and_delete_messages
-
-def session_messages (request):
- """
- Returns session messages for the current session.
-
- """
- return { 'session_messages': LazyMessages(request) }
-
-class LazyMessages (StrAndUnicode):
- """
- Lazy message container, so messages aren't actually retrieved from
- session and deleted until the template asks for them.
-
- """
- def __init__(self, request):
- self.request = request
-
- def __iter__(self):
- return iter(self.messages)
-
- def __len__(self):
- return len(self.messages)
-
- def __nonzero__(self):
- return bool(self.messages)
-
- def __unicode__(self):
- return unicode(self.messages)
-
- def __getitem__(self, *args, **kwargs):
- return self.messages.__getitem__(*args, **kwargs)
-
- def _get_messages(self):
- if hasattr(self, '_messages'):
- return self._messages
- self._messages = get_and_delete_messages(self.request)
- return self._messages
- messages = property(_get_messages)
-
diff --git a/session_messages/models.py b/session_messages/models.py
deleted file mode 100644
index b67ead6d..00000000
--- a/session_messages/models.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-blank models.py
-"""
diff --git a/templates/authopenid/complete.html b/templates/authopenid/complete.html
index c967e8e2..62970e38 100644
--- a/templates/authopenid/complete.html
+++ b/templates/authopenid/complete.html
@@ -95,6 +95,9 @@ parameters:
{% trans "receive updates motivational blurb" %}
{{email_feeds_form.subscribe}}
+ {% if email_feeds_form.errors %}
+
{% trans "please select one of the options above" %}
+ {% endif %}
{% trans "Tag filter tool will be your right panel, once you log in." %}
+
+ {% trans "system error log is recorded, error will be fixed as soon as possible" %}
+ {% trans "please report the error to the site administrators if you wish" %}
+
Here you can ask and answer questions, comment
+ and vote for the questions of others and their answers. Both questions and answers
+ can be revised and improved. Questions can be tagged with
+ the relevant keywords to simplify future access and organize the accumulated material.
+
+
+
This Q&A site is moderated by its members, hopefully - including yourself!
+ Moderation rights are gradually assigned to the site users based on the accumulated "reputation"
+ points. These points are added to the users account when others vote for his/her questions or answers.
+ These points (very) roughly reflect the level of trust of the community.
+
+
No points are necessary to ask or answer the questions - so please -
+ join us!
+
{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}
+
+
+
+{% endblock %}
+
diff --git a/forum/templates/authopenid/complete.html b/forum/templates/authopenid/complete.html
new file mode 100644
index 00000000..62970e38
--- /dev/null
+++ b/forum/templates/authopenid/complete.html
@@ -0,0 +1,130 @@
+{% extends "base_content.html" %}
+
+{% comment %}
+views calling this template:
+* django_authopenid.views.register with login_type='openid'
+* django_authopenid.views.signin - with login_type='legacy'
+
+parameters:
+* provider
+* login_type openid|legacy
+* username (same as screen name or username in the models, and nickname in openid sreg)
+* form1 - OpenidRegisterForm
+* form2 - OpenidVerifyForm not clear what this form is supposed to do, not used for legacy
+* email_feeds_form forum.forms.SimpleEmailSubscribeForm
+* openid_username_exists
+{% endcomment %}
+{% load i18n %}
+{% block head %}{% endblock %}
+{% block title %}{% spaceless %}{% trans "Connect your OpenID with this site" %}{% endspaceless %}{% endblock %}
+{% block content %}
+
+ {% trans "Connect your OpenID with your account on this site" %}
+
+
+
+ {% ifequal login_type 'openid' %}
+ {% blocktrans %}register new {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
+ {% else %}
+ {% ifequal login_type 'legacy' %}
+ {% if external_login_name_is_taken %}
+ {% blocktrans %}{{username}} already exists, choose another name for
+ {{provider}}. Email is required too, see {{gravatar_faq_url}}
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans %}register new external {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
+ {% endif %}
+ {% else %}
+ {% blocktrans %}register new Facebook connect account info, see {{gravatar_faq_url}}{% endblocktrans %}
+ {% endifequal %}
+ {% endifequal %}
+
+
{% trans "This account already exists, please use another." %}
+
+
+ {% if form1.errors %}
+
+ {% if form1.non_field_errors %}
+ {% for error in form1.non_field_errors %}
+
+{% endblock %}
+
diff --git a/forum/templates/authopenid/email_validation.txt b/forum/templates/authopenid/email_validation.txt
new file mode 100644
index 00000000..5b166a9b
--- /dev/null
+++ b/forum/templates/authopenid/email_validation.txt
@@ -0,0 +1,15 @@
+{% load i18n %}
+{% trans "Greetings from the Q&A forum" %},
+
+{% trans "To make use of the Forum, please follow the link below:" %}
+
+{{validation_link}}
+
+{% trans "Following the link above will help us verify your email address." %}
+
+{% blocktrans %}If you beleive that this message was sent in mistake -
+no further action is needed. Just ingore this email, we apologize
+for any inconvenience{% endblocktrans %}
+
+{% blocktrans %}Sincerely,
+Forum Administrator{% endblocktrans %}
diff --git a/forum/templates/authopenid/external_legacy_login_info.html b/forum/templates/authopenid/external_legacy_login_info.html
new file mode 100644
index 00000000..3318499c
--- /dev/null
+++ b/forum/templates/authopenid/external_legacy_login_info.html
@@ -0,0 +1,15 @@
+{% extends "base_content.html" %}
+
+{% load i18n %}
+{% block title %}{% spaceless %}{% trans "Traditional login information" %}{% endspaceless %}{% endblock %}
+{% block content %}
+
+ {% trans "Traditional login information" %}
+
+{% spaceless %}
+
+
+{% blocktrans %}how to login with password through external login website or use {{feedback_url}}{% endblocktrans %}
+
+ {% blocktrans with answer.question.title as title and answer.summary as summary %}
+ Your answer to {{title}} {{summary}} will be posted once you log in
+ {% endblocktrans %}
+
+ {% endif %}
+ {% if question %}
+
+ {% blocktrans with question.title as title and question.summary as summary %}Your question
+ {{title}} {{summary}} will be posted once you log in
+ {% endblocktrans %}
+
+ {% trans "Community gives you awards for your questions, answers and votes." %}
+ {% blocktrans %}Below is the list of available badges and number
+ of times each type of badge has been awarded. Give us feedback at {{feedback_faq_url}}.
+ {% endblocktrans %}
+
+
+ {% for badge in badges %}
+
+
+ {% for a in mybadges %}
+ {% ifequal a.badge_id badge.id %}
+ ✔
+ {% endifequal %}
+ {% endfor %}
+
"',
+ 'upload image':'æˆ–è€…ä¸Šä¼ æœ¬åœ°å›¾ç‰‡ï¼š'
+};
+
+var i18nEn = {
+ 'need >15 points to report spam':'need >15 points to report spam ',
+ '>15 points requried to upvote':'>15 points required to upvote ',
+ 'tags cannot be empty':'please enter at least one tag',
+ 'anonymous users cannot vote':'sorry, anonymous users cannot vote ',
+ 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ',
+ 'to comment, need': '(to comment other people\'s posts, karma ',
+ 'please see':'please see ',
+ 'community karma points':' or more is necessary) - ',
+ 'upload image':'Upload image:',
+ 'enter image url':'enter URL of the image, e.g. http://www.example.com/image.jpg \"image title\"',
+ 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"',
+ 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap',
+ 'cannot pick own answer as best':'sorry, you cannot accept your own answer',
+ 'cannot revoke old vote':'sorry, older votes cannot be revoked',
+ 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?',
+ 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ',
+ 'confirm delete':'are you sure you want to delete this?',
+ 'anonymous users cannot delete/undelete':'sorry, anonymous users cannot delete or undelete posts',
+ 'post recovered':'your post is now restored!',
+ 'post deleted':'your post has been deleted',
+ 'confirm delete comment':'do you really want to delete this comment?',
+ 'can write':'have ',
+ 'tablimits info':'up to 5 tags, no more than 20 characters each',
+ 'content minchars': 'please enter more than {0} characters',
+ 'title minchars':"please enter at least {0} characters",
+ 'characters':'characters left',
+ 'cannot vote for own posts':'sorry, you cannot vote for your own posts',
+ 'cannot flag message as offensive twice':'cannot flag message as offensive twice ',
+ '>100 points required to downvote':'>100 points required to downvote '
+};
+
+var i18nEs = {
+ 'insufficient privilege':'privilegio insuficiente',
+ 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor',
+ 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar',
+ 'please login':'por favor inicie sesión',
+ 'anonymous users cannot vote':'usuarios anónimos no pueden votar',
+ '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente',
+ '>100 points required to downvote':'>100 puntos requeridos para votar negativamente',
+ 'please see': 'por favor vea',
+ 'cannot vote for own posts':'no se puede votar por sus propias publicaciones',
+ 'daily vote cap exhausted':'cuota de votos diarios excedida',
+ 'cannot revoke old vote':'no puede revocar un voto viejo',
+ 'please confirm offensive':"por favor confirme ofensiva",
+ 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas',
+ 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces',
+ 'flag offensive cap exhausted':'cuota para marcar ofensivas ha sido excedida',
+ 'need >15 points to report spam':"necesita >15 puntos para reportar spam",
+ 'confirm delete':"¿Está seguro que desea borrar esto?",
+ 'anonymous users cannot delete/undelete':"usuarios anónimos no pueden borrar o recuperar publicaciones",
+ 'post recovered':"publicación recuperada",
+ 'post deleted':"publicación borrada。",
+ 'add comment':'agregar comentario',
+ 'community karma points':'reputación comunitaria',
+ 'to comment, need':'para comentar, necesita reputación',
+ 'delete this comment':'borrar este comentario',
+ 'hide comments':"ocultar comentarios",
+ 'add a comment':"agregar comentarios",
+ 'comments':"comentarios",
+ 'confirm delete comment':"¿Realmente desea borrar este comentario?",
+ 'characters':'caracteres faltantes',
+ 'can write':'tiene ',
+ 'click to close':'haga click para cerrar',
+ 'loading...':'cargando...',
+ 'tags cannot be empty':'las etiquetas no pueden estar vacÃas',
+ 'tablimits info':"hasta 5 etiquetas de no mas de 20 caracteres cada una",
+ 'content cannot be empty':'el contenido no puede estar vacÃo',
+ 'content minchars': 'por favor introduzca mas de {0} caracteres',
+ 'please enter title':'por favor ingrese un tÃtulo',
+ 'title minchars':"por favor introduzca al menos {0} caracteres",
+ 'delete':'borrar',
+ 'undelete': 'recuperar',
+ 'bold': 'negrita',
+ 'italic':'cursiva',
+ 'link':'enlace',
+ 'quote':'citar',
+ 'preformatted text':'texto preformateado',
+ 'image':'imagen',
+ 'numbered list':'lista numerada',
+ 'bulleted list':'lista no numerada',
+ 'heading':'æ ‡é¢˜',
+ 'horizontal bar':'barra horizontal',
+ 'undo':'deshacer',
+ 'redo':'rehacer',
+ 'enter image url':'introduzca la URL de la imagen, por ejemplo: http://www.example.com/image.jpg \"titulo de imagen\"',
+ 'enter url':'introduzca direcciones web, ejemplo: http://www.cnprog.com/ \"titulo del enlace\""',
+ 'upload image':'cargar imagen:',
+ 'questions/' : 'preguntas/',
+ 'vote/' : 'votar/'
+};
+
+var i18n = {
+ 'en':i18nEn,
+ 'zh_CN':i18nZh,
+ 'es':i18nEs
+};
+
+var i18n_dict = i18n[i18nLang];
diff --git a/forum/templates/content/js/com.cnprog.post.js b/forum/templates/content/js/com.cnprog.post.js
new file mode 100644
index 00000000..668c80fe
--- /dev/null
+++ b/forum/templates/content/js/com.cnprog.post.js
@@ -0,0 +1,691 @@
+/*
+Scripts for cnprog.com
+Project Name: Lanai
+All Rights Resevred 2008. CNPROG.COM
+*/
+var lanai =
+{
+ /**
+ * Finds any
");});
+
+ // atx-style headers:
+ // # Header 1
+ // ## Header 2
+ // ## Header 2 with closing hashes ##
+ // ...
+ // ###### Header 6
+ //
+
+ /*
+ text = text.replace(/
+ ^(\#{1,6}) // $1 = string of #'s
+ [ \t]*
+ (.+?) // $2 = Header text
+ [ \t]*
+ \#* // optional closing #'s (not counted)
+ \n+
+ /gm, function() {...});
+ */
+
+ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
+ function(wholeMatch,m1,m2) {
+ var h_level = m1.length;
+ return hashBlock("" + _RunSpanGamut(m2) + "");
+ });
+
+ return text;
+}
+
+// This declaration keeps Dojo compressor from outputting garbage:
+var _ProcessListItems;
+
+var _DoLists = function(text) {
+//
+// Form HTML ordered (numbered) and unordered (bulleted) lists.
+//
+
+ // attacklab: add sentinel to hack around khtml/safari bug:
+ // http://bugs.webkit.org/show_bug.cgi?id=11231
+ text += "~0";
+
+ // Re-usable pattern to match any entirel ul or ol list:
+
+ /*
+ var whole_list = /
+ ( // $1 = whole list
+ ( // $2
+ [ ]{0,3} // attacklab: g_tab_width - 1
+ ([*+-]|\d+[.]) // $3 = first list item marker
+ [ \t]+
+ )
+ [^\r]+?
+ ( // $4
+ ~0 // sentinel for workaround; should be $
+ |
+ \n{2,}
+ (?=\S)
+ (?! // Negative lookahead for another list item marker
+ [ \t]*
+ (?:[*+-]|\d+[.])[ \t]+
+ )
+ )
+ )/g
+ */
+ var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
+
+ if (g_list_level) {
+ text = text.replace(whole_list,function(wholeMatch,m1,m2) {
+ var list = m1;
+ var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
+
+ // Turn double returns into triple returns, so that we can make a
+ // paragraph for the last item in a list, if necessary:
+ list = list.replace(/\n{2,}/g,"\n\n\n");;
+ var result = _ProcessListItems(list);
+
+ // Trim any trailing whitespace, to put the closing `$list_type>`
+ // up on the preceding line, to get it past the current stupid
+ // HTML block parser. This is a hack to work around the terrible
+ // hack that is the HTML block parser.
+ result = result.replace(/\s+$/,"");
+ result = "<"+list_type+">" + result + ""+list_type+">\n";
+ return result;
+ });
+ } else {
+ whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
+ text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
+ var runup = m1;
+ var list = m2;
+
+ var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
+ // Turn double returns into triple returns, so that we can make a
+ // paragraph for the last item in a list, if necessary:
+ var list = list.replace(/\n{2,}/g,"\n\n\n");;
+ var result = _ProcessListItems(list);
+ result = runup + "<"+list_type+">\n" + result + ""+list_type+">\n";
+ return result;
+ });
+ }
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/,"");
+
+ return text;
+}
+
+_ProcessListItems = function(list_str) {
+//
+// Process the contents of a single ordered or unordered list, splitting it
+// into individual list items.
+//
+ // The $g_list_level global keeps track of when we're inside a list.
+ // Each time we enter a list, we increment it; when we leave a list,
+ // we decrement. If it's zero, we're not in a list anymore.
+ //
+ // We do this because when we're not inside a list, we want to treat
+ // something like this:
+ //
+ // I recommend upgrading to version
+ // 8. Oops, now this line is treated
+ // as a sub-list.
+ //
+ // As a single paragraph, despite the fact that the second line starts
+ // with a digit-period-space sequence.
+ //
+ // Whereas when we're inside a list (or sub-list), that line will be
+ // treated as the start of a sub-list. What a kludge, huh? This is
+ // an aspect of Markdown's syntax that's hard to parse perfectly
+ // without resorting to mind-reading. Perhaps the solution is to
+ // change the syntax rules such that sub-lists must start with a
+ // starting cardinal number; e.g. "1." or "a.".
+
+ g_list_level++;
+
+ // trim trailing blank lines:
+ list_str = list_str.replace(/\n{2,}$/,"\n");
+
+ // attacklab: add sentinel to emulate \z
+ list_str += "~0";
+
+ /*
+ list_str = list_str.replace(/
+ (\n)? // leading line = $1
+ (^[ \t]*) // leading whitespace = $2
+ ([*+-]|\d+[.]) [ \t]+ // list marker = $3
+ ([^\r]+? // list item text = $4
+ (\n{1,2}))
+ (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
+ /gm, function(){...});
+ */
+ list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
+ function(wholeMatch,m1,m2,m3,m4){
+ var item = m4;
+ var leading_line = m1;
+ var leading_space = m2;
+
+ if (leading_line || (item.search(/\n{2,}/)>-1)) {
+ item = _RunBlockGamut(_Outdent(item));
+ }
+ else {
+ // Recursion for sub-lists:
+ item = _DoLists(_Outdent(item));
+ item = item.replace(/\n$/,""); // chomp(item)
+ item = _RunSpanGamut(item);
+ }
+
+ return "
` blocks.
+//
+
+ /*
+ text = text.replace(text,
+ /(?:\n\n|^)
+ ( // $1 = the code block -- one or more lines, starting with a space/tab
+ (?:
+ (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
+ .*\n+
+ )+
+ )
+ (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
+ /g,function(){...});
+ */
+
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+ text += "~0";
+
+ text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
+ function(wholeMatch,m1,m2) {
+ var codeblock = m1;
+ var nextChar = m2;
+
+ codeblock = _EncodeCode( _Outdent(codeblock));
+ codeblock = _Detab(codeblock);
+ codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
+ codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
+
+ codeblock = "
" + codeblock + "\n
";
+
+ return hashBlock(codeblock) + nextChar;
+ }
+ );
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/,"");
+
+ return text;
+}
+
+var hashBlock = function(text) {
+ text = text.replace(/(^\n+|\n+$)/g,"");
+ return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
+}
+
+
+var _DoCodeSpans = function(text) {
+//
+// * Backtick quotes are used for spans.
+//
+// * You can use multiple backticks as the delimiters if you want to
+// include literal backticks in the code span. So, this input:
+//
+// Just type ``foo `bar` baz`` at the prompt.
+//
+// Will translate to:
+//
+//
Just type foo `bar` baz at the prompt.
+//
+// There's no arbitrary limit to the number of backticks you
+// can use as delimters. If you need three consecutive backticks
+// in your code, use four for delimiters, etc.
+//
+// * You can use spaces to get literal backticks at the edges:
+//
+// ... type `` `bar` `` ...
+//
+// Turns to:
+//
+// ... type `bar` ...
+//
+
+ /*
+ text = text.replace(/
+ (^|[^\\]) // Character before opening ` can't be a backslash
+ (`+) // $2 = Opening run of `
+ ( // $3 = The code block
+ [^\r]*?
+ [^`] // attacklab: work around lack of lookbehind
+ )
+ \2 // Matching closer
+ (?!`)
+ /gm, function(){...});
+ */
+
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
+ function(wholeMatch,m1,m2,m3,m4) {
+ var c = m3;
+ c = c.replace(/^([ \t]*)/g,""); // leading whitespace
+ c = c.replace(/[ \t]*$/g,""); // trailing whitespace
+ c = _EncodeCode(c);
+ return m1+""+c+"";
+ });
+
+ return text;
+}
+
+
+var _EncodeCode = function(text) {
+//
+// Encode/escape certain characters inside Markdown code runs.
+// The point is that in code, these characters are literals,
+// and lose their special Markdown meanings.
+//
+ // Encode all ampersands; HTML entities are not
+ // entities within a Markdown code span.
+ text = text.replace(/&/g,"&");
+
+ // Do the angle bracket song and dance:
+ text = text.replace(//g,">");
+
+ // Now, escape characters that are magic in Markdown:
+ text = escapeCharacters(text,"\*_{}[]\\",false);
+
+// jj the line above breaks this:
+//---
+
+//* Item
+
+// 1. Subitem
+
+// special char: *
+//---
+
+ return text;
+}
+
+
+var _DoItalicsAndBold = function(text) {
+
+ // must go first:
+ text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,
+ "$2");
+
+ text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
+ "$2");
+
+ return text;
+}
+
+
+var _DoBlockQuotes = function(text) {
+
+ /*
+ text = text.replace(/
+ ( // Wrap whole match in $1
+ (
+ ^[ \t]*>[ \t]? // '>' at the start of a line
+ .+\n // rest of the first line
+ (.+\n)* // subsequent consecutive lines
+ \n* // blanks
+ )+
+ )
+ /gm, function(){...});
+ */
+
+ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
+ function(wholeMatch,m1) {
+ var bq = m1;
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
+
+ // attacklab: clean up hack
+ bq = bq.replace(/~0/g,"");
+
+ bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
+ bq = _RunBlockGamut(bq); // recurse
+
+ bq = bq.replace(/(^|\n)/g,"$1 ");
+ // These leading spaces screw with
content, so we need to fix that:
+ bq = bq.replace(
+ /(\s*
[^\r]+?<\/pre>)/gm,
+ function(wholeMatch,m1) {
+ var pre = m1;
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ pre = pre.replace(/^ /mg,"~0");
+ pre = pre.replace(/~0/g,"");
+ return pre;
+ });
+
+ return hashBlock("
\n" + bq + "\n
");
+ });
+ return text;
+}
+
+
+var _FormParagraphs = function(text) {
+//
+// Params:
+// $text - string to process with html
tags
+//
+
+ // Strip leading and trailing lines:
+ text = text.replace(/^\n+/g,"");
+ text = text.replace(/\n+$/g,"");
+
+ var grafs = text.split(/\n{2,}/g);
+ var grafsOut = new Array();
+
+ //
+ // Wrap
tags.
+ //
+ var end = grafs.length;
+ for (var i=0; i= 0) {
+ grafsOut.push(str);
+ }
+ else if (str.search(/\S/) >= 0) {
+ str = _RunSpanGamut(str);
+ str = str.replace(/^([ \t]*)/g,"
");
+ str += "
"
+ grafsOut.push(str);
+ }
+
+ }
+
+ //
+ // Unhashify HTML blocks
+ //
+ end = grafsOut.length;
+ for (var i=0; i= 0) {
+ var blockText = g_html_blocks[RegExp.$1];
+ blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
+ grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
+ }
+ }
+
+ return grafsOut.join("\n\n");
+}
+
+
+var _EncodeAmpsAndAngles = function(text) {
+// Smart processing for ampersands and angle brackets that need to be encoded.
+
+ // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
+ // http://bumppo.net/projects/amputator/
+ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
+
+ // Encode naked <'s
+ text = text.replace(/<(?![a-z\/?\$!])/gi,"<");
+
+ return text;
+}
+
+
+var _EncodeBackslashEscapes = function(text) {
+//
+// Parameter: String.
+// Returns: The string, with after processing the following backslash
+// escape sequences.
+//
+
+ // attacklab: The polite way to do this is with the new
+ // escapeCharacters() function:
+ //
+ // text = escapeCharacters(text,"\\",true);
+ // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
+ //
+ // ...but we're sidestepping its use of the (slow) RegExp constructor
+ // as an optimization for Firefox. This function gets called a LOT.
+
+ text = text.replace(/\\(\\)/g,escapeCharacters_callback);
+ text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
+ return text;
+}
+
+
+var _DoAutoLinks = function(text) {
+
+ text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1");
+
+ // Email addresses:
+
+ /*
+ text = text.replace(/
+ <
+ (?:mailto:)?
+ (
+ [-.\w]+
+ \@
+ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
+ )
+ >
+ /gi, _DoAutoLinks_callback());
+ */
+ text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
+ function(wholeMatch,m1) {
+ return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
+ }
+ );
+
+ return text;
+}
+
+
+var _EncodeEmailAddress = function(addr) {
+//
+// Input: an email address, e.g. "foo@example.com"
+//
+// Output: the email address as a mailto link, with each character
+// of the address encoded as either a decimal or hex entity, in
+// the hopes of foiling most address harvesting spam bots. E.g.:
+//
+// foo
+// @example.com
+//
+// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
+// mailing list:
+//
+
+ // attacklab: why can't javascript speak hex?
+ function char2hex(ch) {
+ var hexDigits = '0123456789ABCDEF';
+ var dec = ch.charCodeAt(0);
+ return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
+ }
+
+ var encode = [
+ function(ch){return ""+ch.charCodeAt(0)+";";},
+ function(ch){return ""+char2hex(ch)+";";},
+ function(ch){return ch;}
+ ];
+
+ addr = "mailto:" + addr;
+
+ addr = addr.replace(/./g, function(ch) {
+ if (ch == "@") {
+ // this *must* be encoded. I insist.
+ ch = encode[Math.floor(Math.random()*2)](ch);
+ } else if (ch !=":") {
+ // leave ':' alone (to spot mailto: later)
+ var r = Math.random();
+ // roughly 10% raw, 45% hex, 45% dec
+ ch = (
+ r > .9 ? encode[2](ch) :
+ r > .45 ? encode[1](ch) :
+ encode[0](ch)
+ );
+ }
+ return ch;
+ });
+
+ addr = "" + addr + "";
+ addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
+
+ return addr;
+}
+
+
+var _UnescapeSpecialChars = function(text) {
+//
+// Swap back in all the special characters we've hidden.
+//
+ text = text.replace(/~E(\d+)E/g,
+ function(wholeMatch,m1) {
+ var charCodeToReplace = parseInt(m1);
+ return String.fromCharCode(charCodeToReplace);
+ }
+ );
+ return text;
+}
+
+
+var _Outdent = function(text) {
+//
+// Remove one level of line-leading tabs or spaces
+//
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
+
+ // attacklab: clean up hack
+ text = text.replace(/~0/g,"")
+
+ return text;
+}
+
+var _Detab = function(text) {
+// attacklab: Detab's completely rewritten for speed.
+// In perl we could fix it by anchoring the regexp with \G.
+// In javascript we're less fortunate.
+
+ // expand first n-1 tabs
+ text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
+
+ // replace the nth with two sentinels
+ text = text.replace(/\t/g,"~A~B");
+
+ // use the sentinel to anchor our regex so it doesn't explode
+ text = text.replace(/~B(.+?)~A/g,
+ function(wholeMatch,m1,m2) {
+ var leadingText = m1;
+ var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
+
+ // there *must* be a better way to do this:
+ for (var i=0; i";var D="
+
+ {% blocktrans %}how to validate email info with {{send_email_key_url}} {{gravatar_faq_url}}{% endblocktrans %}
+
+ {% endifequal %}
+
+
{% trans "what is gravatar" %}
+
{% trans "gravatar faq info" %}
+
+
+
{% trans "To register, do I need to create new password?" %}
+
{% trans "No, you don't have to. You can login through any service that supports OpenID, e.g. Google, Yahoo, AOL, etc." %}
+ {% trans "Login now!" %} »
+
+
+
+
+
{% trans "Why other people can edit my questions/answers?" %}
+
{% trans "Goal of this site is..." %} {% trans "So questions and answers can be edited like wiki pages by experienced users of this site and this improves the overall quality of the knowledge base content." %}
+ {% trans "If this approach is not for you, we respect your choice." %}
+
+
+
+
{% trans "Still have questions?" %}
+
{% blocktrans %}Please ask your question at {{ask_question_url}}, help make our community better!{% endblocktrans %}
+
+
{% blocktrans with question.get_close_reason_display as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %}
+ {{ question.closed_by.username }}
+ {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}
+ {% if searchtag %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% plural %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% endblocktrans %}
+ {% else %}
+ {% if searchtitle %}
+ {% if settings.USE_SPHINX_SEARCH %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions containing {{searchtitle}} in full text
+ {% plural %}
+ have total {{q_num}} questions containing {{searchtitle}} in full text
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions containing {{searchtitle}}
+ {% plural %}
+ have total {{q_num}} questions containing {{searchtitle}}
+ {% endblocktrans %}
+ {% endif %}
+ {% else %}
+ {% if is_unanswered %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} unanswered questions
+ {% plural %}
+ have total {{q_num}} unanswered questions
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions
+ {% plural %}
+ have total {{q_num}} questions
+ {% endblocktrans %}
+ {% endif %}
+ {% endif %}
+ {% endif %}
+
+ {% ifequal tab_id "latest" %}
+ {% trans "latest questions info" %}
+ {% endifequal %}
+
+ {% ifequal tab_id "active" %}
+ {% trans "Questions are sorted by the time of last update." %}
+ {% trans "Most recently answered ones are shown first." %}
+ {% endifequal %}
+
+ {% ifequal tab_id "hottest" %}
+ {% trans "Questions sorted by number of responses." %}
+ {% trans "Most answered questions are shown first." %}
+ {% endifequal %}
+
+ {% ifequal tab_id "mostvoted" %}
+ {% trans "Questions are sorted by the number of votes." %}
+ {% trans "Most voted questions are shown first." %}
+ {% endifequal %}
+
+
+{% if request.user.is_authenticated %}
+{% include "tag_selector.html" %}
+{% endif %}
+
+
{% trans "Related tags" %}
+
+ {% for tag in tags %}
+ {{ tag.name }}
+ × {{ tag.used_count|intcomma }}
+
+ {% endfor %}
+
{% trans "The question was closed for the following reason " %}"{{ question.get_close_reason_display }}"{% trans "reason - leave blank in english" %} {{ question.closed_by.username }} {% trans "on "%} {% diff_date question.closed_at %}{% trans "date closed" %}
+
+
+{% if stag %}
+ {% trans "All tags matching query" %} '{{ stag }}' {% trans "all tags - make this empty in english" %}:
+{% endif %}
+{% if not tags.object_list %}
+ {% trans "Nothing found" %}
+{% endif %}
+
-
- {% trans "system error log is recorded, error will be fixed as soon as possible" %}
- {% trans "please report the error to the site administrators if you wish" %}
-
Here you can ask and answer questions, comment
- and vote for the questions of others and their answers. Both questions and answers
- can be revised and improved. Questions can be tagged with
- the relevant keywords to simplify future access and organize the accumulated material.
-
-
-
This Q&A site is moderated by its members, hopefully - including yourself!
- Moderation rights are gradually assigned to the site users based on the accumulated "reputation"
- points. These points are added to the users account when others vote for his/her questions or answers.
- These points (very) roughly reflect the level of trust of the community.
-
-
No points are necessary to ask or answer the questions - so please -
- join us!
-
{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}
-
-
-
-{% endblock %}
-
diff --git a/templates/authopenid/complete.html b/templates/authopenid/complete.html
deleted file mode 100644
index 62970e38..00000000
--- a/templates/authopenid/complete.html
+++ /dev/null
@@ -1,130 +0,0 @@
-{% extends "base_content.html" %}
-
-{% comment %}
-views calling this template:
-* django_authopenid.views.register with login_type='openid'
-* django_authopenid.views.signin - with login_type='legacy'
-
-parameters:
-* provider
-* login_type openid|legacy
-* username (same as screen name or username in the models, and nickname in openid sreg)
-* form1 - OpenidRegisterForm
-* form2 - OpenidVerifyForm not clear what this form is supposed to do, not used for legacy
-* email_feeds_form forum.forms.SimpleEmailSubscribeForm
-* openid_username_exists
-{% endcomment %}
-{% load i18n %}
-{% block head %}{% endblock %}
-{% block title %}{% spaceless %}{% trans "Connect your OpenID with this site" %}{% endspaceless %}{% endblock %}
-{% block content %}
-
- {% trans "Connect your OpenID with your account on this site" %}
-
-
-
- {% ifequal login_type 'openid' %}
- {% blocktrans %}register new {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% else %}
- {% ifequal login_type 'legacy' %}
- {% if external_login_name_is_taken %}
- {% blocktrans %}{{username}} already exists, choose another name for
- {{provider}}. Email is required too, see {{gravatar_faq_url}}
- {% endblocktrans %}
- {% else %}
- {% blocktrans %}register new external {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% endif %}
- {% else %}
- {% blocktrans %}register new Facebook connect account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% endifequal %}
- {% endifequal %}
-
-
{% trans "This account already exists, please use another." %}
-
-
- {% if form1.errors %}
-
- {% if form1.non_field_errors %}
- {% for error in form1.non_field_errors %}
-
-{% endblock %}
-
diff --git a/templates/authopenid/email_validation.txt b/templates/authopenid/email_validation.txt
deleted file mode 100644
index 5b166a9b..00000000
--- a/templates/authopenid/email_validation.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{% load i18n %}
-{% trans "Greetings from the Q&A forum" %},
-
-{% trans "To make use of the Forum, please follow the link below:" %}
-
-{{validation_link}}
-
-{% trans "Following the link above will help us verify your email address." %}
-
-{% blocktrans %}If you beleive that this message was sent in mistake -
-no further action is needed. Just ingore this email, we apologize
-for any inconvenience{% endblocktrans %}
-
-{% blocktrans %}Sincerely,
-Forum Administrator{% endblocktrans %}
diff --git a/templates/authopenid/external_legacy_login_info.html b/templates/authopenid/external_legacy_login_info.html
deleted file mode 100644
index 3318499c..00000000
--- a/templates/authopenid/external_legacy_login_info.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "base_content.html" %}
-
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Traditional login information" %}{% endspaceless %}{% endblock %}
-{% block content %}
-
- {% trans "Traditional login information" %}
-
-{% spaceless %}
-
-
-{% blocktrans %}how to login with password through external login website or use {{feedback_url}}{% endblocktrans %}
-
- {% blocktrans with answer.question.title as title and answer.summary as summary %}
- Your answer to {{title}} {{summary}} will be posted once you log in
- {% endblocktrans %}
-
- {% endif %}
- {% if question %}
-
- {% blocktrans with question.title as title and question.summary as summary %}Your question
- {{title}} {{summary}} will be posted once you log in
- {% endblocktrans %}
-
- {% trans "Community gives you awards for your questions, answers and votes." %}
- {% blocktrans %}Below is the list of available badges and number
- of times each type of badge has been awarded. Give us feedback at {{feedback_faq_url}}.
- {% endblocktrans %}
-
-
- {% for badge in badges %}
-
-
- {% for a in mybadges %}
- {% ifequal a.badge_id badge.id %}
- ✔
- {% endifequal %}
- {% endfor %}
-
"',
- 'upload image':'æˆ–è€…ä¸Šä¼ æœ¬åœ°å›¾ç‰‡ï¼š'
-};
-
-var i18nEn = {
- 'need >15 points to report spam':'need >15 points to report spam ',
- '>15 points requried to upvote':'>15 points required to upvote ',
- 'tags cannot be empty':'please enter at least one tag',
- 'anonymous users cannot vote':'sorry, anonymous users cannot vote ',
- 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ',
- 'to comment, need': '(to comment other people\'s posts, karma ',
- 'please see':'please see ',
- 'community karma points':' or more is necessary) - ',
- 'upload image':'Upload image:',
- 'enter image url':'enter URL of the image, e.g. http://www.example.com/image.jpg \"image title\"',
- 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"',
- 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap',
- 'cannot pick own answer as best':'sorry, you cannot accept your own answer',
- 'cannot revoke old vote':'sorry, older votes cannot be revoked',
- 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?',
- 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ',
- 'confirm delete':'are you sure you want to delete this?',
- 'anonymous users cannot delete/undelete':'sorry, anonymous users cannot delete or undelete posts',
- 'post recovered':'your post is now restored!',
- 'post deleted':'your post has been deleted',
- 'confirm delete comment':'do you really want to delete this comment?',
- 'can write':'have ',
- 'tablimits info':'up to 5 tags, no more than 20 characters each',
- 'content minchars': 'please enter more than {0} characters',
- 'title minchars':"please enter at least {0} characters",
- 'characters':'characters left',
- 'cannot vote for own posts':'sorry, you cannot vote for your own posts',
- 'cannot flag message as offensive twice':'cannot flag message as offensive twice ',
- '>100 points required to downvote':'>100 points required to downvote '
-};
-
-var i18nEs = {
- 'insufficient privilege':'privilegio insuficiente',
- 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor',
- 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar',
- 'please login':'por favor inicie sesión',
- 'anonymous users cannot vote':'usuarios anónimos no pueden votar',
- '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente',
- '>100 points required to downvote':'>100 puntos requeridos para votar negativamente',
- 'please see': 'por favor vea',
- 'cannot vote for own posts':'no se puede votar por sus propias publicaciones',
- 'daily vote cap exhausted':'cuota de votos diarios excedida',
- 'cannot revoke old vote':'no puede revocar un voto viejo',
- 'please confirm offensive':"por favor confirme ofensiva",
- 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas',
- 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces',
- 'flag offensive cap exhausted':'cuota para marcar ofensivas ha sido excedida',
- 'need >15 points to report spam':"necesita >15 puntos para reportar spam",
- 'confirm delete':"¿Está seguro que desea borrar esto?",
- 'anonymous users cannot delete/undelete':"usuarios anónimos no pueden borrar o recuperar publicaciones",
- 'post recovered':"publicación recuperada",
- 'post deleted':"publicación borrada。",
- 'add comment':'agregar comentario',
- 'community karma points':'reputación comunitaria',
- 'to comment, need':'para comentar, necesita reputación',
- 'delete this comment':'borrar este comentario',
- 'hide comments':"ocultar comentarios",
- 'add a comment':"agregar comentarios",
- 'comments':"comentarios",
- 'confirm delete comment':"¿Realmente desea borrar este comentario?",
- 'characters':'caracteres faltantes',
- 'can write':'tiene ',
- 'click to close':'haga click para cerrar',
- 'loading...':'cargando...',
- 'tags cannot be empty':'las etiquetas no pueden estar vacÃas',
- 'tablimits info':"hasta 5 etiquetas de no mas de 20 caracteres cada una",
- 'content cannot be empty':'el contenido no puede estar vacÃo',
- 'content minchars': 'por favor introduzca mas de {0} caracteres',
- 'please enter title':'por favor ingrese un tÃtulo',
- 'title minchars':"por favor introduzca al menos {0} caracteres",
- 'delete':'borrar',
- 'undelete': 'recuperar',
- 'bold': 'negrita',
- 'italic':'cursiva',
- 'link':'enlace',
- 'quote':'citar',
- 'preformatted text':'texto preformateado',
- 'image':'imagen',
- 'numbered list':'lista numerada',
- 'bulleted list':'lista no numerada',
- 'heading':'æ ‡é¢˜',
- 'horizontal bar':'barra horizontal',
- 'undo':'deshacer',
- 'redo':'rehacer',
- 'enter image url':'introduzca la URL de la imagen, por ejemplo: http://www.example.com/image.jpg \"titulo de imagen\"',
- 'enter url':'introduzca direcciones web, ejemplo: http://www.cnprog.com/ \"titulo del enlace\""',
- 'upload image':'cargar imagen:',
- 'questions/' : 'preguntas/',
- 'vote/' : 'votar/'
-};
-
-var i18n = {
- 'en':i18nEn,
- 'zh_CN':i18nZh,
- 'es':i18nEs
-};
-
-var i18n_dict = i18n[i18nLang];
diff --git a/templates/content/js/com.cnprog.post.js b/templates/content/js/com.cnprog.post.js
deleted file mode 100644
index 668c80fe..00000000
--- a/templates/content/js/com.cnprog.post.js
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
-Scripts for cnprog.com
-Project Name: Lanai
-All Rights Resevred 2008. CNPROG.COM
-*/
-var lanai =
-{
- /**
- * Finds any
");});
-
- // atx-style headers:
- // # Header 1
- // ## Header 2
- // ## Header 2 with closing hashes ##
- // ...
- // ###### Header 6
- //
-
- /*
- text = text.replace(/
- ^(\#{1,6}) // $1 = string of #'s
- [ \t]*
- (.+?) // $2 = Header text
- [ \t]*
- \#* // optional closing #'s (not counted)
- \n+
- /gm, function() {...});
- */
-
- text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
- function(wholeMatch,m1,m2) {
- var h_level = m1.length;
- return hashBlock("" + _RunSpanGamut(m2) + "");
- });
-
- return text;
-}
-
-// This declaration keeps Dojo compressor from outputting garbage:
-var _ProcessListItems;
-
-var _DoLists = function(text) {
-//
-// Form HTML ordered (numbered) and unordered (bulleted) lists.
-//
-
- // attacklab: add sentinel to hack around khtml/safari bug:
- // http://bugs.webkit.org/show_bug.cgi?id=11231
- text += "~0";
-
- // Re-usable pattern to match any entirel ul or ol list:
-
- /*
- var whole_list = /
- ( // $1 = whole list
- ( // $2
- [ ]{0,3} // attacklab: g_tab_width - 1
- ([*+-]|\d+[.]) // $3 = first list item marker
- [ \t]+
- )
- [^\r]+?
- ( // $4
- ~0 // sentinel for workaround; should be $
- |
- \n{2,}
- (?=\S)
- (?! // Negative lookahead for another list item marker
- [ \t]*
- (?:[*+-]|\d+[.])[ \t]+
- )
- )
- )/g
- */
- var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
-
- if (g_list_level) {
- text = text.replace(whole_list,function(wholeMatch,m1,m2) {
- var list = m1;
- var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
-
- // Turn double returns into triple returns, so that we can make a
- // paragraph for the last item in a list, if necessary:
- list = list.replace(/\n{2,}/g,"\n\n\n");;
- var result = _ProcessListItems(list);
-
- // Trim any trailing whitespace, to put the closing `$list_type>`
- // up on the preceding line, to get it past the current stupid
- // HTML block parser. This is a hack to work around the terrible
- // hack that is the HTML block parser.
- result = result.replace(/\s+$/,"");
- result = "<"+list_type+">" + result + ""+list_type+">\n";
- return result;
- });
- } else {
- whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
- text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
- var runup = m1;
- var list = m2;
-
- var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
- // Turn double returns into triple returns, so that we can make a
- // paragraph for the last item in a list, if necessary:
- var list = list.replace(/\n{2,}/g,"\n\n\n");;
- var result = _ProcessListItems(list);
- result = runup + "<"+list_type+">\n" + result + ""+list_type+">\n";
- return result;
- });
- }
-
- // attacklab: strip sentinel
- text = text.replace(/~0/,"");
-
- return text;
-}
-
-_ProcessListItems = function(list_str) {
-//
-// Process the contents of a single ordered or unordered list, splitting it
-// into individual list items.
-//
- // The $g_list_level global keeps track of when we're inside a list.
- // Each time we enter a list, we increment it; when we leave a list,
- // we decrement. If it's zero, we're not in a list anymore.
- //
- // We do this because when we're not inside a list, we want to treat
- // something like this:
- //
- // I recommend upgrading to version
- // 8. Oops, now this line is treated
- // as a sub-list.
- //
- // As a single paragraph, despite the fact that the second line starts
- // with a digit-period-space sequence.
- //
- // Whereas when we're inside a list (or sub-list), that line will be
- // treated as the start of a sub-list. What a kludge, huh? This is
- // an aspect of Markdown's syntax that's hard to parse perfectly
- // without resorting to mind-reading. Perhaps the solution is to
- // change the syntax rules such that sub-lists must start with a
- // starting cardinal number; e.g. "1." or "a.".
-
- g_list_level++;
-
- // trim trailing blank lines:
- list_str = list_str.replace(/\n{2,}$/,"\n");
-
- // attacklab: add sentinel to emulate \z
- list_str += "~0";
-
- /*
- list_str = list_str.replace(/
- (\n)? // leading line = $1
- (^[ \t]*) // leading whitespace = $2
- ([*+-]|\d+[.]) [ \t]+ // list marker = $3
- ([^\r]+? // list item text = $4
- (\n{1,2}))
- (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
- /gm, function(){...});
- */
- list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
- function(wholeMatch,m1,m2,m3,m4){
- var item = m4;
- var leading_line = m1;
- var leading_space = m2;
-
- if (leading_line || (item.search(/\n{2,}/)>-1)) {
- item = _RunBlockGamut(_Outdent(item));
- }
- else {
- // Recursion for sub-lists:
- item = _DoLists(_Outdent(item));
- item = item.replace(/\n$/,""); // chomp(item)
- item = _RunSpanGamut(item);
- }
-
- return "
` blocks.
-//
-
- /*
- text = text.replace(text,
- /(?:\n\n|^)
- ( // $1 = the code block -- one or more lines, starting with a space/tab
- (?:
- (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
- .*\n+
- )+
- )
- (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
- /g,function(){...});
- */
-
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
- text += "~0";
-
- text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
- function(wholeMatch,m1,m2) {
- var codeblock = m1;
- var nextChar = m2;
-
- codeblock = _EncodeCode( _Outdent(codeblock));
- codeblock = _Detab(codeblock);
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
-
- codeblock = "
" + codeblock + "\n
";
-
- return hashBlock(codeblock) + nextChar;
- }
- );
-
- // attacklab: strip sentinel
- text = text.replace(/~0/,"");
-
- return text;
-}
-
-var hashBlock = function(text) {
- text = text.replace(/(^\n+|\n+$)/g,"");
- return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
-}
-
-
-var _DoCodeSpans = function(text) {
-//
-// * Backtick quotes are used for spans.
-//
-// * You can use multiple backticks as the delimiters if you want to
-// include literal backticks in the code span. So, this input:
-//
-// Just type ``foo `bar` baz`` at the prompt.
-//
-// Will translate to:
-//
-//
Just type foo `bar` baz at the prompt.
-//
-// There's no arbitrary limit to the number of backticks you
-// can use as delimters. If you need three consecutive backticks
-// in your code, use four for delimiters, etc.
-//
-// * You can use spaces to get literal backticks at the edges:
-//
-// ... type `` `bar` `` ...
-//
-// Turns to:
-//
-// ... type `bar` ...
-//
-
- /*
- text = text.replace(/
- (^|[^\\]) // Character before opening ` can't be a backslash
- (`+) // $2 = Opening run of `
- ( // $3 = The code block
- [^\r]*?
- [^`] // attacklab: work around lack of lookbehind
- )
- \2 // Matching closer
- (?!`)
- /gm, function(){...});
- */
-
- text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
- function(wholeMatch,m1,m2,m3,m4) {
- var c = m3;
- c = c.replace(/^([ \t]*)/g,""); // leading whitespace
- c = c.replace(/[ \t]*$/g,""); // trailing whitespace
- c = _EncodeCode(c);
- return m1+""+c+"";
- });
-
- return text;
-}
-
-
-var _EncodeCode = function(text) {
-//
-// Encode/escape certain characters inside Markdown code runs.
-// The point is that in code, these characters are literals,
-// and lose their special Markdown meanings.
-//
- // Encode all ampersands; HTML entities are not
- // entities within a Markdown code span.
- text = text.replace(/&/g,"&");
-
- // Do the angle bracket song and dance:
- text = text.replace(//g,">");
-
- // Now, escape characters that are magic in Markdown:
- text = escapeCharacters(text,"\*_{}[]\\",false);
-
-// jj the line above breaks this:
-//---
-
-//* Item
-
-// 1. Subitem
-
-// special char: *
-//---
-
- return text;
-}
-
-
-var _DoItalicsAndBold = function(text) {
-
- // must go first:
- text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,
- "$2");
-
- text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
- "$2");
-
- return text;
-}
-
-
-var _DoBlockQuotes = function(text) {
-
- /*
- text = text.replace(/
- ( // Wrap whole match in $1
- (
- ^[ \t]*>[ \t]? // '>' at the start of a line
- .+\n // rest of the first line
- (.+\n)* // subsequent consecutive lines
- \n* // blanks
- )+
- )
- /gm, function(){...});
- */
-
- text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
- function(wholeMatch,m1) {
- var bq = m1;
-
- // attacklab: hack around Konqueror 3.5.4 bug:
- // "----------bug".replace(/^-/g,"") == "bug"
-
- bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
-
- // attacklab: clean up hack
- bq = bq.replace(/~0/g,"");
-
- bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
- bq = _RunBlockGamut(bq); // recurse
-
- bq = bq.replace(/(^|\n)/g,"$1 ");
- // These leading spaces screw with
content, so we need to fix that:
- bq = bq.replace(
- /(\s*
[^\r]+?<\/pre>)/gm,
- function(wholeMatch,m1) {
- var pre = m1;
- // attacklab: hack around Konqueror 3.5.4 bug:
- pre = pre.replace(/^ /mg,"~0");
- pre = pre.replace(/~0/g,"");
- return pre;
- });
-
- return hashBlock("
\n" + bq + "\n
");
- });
- return text;
-}
-
-
-var _FormParagraphs = function(text) {
-//
-// Params:
-// $text - string to process with html
tags
-//
-
- // Strip leading and trailing lines:
- text = text.replace(/^\n+/g,"");
- text = text.replace(/\n+$/g,"");
-
- var grafs = text.split(/\n{2,}/g);
- var grafsOut = new Array();
-
- //
- // Wrap
tags.
- //
- var end = grafs.length;
- for (var i=0; i= 0) {
- grafsOut.push(str);
- }
- else if (str.search(/\S/) >= 0) {
- str = _RunSpanGamut(str);
- str = str.replace(/^([ \t]*)/g,"
");
- str += "
"
- grafsOut.push(str);
- }
-
- }
-
- //
- // Unhashify HTML blocks
- //
- end = grafsOut.length;
- for (var i=0; i= 0) {
- var blockText = g_html_blocks[RegExp.$1];
- blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
- grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
- }
- }
-
- return grafsOut.join("\n\n");
-}
-
-
-var _EncodeAmpsAndAngles = function(text) {
-// Smart processing for ampersands and angle brackets that need to be encoded.
-
- // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
- // http://bumppo.net/projects/amputator/
- text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
-
- // Encode naked <'s
- text = text.replace(/<(?![a-z\/?\$!])/gi,"<");
-
- return text;
-}
-
-
-var _EncodeBackslashEscapes = function(text) {
-//
-// Parameter: String.
-// Returns: The string, with after processing the following backslash
-// escape sequences.
-//
-
- // attacklab: The polite way to do this is with the new
- // escapeCharacters() function:
- //
- // text = escapeCharacters(text,"\\",true);
- // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
- //
- // ...but we're sidestepping its use of the (slow) RegExp constructor
- // as an optimization for Firefox. This function gets called a LOT.
-
- text = text.replace(/\\(\\)/g,escapeCharacters_callback);
- text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
- return text;
-}
-
-
-var _DoAutoLinks = function(text) {
-
- text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1");
-
- // Email addresses:
-
- /*
- text = text.replace(/
- <
- (?:mailto:)?
- (
- [-.\w]+
- \@
- [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
- )
- >
- /gi, _DoAutoLinks_callback());
- */
- text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
- function(wholeMatch,m1) {
- return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
- }
- );
-
- return text;
-}
-
-
-var _EncodeEmailAddress = function(addr) {
-//
-// Input: an email address, e.g. "foo@example.com"
-//
-// Output: the email address as a mailto link, with each character
-// of the address encoded as either a decimal or hex entity, in
-// the hopes of foiling most address harvesting spam bots. E.g.:
-//
-// foo
-// @example.com
-//
-// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
-// mailing list:
-//
-
- // attacklab: why can't javascript speak hex?
- function char2hex(ch) {
- var hexDigits = '0123456789ABCDEF';
- var dec = ch.charCodeAt(0);
- return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
- }
-
- var encode = [
- function(ch){return ""+ch.charCodeAt(0)+";";},
- function(ch){return ""+char2hex(ch)+";";},
- function(ch){return ch;}
- ];
-
- addr = "mailto:" + addr;
-
- addr = addr.replace(/./g, function(ch) {
- if (ch == "@") {
- // this *must* be encoded. I insist.
- ch = encode[Math.floor(Math.random()*2)](ch);
- } else if (ch !=":") {
- // leave ':' alone (to spot mailto: later)
- var r = Math.random();
- // roughly 10% raw, 45% hex, 45% dec
- ch = (
- r > .9 ? encode[2](ch) :
- r > .45 ? encode[1](ch) :
- encode[0](ch)
- );
- }
- return ch;
- });
-
- addr = "" + addr + "";
- addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
-
- return addr;
-}
-
-
-var _UnescapeSpecialChars = function(text) {
-//
-// Swap back in all the special characters we've hidden.
-//
- text = text.replace(/~E(\d+)E/g,
- function(wholeMatch,m1) {
- var charCodeToReplace = parseInt(m1);
- return String.fromCharCode(charCodeToReplace);
- }
- );
- return text;
-}
-
-
-var _Outdent = function(text) {
-//
-// Remove one level of line-leading tabs or spaces
-//
-
- // attacklab: hack around Konqueror 3.5.4 bug:
- // "----------bug".replace(/^-/g,"") == "bug"
-
- text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
-
- // attacklab: clean up hack
- text = text.replace(/~0/g,"")
-
- return text;
-}
-
-var _Detab = function(text) {
-// attacklab: Detab's completely rewritten for speed.
-// In perl we could fix it by anchoring the regexp with \G.
-// In javascript we're less fortunate.
-
- // expand first n-1 tabs
- text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
-
- // replace the nth with two sentinels
- text = text.replace(/\t/g,"~A~B");
-
- // use the sentinel to anchor our regex so it doesn't explode
- text = text.replace(/~B(.+?)~A/g,
- function(wholeMatch,m1,m2) {
- var leadingText = m1;
- var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
-
- // there *must* be a better way to do this:
- for (var i=0; i";var D="
-
- {% blocktrans %}how to validate email info with {{send_email_key_url}} {{gravatar_faq_url}}{% endblocktrans %}
-
- {% endifequal %}
-
-
{% trans "what is gravatar" %}
-
{% trans "gravatar faq info" %}
-
-
-
{% trans "To register, do I need to create new password?" %}
-
{% trans "No, you don't have to. You can login through any service that supports OpenID, e.g. Google, Yahoo, AOL, etc." %}
- {% trans "Login now!" %} »
-
-
-
-
-
{% trans "Why other people can edit my questions/answers?" %}
-
{% trans "Goal of this site is..." %} {% trans "So questions and answers can be edited like wiki pages by experienced users of this site and this improves the overall quality of the knowledge base content." %}
- {% trans "If this approach is not for you, we respect your choice." %}
-
-
-
-
{% trans "Still have questions?" %}
-
{% blocktrans %}Please ask your question at {{ask_question_url}}, help make our community better!{% endblocktrans %}
-
-
{% blocktrans with question.get_close_reason_display as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %}
- {{ question.closed_by.username }}
- {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}
- {% if searchtag %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
- have total {{q_num}} questions tagged {{tagname}}
- {% plural %}
- have total {{q_num}} questions tagged {{tagname}}
- {% endblocktrans %}
- {% else %}
- {% if searchtitle %}
- {% if settings.USE_SPHINX_SEARCH %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions containing {{searchtitle}} in full text
- {% plural %}
- have total {{q_num}} questions containing {{searchtitle}} in full text
- {% endblocktrans %}
- {% else %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions containing {{searchtitle}}
- {% plural %}
- have total {{q_num}} questions containing {{searchtitle}}
- {% endblocktrans %}
- {% endif %}
- {% else %}
- {% if is_unanswered %}
- {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} unanswered questions
- {% plural %}
- have total {{q_num}} unanswered questions
- {% endblocktrans %}
- {% else %}
- {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions
- {% plural %}
- have total {{q_num}} questions
- {% endblocktrans %}
- {% endif %}
- {% endif %}
- {% endif %}
-
- {% ifequal tab_id "latest" %}
- {% trans "latest questions info" %}
- {% endifequal %}
-
- {% ifequal tab_id "active" %}
- {% trans "Questions are sorted by the time of last update." %}
- {% trans "Most recently answered ones are shown first." %}
- {% endifequal %}
-
- {% ifequal tab_id "hottest" %}
- {% trans "Questions sorted by number of responses." %}
- {% trans "Most answered questions are shown first." %}
- {% endifequal %}
-
- {% ifequal tab_id "mostvoted" %}
- {% trans "Questions are sorted by the number of votes." %}
- {% trans "Most voted questions are shown first." %}
- {% endifequal %}
-
-
-{% if request.user.is_authenticated %}
-{% include "tag_selector.html" %}
-{% endif %}
-
-
{% trans "Related tags" %}
-
- {% for tag in tags %}
- {{ tag.name }}
- × {{ tag.used_count|intcomma }}
-
- {% endfor %}
-
{% trans "The question was closed for the following reason " %}"{{ question.get_close_reason_display }}"{% trans "reason - leave blank in english" %} {{ question.closed_by.username }} {% trans "on "%} {% diff_date question.closed_at %}{% trans "date closed" %}
-
-
-{% if stag %}
- {% trans "All tags matching query" %} '{{ stag }}' {% trans "all tags - make this empty in english" %}:
-{% endif %}
-{% if not tags.object_list %}
- {% trans "Nothing found" %}
-{% endif %}
-
"',
+ 'upload image':'æˆ–è€…ä¸Šä¼ æœ¬åœ°å›¾ç‰‡ï¼š'
+};
+
+var i18nEn = {
+ 'need >15 points to report spam':'need >15 points to report spam ',
+ '>15 points requried to upvote':'>15 points required to upvote ',
+ 'tags cannot be empty':'please enter at least one tag',
+ 'anonymous users cannot vote':'sorry, anonymous users cannot vote ',
+ 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ',
+ 'to comment, need': '(to comment other people\'s posts, karma ',
+ 'please see':'please see ',
+ 'community karma points':' or more is necessary) - ',
+ 'upload image':'Upload image:',
+ 'enter image url':'enter URL of the image, e.g. http://www.example.com/image.jpg \"image title\"',
+ 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"',
+ 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap',
+ 'cannot pick own answer as best':'sorry, you cannot accept your own answer',
+ 'cannot revoke old vote':'sorry, older votes cannot be revoked',
+ 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?',
+ 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ',
+ 'confirm delete':'are you sure you want to delete this?',
+ 'anonymous users cannot delete/undelete':'sorry, anonymous users cannot delete or undelete posts',
+ 'post recovered':'your post is now restored!',
+ 'post deleted':'your post has been deleted',
+ 'confirm delete comment':'do you really want to delete this comment?',
+ 'can write':'have ',
+ 'tablimits info':'up to 5 tags, no more than 20 characters each',
+ 'content minchars': 'please enter more than {0} characters',
+ 'title minchars':"please enter at least {0} characters",
+ 'characters':'characters left',
+ 'cannot vote for own posts':'sorry, you cannot vote for your own posts',
+ 'cannot flag message as offensive twice':'cannot flag message as offensive twice ',
+ '>100 points required to downvote':'>100 points required to downvote '
+};
+
+var i18nEs = {
+ 'insufficient privilege':'privilegio insuficiente',
+ 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor',
+ 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar',
+ 'please login':'por favor inicie sesión',
+ 'anonymous users cannot vote':'usuarios anónimos no pueden votar',
+ '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente',
+ '>100 points required to downvote':'>100 puntos requeridos para votar negativamente',
+ 'please see': 'por favor vea',
+ 'cannot vote for own posts':'no se puede votar por sus propias publicaciones',
+ 'daily vote cap exhausted':'cuota de votos diarios excedida',
+ 'cannot revoke old vote':'no puede revocar un voto viejo',
+ 'please confirm offensive':"por favor confirme ofensiva",
+ 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas',
+ 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces',
+ 'flag offensive cap exhausted':'cuota para marcar ofensivas ha sido excedida',
+ 'need >15 points to report spam':"necesita >15 puntos para reportar spam",
+ 'confirm delete':"¿Está seguro que desea borrar esto?",
+ 'anonymous users cannot delete/undelete':"usuarios anónimos no pueden borrar o recuperar publicaciones",
+ 'post recovered':"publicación recuperada",
+ 'post deleted':"publicación borrada。",
+ 'add comment':'agregar comentario',
+ 'community karma points':'reputación comunitaria',
+ 'to comment, need':'para comentar, necesita reputación',
+ 'delete this comment':'borrar este comentario',
+ 'hide comments':"ocultar comentarios",
+ 'add a comment':"agregar comentarios",
+ 'comments':"comentarios",
+ 'confirm delete comment':"¿Realmente desea borrar este comentario?",
+ 'characters':'caracteres faltantes',
+ 'can write':'tiene ',
+ 'click to close':'haga click para cerrar',
+ 'loading...':'cargando...',
+ 'tags cannot be empty':'las etiquetas no pueden estar vacÃas',
+ 'tablimits info':"hasta 5 etiquetas de no mas de 20 caracteres cada una",
+ 'content cannot be empty':'el contenido no puede estar vacÃo',
+ 'content minchars': 'por favor introduzca mas de {0} caracteres',
+ 'please enter title':'por favor ingrese un tÃtulo',
+ 'title minchars':"por favor introduzca al menos {0} caracteres",
+ 'delete':'borrar',
+ 'undelete': 'recuperar',
+ 'bold': 'negrita',
+ 'italic':'cursiva',
+ 'link':'enlace',
+ 'quote':'citar',
+ 'preformatted text':'texto preformateado',
+ 'image':'imagen',
+ 'numbered list':'lista numerada',
+ 'bulleted list':'lista no numerada',
+ 'heading':'æ ‡é¢˜',
+ 'horizontal bar':'barra horizontal',
+ 'undo':'deshacer',
+ 'redo':'rehacer',
+ 'enter image url':'introduzca la URL de la imagen, por ejemplo: http://www.example.com/image.jpg \"titulo de imagen\"',
+ 'enter url':'introduzca direcciones web, ejemplo: http://www.cnprog.com/ \"titulo del enlace\""',
+ 'upload image':'cargar imagen:',
+ 'questions/' : 'preguntas/',
+ 'vote/' : 'votar/'
+};
+
+var i18n = {
+ 'en':i18nEn,
+ 'zh_CN':i18nZh,
+ 'es':i18nEs
+};
+
+var i18n_dict = i18n[i18nLang];
diff --git a/forum/media/js/com.cnprog.post.js b/forum/media/js/com.cnprog.post.js
new file mode 100644
index 00000000..5469a374
--- /dev/null
+++ b/forum/media/js/com.cnprog.post.js
@@ -0,0 +1,691 @@
+/*
+Scripts for cnprog.com
+Project Name: Lanai
+All Rights Resevred 2008. CNPROG.COM
+*/
+var lanai =
+{
+ /**
+ * Finds any
");});
+
+ // atx-style headers:
+ // # Header 1
+ // ## Header 2
+ // ## Header 2 with closing hashes ##
+ // ...
+ // ###### Header 6
+ //
+
+ /*
+ text = text.replace(/
+ ^(\#{1,6}) // $1 = string of #'s
+ [ \t]*
+ (.+?) // $2 = Header text
+ [ \t]*
+ \#* // optional closing #'s (not counted)
+ \n+
+ /gm, function() {...});
+ */
+
+ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
+ function(wholeMatch,m1,m2) {
+ var h_level = m1.length;
+ return hashBlock("" + _RunSpanGamut(m2) + "");
+ });
+
+ return text;
+}
+
+// This declaration keeps Dojo compressor from outputting garbage:
+var _ProcessListItems;
+
+var _DoLists = function(text) {
+//
+// Form HTML ordered (numbered) and unordered (bulleted) lists.
+//
+
+ // attacklab: add sentinel to hack around khtml/safari bug:
+ // http://bugs.webkit.org/show_bug.cgi?id=11231
+ text += "~0";
+
+ // Re-usable pattern to match any entirel ul or ol list:
+
+ /*
+ var whole_list = /
+ ( // $1 = whole list
+ ( // $2
+ [ ]{0,3} // attacklab: g_tab_width - 1
+ ([*+-]|\d+[.]) // $3 = first list item marker
+ [ \t]+
+ )
+ [^\r]+?
+ ( // $4
+ ~0 // sentinel for workaround; should be $
+ |
+ \n{2,}
+ (?=\S)
+ (?! // Negative lookahead for another list item marker
+ [ \t]*
+ (?:[*+-]|\d+[.])[ \t]+
+ )
+ )
+ )/g
+ */
+ var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
+
+ if (g_list_level) {
+ text = text.replace(whole_list,function(wholeMatch,m1,m2) {
+ var list = m1;
+ var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
+
+ // Turn double returns into triple returns, so that we can make a
+ // paragraph for the last item in a list, if necessary:
+ list = list.replace(/\n{2,}/g,"\n\n\n");;
+ var result = _ProcessListItems(list);
+
+ // Trim any trailing whitespace, to put the closing `$list_type>`
+ // up on the preceding line, to get it past the current stupid
+ // HTML block parser. This is a hack to work around the terrible
+ // hack that is the HTML block parser.
+ result = result.replace(/\s+$/,"");
+ result = "<"+list_type+">" + result + ""+list_type+">\n";
+ return result;
+ });
+ } else {
+ whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
+ text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
+ var runup = m1;
+ var list = m2;
+
+ var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
+ // Turn double returns into triple returns, so that we can make a
+ // paragraph for the last item in a list, if necessary:
+ var list = list.replace(/\n{2,}/g,"\n\n\n");;
+ var result = _ProcessListItems(list);
+ result = runup + "<"+list_type+">\n" + result + ""+list_type+">\n";
+ return result;
+ });
+ }
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/,"");
+
+ return text;
+}
+
+_ProcessListItems = function(list_str) {
+//
+// Process the contents of a single ordered or unordered list, splitting it
+// into individual list items.
+//
+ // The $g_list_level global keeps track of when we're inside a list.
+ // Each time we enter a list, we increment it; when we leave a list,
+ // we decrement. If it's zero, we're not in a list anymore.
+ //
+ // We do this because when we're not inside a list, we want to treat
+ // something like this:
+ //
+ // I recommend upgrading to version
+ // 8. Oops, now this line is treated
+ // as a sub-list.
+ //
+ // As a single paragraph, despite the fact that the second line starts
+ // with a digit-period-space sequence.
+ //
+ // Whereas when we're inside a list (or sub-list), that line will be
+ // treated as the start of a sub-list. What a kludge, huh? This is
+ // an aspect of Markdown's syntax that's hard to parse perfectly
+ // without resorting to mind-reading. Perhaps the solution is to
+ // change the syntax rules such that sub-lists must start with a
+ // starting cardinal number; e.g. "1." or "a.".
+
+ g_list_level++;
+
+ // trim trailing blank lines:
+ list_str = list_str.replace(/\n{2,}$/,"\n");
+
+ // attacklab: add sentinel to emulate \z
+ list_str += "~0";
+
+ /*
+ list_str = list_str.replace(/
+ (\n)? // leading line = $1
+ (^[ \t]*) // leading whitespace = $2
+ ([*+-]|\d+[.]) [ \t]+ // list marker = $3
+ ([^\r]+? // list item text = $4
+ (\n{1,2}))
+ (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
+ /gm, function(){...});
+ */
+ list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
+ function(wholeMatch,m1,m2,m3,m4){
+ var item = m4;
+ var leading_line = m1;
+ var leading_space = m2;
+
+ if (leading_line || (item.search(/\n{2,}/)>-1)) {
+ item = _RunBlockGamut(_Outdent(item));
+ }
+ else {
+ // Recursion for sub-lists:
+ item = _DoLists(_Outdent(item));
+ item = item.replace(/\n$/,""); // chomp(item)
+ item = _RunSpanGamut(item);
+ }
+
+ return "
` blocks.
+//
+
+ /*
+ text = text.replace(text,
+ /(?:\n\n|^)
+ ( // $1 = the code block -- one or more lines, starting with a space/tab
+ (?:
+ (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
+ .*\n+
+ )+
+ )
+ (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
+ /g,function(){...});
+ */
+
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+ text += "~0";
+
+ text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
+ function(wholeMatch,m1,m2) {
+ var codeblock = m1;
+ var nextChar = m2;
+
+ codeblock = _EncodeCode( _Outdent(codeblock));
+ codeblock = _Detab(codeblock);
+ codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
+ codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
+
+ codeblock = "
" + codeblock + "\n
";
+
+ return hashBlock(codeblock) + nextChar;
+ }
+ );
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/,"");
+
+ return text;
+}
+
+var hashBlock = function(text) {
+ text = text.replace(/(^\n+|\n+$)/g,"");
+ return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
+}
+
+
+var _DoCodeSpans = function(text) {
+//
+// * Backtick quotes are used for spans.
+//
+// * You can use multiple backticks as the delimiters if you want to
+// include literal backticks in the code span. So, this input:
+//
+// Just type ``foo `bar` baz`` at the prompt.
+//
+// Will translate to:
+//
+//
Just type foo `bar` baz at the prompt.
+//
+// There's no arbitrary limit to the number of backticks you
+// can use as delimters. If you need three consecutive backticks
+// in your code, use four for delimiters, etc.
+//
+// * You can use spaces to get literal backticks at the edges:
+//
+// ... type `` `bar` `` ...
+//
+// Turns to:
+//
+// ... type `bar` ...
+//
+
+ /*
+ text = text.replace(/
+ (^|[^\\]) // Character before opening ` can't be a backslash
+ (`+) // $2 = Opening run of `
+ ( // $3 = The code block
+ [^\r]*?
+ [^`] // attacklab: work around lack of lookbehind
+ )
+ \2 // Matching closer
+ (?!`)
+ /gm, function(){...});
+ */
+
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
+ function(wholeMatch,m1,m2,m3,m4) {
+ var c = m3;
+ c = c.replace(/^([ \t]*)/g,""); // leading whitespace
+ c = c.replace(/[ \t]*$/g,""); // trailing whitespace
+ c = _EncodeCode(c);
+ return m1+""+c+"";
+ });
+
+ return text;
+}
+
+
+var _EncodeCode = function(text) {
+//
+// Encode/escape certain characters inside Markdown code runs.
+// The point is that in code, these characters are literals,
+// and lose their special Markdown meanings.
+//
+ // Encode all ampersands; HTML entities are not
+ // entities within a Markdown code span.
+ text = text.replace(/&/g,"&");
+
+ // Do the angle bracket song and dance:
+ text = text.replace(//g,">");
+
+ // Now, escape characters that are magic in Markdown:
+ text = escapeCharacters(text,"\*_{}[]\\",false);
+
+// jj the line above breaks this:
+//---
+
+//* Item
+
+// 1. Subitem
+
+// special char: *
+//---
+
+ return text;
+}
+
+
+var _DoItalicsAndBold = function(text) {
+
+ // must go first:
+ text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,
+ "$2");
+
+ text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
+ "$2");
+
+ return text;
+}
+
+
+var _DoBlockQuotes = function(text) {
+
+ /*
+ text = text.replace(/
+ ( // Wrap whole match in $1
+ (
+ ^[ \t]*>[ \t]? // '>' at the start of a line
+ .+\n // rest of the first line
+ (.+\n)* // subsequent consecutive lines
+ \n* // blanks
+ )+
+ )
+ /gm, function(){...});
+ */
+
+ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
+ function(wholeMatch,m1) {
+ var bq = m1;
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
+
+ // attacklab: clean up hack
+ bq = bq.replace(/~0/g,"");
+
+ bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
+ bq = _RunBlockGamut(bq); // recurse
+
+ bq = bq.replace(/(^|\n)/g,"$1 ");
+ // These leading spaces screw with
content, so we need to fix that:
+ bq = bq.replace(
+ /(\s*
[^\r]+?<\/pre>)/gm,
+ function(wholeMatch,m1) {
+ var pre = m1;
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ pre = pre.replace(/^ /mg,"~0");
+ pre = pre.replace(/~0/g,"");
+ return pre;
+ });
+
+ return hashBlock("
\n" + bq + "\n
");
+ });
+ return text;
+}
+
+
+var _FormParagraphs = function(text) {
+//
+// Params:
+// $text - string to process with html
tags
+//
+
+ // Strip leading and trailing lines:
+ text = text.replace(/^\n+/g,"");
+ text = text.replace(/\n+$/g,"");
+
+ var grafs = text.split(/\n{2,}/g);
+ var grafsOut = new Array();
+
+ //
+ // Wrap
tags.
+ //
+ var end = grafs.length;
+ for (var i=0; i= 0) {
+ grafsOut.push(str);
+ }
+ else if (str.search(/\S/) >= 0) {
+ str = _RunSpanGamut(str);
+ str = str.replace(/^([ \t]*)/g,"
");
+ str += "
"
+ grafsOut.push(str);
+ }
+
+ }
+
+ //
+ // Unhashify HTML blocks
+ //
+ end = grafsOut.length;
+ for (var i=0; i= 0) {
+ var blockText = g_html_blocks[RegExp.$1];
+ blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
+ grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
+ }
+ }
+
+ return grafsOut.join("\n\n");
+}
+
+
+var _EncodeAmpsAndAngles = function(text) {
+// Smart processing for ampersands and angle brackets that need to be encoded.
+
+ // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
+ // http://bumppo.net/projects/amputator/
+ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
+
+ // Encode naked <'s
+ text = text.replace(/<(?![a-z\/?\$!])/gi,"<");
+
+ return text;
+}
+
+
+var _EncodeBackslashEscapes = function(text) {
+//
+// Parameter: String.
+// Returns: The string, with after processing the following backslash
+// escape sequences.
+//
+
+ // attacklab: The polite way to do this is with the new
+ // escapeCharacters() function:
+ //
+ // text = escapeCharacters(text,"\\",true);
+ // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
+ //
+ // ...but we're sidestepping its use of the (slow) RegExp constructor
+ // as an optimization for Firefox. This function gets called a LOT.
+
+ text = text.replace(/\\(\\)/g,escapeCharacters_callback);
+ text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
+ return text;
+}
+
+
+var _DoAutoLinks = function(text) {
+
+ text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1");
+
+ // Email addresses:
+
+ /*
+ text = text.replace(/
+ <
+ (?:mailto:)?
+ (
+ [-.\w]+
+ \@
+ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
+ )
+ >
+ /gi, _DoAutoLinks_callback());
+ */
+ text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
+ function(wholeMatch,m1) {
+ return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
+ }
+ );
+
+ return text;
+}
+
+
+var _EncodeEmailAddress = function(addr) {
+//
+// Input: an email address, e.g. "foo@example.com"
+//
+// Output: the email address as a mailto link, with each character
+// of the address encoded as either a decimal or hex entity, in
+// the hopes of foiling most address harvesting spam bots. E.g.:
+//
+// foo
+// @example.com
+//
+// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
+// mailing list:
+//
+
+ // attacklab: why can't javascript speak hex?
+ function char2hex(ch) {
+ var hexDigits = '0123456789ABCDEF';
+ var dec = ch.charCodeAt(0);
+ return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
+ }
+
+ var encode = [
+ function(ch){return ""+ch.charCodeAt(0)+";";},
+ function(ch){return ""+char2hex(ch)+";";},
+ function(ch){return ch;}
+ ];
+
+ addr = "mailto:" + addr;
+
+ addr = addr.replace(/./g, function(ch) {
+ if (ch == "@") {
+ // this *must* be encoded. I insist.
+ ch = encode[Math.floor(Math.random()*2)](ch);
+ } else if (ch !=":") {
+ // leave ':' alone (to spot mailto: later)
+ var r = Math.random();
+ // roughly 10% raw, 45% hex, 45% dec
+ ch = (
+ r > .9 ? encode[2](ch) :
+ r > .45 ? encode[1](ch) :
+ encode[0](ch)
+ );
+ }
+ return ch;
+ });
+
+ addr = "" + addr + "";
+ addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
+
+ return addr;
+}
+
+
+var _UnescapeSpecialChars = function(text) {
+//
+// Swap back in all the special characters we've hidden.
+//
+ text = text.replace(/~E(\d+)E/g,
+ function(wholeMatch,m1) {
+ var charCodeToReplace = parseInt(m1);
+ return String.fromCharCode(charCodeToReplace);
+ }
+ );
+ return text;
+}
+
+
+var _Outdent = function(text) {
+//
+// Remove one level of line-leading tabs or spaces
+//
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
+
+ // attacklab: clean up hack
+ text = text.replace(/~0/g,"")
+
+ return text;
+}
+
+var _Detab = function(text) {
+// attacklab: Detab's completely rewritten for speed.
+// In perl we could fix it by anchoring the regexp with \G.
+// In javascript we're less fortunate.
+
+ // expand first n-1 tabs
+ text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
+
+ // replace the nth with two sentinels
+ text = text.replace(/\t/g,"~A~B");
+
+ // use the sentinel to anchor our regex so it doesn't explode
+ text = text.replace(/~B(.+?)~A/g,
+ function(wholeMatch,m1,m2) {
+ var leadingText = m1;
+ var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
+
+ // there *must* be a better way to do this:
+ for (var i=0; i";var D="
");});
-
- // atx-style headers:
- // # Header 1
- // ## Header 2
- // ## Header 2 with closing hashes ##
- // ...
- // ###### Header 6
- //
-
- /*
- text = text.replace(/
- ^(\#{1,6}) // $1 = string of #'s
- [ \t]*
- (.+?) // $2 = Header text
- [ \t]*
- \#* // optional closing #'s (not counted)
- \n+
- /gm, function() {...});
- */
-
- text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
- function(wholeMatch,m1,m2) {
- var h_level = m1.length;
- return hashBlock("" + _RunSpanGamut(m2) + "");
- });
-
- return text;
-}
-
-// This declaration keeps Dojo compressor from outputting garbage:
-var _ProcessListItems;
-
-var _DoLists = function(text) {
-//
-// Form HTML ordered (numbered) and unordered (bulleted) lists.
-//
-
- // attacklab: add sentinel to hack around khtml/safari bug:
- // http://bugs.webkit.org/show_bug.cgi?id=11231
- text += "~0";
-
- // Re-usable pattern to match any entirel ul or ol list:
-
- /*
- var whole_list = /
- ( // $1 = whole list
- ( // $2
- [ ]{0,3} // attacklab: g_tab_width - 1
- ([*+-]|\d+[.]) // $3 = first list item marker
- [ \t]+
- )
- [^\r]+?
- ( // $4
- ~0 // sentinel for workaround; should be $
- |
- \n{2,}
- (?=\S)
- (?! // Negative lookahead for another list item marker
- [ \t]*
- (?:[*+-]|\d+[.])[ \t]+
- )
- )
- )/g
- */
- var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
-
- if (g_list_level) {
- text = text.replace(whole_list,function(wholeMatch,m1,m2) {
- var list = m1;
- var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
-
- // Turn double returns into triple returns, so that we can make a
- // paragraph for the last item in a list, if necessary:
- list = list.replace(/\n{2,}/g,"\n\n\n");;
- var result = _ProcessListItems(list);
-
- // Trim any trailing whitespace, to put the closing `$list_type>`
- // up on the preceding line, to get it past the current stupid
- // HTML block parser. This is a hack to work around the terrible
- // hack that is the HTML block parser.
- result = result.replace(/\s+$/,"");
- result = "<"+list_type+">" + result + ""+list_type+">\n";
- return result;
- });
- } else {
- whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
- text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
- var runup = m1;
- var list = m2;
-
- var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
- // Turn double returns into triple returns, so that we can make a
- // paragraph for the last item in a list, if necessary:
- var list = list.replace(/\n{2,}/g,"\n\n\n");;
- var result = _ProcessListItems(list);
- result = runup + "<"+list_type+">\n" + result + ""+list_type+">\n";
- return result;
- });
- }
-
- // attacklab: strip sentinel
- text = text.replace(/~0/,"");
-
- return text;
-}
-
-_ProcessListItems = function(list_str) {
-//
-// Process the contents of a single ordered or unordered list, splitting it
-// into individual list items.
-//
- // The $g_list_level global keeps track of when we're inside a list.
- // Each time we enter a list, we increment it; when we leave a list,
- // we decrement. If it's zero, we're not in a list anymore.
- //
- // We do this because when we're not inside a list, we want to treat
- // something like this:
- //
- // I recommend upgrading to version
- // 8. Oops, now this line is treated
- // as a sub-list.
- //
- // As a single paragraph, despite the fact that the second line starts
- // with a digit-period-space sequence.
- //
- // Whereas when we're inside a list (or sub-list), that line will be
- // treated as the start of a sub-list. What a kludge, huh? This is
- // an aspect of Markdown's syntax that's hard to parse perfectly
- // without resorting to mind-reading. Perhaps the solution is to
- // change the syntax rules such that sub-lists must start with a
- // starting cardinal number; e.g. "1." or "a.".
-
- g_list_level++;
-
- // trim trailing blank lines:
- list_str = list_str.replace(/\n{2,}$/,"\n");
-
- // attacklab: add sentinel to emulate \z
- list_str += "~0";
-
- /*
- list_str = list_str.replace(/
- (\n)? // leading line = $1
- (^[ \t]*) // leading whitespace = $2
- ([*+-]|\d+[.]) [ \t]+ // list marker = $3
- ([^\r]+? // list item text = $4
- (\n{1,2}))
- (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
- /gm, function(){...});
- */
- list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
- function(wholeMatch,m1,m2,m3,m4){
- var item = m4;
- var leading_line = m1;
- var leading_space = m2;
-
- if (leading_line || (item.search(/\n{2,}/)>-1)) {
- item = _RunBlockGamut(_Outdent(item));
- }
- else {
- // Recursion for sub-lists:
- item = _DoLists(_Outdent(item));
- item = item.replace(/\n$/,""); // chomp(item)
- item = _RunSpanGamut(item);
- }
-
- return "
` blocks.
-//
-
- /*
- text = text.replace(text,
- /(?:\n\n|^)
- ( // $1 = the code block -- one or more lines, starting with a space/tab
- (?:
- (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
- .*\n+
- )+
- )
- (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
- /g,function(){...});
- */
-
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
- text += "~0";
-
- text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
- function(wholeMatch,m1,m2) {
- var codeblock = m1;
- var nextChar = m2;
-
- codeblock = _EncodeCode( _Outdent(codeblock));
- codeblock = _Detab(codeblock);
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
-
- codeblock = "
" + codeblock + "\n
";
-
- return hashBlock(codeblock) + nextChar;
- }
- );
-
- // attacklab: strip sentinel
- text = text.replace(/~0/,"");
-
- return text;
-}
-
-var hashBlock = function(text) {
- text = text.replace(/(^\n+|\n+$)/g,"");
- return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
-}
-
-
-var _DoCodeSpans = function(text) {
-//
-// * Backtick quotes are used for spans.
-//
-// * You can use multiple backticks as the delimiters if you want to
-// include literal backticks in the code span. So, this input:
-//
-// Just type ``foo `bar` baz`` at the prompt.
-//
-// Will translate to:
-//
-//
Just type foo `bar` baz at the prompt.
-//
-// There's no arbitrary limit to the number of backticks you
-// can use as delimters. If you need three consecutive backticks
-// in your code, use four for delimiters, etc.
-//
-// * You can use spaces to get literal backticks at the edges:
-//
-// ... type `` `bar` `` ...
-//
-// Turns to:
-//
-// ... type `bar` ...
-//
-
- /*
- text = text.replace(/
- (^|[^\\]) // Character before opening ` can't be a backslash
- (`+) // $2 = Opening run of `
- ( // $3 = The code block
- [^\r]*?
- [^`] // attacklab: work around lack of lookbehind
- )
- \2 // Matching closer
- (?!`)
- /gm, function(){...});
- */
-
- text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
- function(wholeMatch,m1,m2,m3,m4) {
- var c = m3;
- c = c.replace(/^([ \t]*)/g,""); // leading whitespace
- c = c.replace(/[ \t]*$/g,""); // trailing whitespace
- c = _EncodeCode(c);
- return m1+""+c+"";
- });
-
- return text;
-}
-
-
-var _EncodeCode = function(text) {
-//
-// Encode/escape certain characters inside Markdown code runs.
-// The point is that in code, these characters are literals,
-// and lose their special Markdown meanings.
-//
- // Encode all ampersands; HTML entities are not
- // entities within a Markdown code span.
- text = text.replace(/&/g,"&");
-
- // Do the angle bracket song and dance:
- text = text.replace(//g,">");
-
- // Now, escape characters that are magic in Markdown:
- text = escapeCharacters(text,"\*_{}[]\\",false);
-
-// jj the line above breaks this:
-//---
-
-//* Item
-
-// 1. Subitem
-
-// special char: *
-//---
-
- return text;
-}
-
-
-var _DoItalicsAndBold = function(text) {
-
- // must go first:
- text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,
- "$2");
-
- text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
- "$2");
-
- return text;
-}
-
-
-var _DoBlockQuotes = function(text) {
-
- /*
- text = text.replace(/
- ( // Wrap whole match in $1
- (
- ^[ \t]*>[ \t]? // '>' at the start of a line
- .+\n // rest of the first line
- (.+\n)* // subsequent consecutive lines
- \n* // blanks
- )+
- )
- /gm, function(){...});
- */
-
- text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
- function(wholeMatch,m1) {
- var bq = m1;
-
- // attacklab: hack around Konqueror 3.5.4 bug:
- // "----------bug".replace(/^-/g,"") == "bug"
-
- bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
-
- // attacklab: clean up hack
- bq = bq.replace(/~0/g,"");
-
- bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
- bq = _RunBlockGamut(bq); // recurse
-
- bq = bq.replace(/(^|\n)/g,"$1 ");
- // These leading spaces screw with
content, so we need to fix that:
- bq = bq.replace(
- /(\s*
[^\r]+?<\/pre>)/gm,
- function(wholeMatch,m1) {
- var pre = m1;
- // attacklab: hack around Konqueror 3.5.4 bug:
- pre = pre.replace(/^ /mg,"~0");
- pre = pre.replace(/~0/g,"");
- return pre;
- });
-
- return hashBlock("
\n" + bq + "\n
");
- });
- return text;
-}
-
-
-var _FormParagraphs = function(text) {
-//
-// Params:
-// $text - string to process with html
tags
-//
-
- // Strip leading and trailing lines:
- text = text.replace(/^\n+/g,"");
- text = text.replace(/\n+$/g,"");
-
- var grafs = text.split(/\n{2,}/g);
- var grafsOut = new Array();
-
- //
- // Wrap
tags.
- //
- var end = grafs.length;
- for (var i=0; i= 0) {
- grafsOut.push(str);
- }
- else if (str.search(/\S/) >= 0) {
- str = _RunSpanGamut(str);
- str = str.replace(/^([ \t]*)/g,"
");
- str += "
"
- grafsOut.push(str);
- }
-
- }
-
- //
- // Unhashify HTML blocks
- //
- end = grafsOut.length;
- for (var i=0; i= 0) {
- var blockText = g_html_blocks[RegExp.$1];
- blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
- grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
- }
- }
-
- return grafsOut.join("\n\n");
-}
-
-
-var _EncodeAmpsAndAngles = function(text) {
-// Smart processing for ampersands and angle brackets that need to be encoded.
-
- // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
- // http://bumppo.net/projects/amputator/
- text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
-
- // Encode naked <'s
- text = text.replace(/<(?![a-z\/?\$!])/gi,"<");
-
- return text;
-}
-
-
-var _EncodeBackslashEscapes = function(text) {
-//
-// Parameter: String.
-// Returns: The string, with after processing the following backslash
-// escape sequences.
-//
-
- // attacklab: The polite way to do this is with the new
- // escapeCharacters() function:
- //
- // text = escapeCharacters(text,"\\",true);
- // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
- //
- // ...but we're sidestepping its use of the (slow) RegExp constructor
- // as an optimization for Firefox. This function gets called a LOT.
-
- text = text.replace(/\\(\\)/g,escapeCharacters_callback);
- text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
- return text;
-}
-
-
-var _DoAutoLinks = function(text) {
-
- text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1");
-
- // Email addresses:
-
- /*
- text = text.replace(/
- <
- (?:mailto:)?
- (
- [-.\w]+
- \@
- [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
- )
- >
- /gi, _DoAutoLinks_callback());
- */
- text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
- function(wholeMatch,m1) {
- return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
- }
- );
-
- return text;
-}
-
-
-var _EncodeEmailAddress = function(addr) {
-//
-// Input: an email address, e.g. "foo@example.com"
-//
-// Output: the email address as a mailto link, with each character
-// of the address encoded as either a decimal or hex entity, in
-// the hopes of foiling most address harvesting spam bots. E.g.:
-//
-// foo
-// @example.com
-//
-// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
-// mailing list:
-//
-
- // attacklab: why can't javascript speak hex?
- function char2hex(ch) {
- var hexDigits = '0123456789ABCDEF';
- var dec = ch.charCodeAt(0);
- return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
- }
-
- var encode = [
- function(ch){return ""+ch.charCodeAt(0)+";";},
- function(ch){return ""+char2hex(ch)+";";},
- function(ch){return ch;}
- ];
-
- addr = "mailto:" + addr;
-
- addr = addr.replace(/./g, function(ch) {
- if (ch == "@") {
- // this *must* be encoded. I insist.
- ch = encode[Math.floor(Math.random()*2)](ch);
- } else if (ch !=":") {
- // leave ':' alone (to spot mailto: later)
- var r = Math.random();
- // roughly 10% raw, 45% hex, 45% dec
- ch = (
- r > .9 ? encode[2](ch) :
- r > .45 ? encode[1](ch) :
- encode[0](ch)
- );
- }
- return ch;
- });
-
- addr = "" + addr + "";
- addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
-
- return addr;
-}
-
-
-var _UnescapeSpecialChars = function(text) {
-//
-// Swap back in all the special characters we've hidden.
-//
- text = text.replace(/~E(\d+)E/g,
- function(wholeMatch,m1) {
- var charCodeToReplace = parseInt(m1);
- return String.fromCharCode(charCodeToReplace);
- }
- );
- return text;
-}
-
-
-var _Outdent = function(text) {
-//
-// Remove one level of line-leading tabs or spaces
-//
-
- // attacklab: hack around Konqueror 3.5.4 bug:
- // "----------bug".replace(/^-/g,"") == "bug"
-
- text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
-
- // attacklab: clean up hack
- text = text.replace(/~0/g,"")
-
- return text;
-}
-
-var _Detab = function(text) {
-// attacklab: Detab's completely rewritten for speed.
-// In perl we could fix it by anchoring the regexp with \G.
-// In javascript we're less fortunate.
-
- // expand first n-1 tabs
- text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
-
- // replace the nth with two sentinels
- text = text.replace(/\t/g,"~A~B");
-
- // use the sentinel to anchor our regex so it doesn't explode
- text = text.replace(/~B(.+?)~A/g,
- function(wholeMatch,m1,m2) {
- var leadingText = m1;
- var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
-
- // there *must* be a better way to do this:
- for (var i=0; i";var D="
");});
-
- // atx-style headers:
- // # Header 1
- // ## Header 2
- // ## Header 2 with closing hashes ##
- // ...
- // ###### Header 6
- //
-
- /*
- text = text.replace(/
- ^(\#{1,6}) // $1 = string of #'s
- [ \t]*
- (.+?) // $2 = Header text
- [ \t]*
- \#* // optional closing #'s (not counted)
- \n+
- /gm, function() {...});
- */
-
- text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
- function(wholeMatch,m1,m2) {
- var h_level = m1.length;
- return hashBlock("" + _RunSpanGamut(m2) + "");
- });
-
- return text;
-}
-
-// This declaration keeps Dojo compressor from outputting garbage:
-var _ProcessListItems;
-
-var _DoLists = function(text) {
-//
-// Form HTML ordered (numbered) and unordered (bulleted) lists.
-//
-
- // attacklab: add sentinel to hack around khtml/safari bug:
- // http://bugs.webkit.org/show_bug.cgi?id=11231
- text += "~0";
-
- // Re-usable pattern to match any entirel ul or ol list:
-
- /*
- var whole_list = /
- ( // $1 = whole list
- ( // $2
- [ ]{0,3} // attacklab: g_tab_width - 1
- ([*+-]|\d+[.]) // $3 = first list item marker
- [ \t]+
- )
- [^\r]+?
- ( // $4
- ~0 // sentinel for workaround; should be $
- |
- \n{2,}
- (?=\S)
- (?! // Negative lookahead for another list item marker
- [ \t]*
- (?:[*+-]|\d+[.])[ \t]+
- )
- )
- )/g
- */
- var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
-
- if (g_list_level) {
- text = text.replace(whole_list,function(wholeMatch,m1,m2) {
- var list = m1;
- var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
-
- // Turn double returns into triple returns, so that we can make a
- // paragraph for the last item in a list, if necessary:
- list = list.replace(/\n{2,}/g,"\n\n\n");;
- var result = _ProcessListItems(list);
-
- // Trim any trailing whitespace, to put the closing `$list_type>`
- // up on the preceding line, to get it past the current stupid
- // HTML block parser. This is a hack to work around the terrible
- // hack that is the HTML block parser.
- result = result.replace(/\s+$/,"");
- result = "<"+list_type+">" + result + ""+list_type+">\n";
- return result;
- });
- } else {
- whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
- text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
- var runup = m1;
- var list = m2;
-
- var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
- // Turn double returns into triple returns, so that we can make a
- // paragraph for the last item in a list, if necessary:
- var list = list.replace(/\n{2,}/g,"\n\n\n");;
- var result = _ProcessListItems(list);
- result = runup + "<"+list_type+">\n" + result + ""+list_type+">\n";
- return result;
- });
- }
-
- // attacklab: strip sentinel
- text = text.replace(/~0/,"");
-
- return text;
-}
-
-_ProcessListItems = function(list_str) {
-//
-// Process the contents of a single ordered or unordered list, splitting it
-// into individual list items.
-//
- // The $g_list_level global keeps track of when we're inside a list.
- // Each time we enter a list, we increment it; when we leave a list,
- // we decrement. If it's zero, we're not in a list anymore.
- //
- // We do this because when we're not inside a list, we want to treat
- // something like this:
- //
- // I recommend upgrading to version
- // 8. Oops, now this line is treated
- // as a sub-list.
- //
- // As a single paragraph, despite the fact that the second line starts
- // with a digit-period-space sequence.
- //
- // Whereas when we're inside a list (or sub-list), that line will be
- // treated as the start of a sub-list. What a kludge, huh? This is
- // an aspect of Markdown's syntax that's hard to parse perfectly
- // without resorting to mind-reading. Perhaps the solution is to
- // change the syntax rules such that sub-lists must start with a
- // starting cardinal number; e.g. "1." or "a.".
-
- g_list_level++;
-
- // trim trailing blank lines:
- list_str = list_str.replace(/\n{2,}$/,"\n");
-
- // attacklab: add sentinel to emulate \z
- list_str += "~0";
-
- /*
- list_str = list_str.replace(/
- (\n)? // leading line = $1
- (^[ \t]*) // leading whitespace = $2
- ([*+-]|\d+[.]) [ \t]+ // list marker = $3
- ([^\r]+? // list item text = $4
- (\n{1,2}))
- (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
- /gm, function(){...});
- */
- list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
- function(wholeMatch,m1,m2,m3,m4){
- var item = m4;
- var leading_line = m1;
- var leading_space = m2;
-
- if (leading_line || (item.search(/\n{2,}/)>-1)) {
- item = _RunBlockGamut(_Outdent(item));
- }
- else {
- // Recursion for sub-lists:
- item = _DoLists(_Outdent(item));
- item = item.replace(/\n$/,""); // chomp(item)
- item = _RunSpanGamut(item);
- }
-
- return "
` blocks.
-//
-
- /*
- text = text.replace(text,
- /(?:\n\n|^)
- ( // $1 = the code block -- one or more lines, starting with a space/tab
- (?:
- (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
- .*\n+
- )+
- )
- (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
- /g,function(){...});
- */
-
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
- text += "~0";
-
- text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
- function(wholeMatch,m1,m2) {
- var codeblock = m1;
- var nextChar = m2;
-
- codeblock = _EncodeCode( _Outdent(codeblock));
- codeblock = _Detab(codeblock);
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
-
- codeblock = "
" + codeblock + "\n
";
-
- return hashBlock(codeblock) + nextChar;
- }
- );
-
- // attacklab: strip sentinel
- text = text.replace(/~0/,"");
-
- return text;
-}
-
-var hashBlock = function(text) {
- text = text.replace(/(^\n+|\n+$)/g,"");
- return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
-}
-
-
-var _DoCodeSpans = function(text) {
-//
-// * Backtick quotes are used for spans.
-//
-// * You can use multiple backticks as the delimiters if you want to
-// include literal backticks in the code span. So, this input:
-//
-// Just type ``foo `bar` baz`` at the prompt.
-//
-// Will translate to:
-//
-//
Just type foo `bar` baz at the prompt.
-//
-// There's no arbitrary limit to the number of backticks you
-// can use as delimters. If you need three consecutive backticks
-// in your code, use four for delimiters, etc.
-//
-// * You can use spaces to get literal backticks at the edges:
-//
-// ... type `` `bar` `` ...
-//
-// Turns to:
-//
-// ... type `bar` ...
-//
-
- /*
- text = text.replace(/
- (^|[^\\]) // Character before opening ` can't be a backslash
- (`+) // $2 = Opening run of `
- ( // $3 = The code block
- [^\r]*?
- [^`] // attacklab: work around lack of lookbehind
- )
- \2 // Matching closer
- (?!`)
- /gm, function(){...});
- */
-
- text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
- function(wholeMatch,m1,m2,m3,m4) {
- var c = m3;
- c = c.replace(/^([ \t]*)/g,""); // leading whitespace
- c = c.replace(/[ \t]*$/g,""); // trailing whitespace
- c = _EncodeCode(c);
- return m1+""+c+"";
- });
-
- return text;
-}
-
-
-var _EncodeCode = function(text) {
-//
-// Encode/escape certain characters inside Markdown code runs.
-// The point is that in code, these characters are literals,
-// and lose their special Markdown meanings.
-//
- // Encode all ampersands; HTML entities are not
- // entities within a Markdown code span.
- text = text.replace(/&/g,"&");
-
- // Do the angle bracket song and dance:
- text = text.replace(//g,">");
-
- // Now, escape characters that are magic in Markdown:
- text = escapeCharacters(text,"\*_{}[]\\",false);
-
-// jj the line above breaks this:
-//---
-
-//* Item
-
-// 1. Subitem
-
-// special char: *
-//---
-
- return text;
-}
-
-
-var _DoItalicsAndBold = function(text) {
-
- // must go first:
- text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,
- "$2");
-
- text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
- "$2");
-
- return text;
-}
-
-
-var _DoBlockQuotes = function(text) {
-
- /*
- text = text.replace(/
- ( // Wrap whole match in $1
- (
- ^[ \t]*>[ \t]? // '>' at the start of a line
- .+\n // rest of the first line
- (.+\n)* // subsequent consecutive lines
- \n* // blanks
- )+
- )
- /gm, function(){...});
- */
-
- text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
- function(wholeMatch,m1) {
- var bq = m1;
-
- // attacklab: hack around Konqueror 3.5.4 bug:
- // "----------bug".replace(/^-/g,"") == "bug"
-
- bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
-
- // attacklab: clean up hack
- bq = bq.replace(/~0/g,"");
-
- bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
- bq = _RunBlockGamut(bq); // recurse
-
- bq = bq.replace(/(^|\n)/g,"$1 ");
- // These leading spaces screw with
content, so we need to fix that:
- bq = bq.replace(
- /(\s*
[^\r]+?<\/pre>)/gm,
- function(wholeMatch,m1) {
- var pre = m1;
- // attacklab: hack around Konqueror 3.5.4 bug:
- pre = pre.replace(/^ /mg,"~0");
- pre = pre.replace(/~0/g,"");
- return pre;
- });
-
- return hashBlock("
\n" + bq + "\n
");
- });
- return text;
-}
-
-
-var _FormParagraphs = function(text) {
-//
-// Params:
-// $text - string to process with html
tags
-//
-
- // Strip leading and trailing lines:
- text = text.replace(/^\n+/g,"");
- text = text.replace(/\n+$/g,"");
-
- var grafs = text.split(/\n{2,}/g);
- var grafsOut = new Array();
-
- //
- // Wrap
tags.
- //
- var end = grafs.length;
- for (var i=0; i= 0) {
- grafsOut.push(str);
- }
- else if (str.search(/\S/) >= 0) {
- str = _RunSpanGamut(str);
- str = str.replace(/^([ \t]*)/g,"
");
- str += "
"
- grafsOut.push(str);
- }
-
- }
-
- //
- // Unhashify HTML blocks
- //
- end = grafsOut.length;
- for (var i=0; i= 0) {
- var blockText = g_html_blocks[RegExp.$1];
- blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
- grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
- }
- }
-
- return grafsOut.join("\n\n");
-}
-
-
-var _EncodeAmpsAndAngles = function(text) {
-// Smart processing for ampersands and angle brackets that need to be encoded.
-
- // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
- // http://bumppo.net/projects/amputator/
- text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
-
- // Encode naked <'s
- text = text.replace(/<(?![a-z\/?\$!])/gi,"<");
-
- return text;
-}
-
-
-var _EncodeBackslashEscapes = function(text) {
-//
-// Parameter: String.
-// Returns: The string, with after processing the following backslash
-// escape sequences.
-//
-
- // attacklab: The polite way to do this is with the new
- // escapeCharacters() function:
- //
- // text = escapeCharacters(text,"\\",true);
- // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
- //
- // ...but we're sidestepping its use of the (slow) RegExp constructor
- // as an optimization for Firefox. This function gets called a LOT.
-
- text = text.replace(/\\(\\)/g,escapeCharacters_callback);
- text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
- return text;
-}
-
-
-var _DoAutoLinks = function(text) {
-
- text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1");
-
- // Email addresses:
-
- /*
- text = text.replace(/
- <
- (?:mailto:)?
- (
- [-.\w]+
- \@
- [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
- )
- >
- /gi, _DoAutoLinks_callback());
- */
- text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
- function(wholeMatch,m1) {
- return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
- }
- );
-
- return text;
-}
-
-
-var _EncodeEmailAddress = function(addr) {
-//
-// Input: an email address, e.g. "foo@example.com"
-//
-// Output: the email address as a mailto link, with each character
-// of the address encoded as either a decimal or hex entity, in
-// the hopes of foiling most address harvesting spam bots. E.g.:
-//
-// foo
-// @example.com
-//
-// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
-// mailing list:
-//
-
- // attacklab: why can't javascript speak hex?
- function char2hex(ch) {
- var hexDigits = '0123456789ABCDEF';
- var dec = ch.charCodeAt(0);
- return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
- }
-
- var encode = [
- function(ch){return ""+ch.charCodeAt(0)+";";},
- function(ch){return ""+char2hex(ch)+";";},
- function(ch){return ch;}
- ];
-
- addr = "mailto:" + addr;
-
- addr = addr.replace(/./g, function(ch) {
- if (ch == "@") {
- // this *must* be encoded. I insist.
- ch = encode[Math.floor(Math.random()*2)](ch);
- } else if (ch !=":") {
- // leave ':' alone (to spot mailto: later)
- var r = Math.random();
- // roughly 10% raw, 45% hex, 45% dec
- ch = (
- r > .9 ? encode[2](ch) :
- r > .45 ? encode[1](ch) :
- encode[0](ch)
- );
- }
- return ch;
- });
-
- addr = "" + addr + "";
- addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
-
- return addr;
-}
-
-
-var _UnescapeSpecialChars = function(text) {
-//
-// Swap back in all the special characters we've hidden.
-//
- text = text.replace(/~E(\d+)E/g,
- function(wholeMatch,m1) {
- var charCodeToReplace = parseInt(m1);
- return String.fromCharCode(charCodeToReplace);
- }
- );
- return text;
-}
-
-
-var _Outdent = function(text) {
-//
-// Remove one level of line-leading tabs or spaces
-//
-
- // attacklab: hack around Konqueror 3.5.4 bug:
- // "----------bug".replace(/^-/g,"") == "bug"
-
- text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
-
- // attacklab: clean up hack
- text = text.replace(/~0/g,"")
-
- return text;
-}
-
-var _Detab = function(text) {
-// attacklab: Detab's completely rewritten for speed.
-// In perl we could fix it by anchoring the regexp with \G.
-// In javascript we're less fortunate.
-
- // expand first n-1 tabs
- text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
-
- // replace the nth with two sentinels
- text = text.replace(/\t/g,"~A~B");
-
- // use the sentinel to anchor our regex so it doesn't explode
- text = text.replace(/~B(.+?)~A/g,
- function(wholeMatch,m1,m2) {
- var leadingText = m1;
- var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
-
- // there *must* be a better way to do this:
- for (var i=0; i";var D="
");});
+
+ // atx-style headers:
+ // # Header 1
+ // ## Header 2
+ // ## Header 2 with closing hashes ##
+ // ...
+ // ###### Header 6
+ //
+
+ /*
+ text = text.replace(/
+ ^(\#{1,6}) // $1 = string of #'s
+ [ \t]*
+ (.+?) // $2 = Header text
+ [ \t]*
+ \#* // optional closing #'s (not counted)
+ \n+
+ /gm, function() {...});
+ */
+
+ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
+ function(wholeMatch,m1,m2) {
+ var h_level = m1.length;
+ return hashBlock("" + _RunSpanGamut(m2) + "");
+ });
+
+ return text;
+}
+
+// This declaration keeps Dojo compressor from outputting garbage:
+var _ProcessListItems;
+
+var _DoLists = function(text) {
+//
+// Form HTML ordered (numbered) and unordered (bulleted) lists.
+//
+
+ // attacklab: add sentinel to hack around khtml/safari bug:
+ // http://bugs.webkit.org/show_bug.cgi?id=11231
+ text += "~0";
+
+ // Re-usable pattern to match any entirel ul or ol list:
+
+ /*
+ var whole_list = /
+ ( // $1 = whole list
+ ( // $2
+ [ ]{0,3} // attacklab: g_tab_width - 1
+ ([*+-]|\d+[.]) // $3 = first list item marker
+ [ \t]+
+ )
+ [^\r]+?
+ ( // $4
+ ~0 // sentinel for workaround; should be $
+ |
+ \n{2,}
+ (?=\S)
+ (?! // Negative lookahead for another list item marker
+ [ \t]*
+ (?:[*+-]|\d+[.])[ \t]+
+ )
+ )
+ )/g
+ */
+ var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
+
+ if (g_list_level) {
+ text = text.replace(whole_list,function(wholeMatch,m1,m2) {
+ var list = m1;
+ var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
+
+ // Turn double returns into triple returns, so that we can make a
+ // paragraph for the last item in a list, if necessary:
+ list = list.replace(/\n{2,}/g,"\n\n\n");;
+ var result = _ProcessListItems(list);
+
+ // Trim any trailing whitespace, to put the closing `$list_type>`
+ // up on the preceding line, to get it past the current stupid
+ // HTML block parser. This is a hack to work around the terrible
+ // hack that is the HTML block parser.
+ result = result.replace(/\s+$/,"");
+ result = "<"+list_type+">" + result + ""+list_type+">\n";
+ return result;
+ });
+ } else {
+ whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
+ text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
+ var runup = m1;
+ var list = m2;
+
+ var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
+ // Turn double returns into triple returns, so that we can make a
+ // paragraph for the last item in a list, if necessary:
+ var list = list.replace(/\n{2,}/g,"\n\n\n");;
+ var result = _ProcessListItems(list);
+ result = runup + "<"+list_type+">\n" + result + ""+list_type+">\n";
+ return result;
+ });
+ }
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/,"");
+
+ return text;
+}
+
+_ProcessListItems = function(list_str) {
+//
+// Process the contents of a single ordered or unordered list, splitting it
+// into individual list items.
+//
+ // The $g_list_level global keeps track of when we're inside a list.
+ // Each time we enter a list, we increment it; when we leave a list,
+ // we decrement. If it's zero, we're not in a list anymore.
+ //
+ // We do this because when we're not inside a list, we want to treat
+ // something like this:
+ //
+ // I recommend upgrading to version
+ // 8. Oops, now this line is treated
+ // as a sub-list.
+ //
+ // As a single paragraph, despite the fact that the second line starts
+ // with a digit-period-space sequence.
+ //
+ // Whereas when we're inside a list (or sub-list), that line will be
+ // treated as the start of a sub-list. What a kludge, huh? This is
+ // an aspect of Markdown's syntax that's hard to parse perfectly
+ // without resorting to mind-reading. Perhaps the solution is to
+ // change the syntax rules such that sub-lists must start with a
+ // starting cardinal number; e.g. "1." or "a.".
+
+ g_list_level++;
+
+ // trim trailing blank lines:
+ list_str = list_str.replace(/\n{2,}$/,"\n");
+
+ // attacklab: add sentinel to emulate \z
+ list_str += "~0";
+
+ /*
+ list_str = list_str.replace(/
+ (\n)? // leading line = $1
+ (^[ \t]*) // leading whitespace = $2
+ ([*+-]|\d+[.]) [ \t]+ // list marker = $3
+ ([^\r]+? // list item text = $4
+ (\n{1,2}))
+ (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
+ /gm, function(){...});
+ */
+ list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
+ function(wholeMatch,m1,m2,m3,m4){
+ var item = m4;
+ var leading_line = m1;
+ var leading_space = m2;
+
+ if (leading_line || (item.search(/\n{2,}/)>-1)) {
+ item = _RunBlockGamut(_Outdent(item));
+ }
+ else {
+ // Recursion for sub-lists:
+ item = _DoLists(_Outdent(item));
+ item = item.replace(/\n$/,""); // chomp(item)
+ item = _RunSpanGamut(item);
+ }
+
+ return "
` blocks.
+//
+
+ /*
+ text = text.replace(text,
+ /(?:\n\n|^)
+ ( // $1 = the code block -- one or more lines, starting with a space/tab
+ (?:
+ (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
+ .*\n+
+ )+
+ )
+ (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
+ /g,function(){...});
+ */
+
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+ text += "~0";
+
+ text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
+ function(wholeMatch,m1,m2) {
+ var codeblock = m1;
+ var nextChar = m2;
+
+ codeblock = _EncodeCode( _Outdent(codeblock));
+ codeblock = _Detab(codeblock);
+ codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
+ codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
+
+ codeblock = "
" + codeblock + "\n
";
+
+ return hashBlock(codeblock) + nextChar;
+ }
+ );
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/,"");
+
+ return text;
+}
+
+var hashBlock = function(text) {
+ text = text.replace(/(^\n+|\n+$)/g,"");
+ return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
+}
+
+
+var _DoCodeSpans = function(text) {
+//
+// * Backtick quotes are used for spans.
+//
+// * You can use multiple backticks as the delimiters if you want to
+// include literal backticks in the code span. So, this input:
+//
+// Just type ``foo `bar` baz`` at the prompt.
+//
+// Will translate to:
+//
+//
Just type foo `bar` baz at the prompt.
+//
+// There's no arbitrary limit to the number of backticks you
+// can use as delimters. If you need three consecutive backticks
+// in your code, use four for delimiters, etc.
+//
+// * You can use spaces to get literal backticks at the edges:
+//
+// ... type `` `bar` `` ...
+//
+// Turns to:
+//
+// ... type `bar` ...
+//
+
+ /*
+ text = text.replace(/
+ (^|[^\\]) // Character before opening ` can't be a backslash
+ (`+) // $2 = Opening run of `
+ ( // $3 = The code block
+ [^\r]*?
+ [^`] // attacklab: work around lack of lookbehind
+ )
+ \2 // Matching closer
+ (?!`)
+ /gm, function(){...});
+ */
+
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
+ function(wholeMatch,m1,m2,m3,m4) {
+ var c = m3;
+ c = c.replace(/^([ \t]*)/g,""); // leading whitespace
+ c = c.replace(/[ \t]*$/g,""); // trailing whitespace
+ c = _EncodeCode(c);
+ return m1+""+c+"";
+ });
+
+ return text;
+}
+
+
+var _EncodeCode = function(text) {
+//
+// Encode/escape certain characters inside Markdown code runs.
+// The point is that in code, these characters are literals,
+// and lose their special Markdown meanings.
+//
+ // Encode all ampersands; HTML entities are not
+ // entities within a Markdown code span.
+ text = text.replace(/&/g,"&");
+
+ // Do the angle bracket song and dance:
+ text = text.replace(//g,">");
+
+ // Now, escape characters that are magic in Markdown:
+ text = escapeCharacters(text,"\*_{}[]\\",false);
+
+// jj the line above breaks this:
+//---
+
+//* Item
+
+// 1. Subitem
+
+// special char: *
+//---
+
+ return text;
+}
+
+
+var _DoItalicsAndBold = function(text) {
+
+ // must go first:
+ text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,
+ "$2");
+
+ text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
+ "$2");
+
+ return text;
+}
+
+
+var _DoBlockQuotes = function(text) {
+
+ /*
+ text = text.replace(/
+ ( // Wrap whole match in $1
+ (
+ ^[ \t]*>[ \t]? // '>' at the start of a line
+ .+\n // rest of the first line
+ (.+\n)* // subsequent consecutive lines
+ \n* // blanks
+ )+
+ )
+ /gm, function(){...});
+ */
+
+ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
+ function(wholeMatch,m1) {
+ var bq = m1;
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
+
+ // attacklab: clean up hack
+ bq = bq.replace(/~0/g,"");
+
+ bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
+ bq = _RunBlockGamut(bq); // recurse
+
+ bq = bq.replace(/(^|\n)/g,"$1 ");
+ // These leading spaces screw with
content, so we need to fix that:
+ bq = bq.replace(
+ /(\s*
[^\r]+?<\/pre>)/gm,
+ function(wholeMatch,m1) {
+ var pre = m1;
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ pre = pre.replace(/^ /mg,"~0");
+ pre = pre.replace(/~0/g,"");
+ return pre;
+ });
+
+ return hashBlock("
\n" + bq + "\n
");
+ });
+ return text;
+}
+
+
+var _FormParagraphs = function(text) {
+//
+// Params:
+// $text - string to process with html
tags
+//
+
+ // Strip leading and trailing lines:
+ text = text.replace(/^\n+/g,"");
+ text = text.replace(/\n+$/g,"");
+
+ var grafs = text.split(/\n{2,}/g);
+ var grafsOut = new Array();
+
+ //
+ // Wrap
tags.
+ //
+ var end = grafs.length;
+ for (var i=0; i= 0) {
+ grafsOut.push(str);
+ }
+ else if (str.search(/\S/) >= 0) {
+ str = _RunSpanGamut(str);
+ str = str.replace(/^([ \t]*)/g,"
");
+ str += "
"
+ grafsOut.push(str);
+ }
+
+ }
+
+ //
+ // Unhashify HTML blocks
+ //
+ end = grafsOut.length;
+ for (var i=0; i= 0) {
+ var blockText = g_html_blocks[RegExp.$1];
+ blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
+ grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
+ }
+ }
+
+ return grafsOut.join("\n\n");
+}
+
+
+var _EncodeAmpsAndAngles = function(text) {
+// Smart processing for ampersands and angle brackets that need to be encoded.
+
+ // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
+ // http://bumppo.net/projects/amputator/
+ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&");
+
+ // Encode naked <'s
+ text = text.replace(/<(?![a-z\/?\$!])/gi,"<");
+
+ return text;
+}
+
+
+var _EncodeBackslashEscapes = function(text) {
+//
+// Parameter: String.
+// Returns: The string, with after processing the following backslash
+// escape sequences.
+//
+
+ // attacklab: The polite way to do this is with the new
+ // escapeCharacters() function:
+ //
+ // text = escapeCharacters(text,"\\",true);
+ // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
+ //
+ // ...but we're sidestepping its use of the (slow) RegExp constructor
+ // as an optimization for Firefox. This function gets called a LOT.
+
+ text = text.replace(/\\(\\)/g,escapeCharacters_callback);
+ text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
+ return text;
+}
+
+
+var _DoAutoLinks = function(text) {
+
+ text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"$1");
+
+ // Email addresses:
+
+ /*
+ text = text.replace(/
+ <
+ (?:mailto:)?
+ (
+ [-.\w]+
+ \@
+ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
+ )
+ >
+ /gi, _DoAutoLinks_callback());
+ */
+ text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
+ function(wholeMatch,m1) {
+ return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
+ }
+ );
+
+ return text;
+}
+
+
+var _EncodeEmailAddress = function(addr) {
+//
+// Input: an email address, e.g. "foo@example.com"
+//
+// Output: the email address as a mailto link, with each character
+// of the address encoded as either a decimal or hex entity, in
+// the hopes of foiling most address harvesting spam bots. E.g.:
+//
+// foo
+// @example.com
+//
+// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
+// mailing list:
+//
+
+ // attacklab: why can't javascript speak hex?
+ function char2hex(ch) {
+ var hexDigits = '0123456789ABCDEF';
+ var dec = ch.charCodeAt(0);
+ return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
+ }
+
+ var encode = [
+ function(ch){return ""+ch.charCodeAt(0)+";";},
+ function(ch){return ""+char2hex(ch)+";";},
+ function(ch){return ch;}
+ ];
+
+ addr = "mailto:" + addr;
+
+ addr = addr.replace(/./g, function(ch) {
+ if (ch == "@") {
+ // this *must* be encoded. I insist.
+ ch = encode[Math.floor(Math.random()*2)](ch);
+ } else if (ch !=":") {
+ // leave ':' alone (to spot mailto: later)
+ var r = Math.random();
+ // roughly 10% raw, 45% hex, 45% dec
+ ch = (
+ r > .9 ? encode[2](ch) :
+ r > .45 ? encode[1](ch) :
+ encode[0](ch)
+ );
+ }
+ return ch;
+ });
+
+ addr = "" + addr + "";
+ addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
+
+ return addr;
+}
+
+
+var _UnescapeSpecialChars = function(text) {
+//
+// Swap back in all the special characters we've hidden.
+//
+ text = text.replace(/~E(\d+)E/g,
+ function(wholeMatch,m1) {
+ var charCodeToReplace = parseInt(m1);
+ return String.fromCharCode(charCodeToReplace);
+ }
+ );
+ return text;
+}
+
+
+var _Outdent = function(text) {
+//
+// Remove one level of line-leading tabs or spaces
+//
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
+
+ // attacklab: clean up hack
+ text = text.replace(/~0/g,"")
+
+ return text;
+}
+
+var _Detab = function(text) {
+// attacklab: Detab's completely rewritten for speed.
+// In perl we could fix it by anchoring the regexp with \G.
+// In javascript we're less fortunate.
+
+ // expand first n-1 tabs
+ text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
+
+ // replace the nth with two sentinels
+ text = text.replace(/\t/g,"~A~B");
+
+ // use the sentinel to anchor our regex so it doesn't explode
+ text = text.replace(/~B(.+?)~A/g,
+ function(wholeMatch,m1,m2) {
+ var leadingText = m1;
+ var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
+
+ // there *must* be a better way to do this:
+ for (var i=0; i";var D="
+
+ {% trans "system error log is recorded, error will be fixed as soon as possible" %}
+ {% trans "please report the error to the site administrators if you wish" %}
+
Here you can ask and answer questions, comment
+ and vote for the questions of others and their answers. Both questions and answers
+ can be revised and improved. Questions can be tagged with
+ the relevant keywords to simplify future access and organize the accumulated material.
+
+
+
This Q&A site is moderated by its members, hopefully - including yourself!
+ Moderation rights are gradually assigned to the site users based on the accumulated "reputation"
+ points. These points are added to the users account when others vote for his/her questions or answers.
+ These points (very) roughly reflect the level of trust of the community.
+
+
No points are necessary to ask or answer the questions - so please -
+ join us!
+
{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}
+
+
+
+{% endblock %}
+
diff --git a/forum/skins/default/templates/authopenid/complete.html b/forum/skins/default/templates/authopenid/complete.html
new file mode 100644
index 00000000..62970e38
--- /dev/null
+++ b/forum/skins/default/templates/authopenid/complete.html
@@ -0,0 +1,130 @@
+{% extends "base_content.html" %}
+
+{% comment %}
+views calling this template:
+* django_authopenid.views.register with login_type='openid'
+* django_authopenid.views.signin - with login_type='legacy'
+
+parameters:
+* provider
+* login_type openid|legacy
+* username (same as screen name or username in the models, and nickname in openid sreg)
+* form1 - OpenidRegisterForm
+* form2 - OpenidVerifyForm not clear what this form is supposed to do, not used for legacy
+* email_feeds_form forum.forms.SimpleEmailSubscribeForm
+* openid_username_exists
+{% endcomment %}
+{% load i18n %}
+{% block head %}{% endblock %}
+{% block title %}{% spaceless %}{% trans "Connect your OpenID with this site" %}{% endspaceless %}{% endblock %}
+{% block content %}
+
+ {% trans "Connect your OpenID with your account on this site" %}
+
+
+
+ {% ifequal login_type 'openid' %}
+ {% blocktrans %}register new {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
+ {% else %}
+ {% ifequal login_type 'legacy' %}
+ {% if external_login_name_is_taken %}
+ {% blocktrans %}{{username}} already exists, choose another name for
+ {{provider}}. Email is required too, see {{gravatar_faq_url}}
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans %}register new external {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
+ {% endif %}
+ {% else %}
+ {% blocktrans %}register new Facebook connect account info, see {{gravatar_faq_url}}{% endblocktrans %}
+ {% endifequal %}
+ {% endifequal %}
+
+
{% trans "This account already exists, please use another." %}
+
+
+ {% if form1.errors %}
+
+ {% if form1.non_field_errors %}
+ {% for error in form1.non_field_errors %}
+
+{% endblock %}
+
diff --git a/forum/skins/default/templates/authopenid/email_validation.txt b/forum/skins/default/templates/authopenid/email_validation.txt
new file mode 100644
index 00000000..5b166a9b
--- /dev/null
+++ b/forum/skins/default/templates/authopenid/email_validation.txt
@@ -0,0 +1,15 @@
+{% load i18n %}
+{% trans "Greetings from the Q&A forum" %},
+
+{% trans "To make use of the Forum, please follow the link below:" %}
+
+{{validation_link}}
+
+{% trans "Following the link above will help us verify your email address." %}
+
+{% blocktrans %}If you beleive that this message was sent in mistake -
+no further action is needed. Just ingore this email, we apologize
+for any inconvenience{% endblocktrans %}
+
+{% blocktrans %}Sincerely,
+Forum Administrator{% endblocktrans %}
diff --git a/forum/skins/default/templates/authopenid/external_legacy_login_info.html b/forum/skins/default/templates/authopenid/external_legacy_login_info.html
new file mode 100644
index 00000000..3318499c
--- /dev/null
+++ b/forum/skins/default/templates/authopenid/external_legacy_login_info.html
@@ -0,0 +1,15 @@
+{% extends "base_content.html" %}
+
+{% load i18n %}
+{% block title %}{% spaceless %}{% trans "Traditional login information" %}{% endspaceless %}{% endblock %}
+{% block content %}
+
+ {% trans "Traditional login information" %}
+
+{% spaceless %}
+
+
+{% blocktrans %}how to login with password through external login website or use {{feedback_url}}{% endblocktrans %}
+
+ {% blocktrans with answer.question.title as title and answer.summary as summary %}
+ Your answer to {{title}} {{summary}} will be posted once you log in
+ {% endblocktrans %}
+
+ {% endif %}
+ {% if question %}
+
+ {% blocktrans with question.title as title and question.summary as summary %}Your question
+ {{title}} {{summary}} will be posted once you log in
+ {% endblocktrans %}
+
+ {% trans "Community gives you awards for your questions, answers and votes." %}
+ {% blocktrans %}Below is the list of available badges and number
+ of times each type of badge has been awarded. Give us feedback at {{feedback_faq_url}}.
+ {% endblocktrans %}
+
+
+ {% for badge in badges %}
+
+
+ {% for a in mybadges %}
+ {% ifequal a.badge_id badge.id %}
+ ✔
+ {% endifequal %}
+ {% endfor %}
+
+ {% trans "Frequently Asked Questions " %}(FAQ)
+
+
+
+
+
{% trans "What kinds of questions can I ask here?" %}
+
{% trans "Most importanly - questions should be relevant to this community." %}
+ {% trans "Before asking the question - please make sure to use search to see whether your question has alredy been answered."%}
+
+
+
{% trans "What questions should I avoid asking?" %}
+
{% trans "Please avoid asking questions that are not relevant to this community, too subjective and argumentative." %}
+
+
+
+
+
{% trans "What should I avoid in my answers?" %}
+
{{ settings.APP_TITLE }} {% trans "is a Q&A site, not a discussion group. Therefore - please avoid having discussions in your answers, comment facility allows some space for brief discussions." %}
+
+
+
+
{% trans "Who moderates this community?" %}
+
{% trans "The short answer is: you." %}
+ {% trans "This website is moderated by the users." %}
+ {% trans "The reputation system allows users earn the authorization to perform a variety of moderation tasks." %}
+
+
+
+
+
{% trans "How does reputation system work?" %}
+
{% trans "Rep system summary" %}
+
{% blocktrans %}For example, if you ask an interesting question or give a helpful answer, your input will be upvoted. On the other hand if the answer is misleading - it will be downvoted. Each vote in favor will generate 10 points, each vote against will subtract 2 points. There is a limit of 200 points that can be accumulated per question or answer. The table below explains reputation point requirements for each type of moderation task.{% endblocktrans %}
+
+
+
+
+
+
+
+
+
+
50
+
{% trans "add comments" %}
+
+
+
100
+
{% trans "downvote" %}
+
+
250
+
{% trans "open and close own questions" %}
+
+
+
500
+
{% trans "retag questions" %}
+
+ {% if settings.WIKI_ON %}
+
+
750
+
{% trans "edit community wiki questions" %}
+
+ {% endif %}
+
+
2000
+
{% trans "edit any answer" %}
+
+
+
3000
+
{% trans "open any closed question" %}
+
+
+
5000
+
{% trans "delete any comment" %}
+
+
+
10000
+
{% trans "delete any questions and answers and perform other moderation tasks" %}
+
+
+
+
+ {% ifequal settings.EMAIL_VALIDATION 'on' %}
+
+
{% trans "how to validate email title" %}
+
+ {% blocktrans %}how to validate email info with {{send_email_key_url}} {{gravatar_faq_url}}{% endblocktrans %}
+
+ {% endifequal %}
+
+
{% trans "what is gravatar" %}
+
{% trans "gravatar faq info" %}
+
+
+
{% trans "To register, do I need to create new password?" %}
+
{% trans "No, you don't have to. You can login through any service that supports OpenID, e.g. Google, Yahoo, AOL, etc." %}
+ {% trans "Login now!" %} »
+
+
+
+
+
{% trans "Why other people can edit my questions/answers?" %}
+
{% trans "Goal of this site is..." %} {% trans "So questions and answers can be edited like wiki pages by experienced users of this site and this improves the overall quality of the knowledge base content." %}
+ {% trans "If this approach is not for you, we respect your choice." %}
+
+
+
+
{% trans "Still have questions?" %}
+
{% blocktrans %}Please ask your question at {{ask_question_url}}, help make our community better!{% endblocktrans %}
+
+
{% blocktrans with question.get_close_reason_display as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %}
+ {{ question.closed_by.username }}
+ {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}
+ {% if searchtag %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% plural %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% endblocktrans %}
+ {% else %}
+ {% if searchtitle %}
+ {% if settings.USE_SPHINX_SEARCH %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions containing {{searchtitle}} in full text
+ {% plural %}
+ have total {{q_num}} questions containing {{searchtitle}} in full text
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions containing {{searchtitle}}
+ {% plural %}
+ have total {{q_num}} questions containing {{searchtitle}}
+ {% endblocktrans %}
+ {% endif %}
+ {% else %}
+ {% if is_unanswered %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} unanswered questions
+ {% plural %}
+ have total {{q_num}} unanswered questions
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions
+ {% plural %}
+ have total {{q_num}} questions
+ {% endblocktrans %}
+ {% endif %}
+ {% endif %}
+ {% endif %}
+
+ {% ifequal tab_id "latest" %}
+ {% trans "latest questions info" %}
+ {% endifequal %}
+
+ {% ifequal tab_id "active" %}
+ {% trans "Questions are sorted by the time of last update." %}
+ {% trans "Most recently answered ones are shown first." %}
+ {% endifequal %}
+
+ {% ifequal tab_id "hottest" %}
+ {% trans "Questions sorted by number of responses." %}
+ {% trans "Most answered questions are shown first." %}
+ {% endifequal %}
+
+ {% ifequal tab_id "mostvoted" %}
+ {% trans "Questions are sorted by the number of votes." %}
+ {% trans "Most voted questions are shown first." %}
+ {% endifequal %}
+
+
+{% if request.user.is_authenticated %}
+{% include "tag_selector.html" %}
+{% endif %}
+
+
{% trans "Related tags" %}
+
+ {% for tag in tags %}
+ {{ tag.name }}
+ × {{ tag.used_count|intcomma }}
+
+ {% endfor %}
+
{% trans "The question was closed for the following reason " %}"{{ question.get_close_reason_display }}"{% trans "reason - leave blank in english" %} {{ question.closed_by.username }} {% trans "on "%} {% diff_date question.closed_at %}{% trans "date closed" %}
+
+
+{% if stag %}
+ {% trans "All tags matching query" %} '{{ stag }}' {% trans "all tags - make this empty in english" %}:
+{% endif %}
+{% if not tags.object_list %}
+ {% trans "Nothing found" %}
+{% endif %}
+
-
- {% trans "system error log is recorded, error will be fixed as soon as possible" %}
- {% trans "please report the error to the site administrators if you wish" %}
-
Here you can ask and answer questions, comment
- and vote for the questions of others and their answers. Both questions and answers
- can be revised and improved. Questions can be tagged with
- the relevant keywords to simplify future access and organize the accumulated material.
-
-
-
This Q&A site is moderated by its members, hopefully - including yourself!
- Moderation rights are gradually assigned to the site users based on the accumulated "reputation"
- points. These points are added to the users account when others vote for his/her questions or answers.
- These points (very) roughly reflect the level of trust of the community.
-
-
No points are necessary to ask or answer the questions - so please -
- join us!
-
{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}
-
-
-
-{% endblock %}
-
diff --git a/forum/templates/authopenid/complete.html b/forum/templates/authopenid/complete.html
deleted file mode 100644
index 62970e38..00000000
--- a/forum/templates/authopenid/complete.html
+++ /dev/null
@@ -1,130 +0,0 @@
-{% extends "base_content.html" %}
-
-{% comment %}
-views calling this template:
-* django_authopenid.views.register with login_type='openid'
-* django_authopenid.views.signin - with login_type='legacy'
-
-parameters:
-* provider
-* login_type openid|legacy
-* username (same as screen name or username in the models, and nickname in openid sreg)
-* form1 - OpenidRegisterForm
-* form2 - OpenidVerifyForm not clear what this form is supposed to do, not used for legacy
-* email_feeds_form forum.forms.SimpleEmailSubscribeForm
-* openid_username_exists
-{% endcomment %}
-{% load i18n %}
-{% block head %}{% endblock %}
-{% block title %}{% spaceless %}{% trans "Connect your OpenID with this site" %}{% endspaceless %}{% endblock %}
-{% block content %}
-
- {% trans "Connect your OpenID with your account on this site" %}
-
-
-
- {% ifequal login_type 'openid' %}
- {% blocktrans %}register new {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% else %}
- {% ifequal login_type 'legacy' %}
- {% if external_login_name_is_taken %}
- {% blocktrans %}{{username}} already exists, choose another name for
- {{provider}}. Email is required too, see {{gravatar_faq_url}}
- {% endblocktrans %}
- {% else %}
- {% blocktrans %}register new external {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% endif %}
- {% else %}
- {% blocktrans %}register new Facebook connect account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% endifequal %}
- {% endifequal %}
-
-
{% trans "This account already exists, please use another." %}
-
-
- {% if form1.errors %}
-
- {% if form1.non_field_errors %}
- {% for error in form1.non_field_errors %}
-
-{% endblock %}
-
diff --git a/forum/templates/authopenid/email_validation.txt b/forum/templates/authopenid/email_validation.txt
deleted file mode 100644
index 5b166a9b..00000000
--- a/forum/templates/authopenid/email_validation.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{% load i18n %}
-{% trans "Greetings from the Q&A forum" %},
-
-{% trans "To make use of the Forum, please follow the link below:" %}
-
-{{validation_link}}
-
-{% trans "Following the link above will help us verify your email address." %}
-
-{% blocktrans %}If you beleive that this message was sent in mistake -
-no further action is needed. Just ingore this email, we apologize
-for any inconvenience{% endblocktrans %}
-
-{% blocktrans %}Sincerely,
-Forum Administrator{% endblocktrans %}
diff --git a/forum/templates/authopenid/external_legacy_login_info.html b/forum/templates/authopenid/external_legacy_login_info.html
deleted file mode 100644
index 3318499c..00000000
--- a/forum/templates/authopenid/external_legacy_login_info.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "base_content.html" %}
-
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Traditional login information" %}{% endspaceless %}{% endblock %}
-{% block content %}
-
- {% trans "Traditional login information" %}
-
-{% spaceless %}
-
-
-{% blocktrans %}how to login with password through external login website or use {{feedback_url}}{% endblocktrans %}
-
- {% blocktrans with answer.question.title as title and answer.summary as summary %}
- Your answer to {{title}} {{summary}} will be posted once you log in
- {% endblocktrans %}
-
- {% endif %}
- {% if question %}
-
- {% blocktrans with question.title as title and question.summary as summary %}Your question
- {{title}} {{summary}} will be posted once you log in
- {% endblocktrans %}
-
- {% trans "Community gives you awards for your questions, answers and votes." %}
- {% blocktrans %}Below is the list of available badges and number
- of times each type of badge has been awarded. Give us feedback at {{feedback_faq_url}}.
- {% endblocktrans %}
-
-
- {% for badge in badges %}
-
-
- {% for a in mybadges %}
- {% ifequal a.badge_id badge.id %}
- ✔
- {% endifequal %}
- {% endfor %}
-
- {% trans "Frequently Asked Questions " %}(FAQ)
-
-
-
-
-
{% trans "What kinds of questions can I ask here?" %}
-
{% trans "Most importanly - questions should be relevant to this community." %}
- {% trans "Before asking the question - please make sure to use search to see whether your question has alredy been answered."%}
-
-
-
{% trans "What questions should I avoid asking?" %}
-
{% trans "Please avoid asking questions that are not relevant to this community, too subjective and argumentative." %}
-
-
-
-
-
{% trans "What should I avoid in my answers?" %}
-
{{ settings.APP_TITLE }} {% trans "is a Q&A site, not a discussion group. Therefore - please avoid having discussions in your answers, comment facility allows some space for brief discussions." %}
-
-
-
-
{% trans "Who moderates this community?" %}
-
{% trans "The short answer is: you." %}
- {% trans "This website is moderated by the users." %}
- {% trans "The reputation system allows users earn the authorization to perform a variety of moderation tasks." %}
-
-
-
-
-
{% trans "How does reputation system work?" %}
-
{% trans "Rep system summary" %}
-
{% blocktrans %}For example, if you ask an interesting question or give a helpful answer, your input will be upvoted. On the other hand if the answer is misleading - it will be downvoted. Each vote in favor will generate 10 points, each vote against will subtract 2 points. There is a limit of 200 points that can be accumulated per question or answer. The table below explains reputation point requirements for each type of moderation task.{% endblocktrans %}
-
-
-
-
-
-
-
-
-
-
50
-
{% trans "add comments" %}
-
-
-
100
-
{% trans "downvote" %}
-
-
250
-
{% trans "open and close own questions" %}
-
-
-
500
-
{% trans "retag questions" %}
-
- {% if settings.WIKI_ON %}
-
-
750
-
{% trans "edit community wiki questions" %}
-
- {% endif %}
-
-
2000
-
{% trans "edit any answer" %}
-
-
-
3000
-
{% trans "open any closed question" %}
-
-
-
5000
-
{% trans "delete any comment" %}
-
-
-
10000
-
{% trans "delete any questions and answers and perform other moderation tasks" %}
-
-
-
-
- {% ifequal settings.EMAIL_VALIDATION 'on' %}
-
-
{% trans "how to validate email title" %}
-
- {% blocktrans %}how to validate email info with {{send_email_key_url}} {{gravatar_faq_url}}{% endblocktrans %}
-
- {% endifequal %}
-
-
{% trans "what is gravatar" %}
-
{% trans "gravatar faq info" %}
-
-
-
{% trans "To register, do I need to create new password?" %}
-
{% trans "No, you don't have to. You can login through any service that supports OpenID, e.g. Google, Yahoo, AOL, etc." %}
- {% trans "Login now!" %} »
-
-
-
-
-
{% trans "Why other people can edit my questions/answers?" %}
-
{% trans "Goal of this site is..." %} {% trans "So questions and answers can be edited like wiki pages by experienced users of this site and this improves the overall quality of the knowledge base content." %}
- {% trans "If this approach is not for you, we respect your choice." %}
-
-
-
-
{% trans "Still have questions?" %}
-
{% blocktrans %}Please ask your question at {{ask_question_url}}, help make our community better!{% endblocktrans %}
-
-
{% blocktrans with question.get_close_reason_display as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %}
- {{ question.closed_by.username }}
- {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}
- {% if searchtag %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
- have total {{q_num}} questions tagged {{tagname}}
- {% plural %}
- have total {{q_num}} questions tagged {{tagname}}
- {% endblocktrans %}
- {% else %}
- {% if searchtitle %}
- {% if settings.USE_SPHINX_SEARCH %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions containing {{searchtitle}} in full text
- {% plural %}
- have total {{q_num}} questions containing {{searchtitle}} in full text
- {% endblocktrans %}
- {% else %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions containing {{searchtitle}}
- {% plural %}
- have total {{q_num}} questions containing {{searchtitle}}
- {% endblocktrans %}
- {% endif %}
- {% else %}
- {% if is_unanswered %}
- {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} unanswered questions
- {% plural %}
- have total {{q_num}} unanswered questions
- {% endblocktrans %}
- {% else %}
- {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions
- {% plural %}
- have total {{q_num}} questions
- {% endblocktrans %}
- {% endif %}
- {% endif %}
- {% endif %}
-
- {% ifequal tab_id "latest" %}
- {% trans "latest questions info" %}
- {% endifequal %}
-
- {% ifequal tab_id "active" %}
- {% trans "Questions are sorted by the time of last update." %}
- {% trans "Most recently answered ones are shown first." %}
- {% endifequal %}
-
- {% ifequal tab_id "hottest" %}
- {% trans "Questions sorted by number of responses." %}
- {% trans "Most answered questions are shown first." %}
- {% endifequal %}
-
- {% ifequal tab_id "mostvoted" %}
- {% trans "Questions are sorted by the number of votes." %}
- {% trans "Most voted questions are shown first." %}
- {% endifequal %}
-
-
-{% if request.user.is_authenticated %}
-{% include "tag_selector.html" %}
-{% endif %}
-
-
{% trans "Related tags" %}
-
- {% for tag in tags %}
- {{ tag.name }}
- × {{ tag.used_count|intcomma }}
-
- {% endfor %}
-
{% trans "The question was closed for the following reason " %}"{{ question.get_close_reason_display }}"{% trans "reason - leave blank in english" %} {{ question.closed_by.username }} {% trans "on "%} {% diff_date question.closed_at %}{% trans "date closed" %}
-
-
-{% if stag %}
- {% trans "All tags matching query" %} '{{ stag }}' {% trans "all tags - make this empty in english" %}:
-{% endif %}
-{% if not tags.object_list %}
- {% trans "Nothing found" %}
-{% endif %}
-
+ {{comment.comment}} + - {{comment.user}} + {% spaceless %} + ({% diff_date comment.added_at %}) + {% if request.user|can_delete_comment:comment %} + + {% endif %} + {% endspaceless %} +
+ {% endfor %} +