Commit 2af0dd34 by Calen Pennington

Merge pull request #67 from MITx/dogfood

Dogfood
parents 818f243d 0e06132d
This branch (re-)adds dynamic math and symbolicresponse.
Test cases included.
'''
django admin pages for courseware model
'''
from courseware.models import *
from django.contrib import admin
from django.contrib.auth.models import User
admin.site.register(StudentModule)
...@@ -26,7 +26,7 @@ from mako.template import Template ...@@ -26,7 +26,7 @@ from mako.template import Template
from util import contextualize_text from util import contextualize_text
import inputtypes import inputtypes
from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse, StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse,OptionResponse from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse, StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse,OptionResponse, SymbolicResponse
import calc import calc
import eia import eia
...@@ -42,6 +42,7 @@ response_types = {'numericalresponse':NumericalResponse, ...@@ -42,6 +42,7 @@ response_types = {'numericalresponse':NumericalResponse,
'truefalseresponse':TrueFalseResponse, 'truefalseresponse':TrueFalseResponse,
'imageresponse':ImageResponse, 'imageresponse':ImageResponse,
'optionresponse':OptionResponse, 'optionresponse':OptionResponse,
'symbolicresponse':SymbolicResponse,
} }
entry_types = ['textline', 'schematic', 'choicegroup','textbox','imageinput','optioninput'] entry_types = ['textline', 'schematic', 'choicegroup','textbox','imageinput','optioninput']
solution_types = ['solution'] # extra things displayed after "show answers" is pressed solution_types = ['solution'] # extra things displayed after "show answers" is pressed
...@@ -55,6 +56,7 @@ html_transforms = {'problem': {'tag':'div'}, ...@@ -55,6 +56,7 @@ html_transforms = {'problem': {'tag':'div'},
"externalresponse": {'tag':'span'}, "externalresponse": {'tag':'span'},
"schematicresponse": {'tag':'span'}, "schematicresponse": {'tag':'span'},
"formularesponse": {'tag':'span'}, "formularesponse": {'tag':'span'},
"symbolicresponse": {'tag':'span'},
"multiplechoiceresponse": {'tag':'span'}, "multiplechoiceresponse": {'tag':'span'},
"text": {'tag':'span'}, "text": {'tag':'span'},
"math": {'tag':'span'}, "math": {'tag':'span'},
...@@ -70,7 +72,7 @@ global_context={'random':random, ...@@ -70,7 +72,7 @@ global_context={'random':random,
# These should be removed from HTML output, including all subelements # These should be removed from HTML output, including all subelements
html_problem_semantics = ["responseparam", "answer", "script"] html_problem_semantics = ["responseparam", "answer", "script"]
# These should be removed from HTML output, but keeping subelements # These should be removed from HTML output, but keeping subelements
html_skip = ["numericalresponse", "customresponse", "schematicresponse", "formularesponse", "text","externalresponse"] html_skip = ["numericalresponse", "customresponse", "schematicresponse", "formularesponse", "text","externalresponse",'symbolicresponse']
# removed in MC # removed in MC
## These should be transformed ## These should be transformed
...@@ -109,6 +111,8 @@ class LoncapaProblem(object): ...@@ -109,6 +111,8 @@ class LoncapaProblem(object):
self.seed=struct.unpack('i', os.urandom(4))[0] self.seed=struct.unpack('i', os.urandom(4))[0]
## Parse XML file ## Parse XML file
if getattr(system,'DEBUG',False):
log.info("[courseware.capa.capa_problem.lcp.init] fileobject = %s" % fileobject)
file_text = fileobject.read() file_text = fileobject.read()
self.fileobject = fileobject # save it, so we can use for debugging information later self.fileobject = fileobject # save it, so we can use for debugging information later
# Convert startouttext and endouttext to proper <text></text> # Convert startouttext and endouttext to proper <text></text>
...@@ -210,20 +214,26 @@ class LoncapaProblem(object): ...@@ -210,20 +214,26 @@ class LoncapaProblem(object):
Problem XML goes to Python execution context. Runs everything in script tags Problem XML goes to Python execution context. Runs everything in script tags
''' '''
random.seed(self.seed) random.seed(self.seed)
### IKE: Why do we need these two lines?
context = {'global_context':global_context} # save global context in here also context = {'global_context':global_context} # save global context in here also
global_context['context'] = context # and put link to local context in the global one context.update(global_context) # initialize context to have stuff in global_context
context['__builtins__'] = globals()['__builtins__'] # put globals there also
context['the_lcp'] = self # pass instance of LoncapaProblem in
#for script in tree.xpath('/problem/script'): #for script in tree.xpath('/problem/script'):
for script in tree.findall('.//script'): for script in tree.findall('.//script'):
stype = script.get('type')
if stype:
if 'javascript' in stype: continue # skip javascript
if 'perl' in stype: continue # skip perl
# TODO: evaluate only python
code = script.text code = script.text
XMLESC = {"&apos;": "'", "&quot;": '"'} XMLESC = {"&apos;": "'", "&quot;": '"'}
code = unescape(code,XMLESC) code = unescape(code,XMLESC)
try: try:
exec code in global_context, context exec code in context, context # use "context" for global context; thus defs in code are global within code
except Exception,err: except Exception,err:
print "[courseware.capa.capa_problem.extract_context] error %s" % err log.exception("[courseware.capa.capa_problem.extract_context] error %s" % err)
print "in doing exec of this code:",code log.exception("in doing exec of this code: %s" % code)
return context return context
def get_html(self): def get_html(self):
......
...@@ -197,63 +197,77 @@ def choicegroup(element, value, status, msg=''): ...@@ -197,63 +197,77 @@ def choicegroup(element, value, status, msg=''):
type="radio" type="radio"
choices={} choices={}
for choice in element: for choice in element:
assert choice.tag =="choice", "only <choice> tags should be immediate children of a <choicegroup>" if not choice.tag=='choice':
choices[choice.get("name")] = etree.tostring(choice[0]) # TODO: what if choice[0] has math tags in it? raise Exception,"[courseware.capa.inputtypes.choicegroup] Error only <choice> tags should be immediate children of a <choicegroup>, found %s instead" % choice.tag
ctext = ""
ctext += ''.join([etree.tostring(x) for x in choice]) # TODO: what if choice[0] has math tags in it?
ctext += choice.text # TODO: fix order?
choices[choice.get("name")] = ctext
context={'id':eid, 'value':value, 'state':status, 'type':type, 'choices':choices} context={'id':eid, 'value':value, 'state':status, 'type':type, 'choices':choices}
html=render_to_string("choicegroup.html", context) html=render_to_string("choicegroup.html", context)
return etree.XML(html) return etree.XML(html)
@register_render_function @register_render_function
def textline(element, value, state, msg=""): def textline(element, value, state, msg=""):
'''
Simple text line input, with optional size specification.
'''
if element.get('math') or element.get('dojs'): # 'dojs' flag is temporary, for backwards compatibility with 8.02x
return SimpleInput.xml_tags['textline_dynamath'](element,value,state,msg)
eid=element.get('id') eid=element.get('id')
count = int(eid.split('_')[-2])-1 # HACK count = int(eid.split('_')[-2])-1 # HACK
size = element.get('size') size = element.get('size')
context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size} context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size, 'msg': msg}
html=render_to_string("textinput.html", context) html=render_to_string("textinput.html", context)
return etree.XML(html) return etree.XML(html)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function @register_render_function
def js_textline(element, value, status, msg=''): def textline_dynamath(element, value, status, msg=''):
''' '''
Plan: We will inspect element to figure out type Text line input with dynamic math display (equation rendered on client in real time during input).
''' '''
# TODO: Make a wrapper for <formulainput> # TODO: Make a wrapper for <formulainput>
# TODO: Make an AJAX loop to confirm equation is okay in real-time as user types # TODO: Make an AJAX loop to confirm equation is okay in real-time as user types
## TODO: Code should follow PEP8 (4 spaces per indentation level) ## TODO: Code should follow PEP8 (4 spaces per indentation level)
''' '''
textline is used for simple one-line inputs, like formularesponse and symbolicresponse. textline is used for simple one-line inputs, like formularesponse and symbolicresponse.
''' uses a <span id=display_eid>`{::}`</span>
eid=element.get('id') and a hidden textarea with id=input_eid_fromjs for the mathjax rendering and return.
count = int(eid.split('_')[-2])-1 # HACK '''
size = element.get('size') eid=element.get('id')
dojs = element.get('dojs') # dojs is used for client-side javascript display & return count = int(eid.split('_')[-2])-1 # HACK
# when dojs=='math', a <span id=display_eid>`{::}`</span> size = element.get('size')
# and a hidden textarea with id=input_eid_fromjs will be output context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size,
context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size, 'msg':msg,
'dojs':dojs, }
'msg':msg, html=render_to_string("textinput_dynamath.html", context)
} return etree.XML(html)
html=render_to_string("jstext.html", context)
return etree.XML(html)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
## TODO: Make a wrapper for <codeinput> ## TODO: Make a wrapper for <codeinput>
@register_render_function @register_render_function
def textbox(element, value, status, msg=''): def textbox(element, value, status, msg=''):
''' '''
The textbox is used for code input. The message is the return HTML string from The textbox is used for code input. The message is the return HTML string from
evaluating the code, eg error messages, and output from the code tests. evaluating the code, eg error messages, and output from the code tests.
TODO: make this use rows and cols attribs, not size '''
''' eid=element.get('id')
eid=element.get('id') count = int(eid.split('_')[-2])-1 # HACK
count = int(eid.split('_')[-2])-1 # HACK size = element.get('size')
size = element.get('size') rows = element.get('rows') or '30'
context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size, 'msg':msg} cols = element.get('cols') or '80'
html=render_to_string("textbox.html", context) mode = element.get('mode') or 'python' # mode for CodeMirror, eg "python" or "xml"
return etree.XML(html) linenumbers = element.get('linenumbers') # for CodeMirror
if not value: value = element.text # if no student input yet, then use the default input given by the problem
context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size, 'msg':msg,
'mode':mode, 'linenumbers':linenumbers,
'rows':rows, 'cols':cols,
}
html=render_to_string("textbox.html", context)
return etree.XML(html)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
@register_render_function @register_render_function
......
...@@ -8,7 +8,9 @@ Used by capa_problem.py ...@@ -8,7 +8,9 @@ Used by capa_problem.py
''' '''
# standard library imports # standard library imports
import inspect
import json import json
import logging
import math import math
import numbers import numbers
import numpy import numpy
...@@ -34,6 +36,8 @@ import eia ...@@ -34,6 +36,8 @@ import eia
from util import contextualize_text from util import contextualize_text
log = logging.getLogger("mitx.courseware")
def compare_with_tolerance(v1, v2, tol): def compare_with_tolerance(v1, v2, tol):
''' Compare v1 to v2 with maximum tolerance tol ''' Compare v1 to v2 with maximum tolerance tol
tol is relative if it ends in %; otherwise, it is absolute tol is relative if it ends in %; otherwise, it is absolute
...@@ -140,16 +144,13 @@ class TrueFalseResponse(MultipleChoiceResponse): ...@@ -140,16 +144,13 @@ class TrueFalseResponse(MultipleChoiceResponse):
class OptionResponse(GenericResponse): class OptionResponse(GenericResponse):
''' '''
Example: TODO: handle direction and randomize
'''
<optionresponse direction="vertical" randomize="yes"> snippets = [{'snippet': '''<optionresponse direction="vertical" randomize="yes">
<optioninput options="('Up','Down')" correct="Up"><text>The location of the sky</text></optioninput> <optioninput options="('Up','Down')" correct="Up"><text>The location of the sky</text></optioninput>
<optioninput options="('Up','Down')" correct="Down"><text>The location of the earth</text></optioninput> <optioninput options="('Up','Down')" correct="Down"><text>The location of the earth</text></optioninput>
</optionresponse> </optionresponse>'''}]
TODO: handle direction and randomize
'''
def __init__(self, xml, context, system=None): def __init__(self, xml, context, system=None):
self.xml = xml self.xml = xml
self.answer_fields = xml.findall('optioninput') self.answer_fields = xml.findall('optioninput')
...@@ -176,6 +177,10 @@ class OptionResponse(GenericResponse): ...@@ -176,6 +177,10 @@ class OptionResponse(GenericResponse):
class NumericalResponse(GenericResponse): class NumericalResponse(GenericResponse):
def __init__(self, xml, context, system=None): def __init__(self, xml, context, system=None):
self.xml = xml self.xml = xml
if not xml.get('answer'):
msg = "Error in problem specification: numericalresponse missing required answer attribute\n"
msg += "See XML source line %s" % getattr(xml,'sourceline','<unavailable>')
raise Exception,msg
self.correct_answer = contextualize_text(xml.get('answer'), context) self.correct_answer = contextualize_text(xml.get('answer'), context)
try: try:
self.tolerance_xml = xml.xpath('//*[@id=$id]//responseparam[@type="tolerance"]/@default', self.tolerance_xml = xml.xpath('//*[@id=$id]//responseparam[@type="tolerance"]/@default',
...@@ -212,9 +217,9 @@ class NumericalResponse(GenericResponse): ...@@ -212,9 +217,9 @@ class NumericalResponse(GenericResponse):
class CustomResponse(GenericResponse): class CustomResponse(GenericResponse):
''' '''
Custom response. The python code to be run should be in <answer>...</answer>. Example: Custom response. The python code to be run should be in <answer>...</answer>
or in a <script>...</script>
''' '''
snippets = [{'snippet': '''<customresponse> snippets = [{'snippet': '''<customresponse>
<startouttext/> <startouttext/>
<br/> <br/>
...@@ -233,12 +238,8 @@ class CustomResponse(GenericResponse): ...@@ -233,12 +238,8 @@ class CustomResponse(GenericResponse):
if not(r=="IS*u(t-t0)"): if not(r=="IS*u(t-t0)"):
correct[0] ='incorrect' correct[0] ='incorrect'
</answer> </answer>
</customresponse>'''}] </customresponse>'''},
{'snippet': '''<script type="loncapa/python"><![CDATA[
'''Footnote: the check function can also be defined in <script>...</script> Example:
<script type="loncapa/python"><![CDATA[
def sympy_check2(): def sympy_check2():
messages[0] = '%s:%s' % (submission[0],fromjs[0].replace('<','&lt;')) messages[0] = '%s:%s' % (submission[0],fromjs[0].replace('<','&lt;'))
...@@ -251,22 +252,26 @@ def sympy_check2(): ...@@ -251,22 +252,26 @@ def sympy_check2():
<customresponse cfn="sympy_check2" type="cs" expect="2.27E-39" dojs="math" size="30" answer="2.27E-39"> <customresponse cfn="sympy_check2" type="cs" expect="2.27E-39" dojs="math" size="30" answer="2.27E-39">
<textline size="40" dojs="math" /> <textline size="40" dojs="math" />
<responseparam description="Numerical Tolerance" type="tolerance" default="0.00001" name="tol"/> <responseparam description="Numerical Tolerance" type="tolerance" default="0.00001" name="tol"/>
</customresponse> </customresponse>'''}]
'''
def __init__(self, xml, context, system=None): def __init__(self, xml, context, system=None):
self.xml = xml self.xml = xml
self.system = system
## CRITICAL TODO: Should cover all entrytypes ## CRITICAL TODO: Should cover all entrytypes
## NOTE: xpath will look at root of XML tree, not just ## NOTE: xpath will look at root of XML tree, not just
## what's in xml. @id=id keeps us in the right customresponse. ## what's in xml. @id=id keeps us in the right customresponse.
self.answer_ids = xml.xpath('//*[@id=$id]//textline/@id', self.answer_ids = xml.xpath('//*[@id=$id]//textline/@id',
id=xml.get('id')) id=xml.get('id'))
self.answer_ids += [x.get('id') for x in xml.findall('textbox')] # also allow textbox inputs
self.context = context self.context = context
# if <customresponse> has an "expect" attribute then save that # if <customresponse> has an "expect" (or "answer") attribute then save that
self.expect = xml.get('expect') self.expect = xml.get('expect') or xml.get('answer')
self.myid = xml.get('id') self.myid = xml.get('id')
if settings.DEBUG:
log.info('answer_ids=%s' % self.answer_ids)
# the <answer>...</answer> stanza should be local to the current <customresponse>. So try looking there first. # the <answer>...</answer> stanza should be local to the current <customresponse>. So try looking there first.
self.code = None self.code = None
answer = None answer = None
...@@ -279,7 +284,7 @@ def sympy_check2(): ...@@ -279,7 +284,7 @@ def sympy_check2():
# ie the comparison function is defined in the <script>...</script> stanza instead # ie the comparison function is defined in the <script>...</script> stanza instead
cfn = xml.get('cfn') cfn = xml.get('cfn')
if cfn: if cfn:
if settings.DEBUG: print "[courseware.capa.responsetypes] cfn = ",cfn if settings.DEBUG: log.info("[courseware.capa.responsetypes] cfn = %s" % cfn)
if cfn in context: if cfn in context:
self.code = context[cfn] self.code = context[cfn]
else: else:
...@@ -310,8 +315,16 @@ def sympy_check2(): ...@@ -310,8 +315,16 @@ def sympy_check2():
return default return default
idset = sorted(self.answer_ids) # ordered list of answer id's idset = sorted(self.answer_ids) # ordered list of answer id's
submission = [student_answers[k] for k in idset] # ordered list of answers try:
fromjs = [ getkey2(student_answers,k+'_fromjs',None) for k in idset ] # ordered list of fromjs_XXX responses (if exists) submission = [student_answers[k] for k in idset] # ordered list of answers
except Exception, err:
msg = '[courseware.capa.responsetypes.customresponse] error getting student answer from %s' % student_answers
msg += '\n idset = %s, error = %s' % (idset,err)
log.error(msg)
raise Exception,msg
# global variable in context which holds the Presentation MathML from dynamic math input
dynamath = [ student_answers.get(k+'_dynamath',None) for k in idset ] # ordered list of dynamath responses
# if there is only one box, and it's empty, then don't evaluate # if there is only one box, and it's empty, then don't evaluate
if len(idset)==1 and not submission[0]: if len(idset)==1 and not submission[0]:
...@@ -329,13 +342,18 @@ def sympy_check2(): ...@@ -329,13 +342,18 @@ def sympy_check2():
'expect': self.expect, # expected answer (if given as attribute) 'expect': self.expect, # expected answer (if given as attribute)
'submission':submission, # ordered list of student answers from entry boxes in our subtree 'submission':submission, # ordered list of student answers from entry boxes in our subtree
'idset':idset, # ordered list of ID's of all entry boxes in our subtree 'idset':idset, # ordered list of ID's of all entry boxes in our subtree
'fromjs':fromjs, # ordered list of all javascript inputs in our subtree 'dynamath':dynamath, # ordered list of all javascript inputs in our subtree
'answers':student_answers, # dict of student's responses, with keys being entry box IDs 'answers':student_answers, # dict of student's responses, with keys being entry box IDs
'correct':correct, # the list to be filled in by the check function 'correct':correct, # the list to be filled in by the check function
'messages':messages, # the list of messages to be filled in by the check function 'messages':messages, # the list of messages to be filled in by the check function
'options':self.xml.get('options'), # any options to be passed to the cfn
'testdat':'hello world', 'testdat':'hello world',
}) })
# pass self.system.debug to cfn
# if hasattr(self.system,'debug'): self.context['debug'] = self.system.debug
self.context['debug'] = settings.DEBUG
# exec the check function # exec the check function
if type(self.code)==str: if type(self.code)==str:
try: try:
...@@ -348,34 +366,48 @@ def sympy_check2(): ...@@ -348,34 +366,48 @@ def sympy_check2():
# this is an interface to the Tutor2 check functions # this is an interface to the Tutor2 check functions
fn = self.code fn = self.code
ret = None
if settings.DEBUG: log.info(" submission = %s" % submission)
try: try:
answer_given = submission[0] if (len(idset)==1) else submission answer_given = submission[0] if (len(idset)==1) else submission
if fn.func_code.co_argcount>=4: # does it want four arguments (the answers dict, myname)? # handle variable number of arguments in check function, for backwards compatibility
ret = fn(self.expect,answer_given,student_answers,self.answer_ids[0]) # with various Tutor2 check functions
elif fn.func_code.co_argcount>=3: # does it want a third argument (the answers dict)? args = [self.expect,answer_given,student_answers,self.answer_ids[0]]
ret = fn(self.expect,answer_given,student_answers) argspec = inspect.getargspec(fn)
else: nargs = len(argspec.args)-len(argspec.defaults or [])
ret = fn(self.expect,answer_given) kwargs = {}
for argname in argspec.args[nargs:]:
kwargs[argname] = self.context[argname] if argname in self.context else None
if settings.DEBUG:
log.debug('[courseware.capa.responsetypes.customresponse] answer_given=%s' % answer_given)
log.info('nargs=%d, args=%s, kwargs=%s' % (nargs,args,kwargs))
ret = fn(*args[:nargs],**kwargs)
except Exception,err: except Exception,err:
print "oops in customresponse (cfn) error %s" % err log.error("oops in customresponse (cfn) error %s" % err)
# print "context = ",self.context # print "context = ",self.context
print traceback.format_exc() log.error(traceback.format_exc())
if settings.DEBUG: print "[courseware.capa.responsetypes.customresponse.get_score] ret = ",ret raise Exception,"oops in customresponse (cfn) error %s" % err
if settings.DEBUG: log.info("[courseware.capa.responsetypes.customresponse.get_score] ret = %s" % ret)
if type(ret)==dict: if type(ret)==dict:
correct[0] = 'correct' if ret['ok'] else 'incorrect' correct = ['correct']*len(idset) if ret['ok'] else ['incorrect']*len(idset)
msg = ret['msg'] msg = ret['msg']
if 1: if 1:
# try to clean up message html # try to clean up message html
msg = '<html>'+msg+'</html>' msg = '<html>'+msg+'</html>'
msg = etree.tostring(fromstring_bs(msg),pretty_print=True) msg = msg.replace('&#60;','&lt;')
#msg = msg.replace('&lt;','<')
msg = etree.tostring(fromstring_bs(msg,convertEntities=None),pretty_print=True)
#msg = etree.tostring(fromstring_bs(msg),pretty_print=True)
msg = msg.replace('&#13;','') msg = msg.replace('&#13;','')
#msg = re.sub('<html>(.*)</html>','\\1',msg,flags=re.M|re.DOTALL) # python 2.7 #msg = re.sub('<html>(.*)</html>','\\1',msg,flags=re.M|re.DOTALL) # python 2.7
msg = re.sub('(?ms)<html>(.*)</html>','\\1',msg) msg = re.sub('(?ms)<html>(.*)</html>','\\1',msg)
messages[0] = msg messages[0] = msg
else: else:
correct[0] = 'correct' if ret else 'incorrect' correct = ['correct']*len(idset) if ret else ['incorrect']*len(idset)
# build map giving "correct"ness of the answer(s) # build map giving "correct"ness of the answer(s)
#correct_map = dict(zip(idset, self.context['correct'])) #correct_map = dict(zip(idset, self.context['correct']))
...@@ -403,14 +435,78 @@ def sympy_check2(): ...@@ -403,14 +435,78 @@ def sympy_check2():
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
class ExternalResponse(GenericResponse): class SymbolicResponse(CustomResponse):
"""
Symbolic math response checking, using symmath library.
""" """
Grade the student's input using an external server. snippets = [{'snippet': '''<problem>
<text>Compute \[ \exp\left(-i \frac{\theta}{2} \left[ \begin{matrix} 0 & 1 \\ 1 & 0 \end{matrix} \right] \right) \]
and give the resulting \(2\times 2\) matrix: <br/>
<symbolicresponse answer="">
<textline size="40" math="1" />
</symbolicresponse>
<br/>
Your input should be typed in as a list of lists, eg <tt>[[1,2],[3,4]]</tt>.
</text>
</problem>'''}]
def __init__(self, xml, context, system=None):
xml.set('cfn','symmath_check')
code = "from symmath import *"
exec code in context,context
CustomResponse.__init__(self,xml,context,system)
#-----------------------------------------------------------------------------
class ExternalResponse(GenericResponse):
'''
Grade the students input using an external server.
Typically used by coding problems. Typically used by coding problems.
"""
'''
snippets = [{'snippet': '''<externalresponse tests="repeat:10,generate">
<textbox rows="10" cols="70" mode="python"/>
<answer><![CDATA[
initial_display = """
def inc(x):
"""
answer = """
def inc(n):
return n+1
"""
preamble = """
import sympy
"""
test_program = """
import random
def testInc(n = None):
if n is None:
n = random.randint(2, 20)
print 'Test is: inc(%d)'%n
return str(inc(n))
def main():
f = os.fdopen(3,'w')
test = int(sys.argv[1])
rndlist = map(int,os.getenv('rndlist').split(','))
random.seed(rndlist[0])
if test == 1: f.write(testInc(0))
elif test == 2: f.write(testInc(1))
else: f.write(testInc())
f.close()
main()
"""
]]>
</answer>
</externalresponse>'''}]
def __init__(self, xml, context, system=None): def __init__(self, xml, context, system=None):
self.xml = xml self.xml = xml
self.url = xml.get('url') or "http://eecs1.mit.edu:8889/pyloncapa" # FIXME - hardcoded URL
self.answer_ids = xml.xpath('//*[@id=$id]//textbox/@id|//*[@id=$id]//textline/@id', self.answer_ids = xml.xpath('//*[@id=$id]//textbox/@id|//*[@id=$id]//textline/@id',
id=xml.get('id')) id=xml.get('id'))
self.context = context self.context = context
...@@ -423,25 +519,66 @@ class ExternalResponse(GenericResponse): ...@@ -423,25 +519,66 @@ class ExternalResponse(GenericResponse):
else: else:
self.code = answer.text self.code = answer.text
self.tests = xml.get('answer') self.tests = xml.get('tests')
def get_score(self, student_answers): def do_external_request(self,cmd,extra_payload):
submission = [student_answers[k] for k in sorted(self.answer_ids)] '''
self.context.update({'submission':submission}) Perform HTTP request / post to external server.
xmlstr = etree.tostring(self.xml, pretty_print=True) cmd = remote command to perform (str)
extra_payload = dict of extra stuff to post.
Return XML tree of response (from response body)
'''
xmlstr = etree.tostring(self.xml, pretty_print=True)
payload = {'xml': xmlstr, payload = {'xml': xmlstr,
### Question: Is this correct/what we want? Shouldn't this be a json.dumps? 'edX_cmd' : cmd,
'LONCAPA_student_response': ''.join(submission), 'edX_tests': self.tests,
'LONCAPA_correct_answer': self.tests,
'processor' : self.code, 'processor' : self.code,
} }
payload.update(extra_payload)
try:
r = requests.post(self.url,data=payload) # call external server
except Exception,err:
msg = 'Error %s - cannot connect to external server url=%s' % (err,self.url)
log.error(msg)
raise Exception, msg
if settings.DEBUG: log.info('response = %s' % r.text)
if (not r.text ) or (not r.text.strip()):
raise Exception,'Error: no response from external server url=%s' % self.url
try:
rxml = etree.fromstring(r.text) # response is XML; prase it
except Exception,err:
msg = 'Error %s - cannot parse response from external server r.text=%s' % (err,r.text)
log.error(msg)
raise Exception, msg
return rxml
def get_score(self, student_answers):
try:
submission = [student_answers[k] for k in sorted(self.answer_ids)]
except Exception,err:
log.error('Error %s: cannot get student answer for %s; student_answers=%s' % (err,self.answer_ids,student_answers))
raise Exception,err
self.context.update({'submission':submission})
# call external server; TODO: get URL from settings.py extra_payload = {'edX_student_response': json.dumps(submission)}
r = requests.post("http://eecs1.mit.edu:8889/pyloncapa",data=payload)
try:
rxml = self.do_external_request('get_score',extra_payload)
except Exception, err:
log.error('Error %s' % err)
if settings.DEBUG:
correct_map = dict(zip(sorted(self.answer_ids), ['incorrect'] * len(self.answer_ids) ))
correct_map['msg_%s' % self.answer_ids[0]] = '<font color="red" size="+2">%s</font>' % str(err).replace('<','&lt;')
return correct_map
rxml = etree.fromstring(r.text) # response is XML; prase it
ad = rxml.find('awarddetail').text ad = rxml.find('awarddetail').text
admap = {'EXACT_ANS':'correct', # TODO: handle other loncapa responses admap = {'EXACT_ANS':'correct', # TODO: handle other loncapa responses
'WRONG_FORMAT': 'incorrect', 'WRONG_FORMAT': 'incorrect',
...@@ -453,15 +590,29 @@ class ExternalResponse(GenericResponse): ...@@ -453,15 +590,29 @@ class ExternalResponse(GenericResponse):
# self.context['correct'] = ['correct','correct'] # self.context['correct'] = ['correct','correct']
correct_map = dict(zip(sorted(self.answer_ids), self.context['correct'])) correct_map = dict(zip(sorted(self.answer_ids), self.context['correct']))
# TODO: separate message for each answer_id? # store message in correct_map
correct_map['msg'] = rxml.find('message').text.replace('&nbsp;','&#160;') # store message in correct_map correct_map['msg_%s' % self.answer_ids[0]] = rxml.find('message').text.replace('&nbsp;','&#160;')
return correct_map return correct_map
def get_answers(self): def get_answers(self):
# Since this is explicitly specified in the problem, this will '''
# be handled by capa_problem Use external server to get expected answers
return {} '''
try:
rxml = self.do_external_request('get_answers',{})
exans = json.loads(rxml.find('expected').text)
except Exception,err:
log.error('Error %s' % err)
if settings.DEBUG:
msg = '<font color=red size=+2>%s</font>' % str(err).replace('<','&lt;')
exans = [''] * len(self.answer_ids)
exans[0] = msg
if not (len(exans)==len(self.answer_ids)):
log.error('Expected %d answers from external server, only got %d!' % (len(self.answer_ids),len(exans)))
raise Exception,'Short response from external server'
return dict(zip(self.answer_ids,exans))
class StudentInputError(Exception): class StudentInputError(Exception):
pass pass
...@@ -469,6 +620,27 @@ class StudentInputError(Exception): ...@@ -469,6 +620,27 @@ class StudentInputError(Exception):
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
class FormulaResponse(GenericResponse): class FormulaResponse(GenericResponse):
'''
Checking of symbolic math response using numerical sampling.
'''
snippets = [{'snippet': '''<problem>
<script type="loncapa/python">
I = "m*c^2"
</script>
<text>
<br/>
Give an equation for the relativistic energy of an object with mass m.
</text>
<formularesponse type="cs" samples="m,c@1,2:3,4#10" answer="$I">
<responseparam description="Numerical Tolerance" type="tolerance"
default="0.00001" name="tol" />
<textline size="40" math="1" />
</formularesponse>
</problem>'''}]
def __init__(self, xml, context, system=None): def __init__(self, xml, context, system=None):
self.xml = xml self.xml = xml
self.correct_answer = contextualize_text(xml.get('answer'), context) self.correct_answer = contextualize_text(xml.get('answer'), context)
...@@ -587,15 +759,12 @@ class ImageResponse(GenericResponse): ...@@ -587,15 +759,12 @@ class ImageResponse(GenericResponse):
doesn't make sense to me (Ike). Instead, let's have it such that <imageresponse> doesn't make sense to me (Ike). Instead, let's have it such that <imageresponse>
should contain one or more <imageinput> stanzas. Each <imageinput> should specify should contain one or more <imageinput> stanzas. Each <imageinput> should specify
a rectangle, given as an attribute, defining the correct answer. a rectangle, given as an attribute, defining the correct answer.
"""
Example: snippets = [{'snippet': '''<imageresponse>
<imageresponse>
<imageinput src="image1.jpg" width="200" height="100" rectangle="(10,10)-(20,30)" /> <imageinput src="image1.jpg" width="200" height="100" rectangle="(10,10)-(20,30)" />
<imageinput src="image2.jpg" width="210" height="130" rectangle="(12,12)-(40,60)" /> <imageinput src="image2.jpg" width="210" height="130" rectangle="(12,12)-(40,60)" />
</imageresponse> </imageresponse>'''}]
"""
def __init__(self, xml, context, system=None): def __init__(self, xml, context, system=None):
self.xml = xml self.xml = xml
self.context = context self.context = context
...@@ -621,7 +790,7 @@ class ImageResponse(GenericResponse): ...@@ -621,7 +790,7 @@ class ImageResponse(GenericResponse):
# parse given answer # parse given answer
m = re.match('\[([0-9]+),([0-9]+)]',given.strip().replace(' ','')) m = re.match('\[([0-9]+),([0-9]+)]',given.strip().replace(' ',''))
if not m: if not m:
raise Exception,'[capamodule.capa.responsetypes.imageinput] error grading %s (input=%s)' % (err,aid,given) raise Exception,'[capamodule.capa.responsetypes.imageinput] error grading %s (input=%s)' % (aid,given)
(gx,gy) = [int(x) for x in m.groups()] (gx,gy) = [int(x) for x in m.groups()]
# answer is correct if (x,y) is within the specified rectangle # answer is correct if (x,y) is within the specified rectangle
......
...@@ -203,6 +203,10 @@ def course_file(user,coursename=None): ...@@ -203,6 +203,10 @@ def course_file(user,coursename=None):
else: else:
tree_string = None tree_string = None
if settings.DEBUG:
log.info('[courseware.content_parser.course_file] filename=%s, cache_key=%s' % (filename,cache_key))
# print '[courseware.content_parser.course_file] tree_string = ',tree_string
if not tree_string: if not tree_string:
tree = course_xml_process(etree.XML(render_to_string(filename, options, namespace = 'course'))) tree = course_xml_process(etree.XML(render_to_string(filename, options, namespace = 'course')))
tree_string = etree.tostring(tree) tree_string = etree.tostring(tree)
...@@ -231,7 +235,7 @@ def section_file(user, section, coursename=None, dironly=False): ...@@ -231,7 +235,7 @@ def section_file(user, section, coursename=None, dironly=False):
if dironly: return dirname if dironly: return dirname
if filename not in os.listdir(dirname): if filename not in os.listdir(dirname):
print filename+" not in "+str(os.listdir(dirname)) log.error(filename+" not in "+str(os.listdir(dirname)))
return None return None
options = {'dev_content':settings.DEV_CONTENT, options = {'dev_content':settings.DEV_CONTENT,
...@@ -271,9 +275,14 @@ def module_xml(user, module, id_tag, module_id, coursename=None): ...@@ -271,9 +275,14 @@ def module_xml(user, module, id_tag, module_id, coursename=None):
break break
if len(result_set)>1: if len(result_set)>1:
print "WARNING: Potentially malformed course file", module, module_id log.error("WARNING: Potentially malformed course file", module, module_id)
if len(result_set)==0: if len(result_set)==0:
if settings.DEBUG:
log.error('[courseware.content_parser.module_xml] cannot find %s in course.xml tree' % xpath_search)
log.error('tree = %s' % etree.tostring(doc,pretty_print=True))
return None return None
if settings.DEBUG:
log.info('[courseware.content_parser.module_xml] found %s' % result_set)
return etree.tostring(result_set[0]) return etree.tostring(result_set[0])
#return result_set[0].serialize() #return result_set[0].serialize()
......
...@@ -35,8 +35,16 @@ class I4xSystem(object): ...@@ -35,8 +35,16 @@ class I4xSystem(object):
self.filestore = OSFS(settings.DATA_DIR) self.filestore = OSFS(settings.DATA_DIR)
else: else:
self.filestore = filestore self.filestore = filestore
if settings.DEBUG:
log.info("[courseware.module_render.I4xSystem] filestore path = %s" % filestore)
self.render_function = render_function self.render_function = render_function
self.exception404 = Http404 self.exception404 = Http404
self.DEBUG = settings.DEBUG
def get(self,attr): # uniform access to attributes (like etree)
return self.__dict__.get(attr)
def set(self,attr,val): # uniform access to attributes (like etree)
self.__dict__[attr] = val
def __repr__(self): def __repr__(self):
return repr(self.__dict__) return repr(self.__dict__)
def __str__(self): def __str__(self):
...@@ -80,8 +88,7 @@ def grade_histogram(module_id): ...@@ -80,8 +88,7 @@ def grade_histogram(module_id):
return [] return []
return grades return grades
def get_module(user, request, xml_module, module_object_preload, position=None):
def get_module(user, request, xml_module, module_object_preload):
module_type=xml_module.tag module_type=xml_module.tag
module_class=courseware.modules.get_module_class(module_type) module_class=courseware.modules.get_module_class(module_type)
module_id=xml_module.get('id') #module_class.id_attribute) or "" module_id=xml_module.get('id') #module_class.id_attribute) or ""
...@@ -110,10 +117,11 @@ def get_module(user, request, xml_module, module_object_preload): ...@@ -110,10 +117,11 @@ def get_module(user, request, xml_module, module_object_preload):
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/' ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
system = I4xSystem(track_function = make_track_function(request), system = I4xSystem(track_function = make_track_function(request),
render_function = lambda x: render_x_module(user, request, x, module_object_preload), render_function = lambda x: render_x_module(user, request, x, module_object_preload, position),
ajax_url = ajax_url, ajax_url = ajax_url,
filestore = OSFS(data_root), filestore = OSFS(data_root),
) )
system.set('position',position) # pass URL specified position along to module, through I4xSystem
instance=module_class(system, instance=module_class(system,
etree.tostring(xml_module), etree.tostring(xml_module),
module_id, module_id,
...@@ -131,12 +139,30 @@ def get_module(user, request, xml_module, module_object_preload): ...@@ -131,12 +139,30 @@ def get_module(user, request, xml_module, module_object_preload):
return (instance, smod, module_type) return (instance, smod, module_type)
def render_x_module(user, request, xml_module, module_object_preload): def render_x_module(user, request, xml_module, module_object_preload, position=None):
''' Generic module for extensions. This renders to HTML. ''' ''' Generic module for extensions. This renders to HTML.
modules include sequential, vertical, problem, video, html
Note that modules can recurse. problems, video, html, can be inside sequential or vertical.
Arguments:
- user : current django User
- request : current django HTTPrequest
- xml_module : lxml etree of xml subtree for the current module
- module_object_preload : list of StudentModule objects, one of which may match this module type and id
- position : extra information from URL for user-specified position within module
Returns:
- dict which is context for HTML rendering of the specified module
'''
if xml_module==None : if xml_module==None :
return {"content":""} return {"content":""}
(instance, smod, module_type) = get_module(user, request, xml_module, module_object_preload) (instance, smod, module_type) = get_module(user, request, xml_module, module_object_preload, position)
# Grab content # Grab content
content = instance.get_html() content = instance.get_html()
...@@ -156,9 +182,8 @@ def render_x_module(user, request, xml_module, module_object_preload): ...@@ -156,9 +182,8 @@ def render_x_module(user, request, xml_module, module_object_preload):
return content return content
def modx_dispatch(request, module=None, dispatch=None, id=None): def modx_dispatch(request, module=None, dispatch=None, id=None):
''' Generic view for extensions. ''' ''' Generic view for extensions. This is where AJAX calls go.'''
if not request.user.is_authenticated(): if not request.user.is_authenticated():
return redirect('/') return redirect('/')
...@@ -191,7 +216,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): ...@@ -191,7 +216,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
try: try:
xml = content_parser.module_xml(request.user, module, 'id', id, coursename) xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
except: except:
log.exception("Unable to load module during ajax call") log.exception("Unable to load module during ajax call. module=%s, dispatch=%s, id=%s" % (module, dispatch, id))
if accepts(request, 'text/html'): if accepts(request, 'text/html'):
return render_to_response("module-error.html", {}) return render_to_response("module-error.html", {})
else: else:
...@@ -228,4 +253,3 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): ...@@ -228,4 +253,3 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
s.save() s.save()
# Return whatever the module wanted to return to the client/caller # Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return) return HttpResponse(ajax_return)
...@@ -25,6 +25,8 @@ from multicourse import multicourse_settings ...@@ -25,6 +25,8 @@ from multicourse import multicourse_settings
log = logging.getLogger("mitx.courseware") log = logging.getLogger("mitx.courseware")
#-----------------------------------------------------------------------------
class ComplexEncoder(json.JSONEncoder): class ComplexEncoder(json.JSONEncoder):
def default(self, obj): def default(self, obj):
if isinstance(obj, complex): if isinstance(obj, complex):
...@@ -127,7 +129,7 @@ class Module(XModule): ...@@ -127,7 +129,7 @@ class Module(XModule):
html=render_to_string('problem.html', context) html=render_to_string('problem.html', context)
if encapsulate: if encapsulate:
html = '<div id="problem_{id}" class="problem" data-url="{ajax_url}">'.format(id=self.item_id)+html+"</div>" html = '<div id="problem_{id}" class="problem" data-url="{ajax_url}">'.format(id=self.item_id,ajax_url=self.ajax_url)+html+"</div>"
return html return html
...@@ -197,9 +199,27 @@ class Module(XModule): ...@@ -197,9 +199,27 @@ class Module(XModule):
try: try:
fp = self.filestore.open(self.filename) fp = self.filestore.open(self.filename)
except Exception,err: except Exception,err:
print '[courseware.capa.capa_module.Module.init] error %s: cannot open file %s' % (err,self.filename) log.exception('[courseware.capa.capa_module.Module.init] error %s: cannot open file %s' % (err,self.filename))
raise Exception,err if self.DEBUG:
self.lcp=LoncapaProblem(fp, self.item_id, state, seed = seed, system=self.system) # create a dummy problem instead of failing
fp = StringIO.StringIO('<problem><text><font color="red" size="+2">Problem file %s is missing</font></text></problem>' % self.filename)
fp.name = "StringIO"
else:
raise
try:
self.lcp=LoncapaProblem(fp, self.item_id, state, seed = seed, system=self.system)
except Exception,err:
msg = '[courseware.capa.capa_module.Module.init] error %s: cannot create LoncapaProblem %s' % (err,self.filename)
log.exception(msg)
if self.DEBUG:
msg = '<p>%s</p>' % msg.replace('<','&lt;')
msg += '<p><pre>%s</pre></p>' % traceback.format_exc().replace('<','&lt;')
# create a dummy problem with error message instead of failing
fp = StringIO.StringIO('<problem><text><font color="red" size="+2">Problem file %s has an error:</font>%s</text></problem>' % (self.filename,msg))
fp.name = "StringIO"
self.lcp=LoncapaProblem(fp, self.item_id, state, seed = seed, system=self.system)
else:
raise
def handle_ajax(self, dispatch, get): def handle_ajax(self, dispatch, get):
''' '''
...@@ -328,8 +348,15 @@ class Module(XModule): ...@@ -328,8 +348,15 @@ class Module(XModule):
self.tracker('save_problem_check', event_info) self.tracker('save_problem_check', event_info)
try:
html = self.get_problem_html(encapsulate=False)
except Exception,err:
log.error('failed to generate html')
raise Exception,err
return json.dumps({'success': success, return json.dumps({'success': success,
'contents': self.get_problem_html(encapsulate=False)}) 'contents': html,
})
def save_problem(self, get): def save_problem(self, get):
event_info = dict() event_info = dict()
......
import json import json
import logging
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
from x_module import XModule, XModuleDescriptor from x_module import XModule, XModuleDescriptor
from lxml import etree from lxml import etree
log = logging.getLogger("mitx.courseware")
#-----------------------------------------------------------------------------
class ModuleDescriptor(XModuleDescriptor): class ModuleDescriptor(XModuleDescriptor):
pass pass
...@@ -28,7 +32,10 @@ class Module(XModule): ...@@ -28,7 +32,10 @@ class Module(XModule):
filename="html/"+self.filename filename="html/"+self.filename
return self.filestore.open(filename).read() return self.filestore.open(filename).read()
except: # For backwards compatibility. TODO: Remove except: # For backwards compatibility. TODO: Remove
return render_to_string(self.filename, {'id': self.item_id}) if self.DEBUG:
log.info('[courseware.modules.html_module] filename=%s' % self.filename)
#return render_to_string(self.filename, {'id': self.item_id})
return render_to_string(self.filename, {'id': self.item_id},namespace='course')
def __init__(self, system, xml, item_id, state=None): def __init__(self, system, xml, item_id, state=None):
XModule.__init__(self, system, xml, item_id, state) XModule.__init__(self, system, xml, item_id, state)
......
...@@ -31,7 +31,16 @@ class Module(XModule): ...@@ -31,7 +31,16 @@ class Module(XModule):
self.render() self.render()
return self.content return self.content
def handle_ajax(self, dispatch, get): def get_init_js(self):
self.render()
return self.init_js
def get_destroy_js(self):
self.render()
return self.destroy_js
def handle_ajax(self, dispatch, get): # TODO: bounds checking
''' get = request.POST instance '''
if dispatch=='goto_position': if dispatch=='goto_position':
self.position = int(get['position']) self.position = int(get['position'])
return json.dumps({'success':True}) return json.dumps({'success':True})
...@@ -83,4 +92,8 @@ class Module(XModule): ...@@ -83,4 +92,8 @@ class Module(XModule):
state = json.loads(state) state = json.loads(state)
if 'position' in state: self.position = int(state['position']) if 'position' in state: self.position = int(state['position'])
# if position is specified in system, then use that instead
if system.get('position'):
self.position = int(system.get('position'))
self.rendered = False self.rendered = False
...@@ -55,6 +55,7 @@ class XModule(object): ...@@ -55,6 +55,7 @@ class XModule(object):
self.json = json self.json = json
self.item_id = item_id self.item_id = item_id
self.state = state self.state = state
self.DEBUG = False
self.__xmltree = etree.fromstring(xml) # PRIVATE self.__xmltree = etree.fromstring(xml) # PRIVATE
...@@ -65,6 +66,7 @@ class XModule(object): ...@@ -65,6 +66,7 @@ class XModule(object):
self.tracker = system.track_function self.tracker = system.track_function
self.filestore = system.filestore self.filestore = system.filestore
self.render_function = system.render_function self.render_function = system.render_function
self.DEBUG = system.DEBUG
self.system = system self.system = system
### Functions used in the LMS ### Functions used in the LMS
......
<problem>
<text>
<h2>Example: Symbolic Math Response Problem</h2>
<p>
A symbolic math response problem presents one or more symbolic math
input fields for input. Correctness of input is evaluated based on
the symbolic properties of the expression entered. The student enters
text, but sees a proper symbolic rendition of the entered formula, in
real time, next to the input box.
</p>
<p>This is a correct answer which may be entered below: </p>
<p><tt>cos(theta)*[[1,0],[0,1]] + i*sin(theta)*[[0,1],[1,0]]</tt></p>
<script>
from symmath import *
</script>
<text>Compute [mathjax] U = \exp\left( i \theta \left[ \begin{matrix} 0 &amp; 1 \\ 1 &amp; 0 \end{matrix} \right] \right) [/mathjax]
and give the resulting \(2 \times 2\) matrix. <br/>
Your input should be typed in as a list of lists, eg <tt>[[1,2],[3,4]]</tt>. <br/>
[mathjax]U=[/mathjax] <symbolicresponse cfn="symmath_check" answer="[[cos(theta),I*sin(theta)],[I*sin(theta),cos(theta)]]" options="matrix,imaginaryi" id="filenamedogi0VpEBOWedxsymmathresponse_1" state="unsubmitted">
<textline size="80" math="1" response_id="2" answer_id="1" id="filenamedogi0VpEBOWedxsymmathresponse_2_1"/>
</symbolicresponse>
<br/>
</text>
</text>
</problem>
#
# unittests for courseware
#
# Note: run this using a like like this:
#
# django-admin.py test --settings=envs.test_ike --pythonpath=. courseware
import unittest import unittest
import os import os
...@@ -127,6 +134,94 @@ class ImageResponseTest(unittest.TestCase): ...@@ -127,6 +134,94 @@ class ImageResponseTest(unittest.TestCase):
self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_1'], 'correct') self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_1'], 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_2'], 'incorrect') self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_2'], 'incorrect')
class SymbolicResponseTest(unittest.TestCase):
def test_sr_grade(self):
symbolicresponse_file = os.path.dirname(__file__)+"/test_files/symbolicresponse.xml"
test_lcp = lcp.LoncapaProblem(open(symbolicresponse_file), '1', system=i4xs)
correct_answers = {'1_2_1':'cos(theta)*[[1,0],[0,1]] + i*sin(theta)*[[0,1],[1,0]]',
'1_2_1_dynamath': '''
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mrow>
<mi>cos</mi>
<mrow>
<mo>(</mo>
<mi>&#x3B8;</mi>
<mo>)</mo>
</mrow>
</mrow>
<mo>&#x22C5;</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
<mo>+</mo>
<mi>i</mi>
<mo>&#x22C5;</mo>
<mrow>
<mi>sin</mi>
<mrow>
<mo>(</mo>
<mi>&#x3B8;</mi>
<mo>)</mo>
</mrow>
</mrow>
<mo>&#x22C5;</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
</mstyle>
</math>
''',
}
wrong_answers = {'1_2_1':'2',
'1_2_1_dynamath':'''
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mn>2</mn>
</mstyle>
</math>''',
}
self.assertEquals(test_lcp.grade_answers(correct_answers)['1_2_1'], 'correct')
self.assertEquals(test_lcp.grade_answers(wrong_answers)['1_2_1'], 'incorrect')
class OptionResponseTest(unittest.TestCase): class OptionResponseTest(unittest.TestCase):
''' '''
Run this with Run this with
......
...@@ -11,6 +11,7 @@ from django.http import Http404 ...@@ -11,6 +11,7 @@ from django.http import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
#from django.views.decorators.csrf import ensure_csrf_cookie #from django.views.decorators.csrf import ensure_csrf_cookie
from django_future.csrf import ensure_csrf_cookie
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from lxml import etree from lxml import etree
...@@ -145,9 +146,23 @@ def render_section(request, section): ...@@ -145,9 +146,23 @@ def render_section(request, section):
return result return result
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
def index(request, course=None, chapter="Using the System", section="Hints"): def index(request, course=None, chapter="Using the System", section="Hints",position=None):
''' Displays courseware accordion, and any associated content. ''' Displays courseware accordion, and any associated content.
Arguments:
- request : HTTP request
- course : coursename (str)
- chapter : chapter name (str)
- section : section name (str)
- position : position in module, eg of <sequential> module (str)
Returns:
- HTTPresponse
''' '''
user = request.user user = request.user
if not settings.COURSEWARE_ENABLED: if not settings.COURSEWARE_ENABLED:
...@@ -176,12 +191,13 @@ def index(request, course=None, chapter="Using the System", section="Hints"): ...@@ -176,12 +191,13 @@ def index(request, course=None, chapter="Using the System", section="Hints"):
request.session['coursename'] = course # keep track of current course being viewed in django's request.session request.session['coursename'] = course # keep track of current course being viewed in django's request.session
try: try:
# this is the course.xml etree
dom = content_parser.course_file(user,course) # also pass course to it, for course-specific XML path dom = content_parser.course_file(user,course) # also pass course to it, for course-specific XML path
except: except:
log.exception("Unable to parse courseware xml") log.exception("Unable to parse courseware xml")
return render_to_response('courseware-error.html', {}) return render_to_response('courseware-error.html', {})
#dom_module = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]/*[1]", # this is the module's parent's etree
dom_module = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]", dom_module = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]",
course=course, chapter=chapter, section=section) course=course, chapter=chapter, section=section)
...@@ -197,6 +213,7 @@ def index(request, course=None, chapter="Using the System", section="Hints"): ...@@ -197,6 +213,7 @@ def index(request, course=None, chapter="Using the System", section="Hints"):
elif module_wrapper.get("src"): elif module_wrapper.get("src"):
module = content_parser.section_file(user=user, section=module_wrapper.get("src"), coursename=course) module = content_parser.section_file(user=user, section=module_wrapper.get("src"), coursename=course)
else: else:
# this is the module's etree
module = etree.XML(etree.tostring(module_wrapper[0])) # Copy the element out of the tree module = etree.XML(etree.tostring(module_wrapper[0])) # Copy the element out of the tree
module_ids = [] module_ids = []
...@@ -217,7 +234,7 @@ def index(request, course=None, chapter="Using the System", section="Hints"): ...@@ -217,7 +234,7 @@ def index(request, course=None, chapter="Using the System", section="Hints"):
} }
try: try:
module = render_x_module(user, request, module, module_object_preload) module_context = render_x_module(user, request, module, module_object_preload, position)
except: except:
log.exception("Unable to load module") log.exception("Unable to load module")
context.update({ context.update({
...@@ -227,104 +244,48 @@ def index(request, course=None, chapter="Using the System", section="Hints"): ...@@ -227,104 +244,48 @@ def index(request, course=None, chapter="Using the System", section="Hints"):
return render_to_response('courseware.html', context) return render_to_response('courseware.html', context)
context.update({ context.update({
'init': module.get('init_js', ''), 'init': module_context.get('init_js', ''),
'content': module['content'], 'content': module_context['content'],
}) })
result = render_to_response('courseware.html', context) result = render_to_response('courseware.html', context)
return result return result
def jump_to(request, probname=None):
def quickedit(request, id=None):
''' '''
quick-edit capa problem. Jump to viewing a specific problem. The problem is specified by a problem name - currently the filename (minus .xml)
of the problem. Maybe this should change to a more generic tag, eg "name" given as an attribute in <problem>.
Maybe this should be moved into capa/views.py We do the jump by (1) reading course.xml to find the first instance of <problem> with the given filename, then
Or this should take a "module" argument, and the quickedit moved into capa_module. (2) finding the parent element of the problem, then (3) rendering that parent element with a specific computed position
''' value (if it is <sequential>).
print "WARNING: UNDEPLOYABLE CODE. FOR CONTENT DEV USE ONLY."
print "In deployed use, this will only edit on one server"
print "We need a setting to disable for production where there is"
print "a load balanacer"
if not request.user.is_staff:
return redirect('/')
'''
# get coursename if stored # get coursename if stored
coursename = multicourse_settings.get_coursename_from_request(request) coursename = multicourse_settings.get_coursename_from_request(request)
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
def get_lcp(coursename,id): # begin by getting course.xml tree
# Grab the XML corresponding to the request from course.xml xml = content_parser.course_file(request.user,coursename)
module = 'problem'
xml = content_parser.module_xml(request.user, module, 'id', id, coursename) # look for problem of given name
pxml = xml.xpath('//problem[@filename="%s"]' % probname)
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/' if pxml: pxml = pxml[0]
# Create the module (instance of capa_module.Module) # get the parent element
system = I4xSystem(track_function = make_track_function(request), parent = pxml.getparent()
render_function = None,
ajax_url = ajax_url, # figure out chapter and section names
filestore = OSFS(settings.DATA_DIR + xp), chapter = None
#role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this section = None
) branch = parent
instance=courseware.modules.get_module_class(module)(system, for k in range(4): # max depth of recursion
xml, if branch.tag=='section': section = branch.get('name')
id, if branch.tag=='chapter': chapter = branch.get('name')
state=None) branch = branch.getparent()
lcp = instance.lcp
pxml = lcp.tree position = None
pxmls = etree.tostring(pxml,pretty_print=True) if parent.tag=='sequential':
position = parent.index(pxml)+1 # position in sequence
return instance, pxmls
return index(request,course=coursename,chapter=chapter,section=section,position=position)
instance, pxmls = get_lcp(coursename,id)
# if there was a POST, then process it
msg = ''
if 'qesubmit' in request.POST:
action = request.POST['qesubmit']
if "Revert" in action:
msg = "Reverted to original"
elif action=='Change Problem':
key = 'quickedit_%s' % id
if not key in request.POST:
msg = "oops, missing code key=%s" % key
else:
newcode = request.POST[key]
# see if code changed
if str(newcode)==str(pxmls) or '<?xml version="1.0"?>\n'+str(newcode)==str(pxmls):
msg = "No changes"
else:
# check new code
isok = False
try:
newxml = etree.fromstring(newcode)
isok = True
except Exception,err:
msg = "Failed to change problem: XML error \"<font color=red>%s</font>\"" % err
if isok:
filename = instance.lcp.fileobject.name
fp = open(filename,'w') # TODO - replace with filestore call?
fp.write(newcode)
fp.close()
msg = "<font color=green>Problem changed!</font> (<tt>%s</tt>)" % filename
instance, pxmls = get_lcp(coursename,id)
lcp = instance.lcp
# get the rendered problem HTML
phtml = instance.get_problem_html()
context = {'id':id,
'msg' : msg,
'lcp' : lcp,
'filename' : lcp.fileobject.name,
'pxmls' : pxmls,
'phtml' : phtml,
'init_js':instance.get_init_js(),
}
result = render_to_response('quickedit.html', context)
return result
...@@ -78,3 +78,4 @@ def get_course_title(coursename): ...@@ -78,3 +78,4 @@ def get_course_title(coursename):
def get_course_number(coursename): def get_course_number(coursename):
return get_course_property(coursename,'number') return get_course_property(coursename,'number')
...@@ -17,6 +17,8 @@ from multicourse import multicourse_settings ...@@ -17,6 +17,8 @@ from multicourse import multicourse_settings
def mitxhome(request): def mitxhome(request):
''' Home page (link from main header). List of courses. ''' ''' Home page (link from main header). List of courses. '''
if settings.DEBUG:
print "[djangoapps.multicourse.mitxhome] MITX_ROOT_URL = " + settings.MITX_ROOT_URL
if settings.ENABLE_MULTICOURSE: if settings.ENABLE_MULTICOURSE:
context = {'courseinfo' : multicourse_settings.COURSE_SETTINGS} context = {'courseinfo' : multicourse_settings.COURSE_SETTINGS}
return render_to_response("mitxhome.html", context) return render_to_response("mitxhome.html", context)
......
...@@ -173,7 +173,8 @@ class SSLLoginBackend(ModelBackend): ...@@ -173,7 +173,8 @@ class SSLLoginBackend(ModelBackend):
try: try:
user = User.objects.get(username=username) # if user already exists don't create it user = User.objects.get(username=username) # if user already exists don't create it
except User.DoesNotExist: except User.DoesNotExist:
raise "User does not exist. Not creating user; potential schema consistency issues" if not settings.DEBUG:
raise "User does not exist. Not creating user; potential schema consistency issues"
#raise ImproperlyConfigured("[SSLLoginBackend] creating %s" % repr(info)) #raise ImproperlyConfigured("[SSLLoginBackend] creating %s" % repr(info))
user = User(username=username, password=GenPasswd()) # create new User user = User(username=username, password=GenPasswd()) # create new User
user.is_staff = False user.is_staff = False
......
...@@ -35,7 +35,8 @@ PERFSTATS = False ...@@ -35,7 +35,8 @@ PERFSTATS = False
# Features # Features
MITX_FEATURES = { MITX_FEATURES = {
'SAMPLE' : False 'SAMPLE' : False,
'USE_DJANGO_PIPELINE' : True,
} }
# Used for A/B testing # Used for A/B testing
......
"""
This config file runs the simplest dev environment using sqlite, and db-based
sessions. Assumes structure:
/envroot/
/db # This is where it'll write the database file
/mitx # The location of this repo
/log # Where we're going to write log files
"""
import socket
if 'eecs1' in socket.gethostname():
MITX_ROOT_URL = '/mitx2'
from envs.common import *
from envs.logsettings import get_logger_config
from dev import *
if 'eecs1' in socket.gethostname():
MITX_ROOT_URL = '/mitx2'
#-----------------------------------------------------------------------------
# ichuang
DEBUG = True
ENABLE_MULTICOURSE = True # set to False to disable multicourse display (see lib.util.views.mitxhome)
QUICKEDIT = True
MITX_FEATURES['USE_DJANGO_PIPELINE'] = False
COURSE_SETTINGS = {'6.002_Spring_2012': {'number' : '6.002x',
'title' : 'Circuits and Electronics',
'xmlpath': '/6002x/',
'active' : True,
},
'8.02_Spring_2013': {'number' : '8.02x',
'title' : 'Electricity &amp; Magnetism',
'xmlpath': '/802x/',
'active' : True,
},
'8.01_Spring_2013': {'number' : '8.01x',
'title' : 'Mechanics',
'xmlpath': '/801x/',
'active' : False,
},
'6.189_Spring_2013': {'number' : '6.189x',
'title' : 'IAP Python Programming',
'xmlpath': '/6189-pytutor/',
'active' : True,
},
'8.01_Summer_2012': {'number' : '8.01x',
'title' : 'Mechanics',
'xmlpath': '/801x-summer/',
'active': True,
},
'edx4edx': {'number' : 'edX.01',
'title' : 'edx4edx: edX Author Course',
'xmlpath': '/edx4edx/',
'active' : True,
},
}
#-----------------------------------------------------------------------------
MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + (
'ssl_auth.ssl_auth.NginxProxyHeaderMiddleware', # ssl authentication behind nginx proxy
)
AUTHENTICATION_BACKENDS = (
'ssl_auth.ssl_auth.SSLLoginBackend',
'django.contrib.auth.backends.ModelBackend',
)
INSTALLED_APPS = INSTALLED_APPS + (
'ssl_auth',
)
LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/'
LOGIN_URL = MITX_ROOT_URL + '/'
"""
This config file runs the simplest dev environment using sqlite, and db-based
sessions. Assumes structure:
/envroot/
/db # This is where it'll write the database file
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from envs.common import *
from envs.logsettings import get_logger_config
import os
DEBUG = True
INSTALLED_APPS = [
app
for app
in INSTALLED_APPS
if not app.startswith('askbot')
]
# Nose Test Runner
INSTALLED_APPS += ['django_nose']
#NOSE_ARGS = ['--cover-erase', '--with-xunit', '--with-xcoverage', '--cover-html', '--cover-inclusive']
NOSE_ARGS = ['--cover-erase', '--with-xunit', '--cover-html', '--cover-inclusive']
for app in os.listdir(PROJECT_ROOT / 'djangoapps'):
NOSE_ARGS += ['--cover-package', app]
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
# Local Directories
TEST_ROOT = path("test_root")
COURSES_ROOT = TEST_ROOT / "data"
DATA_DIR = COURSES_ROOT
MAKO_TEMPLATES['course'] = [DATA_DIR]
MAKO_TEMPLATES['sections'] = [DATA_DIR / 'sections']
MAKO_TEMPLATES['custom_tags'] = [DATA_DIR / 'custom_tags']
MAKO_TEMPLATES['main'] = [PROJECT_ROOT / 'templates',
DATA_DIR / 'info',
DATA_DIR / 'problems']
LOGGING = get_logger_config(TEST_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': PROJECT_ROOT / "db" / "mitx.db",
}
}
CACHES = {
# This is the cache used for most things. Askbot will not work without a
# functioning cache -- it relies on caching to load its settings in places.
# In staging/prod envs, the sessions also live here.
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'mitx_loc_mem_cache',
'KEY_FUNCTION': 'util.memcache.safe_key',
},
# The general cache is what you get if you use our util.cache. It's used for
# things like caching the course.xml file for different A/B test groups.
# We set it to be a DummyCache to force reloading of course.xml in dev.
# In staging environments, we would grab VERSION from data uploaded by the
# push process.
'general': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
'KEY_FUNCTION': 'util.memcache.safe_key',
}
}
# Dummy secret key for dev
SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
############################ FILE UPLOADS (ASKBOT) #############################
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = PROJECT_ROOT / "uploads"
MEDIA_URL = "/static/uploads/"
STATICFILES_DIRS.append(("uploads", MEDIA_ROOT))
FILE_UPLOAD_TEMP_DIR = PROJECT_ROOT / "uploads"
FILE_UPLOAD_HANDLERS = (
'django.core.files.uploadhandler.MemoryFileUploadHandler',
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
)
from check import *
#!/usr/bin/python
from random import choice
import string
import traceback
from django.conf import settings
import courseware.capa.capa_problem as lcp
from dogfood.views import update_problem
def GenID(length=8, chars=string.letters + string.digits):
return ''.join([choice(chars) for i in range(length)])
randomid = GenID()
def check_problem_code(ans,the_lcp,correct_answers,false_answers):
"""
ans = student's answer
the_lcp = LoncapaProblem instance
returns dict {'ok':is_ok,'msg': message with iframe}
"""
pfn = "dog%s" % randomid
pfn += the_lcp.problem_id.replace('filename','') # add problem ID to dogfood problem name
update_problem(pfn,ans,filestore=the_lcp.system.filestore)
msg = '<hr width="100%"/>'
msg += '<iframe src="%s/dogfood/filename%s" width="95%%" frameborder="1">No iframe support!</iframe>' % (settings.MITX_ROOT_URL,pfn)
msg += '<hr width="100%"/>'
endmsg = """<p><font size="-1" color="purple">Note: if the code text box disappears after clicking on "Check",
please type something in the box to make it refresh properly. This is a
bug with Chrome; it does not happen with Firefox. It is being fixed.
</font></p>"""
is_ok = True
if (not correct_answers) or (not false_answers):
ret = {'ok':is_ok,
'msg': msg+endmsg,
}
return ret
try:
# check correctness
fp = the_lcp.system.filestore.open('problems/%s.xml' % pfn)
test_lcp = lcp.LoncapaProblem(fp, '1', system=the_lcp.system)
if not (test_lcp.grade_answers(correct_answers)['1_2_1']=='correct'):
is_ok = False
if (test_lcp.grade_answers(false_answers)['1_2_1']=='correct'):
is_ok = False
except Exception,err:
is_ok = False
msg += "<p>Error: %s</p>" % str(err).replace('<','&#60;')
msg += "<p><pre>%s</pre></p>" % traceback.format_exc().replace('<','&#60;')
ret = {'ok':is_ok,
'msg': msg+endmsg,
}
return ret
'''
dogfood.py
For using mitx / edX / i4x in checking itself.
df_capa_problem: accepts an XML file for a problem, and renders it.
'''
import logging
import datetime
import re
import os # FIXME - use OSFS instead
from fs.osfs import OSFS
from django.conf import settings
from django.contrib.auth.models import User
from django.core.context_processors import csrf
from django.core.mail import send_mail
from django.http import Http404
from django.http import HttpResponse
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
import courseware.capa.calc
import track.views
from lxml import etree
from courseware.module_render import make_track_function, I4xSystem
from courseware.models import StudentModule
from multicourse import multicourse_settings
from student.models import UserProfile
from util.cache import cache
from util.views import accepts
import courseware.content_parser as content_parser
import courseware.modules
log = logging.getLogger("mitx.courseware")
etree.set_default_parser(etree.XMLParser(dtd_validation=False, load_dtd=False,
remove_comments = True))
DOGFOOD_COURSENAME = 'edx_dogfood' # FIXME - should not be here; maybe in settings
def update_problem(pfn,pxml,coursename=None,overwrite=True,filestore=None):
'''
update problem with filename pfn, and content (xml) pxml.
'''
if not filestore:
if not coursename: coursename = DOGFOOD_COURSENAME
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
pfn2 = settings.DATA_DIR + xp + 'problems/%s.xml' % pfn
fp = open(pfn2,'w')
else:
pfn2 = 'problems/%s.xml' % pfn
fp = filestore.open(pfn2,'w')
if os.path.exists(pfn2) and not overwrite: return # don't overwrite if already exists and overwrite=False
pxmls = pxml if type(pxml) in [str,unicode] else etree.tostring(pxml,pretty_print=True)
fp.write(pxmls)
fp.close()
def df_capa_problem(request, id=None):
'''
dogfood capa problem.
Accepts XML for a problem, inserts it into the dogfood course.xml.
Returns rendered problem.
'''
# "WARNING: UNDEPLOYABLE CODE. FOR DEV USE ONLY."
if settings.DEBUG:
print '[lib.dogfood.df_capa_problem] id=%s' % id
if not 'coursename' in request.session:
coursename = DOGFOOD_COURSENAME
else:
coursename = request.session['coursename']
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
# Grab the XML corresponding to the request from course.xml
module = 'problem'
try:
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
except Exception,err:
print "[lib.dogfood.df_capa_problem] error in calling content_parser: %s" % err
xml = None
# if problem of given ID does not exist, then create it
# do this only if course.xml has a section named "DogfoodProblems"
if not xml:
m = re.match('filename([A-Za-z0-9_]+)$',id) # extract problem filename from ID given
if not m:
raise Exception,'[lib.dogfood.df_capa_problem] Illegal problem id %s' % id
pfn = m.group(1)
print '[lib.dogfood.df_capa_problem] creating new problem pfn=%s' % pfn
# add problem to course.xml
fn = settings.DATA_DIR + xp + 'course.xml'
xml = etree.parse(fn)
seq = xml.find('chapter/section[@name="DogfoodProblems"]/sequential') # assumes simplistic course.xml structure!
if seq==None:
raise Exception,"[lib.dogfood.views.df_capa_problem] missing DogfoodProblems section in course.xml!"
newprob = etree.Element('problem')
newprob.set('type','lecture')
newprob.set('showanswer','attempted')
newprob.set('rerandomize','never')
newprob.set('title',pfn)
newprob.set('filename',pfn)
newprob.set('name',pfn)
seq.append(newprob)
fp = open(fn,'w')
fp.write(etree.tostring(xml,pretty_print=True)) # write new XML
fp.close()
# now create new problem file
# update_problem(pfn,'<problem>\n<text>\nThis is a new problem\n</text>\n</problem>\n',coursename,overwrite=False)
# reset cache entry
user = request.user
groups = content_parser.user_groups(user)
options = {'dev_content':settings.DEV_CONTENT,
'groups' : groups}
filename = xp + 'course.xml'
cache_key = filename + "_processed?dev_content:" + str(options['dev_content']) + "&groups:" + str(sorted(groups))
print '[lib.dogfood.df_capa_problem] cache_key = %s' % cache_key
#cache.delete(cache_key)
tree = content_parser.course_xml_process(xml) # add ID tags
cache.set(cache_key,etree.tostring(tree),60)
# settings.DEFAULT_GROUPS.append('dev') # force content_parser.course_file to not use cache
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
if not xml:
print "[lib.dogfood.df_capa_problem] problem xml not found!"
# add problem ID to list so that is_staff check can be bypassed
request.session['dogfood_id'] = id
# hand over to quickedit to do the rest
return quickedit(request,id=id,qetemplate='dogfood.html',coursename=coursename)
def quickedit(request, id=None, qetemplate='quickedit.html',coursename=None):
'''
quick-edit capa problem.
Maybe this should be moved into capa/views.py
Or this should take a "module" argument, and the quickedit moved into capa_module.
id is passed in from url resolution
qetemplate is used by dogfood.views.dj_capa_problem, to override normal template
'''
print "WARNING: UNDEPLOYABLE CODE. FOR DEV USE ONLY."
print "In deployed use, this will only edit on one server"
print "We need a setting to disable for production where there is"
print "a load balanacer"
if not request.user.is_staff:
if not ('dogfood_id' in request.session and request.session['dogfood_id']==id):
return redirect('/')
if id=='course.xml':
return quickedit_git_reload(request)
# get coursename if stored
if not coursename:
coursename = multicourse_settings.get_coursename_from_request(request)
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
def get_lcp(coursename,id):
# Grab the XML corresponding to the request from course.xml
module = 'problem'
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
# Create the module (instance of capa_module.Module)
system = I4xSystem(track_function = make_track_function(request),
render_function = None,
ajax_url = ajax_url,
filestore = OSFS(settings.DATA_DIR + xp),
#role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this
)
instance=courseware.modules.get_module_class(module)(system,
xml,
id,
state=None)
log.info('ajax_url = ' + instance.ajax_url)
# create empty student state for this problem, if not previously existing
s = StudentModule.objects.filter(student=request.user,
module_id=id)
if len(s) == 0 or s is None:
smod=StudentModule(student=request.user,
module_type = 'problem',
module_id=id,
state=instance.get_state())
smod.save()
lcp = instance.lcp
pxml = lcp.tree
pxmls = etree.tostring(pxml,pretty_print=True)
return instance, pxmls
instance, pxmls = get_lcp(coursename,id)
# if there was a POST, then process it
msg = ''
if 'qesubmit' in request.POST:
action = request.POST['qesubmit']
if "Revert" in action:
msg = "Reverted to original"
elif action=='Change Problem':
key = 'quickedit_%s' % id
if not key in request.POST:
msg = "oops, missing code key=%s" % key
else:
newcode = request.POST[key]
# see if code changed
if str(newcode)==str(pxmls) or '<?xml version="1.0"?>\n'+str(newcode)==str(pxmls):
msg = "No changes"
else:
# check new code
isok = False
try:
newxml = etree.fromstring(newcode)
isok = True
except Exception,err:
msg = "Failed to change problem: XML error \"<font color=red>%s</font>\"" % err
if isok:
filename = instance.lcp.fileobject.name
fp = open(filename,'w') # TODO - replace with filestore call?
fp.write(newcode)
fp.close()
msg = "<font color=green>Problem changed!</font> (<tt>%s</tt>)" % filename
instance, pxmls = get_lcp(coursename,id)
lcp = instance.lcp
# get the rendered problem HTML
phtml = instance.get_html()
# phtml = instance.get_problem_html()
# init_js = instance.get_init_js()
# destory_js = instance.get_destroy_js()
context = {'id':id,
'msg' : msg,
'lcp' : lcp,
'filename' : lcp.fileobject.name,
'pxmls' : pxmls,
'phtml' : phtml,
"destroy_js":'',
'init_js':'',
'csrf':csrf(request)['csrf_token'],
}
result = render_to_response(qetemplate, context)
return result
def quickedit_git_reload(request):
'''
reload course.xml and all courseware files for this course, from the git repo.
assumes the git repo has already been setup.
staff only.
'''
if not request.user.is_staff:
return redirect('/')
# get coursename if stored
coursename = multicourse_settings.get_coursename_from_request(request)
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
msg = ""
if 'cancel' in request.POST:
return redirect("/courseware")
if 'gitupdate' in request.POST:
import os # FIXME - put at top?
cmd = "cd ../data%s; git reset --hard HEAD; git pull origin %s" % (xp,xp.replace('/',''))
msg += '<p>cmd: %s</p>' % cmd
ret = os.popen(cmd).read()
msg += '<p><pre>%s</pre></p>' % ret.replace('<','&lt;')
msg += "<p>git update done!</p>"
context = {'id':id,
'msg' : msg,
'coursename' : coursename,
'csrf':csrf(request)['csrf_token'],
}
result = render_to_response("gitupdate.html", context)
return result
from formula import *
from symmath_check import *
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# File: formula.py # File: formula.py
# Date: 04-May-12 # Date: 04-May-12 (creation)
# Author: I. Chuang <ichuang@mit.edu> # Author: I. Chuang <ichuang@mit.edu>
# #
# flexible python representation of a symbolic mathematical formula. # flexible python representation of a symbolic mathematical formula.
...@@ -30,7 +30,7 @@ from lxml import etree ...@@ -30,7 +30,7 @@ from lxml import etree
import requests import requests
from copy import deepcopy from copy import deepcopy
print "[lib.sympy_check.formula] Warning: Dark code. Needs review before enabling in prod." print "[lib.symmath.formula] Warning: Dark code. Needs review before enabling in prod."
os.environ['PYTHONIOENCODING'] = 'utf-8' os.environ['PYTHONIOENCODING'] = 'utf-8'
...@@ -143,11 +143,12 @@ class formula(object): ...@@ -143,11 +143,12 @@ class formula(object):
Representation of a mathematical formula object. Accepts mathml math expression for constructing, Representation of a mathematical formula object. Accepts mathml math expression for constructing,
and can produce sympy translation. The formula may or may not include an assignment (=). and can produce sympy translation. The formula may or may not include an assignment (=).
''' '''
def __init__(self,expr,asciimath=''): def __init__(self,expr,asciimath='',options=None):
self.expr = expr.strip() self.expr = expr.strip()
self.asciimath = asciimath self.asciimath = asciimath
self.the_cmathml = None self.the_cmathml = None
self.the_sympy = None self.the_sympy = None
self.options = options
def is_presentation_mathml(self): def is_presentation_mathml(self):
return '<mstyle' in self.expr return '<mstyle' in self.expr
...@@ -234,7 +235,10 @@ class formula(object): ...@@ -234,7 +235,10 @@ class formula(object):
if self.the_cmathml: return self.the_cmathml if self.the_cmathml: return self.the_cmathml
# pre-process the presentation mathml before sending it to snuggletex to convert to content mathml # pre-process the presentation mathml before sending it to snuggletex to convert to content mathml
xml = self.preprocess_pmathml(self.expr) try:
xml = self.preprocess_pmathml(self.expr)
except Exception,err:
return "<html>Error! Cannot process pmathml</html>"
pmathml = etree.tostring(xml,pretty_print=True) pmathml = etree.tostring(xml,pretty_print=True)
self.the_pmathml = pmathml self.the_pmathml = pmathml
...@@ -246,7 +250,8 @@ class formula(object): ...@@ -246,7 +250,8 @@ class formula(object):
def make_sympy(self,xml=None): def make_sympy(self,xml=None):
''' '''
Return sympy expression for the math formula Return sympy expression for the math formula.
The math formula is converted to Content MathML then that is parsed.
''' '''
if self.the_sympy: return self.the_sympy if self.the_sympy: return self.the_sympy
...@@ -255,7 +260,11 @@ class formula(object): ...@@ -255,7 +260,11 @@ class formula(object):
if not self.is_mathml(): if not self.is_mathml():
return my_sympify(self.expr) return my_sympify(self.expr)
if self.is_presentation_mathml(): if self.is_presentation_mathml():
xml = etree.fromstring(str(self.cmathml)) try:
cmml = self.cmathml
xml = etree.fromstring(str(cmml))
except Exception,err:
raise Exception,'Err %s while converting cmathml to xml; cmml=%s' % (err,cmml)
xml = self.fix_greek_in_mathml(xml) xml = self.fix_greek_in_mathml(xml)
self.the_sympy = self.make_sympy(xml[0]) self.the_sympy = self.make_sympy(xml[0])
else: else:
...@@ -274,7 +283,7 @@ class formula(object): ...@@ -274,7 +283,7 @@ class formula(object):
# print "divide: arg0=%s, arg1=%s" % (args[0],args[1]) # print "divide: arg0=%s, arg1=%s" % (args[0],args[1])
return sympy.Mul(args[0],sympy.Pow(args[1],-1)) return sympy.Mul(args[0],sympy.Pow(args[1],-1))
def op_plus(*args): return sum(args) def op_plus(*args): return args[0] if len(args)==1 else op_plus(*args[:-1])+args[-1]
def op_times(*args): return reduce(operator.mul,args) def op_times(*args): return reduce(operator.mul,args)
def op_minus(*args): def op_minus(*args):
...@@ -314,9 +323,9 @@ class formula(object): ...@@ -314,9 +323,9 @@ class formula(object):
elif tag=='msup': return '^'.join([parsePresentationMathMLSymbol(y) for y in xml]) elif tag=='msup': return '^'.join([parsePresentationMathMLSymbol(y) for y in xml])
raise Exception,'[parsePresentationMathMLSymbol] unknown tag %s' % tag raise Exception,'[parsePresentationMathMLSymbol] unknown tag %s' % tag
# parser tree for content MathML # parser tree for Content MathML
tag = gettag(xml) tag = gettag(xml)
print "tag = ",tag # print "tag = ",tag
# first do compound objects # first do compound objects
...@@ -325,7 +334,13 @@ class formula(object): ...@@ -325,7 +334,13 @@ class formula(object):
if opstr in opdict: if opstr in opdict:
op = opdict[opstr] op = opdict[opstr]
args = [ self.make_sympy(x) for x in xml[1:]] args = [ self.make_sympy(x) for x in xml[1:]]
return op(*args) try:
res = op(*args)
except Exception,err:
self.args = args
self.op = op
raise Exception,'[formula] error=%s failed to apply %s to args=%s' % (err,opstr,args)
return res
else: else:
raise Exception,'[formula]: unknown operator tag %s' % (opstr) raise Exception,'[formula]: unknown operator tag %s' % (opstr)
...@@ -348,7 +363,7 @@ class formula(object): ...@@ -348,7 +363,7 @@ class formula(object):
return float(xml.text) return float(xml.text)
elif tag=='ci': # variable (symbol) elif tag=='ci': # variable (symbol)
if len(xml)>0 and (gettag(xml[0])=='msub' or gettag(xml[0])=='msup'): if len(xml)>0 and (gettag(xml[0])=='msub' or gettag(xml[0])=='msup'): # subscript or superscript
usym = parsePresentationMathMLSymbol(xml[0]) usym = parsePresentationMathMLSymbol(xml[0])
sym = sympy.Symbol(str(usym)) sym = sympy.Symbol(str(usym))
else: else:
...@@ -356,7 +371,11 @@ class formula(object): ...@@ -356,7 +371,11 @@ class formula(object):
if 'hat' in usym: if 'hat' in usym:
sym = my_sympify(usym) sym = my_sympify(usym)
else: else:
sym = sympy.Symbol(str(usym)) if usym=='i': print "options=",self.options
if usym=='i' and 'imaginary' in self.options: # i = sqrt(-1)
sym = sympy.I
else:
sym = sympy.Symbol(str(usym))
return sym return sym
else: # unknown tag else: # unknown tag
...@@ -459,3 +478,78 @@ def test4(): ...@@ -459,3 +478,78 @@ def test4():
</math> </math>
''' '''
return formula(xmlstr) return formula(xmlstr)
def test5(): # sum of two matrices
xmlstr = u'''
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mrow>
<mi>cos</mi>
<mrow>
<mo>(</mo>
<mi>&#x3B8;</mi>
<mo>)</mo>
</mrow>
</mrow>
<mo>&#x22C5;</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
<mo>+</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
</mstyle>
</math>
'''
return formula(xmlstr)
def test6(): # imaginary numbers
xmlstr = u'''
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mn>1</mn>
<mo>+</mo>
<mi>i</mi>
</mstyle>
</math>
'''
return formula(xmlstr,options='imaginaryi')
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# File: sympy_check2.py # File: symmath_check.py
# Date: 02-May-12 # Date: 02-May-12 (creation)
# Author: I. Chuang <ichuang@mit.edu>
# #
# Use sympy to check for expression equality # Symbolic mathematical expression checker for edX. Uses sympy to check for expression equality.
# #
# Takes in math expressions given as Presentation MathML (from ASCIIMathML), converts to Content MathML using SnuggleTeX # Takes in math expressions given as Presentation MathML (from ASCIIMathML), converts to Content MathML using SnuggleTeX
...@@ -15,8 +14,14 @@ from formula import * ...@@ -15,8 +14,14 @@ from formula import *
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# check function interface # check function interface
#
# This is one of the main entry points to call.
def sympy_check(expect,ans,adict={},symtab=None,extra_options=None): def symmath_check_simple(expect,ans,adict={},symtab=None,extra_options=None):
'''
Check a symbolic mathematical expression using sympy.
The input is an ascii string (not MathML) converted to math using sympy.sympify.
'''
options = {'__MATRIX__':False,'__ABC__':False,'__LOWER__':False} options = {'__MATRIX__':False,'__ABC__':False,'__LOWER__':False}
if extra_options: options.update(extra_options) if extra_options: options.update(extra_options)
...@@ -129,30 +134,41 @@ def check(expect,given,numerical=False,matrix=False,normphase=False,abcsym=False ...@@ -129,30 +134,41 @@ def check(expect,given,numerical=False,matrix=False,normphase=False,abcsym=False
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# Check function interface, which takes pmathml input # Check function interface, which takes pmathml input
#
# This is one of the main entry points to call.
def sympy_check2(expect,ans,adict={},abname=''): def symmath_check(expect,ans,dynamath=None,options=None,debug=None):
'''
Check a symbolic mathematical expression using sympy.
The input may be presentation MathML. Uses formula.
'''
msg = '' msg = ''
# msg += '<p/>abname=%s' % abname # msg += '<p/>abname=%s' % abname
# msg += '<p/>adict=%s' % (repr(adict).replace('<','&lt;')) # msg += '<p/>adict=%s' % (repr(adict).replace('<','&lt;'))
threshold = 1.0e-3 threshold = 1.0e-3
DEBUG = True DEBUG = debug
# options
do_matrix = 'matrix' in (options or '')
do_qubit = 'qubit' in (options or '')
do_imaginary = 'imaginary' in (options or '')
# parse expected answer # parse expected answer
try: try:
fexpect = my_sympify(str(expect)) fexpect = my_sympify(str(expect),matrix=do_matrix,do_qubit=do_qubit)
except Exception,err: except Exception,err:
msg += '<p>Error %s in parsing OUR expected answer "%s"</p>' % (err,expect) msg += '<p>Error %s in parsing OUR expected answer "%s"</p>' % (err,expect)
return {'ok':False,'msg':msg} return {'ok':False,'msg':msg}
# if expected answer is a number, try parsing provided answer as a number also # if expected answer is a number, try parsing provided answer as a number also
try: try:
fans = my_sympify(str(ans)) fans = my_sympify(str(ans),matrix=do_matrix,do_qubit=do_qubit)
except Exception,err: except Exception,err:
fans = None fans = None
if fexpect.is_number and fans and fans.is_number: if hasattr(fexpect,'is_number') and fexpect.is_number and fans and hasattr(fans,'is_number') and fans.is_number:
if abs(abs(fans-fexpect)/fexpect)<threshold: if abs(abs(fans-fexpect)/fexpect)<threshold:
return {'ok':True,'msg':msg} return {'ok':True,'msg':msg}
else: else:
...@@ -164,10 +180,14 @@ def sympy_check2(expect,ans,adict={},abname=''): ...@@ -164,10 +180,14 @@ def sympy_check2(expect,ans,adict={},abname=''):
return {'ok':True,'msg':msg} return {'ok':True,'msg':msg}
# convert mathml answer to formula # convert mathml answer to formula
mmlbox = abname+'_fromjs' try:
if mmlbox in adict: if dynamath:
mmlans = adict[mmlbox] mmlans = dynamath[0]
f = formula(mmlans) except Exception,err:
mmlans = None
if not mmlans:
return {'ok':False,'msg':'[symmath_check] failed to get MathML for input; dynamath=%s' % dynamath}
f = formula(mmlans,options=options)
# get sympy representation of the formula # get sympy representation of the formula
# if DEBUG: msg += '<p/> mmlans=%s' % repr(mmlans).replace('<','&lt;') # if DEBUG: msg += '<p/> mmlans=%s' % repr(mmlans).replace('<','&lt;')
...@@ -175,13 +195,20 @@ def sympy_check2(expect,ans,adict={},abname=''): ...@@ -175,13 +195,20 @@ def sympy_check2(expect,ans,adict={},abname=''):
fsym = f.sympy fsym = f.sympy
msg += '<p>You entered: %s</p>' % to_latex(f.sympy) msg += '<p>You entered: %s</p>' % to_latex(f.sympy)
except Exception,err: except Exception,err:
msg += "<p>Error %s in converting to sympy</p>" % str(err).replace('<','&lt;') msg += "<p>Error %s in evaluating your expression '%s' as a valid equation</p>" % (str(err).replace('<','&lt;'),
if DEBUG: msg += "<p><pre>%s</pre></p>" % traceback.format_exc() ans)
if DEBUG:
msg += '<hr>'
msg += '<p><font color="blue">DEBUG messages:</p>'
msg += "<p><pre>%s</pre></p>" % traceback.format_exc()
msg += '<p>cmathml=<pre>%s</pre></p>' % f.cmathml.replace('<','&lt;')
msg += '<p>pmathml=<pre>%s</pre></p>' % mmlans.replace('<','&lt;')
msg += '<hr>'
return {'ok':False,'msg':msg} return {'ok':False,'msg':msg}
# compare with expected # compare with expected
if fexpect.is_number: if hasattr(fexpect,'is_number') and fexpect.is_number:
if fsym.is_number: if hasattr(fsym,'is_number') and fsym.is_number:
if abs(abs(fsym-fexpect)/fexpect)<threshold: if abs(abs(fsym-fexpect)/fexpect)<threshold:
return {'ok':True,'msg':msg} return {'ok':True,'msg':msg}
return {'ok':False,'msg':msg} return {'ok':False,'msg':msg}
...@@ -214,15 +241,21 @@ def sympy_check2(expect,ans,adict={},abname=''): ...@@ -214,15 +241,21 @@ def sympy_check2(expect,ans,adict={},abname=''):
diff = None diff = None
if DEBUG: if DEBUG:
msg += '<hr>'
msg += '<p><font color="blue">DEBUG messages:</p>'
msg += "<p>Got: %s</p>" % repr(fsym) msg += "<p>Got: %s</p>" % repr(fsym)
# msg += "<p/>Got: %s" % str([type(x) for x in fsym.atoms()]).replace('<','&lt;') # msg += "<p/>Got: %s" % str([type(x) for x in fsym.atoms()]).replace('<','&lt;')
msg += "<p>Expecting: %s</p>" % repr(fexpect).replace('**','^').replace('hat(I)','hat(i)') msg += "<p>Expecting: %s</p>" % repr(fexpect).replace('**','^').replace('hat(I)','hat(i)')
# msg += "<p/>Expecting: %s" % str([type(x) for x in fexpect.atoms()]).replace('<','&lt;') # msg += "<p/>Expecting: %s" % str([type(x) for x in fexpect.atoms()]).replace('<','&lt;')
if diff: if diff:
msg += "<p>Difference: %s</p>" % to_latex(diff) msg += "<p>Difference: %s</p>" % to_latex(diff)
msg += '<hr>'
return {'ok':False,'msg':msg,'ex':fexpect,'got':fsym} return {'ok':False,'msg':msg,'ex':fexpect,'got':fsym}
#-----------------------------------------------------------------------------
# tests
def sctest1(): def sctest1():
x = "1/2*(1+(k_e* Q* q)/(m *g *h^2))" x = "1/2*(1+(k_e* Q* q)/(m *g *h^2))"
y = ''' y = '''
......
/*
DJANGO Admin styles
*/
body {
margin: 0;
padding: 0;
font-size: 12px;
font-family: "Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
color: #333;
background: #fff;
}
/* LINKS */
a:link, a:visited {
color: #5b80b2;
text-decoration: none;
}
a:hover {
color: #036;
}
a img {
border: none;
}
a.section:link, a.section:visited {
color: white;
text-decoration: none;
}
/* GLOBAL DEFAULTS */
p, ol, ul, dl {
margin: .2em 0 .8em 0;
}
p {
padding: 0;
line-height: 140%;
}
h1,h2,h3,h4,h5 {
font-weight: bold;
}
h1 {
font-size: 18px;
color: #666;
padding: 0 6px 0 0;
margin: 0 0 .2em 0;
}
h2 {
font-size: 16px;
margin: 1em 0 .5em 0;
}
h2.subhead {
font-weight: normal;
margin-top: 0;
}
h3 {
font-size: 14px;
margin: .8em 0 .3em 0;
color: #666;
font-weight: bold;
}
h4 {
font-size: 12px;
margin: 1em 0 .8em 0;
padding-bottom: 3px;
}
h5 {
font-size: 10px;
margin: 1.5em 0 .5em 0;
color: #666;
text-transform: uppercase;
letter-spacing: 1px;
}
ul li {
list-style-type: square;
padding: 1px 0;
}
ul.plainlist {
margin-left: 0 !important;
}
ul.plainlist li {
list-style-type: none;
}
li ul {
margin-bottom: 0;
}
li, dt, dd {
font-size: 11px;
line-height: 14px;
}
dt {
font-weight: bold;
margin-top: 4px;
}
dd {
margin-left: 0;
}
form {
margin: 0;
padding: 0;
}
fieldset {
margin: 0;
padding: 0;
}
blockquote {
font-size: 11px;
color: #777;
margin-left: 2px;
padding-left: 10px;
border-left: 5px solid #ddd;
}
code, pre {
font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
background: inherit;
color: #666;
font-size: 11px;
}
pre.literal-block {
margin: 10px;
background: #eee;
padding: 6px 8px;
}
code strong {
color: #930;
}
hr {
clear: both;
color: #eee;
background-color: #eee;
height: 1px;
border: none;
margin: 0;
padding: 0;
font-size: 1px;
line-height: 1px;
}
/* TEXT STYLES & MODIFIERS */
.small {
font-size: 11px;
}
.tiny {
font-size: 10px;
}
p.tiny {
margin-top: -2px;
}
.mini {
font-size: 9px;
}
p.mini {
margin-top: -3px;
}
.help, p.help {
font-size: 10px !important;
color: #999;
}
p img, h1 img, h2 img, h3 img, h4 img, td img {
vertical-align: middle;
}
.quiet, a.quiet:link, a.quiet:visited {
color: #999 !important;
font-weight: normal !important;
}
.quiet strong {
font-weight: bold !important;
}
.float-right {
float: right;
}
.float-left {
float: left;
}
.clear {
clear: both;
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.example {
margin: 10px 0;
padding: 5px 10px;
background: #efefef;
}
.nowrap {
white-space: nowrap;
}
/* TABLES */
table {
border-collapse: collapse;
border-color: #ccc;
}
td, th {
font-size: 11px;
line-height: 13px;
border-bottom: 1px solid #eee;
vertical-align: top;
padding: 5px;
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
}
th {
text-align: left;
font-size: 12px;
font-weight: bold;
}
thead th,
tfoot td {
color: #666;
padding: 2px 5px;
font-size: 11px;
background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;
border-left: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
tfoot td {
border-bottom: none;
border-top: 1px solid #ddd;
}
thead th:first-child,
tfoot td:first-child {
border-left: none !important;
}
thead th.optional {
font-weight: normal !important;
}
fieldset table {
border-right: 1px solid #eee;
}
tr.row-label td {
font-size: 9px;
padding-top: 2px;
padding-bottom: 0;
border-bottom: none;
color: #666;
margin-top: -1px;
}
tr.alt {
background: #f6f6f6;
}
.row1 {
background: #EDF3FE;
}
.row2 {
background: white;
}
/* SORTABLE TABLES */
thead th a:link, thead th a:visited {
color: #666;
display: block;
}
table thead th.sorted {
background-position: bottom left !important;
}
table thead th.sorted a {
padding-right: 13px;
}
table thead th.ascending a {
background: url(../img/admin/arrow-up.gif) right .4em no-repeat;
}
table thead th.descending a {
background: url(../img/admin/arrow-down.gif) right .4em no-repeat;
}
/* ORDERABLE TABLES */
table.orderable tbody tr td:hover {
cursor: move;
}
table.orderable tbody tr td:first-child {
padding-left: 14px;
background-image: url(../img/admin/nav-bg-grabber.gif);
background-repeat: repeat-y;
}
table.orderable-initalized .order-cell, body>tr>td.order-cell {
display: none;
}
/* FORM DEFAULTS */
input, textarea, select, .form-row p {
margin: 2px 0;
padding: 2px 3px;
vertical-align: middle;
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
font-weight: normal;
font-size: 11px;
}
textarea {
vertical-align: top !important;
}
input[type=text], input[type=password], textarea, select, .vTextField {
border: 1px solid #ccc;
}
/* FORM BUTTONS */
.button, input[type=submit], input[type=button], .submit-row input {
background: white url(../img/admin/nav-bg.gif) bottom repeat-x;
padding: 3px 5px;
color: black;
border: 1px solid #bbb;
border-color: #ddd #aaa #aaa #ddd;
}
.button:active, input[type=submit]:active, input[type=button]:active {
background-image: url(../img/admin/nav-bg-reverse.gif);
background-position: top;
}
.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
background-image: url(../img/admin/nav-bg.gif);
background-position: bottom;
opacity: 0.4;
}
.button.default, input[type=submit].default, .submit-row input.default {
border: 2px solid #5b80b2;
background: #7CA0C7 url(../img/admin/default-bg.gif) bottom repeat-x;
font-weight: bold;
color: white;
float: right;
}
.button.default:active, input[type=submit].default:active {
background-image: url(../img/admin/default-bg-reverse.gif);
background-position: top;
}
.button[disabled].default, input[type=submit][disabled].default, input[type=button][disabled].default {
background-image: url(../img/admin/default-bg.gif);
background-position: bottom;
opacity: 0.4;
}
/* MODULES */
.module {
border: 1px solid #ccc;
margin-bottom: 5px;
background: white;
}
.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
padding-left: 10px;
padding-right: 10px;
}
.module blockquote {
margin-left: 12px;
}
.module ul, .module ol {
margin-left: 1.5em;
}
.module h3 {
margin-top: .6em;
}
.module h2, .module caption, .inline-group h2 {
margin: 0;
padding: 2px 5px 3px 5px;
font-size: 11px;
text-align: left;
font-weight: bold;
background: #7CA0C7 url(../img/admin/default-bg.gif) top left repeat-x;
color: white;
}
.module table {
border-collapse: collapse;
}
/* MESSAGES & ERRORS */
ul.messagelist {
padding: 0 0 5px 0;
margin: 0;
}
ul.messagelist li {
font-size: 12px;
display: block;
padding: 4px 5px 4px 25px;
margin: 0 0 3px 0;
border-bottom: 1px solid #ddd;
color: #666;
background: #ffc url(../img/admin/icon_success.gif) 5px .3em no-repeat;
}
ul.messagelist li.warning{
background-image: url(../img/admin/icon_alert.gif);
}
ul.messagelist li.error{
background-image: url(../img/admin/icon_error.gif);
}
.errornote {
font-size: 12px !important;
display: block;
padding: 4px 5px 4px 25px;
margin: 0 0 3px 0;
border: 1px solid red;
color: red;
background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat;
}
ul.errorlist {
margin: 0 !important;
padding: 0 !important;
}
.errorlist li {
font-size: 12px !important;
display: block;
padding: 4px 5px 4px 25px;
margin: 0 0 3px 0;
border: 1px solid red;
color: white;
background: red url(../img/admin/icon_alert.gif) 5px .3em no-repeat;
}
.errorlist li a {
color: white;
text-decoration: underline;
}
td ul.errorlist {
margin: 0 !important;
padding: 0 !important;
}
td ul.errorlist li {
margin: 0 !important;
}
.errors {
background: #ffc;
}
.errors input, .errors select, .errors textarea {
border: 1px solid red;
}
div.system-message {
background: #ffc;
margin: 10px;
padding: 6px 8px;
font-size: .8em;
}
div.system-message p.system-message-title {
padding: 4px 5px 4px 25px;
margin: 0;
color: red;
background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat;
}
.description {
font-size: 12px;
padding: 5px 0 0 12px;
}
/* BREADCRUMBS */
div.breadcrumbs {
background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
padding: 2px 8px 3px 8px;
font-size: 11px;
color: #999;
border-top: 1px solid white;
border-bottom: 1px solid #ccc;
text-align: left;
}
/* ACTION ICONS */
.addlink {
padding-left: 12px;
background: url(../img/admin/icon_addlink.gif) 0 .2em no-repeat;
}
.changelink {
padding-left: 12px;
background: url(../img/admin/icon_changelink.gif) 0 .2em no-repeat;
}
.deletelink {
padding-left: 12px;
background: url(../img/admin/icon_deletelink.gif) 0 .25em no-repeat;
}
a.deletelink:link, a.deletelink:visited {
color: #CC3434;
}
a.deletelink:hover {
color: #993333;
}
/* OBJECT TOOLS */
.object-tools {
font-size: 10px;
font-weight: bold;
font-family: Arial,Helvetica,sans-serif;
padding-left: 0;
float: right;
position: relative;
margin-top: -2.4em;
margin-bottom: -2em;
}
.form-row .object-tools {
margin-top: 5px;
margin-bottom: 5px;
float: none;
height: 2em;
padding-left: 3.5em;
}
.object-tools li {
display: block;
float: left;
background: url(../img/admin/tool-left.gif) 0 0 no-repeat;
padding: 0 0 0 8px;
margin-left: 2px;
height: 16px;
}
.object-tools li:hover {
background: url(../img/admin/tool-left_over.gif) 0 0 no-repeat;
}
.object-tools a:link, .object-tools a:visited {
display: block;
float: left;
color: white;
padding: .1em 14px .1em 8px;
height: 14px;
background: #999 url(../img/admin/tool-right.gif) 100% 0 no-repeat;
}
.object-tools a:hover, .object-tools li:hover a {
background: #5b80b2 url(../img/admin/tool-right_over.gif) 100% 0 no-repeat;
}
.object-tools a.viewsitelink, .object-tools a.golink {
background: #999 url(../img/admin/tooltag-arrowright.gif) top right no-repeat;
padding-right: 28px;
}
.object-tools a.viewsitelink:hover, .object-tools a.golink:hover {
background: #5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat;
}
.object-tools a.addlink {
background: #999 url(../img/admin/tooltag-add.gif) top right no-repeat;
padding-right: 28px;
}
.object-tools a.addlink:hover {
background: #5b80b2 url(../img/admin/tooltag-add_over.gif) top right no-repeat;
}
/* OBJECT HISTORY */
table#change-history {
width: 100%;
}
table#change-history tbody th {
width: 16em;
}
/* PAGE STRUCTURE */
#container {
position: relative;
width: 100%;
min-width: 760px;
padding: 0;
}
#content {
margin: 10px 15px;
}
#header {
width: 100%;
}
#content-main {
float: left;
width: 100%;
}
#content-related {
float: right;
width: 18em;
position: relative;
margin-right: -19em;
}
#footer {
clear: both;
padding: 10px;
}
/* COLUMN TYPES */
.colMS {
margin-right: 20em !important;
}
.colSM {
margin-left: 20em !important;
}
.colSM #content-related {
float: left;
margin-right: 0;
margin-left: -19em;
}
.colSM #content-main {
float: right;
}
.popup .colM {
width: 95%;
}
.subcol {
float: left;
width: 46%;
margin-right: 15px;
}
.dashboard #content {
width: 500px;
}
/* HEADER */
#header {
background: #417690;
color: #ffc;
overflow: hidden;
}
#header a:link, #header a:visited {
color: white;
}
#header a:hover {
text-decoration: underline;
}
#branding h1 {
padding: 0 10px;
font-size: 18px;
margin: 8px 0;
font-weight: normal;
color: #f4f379;
}
#branding h2 {
padding: 0 10px;
font-size: 14px;
margin: -8px 0 8px 0;
font-weight: normal;
color: #ffc;
}
#user-tools {
position: absolute;
top: 0;
right: 0;
padding: 1.2em 10px;
font-size: 11px;
text-align: right;
}
/* SIDEBAR */
#content-related h3 {
font-size: 12px;
color: #666;
margin-bottom: 3px;
}
#content-related h4 {
font-size: 11px;
}
#content-related .module h2 {
background: #eee url(../img/admin/nav-bg.gif) bottom left repeat-x;
color: #666;
}
/* CHANGELISTS */
#changelist {
position: relative;
width: 100%;
}
#changelist table {
width: 100%;
}
.change-list .hiddenfields { display:none; }
.change-list .filtered table {
border-right: 1px solid #ddd;
}
.change-list .filtered {
min-height: 400px;
}
.change-list .filtered {
background: white url(../img/admin/changelist-bg.gif) top right repeat-y !important;
}
.change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {
margin-right: 160px !important;
width: auto !important;
}
.change-list .filtered table tbody th {
padding-right: 1em;
}
#changelist .toplinks {
border-bottom: 1px solid #ccc !important;
}
#changelist .paginator {
color: #666;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
background: white url(../img/admin/nav-bg.gif) 0 180% repeat-x;
overflow: hidden;
}
.change-list .filtered .paginator {
border-right: 1px solid #ddd;
}
/* CHANGELIST TABLES */
#changelist table thead th {
white-space: nowrap;
vertical-align: middle;
}
#changelist table thead th.action-checkbox-column {
width: 1.5em;
text-align: center;
}
#changelist table tbody td, #changelist table tbody th {
border-left: 1px solid #ddd;
}
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
border-left: 0;
border-right: 1px solid #ddd;
}
#changelist table tbody td.action-checkbox {
text-align:center;
}
#changelist table tfoot {
color: #666;
}
/* TOOLBAR */
#changelist #toolbar {
padding: 3px;
border-bottom: 1px solid #ddd;
background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;
color: #666;
}
#changelist #toolbar form input {
font-size: 11px;
padding: 1px 2px;
}
#changelist #toolbar form #searchbar {
padding: 2px;
}
#changelist #changelist-search img {
vertical-align: middle;
}
/* FILTER COLUMN */
#changelist-filter {
position: absolute;
top: 0;
right: 0;
z-index: 1000;
width: 160px;
border-left: 1px solid #ddd;
background: #efefef;
margin: 0;
}
#changelist-filter h2 {
font-size: 11px;
padding: 2px 5px;
border-bottom: 1px solid #ddd;
}
#changelist-filter h3 {
font-size: 12px;
margin-bottom: 0;
}
#changelist-filter ul {
padding-left: 0;
margin-left: 10px;
}
#changelist-filter li {
list-style-type: none;
margin-left: 0;
padding-left: 0;
}
#changelist-filter a {
color: #999;
}
#changelist-filter a:hover {
color: #036;
}
#changelist-filter li.selected {
border-left: 5px solid #ccc;
padding-left: 5px;
margin-left: -10px;
}
#changelist-filter li.selected a {
color: #5b80b2 !important;
}
/* DATE DRILLDOWN */
.change-list ul.toplinks {
display: block;
background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
border-top: 1px solid white;
float: left;
padding: 0 !important;
margin: 0 !important;
width: 100%;
}
.change-list ul.toplinks li {
float: left;
width: 9em;
padding: 3px 6px;
font-weight: bold;
list-style-type: none;
}
.change-list ul.toplinks .date-back a {
color: #999;
}
.change-list ul.toplinks .date-back a:hover {
color: #036;
}
/* PAGINATOR */
.paginator {
font-size: 11px;
padding-top: 10px;
padding-bottom: 10px;
line-height: 22px;
margin: 0;
border-top: 1px solid #ddd;
}
.paginator a:link, .paginator a:visited {
padding: 2px 6px;
border: solid 1px #ccc;
background: white;
text-decoration: none;
}
.paginator a.showall {
padding: 0 !important;
border: none !important;
}
.paginator a.showall:hover {
color: #036 !important;
background: transparent !important;
}
.paginator .end {
border-width: 2px !important;
margin-right: 6px;
}
.paginator .this-page {
padding: 2px 6px;
font-weight: bold;
font-size: 13px;
vertical-align: top;
}
.paginator a:hover {
color: white;
background: #5b80b2;
border-color: #036;
}
/* ACTIONS */
.filtered .actions {
margin-right: 160px !important;
border-right: 1px solid #ddd;
}
#changelist table input {
margin: 0;
}
#changelist table tbody tr.selected {
background-color: #FFFFCC;
}
#changelist .actions {
color: #999;
padding: 3px;
border-top: 1px solid #fff;
border-bottom: 1px solid #ddd;
background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
}
#changelist .actions.selected {
background: #fffccf;
border-top: 1px solid #fffee8;
border-bottom: 1px solid #edecd6;
}
#changelist .actions span.all,
#changelist .actions span.action-counter,
#changelist .actions span.clear,
#changelist .actions span.question {
font-size: 11px;
margin: 0 0.5em;
display: none;
}
#changelist .actions:last-child {
border-bottom: none;
}
#changelist .actions select {
border: 1px solid #aaa;
margin-left: 0.5em;
padding: 1px 2px;
}
#changelist .actions label {
font-size: 11px;
margin-left: 0.5em;
}
#changelist #action-toggle {
display: none;
}
#changelist .actions .button {
font-size: 11px;
padding: 1px 2px;
}
/* DASHBOARD */
.dashboard .module table th {
width: 100%;
}
.dashboard .module table td {
white-space: nowrap;
}
.dashboard .module table td a {
display: block;
padding-right: .6em;
}
/* RECENT ACTIONS MODULE */
.module ul.actionlist {
margin-left: 0;
}
ul.actionlist li {
list-style-type: none;
}
ul.actionlist li.changelink {
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}
\ No newline at end of file
@import url('widgets.css');
/* FORM ROWS */
.form-row {
overflow: hidden;
padding: 8px 12px;
font-size: 11px;
border-bottom: 1px solid #eee;
}
.form-row img, .form-row input {
vertical-align: middle;
}
form .form-row p {
padding-left: 0;
font-size: 11px;
}
/* FORM LABELS */
form h4 {
margin: 0 !important;
padding: 0 !important;
border: none !important;
}
label {
font-weight: normal !important;
color: #666;
font-size: 12px;
}
.required label, label.required {
font-weight: bold !important;
color: #333 !important;
}
/* RADIO BUTTONS */
form ul.radiolist li {
list-style-type: none;
}
form ul.radiolist label {
float: none;
display: inline;
}
form ul.inline {
margin-left: 0;
padding: 0;
}
form ul.inline li {
float: left;
padding-right: 7px;
}
/* ALIGNED FIELDSETS */
.aligned label {
display: block;
padding: 3px 10px 0 0;
float: left;
width: 8em;
}
.aligned ul label {
display: inline;
float: none;
width: auto;
}
.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
width: 350px;
}
form .aligned p, form .aligned ul {
margin-left: 7em;
padding-left: 30px;
}
form .aligned table p {
margin-left: 0;
padding-left: 0;
}
form .aligned p.help {
padding-left: 38px;
}
.aligned .vCheckboxLabel {
float: none !important;
display: inline;
padding-left: 4px;
}
.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
width: 610px;
}
.checkbox-row p.help {
margin-left: 0;
padding-left: 0 !important;
}
fieldset .field-box {
float: left;
margin-right: 20px;
}
/* WIDE FIELDSETS */
.wide label {
width: 15em !important;
}
form .wide p {
margin-left: 15em;
}
form .wide p.help {
padding-left: 38px;
}
.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
width: 450px;
}
/* COLLAPSED FIELDSETS */
fieldset.collapsed * {
display: none;
}
fieldset.collapsed h2, fieldset.collapsed {
display: block !important;
}
fieldset.collapsed h2 {
background-image: url(../img/admin/nav-bg.gif);
background-position: bottom left;
color: #999;
}
fieldset.collapsed .collapse-toggle {
background: transparent;
display: inline !important;
}
/* MONOSPACE TEXTAREAS */
fieldset.monospace textarea {
font-family: "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace;
}
/* SUBMIT ROW */
.submit-row {
padding: 5px 7px;
text-align: right;
background: white url(../img/admin/nav-bg.gif) 0 100% repeat-x;
border: 1px solid #ccc;
margin: 5px 0;
overflow: hidden;
}
.submit-row input {
margin: 0 0 0 5px;
}
.submit-row p {
margin: 0.3em;
}
.submit-row p.deletelink-box {
float: left;
}
.submit-row .deletelink {
background: url(../img/admin/icon_deletelink.gif) 0 50% no-repeat;
padding-left: 14px;
}
/* CUSTOM FORM FIELDS */
.vSelectMultipleField {
vertical-align: top !important;
}
.vCheckboxField {
border: none;
}
.vDateField, .vTimeField {
margin-right: 2px;
}
.vURLField {
width: 30em;
}
.vLargeTextField, .vXMLLargeTextField {
width: 48em;
}
.flatpages-flatpage #id_content {
height: 40.2em;
}
.module table .vPositiveSmallIntegerField {
width: 2.2em;
}
.vTextField {
width: 20em;
}
.vIntegerField {
width: 5em;
}
.vForeignKeyRawIdAdminField {
width: 5em;
}
/* INLINES */
.inline-group {
padding: 0;
border: 1px solid #ccc;
margin: 10px 0;
}
.inline-group .aligned label {
width: 8em;
}
.inline-related {
position: relative;
}
.inline-related h3 {
margin: 0;
color: #666;
padding: 3px 5px;
font-size: 11px;
background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;
border-bottom: 1px solid #ddd;
}
.inline-related h3 span.delete {
float: right;
}
.inline-related h3 span.delete label {
margin-left: 2px;
font-size: 11px;
}
.inline-related fieldset {
margin: 0;
background: #fff;
border: none;
}
.inline-related fieldset.module h3 {
margin: 0;
padding: 2px 5px 3px 5px;
font-size: 11px;
text-align: left;
font-weight: bold;
background: #bcd;
color: #fff;
}
.inline-group .tabular fieldset.module {
border: none;
border-bottom: 1px solid #ddd;
}
.inline-related.tabular fieldset.module table {
width: 100%;
}
.last-related fieldset {
border: none;
}
.inline-group .tabular tr.has_original td {
padding-top: 2em;
}
.inline-group .tabular tr td.original {
padding: 2px 0 0 0;
width: 0;
_position: relative;
}
.inline-group .tabular th.original {
width: 0px;
padding: 0;
}
.inline-group .tabular td.original p {
position: absolute;
left: 0;
height: 1.1em;
padding: 2px 7px;
overflow: hidden;
font-size: 9px;
font-weight: bold;
color: #666;
_width: 700px;
}
.inline-group ul.tools {
padding: 0;
margin: 0;
list-style: none;
}
.inline-group ul.tools li {
display: inline;
padding: 0 5px;
}
.inline-group div.add-row,
.inline-group .tabular tr.add-row td {
color: #666;
padding: 3px 5px;
border-bottom: 1px solid #ddd;
background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;
}
.inline-group .tabular tr.add-row td {
padding: 4px 5px 3px;
border-bottom: none;
}
.inline-group ul.tools a.add,
.inline-group div.add-row a,
.inline-group .tabular tr.add-row td a {
background: url(../img/admin/icon_addlink.gif) 0 50% no-repeat;
padding-left: 14px;
font-size: 11px;
outline: 0; /* Remove dotted border around link */
}
.empty-form {
display: none;
}
/* IE7 specific bug fixes */
.submit-row input {
float: right;
}
\ No newline at end of file
/* IE 6 & 7 */
/* Proper fixed width for dashboard in IE6 */
.dashboard #content {
*width: 768px;
}
.dashboard #content-main {
*width: 535px;
}
/* IE 6 ONLY */
/* Keep header from flowing off the page */
#container {
_position: static;
}
/* Put the right sidebars back on the page */
.colMS #content-related {
_margin-right: 0;
_margin-left: 10px;
_position: static;
}
/* Put the left sidebars back on the page */
.colSM #content-related {
_margin-right: 10px;
_margin-left: -115px;
_position: static;
}
.form-row {
_height: 1%;
}
/* Fix right margin for changelist filters in IE6 */
#changelist-filter ul {
_margin-right: -10px;
}
/* IE ignores min-height, but treats height as if it were min-height */
.change-list .filtered {
_height: 400px;
}
/* IE doesn't know alpha transparency in PNGs */
.inline-deletelink {
background: transparent url(../img/admin/inline-delete-8bit.png) no-repeat;
}
\ No newline at end of file
/* LOGIN FORM */
body.login {
background: #eee;
}
.login #container {
background: white;
border: 1px solid #ccc;
width: 28em;
min-width: 300px;
margin-left: auto;
margin-right: auto;
margin-top: 100px;
}
.login #content-main {
width: 100%;
}
.login form {
margin-top: 1em;
}
.login .form-row {
padding: 4px 0;
float: left;
width: 100%;
}
.login .form-row label {
float: left;
width: 9em;
padding-right: 0.5em;
line-height: 2em;
text-align: right;
font-size: 1em;
color: #333;
}
.login .form-row #id_username, .login .form-row #id_password {
width: 14em;
}
.login span.help {
font-size: 10px;
display: block;
}
.login .submit-row {
clear: both;
padding: 1em 0 0 9.4em;
}
body {
direction: rtl;
}
/* LOGIN */
.login .form-row {
float: right;
}
.login .form-row label {
float: right;
padding-left: 0.5em;
padding-right: 0;
text-align: left;
}
.login .submit-row {
clear: both;
padding: 1em 9.4em 0 0;
}
/* GLOBAL */
th {
text-align: right;
}
.module h2, .module caption {
text-align: right;
}
.addlink, .changelink {
padding-left: 0px;
padding-right: 12px;
background-position: 100% 0.2em;
}
.deletelink {
padding-left: 0px;
padding-right: 12px;
background-position: 100% 0.25em;
}
.object-tools {
float: left;
}
thead th:first-child,
tfoot td:first-child {
border-left: 1px solid #ddd !important;
}
/* LAYOUT */
#user-tools {
right: auto;
left: 0;
text-align: left;
}
div.breadcrumbs {
text-align: right;
}
#content-main {
float: right;
}
#content-related {
float: left;
margin-left: -19em;
margin-right: auto;
}
.colMS {
margin-left: 20em !important;
margin-right: 10px !important;
}
/* SORTABLE TABLES */
table thead th.sorted a {
padding-left: 13px;
padding-right: 0px;
}
table thead th.ascending a,
table thead th.descending a {
background-position: left;
}
/* dashboard styles */
.dashboard .module table td a {
padding-left: .6em;
padding-right: 12px;
}
/* changelists styles */
.change-list ul.toplinks li {
float: right;
}
.change-list .filtered {
background: white url(../img/admin/changelist-bg_rtl.gif) top left repeat-y !important;
}
.change-list .filtered table {
border-left: 1px solid #ddd;
border-right: 0px none;
}
#changelist-filter {
right: auto;
left: 0;
border-left: 0px none;
border-right: 1px solid #ddd;
}
.change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {
margin-right: 0px !important;
margin-left: 160px !important;
}
#changelist-filter li.selected {
border-left: 0px none;
padding-left: 0px;
margin-left: 0;
border-right: 5px solid #ccc;
padding-right: 5px;
margin-right: -10px;
}
.filtered .actions {
border-left:1px solid #DDDDDD;
margin-left:160px !important;
border-right: 0 none;
margin-right:0 !important;
}
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
border-right: 0;
border-left: 1px solid #ddd;
}
/* FORMS */
.aligned label {
padding: 0 0 3px 1em;
float: right;
}
.submit-row {
text-align: left
}
.submit-row p.deletelink-box {
float: right;
}
.submit-row .deletelink {
background: url(../img/admin/icon_deletelink.gif) 0 50% no-repeat;
padding-right: 14px;
}
.vDateField, .vTimeField {
margin-left: 2px;
}
form ul.inline li {
float: right;
padding-right: 0;
padding-left: 7px;
}
input[type=submit].default, .submit-row input.default {
float: left;
}
fieldset .field-box {
float: right;
margin-left: 20px;
}
.errorlist li {
background-position: 100% .3em;
padding: 4px 25px 4px 5px;
}
.errornote {
background-position: 100% .3em;
padding: 4px 25px 4px 5px;
}
/* WIDGETS */
.calendarnav-previous {
top: 0;
left: auto;
right: 0;
}
.calendarnav-next {
top: 0;
right: auto;
left: 0;
}
.calendar caption, .calendarbox h2 {
text-align: center;
}
.selector {
float: right;
}
.selector .selector-filter {
text-align: right;
}
.inline-deletelink {
float: left;
}
/* MISC */
.inline-related h2, .inline-group h2 {
text-align: right
}
.inline-related h3 span.delete {
padding-right: 20px;
padding-left: inherit;
left: 10px;
right: inherit;
}
.inline-related h3 span.delete label {
margin-left: inherit;
margin-right: 2px;
}
/* SELECTOR (FILTER INTERFACE) */
.selector {
width: 580px;
float: left;
}
.selector select {
width: 270px;
height: 17.2em;
}
.selector-available, .selector-chosen {
float: left;
width: 270px;
text-align: center;
margin-bottom: 5px;
}
.selector-available h2, .selector-chosen h2 {
border: 1px solid #ccc;
}
.selector .selector-available h2 {
background: white url(../img/admin/nav-bg.gif) bottom left repeat-x;
color: #666;
}
.selector .selector-filter {
background: white;
border: 1px solid #ccc;
border-width: 0 1px;
padding: 3px;
color: #999;
font-size: 10px;
margin: 0;
text-align: left;
}
.selector .selector-chosen .selector-filter {
padding: 4px 5px;
}
.selector .selector-available input {
width: 230px;
}
.selector ul.selector-chooser {
float: left;
width: 22px;
height: 50px;
background: url(../img/admin/chooser-bg.gif) top center no-repeat;
margin: 8em 3px 0 3px;
padding: 0;
}
.selector-chooser li {
margin: 0;
padding: 3px;
list-style-type: none;
}
.selector select {
margin-bottom: 5px;
margin-top: 0;
}
.selector-add, .selector-remove {
width: 16px;
height: 16px;
display: block;
text-indent: -3000px;
overflow: hidden;
}
.selector-add {
background: url(../img/admin/selector-add.gif) top center no-repeat;
margin-bottom: 2px;
}
.selector-remove {
background: url(../img/admin/selector-remove.gif) top center no-repeat;
}
a.selector-chooseall, a.selector-clearall {
display: block;
width: 6em;
text-align: left;
margin-left: auto;
margin-right: auto;
font-weight: bold;
color: #666;
padding: 3px 0 3px 18px;
}
a.selector-chooseall:hover, a.selector-clearall:hover {
color: #036;
}
a.selector-chooseall {
width: 7em;
background: url(../img/admin/selector-addall.gif) left center no-repeat;
}
a.selector-clearall {
background: url(../img/admin/selector-removeall.gif) left center no-repeat;
}
/* STACKED SELECTORS */
.stacked {
float: left;
width: 500px;
}
.stacked select {
width: 480px;
height: 10.1em;
}
.stacked .selector-available, .stacked .selector-chosen {
width: 480px;
}
.stacked .selector-available {
margin-bottom: 0;
}
.stacked .selector-available input {
width: 442px;
}
.stacked ul.selector-chooser {
height: 22px;
width: 50px;
margin: 0 0 3px 40%;
background: url(../img/admin/chooser_stacked-bg.gif) top center no-repeat;
}
.stacked .selector-chooser li {
float: left;
padding: 3px 3px 3px 5px;
}
.stacked .selector-chooseall, .stacked .selector-clearall {
display: none;
}
.stacked .selector-add {
background-image: url(../img/admin/selector_stacked-add.gif);
}
.stacked .selector-remove {
background-image: url(../img/admin/selector_stacked-remove.gif);
}
/* DATE AND TIME */
p.datetime {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-size: 11px;
font-weight: bold;
}
.datetime span {
font-size: 11px;
color: #ccc;
font-weight: normal;
white-space: nowrap;
}
table p.datetime {
font-size: 10px;
margin-left: 0;
padding-left: 0;
}
/* FILE UPLOADS */
p.file-upload {
line-height: 20px;
margin: 0;
padding: 0;
color: #666;
font-size: 11px;
font-weight: bold;
}
.file-upload a {
font-weight: normal;
}
.file-upload .deletelink {
margin-left: 5px;
}
span.clearable-file-input label {
color: #333;
font-size: 11px;
display: inline;
float: none;
}
/* CALENDARS & CLOCKS */
.calendarbox, .clockbox {
margin: 5px auto;
font-size: 11px;
width: 16em;
text-align: center;
background: white;
position: relative;
}
.clockbox {
width: auto;
}
.calendar {
margin: 0;
padding: 0;
}
.calendar table {
margin: 0;
padding: 0;
border-collapse: collapse;
background: white;
width: 99%;
}
.calendar caption, .calendarbox h2 {
margin: 0;
font-size: 11px;
text-align: center;
border-top: none;
}
.calendar th {
font-size: 10px;
color: #666;
padding: 2px 3px;
text-align: center;
background: #e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x;
border-bottom: 1px solid #ddd;
}
.calendar td {
font-size: 11px;
text-align: center;
padding: 0;
border-top: 1px solid #eee;
border-bottom: none;
}
.calendar td.selected a {
background: #C9DBED;
}
.calendar td.nonday {
background: #efefef;
}
.calendar td.today a {
background: #ffc;
}
.calendar td a, .timelist a {
display: block;
font-weight: bold;
padding: 4px;
text-decoration: none;
color: #444;
}
.calendar td a:hover, .timelist a:hover {
background: #5b80b2;
color: white;
}
.calendar td a:active, .timelist a:active {
background: #036;
color: white;
}
.calendarnav {
font-size: 10px;
text-align: center;
color: #ccc;
margin: 0;
padding: 1px 3px;
}
.calendarnav a:link, #calendarnav a:visited, #calendarnav a:hover {
color: #999;
}
.calendar-shortcuts {
background: white;
font-size: 10px;
line-height: 11px;
border-top: 1px solid #eee;
padding: 3px 0 4px;
color: #ccc;
}
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
display: block;
position: absolute;
font-weight: bold;
font-size: 12px;
background: #C9DBED url(../img/admin/default-bg.gif) bottom left repeat-x;
padding: 1px 4px 2px 4px;
color: white;
}
.calendarnav-previous:hover, .calendarnav-next:hover {
background: #036;
}
.calendarnav-previous {
top: 0;
left: 0;
}
.calendarnav-next {
top: 0;
right: 0;
}
.calendar-cancel {
margin: 0 !important;
padding: 0;
font-size: 10px;
background: #e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x;
border-top: 1px solid #ddd;
}
.calendar-cancel a {
padding: 2px;
color: #999;
}
ul.timelist, .timelist li {
list-style-type: none;
margin: 0;
padding: 0;
}
.timelist a {
padding: 2px;
}
/* INLINE ORDERER */
ul.orderer {
position: relative;
padding: 0 !important;
margin: 0 !important;
list-style-type: none;
}
ul.orderer li {
list-style-type: none;
display: block;
padding: 0;
margin: 0;
border: 1px solid #bbb;
border-width: 0 1px 1px 0;
white-space: nowrap;
overflow: hidden;
background: #e2e2e2 url(../img/admin/nav-bg-grabber.gif) repeat-y;
}
ul.orderer li:hover {
cursor: move;
background-color: #ddd;
}
ul.orderer li a.selector {
margin-left: 12px;
overflow: hidden;
width: 83%;
font-size: 10px !important;
padding: 0.6em 0;
}
ul.orderer li a:link, ul.orderer li a:visited {
color: #333;
}
ul.orderer li .inline-deletelink {
position: absolute;
right: 4px;
margin-top: 0.6em;
}
ul.orderer li.selected {
background-color: #f8f8f8;
border-right-color: #f8f8f8;
}
ul.orderer li.deleted {
background: #bbb url(../img/admin/deleted-overlay.gif);
}
ul.orderer li.deleted a:link, ul.orderer li.deleted a:visited {
color: #888;
}
ul.orderer li.deleted .inline-deletelink {
background-image: url(../img/admin/inline-restore.png);
}
ul.orderer li.deleted:hover, ul.orderer li.deleted a.selector:hover {
cursor: default;
}
/* EDIT INLINE */
.inline-deletelink {
float: right;
text-indent: -9999px;
background: transparent url(../img/admin/inline-delete.png) no-repeat;
width: 15px;
height: 15px;
border: 0px none;
outline: 0; /* Remove dotted border around link */
}
.inline-deletelink:hover {
background-position: -15px 0;
cursor: pointer;
}
.editinline button.addlink {
border: 0px none;
color: #5b80b2;
font-size: 100%;
cursor: pointer;
}
.editinline button.addlink:hover {
color: #036;
cursor: pointer;
}
.editinline table .help {
text-align: right;
float: right;
padding-left: 2em;
}
.editinline tfoot .addlink {
white-space: nowrap;
}
.editinline table thead th:last-child {
border-left: none;
}
.editinline tr.deleted {
background: #ddd url(../img/admin/deleted-overlay.gif);
}
.editinline tr.deleted .inline-deletelink {
background-image: url(../img/admin/inline-restore.png);
}
.editinline tr.deleted td:hover {
cursor: default;
}
.editinline tr.deleted td:first-child {
background-image: none !important;
}
/* EDIT INLINE - STACKED */
.editinline-stacked {
min-width: 758px;
}
.editinline-stacked .inline-object {
margin-left: 210px;
background: white;
}
.editinline-stacked .inline-source {
float: left;
width: 200px;
background: #f8f8f8;
}
.editinline-stacked .inline-splitter {
float: left;
width: 9px;
background: #f8f8f8 url(../img/admin/inline-splitter-bg.gif) 50% 50% no-repeat;
border-right: 1px solid #ccc;
}
.editinline-stacked .controls {
clear: both;
background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;
padding: 3px 4px;
font-size: 11px;
border-top: 1px solid #ddd;
}
Copyright (c) 2010 John Resig, http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
var SelectBox = {
cache: new Object(),
init: function(id) {
var box = document.getElementById(id);
var node;
SelectBox.cache[id] = new Array();
var cache = SelectBox.cache[id];
for (var i = 0; (node = box.options[i]); i++) {
cache.push({value: node.value, text: node.text, displayed: 1});
}
},
redisplay: function(id) {
// Repopulate HTML select box from cache
var box = document.getElementById(id);
box.options.length = 0; // clear all options
for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) {
var node = SelectBox.cache[id][i];
if (node.displayed) {
box.options[box.options.length] = new Option(node.text, node.value, false, false);
}
}
},
filter: function(id, text) {
// Redisplay the HTML select box, displaying only the choices containing ALL
// the words in text. (It's an AND search.)
var tokens = text.toLowerCase().split(/\s+/);
var node, token;
for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
node.displayed = 1;
for (var j = 0; (token = tokens[j]); j++) {
if (node.text.toLowerCase().indexOf(token) == -1) {
node.displayed = 0;
}
}
}
SelectBox.redisplay(id);
},
delete_from_cache: function(id, value) {
var node, delete_index = null;
for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
if (node.value == value) {
delete_index = i;
break;
}
}
var j = SelectBox.cache[id].length - 1;
for (var i = delete_index; i < j; i++) {
SelectBox.cache[id][i] = SelectBox.cache[id][i+1];
}
SelectBox.cache[id].length--;
},
add_to_cache: function(id, option) {
SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
},
cache_contains: function(id, value) {
// Check if an item is contained in the cache
var node;
for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
if (node.value == value) {
return true;
}
}
return false;
},
move: function(from, to) {
var from_box = document.getElementById(from);
var to_box = document.getElementById(to);
var option;
for (var i = 0; (option = from_box.options[i]); i++) {
if (option.selected && SelectBox.cache_contains(from, option.value)) {
SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
SelectBox.delete_from_cache(from, option.value);
}
}
SelectBox.redisplay(from);
SelectBox.redisplay(to);
},
move_all: function(from, to) {
var from_box = document.getElementById(from);
var to_box = document.getElementById(to);
var option;
for (var i = 0; (option = from_box.options[i]); i++) {
if (SelectBox.cache_contains(from, option.value)) {
SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
SelectBox.delete_from_cache(from, option.value);
}
}
SelectBox.redisplay(from);
SelectBox.redisplay(to);
},
sort: function(id) {
SelectBox.cache[id].sort( function(a, b) {
a = a.text.toLowerCase();
b = b.text.toLowerCase();
try {
if (a > b) return 1;
if (a < b) return -1;
}
catch (e) {
// silently fail on IE 'unknown' exception
}
return 0;
} );
},
select_all: function(id) {
var box = document.getElementById(id);
for (var i = 0; i < box.options.length; i++) {
box.options[i].selected = 'selected';
}
}
}
/*
SelectFilter2 - Turns a multiple-select box into a filter interface.
Different than SelectFilter because this is coupled to the admin framework.
Requires core.js, SelectBox.js and addevent.js.
*/
function findForm(node) {
// returns the node of the form containing the given node
if (node.tagName.toLowerCase() != 'form') {
return findForm(node.parentNode);
}
return node;
}
var SelectFilter = {
init: function(field_id, field_name, is_stacked, admin_media_prefix) {
if (field_id.match(/__prefix__/)){
// Don't intialize on empty forms.
return;
}
var from_box = document.getElementById(field_id);
from_box.id += '_from'; // change its ID
from_box.className = 'filtered';
var ps = from_box.parentNode.getElementsByTagName('p');
for (var i=0; i<ps.length; i++) {
if (ps[i].className.indexOf("info") != -1) {
// Remove <p class="info">, because it just gets in the way.
from_box.parentNode.removeChild(ps[i]);
} else if (ps[i].className.indexOf("help") != -1) {
// Move help text up to the top so it isn't below the select
// boxes or wrapped off on the side to the right of the add
// button:
from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
}
}
// <div class="selector"> or <div class="selector stacked">
var selector_div = quickElement('div', from_box.parentNode);
selector_div.className = is_stacked ? 'selector stacked' : 'selector';
// <div class="selector-available">
var selector_available = quickElement('div', selector_div, '');
selector_available.className = 'selector-available';
quickElement('h2', selector_available, interpolate(gettext('Available %s'), [field_name]));
var filter_p = quickElement('p', selector_available, '');
filter_p.className = 'selector-filter';
var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + "_input", 'style', 'width:16px;padding:2px');
var search_selector_img = quickElement('img', search_filter_label, '', 'src', admin_media_prefix + 'img/admin/selector-search.gif');
search_selector_img.alt = gettext("Filter");
filter_p.appendChild(document.createTextNode(' '));
var filter_input = quickElement('input', filter_p, '', 'type', 'text');
filter_input.id = field_id + '_input';
selector_available.appendChild(from_box);
var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + '_from", "' + field_id + '_to"); })()');
choose_all.className = 'selector-chooseall';
// <ul class="selector-chooser">
var selector_chooser = quickElement('ul', selector_div, '');
selector_chooser.className = 'selector-chooser';
var add_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Add'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_from","' + field_id + '_to");})()');
add_link.className = 'selector-add';
var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Remove'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_to","' + field_id + '_from");})()');
remove_link.className = 'selector-remove';
// <div class="selector-chosen">
var selector_chosen = quickElement('div', selector_div, '');
selector_chosen.className = 'selector-chosen';
quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s'), [field_name]));
var selector_filter = quickElement('p', selector_chosen, gettext('Select your choice(s) and click '));
selector_filter.className = 'selector-filter';
quickElement('img', selector_filter, '', 'src', admin_media_prefix + (is_stacked ? 'img/admin/selector_stacked-add.gif':'img/admin/selector-add.gif'), 'alt', 'Add');
var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
to_box.className = 'filtered';
var clear_all = quickElement('a', selector_chosen, gettext('Clear all'), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from");})()');
clear_all.className = 'selector-clearall';
from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
// Set up the JavaScript event handlers for the select box filter interface
addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); });
addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); });
addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
SelectBox.init(field_id + '_from');
SelectBox.init(field_id + '_to');
// Move selected from_box options to to_box
SelectBox.move(field_id + '_from', field_id + '_to');
},
filter_key_up: function(event, field_id) {
from = document.getElementById(field_id + '_from');
// don't submit form if user pressed Enter
if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
from.selectedIndex = 0;
SelectBox.move(field_id + '_from', field_id + '_to');
from.selectedIndex = 0;
return false;
}
var temp = from.selectedIndex;
SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
from.selectedIndex = temp;
return true;
},
filter_key_down: function(event, field_id) {
from = document.getElementById(field_id + '_from');
// right arrow -- move across
if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
var old_index = from.selectedIndex;
SelectBox.move(field_id + '_from', field_id + '_to');
from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
return false;
}
// down arrow -- wrap around
if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
}
// up arrow -- wrap around
if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
}
return true;
}
}
(function($) {
$.fn.actions = function(opts) {
var options = $.extend({}, $.fn.actions.defaults, opts);
var actionCheckboxes = $(this);
var list_editable_changed = false;
checker = function(checked) {
if (checked) {
showQuestion();
} else {
reset();
}
$(actionCheckboxes).attr("checked", checked)
.parent().parent().toggleClass(options.selectedClass, checked);
}
updateCounter = function() {
var sel = $(actionCheckboxes).filter(":checked").length;
$(options.counterContainer).html(interpolate(
ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
sel: sel,
cnt: _actions_icnt
}, true));
$(options.allToggle).attr("checked", function() {
if (sel == actionCheckboxes.length) {
value = true;
showQuestion();
} else {
value = false;
clearAcross();
}
return value;
});
}
showQuestion = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).show();
$(options.allContainer).hide();
}
showClear = function() {
$(options.acrossClears).show();
$(options.acrossQuestions).hide();
$(options.actionContainer).toggleClass(options.selectedClass);
$(options.allContainer).show();
$(options.counterContainer).hide();
}
reset = function() {
$(options.acrossClears).hide();
$(options.acrossQuestions).hide();
$(options.allContainer).hide();
$(options.counterContainer).show();
}
clearAcross = function() {
reset();
$(options.acrossInput).val(0);
$(options.actionContainer).removeClass(options.selectedClass);
}
// Show counter by default
$(options.counterContainer).show();
// Check state of checkboxes and reinit state if needed
$(this).filter(":checked").each(function(i) {
$(this).parent().parent().toggleClass(options.selectedClass);
updateCounter();
if ($(options.acrossInput).val() == 1) {
showClear();
}
});
$(options.allToggle).show().click(function() {
checker($(this).attr("checked"));
updateCounter();
});
$("div.actions span.question a").click(function(event) {
event.preventDefault();
$(options.acrossInput).val(1);
showClear();
});
$("div.actions span.clear a").click(function(event) {
event.preventDefault();
$(options.allToggle).attr("checked", false);
clearAcross();
checker(0);
updateCounter();
});
lastChecked = null;
$(actionCheckboxes).click(function(event) {
if (!event) { var event = window.event; }
var target = event.target ? event.target : event.srcElement;
if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey == true) {
var inrange = false;
$(lastChecked).attr("checked", target.checked)
.parent().parent().toggleClass(options.selectedClass, target.checked);
$(actionCheckboxes).each(function() {
if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) {
inrange = (inrange) ? false : true;
}
if (inrange) {
$(this).attr("checked", target.checked)
.parent().parent().toggleClass(options.selectedClass, target.checked);
}
});
}
$(target).parent().parent().toggleClass(options.selectedClass, target.checked);
lastChecked = target;
updateCounter();
});
$('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {
list_editable_changed = true;
});
$('form#changelist-form button[name="index"]').click(function(event) {
if (list_editable_changed) {
return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
}
});
$('form#changelist-form input[name="_save"]').click(function(event) {
var action_changed = false;
$('div.actions select option:selected').each(function() {
if ($(this).val()) {
action_changed = true;
}
});
if (action_changed) {
if (list_editable_changed) {
return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action."));
} else {
return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."));
}
}
});
}
/* Setup plugin defaults */
$.fn.actions.defaults = {
actionContainer: "div.actions",
counterContainer: "span.action-counter",
allContainer: "div.actions span.all",
acrossInput: "div.actions input.select-across",
acrossQuestions: "div.actions span.question",
acrossClears: "div.actions span.clear",
allToggle: "#action-toggle",
selectedClass: "selected"
}
})(django.jQuery);
(function(a){a.fn.actions=function(h){var b=a.extend({},a.fn.actions.defaults,h),e=a(this),f=false;checker=function(c){c?showQuestion():reset();a(e).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)};updateCounter=function(){var c=a(e).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},true));a(b.allToggle).attr("checked",function(){if(c==e.length){value=true;showQuestion()}else{value=
false;clearAcross()}return value})};showQuestion=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()};showClear=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()};reset=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()};clearAcross=function(){reset();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};
a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);updateCounter();a(b.acrossInput).val()==1&&showClear()});a(b.allToggle).show().click(function(){checker(a(this).attr("checked"));updateCounter()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);showClear()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",false);clearAcross();checker(0);
updateCounter()});lastChecked=null;a(e).click(function(c){if(!c)c=window.event;var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&c.shiftKey==true){var g=false;a(lastChecked).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(e).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))g=g?false:true;g&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,
d.checked);lastChecked=d;updateCounter()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){f=true});a('form#changelist-form button[name="index"]').click(function(){if(f)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var c=false;a("div.actions select option:selected").each(function(){if(a(this).val())c=
true});if(c)return f?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",
acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);
// Inserts shortcut buttons after all of the following:
// <input type="text" class="vDateField">
// <input type="text" class="vTimeField">
var DateTimeShortcuts = {
calendars: [],
calendarInputs: [],
clockInputs: [],
calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
calendarDivName2: 'calendarin', // name of <div> that contains calendar
calendarLinkName: 'calendarlink',// name of the link that is used to toggle
clockDivName: 'clockbox', // name of clock <div> that gets toggled
clockLinkName: 'clocklink', // name of the link that is used to toggle
shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
admin_media_prefix: '',
init: function() {
// Get admin_media_prefix by grabbing it off the window object. It's
// set in the admin/base.html template, so if it's not there, someone's
// overridden the template. In that case, we'll set a clearly-invalid
// value in the hopes that someone will examine HTTP requests and see it.
if (window.__admin_media_prefix__ != undefined) {
DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__;
} else {
DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/';
}
var inputs = document.getElementsByTagName('input');
for (i=0; i<inputs.length; i++) {
var inp = inputs[i];
if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
DateTimeShortcuts.addClock(inp);
}
else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
DateTimeShortcuts.addCalendar(inp);
}
}
},
// Add clock widget to a given field
addClock: function(inp) {
var num = DateTimeShortcuts.clockInputs.length;
DateTimeShortcuts.clockInputs[num] = inp;
// Shortcut links (clock icon and "Now" link)
var shortcuts_span = document.createElement('span');
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var now_link = document.createElement('a');
now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + get_format('TIME_INPUT_FORMATS')[0] + "'));");
now_link.appendChild(document.createTextNode(gettext('Now')));
var clock_link = document.createElement('a');
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
clock_link.id = DateTimeShortcuts.clockLinkName + num;
quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_clock.gif', 'alt', gettext('Clock'));
shortcuts_span.appendChild(document.createTextNode('\240'));
shortcuts_span.appendChild(now_link);
shortcuts_span.appendChild(document.createTextNode('\240|\240'));
shortcuts_span.appendChild(clock_link);
// Create clock link div
//
// Markup looks like:
// <div id="clockbox1" class="clockbox module">
// <h2>Choose a time</h2>
// <ul class="timelist">
// <li><a href="#">Now</a></li>
// <li><a href="#">Midnight</a></li>
// <li><a href="#">6 a.m.</a></li>
// <li><a href="#">Noon</a></li>
// </ul>
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
// </div>
var clock_box = document.createElement('div');
clock_box.style.display = 'none';
clock_box.style.position = 'absolute';
clock_box.className = 'clockbox module';
clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
document.body.appendChild(clock_box);
addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation);
quickElement('h2', clock_box, gettext('Choose a time'));
time_list = quickElement('ul', clock_box, '');
time_list.className = 'timelist';
time_format = get_format('TIME_INPUT_FORMATS')[0];
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");
cancel_p = quickElement('p', clock_box, '');
cancel_p.className = 'calendar-cancel';
quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
},
openClock: function(num) {
var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
clock_box.style.left = findPosX(clock_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
clock_box.style.left = findPosX(clock_link) - 110 + 'px';
}
clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px';
// Show the clock box
clock_box.style.display = 'block';
addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; });
},
dismissClock: function(num) {
document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
window.document.onclick = null;
},
handleClockQuicklink: function(num, val) {
DateTimeShortcuts.clockInputs[num].value = val;
DateTimeShortcuts.clockInputs[num].focus();
DateTimeShortcuts.dismissClock(num);
},
// Add calendar widget to a given field.
addCalendar: function(inp) {
var num = DateTimeShortcuts.calendars.length;
DateTimeShortcuts.calendarInputs[num] = inp;
// Shortcut links (calendar icon and "Today" link)
var shortcuts_span = document.createElement('span');
shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var today_link = document.createElement('a');
today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
today_link.appendChild(document.createTextNode(gettext('Today')));
var cal_link = document.createElement('a');
cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
cal_link.id = DateTimeShortcuts.calendarLinkName + num;
quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_calendar.gif', 'alt', gettext('Calendar'));
shortcuts_span.appendChild(document.createTextNode('\240'));
shortcuts_span.appendChild(today_link);
shortcuts_span.appendChild(document.createTextNode('\240|\240'));
shortcuts_span.appendChild(cal_link);
// Create calendarbox div.
//
// Markup looks like:
//
// <div id="calendarbox3" class="calendarbox module">
// <h2>
// <a href="#" class="link-previous">&lsaquo;</a>
// <a href="#" class="link-next">&rsaquo;</a> February 2003
// </h2>
// <div class="calendar" id="calendarin3">
// <!-- (cal) -->
// </div>
// <div class="calendar-shortcuts">
// <a href="#">Yesterday</a> | <a href="#">Today</a> | <a href="#">Tomorrow</a>
// </div>
// <p class="calendar-cancel"><a href="#">Cancel</a></p>
// </div>
var cal_box = document.createElement('div');
cal_box.style.display = 'none';
cal_box.style.position = 'absolute';
cal_box.className = 'calendarbox module';
cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
document.body.appendChild(cal_box);
addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation);
// next-prev links
var cal_nav = quickElement('div', cal_box, '');
var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');
cal_nav_prev.className = 'calendarnav-previous';
var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');
cal_nav_next.className = 'calendarnav-next';
// main box
var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
cal_main.className = 'calendar';
DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
DateTimeShortcuts.calendars[num].drawCurrent();
// calendar shortcuts
var shortcuts = quickElement('div', cal_box, '');
shortcuts.className = 'calendar-shortcuts';
quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');
shortcuts.appendChild(document.createTextNode('\240|\240'));
quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
shortcuts.appendChild(document.createTextNode('\240|\240'));
quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');
// cancel bar
var cancel_p = quickElement('p', cal_box, '');
cancel_p.className = 'calendar-cancel';
quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
},
openCalendar: function(num) {
var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
var inp = DateTimeShortcuts.calendarInputs[num];
// Determine if the current value in the input has a valid date.
// If so, draw the calendar with that date's year and month.
if (inp.value) {
var date_parts = inp.value.split('-');
var year = date_parts[0];
var month = parseFloat(date_parts[1]);
if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
DateTimeShortcuts.calendars[num].drawDate(month, year);
}
}
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
cal_box.style.left = findPosX(cal_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
cal_box.style.left = findPosX(cal_link) - 180 + 'px';
}
cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px';
cal_box.style.display = 'block';
addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; });
},
dismissCalendar: function(num) {
document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';
window.document.onclick = null;
},
drawPrev: function(num) {
DateTimeShortcuts.calendars[num].drawPreviousMonth();
},
drawNext: function(num) {
DateTimeShortcuts.calendars[num].drawNextMonth();
},
handleCalendarCallback: function(num) {
format = get_format('DATE_INPUT_FORMATS')[0];
// the format needs to be escaped a little
format = format.replace('\\', '\\\\');
format = format.replace('\r', '\\r');
format = format.replace('\n', '\\n');
format = format.replace('\t', '\\t');
format = format.replace("'", "\\'");
return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[",
num,
"].value = new Date(y, m-1, d).strftime('",
format,
"');DateTimeShortcuts.calendarInputs[",
num,
"].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+",
num,
").style.display='none';}"].join('');
},
handleCalendarQuickLink: function(num, offset) {
var d = new Date();
d.setDate(d.getDate() + offset)
DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
DateTimeShortcuts.calendarInputs[num].focus();
DateTimeShortcuts.dismissCalendar(num);
},
cancelEventPropagation: function(e) {
if (!e) e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
}
addEvent(window, 'load', DateTimeShortcuts.init);
// Handles related-objects functionality: lookup link for raw_id_fields
// and Add Another links.
function html_unescape(text) {
// Unescape a string that was escaped using django.utils.html.escape.
text = text.replace(/&lt;/g, '<');
text = text.replace(/&gt;/g, '>');
text = text.replace(/&quot;/g, '"');
text = text.replace(/&#39;/g, "'");
text = text.replace(/&amp;/g, '&');
return text;
}
// IE doesn't accept periods or dashes in the window name, but the element IDs
// we use to generate popup window names may contain them, therefore we map them
// to allowed characters in a reversible way so that we can locate the correct
// element when the popup window is dismissed.
function id_to_windowname(text) {
text = text.replace(/\./g, '__dot__');
text = text.replace(/\-/g, '__dash__');
return text;
}
function windowname_to_id(text) {
text = text.replace(/__dot__/g, '.');
text = text.replace(/__dash__/g, '-');
return text;
}
function showRelatedObjectLookupPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^lookup_/, '');
name = id_to_windowname(name);
var href;
if (triggeringLink.href.search(/\?/) >= 0) {
href = triggeringLink.href + '&pop=1';
} else {
href = triggeringLink.href + '?pop=1';
}
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
function dismissRelatedLookupPopup(win, chosenId) {
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + chosenId;
} else {
document.getElementById(name).value = chosenId;
}
win.close();
}
function showAddAnotherPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^add_/, '');
name = id_to_windowname(name);
href = triggeringLink.href
if (href.indexOf('?') == -1) {
href += '?_popup=1';
} else {
href += '&_popup=1';
}
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
function dismissAddAnotherPopup(win, newId, newRepr) {
// newId and newRepr are expected to have previously been escaped by
// django.utils.html.escape.
newId = html_unescape(newId);
newRepr = html_unescape(newRepr);
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
if (elem) {
if (elem.nodeName == 'SELECT') {
var o = new Option(newRepr, newId);
elem.options[elem.options.length] = o;
o.selected = true;
} else if (elem.nodeName == 'INPUT') {
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + newId;
} else {
elem.value = newId;
}
}
} else {
var toId = name + "_to";
elem = document.getElementById(toId);
var o = new Option(newRepr, newId);
SelectBox.add_to_cache(toId, o);
SelectBox.redisplay(toId);
}
win.close();
}
addEvent(window, 'load', reorder_init);
var lis;
var top = 0;
var left = 0;
var height = 30;
function reorder_init() {
lis = document.getElementsBySelector('ul#orderthese li');
var input = document.getElementsBySelector('input[name=order_]')[0];
setOrder(input.value.split(','));
input.disabled = true;
draw();
// Now initialise the dragging behaviour
var limit = (lis.length - 1) * height;
for (var i = 0; i < lis.length; i++) {
var li = lis[i];
var img = document.getElementById('handle'+li.id);
li.style.zIndex = 1;
Drag.init(img, li, left + 10, left + 10, top + 10, top + 10 + limit);
li.onDragStart = startDrag;
li.onDragEnd = endDrag;
img.style.cursor = 'move';
}
}
function submitOrderForm() {
var inputOrder = document.getElementsBySelector('input[name=order_]')[0];
inputOrder.value = getOrder();
inputOrder.disabled=false;
}
function startDrag() {
this.style.zIndex = '10';
this.className = 'dragging';
}
function endDrag(x, y) {
this.style.zIndex = '1';
this.className = '';
// Work out how far along it has been dropped, using x co-ordinate
var oldIndex = this.index;
var newIndex = Math.round((y - 10 - top) / height);
// 'Snap' to the correct position
this.style.top = (10 + top + newIndex * height) + 'px';
this.index = newIndex;
moveItem(oldIndex, newIndex);
}
function moveItem(oldIndex, newIndex) {
// Swaps two items, adjusts the index and left co-ord for all others
if (oldIndex == newIndex) {
return; // Nothing to swap;
}
var direction, lo, hi;
if (newIndex > oldIndex) {
lo = oldIndex;
hi = newIndex;
direction = -1;
} else {
direction = 1;
hi = oldIndex;
lo = newIndex;
}
var lis2 = new Array(); // We will build the new order in this array
for (var i = 0; i < lis.length; i++) {
if (i < lo || i > hi) {
// Position of items not between the indexes is unaffected
lis2[i] = lis[i];
continue;
} else if (i == newIndex) {
lis2[i] = lis[oldIndex];
continue;
} else {
// Item is between the two indexes - move it along 1
lis2[i] = lis[i - direction];
}
}
// Re-index everything
reIndex(lis2);
lis = lis2;
draw();
// document.getElementById('hiddenOrder').value = getOrder();
document.getElementsBySelector('input[name=order_]')[0].value = getOrder();
}
function reIndex(lis) {
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
}
}
function draw() {
for (var i = 0; i < lis.length; i++) {
var li = lis[i];
li.index = i;
li.style.position = 'absolute';
li.style.left = (10 + left) + 'px';
li.style.top = (10 + top + (i * height)) + 'px';
}
}
function getOrder() {
var order = new Array(lis.length);
for (var i = 0; i < lis.length; i++) {
order[i] = lis[i].id.substring(1, 100);
}
return order.join(',');
}
function setOrder(id_list) {
/* Set the current order to match the lsit of IDs */
var temp_lis = new Array();
for (var i = 0; i < id_list.length; i++) {
var id = 'p' + id_list[i];
temp_lis[temp_lis.length] = document.getElementById(id);
}
reIndex(temp_lis);
lis = temp_lis;
draw();
}
function addEvent(elm, evType, fn, useCapture)
// addEvent and removeEvent
// cross-browser event handling for IE5+, NS6 and Mozilla
// By Scott Andrew
{
if (elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent){
var r = elm.attachEvent("on"+evType, fn);
return r;
} else {
elm['on'+evType] = fn;
}
}
/*
calendar.js - Calendar functions by Adrian Holovaty
*/
function removeChildren(a) { // "a" is reference to an object
while (a.hasChildNodes()) a.removeChild(a.lastChild);
}
// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]);
function quickElement() {
var obj = document.createElement(arguments[0]);
if (arguments[2] != '' && arguments[2] != null) {
var textNode = document.createTextNode(arguments[2]);
obj.appendChild(textNode);
}
var len = arguments.length;
for (var i = 3; i < len; i += 2) {
obj.setAttribute(arguments[i], arguments[i+1]);
}
arguments[1].appendChild(obj);
return obj;
}
// CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
var CalendarNamespace = {
monthsOfYear: gettext('January February March April May June July August September October November December').split(' '),
daysOfWeek: gettext('S M T W T F S').split(' '),
firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
isLeapYear: function(year) {
return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0));
},
getDaysInMonth: function(month,year) {
var days;
if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) {
days = 31;
}
else if (month==4 || month==6 || month==9 || month==11) {
days = 30;
}
else if (month==2 && CalendarNamespace.isLeapYear(year)) {
days = 29;
}
else {
days = 28;
}
return days;
},
draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999
var today = new Date();
var todayDay = today.getDate();
var todayMonth = today.getMonth()+1;
var todayYear = today.getFullYear();
var todayClass = '';
month = parseInt(month);
year = parseInt(year);
var calDiv = document.getElementById(div_id);
removeChildren(calDiv);
var calTable = document.createElement('table');
quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year);
var tableBody = quickElement('tbody', calTable);
// Draw days-of-week header
var tableRow = quickElement('tr', tableBody);
for (var i = 0; i < 7; i++) {
quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);
}
var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
var days = CalendarNamespace.getDaysInMonth(month, year);
// Draw blanks before first of month
tableRow = quickElement('tr', tableBody);
for (var i = 0; i < startingPos; i++) {
var _cell = quickElement('td', tableRow, ' ');
_cell.style.backgroundColor = '#f3f3f3';
}
// Draw days of month
var currentDay = 1;
for (var i = startingPos; currentDay <= days; i++) {
if (i%7 == 0 && currentDay != 1) {
tableRow = quickElement('tr', tableBody);
}
if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) {
todayClass='today';
} else {
todayClass='';
}
var cell = quickElement('td', tableRow, '', 'class', todayClass);
quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));');
currentDay++;
}
// Draw blanks after end of month (optional, but makes for valid code)
while (tableRow.childNodes.length < 7) {
var _cell = quickElement('td', tableRow, ' ');
_cell.style.backgroundColor = '#f3f3f3';
}
calDiv.appendChild(calTable);
}
}
// Calendar -- A calendar instance
function Calendar(div_id, callback) {
// div_id (string) is the ID of the element in which the calendar will
// be displayed
// callback (string) is the name of a JavaScript function that will be
// called with the parameters (year, month, day) when a day in the
// calendar is clicked
this.div_id = div_id;
this.callback = callback;
this.today = new Date();
this.currentMonth = this.today.getMonth() + 1;
this.currentYear = this.today.getFullYear();
}
Calendar.prototype = {
drawCurrent: function() {
CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback);
},
drawDate: function(month, year) {
this.currentMonth = month;
this.currentYear = year;
this.drawCurrent();
},
drawPreviousMonth: function() {
if (this.currentMonth == 1) {
this.currentMonth = 12;
this.currentYear--;
}
else {
this.currentMonth--;
}
this.drawCurrent();
},
drawNextMonth: function() {
if (this.currentMonth == 12) {
this.currentMonth = 1;
this.currentYear++;
}
else {
this.currentMonth++;
}
this.drawCurrent();
},
drawPreviousYear: function() {
this.currentYear--;
this.drawCurrent();
},
drawNextYear: function() {
this.currentYear++;
this.drawCurrent();
}
}
(function($) {
$(document).ready(function() {
// Add anchor tag for Show/Hide link
$("fieldset.collapse").each(function(i, elem) {
// Don't hide if fields in this fieldset have errors
if ( $(elem).find("div.errors").length == 0 ) {
$(elem).addClass("collapsed");
$(elem).find("h2").first().append(' (<a id="fieldsetcollapser' +
i +'" class="collapse-toggle" href="#">' + gettext("Show") +
'</a>)');
}
});
// Add toggle to anchor tag
$("fieldset.collapse a.collapse-toggle").toggle(
function() { // Show
$(this).text(gettext("Hide"));
$(this).closest("fieldset").removeClass("collapsed");
return false;
},
function() { // Hide
$(this).text(gettext("Show"));
$(this).closest("fieldset").addClass("collapsed");
return false;
}
);
});
})(django.jQuery);
(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){if(a(b).find("div.errors").length==0){a(b).addClass("collapsed");a(b).find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")}});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide"));a(this).closest("fieldset").removeClass("collapsed");return false},function(){a(this).text(gettext("Show"));a(this).closest("fieldset").addClass("collapsed");
return false})})})(django.jQuery);
#!/usr/bin/env python
import os
import optparse
import subprocess
import sys
here = os.path.dirname(__file__)
def main():
usage = "usage: %prog [file1..fileN]"
description = """With no file paths given this script will automatically
compress all jQuery-based files of the admin app. Requires the Google Closure
Compiler library and Java version 6 or later."""
parser = optparse.OptionParser(usage, description=description)
parser.add_option("-c", dest="compiler", default="~/bin/compiler.jar",
help="path to Closure Compiler jar file")
parser.add_option("-v", "--verbose",
action="store_true", dest="verbose")
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose")
(options, args) = parser.parse_args()
compiler = os.path.expanduser(options.compiler)
if not os.path.exists(compiler):
sys.exit("Google Closure compiler jar file %s not found. Please use the -c option to specify the path." % compiler)
if not args:
if options.verbose:
sys.stdout.write("No filenames given; defaulting to admin scripts\n")
args = [os.path.join(here, f) for f in [
"actions.js", "collapse.js", "inlines.js", "prepopulate.js"]]
for arg in args:
if not arg.endswith(".js"):
arg = arg + ".js"
to_compress = os.path.expanduser(arg)
if os.path.exists(to_compress):
to_compress_min = "%s.min.js" % "".join(arg.rsplit(".js"))
cmd = "java -jar %s --js %s --js_output_file %s" % (compiler, to_compress, to_compress_min)
if options.verbose:
sys.stdout.write("Running: %s\n" % cmd)
subprocess.call(cmd.split())
else:
sys.stdout.write("File %s not found. Sure it exists?\n" % to_compress)
if __name__ == '__main__':
main()
// Core javascript helper functions
// basic browser identification & version
var isOpera = (navigator.userAgent.indexOf("Opera")>=0) && parseFloat(navigator.appVersion);
var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]);
// Cross-browser event handlers.
function addEvent(obj, evType, fn) {
if (obj.addEventListener) {
obj.addEventListener(evType, fn, false);
return true;
} else if (obj.attachEvent) {
var r = obj.attachEvent("on" + evType, fn);
return r;
} else {
return false;
}
}
function removeEvent(obj, evType, fn) {
if (obj.removeEventListener) {
obj.removeEventListener(evType, fn, false);
return true;
} else if (obj.detachEvent) {
obj.detachEvent("on" + evType, fn);
return true;
} else {
return false;
}
}
// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]);
function quickElement() {
var obj = document.createElement(arguments[0]);
if (arguments[2] != '' && arguments[2] != null) {
var textNode = document.createTextNode(arguments[2]);
obj.appendChild(textNode);
}
var len = arguments.length;
for (var i = 3; i < len; i += 2) {
obj.setAttribute(arguments[i], arguments[i+1]);
}
arguments[1].appendChild(obj);
return obj;
}
// ----------------------------------------------------------------------------
// Cross-browser xmlhttp object
// from http://jibbering.com/2002/4/httprequest.html
// ----------------------------------------------------------------------------
var xmlhttp;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
xmlhttp = false;
}
}
@else
xmlhttp = false;
@end @*/
if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
xmlhttp = new XMLHttpRequest();
}
// ----------------------------------------------------------------------------
// Find-position functions by PPK
// See http://www.quirksmode.org/js/findpos.html
// ----------------------------------------------------------------------------
function findPosX(obj) {
var curleft = 0;
if (obj.offsetParent) {
while (obj.offsetParent) {
curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft);
obj = obj.offsetParent;
}
// IE offsetParent does not include the top-level
if (isIE && obj.parentElement){
curleft += obj.offsetLeft - obj.scrollLeft;
}
} else if (obj.x) {
curleft += obj.x;
}
return curleft;
}
function findPosY(obj) {
var curtop = 0;
if (obj.offsetParent) {
while (obj.offsetParent) {
curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop);
obj = obj.offsetParent;
}
// IE offsetParent does not include the top-level
if (isIE && obj.parentElement){
curtop += obj.offsetTop - obj.scrollTop;
}
} else if (obj.y) {
curtop += obj.y;
}
return curtop;
}
//-----------------------------------------------------------------------------
// Date object extensions
// ----------------------------------------------------------------------------
Date.prototype.getCorrectYear = function() {
// Date.getYear() is unreliable --
// see http://www.quirksmode.org/js/introdate.html#year
var y = this.getYear() % 100;
return (y < 38) ? y + 2000 : y + 1900;
}
Date.prototype.getTwelveHours = function() {
hours = this.getHours();
if (hours == 0) {
return 12;
}
else {
return hours <= 12 ? hours : hours-12
}
}
Date.prototype.getTwoDigitMonth = function() {
return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1);
}
Date.prototype.getTwoDigitDate = function() {
return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
}
Date.prototype.getTwoDigitTwelveHour = function() {
return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();
}
Date.prototype.getTwoDigitHour = function() {
return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
}
Date.prototype.getTwoDigitMinute = function() {
return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
}
Date.prototype.getTwoDigitSecond = function() {
return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
}
Date.prototype.getISODate = function() {
return this.getCorrectYear() + '-' + this.getTwoDigitMonth() + '-' + this.getTwoDigitDate();
}
Date.prototype.getHourMinute = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();
}
Date.prototype.getHourMinuteSecond = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
}
Date.prototype.strftime = function(format) {
var fields = {
c: this.toString(),
d: this.getTwoDigitDate(),
H: this.getTwoDigitHour(),
I: this.getTwoDigitTwelveHour(),
m: this.getTwoDigitMonth(),
M: this.getTwoDigitMinute(),
p: (this.getHours() >= 12) ? 'PM' : 'AM',
S: this.getTwoDigitSecond(),
w: '0' + this.getDay(),
x: this.toLocaleDateString(),
X: this.toLocaleTimeString(),
y: ('' + this.getFullYear()).substr(2, 4),
Y: '' + this.getFullYear(),
'%' : '%'
};
var result = '', i = 0;
while (i < format.length) {
if (format.charAt(i) === '%') {
result = result + fields[format.charAt(i + 1)];
++i;
}
else {
result = result + format.charAt(i);
}
++i;
}
return result;
}
// ----------------------------------------------------------------------------
// String object extensions
// ----------------------------------------------------------------------------
String.prototype.pad_left = function(pad_length, pad_string) {
var new_string = this;
for (var i = 0; new_string.length < pad_length; i++) {
new_string = pad_string + new_string;
}
return new_string;
}
// ----------------------------------------------------------------------------
// Get the computed style for and element
// ----------------------------------------------------------------------------
function getStyle(oElm, strCssRule){
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}
/* 'Magic' date parsing, by Simon Willison (6th October 2003)
http://simon.incutio.com/archive/2003/10/06/betterDateInput
Adapted for 6newslawrence.com, 28th January 2004
*/
/* Finds the index of the first occurence of item in the array, or -1 if not found */
if (typeof Array.prototype.indexOf == 'undefined') {
Array.prototype.indexOf = function(item) {
var len = this.length;
for (var i = 0; i < len; i++) {
if (this[i] == item) {
return i;
}
}
return -1;
};
}
/* Returns an array of items judged 'true' by the passed in test function */
if (typeof Array.prototype.filter == 'undefined') {
Array.prototype.filter = function(test) {
var matches = [];
var len = this.length;
for (var i = 0; i < len; i++) {
if (test(this[i])) {
matches[matches.length] = this[i];
}
}
return matches;
};
}
var monthNames = gettext("January February March April May June July August September October November December").split(" ");
var weekdayNames = gettext("Sunday Monday Tuesday Wednesday Thursday Friday Saturday").split(" ");
/* Takes a string, returns the index of the month matching that string, throws
an error if 0 or more than 1 matches
*/
function parseMonth(month) {
var matches = monthNames.filter(function(item) {
return new RegExp("^" + month, "i").test(item);
});
if (matches.length == 0) {
throw new Error("Invalid month string");
}
if (matches.length > 1) {
throw new Error("Ambiguous month");
}
return monthNames.indexOf(matches[0]);
}
/* Same as parseMonth but for days of the week */
function parseWeekday(weekday) {
var matches = weekdayNames.filter(function(item) {
return new RegExp("^" + weekday, "i").test(item);
});
if (matches.length == 0) {
throw new Error("Invalid day string");
}
if (matches.length > 1) {
throw new Error("Ambiguous weekday");
}
return weekdayNames.indexOf(matches[0]);
}
/* Array of objects, each has 're', a regular expression and 'handler', a
function for creating a date from something that matches the regular
expression. Handlers may throw errors if string is unparseable.
*/
var dateParsePatterns = [
// Today
{ re: /^tod/i,
handler: function() {
return new Date();
}
},
// Tomorrow
{ re: /^tom/i,
handler: function() {
var d = new Date();
d.setDate(d.getDate() + 1);
return d;
}
},
// Yesterday
{ re: /^yes/i,
handler: function() {
var d = new Date();
d.setDate(d.getDate() - 1);
return d;
}
},
// 4th
{ re: /^(\d{1,2})(st|nd|rd|th)?$/i,
handler: function(bits) {
var d = new Date();
d.setDate(parseInt(bits[1], 10));
return d;
}
},
// 4th Jan
{ re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i,
handler: function(bits) {
var d = new Date();
d.setDate(1);
d.setMonth(parseMonth(bits[2]));
d.setDate(parseInt(bits[1], 10));
return d;
}
},
// 4th Jan 2003
{ re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i,
handler: function(bits) {
var d = new Date();
d.setDate(1);
d.setYear(bits[3]);
d.setMonth(parseMonth(bits[2]));
d.setDate(parseInt(bits[1], 10));
return d;
}
},
// Jan 4th
{ re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?$/i,
handler: function(bits) {
var d = new Date();
d.setDate(1);
d.setMonth(parseMonth(bits[1]));
d.setDate(parseInt(bits[2], 10));
return d;
}
},
// Jan 4th 2003
{ re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,
handler: function(bits) {
var d = new Date();
d.setDate(1);
d.setYear(bits[3]);
d.setMonth(parseMonth(bits[1]));
d.setDate(parseInt(bits[2], 10));
return d;
}
},
// next Tuesday - this is suspect due to weird meaning of "next"
{ re: /^next (\w+)$/i,
handler: function(bits) {
var d = new Date();
var day = d.getDay();
var newDay = parseWeekday(bits[1]);
var addDays = newDay - day;
if (newDay <= day) {
addDays += 7;
}
d.setDate(d.getDate() + addDays);
return d;
}
},
// last Tuesday
{ re: /^last (\w+)$/i,
handler: function(bits) {
throw new Error("Not yet implemented");
}
},
// mm/dd/yyyy (American style)
{ re: /(\d{1,2})\/(\d{1,2})\/(\d{4})/,
handler: function(bits) {
var d = new Date();
d.setDate(1);
d.setYear(bits[3]);
d.setMonth(parseInt(bits[1], 10) - 1); // Because months indexed from 0
d.setDate(parseInt(bits[2], 10));
return d;
}
},
// yyyy-mm-dd (ISO style)
{ re: /(\d{4})-(\d{1,2})-(\d{1,2})/,
handler: function(bits) {
var d = new Date();
d.setDate(1);
d.setYear(parseInt(bits[1]));
d.setMonth(parseInt(bits[2], 10) - 1);
d.setDate(parseInt(bits[3], 10));
return d;
}
},
];
function parseDateString(s) {
for (var i = 0; i < dateParsePatterns.length; i++) {
var re = dateParsePatterns[i].re;
var handler = dateParsePatterns[i].handler;
var bits = re.exec(s);
if (bits) {
return handler(bits);
}
}
throw new Error("Invalid date string");
}
function fmt00(x) {
// fmt00: Tags leading zero onto numbers 0 - 9.
// Particularly useful for displaying results from Date methods.
//
if (Math.abs(parseInt(x)) < 10){
x = "0"+ Math.abs(x);
}
return x;
}
function parseDateStringISO(s) {
try {
var d = parseDateString(s);
return d.getFullYear() + '-' + (fmt00(d.getMonth() + 1)) + '-' + fmt00(d.getDate())
}
catch (e) { return s; }
}
function magicDate(input) {
var messagespan = input.id + 'Msg';
try {
var d = parseDateString(input.value);
input.value = d.getFullYear() + '-' + (fmt00(d.getMonth() + 1)) + '-' +
fmt00(d.getDate());
input.className = '';
// Human readable date
if (document.getElementById(messagespan)) {
document.getElementById(messagespan).firstChild.nodeValue = d.toDateString();
document.getElementById(messagespan).className = 'normal';
}
}
catch (e) {
input.className = 'error';
var message = e.message;
// Fix for IE6 bug
if (message.indexOf('is null or not an object') > -1) {
message = 'Invalid date string';
}
if (document.getElementById(messagespan)) {
document.getElementById(messagespan).firstChild.nodeValue = message;
document.getElementById(messagespan).className = 'error';
}
}
}
/* document.getElementsBySelector(selector)
- returns an array of element objects from the current document
matching the CSS selector. Selectors can contain element names,
class names and ids and can be nested. For example:
elements = document.getElementsBySelect('div#main p a.external')
Will return an array of all 'a' elements with 'external' in their
class attribute that are contained inside 'p' elements that are
contained inside the 'div' element which has id="main"
New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
See http://www.w3.org/TR/css3-selectors/#attribute-selectors
Version 0.4 - Simon Willison, March 25th 2003
-- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
-- Opera 7 fails
*/
function getAllChildren(e) {
// Returns all children of element. Workaround required for IE5/Windows. Ugh.
return e.all ? e.all : e.getElementsByTagName('*');
}
document.getElementsBySelector = function(selector) {
// Attempt to fail gracefully in lesser browsers
if (!document.getElementsByTagName) {
return new Array();
}
// Split selector in to tokens
var tokens = selector.split(' ');
var currentContext = new Array(document);
for (var i = 0; i < tokens.length; i++) {
token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
if (token.indexOf('#') > -1) {
// Token is an ID selector
var bits = token.split('#');
var tagName = bits[0];
var id = bits[1];
var element = document.getElementById(id);
if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) {
// ID not found or tag with that ID not found, return false.
return new Array();
}
// Set currentContext to contain just this element
currentContext = new Array(element);
continue; // Skip to next token
}
if (token.indexOf('.') > -1) {
// Token contains a class selector
var bits = token.split('.');
var tagName = bits[0];
var className = bits[1];
if (!tagName) {
tagName = '*';
}
// Get elements matching tag, filter them for class selector
var found = new Array;
var foundCount = 0;
for (var h = 0; h < currentContext.length; h++) {
var elements;
if (tagName == '*') {
elements = getAllChildren(currentContext[h]);
} else {
try {
elements = currentContext[h].getElementsByTagName(tagName);
}
catch(e) {
elements = [];
}
}
for (var j = 0; j < elements.length; j++) {
found[foundCount++] = elements[j];
}
}
currentContext = new Array;
var currentContextIndex = 0;
for (var k = 0; k < found.length; k++) {
if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
currentContext[currentContextIndex++] = found[k];
}
}
continue; // Skip to next token
}
// Code to deal with attribute selectors
if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
var tagName = RegExp.$1;
var attrName = RegExp.$2;
var attrOperator = RegExp.$3;
var attrValue = RegExp.$4;
if (!tagName) {
tagName = '*';
}
// Grab all of the tagName elements within current context
var found = new Array;
var foundCount = 0;
for (var h = 0; h < currentContext.length; h++) {
var elements;
if (tagName == '*') {
elements = getAllChildren(currentContext[h]);
} else {
elements = currentContext[h].getElementsByTagName(tagName);
}
for (var j = 0; j < elements.length; j++) {
found[foundCount++] = elements[j];
}
}
currentContext = new Array;
var currentContextIndex = 0;
var checkFunction; // This function will be used to filter the elements
switch (attrOperator) {
case '=': // Equality
checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
break;
case '~': // Match one of space seperated words
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
break;
case '|': // Match start with value followed by optional hyphen
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
break;
case '^': // Match starts with value
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
break;
case '$': // Match ends with value - fails with "Warning" in Opera 7
checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
break;
case '*': // Match ends with value
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
break;
default :
// Just test for existence of attribute
checkFunction = function(e) { return e.getAttribute(attrName); };
}
currentContext = new Array;
var currentContextIndex = 0;
for (var k = 0; k < found.length; k++) {
if (checkFunction(found[k])) {
currentContext[currentContextIndex++] = found[k];
}
}
// alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
continue; // Skip to next token
}
// If we get here, token is JUST an element (not a class or ID selector)
tagName = token;
var found = new Array;
var foundCount = 0;
for (var h = 0; h < currentContext.length; h++) {
var elements = currentContext[h].getElementsByTagName(tagName);
for (var j = 0; j < elements.length; j++) {
found[foundCount++] = elements[j];
}
}
currentContext = found;
}
return currentContext;
}
/* That revolting regular expression explained
/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
\---/ \---/\-------------/ \-------/
| | | |
| | | The value
| | ~,|,^,$,* or =
| Attribute
Tag
*/
/**
* Django admin inlines
*
* Based on jQuery Formset 1.1
* @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
* @requires jQuery 1.2.6 or later
*
* Copyright (c) 2009, Stanislaus Madueke
* All rights reserved.
*
* Spiced up with Code from Zain Memon's GSoC project 2009
* and modified for Django by Jannis Leidel
*
* Licensed under the New BSD License
* See: http://www.opensource.org/licenses/bsd-license.php
*/
(function($) {
$.fn.formset = function(opts) {
var options = $.extend({}, $.fn.formset.defaults, opts);
var updateElementIndex = function(el, prefix, ndx) {
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
var replacement = prefix + "-" + ndx;
if ($(el).attr("for")) {
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
}
if (el.id) {
el.id = el.id.replace(id_regex, replacement);
}
if (el.name) {
el.name = el.name.replace(id_regex, replacement);
}
};
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
var nextIndex = parseInt(totalForms.val());
var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
// only show the add button if we are allowed to add more items,
// note that max_num = None translates to a blank string.
var showAddButton = maxForms.val() == '' || (maxForms.val()-totalForms.val()) > 0;
$(this).each(function(i) {
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
});
if ($(this).length && showAddButton) {
var addButton;
if ($(this).attr("tagName") == "TR") {
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
var numCols = this.eq(0).children().length;
$(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
addButton = $(this).parent().find("tr:last a");
} else {
// Otherwise, insert it immediately after the last form:
$(this).filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
addButton = $(this).filter(":last").next().find("a");
}
addButton.click(function() {
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
var template = $("#" + options.prefix + "-empty");
var row = template.clone(true);
row.removeClass(options.emptyCssClass)
.addClass(options.formCssClass)
.attr("id", options.prefix + "-" + nextIndex);
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
} else if (row.is("ul") || row.is("ol")) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
}
row.find("*").each(function() {
updateElementIndex(this, options.prefix, totalForms.val());
});
// Insert the new form when it has been fully edited
row.insertBefore($(template));
// Update number of total forms
$(totalForms).val(parseInt(totalForms.val()) + 1);
nextIndex += 1;
// Hide add button in case we've hit the max, except we want to add infinitely
if ((maxForms.val() != '') && (maxForms.val()-totalForms.val()) <= 0) {
addButton.parent().hide();
}
// The delete button of each row triggers a bunch of other things
row.find("a." + options.deleteCssClass).click(function() {
// Remove the parent form containing this button:
var row = $(this).parents("." + options.formCssClass);
row.remove();
nextIndex -= 1;
// If a post-delete callback was provided, call it with the deleted form:
if (options.removed) {
options.removed(row);
}
// Update the TOTAL_FORMS form count.
var forms = $("." + options.formCssClass);
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
// Show add button again once we drop below max
if ((maxForms.val() == '') || (maxForms.val()-forms.length) > 0) {
addButton.parent().show();
}
// Also, update names and ids for all remaining form controls
// so they remain in sequence:
for (var i=0, formCount=forms.length; i<formCount; i++)
{
updateElementIndex($(forms).get(i), options.prefix, i);
$(forms.get(i)).find("*").each(function() {
updateElementIndex(this, options.prefix, i);
});
}
return false;
});
// If a post-add callback was supplied, call it with the added form:
if (options.added) {
options.added(row);
}
return false;
});
}
return this;
}
/* Setup plugin defaults */
$.fn.formset.defaults = {
prefix: "form", // The form prefix for your django formset
addText: "add another", // Text for the add link
deleteText: "remove", // Text for the delete link
addCssClass: "add-row", // CSS class applied to the add link
deleteCssClass: "delete-row", // CSS class applied to the delete link
emptyCssClass: "empty-row", // CSS class applied to the empty row
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
added: null, // Function called each time a new form is added
removed: null // Function called each time a form is deleted
}
})(django.jQuery);
(function(b){b.fn.formset=function(g){var a=b.extend({},b.fn.formset.defaults,g),k=function(c,f,d){var e=new RegExp("("+f+"-(\\d+|__prefix__))");f=f+"-"+d;b(c).attr("for")&&b(c).attr("for",b(c).attr("for").replace(e,f));if(c.id)c.id=c.id.replace(e,f);if(c.name)c.name=c.name.replace(e,f)};g=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off");var l=parseInt(g.val()),h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off");g=h.val()==""||h.val()-g.val()>0;b(this).each(function(){b(this).not("."+
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&g){var j;if(b(this).attr("tagName")=="TR"){g=this.eq(0).children().length;b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+g+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>");j=b(this).parent().find("tr:last a")}else{b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>");j=b(this).filter(":last").next().find("a")}j.click(function(){var c=b("#id_"+
a.prefix+"-TOTAL_FORMS"),f=b("#"+a.prefix+"-empty"),d=f.clone(true);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);if(d.is("tr"))d.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>");else d.is("ul")||d.is("ol")?d.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):d.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
a.deleteText+"</a></span>");d.find("*").each(function(){k(this,a.prefix,c.val())});d.insertBefore(b(f));b(c).val(parseInt(c.val())+1);l+=1;h.val()!=""&&h.val()-c.val()<=0&&j.parent().hide();d.find("a."+a.deleteCssClass).click(function(){var e=b(this).parents("."+a.formCssClass);e.remove();l-=1;a.removed&&a.removed(e);e=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(e.length);if(h.val()==""||h.val()-e.length>0)j.parent().show();for(var i=0,m=e.length;i<m;i++){k(b(e).get(i),a.prefix,i);
b(e.get(i)).find("*").each(function(){k(this,a.prefix,i)})}return false});a.added&&a.added(d);return false})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);
// Puts the included jQuery into our own namespace
var django = {
"jQuery": jQuery.noConflict(true)
};
This source diff could not be displayed because it is too large. You can view the blob instead.
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Sat Feb 13 22:33:48 2010 -0500
*/
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
(function($) {
$.fn.prepopulate = function(dependencies, maxLength) {
/*
Depends on urlify.js
Populates a selected field with the values of the dependent fields,
URLifies and shortens the string.
dependencies - array of dependent fields id's
maxLength - maximum length of the URLify'd string
*/
return this.each(function() {
var field = $(this);
field.data('_changed', false);
field.change(function() {
field.data('_changed', true);
});
var populate = function () {
// Bail if the fields value has changed
if (field.data('_changed') == true) return;
var values = [];
$.each(dependencies, function(i, field) {
if ($(field).val().length > 0) {
values.push($(field).val());
}
})
field.val(URLify(values.join(' '), maxLength));
};
$(dependencies.join(',')).keyup(populate).change(populate).focus(populate);
});
};
})(django.jQuery);
(function(a){a.fn.prepopulate=function(d,g){return this.each(function(){var b=a(this);b.data("_changed",false);b.change(function(){b.data("_changed",true)});var c=function(){if(b.data("_changed")!=true){var e=[];a.each(d,function(h,f){a(f).val().length>0&&e.push(a(f).val())});b.val(URLify(e.join(" "),g))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery);
var timeParsePatterns = [
// 9
{ re: /^\d{1,2}$/i,
handler: function(bits) {
if (bits[0].length == 1) {
return '0' + bits[0] + ':00';
} else {
return bits[0] + ':00';
}
}
},
// 13:00
{ re: /^\d{2}[:.]\d{2}$/i,
handler: function(bits) {
return bits[0].replace('.', ':');
}
},
// 9:00
{ re: /^\d[:.]\d{2}$/i,
handler: function(bits) {
return '0' + bits[0].replace('.', ':');
}
},
// 3 am / 3 a.m. / 3am
{ re: /^(\d+)\s*([ap])(?:.?m.?)?$/i,
handler: function(bits) {
var hour = parseInt(bits[1]);
if (hour == 12) {
hour = 0;
}
if (bits[2].toLowerCase() == 'p') {
if (hour == 12) {
hour = 0;
}
return (hour + 12) + ':00';
} else {
if (hour < 10) {
return '0' + hour + ':00';
} else {
return hour + ':00';
}
}
}
},
// 3.30 am / 3:15 a.m. / 3.00am
{ re: /^(\d+)[.:](\d{2})\s*([ap]).?m.?$/i,
handler: function(bits) {
var hour = parseInt(bits[1]);
var mins = parseInt(bits[2]);
if (mins < 10) {
mins = '0' + mins;
}
if (hour == 12) {
hour = 0;
}
if (bits[3].toLowerCase() == 'p') {
if (hour == 12) {
hour = 0;
}
return (hour + 12) + ':' + mins;
} else {
if (hour < 10) {
return '0' + hour + ':' + mins;
} else {
return hour + ':' + mins;
}
}
}
},
// noon
{ re: /^no/i,
handler: function(bits) {
return '12:00';
}
},
// midnight
{ re: /^mid/i,
handler: function(bits) {
return '00:00';
}
}
];
function parseTimeString(s) {
for (var i = 0; i < timeParsePatterns.length; i++) {
var re = timeParsePatterns[i].re;
var handler = timeParsePatterns[i].handler;
var bits = re.exec(s);
if (bits) {
return handler(bits);
}
}
return s;
}
var LATIN_MAP = {
'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç':
'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I',
'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö':
'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U',
'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 'a', 'ä':
'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e',
'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó':
'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u',
'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y'
}
var LATIN_SYMBOLS_MAP = {
'©':'(c)'
}
var GREEK_MAP = {
'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8',
'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p',
'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w',
'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s',
'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i',
'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8',
'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P',
'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W',
'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I',
'Ϋ':'Y'
}
var TURKISH_MAP = {
'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U',
'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G'
}
var RUSSIAN_MAP = {
'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh',
'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o',
'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c',
'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu',
'я':'ya',
'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh',
'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O',
'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C',
'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu',
'Я':'Ya'
}
var UKRAINIAN_MAP = {
'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g'
}
var CZECH_MAP = {
'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',
'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T',
'Ů':'U', 'Ž':'Z'
}
var POLISH_MAP = {
'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z',
'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S',
'Ź':'Z', 'Ż':'Z'
}
var LATVIAN_MAP = {
'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n',
'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i',
'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z'
}
var ALL_DOWNCODE_MAPS=new Array()
ALL_DOWNCODE_MAPS[0]=LATIN_MAP
ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP
ALL_DOWNCODE_MAPS[2]=GREEK_MAP
ALL_DOWNCODE_MAPS[3]=TURKISH_MAP
ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP
ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP
ALL_DOWNCODE_MAPS[6]=CZECH_MAP
ALL_DOWNCODE_MAPS[7]=POLISH_MAP
ALL_DOWNCODE_MAPS[8]=LATVIAN_MAP
var Downcoder = new Object();
Downcoder.Initialize = function()
{
if (Downcoder.map) // already made
return ;
Downcoder.map ={}
Downcoder.chars = '' ;
for(var i in ALL_DOWNCODE_MAPS)
{
var lookup = ALL_DOWNCODE_MAPS[i]
for (var c in lookup)
{
Downcoder.map[c] = lookup[c] ;
Downcoder.chars += c ;
}
}
Downcoder.regex = new RegExp('[' + Downcoder.chars + ']|[^' + Downcoder.chars + ']+','g') ;
}
downcode= function( slug )
{
Downcoder.Initialize() ;
var downcoded =""
var pieces = slug.match(Downcoder.regex);
if(pieces)
{
for (var i = 0 ; i < pieces.length ; i++)
{
if (pieces[i].length == 1)
{
var mapped = Downcoder.map[pieces[i]] ;
if (mapped != null)
{
downcoded+=mapped;
continue ;
}
}
downcoded+=pieces[i];
}
}
else
{
downcoded = slug;
}
return downcoded;
}
function URLify(s, num_chars) {
// changes, e.g., "Petty theft" to "petty_theft"
// remove all these words from the string before urlifying
s = downcode(s);
removelist = ["a", "an", "as", "at", "before", "but", "by", "for", "from",
"is", "in", "into", "like", "of", "off", "on", "onto", "per",
"since", "than", "the", "this", "that", "to", "up", "via",
"with"];
r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi');
s = s.replace(r, '');
// if downcode doesn't hit, the char will be stripped here
s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars
s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens
s = s.toLowerCase(); // convert to lowercase
return s.substring(0, num_chars);// trim to first num_chars chars
}
.CodeMirror {
line-height: 1em;
font-family: monospace;
}
.CodeMirror-scroll {
overflow: auto;
height: 300px;
/* This is needed to prevent an IE[67] bug where the scrolled content
is visible outside of the scrolling box. */
position: relative;
outline: none;
}
.CodeMirror-gutter {
position: absolute; left: 0; top: 0;
z-index: 10;
background-color: #f7f7f7;
border-right: 1px solid #eee;
min-width: 2em;
height: 100%;
}
.CodeMirror-gutter-text {
color: #aaa;
text-align: right;
padding: .4em .2em .4em .4em;
white-space: pre !important;
}
.CodeMirror-lines {
padding: .4em;
white-space: pre;
}
.CodeMirror pre {
-moz-border-radius: 0;
-webkit-border-radius: 0;
-o-border-radius: 0;
border-radius: 0;
border-width: 0; margin: 0; padding: 0; background: transparent;
font-family: inherit;
font-size: inherit;
padding: 0; margin: 0;
white-space: pre;
word-wrap: normal;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror textarea {
outline: none !important;
}
.CodeMirror pre.CodeMirror-cursor {
z-index: 10;
position: absolute;
visibility: hidden;
border-left: 1px solid black;
border-right:none;
width:0;
}
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
.CodeMirror-focused pre.CodeMirror-cursor {
visibility: visible;
}
div.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Default theme */
.cm-s-default span.cm-keyword {color: #708;}
.cm-s-default span.cm-atom {color: #219;}
.cm-s-default span.cm-number {color: #164;}
.cm-s-default span.cm-def {color: #00f;}
.cm-s-default span.cm-variable {color: black;}
.cm-s-default span.cm-variable-2 {color: #05a;}
.cm-s-default span.cm-variable-3 {color: #085;}
.cm-s-default span.cm-property {color: black;}
.cm-s-default span.cm-operator {color: black;}
.cm-s-default span.cm-comment {color: #a50;}
.cm-s-default span.cm-string {color: #a11;}
.cm-s-default span.cm-string-2 {color: #f50;}
.cm-s-default span.cm-meta {color: #555;}
.cm-s-default span.cm-error {color: #f00;}
.cm-s-default span.cm-qualifier {color: #555;}
.cm-s-default span.cm-builtin {color: #30a;}
.cm-s-default span.cm-bracket {color: #cc7;}
.cm-s-default span.cm-tag {color: #170;}
.cm-s-default span.cm-attribute {color: #00c;}
.cm-s-default span.cm-header {color: #a0a;}
.cm-s-default span.cm-quote {color: #090;}
.cm-s-default span.cm-hr {color: #999;}
.cm-s-default span.cm-link {color: #00c;}
span.cm-header, span.cm-strong {font-weight: bold;}
span.cm-em {font-style: italic;}
span.cm-emstrong {font-style: italic; font-weight: bold;}
span.cm-link {text-decoration: underline;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
...@@ -450,8 +450,9 @@ var AMsymbols = [ ...@@ -450,8 +450,9 @@ var AMsymbols = [
{input:"QQ", tag:"mo", output:"\u211A", tex:null, ttype:CONST}, {input:"QQ", tag:"mo", output:"\u211A", tex:null, ttype:CONST},
{input:"RR", tag:"mo", output:"\u211D", tex:null, ttype:CONST}, {input:"RR", tag:"mo", output:"\u211D", tex:null, ttype:CONST},
{input:"ZZ", tag:"mo", output:"\u2124", tex:null, ttype:CONST}, {input:"ZZ", tag:"mo", output:"\u2124", tex:null, ttype:CONST},
{input:"f", tag:"mi", output:"f", tex:null, ttype:UNARY, func:true}, // not for edX (ichuang jun12)
{input:"g", tag:"mi", output:"g", tex:null, ttype:UNARY, func:true}, //{input:"f", tag:"mi", output:"f", tex:null, ttype:UNARY, func:true},
//{input:"g", tag:"mi", output:"g", tex:null, ttype:UNARY, func:true},
//standard functions //standard functions
{input:"lim", tag:"mo", output:"lim", tex:null, ttype:UNDEROVER}, {input:"lim", tag:"mo", output:"lim", tex:null, ttype:UNDEROVER},
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<ul> <ul>
% for section in chapter['sections']: % for section in chapter['sections']:
<li${' class="active"' if 'active' in section and section['active'] else ''}> <li${' class="active"' if 'active' in section and section['active'] else ''}>
<a href="${reverse('courseware_section', args=format_url_params([course_name, chapter['name'], section['name']]))}"> <a href="${ MITX_ROOT_URL}${reverse('courseware_section', args=format_url_params([course_name, chapter['name'], section['name']]))}">
<p>${section['name']} <p>${section['name']}
<span class="subtitle"> <span class="subtitle">
${section['format']} ${"due " + section['due'] if 'due' in section and section['due'] != '' else ''} ${section['format']} ${"due " + section['due'] if 'due' in section and section['due'] != '' else ''}
...@@ -23,3 +23,9 @@ ...@@ -23,3 +23,9 @@
% for chapter in toc: % for chapter in toc:
${make_chapter(chapter)} ${make_chapter(chapter)}
% endfor % endfor
## add a link to allow course.xml to be reloaded from the git content repo
% if settings.QUICKEDIT:
<h3><a href="#">quickedit</a></h3>
<ul><li><a href="${MITX_ROOT_URL}/quickedit/course.xml">gitreload</a></li></ul>
% endif
<%namespace name='static' file='static_content.html'/>
<!DOCTYPE html>
<html>
## -----------------------------------------------------------------------------
## Template for lib.dogfood.views.dj_capa_problem
##
## Used for viewing assesment problems in "dogfood" self-evaluation mode
## -----------------------------------------------------------------------------
<head>
<link rel="stylesheet" href="${static.url('js/jquery.treeview.css')}" type="text/css" media="all" />
## <link rel="stylesheet" href="${ settings.LIB_URL }jquery.treeview.css" type="text/css" media="all" />
## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
## <%static:css group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
% endif
<script type="text/javascript" src="${static.url('js/jquery-1.6.2.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery-ui-1.8.16.custom.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/swfobject/swfobject.js')}"></script>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:js group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
% for jsfn in [ '/static/%s' % x.replace('.coffee','.js') for x in settings.PIPELINE_JS['application']['source_filenames'] ]:
<script type="text/javascript" src="${jsfn}"></script>
% endfor
% endif
## codemirror
<link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" />
<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script>
## alternate codemirror
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/lib/codemirror.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/xml/xml.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/python/python.js"></script>
## image input: for clicking on images (see imageinput.html)
<script type="text/javascript" src="/static/js/imageinput.js"></script>
<%include file="mathjax_include.html" />
</head>
<body class="courseware">
<!--[if lt IE 9]>
<script src="/static/js/html5shiv.js"></script>
<![endif]-->
<div class="courseware"></div>
## -----------------------------------------------------------------------------
## information
## <hr width="100%">
## <h2>Rendition of your problem code</h2>
## <hr width="100%">
## -----------------------------------------------------------------------------
## rendered problem display
<script>
${init_js}
</script>
<style type="text/css">
.problem-header {display:none;}
.staff {display:none;}
.correct { display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/correct-icon.png") center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px; }
.incorrect{
display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/incorrect-icon.png") center center no-repeat;
height: 20px;
width: 20px;
position: relative;
top: 6px; }
.unanswered {
display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/unanswered-icon.png") center center no-repeat;
height: 14px;
position: relative;
top: 4px;
width: 14px; }
}
</style>
<meta name="path_prefix" content="${MITX_ROOT_URL}">
<section class="course-content">
<form>
${phtml}
</form>
</section>
<script type="text/javascript" src="${static.url('js/jquery.treeview.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.leanModal.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.qtip.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.cookie.js')}"></script>
## <script type="text/javascript" src="${static.url('js/video_player.js')}"></script>
<script type="text/javascript" src="${static.url('js/schematic.js')}"></script>
<script type="text/javascript" src="${static.url('js/cktsim.js')}"></script>
## image input: for clicking on images (see imageinput.html)
<script type="text/javascript" src="/static/js/imageinput.js"></script>
<script type="text/javascript" >
var codemirror_set= {}; // associative array of codemirror objects
</script>
<%block name="js_extra"/>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html> <head>
<title>edX gitupdate</title>
</head>
<body>
<hr>
<h1>edX gitupdate</h1>
<hr>
<h2>Coursename: ${coursename}</h2>
% if msg:
${msg}
% else:
<p>
Do you REALLY want to overwrite all the course.xml + problems + html
files with version from the main git repository?
</p>
<form method="post">
<input type="submit" value="Do git update" name="gitupdate" />
## <input type="submit" value="Cancel" name="cancel" />
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf}"/>
</form>
% endif
<p><a href="${MITX_ROOT_URL}/courseware/">Return to site</a></p>
</body> </html>
...@@ -5,29 +5,52 @@ ...@@ -5,29 +5,52 @@
<%block name="title"><title>MITx 6.002x</title></%block> <%block name="title"><title>MITx 6.002x</title></%block>
<link rel="stylesheet" href="${static.url('js/jquery.treeview.css')}" type="text/css" media="all" /> <link rel="stylesheet" href="${static.url('js/jquery.treeview.css')}" type="text/css" media="all" />
<%static:css group='application'/>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:css group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
% endif
<script type="text/javascript" src="${static.url('js/jquery-1.6.2.min.js')}"></script> <script type="text/javascript" src="${static.url('js/jquery-1.6.2.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery-ui-1.8.16.custom.min.js')}"></script> <script type="text/javascript" src="${static.url('js/jquery-ui-1.8.16.custom.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/swfobject/swfobject.js')}"></script> <script type="text/javascript" src="${static.url('js/swfobject/swfobject.js')}"></script>
<%static:js group='application'/>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:js group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
% for jsfn in [ '/static/%s' % x.replace('.coffee','.js') for x in settings.PIPELINE_JS['application']['source_filenames'] ]:
<script type="text/javascript" src="${jsfn}"></script>
% endfor
% endif
## codemirror
<link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" />
<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script>
## alternate codemirror
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/lib/codemirror.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/xml/xml.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/python/python.js"></script>
## image input: for clicking on images (see imageinput.html)
<script type="text/javascript" src="/static/js/imageinput.js"></script>
## <script type="text/javascript">
## var codemirror_set = {}; // track all codemirror textareas, so they can be refreshed on page changes
## </script>
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="${static.url('js/html5shiv.js')}"></script> <script src="${static.url('js/html5shiv.js')}"></script>
<![endif]--> <![endif]-->
<script type="text/x-mathjax-config"> <%block name="headextra"/>
MathJax.Hub.Config({
tex2jax: {inlineMath: [["\\(","\\)"]],
displayMath: [["\\[","\\]"]]}
});
</script>
<%block name="headextra"/>
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates. <%include file="mathjax_include.html" />
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
MathJax extension libraries -->
<script type="text/javascript" src="/static/js/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-AMS_HTML-full"></script>
<meta name="path_prefix" content="${MITX_ROOT_URL}"> <meta name="path_prefix" content="${MITX_ROOT_URL}">
</head> </head>
...@@ -116,6 +139,11 @@ ...@@ -116,6 +139,11 @@
<script type="text/javascript" src="${static.url('js/jquery.cookie.js')}"></script> <script type="text/javascript" src="${static.url('js/jquery.cookie.js')}"></script>
<script type="text/javascript" src="${static.url('js/schematic.js')}"></script> <script type="text/javascript" src="${static.url('js/schematic.js')}"></script>
<script type="text/javascript" src="${static.url('js/cktsim.js')}"></script> <script type="text/javascript" src="${static.url('js/cktsim.js')}"></script>
<script type="text/javascript" >
var codemirror_set= {}; // associative array of codemirror objects
</script>
<script type="text/javascript" src="${static.url('js/jquery.scrollTo-1.4.2-min.js')}"></script> <script type="text/javascript" src="${static.url('js/jquery.scrollTo-1.4.2-min.js')}"></script>
<%block name="js_extra"/> <%block name="js_extra"/>
</body> </body>
......
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
// function to queue in MathJax to get put the MathML expression in in the right document element // function to queue in MathJax to get put the MathML expression in in the right document element
function UpdateMathML(jax,id) { function UpdateMathML(jax,id) {
toMathML(jax,function (mml) { toMathML(jax,function (mml) {
// document.getElementById(id+'_fromjs').value=math.originalText+ "\n\n=>\n\n"+ mml; // document.getElementById(id+'_dynamath').value=math.originalText+ "\n\n=>\n\n"+ mml;
delem = document.getElementById("input_" + id + "_fromjs"); delem = document.getElementById("input_" + id + "_dynamath");
if (delem) { delem.value=mml; }; if (delem) { delem.value=mml; };
mmlset[id] = mml; mmlset[id] = mml;
}) })
...@@ -79,11 +79,8 @@ function DoUpdateMath(inputId) { ...@@ -79,11 +79,8 @@ function DoUpdateMath(inputId) {
} }
</script> </script>
<%block name="headextra"/>
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates -->
<!-- TODO: move to settings -->
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/2.0-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full">
</script>
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
MathJax extension libraries -->
<script type="text/javascript" src="/static/js/mathjax-MathJax-c9db6ac/MathJax.js?config=TeX-MML-AM_HTMLorMML-full"></script>
...@@ -29,7 +29,9 @@ $(document).ready(function(){ ...@@ -29,7 +29,9 @@ $(document).ready(function(){
<h3>Courses available:</h3> <h3>Courses available:</h3>
<ul> <ul>
% for coursename, info in courseinfo.items(): % for coursename, info in courseinfo.items():
% if info['active']:
<li><a href=${ MITX_ROOT_URL }/courseware/${coursename}/>${info['title']} (${coursename})</a></li> <li><a href=${ MITX_ROOT_URL }/courseware/${coursename}/>${info['title']} (${coursename})</a></li>
% endif
% endfor % endfor
</ul> </ul>
</section> </section>
......
function ${ id }_content_updated() {
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
update_schematics();
// dynamic math display: generate MathML on click
$.each($("[id^=display_${ id }_]"), function(index,value){
theid = value.id.replace("display_",""); // ID of the response
if (document.getElementById("input_" + theid)){
MathJax.Hub.queue.Push(function () {
math = MathJax.Hub.getAllJax(value.id)[0];
if (math){
math.Text(document.getElementById("input_" + theid).value);
}
});
};
});
$('#check_${ id }').unbind('click').click(function() {
$("input.schematic").each(function(index,element){ element.schematic.update_value(); });
$(".CodeMirror").each(function(index,element){ if (element.CodeMirror.save) element.CodeMirror.save(); });
// dynamic math display: generate MathML on click
$.each($("[id^=input_${ id }_]"), function(index,value){
theid = value.id.replace("input_",""); // ID of the response
if (document.getElementById("display_" + theid)){
MathJax.Hub.queue.Push(function () {
math = MathJax.Hub.getAllJax("display_" + theid)[0];
if (math){
math.Text(document.getElementById("input_" + theid).value);
UpdateMathML(math,theid);
}
});
};
});
var submit_data={};
$.each($("[id^=input_${ id }_]"), function(index,value){
if (value.type==="checkbox"){
if (value.checked) {
if (typeof submit_data[value.name] == 'undefined'){
submit_data[value.name]=[];
}
submit_data[value.name].push(value.value);
}
}
if (value.type==="radio"){
if (value.checked) {
submit_data[value.name]= value.value;
}
}
else{
submit_data[value.id]=value.value;
}
});
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_check',
submit_data,
function(json) {
switch(json.success) {
case 'incorrect': // Worked, but answer not
case 'correct':
$('#main_${ id }').html(json.contents);
${ id }_content_updated();
break;
default:
alert(json.success);
}
}
);
log_event('problem_check', submit_data);
});
$('#reset_${ id }').unbind('click').click(function() {
var submit_data={};
$.each($("[id^=input_${ id }_]"), function(index,value){
submit_data[value.id]=value.value;
});
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_reset', {'id':'${ id }'}, function(html_as_json) {
$('#main_${ id }').html(html_as_json);
${ id }_content_updated();
});
log_event('problem_reset', submit_data);
});
// show answer button
// TODO: the button should turn into "hide answer" afterwards
$('#show_${ id }').unbind('click').click(function() {
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_show', {}, function(data) {
for (var key in data) {
if ($.isArray(data[key])){
for (var ans_index in data[key]){
var choice_id = 'input_'+key+'_'+data[key][ans_index];
$("label[for="+choice_id+"]").attr("correct_answer", "true");
}
}
// $("#answer_"+key).text(data[key]);
$("#answer_"+key).html(data[key]);
}
});
for (var key in codemirror_set) {
codemirror_set[key].refresh();
}
log_event('problem_show', {'problem':'${ id }'});
});
$('#save_${ id }').unbind('click').click(function() {
$("input.schematic").each(function(index,element){ element.schematic.update_value(); });
var submit_data={};
$.each($("[id^=input_${ id }_]"), function(index,value) {
submit_data[value.id]=value.value;
});
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_save',
submit_data,
function(data) {
if(data.success) {
alert('Saved');
}});
log_event('problem_save', submit_data);
});
}
function ${ id }_load() {
$('#main_${ id }').load('${ ajax_url }problem_get?id=${ id }', ${ id }_content_updated);
}
$(function() {
${ id }_load();
});
<%namespace name='static' file='static_content.html'/>
<!DOCTYPE html>
<html> <html>
## -----------------------------------------------------------------------------
## Template for courseware.views.quickedit
##
## Used for quick-edit link present when viewing capa-format assesment problems.
## -----------------------------------------------------------------------------
<head> <head>
<link rel="stylesheet" href="${ settings.LIB_URL }jquery.treeview.css" type="text/css" media="all" /> <link rel="stylesheet" href="${static.url('js/jquery.treeview.css')}" type="text/css" media="all" />
<link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" /> ## <link rel="stylesheet" href="${ settings.LIB_URL }jquery.treeview.css" type="text/css" media="all" />
## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
<script type="text/javascript" src="${ settings.LIB_URL }jquery-1.6.2.min.js"></script> % if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<script type="text/javascript" src="${ settings.LIB_URL }jquery-ui-1.8.16.custom.min.js"></script> <%static:css group='application'/>
<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script> % endif
<script type="text/javascript" src="/static/js/schematic.js"></script>
<%include file="mathjax_include.html" />
<script> % if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
function postJSON(url, data, callback) { ## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
$.ajax({type:'POST', % endif
url: url,
dataType: 'json',
data: data,
success: callback,
headers : {'X-CSRFToken':'none'} // getCookie('csrftoken')}
});
}
</script>
</head> <script type="text/javascript" src="${static.url('js/jquery-1.6.2.min.js')}"></script>
<body> <script type="text/javascript" src="${static.url('js/jquery-ui-1.8.16.custom.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/swfobject/swfobject.js')}"></script>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:js group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
% for jsfn in [ '/static/%s' % x.replace('.coffee','.js') for x in settings.PIPELINE_JS['application']['source_filenames'] ]:
<script type="text/javascript" src="${jsfn}"></script>
% endfor
% endif
## codemirror
<link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" />
<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script>
## alternate codemirror
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/lib/codemirror.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/xml/xml.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/python/python.js"></script>
## image input: for clicking on images (see imageinput.html)
<script type="text/javascript" src="/static/js/imageinput.js"></script>
## <script type="text/javascript">
## var codemirror_set = {}; // track all codemirror textareas, so they can be refreshed on page changes
## </script>
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="/static/js/html5shiv.js"></script> <script src="${static.url('js/html5shiv.js')}"></script>
<![endif]--> <![endif]-->
<%block name="headextra"/>
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
MathJax extension libraries -->
<%include file="mathjax_include.html" />
</head>
<body class="courseware">
<style type="text/css"> <style type="text/css">
.CodeMirror {border-style: solid; .CodeMirror {border-style: solid;
border-width: 1px;} border-width: 1px;}
...@@ -53,6 +89,7 @@ function postJSON(url, data, callback) { ...@@ -53,6 +89,7 @@ function postJSON(url, data, callback) {
<br/> <br/>
<input type="submit" value="Change Problem" name="qesubmit" /> <input type="submit" value="Change Problem" name="qesubmit" />
<input type="submit" value="Revert to original" name="qesubmit" /> <input type="submit" value="Revert to original" name="qesubmit" />
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf}"/>
</form> </form>
<span>${msg|n}</span> <span>${msg|n}</span>
...@@ -84,12 +121,60 @@ ${init_js} ...@@ -84,12 +121,60 @@ ${init_js}
<style type="text/css"> <style type="text/css">
.staff {display:none;} .staff {display:none;}
.correct { display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/correct-icon.png") center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px; }
.incorrect{
display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/incorrect-icon.png") center center no-repeat;
height: 20px;
width: 20px;
position: relative;
top: 6px; }
} }
</style> </style>
<form> <meta name="path_prefix" content="${MITX_ROOT_URL}">
${phtml}
</form> <section class="course-content">
<div id="seq_content">
<form>
${phtml}
</form>
</div>
</section>
<script type="text/javascript" src="${static.url('js/jquery.treeview.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.leanModal.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.qtip.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.cookie.js')}"></script>
## <script type="text/javascript" src="${static.url('js/video_player.js')}"></script>
<script type="text/javascript" src="${static.url('js/schematic.js')}"></script>
<script type="text/javascript" src="${static.url('js/cktsim.js')}"></script>
<script type="text/javascript" >
var codemirror_set= {}; // associative array of codemirror objects
</script>
<script type="text/javascript" src="${static.url('js/jquery.scrollTo-1.4.2-min.js')}"></script>
<%block name="js_extra"/>
</body> </body>
</html> </html>
<section id="textbox_${id}" class="textbox"> <section id="textbox_${id}" class="textbox">
<textarea rows="30" cols="80" name="input_${id}" id="input_${id}">${value|h}</textarea> <textarea rows="${rows}" cols="${cols}" name="input_${id}" id="input_${id}">${value|h}</textarea>
<span id="answer_${id}"></span> <span id="answer_${id}"></span>
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<br/> <br/>
<span class="debug">(${state})</span> <span class="debug">(${state})</span>
<br/> <br/>
<span class="debug">${msg|n}</span> <span class="message">${msg|n}</span>
<br/> <br/>
<br/> <br/>
...@@ -27,7 +27,10 @@ ...@@ -27,7 +27,10 @@
// Note: We need to make the area follow the CodeMirror for this to work. // Note: We need to make the area follow the CodeMirror for this to work.
$(function(){ $(function(){
var cm = CodeMirror.fromTextArea(document.getElementById("input_${id}"), { var cm = CodeMirror.fromTextArea(document.getElementById("input_${id}"), {
mode: "python" % if linenumbers == 'true':
lineNumbers: true,
% endif
mode: "${mode}"
}); });
}); });
</script> </script>
......
...@@ -16,4 +16,7 @@ ...@@ -16,4 +16,7 @@
% elif state == 'incomplete': % elif state == 'incomplete':
<span class="incorrect" id="status_${id}"></span> <span class="incorrect" id="status_${id}"></span>
% endif % endif
% if msg:
<span class="message">${msg|n}</span>
% endif
</section> </section>
###
### version of textline.html which does dynammic math
###
<section class="text-input-dynamath">
<table style="display:inline; vertical-align:middle;"><tr><td>
<input type="text" name="input_${id}" id="input_${id}" value="${value}"
% if size:
size="${size}"
% endif
onkeyup="DoUpdateMath('${id}')"
/>
</td><td>
<span id="answer_${id}"></span>
% if state == 'unsubmitted':
<span class="unanswered" style="display:inline-block;" id="status_${id}"></span>
% elif state == 'correct':
<span class="correct" id="status_${id}"></span>
% elif state == 'incorrect':
<span class="incorrect" id="status_${id}"></span>
% elif state == 'incomplete':
<span class="incorrect" id="status_${id}"></span>
% endif
</td></tr><tr><td>
<span id="display_${id}">`{::}`</span>
</td><td>
<textarea style="display:none" id="input_${id}_dynamath" name="input_${id}_dynamath"> </textarea>
</td></tr>
</table>
##
## javascript for dynamic math: add this math element to the MathJax rendering queue
## also adds to global jaxset js array
##
<script type="text/javascript">
MathJax.Hub.queue.Push(function () {
math = MathJax.Hub.getAllJax("display_${id}")[0];
if (math){
jaxset["${id}"] = math;
math.Text(document.getElementById("input_${id}").value);
// UpdateMathML(math,"${id}");
}
});
</script>
% if msg:
<br/>
<span class="debug">${msg|n}</span>
% endif
</section>
...@@ -6,8 +6,9 @@ from django.conf.urls.static import static ...@@ -6,8 +6,9 @@ from django.conf.urls.static import static
import django.contrib.auth.views import django.contrib.auth.views
# Uncomment the next two lines to enable the admin: # Uncomment the next two lines to enable the admin:
# from django.contrib import admin if settings.DEBUG:
# admin.autodiscover() from django.contrib import admin
admin.autodiscover()
urlpatterns = ('', urlpatterns = ('',
url(r'^$', 'student.views.index'), # Main marketing page, or redirect to courseware url(r'^$', 'student.views.index'), # Main marketing page, or redirect to courseware
...@@ -50,9 +51,11 @@ if settings.COURSEWARE_ENABLED: ...@@ -50,9 +51,11 @@ if settings.COURSEWARE_ENABLED:
url(r'^info$', 'util.views.info'), url(r'^info$', 'util.views.info'),
url(r'^wiki/', include('simplewiki.urls')), url(r'^wiki/', include('simplewiki.urls')),
url(r'^masquerade/', include('masquerade.urls')), url(r'^masquerade/', include('masquerade.urls')),
url(r'^courseware/(?P<course>[^/]*)/(?P<chapter>[^/]*)/(?P<section>[^/]*)/(?P<position>[^/]*)$', 'courseware.views.index'),
url(r'^courseware/(?P<course>[^/]*)/(?P<chapter>[^/]*)/(?P<section>[^/]*)/$', 'courseware.views.index', name="courseware_section"), url(r'^courseware/(?P<course>[^/]*)/(?P<chapter>[^/]*)/(?P<section>[^/]*)/$', 'courseware.views.index', name="courseware_section"),
url(r'^courseware/(?P<course>[^/]*)/(?P<chapter>[^/]*)/$', 'courseware.views.index', name="courseware_chapter"), url(r'^courseware/(?P<course>[^/]*)/(?P<chapter>[^/]*)/$', 'courseware.views.index', name="courseware_chapter"),
url(r'^courseware/(?P<course>[^/]*)/$', 'courseware.views.index', name="courseware_course"), url(r'^courseware/(?P<course>[^/]*)/$', 'courseware.views.index', name="courseware_course"),
url(r'^jumpto/(?P<probname>[^/]+)/$', 'courseware.views.jump_to'),
url(r'^section/(?P<section>[^/]*)/$', 'courseware.views.render_section'), url(r'^section/(?P<section>[^/]*)/$', 'courseware.views.render_section'),
url(r'^modx/(?P<module>[^/]*)/(?P<id>[^/]*)/(?P<dispatch>[^/]*)$', 'courseware.module_render.modx_dispatch'), #reset_problem'), url(r'^modx/(?P<module>[^/]*)/(?P<id>[^/]*)/(?P<dispatch>[^/]*)$', 'courseware.module_render.modx_dispatch'), #reset_problem'),
url(r'^profile$', 'courseware.views.profile'), url(r'^profile$', 'courseware.views.profile'),
...@@ -74,7 +77,8 @@ if settings.ENABLE_MULTICOURSE: ...@@ -74,7 +77,8 @@ if settings.ENABLE_MULTICOURSE:
urlpatterns += (url(r'^mitxhome$', 'multicourse.views.mitxhome'),) urlpatterns += (url(r'^mitxhome$', 'multicourse.views.mitxhome'),)
if settings.QUICKEDIT: if settings.QUICKEDIT:
urlpatterns += (url(r'^quickedit/(?P<id>[^/]*)$', 'courseware.views.quickedit'),) urlpatterns += (url(r'^quickedit/(?P<id>[^/]*)$', 'dogfood.views.quickedit'),)
urlpatterns += (url(r'^dogfood/(?P<id>[^/]*)$', 'dogfood.views.df_capa_problem'),)
if settings.ASKBOT_ENABLED: if settings.ASKBOT_ENABLED:
urlpatterns += (url(r'^%s' % settings.ASKBOT_URL, include('askbot.urls')), \ urlpatterns += (url(r'^%s' % settings.ASKBOT_URL, include('askbot.urls')), \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment