Commit f30596c4 by Calen Pennington

Merge branch 'master' into specific-error-handling

Conflicts:
	djangoapps/courseware/views.py
parents 69321b44 cc6e0c55
......@@ -4,6 +4,9 @@
*.swp
*.orig
*.DS_Store
:2e_*
:2e#
.AppleDouble
database.sqlite
courseware/static/js/mathjax/*
db.newaskbot
......
......@@ -25,7 +25,7 @@ from mako.template import Template
from util import contextualize_text
import inputtypes
from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse, StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse
from responsetypes import NumericalResponse, FormulaResponse, CustomResponse, SchematicResponse, MultipleChoiceResponse, StudentInputError, TrueFalseResponse, ExternalResponse,ImageResponse,OptionResponse
import calc
import eia
......@@ -40,8 +40,9 @@ response_types = {'numericalresponse':NumericalResponse,
'multiplechoiceresponse':MultipleChoiceResponse,
'truefalseresponse':TrueFalseResponse,
'imageresponse':ImageResponse,
'optionresponse':OptionResponse,
}
entry_types = ['textline', 'schematic', 'choicegroup','textbox','imageinput']
entry_types = ['textline', 'schematic', 'choicegroup','textbox','imageinput','optioninput']
solution_types = ['solution'] # extra things displayed after "show answers" is pressed
response_properties = ["responseparam", "answer"] # these get captured as student responses
......@@ -186,6 +187,13 @@ class LoncapaProblem(object):
if answer:
answer_map[entry.get('id')] = contextualize_text(answer, self.context)
# include solutions from <solution>...</solution> stanzas
# Tentative merge; we should figure out how we want to handle hints and solutions
for entry in self.tree.xpath("//"+"|//".join(solution_types)):
answer = etree.tostring(entry)
if answer:
answer_map[entry.get('id')] = answer
return answer_map
# ======= Private ========
......@@ -241,7 +249,24 @@ class LoncapaProblem(object):
if self.student_answers and problemid in self.student_answers:
value = self.student_answers[problemid]
return getattr(inputtypes, problemtree.tag)(problemtree, value, status) #TODO
#### This code is a hack. It was merged to help bring two branches
#### in sync, but should be replaced. msg should be passed in a
#### response_type
# prepare the response message, if it exists in correct_map
if 'msg' in self.correct_map:
msg = self.correct_map['msg']
elif ('msg_%s' % problemid) in self.correct_map:
msg = self.correct_map['msg_%s' % problemid]
else:
msg = ''
#if settings.DEBUG:
# print "[courseware.capa.capa_problem.extract_html] msg = ",msg
# do the rendering
#render_function = html_special_response[problemtree.tag]
render_function = getattr(inputtypes, problemtree.tag)
return render_function(problemtree, value, status, msg) # render the special response (textline, schematic,...)
tree=Element(problemtree.tag)
for item in problemtree:
......@@ -287,6 +312,7 @@ class LoncapaProblem(object):
answer_id = 1
for entry in tree.xpath("|".join(['//'+response.tag+'[@id=$id]//'+x for x in (entry_types + solution_types)]),
id=response_id_str):
# assign one answer_id for each entry_type or solution_type
entry.attrib['response_id'] = str(response_id)
entry.attrib['answer_id'] = str(answer_id)
entry.attrib['id'] = "%s_%i_%i"%(self.problem_id, response_id, answer_id)
......
......@@ -6,11 +6,16 @@
Module containing the problem elements which render into input objects
- textline
- textbox (change this to textarea?)
- textbox (change this to textarea?)
- schemmatic
- choicegroup (for multiplechoice: checkbox, radio, or select option)
- imageinput (for clickable image)
- optioninput (for option list)
These are matched by *.html files templates/*.html which are mako templates with the actual html.
Each input type takes the xml tree as 'element', the previous answer as 'value', and the graded status as 'status'
'''
# TODO: rename "state" to "status" for all below
......@@ -18,6 +23,7 @@ These are matched by *.html files templates/*.html which are mako templates with
# but it will turn into a dict containing both the answer and any associated message for the problem ID for the input element.
import re
import shlex # for splitting quoted strings
from django.conf import settings
......@@ -27,9 +33,42 @@ from lxml import etree
from mitxmako.shortcuts import render_to_string
#-----------------------------------------------------------------------------
#takes the xml tree as 'element', the student's previous answer as 'value', and the graded status as 'state'
def choicegroup(element, value, state, msg=""):
def optioninput(element, value, status, msg=''):
'''
Select option input type.
Example:
<optioninput options="('Up','Down')" correct="Up"/><text>The location of the sky</text>
'''
eid=element.get('id')
options = element.get('options')
if not options:
raise Exception,"[courseware.capa.inputtypes.optioninput] Missing options specification in " + etree.tostring(element)
oset = shlex.shlex(options[1:-1])
oset.quotes = "'"
oset.whitespace = ","
oset = [x[1:-1] for x in list(oset)]
# osetdict = dict([('option_%s_%s' % (eid,x),oset[x]) for x in range(len(oset)) ]) # make dict with IDs
osetdict = dict([(oset[x],oset[x]) for x in range(len(oset)) ]) # make dict with key,value same
if settings.DEBUG:
print '[courseware.capa.inputtypes.optioninput] osetdict=',osetdict
context={'id':eid,
'value':value,
'state':status,
'msg':msg,
'options':osetdict,
}
html=render_to_string("optioninput.html", context)
return etree.XML(html)
#-----------------------------------------------------------------------------
def choicegroup(element, value, status, msg=''):
'''
Radio button inputs: multiple choice or true/false
......@@ -47,7 +86,7 @@ def choicegroup(element, value, state, msg=""):
for choice in element:
assert choice.tag =="choice", "only <choice> tags should be immediate children of a <choicegroup>"
choices[choice.get("name")] = etree.tostring(choice[0]) # TODO: what if choice[0] has math tags in it?
context={'id':eid, 'value':value, 'state':state, 'type':type, 'choices':choices}
context={'id':eid, 'value':value, 'state':status, 'type':type, 'choices':choices}
html=render_to_string("choicegroup.html", context)
return etree.XML(html)
......@@ -60,9 +99,9 @@ def textline(element, value, state, msg=""):
return etree.XML(html)
#-----------------------------------------------------------------------------
# TODO: Make a wrapper for <formulainput>
# TODO: Make an AJAX loop to confirm equation is okay in real-time as user types
def jstextline(element, value, state, msg=""):
def js_textline(element, value, status, msg=''):
## TODO: Code should follow PEP8 (4 spaces per indentation level)
'''
textline is used for simple one-line inputs, like formularesponse and symbolicresponse.
'''
......@@ -72,7 +111,7 @@ def jstextline(element, value, state, msg=""):
dojs = element.get('dojs') # dojs is used for client-side javascript display & return
# when dojs=='math', a <span id=display_eid>`{::}`</span>
# and a hidden textarea with id=input_eid_fromjs will be output
context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size,
context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size,
'dojs':dojs,
'msg':msg,
}
......@@ -81,7 +120,7 @@ def jstextline(element, value, state, msg=""):
#-----------------------------------------------------------------------------
## TODO: Make a wrapper for <codeinput>
def textbox(element, value, state, msg=''):
def textbox(element, value, status, msg=''):
'''
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.
......@@ -91,12 +130,12 @@ def textbox(element, value, state, msg=''):
eid=element.get('id')
count = int(eid.split('_')[-2])-1 # HACK
size = element.get('size')
context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size, 'msg':msg}
context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size, 'msg':msg}
html=render_to_string("textbox.html", context)
return etree.XML(html)
#-----------------------------------------------------------------------------
def schematic(element, value, state):
def schematic(element, value, status, msg=''):
eid = element.get('id')
height = element.get('height')
width = element.get('width')
......@@ -120,7 +159,7 @@ def schematic(element, value, state):
#-----------------------------------------------------------------------------
### TODO: Move out of inputtypes
def math(element, value, state, msg=''):
def math(element, value, status, msg=''):
'''
This is not really an input type. It is a convention from Lon-CAPA, used for
displaying a math equation.
......@@ -134,21 +173,27 @@ def math(element, value, state, msg=''):
TODO: use shorter tags (but this will require converting problem XML files!)
'''
mathstr = element.text[1:-1]
if '\\displaystyle' in mathstr:
isinline = False
mathstr = mathstr.replace('\\displaystyle','')
else:
isinline = True
html=render_to_string("mathstring.html",{'mathstr':mathstr,'isinline':isinline,'tail':element.tail})
mathstr = re.sub('\$(.*)\$','[mathjaxinline]\\1[/mathjaxinline]',element.text)
mtag = 'mathjax'
if not '\\displaystyle' in mathstr: mtag += 'inline'
else: mathstr = mathstr.replace('\\displaystyle','')
mathstr = mathstr.replace('mathjaxinline]','%s]'%mtag)
#if '\\displaystyle' in mathstr:
# isinline = False
# mathstr = mathstr.replace('\\displaystyle','')
#else:
# isinline = True
# html=render_to_string("mathstring.html",{'mathstr':mathstr,'isinline':isinline,'tail':element.tail})
html = '<html><html>%s</html><html>%s</html></html>' % (mathstr,element.tail)
xhtml = etree.XML(html)
# xhtml.tail = element.tail # don't forget to include the tail!
return xhtml
#-----------------------------------------------------------------------------
def solution(element, value, state, msg=''):
def solution(element, value, status, msg=''):
'''
This is not really an input type. It is just a <span>...</span> which is given an ID,
that is used for displaying an extended answer (a problem "solution") after "show answers"
......@@ -159,7 +204,7 @@ def solution(element, value, state, msg=''):
size = element.get('size')
context = {'id':eid,
'value':value,
'state':state,
'state':status,
'size': size,
'msg':msg,
}
......
......@@ -24,7 +24,9 @@ try: # This lets us do __name__ == ='__main__'
from student.models import UserTestGroup
from mitxmako.shortcuts import render_to_string
from util.cache import cache
from multicourse import multicourse_settings
except:
print "Could not import/content_parser"
settings = None
''' This file will eventually form an abstraction layer between the
......@@ -181,7 +183,7 @@ def course_xml_process(tree):
propogate_downward_tag(tree, "rerandomize")
return tree
def course_file(user):
def course_file(user,coursename=None):
''' Given a user, return course.xml'''
if user.is_authenticated():
......@@ -189,6 +191,11 @@ def course_file(user):
else:
filename = 'guest_course.xml'
# if a specific course is specified, then use multicourse to get the right path to the course XML directory
if coursename and settings.ENABLE_MULTICOURSE:
xp = multicourse_settings.get_course_xmlpath(coursename)
filename = xp + filename # prefix the filename with the path
groups = user_groups(user)
options = {'dev_content':settings.DEV_CONTENT,
'groups' : groups}
......@@ -210,13 +217,24 @@ def course_file(user):
return tree
def section_file(user, section):
''' Given a user and the name of a section, return that section
def section_file(user, section, coursename=None, dironly=False):
'''
Given a user and the name of a section, return that section.
This is done specific to each course.
If dironly=True then return the sections directory.
'''
filename = section+".xml"
if filename not in os.listdir(settings.DATA_DIR + '/sections/'):
print filename+" not in "+str(os.listdir(settings.DATA_DIR + '/sections/'))
# if a specific course is specified, then use multicourse to get the right path to the course XML directory
xp = ''
if coursename and settings.ENABLE_MULTICOURSE: xp = multicourse_settings.get_course_xmlpath(coursename)
dirname = settings.DATA_DIR + xp + '/sections/'
if dironly: return dirname
if filename not in os.listdir(dirname):
print filename+" not in "+str(os.listdir(dirname))
return None
options = {'dev_content':settings.DEV_CONTENT,
......@@ -226,7 +244,7 @@ def section_file(user, section):
return tree
def module_xml(user, module, id_tag, module_id):
def module_xml(user, module, id_tag, module_id, coursename=None):
''' Get XML for a module based on module and module_id. Assumes
module occurs once in courseware XML file or hidden section. '''
# Sanitize input
......@@ -239,14 +257,15 @@ def module_xml(user, module, id_tag, module_id):
id_tag=id_tag,
id=module_id)
#result_set=doc.xpathEval(xpath_search)
doc = course_file(user)
section_list = (s[:-4] for s in os.listdir(settings.DATA_DIR+'/sections') if s[-4:]=='.xml')
doc = course_file(user,coursename)
sdirname = section_file(user,'',coursename,True) # get directory where sections information is stored
section_list = (s[:-4] for s in os.listdir(sdirname) if s[-4:]=='.xml')
result_set=doc.xpath(xpath_search)
if len(result_set)<1:
for section in section_list:
try:
s = section_file(user, section)
s = section_file(user, section, coursename)
except etree.XMLSyntaxError:
ex= sys.exc_info()
raise ContentException("Malformed XML in " + section+ "("+str(ex[1].msg)+")")
......
......@@ -67,7 +67,7 @@ course_settings = Settings()
def grade_sheet(student):
def grade_sheet(student,coursename=None):
"""
This pulls a summary of all problems in the course. It returns a dictionary with two datastructures:
......@@ -77,7 +77,7 @@ def grade_sheet(student):
- grade_summary is the output from the course grader. More information on the format is in the docstring for CourseGrader.
"""
dom=content_parser.course_file(student)
dom=content_parser.course_file(student,coursename)
course = dom.xpath('//course/@name')[0]
xmlChapters = dom.xpath('//course[@name=$course]/chapter', course=course)
......@@ -103,7 +103,7 @@ def grade_sheet(student):
scores=[]
if len(problems)>0:
for p in problems:
(correct,total) = get_score(student, p, response_by_id)
(correct,total) = get_score(student, p, response_by_id, coursename=coursename)
if settings.GENERATE_PROFILE_SCORES:
if total > 1:
......@@ -167,7 +167,7 @@ def aggregate_scores(scores, section_name = "summary"):
return all_total, graded_total
def get_score(user, problem, cache):
def get_score(user, problem, cache, coursename=None):
## HACK: assumes max score is fixed per problem
id = problem.get('id')
correct = 0.0
......@@ -196,7 +196,7 @@ def get_score(user, problem, cache):
## HACK 1: We shouldn't specifically reference capa_module
## HACK 2: Backwards-compatibility: This should be written when a grade is saved, and removed from the system
from module_render import I4xSystem
system = I4xSystem(None, None, None)
system = I4xSystem(None, None, None, coursename=coursename)
total=float(courseware.modules.capa_module.Module(system, etree.tostring(problem), "id").max_score())
response.max_grade = total
response.save()
......
......@@ -22,6 +22,11 @@ import courseware.modules
log = logging.getLogger("mitx.courseware")
class I4xSystem(object):
'''
This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS,
or if we want to have a sandbox server for user-contributed content)
'''
def __init__(self, ajax_url, track_function, render_function, filestore=None):
self.ajax_url = ajax_url
self.track_function = track_function
......@@ -29,6 +34,10 @@ class I4xSystem(object):
self.filestore = OSFS(settings.DATA_DIR)
self.render_function = render_function
self.exception404 = Http404
def __repr__(self):
return repr(self.__dict__)
def __str__(self):
return str(self.__dict__)
def object_cache(cache, user, module_type, module_id):
# We don't look up on user -- all queries include user
......@@ -50,6 +59,7 @@ def make_track_function(request):
def f(event_type, event):
return track.views.server_track(request, event_type, event, page='x_module')
return f
def grade_histogram(module_id):
''' Print out a histogram of grades on a given problem.
Part of staff member debug info.
......@@ -83,6 +93,10 @@ def render_x_module(user, request, xml_module, module_object_preload):
else:
state = smod.state
# get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
# Create a new instance
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
system = I4xSystem(track_function = make_track_function(request),
......@@ -104,6 +118,7 @@ def render_x_module(user, request, xml_module, module_object_preload):
state=instance.get_state())
smod.save()
module_object_preload.append(smod)
# Grab content
content = instance.get_html()
init_js = instance.get_init_js()
......
......@@ -21,6 +21,7 @@ from mitxmako.shortcuts import render_to_string
from x_module import XModule
from courseware.capa.capa_problem import LoncapaProblem, StudentInputError
import courseware.content_parser as content_parser
from multicourse import multicourse_settings
log = logging.getLogger("mitx.courseware")
......@@ -115,18 +116,19 @@ class Module(XModule):
if len(explain) == 0:
explain = False
html=render_to_string('problem.html',
{'problem' : content,
'id' : self.item_id,
'check_button' : check_button,
'reset_button' : reset_button,
'save_button' : save_button,
'answer_available' : self.answer_available(),
'ajax_url' : self.ajax_url,
'attempts_used': self.attempts,
'attempts_allowed': self.max_attempts,
'explain': explain
})
context = {'problem' : content,
'id' : self.item_id,
'check_button' : check_button,
'reset_button' : reset_button,
'save_button' : save_button,
'answer_available' : self.answer_available(),
'ajax_url' : self.ajax_url,
'attempts_used': self.attempts,
'attempts_allowed': self.max_attempts,
'explain': explain,
}
html=render_to_string('problem.html', context)
if encapsulate:
html = '<div id="main_{id}">'.format(id=self.item_id)+html+"</div>"
......@@ -193,7 +195,12 @@ class Module(XModule):
seed = 1
else:
seed = None
self.lcp=LoncapaProblem(self.filestore.open(self.filename), self.item_id, state, seed = seed)
try:
fp = self.filestore.open(self.filename)
except Exception,err:
print '[courseware.capa.capa_module.Module.init] error %s: cannot open file %s' % (err,self.filename)
raise Exception,err
self.lcp=LoncapaProblem(fp, self.item_id, state, seed = seed)
def handle_ajax(self, dispatch, get):
'''
......@@ -306,7 +313,7 @@ class Module(XModule):
except:
self.lcp = LoncapaProblem(self.filestore.open(self.filename), id=lcp_id, state=old_state)
traceback.print_exc()
raise
raise Exception,"error in capa_module"
return json.dumps({'success':'Unknown Error'})
self.attempts = self.attempts + 1
......
<problem>
<text>
<p>
Why do bicycles benefit from having larger wheels when going up a bump as shown in the picture? <br/>
Assume that for both bicycles:<br/>
1.) The tires have equal air pressure.<br/>
2.) The bicycles never leave the contact with the bump.<br/>
3.) The bicycles have the same mass. The bicycle tires (regardless of size) have the same mass.<br/>
</p>
</text>
<optionresponse texlayout="horizontal" max="10" randomize="yes">
<ul>
<li>
<text>
<p>The bicycles with larger wheels have more time to go over the bump. This decreases the magnitude of the force needed to lift the bicycle.</p>
</text>
<optioninput name="Foil1" location="random" options="('True','False')" correct="True">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels always have a smaller vertical displacement regardless of speed.</p>
</text>
<optioninput name="Foil2" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels experience a force backward with less magnitude for the same amount of time.</p>
</text>
<optioninput name="Foil3" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels experience a force backward with less magnitude for a greater amount of time.</p>
</text>
<optioninput name="Foil4" location="random" options="('True','False')" correct="True">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels have more kinetic energy turned into gravitational potential energy.</p>
</text>
<optioninput name="Foil5" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
<li>
<text>
<p>The bicycles with larger wheels have more rotational kinetic energy, so the horizontal velocity of the biker changes less.</p>
</text>
<optioninput name="Foil6" location="random" options="('True','False')" correct="False">
</optioninput>
</li>
</ul>
<hintgroup showoncorrect="no">
<text>
<br/>
<br/>
</text>
</hintgroup>
</optionresponse>
</problem>
......@@ -63,6 +63,9 @@ class ModelsTest(unittest.TestCase):
exception_happened = True
self.assertTrue(exception_happened)
#-----------------------------------------------------------------------------
# tests of capa_problem inputtypes
class MultiChoiceTest(unittest.TestCase):
def test_MC_grade(self):
multichoice_file = os.path.dirname(__file__)+"/test_files/multichoice.xml"
......@@ -93,6 +96,38 @@ class MultiChoiceTest(unittest.TestCase):
self.assertEquals(test_lcp.grade_answers(false_answers)['1_2_1'], 'incorrect')
false_answers = {'1_2_1':['choice_foil1', 'choice_foil2', 'choice_foil3']}
self.assertEquals(test_lcp.grade_answers(false_answers)['1_2_1'], 'incorrect')
class ImageResponseTest(unittest.TestCase):
def test_ir_grade(self):
imageresponse_file = os.path.dirname(__file__)+"/test_files/imageresponse.xml"
test_lcp = lcp.LoncapaProblem(open(imageresponse_file), '1')
correct_answers = {'1_2_1':'(490,11)-(556,98)',
'1_2_2':'(242,202)-(296,276)'}
test_answers = {'1_2_1':'[500,20]',
'1_2_2':'[250,300]',
}
self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_1'], 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_2'], 'incorrect')
class OptionResponseTest(unittest.TestCase):
'''
Run this with
python manage.py test courseware.OptionResponseTest
'''
def test_or_grade(self):
optionresponse_file = os.path.dirname(__file__)+"/test_files/optionresponse.xml"
test_lcp = lcp.LoncapaProblem(open(optionresponse_file), '1')
correct_answers = {'1_2_1':'True',
'1_2_2':'False'}
test_answers = {'1_2_1':'True',
'1_2_2':'True',
}
self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_1'], 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers)['1_2_2'], 'incorrect')
#-----------------------------------------------------------------------------
# Grading tests
class GradesheetTest(unittest.TestCase):
......@@ -118,7 +153,7 @@ class GradesheetTest(unittest.TestCase):
all, graded = aggregate_scores(scores)
self.assertAlmostEqual(all, Score(earned=5, possible=15, graded=False, section="summary"))
self.assertAlmostEqual(graded, Score(earned=5, possible=10, graded=True, section="summary"))
class GraderTest(unittest.TestCase):
empty_gradesheet = {
......
......@@ -18,6 +18,7 @@ from module_render import render_module, make_track_function, I4xSystem
from models import StudentModule
from student.models import UserProfile
from util.errors import record_exception
from multicourse import multicourse_settings
import courseware.content_parser as content_parser
import courseware.modules
......@@ -35,11 +36,16 @@ template_imports={'urllib':urllib}
def gradebook(request):
if 'course_admin' not in content_parser.user_groups(request.user):
raise Http404
# TODO: This should be abstracted out. We repeat this logic many times.
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
student_objects = User.objects.all()[:100]
student_info = [{'username' :s.username,
'id' : s.id,
'email': s.email,
'grade_info' : grades.grade_sheet(s),
'grade_info' : grades.grade_sheet(s,coursename),
'realname' : UserProfile.objects.get(user = s).name
} for s in student_objects]
......@@ -61,6 +67,9 @@ def profile(request, student_id = None):
user_info = UserProfile.objects.get(user=student) # request.user.profile_cache #
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
context={'name':user_info.name,
'username':student.username,
'location':user_info.location,
......@@ -69,7 +78,7 @@ def profile(request, student_id = None):
'format_url_params' : content_parser.format_url_params,
'csrf':csrf(request)['csrf_token']
}
context.update(grades.grade_sheet(student))
context.update(grades.grade_sheet(student,coursename))
return render_to_response('profile.html', context)
......@@ -79,7 +88,7 @@ def render_accordion(request,course,chapter,section):
if not course:
course = "6.002 Spring 2012"
toc=content_parser.toc_from_xml(content_parser.course_file(request.user), chapter, section)
toc=content_parser.toc_from_xml(content_parser.course_file(request.user,course), chapter, section)
active_chapter=1
for i in range(len(toc)):
if toc[i]['active']:
......@@ -100,8 +109,11 @@ def render_section(request, section):
if not settings.COURSEWARE_ENABLED:
return redirect('/')
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
try:
dom = content_parser.section_file(user, section)
dom = content_parser.section_file(user, section, coursename)
except:
record_exception(log, "Unable to parse courseware xml")
return render_to_response('courseware-error.html', {})
......@@ -139,13 +151,21 @@ def render_section(request, section):
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def index(request, course="6.002 Spring 2012", chapter="Using the System", section="Hints"):
def index(request, course=None, chapter="Using the System", section="Hints"):
''' Displays courseware accordion, and any associated content.
'''
user = request.user
if not settings.COURSEWARE_ENABLED:
return redirect('/')
if course==None:
if not settings.ENABLE_MULTICOURSE:
course = "6.002 Spring 2012"
elif 'coursename' in request.session:
course = request.session['coursename']
else:
course = settings.COURSE_DEFAULT
# Fixes URLs -- we don't get funny encoding characters from spaces
# so they remain readable
## TODO: Properly replace underscores
......@@ -153,13 +173,15 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti
chapter=chapter.replace("_"," ")
section=section.replace("_"," ")
# HACK: Force course to 6.002 for now
# Without this, URLs break
if course!="6.002 Spring 2012":
# use multicourse module to determine if "course" is valid
#if course!=settings.COURSE_NAME.replace('_',' '):
if not multicourse_settings.is_valid_course(course):
return redirect('/')
request.session['coursename'] = course # keep track of current course being viewed in django's request.session
try:
dom = content_parser.course_file(user)
dom = content_parser.course_file(user,course) # also pass course to it, for course-specific XML path
except:
record_exception(log, "Unable to parse courseware xml")
return render_to_response('courseware-error.html', {})
......@@ -184,6 +206,7 @@ def index(request, course="6.002 Spring 2012", chapter="Using the System", secti
context = {
'csrf': csrf(request)['csrf_token'],
'accordion': render_accordion(request, course, chapter, section)
'COURSE_TITLE':multicourse_settings.get_course_title(course),
}
try:
......@@ -226,9 +249,13 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
# get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
# Grab the XML corresponding to the request from course.xml
try:
xml = content_parser.module_xml(request.user, module, 'id', id)
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
except:
record_exception(log, "Unable to load module during ajax call")
if 'text/html' in request.accepted_types:
......@@ -267,3 +294,98 @@ def modx_dispatch(request, module=None, dispatch=None, id=None):
s.save()
# Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return)
def quickedit(request, id=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.
'''
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():
return redirect('/')
# get coursename if stored
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
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 = None,
coursename = coursename,
role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this
)
instance=courseware.modules.get_module_class(module)(system,
xml,
id,
state=None)
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_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
# multicourse/multicourse_settings.py
#
# central module for providing fixed settings (course name, number, title)
# for multiple courses. Loads this information from django.conf.settings
#
# Allows backward compatibility with settings configurations without
# multiple courses specified.
#
# The central piece of configuration data is the dict COURSE_SETTINGS, with
# keys being the COURSE_NAME (spaces ok), and the value being a dict of
# parameter,value pairs. The required parameters are:
#
# - number : course number (used in the simplewiki pages)
# - title : humanized descriptive course title
#
# Optional parameters:
#
# - xmlpath : path (relative to data directory) for this course (defaults to "")
#
# If COURSE_SETTINGS does not exist, then fallback to 6.002_Spring_2012 default,
# for now.
from django.conf import settings
#-----------------------------------------------------------------------------
# load course settings
if hasattr(settings,'COURSE_SETTINGS'): # in the future, this could be replaced by reading an XML file
COURSE_SETTINGS = settings.COURSE_SETTINGS
elif hasattr(settings,'COURSE_NAME'): # backward compatibility
COURSE_SETTINGS = {settings.COURSE_NAME: {'number': settings.COURSE_NUMBER,
'title': settings.COURSE_TITLE,
},
}
else: # default to 6.002_Spring_2012
COURSE_SETTINGS = {'6.002_Spring_2012': {'number': '6.002x',
'title': 'Circuits and Electronics',
},
}
#-----------------------------------------------------------------------------
# wrapper functions around course settings
def get_course_settings(coursename):
if not coursename:
if hasattr(settings,'COURSE_DEFAULT'):
coursename = settings.COURSE_DEFAULT
else:
coursename = '6.002_Spring_2012'
if coursename in COURSE_SETTINGS: return COURSE_SETTINGS[coursename]
coursename = coursename.replace(' ','_')
if coursename in COURSE_SETTINGS: return COURSE_SETTINGS[coursename]
return None
def is_valid_course(coursename):
return not (get_course_settings==None)
def get_course_property(coursename,property):
cs = get_course_settings(coursename)
if not cs: return '' # raise exception instead?
if property in cs: return cs[property]
return '' # default
def get_course_xmlpath(coursename):
return get_course_property(coursename,'xmlpath')
def get_course_title(coursename):
return get_course_property(coursename,'title')
def get_course_number(coursename):
return get_course_property(coursename,'number')
# multicourse/views.py
......@@ -9,6 +9,8 @@ from django.utils import simplejson
from django.utils.translation import ugettext_lazy as _
from mitxmako.shortcuts import render_to_response
from multicourse import multicourse_settings
from models import Revision, Article, CreateArticleForm, RevisionFormWithTitle, RevisionForm
import wiki_settings
......@@ -17,6 +19,11 @@ def view(request, wiki_url):
if err:
return err
if 'coursename' in request.session: coursename = request.session['coursename']
else: coursename = None
course_number = multicourse_settings.get_course_number(coursename)
perm_err = check_permissions(request, article, check_read=True, check_deleted=True)
if perm_err:
return perm_err
......@@ -25,7 +32,7 @@ def view(request, wiki_url):
'wiki_write': article.can_write_l(request.user),
'wiki_attachments_write': article.can_attach(request.user),
'wiki_current_revision_deleted' : not (article.current_revision.deleted == 0),
'wiki_title' : article.title + " - MITX 6.002x Wiki"
'wiki_title' : article.title + " - MITX %s Wiki" % course_number
}
d.update(csrf(request))
return render_to_response('simplewiki_view.html', d)
......
......@@ -8,7 +8,8 @@ Common traits:
"""
import json
from common import *
from envs.logsettings import get_logger_config
from envs.common import *
############################### ALWAYS THE SAME ################################
DEBUG = False
......@@ -31,10 +32,10 @@ LOG_DIR = ENV_TOKENS['LOG_DIR']
CACHES = ENV_TOKENS['CACHES']
LOGGING = logsettings.get_logger_config(LOG_DIR,
logging_env=ENV_TOKENS['LOGGING_ENV'],
syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514),
debug=False)
LOGGING = get_logger_config(LOG_DIR,
logging_env=ENV_TOKENS['LOGGING_ENV'],
syslog_addr=(ENV_TOKENS['SYSLOG_SERVER'], 514),
debug=False)
############################## SECURE AUTH ITEMS ###############################
# Secret things: passwords, access keys, etc.
......
......@@ -24,8 +24,7 @@ import tempfile
import djcelery
from path import path
from askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from
import logsettings
from envs.askbotsettings import * # this is where LIVESETTINGS_OPTIONS comes from
################################### FEATURES ###################################
COURSEWARE_ENABLED = True
......
......@@ -2,7 +2,7 @@
These are debug machines used for content creators, so they're kind of a cross
between dev machines and AWS machines.
"""
from aws import *
from envs.aws import *
DEBUG = True
TEMPLATE_DEBUG = True
......
......@@ -7,15 +7,16 @@ sessions. Assumes structure:
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from common import *
from envs.common import *
from envs.logsettings import get_logger_config
DEBUG = False
TEMPLATE_DEBUG = False
LOGGING = logsettings.get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
LOGGING = get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
DATABASES = {
'default': {
......
......@@ -13,7 +13,7 @@ Dir structure:
/log # Where we're going to write log files
"""
from dev import *
from envs.dev import *
DATABASES = {
'default': {
......
......@@ -7,14 +7,15 @@ sessions. Assumes structure:
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from common import *
from envs.common import *
from envs.logsettings import get_logger_config
STATIC_GRAB = True
LOGGING = logsettings.get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=False)
LOGGING = get_logger_config(ENV_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=False)
DATABASES = {
'default': {
......
......@@ -7,7 +7,8 @@ sessions. Assumes structure:
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from common import *
from envs.common import *
from envs.logsettings import get_logger_config
import os
INSTALLED_APPS = [
......@@ -25,7 +26,8 @@ for app in os.listdir(PROJECT_ROOT / 'djangoapps'):
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
# Local Directories
COURSES_ROOT = PROJECT_ROOT / "test_data"
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']
......@@ -34,10 +36,10 @@ MAKO_TEMPLATES['main'] = [PROJECT_ROOT / 'templates',
DATA_DIR / 'info',
DATA_DIR / 'problems']
LOGGING = logsettings.get_logger_config(PROJECT_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
LOGGING = get_logger_config(TEST_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
debug=True)
DATABASES = {
'default': {
......
#!/usr/bin/python
from loncapa_check import *
#!/usr/bin/python
#
# File: mitx/lib/loncapa/loncapa_check.py
#
# Python functions which duplicate the standard comparison functions available to LON-CAPA problems.
# Used in translating LON-CAPA problems to i4x problem specification language.
import random
def lc_random(lower,upper,stepsize):
'''
like random.randrange but lower and upper can be non-integer
'''
nstep = int((upper-lower)/(1.0*stepsize))
choices = [lower+x*stepsize for x in range(nstep)]
return random.choice(choices)
......@@ -34,6 +34,9 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
context_dictionary.update(d)
if context:
context_dictionary.update(context)
## HACK
## We should remove this, and possible set COURSE_TITLE in the middleware from the session.
if 'COURSE_TITLE' not in context_dictionary: context_dictionary['COURSE_TITLE'] = ''
# fetch and render template
template = middleware.lookup[namespace].get_template(template_name)
return template.render(**context_dictionary)
......
......@@ -3,7 +3,6 @@ import json
import sys
from django.conf import settings
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
......@@ -61,3 +60,9 @@ def send_feedback(request):
def info(request):
''' Info page (link from main header) '''
return render_to_response("info.html", {})
def mitxhome(request):
''' Home page (link from main header). List of courses. '''
if settings.ENABLE_MULTICOURSE:
return render_to_response("mitxhome.html", {})
return info(request)
......@@ -29,12 +29,17 @@ INSTALL_DIR_PATH = File.join(DEPLOY_DIR, NORMALIZED_DEPLOY_NAME)
CLOBBER.include(BUILD_DIR, REPORT_DIR, 'cover*', '.coverage')
CLEAN.include("#{BUILD_DIR}/*.deb", "#{BUILD_DIR}/util")
def select_executable(*cmds)
cmds.find_all{ |cmd| system("which #{cmd} > /dev/null 2>&1") }[0] || fail("No executables found from #{cmds.join(', ')}")
end
task :default => [:pep8, :pylint, :test]
directory REPORT_DIR
task :pep8 => REPORT_DIR do
sh("pep8 djangoapps | tee #{REPORT_DIR}/pep8.report")
sh("pep8 --ignore=E501 djangoapps | tee #{REPORT_DIR}/pep8.report")
end
task :pylint => REPORT_DIR do
......@@ -47,7 +52,8 @@ end
task :test => REPORT_DIR do
ENV['NOSE_XUNIT_FILE'] = File.join(REPORT_DIR, "nosetests.xml")
sh("django-admin.py test --settings=envs.test --pythonpath=. $(ls djangoapps)")
django_admin = ENV['DJANGO_ADMIN_PATH'] || select_executable('django-admin.py', 'django-admin')
sh("#{django_admin} test --settings=envs.test --pythonpath=. $(ls djangoapps)")
end
task :package do
......
......@@ -8,6 +8,7 @@ import djcelery
### Dark code. Should be enabled in local settings for devel.
ENABLE_MULTICOURSE = False # set to False to disable multicourse display (see lib.util.views.mitxhome)
QUICKEDIT = False
###
......@@ -20,19 +21,11 @@ COURSE_TITLE = "Circuits and Electronics"
COURSE_DEFAULT = '6.002_Spring_2012'
COURSE_LIST = {'6.002_Spring_2012': {'number' : '6.002x',
'title' : 'Circuits and Electronics',
'datapath': '6002x/',
},
'8.02_Spring_2013': {'number' : '8.02x',
'title' : 'Electricity &amp; Magnetism',
'datapath': '802x/',
},
'8.01_Spring_2013': {'number' : '8.01x',
'title' : 'Mechanics',
'datapath': '801x/',
},
}
COURSE_SETTINGS = {'6.002_Spring_2012': {'number' : '6.002x',
'title' : 'Circuits and Electronics',
'xmlpath': '6002x/',
}
}
ROOT_URLCONF = 'urls'
......@@ -151,6 +144,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware',
'track.middleware.TrackMiddleware',
'mitxmako.middleware.MakoMiddleware',
#'ssl_auth.ssl_auth.NginxProxyHeaderMiddleware', # ssl authentication behind nginx proxy
#'debug_toolbar.middleware.DebugToolbarMiddleware',
# Uncommenting the following will prevent csrf token from being re-set if you
......@@ -180,6 +174,8 @@ INSTALLED_APPS = (
'util',
'masquerade',
'django_jasmine',
#'ssl_auth', ## Broken. Disabled for now.
'multicourse', # multiple courses
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -735,14 +735,14 @@ section.index-content section.staff h1 {
margin-top: 25.888px; }
#lean_overlay {
background: #000;
display: none;
height: 100%;
left: 0px;
position: fixed;
z-index: 100;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
background: #000;
display: none; }
z-index: 100; }
div.leanModal_box {
background: #fff;
......@@ -772,8 +772,8 @@ div.leanModal_box a.modal_close {
width: 14px;
z-index: 2; }
div.leanModal_box a.modal_close:hover {
text-decoration: none;
color: #993333; }
color: #993333;
text-decoration: none; }
div.leanModal_box h1 {
border-bottom: 1px solid #eee;
font-size: 24px;
......@@ -786,8 +786,8 @@ div.leanModal_box#enroll {
div.leanModal_box#enroll ol {
padding-top: 25.888px; }
div.leanModal_box#enroll ol li.terms, div.leanModal_box#enroll ol li.honor-code {
width: auto;
float: none; }
float: none;
width: auto; }
div.leanModal_box#enroll ol li div.tip {
display: none; }
div.leanModal_box#enroll ol li:hover div.tip {
......@@ -828,16 +828,16 @@ div.leanModal_box form ol li.terms, div.leanModal_box form ol li.remember {
padding-top: 25.888px;
width: auto; }
div.leanModal_box form ol li.honor-code {
width: auto;
float: none; }
float: none;
width: auto; }
div.leanModal_box form ol li label {
display: block;
font-weight: bold; }
div.leanModal_box form ol li input[type="email"], div.leanModal_box form ol li input[type="number"], div.leanModal_box form ol li input[type="password"], div.leanModal_box form ol li input[type="search"], div.leanModal_box form ol li input[type="tel"], div.leanModal_box form ol li input[type="text"], div.leanModal_box form ol li input[type="url"], div.leanModal_box form ol li input[type="color"], div.leanModal_box form ol li input[type="date"], div.leanModal_box form ol li input[type="datetime"], div.leanModal_box form ol li input[type="datetime-local"], div.leanModal_box form ol li input[type="month"], div.leanModal_box form ol li input[type="time"], div.leanModal_box form ol li input[type="week"], div.leanModal_box form ol li textarea {
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
box-sizing: border-box;
width: 100%; }
div.leanModal_box form ol li input[type="checkbox"] {
margin-right: 10px; }
div.leanModal_box form ol li ul {
......@@ -904,12 +904,12 @@ div#login header h1 {
padding-bottom: 0;
margin-bottom: 6.472px; }
div#login ol li {
width: auto;
float: none; }
float: none;
width: auto; }
div.lost-password {
text-align: left;
margin-top: 25.888px; }
margin-top: 25.888px;
text-align: left; }
div.lost-password a {
color: #999; }
div.lost-password a:hover {
......@@ -927,11 +927,11 @@ div#apply_name_change ul, div#change_email ul, div#unenroll ul, div#deactivate-a
div#apply_name_change ul li, div#change_email ul li, div#unenroll ul li, div#deactivate-account ul li {
margin-bottom: 12.944px; }
div#apply_name_change ul li textarea, div#apply_name_change ul li input[type="email"], div#apply_name_change ul li input[type="number"], div#apply_name_change ul li input[type="password"], div#apply_name_change ul li input[type="search"], div#apply_name_change ul li input[type="tel"], div#apply_name_change ul li input[type="text"], div#apply_name_change ul li input[type="url"], div#apply_name_change ul li input[type="color"], div#apply_name_change ul li input[type="date"], div#apply_name_change ul li input[type="datetime"], div#apply_name_change ul li input[type="datetime-local"], div#apply_name_change ul li input[type="month"], div#apply_name_change ul li input[type="time"], div#apply_name_change ul li input[type="week"], div#change_email ul li textarea, div#change_email ul li input[type="email"], div#change_email ul li input[type="number"], div#change_email ul li input[type="password"], div#change_email ul li input[type="search"], div#change_email ul li input[type="tel"], div#change_email ul li input[type="text"], div#change_email ul li input[type="url"], div#change_email ul li input[type="color"], div#change_email ul li input[type="date"], div#change_email ul li input[type="datetime"], div#change_email ul li input[type="datetime-local"], div#change_email ul li input[type="month"], div#change_email ul li input[type="time"], div#change_email ul li input[type="week"], div#unenroll ul li textarea, div#unenroll ul li input[type="email"], div#unenroll ul li input[type="number"], div#unenroll ul li input[type="password"], div#unenroll ul li input[type="search"], div#unenroll ul li input[type="tel"], div#unenroll ul li input[type="text"], div#unenroll ul li input[type="url"], div#unenroll ul li input[type="color"], div#unenroll ul li input[type="date"], div#unenroll ul li input[type="datetime"], div#unenroll ul li input[type="datetime-local"], div#unenroll ul li input[type="month"], div#unenroll ul li input[type="time"], div#unenroll ul li input[type="week"], div#deactivate-account ul li textarea, div#deactivate-account ul li input[type="email"], div#deactivate-account ul li input[type="number"], div#deactivate-account ul li input[type="password"], div#deactivate-account ul li input[type="search"], div#deactivate-account ul li input[type="tel"], div#deactivate-account ul li input[type="text"], div#deactivate-account ul li input[type="url"], div#deactivate-account ul li input[type="color"], div#deactivate-account ul li input[type="date"], div#deactivate-account ul li input[type="datetime"], div#deactivate-account ul li input[type="datetime-local"], div#deactivate-account ul li input[type="month"], div#deactivate-account ul li input[type="time"], div#deactivate-account ul li input[type="week"] {
display: block;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
box-sizing: border-box;
display: block;
width: 100%; }
div#apply_name_change ul li textarea, div#change_email ul li textarea, div#unenroll ul li textarea, div#deactivate-account ul li textarea {
height: 60px; }
div#apply_name_change ul li input[type="submit"], div#change_email ul li input[type="submit"], div#unenroll ul li input[type="submit"], div#deactivate-account ul li input[type="submit"] {
......
......@@ -12,10 +12,6 @@
<script type="text/javascript">
$(function() {
${init}
$(".sequence-nav li a").hover(function(){
$(this).siblings().toggleClass("shown");
});
});
</script>
</%block>
......
<html>
<head>
<link rel="stylesheet" href="${ settings.LIB_URL }jquery.treeview.css" type="text/css" media="all" />
<link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" />
<script type="text/javascript" src="${ settings.LIB_URL }jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script>
<script type="text/javascript" src="/static/js/schematic.js"></script>
<%include file="mathjax_include.html" />
<script>
function postJSON(url, data, callback) {
$.ajax({type:'POST',
url: url,
dataType: 'json',
data: data,
success: callback,
headers : {'X-CSRFToken':'none'} // getCookie('csrftoken')}
});
}
</script>
</head>
<body>
<!--[if lt IE 9]>
<script src="/static/js/html5shiv.js"></script>
<![endif]-->
<style type="text/css">
.CodeMirror {border-style: solid;
border-width: 1px;}
.CodeMirror-scroll {
height: 500;
width: 100%
}
</style>
## -----------------------------------------------------------------------------
## information and i4x PSL code
<hr width="100%">
<h2>QuickEdit</h2>
<hr width="100%">
<ul>
<li>File = ${filename}</li>
<li>ID = ${id}</li>
</ul>
<form method="post">
<textarea rows="40" cols="160" name="quickedit_${id}" id="quickedit_${id}">${pxmls|h}</textarea>
<br/>
<input type="submit" value="Change Problem" name="qesubmit" />
<input type="submit" value="Revert to original" name="qesubmit" />
</form>
<span>${msg|n}</span>
## -----------------------------------------------------------------------------
## rendered problem display
<script>
// height: auto;
// overflow-y: hidden;
// overflow-x: auto;
$(function(){
var cm = CodeMirror.fromTextArea(document.getElementById("quickedit_${id}"),
{ 'mode': {name: "xml", alignCDATA: true},
lineNumbers: true
});
// $('.my-wymeditor').wymeditor();
});
</script>
<hr width="100%">
<script>
${init_js}
</script>
<style type="text/css">
.staff {display:none;}
}
</style>
<form>
${phtml}
</form>
</body>
</html>
......@@ -2,10 +2,10 @@ section.help.main-content {
padding: lh();
h1 {
margin-top: 0;
border-bottom: 1px solid #ddd;
margin-bottom: lh();
margin-top: 0;
padding-bottom: lh();
border-bottom: 1px solid #ddd;
}
p {
......@@ -17,9 +17,9 @@ section.help.main-content {
}
section.self-help {
float: left;
margin-bottom: lh();
margin-right: flex-gutter();
float: left;
width: flex-grid(6);
ul {
......@@ -36,17 +36,17 @@ section.help.main-content {
width: flex-grid(6);
dl {
margin-bottom: lh();
display: block;
margin-bottom: lh();
dd {
margin-bottom: lh();
}
dt {
font-weight: bold;
float: left;
clear: left;
float: left;
font-weight: bold;
width: flex-grid(2, 6);
}
}
......
......@@ -16,28 +16,28 @@ div.info-wrapper {
list-style: none;
> li {
padding-bottom: lh(.5);
margin-bottom: lh(.5);
@extend .clearfix;
border-bottom: 1px solid #e3e3e3;
margin-bottom: lh(.5);
padding-bottom: lh(.5);
&:first-child {
padding: lh(.5);
margin: 0 (-(lh(.5))) lh();
background: $cream;
border-bottom: 1px solid darken($cream, 10%);
margin: 0 (-(lh(.5))) lh();
padding: lh(.5);
}
h2 {
float: left;
width: flex-grid(2, 9);
margin: 0 flex-gutter() 0 0;
width: flex-grid(2, 9);
}
section.update-description {
float: left;
width: flex-grid(7, 9);
margin-bottom: 0;
width: flex-grid(7, 9);
li {
margin-bottom: lh(.5);
......@@ -55,9 +55,9 @@ div.info-wrapper {
section.handouts {
@extend .sidebar;
border-left: 1px solid #d3d3d3;
@include border-radius(0 4px 4px 0);
border-right: 0;
border-left: 1px solid #d3d3d3;
header {
@extend .bottom-border;
......@@ -69,32 +69,32 @@ div.info-wrapper {
}
p {
color: #666;
font-size: 12px;
margin-bottom: 0;
margin-top: 4px;
font-size: 12px;
color: #666;
}
}
ol {
list-style: none;
background: none;
list-style: none;
li {
@include box-shadow(0 1px 0 #eee);
@extend .clearfix;
background: none;
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
@include box-sizing(border-box);
@extend .clearfix;
padding: 7px lh(.75);
background: none;
position: relative;
&.expandable,
&.collapsable {
h4 {
padding-left: 18px;
font-style: $body-font-size;
font-weight: normal;
padding-left: 18px;
}
}
......@@ -103,10 +103,10 @@ div.info-wrapper {
margin: 7px (-(lh(.75))) 0;
li {
padding-left: 18px + lh(.75);
@include box-shadow(inset 0 1px 0 #eee);
border-top: 1px solid #d3d3d3;
border-bottom: 0;
border-top: 1px solid #d3d3d3;
@include box-shadow(inset 0 1px 0 #eee);
padding-left: 18px + lh(.75);
}
}
......@@ -116,13 +116,13 @@ div.info-wrapper {
div.hitarea {
background-image: url('/static/images/treeview-default.gif');
width: 100%;
height: 100%;
max-height: 20px;
display: block;
position: absolute;
height: 100%;
left: lh(.75);
margin-left: 0;
max-height: 20px;
position: absolute;
width: 100%;
&:hover {
opacity: 0.6;
......@@ -140,27 +140,27 @@ div.info-wrapper {
h3 {
border-bottom: 0;
text-transform: uppercase;
font-weight: bold;
color: #999;
@include box-shadow(none);
color: #999;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
p {
font-size: $body-font-size;
letter-spacing: 0;
margin: 0;
text-transform: none;
letter-spacing: 0;
font-size: $body-font-size;
a {
padding-right: 8px;
&:before {
color: #ccc;
content: "•";
@include inline-block();
padding-right: 8px;
color: #ccc;
}
&:first-child {
......@@ -173,10 +173,10 @@ div.info-wrapper {
}
a {
@include transition();
color: lighten($text-color, 10%);
text-decoration: none;
@include inline-block();
text-decoration: none;
@include transition();
&:hover {
color: $mit-red;
......
......@@ -4,14 +4,14 @@ div.profile-wrapper {
section.user-info {
@extend .sidebar;
@include border-radius(0px 4px 4px 0);
border-left: 1px solid #d3d3d3;
@include border-radius(0px 4px 4px 0);
border-right: 0;
header {
padding: lh(.5) lh();
margin: 0 ;
@extend .bottom-border;
margin: 0 ;
padding: lh(.5) lh();
h1 {
font-size: 18px;
......@@ -20,12 +20,12 @@ div.profile-wrapper {
}
a {
color: #999;
font-size: 12px;
position: absolute;
top: 13px;
right: lh(.5);
text-transform: uppercase;
font-size: 12px;
color: #999;
top: 13px;
&:hover {
color: #555;
......@@ -37,14 +37,14 @@ div.profile-wrapper {
list-style: none;
li {
@include transition();
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
color: lighten($text-color, 10%);
display: block;
text-decoration: none;
@include box-shadow(0 1px 0 #eee);
padding: 7px lh();
border-bottom: 1px solid #d3d3d3;
position: relative;
text-decoration: none;
@include transition();
div#location_sub, div#language_sub {
font-weight: bold;
......@@ -57,9 +57,9 @@ div.profile-wrapper {
input {
&[type="text"] {
@include box-sizing(border-box);
margin: lh(.5) 0;
width: 100%;
@include box-sizing(border-box);
}
&[type="input"]{
......@@ -80,12 +80,12 @@ div.profile-wrapper {
a.edit-email,
a.name-edit,
a.email-edit {
color: #999;
font-size: 12px;
position: absolute;
top: 9px;
right: lh(.5);
text-transform: uppercase;
font-size: 12px;
color: #999;
top: 9px;
&:hover {
color: #555;
......@@ -93,10 +93,10 @@ div.profile-wrapper {
}
p {
color: #999;
font-size: 12px;
margin-bottom: 0;
margin-top: 4px;
color: #999;
}
a.deactivate {
......@@ -132,10 +132,10 @@ div.profile-wrapper {
padding: 7px lh();
h2 {
margin-top: 0;
font-size: $body-font-size;
font-weight: bold;
margin-top: 0;
text-transform: uppercase;
font-size: $body-font-size;
}
}
}
......@@ -148,14 +148,14 @@ div.profile-wrapper {
@extend .clearfix;
h1 {
margin: 0;
float: left;
margin: 0;
}
}
div#grade-detail-graph {
width: 100%;
min-height: 300px;
width: 100%;
}
> ol {
......
......@@ -3,8 +3,8 @@ div.book-wrapper {
section.book-sidebar {
@extend .sidebar;
@include box-sizing(border-box);
@extend .tran;
@include box-sizing(border-box);
ul#booknav {
font-size: 12px;
......@@ -22,14 +22,14 @@ div.book-wrapper {
padding-left: 30px;
div.hitarea {
margin-left: -22px;
background-image: url('/static/images/treeview-default.gif');
margin-left: -22px;
position: relative;
top: 4px;
&:hover {
opacity: 0.6;
filter: alpha(opacity=60);
opacity: 0.6;
}
}
......@@ -63,13 +63,13 @@ div.book-wrapper {
li {
&.last {
float: left;
display: block;
float: left;
a {
@include box-shadow(inset -1px 0 0 lighten(#f6efd4, 5%));
border-right: 1px solid darken(#f6efd4, 20%);
border-left: 0;
border-right: 1px solid darken(#f6efd4, 20%);
@include box-shadow(inset -1px 0 0 lighten(#f6efd4, 5%));
}
}
......@@ -81,10 +81,10 @@ div.book-wrapper {
}
&.bottom-nav {
margin-top: lh();
margin-bottom: -(lh());
border-bottom: 0;
border-top: 1px solid #EDDFAA;
margin-bottom: -(lh());
margin-top: lh();
}
}
......@@ -110,18 +110,18 @@ div.book-wrapper {
}
h2 {
padding: 0;
visibility: hidden;
width: 10px;
padding: 0;
}
}
ul#booknav {
max-height: 100px;
overflow: hidden;
padding: 0;
visibility: hidden;
width: 10px;
padding: 0;
overflow: hidden;
max-height: 100px;
}
}
......
......@@ -35,10 +35,10 @@ img {
}
#{$all-text-inputs}, textarea {
@include box-shadow(0 -1px 0 #fff);
@include linear-gradient(#eee, #fff);
border: 1px solid #999;
@include box-shadow(0 -1px 0 #fff);
font: $body-font-size $body-font-family;
@include linear-gradient(#eee, #fff);
padding: 4px;
&:focus {
......
.clearfix:after {
clear: both;
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
......@@ -40,27 +40,27 @@ h1.top-header {
-webkit-font-smoothing: antialiased;
&:hover, &:focus {
border: 1px solid darken(#888, 20%);
@include box-shadow(inset 0 1px 0 lighten(#888, 20%), 0 0 3px #ccc);
@include linear-gradient(lighten(#888, 10%), darken(#888, 5%));
border: 1px solid darken(#888, 20%);
}
}
.light-button, a.light-button {
@include box-shadow(inset 0 1px 0 #fff);
@include linear-gradient(#fff, lighten(#888, 40%));
@include border-radius(3px);
border: 1px solid #ccc;
padding: 4px 8px;
@include border-radius(3px);
@include box-shadow(inset 0 1px 0 #fff);
color: #666;
cursor: pointer;
font: normal $body-font-size $body-font-family;
@include linear-gradient(#fff, lighten(#888, 40%));
padding: 4px 8px;
text-decoration: none;
cursor: pointer;
-webkit-font-smoothing: antialiased;
&:hover, &:focus {
@include linear-gradient(#fff, lighten(#888, 37%));
border: 1px solid #ccc;
@include linear-gradient(#fff, lighten(#888, 37%));
text-decoration: none;
}
}
......@@ -70,8 +70,8 @@ h1.top-header {
color: $mit-red;
&:hover {
text-decoration: none;
color: darken($mit-red, 20%);
text-decoration: none;
}
}
}
......@@ -110,13 +110,13 @@ h1.top-header {
}
a {
font-style: normal;
border: none;
font-style: normal;
}
.bottom-border {
@include box-shadow(0 1px 0 #eee);
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
}
@media print {
......@@ -124,10 +124,10 @@ h1.top-header {
}
h3 {
border: none;
border-bottom: 1px solid #d3d3d3;
@extend .bottom-border;
background: none;
border: none;
border-bottom: 1px solid #d3d3d3;
color: #000;
font-weight: normal;
margin: 0;
......@@ -172,8 +172,8 @@ h1.top-header {
position: relative;
h2 {
padding-right: 20px;
margin: 0;
padding-right: 20px;
}
a {
......@@ -205,10 +205,10 @@ h1.top-header {
border-bottom: 1px solid darken($cream, 10%);
@include box-shadow(inset 0 1px 0 #fff, inset 1px 0 0 #fff);
font-size: 12px;
height:46px;
line-height: 46px;
margin: (-$body-line-height) (-$body-line-height) $body-line-height;
text-shadow: 0 1px 0 #fff;
line-height: 46px;
height:46px;
@media print {
display: none;
......@@ -242,10 +242,10 @@ h1.top-header {
}
p.ie-warning {
background: yellow;
display: block !important;
line-height: 1.3em;
background: yellow;
margin-bottom: 0;
padding: lh();
text-align: left;
margin-bottom: 0;
}
// Flexible grid
@function flex-grid($columns, $container-columns: $fg-max-columns) {
$width: $columns * $fg-column + ($columns - 1) * $fg-gutter;
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($width / $container-width);
}
// Flexible grid gutter
@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) {
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($gutter / $container-width);
}
// Percentage of container calculator
@function perc($width, $container-width: $max-width) {
@return percentage($width / $container-width);
}
// Line-height
@function lh($amount: 1) {
@return $body-line-height * $amount;
......
// Variables
// ---------------------------------------- //
// fonts
// Type
$body-font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
$body-font-size: 14px;
// grid
$columns: 12;
$column-width: 80px;
$gutter-width: 25px;
$max-width: ($columns * $column-width) + (($columns - 1) * $gutter-width);
$gw-column: perc($column-width);
$gw-gutter: perc($gutter-width);
$body-line-height: golden-ratio($body-font-size, 1);
//Flexible grid
$fg-column: $column-width;
$fg-gutter: $gutter-width;
$fg-max-columns: $columns;
// Grid
$fg-column: 80px;
$fg-gutter: 25px;
$fg-max-columns: 12;
$fg-max-width: 1400px;
$fg-min-width: 810px;
// color
// Color
$light-gray: #ddd;
$dark-gray: #333;
$mit-red: #993333;
$cream: #F6EFD4;
$text-color: $dark-gray;
$border-color: $light-gray;
// JM MOSFET AMPLIFIER
section.tool-wrapper {
@extend .clearfix;
background: #073642;
border-top: 1px solid darken(#002b36, 10%);
border-bottom: 1px solid darken(#002b36, 10%);
border-top: 1px solid darken(#002b36, 10%);
@include box-shadow(inset 0 0 0 4px darken(#094959, 2%));
margin: lh() (-(lh())) 0;
color: #839496;
@extend .clearfix;
display: table;
margin: lh() (-(lh())) 0;
div#graph-container {
background: none;
......@@ -29,24 +29,24 @@ section.tool-wrapper {
ul.ui-tabs-nav {
background: darken(#073642, 2%);
border-bottom: 1px solid darken(#073642, 8%);
@include border-radius(0);
margin: (-(lh())) (-(lh())) 0;
padding: 0;
position: relative;
width: 110%;
@include border-radius(0);
border-bottom: 1px solid darken(#073642, 8%);
li {
margin-bottom: 0;
background: none;
color: #fff;
border: none;
@include border-radius(0);
color: #fff;
margin-bottom: 0;
&.ui-tabs-selected {
border-right: 1px solid darken(#073642, 8%);
border-left: 1px solid darken(#073642, 8%);
background-color: #073642;
border-left: 1px solid darken(#073642, 8%);
border-right: 1px solid darken(#073642, 8%);
&:first-child {
border-left: none;
......@@ -59,10 +59,10 @@ section.tool-wrapper {
a {
border: none;
color: #839496;
font: bold 12px $body-font-family;
text-transform: uppercase;
letter-spacing: 1px;
color: #839496;
text-transform: uppercase;
&:hover {
color: #eee8d5;
......@@ -86,18 +86,18 @@ section.tool-wrapper {
div.graph-controls {
div.music-wrapper {
padding: 0 0 lh();
margin-bottom: lh();
@extend .clearfix;
border-bottom: 1px solid darken(#073642, 10%);
@include box-shadow(0 1px 0 lighten(#073642, 2%));
@extend .clearfix;
margin-bottom: lh();
padding: 0 0 lh();
input#playButton {
display: block;
@include button(simple, lighten( #586e75, 5% ));
font: bold 14px $body-font-family;
border-color: darken(#002b36, 6%);
@include button(simple, lighten( #586e75, 5% ));
display: block;
float: right;
font: bold 14px $body-font-family;
&:active {
@include box-shadow(none);
......@@ -115,21 +115,21 @@ section.tool-wrapper {
}
div.inputs-wrapper {
@extend .clearfix;
border-bottom: 1px solid darken(#073642, 10%);
@include box-shadow(0 1px 0 lighten(#073642, 2%));
@include clearfix;
margin-bottom: lh();
padding: 0 0 lh();
margin-bottom: lh();
border-bottom: 1px solid darken(#073642, 10%);
@include box-shadow(0 1px 0 lighten(#073642, 2%));
@extend .clearfix;
padding: 0 0 lh();
}
p {
font-weight: bold;
@include inline-block();
margin: 0;
-webkit-font-smoothing: antialiased;
font-weight: bold;
text-shadow: 0 -1px 0 darken(#073642, 10%);
-webkit-font-smoothing: antialiased;
}
ul {
......@@ -147,19 +147,19 @@ section.tool-wrapper {
}
div#graph-listen {
margin-top: 8px;
margin-right: 20px;
display: block;
text-align: right;
float: left;
margin-bottom: 0;
margin-right: 20px;
margin-top: 8px;
text-align: right;
}
}
label {
@include border-radius(2px);
font-weight: bold;
color: #fff;
font-weight: bold;
padding: 3px;
-webkit-font-smoothing: antialiased;
}
......@@ -188,33 +188,31 @@ section.tool-wrapper {
}
div.schematic-sliders {
div.top-sliders {
padding: 0 0 lh();
margin-bottom: lh();
@extend .clearfix;
border-bottom: 1px solid darken(#073642, 10%);
@include box-shadow(0 1px 0 lighten(#073642, 2%));
@extend .clearfix;
select#musicTypeSelect {
@include inline-block();
font: 16px $body-font-family;
margin-bottom: 0;
}
margin-bottom: lh();
padding: 0 0 lh();
p {
@include inline-block();
-webkit-font-smoothing: antialiased;
text-shadow: 0 -1px 0 darken(#073642, 10%);
margin: 0 lh(.5) lh() 0;
font-weight: bold;
}
select#musicTypeSelect {
font: 16px $body-font-family;
@include inline-block();
margin-bottom: 0;
}
p {
font-weight: bold;
@include inline-block();
margin: 0 lh(.5) lh() 0;
text-shadow: 0 -1px 0 darken(#073642, 10%);
-webkit-font-smoothing: antialiased;
}
}
div.slider-label {
margin-bottom: lh(0.5);
font-weight: bold;
margin-bottom: lh(0.5);
text-shadow: 0 -1px 0 darken(#073642, 10%);
-webkit-font-smoothing: antialiased;
}
......@@ -223,10 +221,10 @@ section.tool-wrapper {
margin-bottom: lh(1);
&.ui-slider-horizontal {
height: 0.4em;
background: darken(#002b36, 2%);
border: 1px solid darken(#002b36, 8%);
@include box-shadow(none);
height: 0.4em;
}
.ui-slider-handle {
......@@ -243,5 +241,3 @@ section.tool-wrapper {
}
}
}
......@@ -18,6 +18,8 @@ div.course-wrapper {
section.course-content {
@extend .content;
overflow: hidden;
@include border-top-right-radius(4px);
@include border-bottom-right-radius(4px);
h1 {
margin: 0 0 lh();
......
nav.sequence-nav {
@extend .topbar;
@include box-sizing(border-box);
border-bottom: 1px solid darken($cream, 20%);
margin-bottom: $body-line-height;
position: relative;
top: -1px;
@include border-top-right-radius(4px);
ol {
border-bottom: 1px solid darken($cream, 20%);
border-top: 1px solid darken($cream, 20%);
@include box-sizing(border-box);
display: table;
height: 100%;
padding-right: flex-grid(1, 9);
width: 100%;
......@@ -63,117 +62,117 @@ nav.sequence-nav {
display: block;
height: 17px;
padding: 15px 0 14px;
position: relative;
@include transition(all, .4s, $ease-in-out-quad);
width: 100%;
// @media screen and (max-width: 800px) {
// padding: 12px 8px;
// }
//video
&.seq_video_inactive {
@extend .inactive;
background-image: url('/static/images/sequence-nav/video-icon-normal.png');
background-position: center;
}
&.seq_video_visited {
@extend .visited;
background-image: url('/static/images/sequence-nav/video-icon-visited.png');
background-position: center;
}
//video
&.seq_video_inactive {
@extend .inactive;
background-image: url('/static/images/sequence-nav/video-icon-normal.png');
background-position: center;
}
&.seq_video_active {
@extend .active;
background-image: url('/static/images/sequence-nav/video-icon-current.png');
background-position: center;
}
&.seq_video_visited {
@extend .visited;
background-image: url('/static/images/sequence-nav/video-icon-visited.png');
background-position: center;
}
//other
&.seq_other_inactive {
@extend .inactive;
background-image: url('/static/images/sequence-nav/document-icon-normal.png');
background-position: center;
}
&.seq_video_active {
@extend .active;
background-image: url('/static/images/sequence-nav/video-icon-current.png');
background-position: center;
}
&.seq_other_visited {
@extend .visited;
background-image: url('/static/images/sequence-nav/document-icon-visited.png');
background-position: center;
}
//other
&.seq_other_inactive {
@extend .inactive;
background-image: url('/static/images/sequence-nav/document-icon-normal.png');
background-position: center;
}
&.seq_other_active {
@extend .active;
background-image: url('/static/images/sequence-nav/document-icon-current.png');
background-position: center;
}
&.seq_other_visited {
@extend .visited;
background-image: url('/static/images/sequence-nav/document-icon-visited.png');
background-position: center;
}
//vertical & problems
&.seq_vertical_inactive, &.seq_problem_inactive {
@extend .inactive;
background-image: url('/static/images/sequence-nav/list-icon-normal.png');
background-position: center;
}
&.seq_other_active {
@extend .active;
background-image: url('/static/images/sequence-nav/document-icon-current.png');
background-position: center;
}
&.seq_vertical_visited, &.seq_problem_visited {
@extend .visited;
background-image: url('/static/images/sequence-nav/list-icon-visited.png');
background-position: center;
}
//vertical & problems
&.seq_vertical_inactive, &.seq_problem_inactive {
@extend .inactive;
background-image: url('/static/images/sequence-nav/list-icon-normal.png');
background-position: center;
}
&.seq_vertical_active, &.seq_problem_active {
@extend .active;
background-image: url('/static/images/sequence-nav/list-icon-current.png');
background-position: center;
}
&.seq_vertical_visited, &.seq_problem_visited {
@extend .visited;
background-image: url('/static/images/sequence-nav/list-icon-visited.png');
background-position: center;
}
p {
// display: none;
// visibility: hidden;
background: #333;
color: #fff;
line-height: lh();
margin: 0px 0 0 -5px;
opacity: 0;
padding: 6px;
position: absolute;
text-shadow: 0 -1px 0 #000;
@include transition(all, .6s, $ease-in-out-quart);
white-space: pre-wrap;
z-index: 99;
&.shown {
margin-top: 4px;
opacity: 1;
&.seq_vertical_active, &.seq_problem_active {
@extend .active;
background-image: url('/static/images/sequence-nav/list-icon-current.png');
background-position: center;
}
&:empty {
background: none;
p {
background: #333;
color: #fff;
display: none;
line-height: lh();
left: 0px;
opacity: 0;
padding: 6px;
position: absolute;
top: 48px;
text-shadow: 0 -1px 0 #000;
@include transition(all, .1s, $ease-in-out-quart);
white-space: pre;
z-index: 99;
&:empty {
background: none;
&::after {
display: none;
}
}
&::after {
display: none;
background: #333;
content: " ";
display: block;
height: 10px;
left: 18px;
position: absolute;
top: -5px;
@include transform(rotate(45deg));
width: 10px;
}
}
&::after {
background: #333;
content: " ";
display: block;
height: 10px;
left: 18px;
position: absolute;
top: -5px;
@include transform(rotate(45deg));
width: 10px;
&:hover {
p {
display: block;
margin-top: 4px;
opacity: 1;
}
}
}
}
}
ul {
margin-right: 1px;
list-style: none !important;
height: 100%;
position: absolute;
right: 0;
top: 0;
......@@ -223,6 +222,7 @@ nav.sequence-nav {
&.next {
a {
background-image: url('/static/images/sequence-nav/next-icon.png');
@include border-top-right-radius(4px);
&:hover {
background-color: none;
......
......@@ -26,6 +26,7 @@ section.course-index {
}
&.ui-state-active {
@include background-image(linear-gradient(-90deg, rgb(245,245,245), rgb(225,225,225)));
@extend .active;
}
}
......@@ -33,59 +34,99 @@ section.course-index {
ul.ui-accordion-content {
@include border-radius(0);
@include box-shadow( inset -1px 0 0 #e6e6e6);
@include box-shadow(inset -1px 0 0 #e6e6e6);
background: #dadada;
border: none;
border-bottom: 1px solid #c3c3c3;
font-size: 12px;
margin: 0;
// overflow: visible;
padding: 1em 1.5em;
li {
background: transparent;
border: 1px solid transparent;
@include border-radius(4px);
margin-bottom: lh(.5);
position: relative;
&.active {
font-weight: bold;
span.subtitle {
font-weight: normal;
}
// &:after {
// content: " ";
// width: 16px;
// height: 16px;
// position: absolute;
// right: -35px;
// top: 7px;
// display: block;
// background-color: #dadada;
// border-top: 1px solid #c3c3c3;
// border-right: 1px solid #c3c3c3;
// z-index: 99;
// @include transform(rotate(45deg));
// }
}
padding: 5px 36px 5px 10px;
a {
text-decoration: none;
margin-bottom: lh(.5);
display: block;
color: #000;
&:hover {
color: #666;
}
color: #666;
p {
font-weight: bold;
margin-bottom: 0;
span.subtitle {
color: #666;
font-weight: normal;
display: block;
}
}
}
&:after {
background: transparent;
border-top: 1px solid rgb(180,180,180);
border-right: 1px solid rgb(180,180,180);
content: "";
display: block;
height: 12px;
margin-top: -6px;
opacity: 0;
position: absolute;
top: 50%;
right: 30px;
@include transform(rotate(45deg));
width: 12px;
}
&:hover {
@include background-image(linear-gradient(-90deg, rgba(245,245,245, 0.4), rgba(230,230,230, 0.4)));
border-color: rgb(200,200,200);
&:after {
opacity: 1;
right: 15px;
@include transition(all, 0.2s, linear);
}
> a p {
color: #333;
}
}
&:active {
@include box-shadow(inset 0 1px 14px 0 rgba(0,0,0, 0.1));
top: 1px;
&:after {
opacity: 1;
right: 15px;
}
}
&.active {
background: rgb(240,240,240);
@include background-image(linear-gradient(-90deg, rgb(245,245,245), rgb(230,230,230)));
border-color: rgb(200,200,200);
font-weight: bold;
> a p {
color: #333;
}
span.subtitle {
font-weight: normal;
}
&:after {
opacity: 1;
right: 15px;
}
}
}
}
}
......
......@@ -2,10 +2,10 @@ li.calc-main {
bottom: -126px;
left: 0;
position: fixed;
width: 100%;
@include transition(bottom);
z-index: 99;
-webkit-appearance: none;
width: 100%;
z-index: 99;
&.open {
bottom: -36px;
......@@ -16,19 +16,19 @@ li.calc-main {
}
a.calc {
@include hide-text;
background: url("/static/images/calc-icon.png") rgba(#111, .9) no-repeat center;
border-bottom: 0;
@include border-radius(3px 3px 0 0);
color: #fff;
float: right;
margin-right: 10px;
@include border-radius(3px 3px 0 0);
height: 20px;
@include hide-text;
@include inline-block;
margin-right: 10px;
padding: 8px 12px;
width: 16px;
height: 20px;
position: relative;
top: -36px;
width: 16px;
&:hover {
opacity: .8;
......@@ -41,15 +41,15 @@ li.calc-main {
div#calculator_wrapper {
background: rgba(#111, .9);
position: relative;
top: -36px;
clear: both;
max-height: 90px;
position: relative;
top: -36px;
form {
padding: lh();
@extend .clearfix;
@include box-sizing(border-box);
padding: lh();
input#calculator_button {
background: #111;
......@@ -94,20 +94,20 @@ li.calc-main {
position: relative;
width: flex-grid(7.5);
input#calculator_input {
border: none;
@include box-shadow(none);
@include box-sizing(border-box);
font-size: 16px;
padding: 10px;
-webkit-appearance: none;
width: 100%;
&:focus {
outline: none;
input#calculator_input {
border: none;
@include box-shadow(none);
@include box-sizing(border-box);
font-size: 16px;
padding: 10px;
-webkit-appearance: none;
width: 100%;
&:focus {
outline: none;
border: none;
}
}
}
div.help-wrapper {
position: absolute;
......@@ -115,10 +115,10 @@ li.calc-main {
top: 15px;
a {
background: url("/static/images/info-icon.png") center center no-repeat;
height: 17px;
@include hide-text;
width: 17px;
height: 17px;
background: url("/static/images/info-icon.png") center center no-repeat;
}
dl {
......@@ -126,14 +126,14 @@ li.calc-main {
@include border-radius(3px);
@include box-shadow(0 0 3px #999);
color: #333;
display: none;
opacity: 0;
padding: 10px;
position: absolute;
right: -40px;
top: -110px;
width: 500px;
display: none;
@include transition();
width: 500px;
&.shown {
opacity: 1;
......
......@@ -68,11 +68,11 @@ footer {
}
a {
border-bottom: 0;
display: block;
height: 29px;
width: 28px;
text-indent: -9999px;
border-bottom: 0;
width: 28px;
&:hover {
opacity: .8;
......
......@@ -100,12 +100,12 @@ div.header-wrapper {
float: left;
a {
border: none;
color: #fff;
display: block;
font-style: normal;
font-weight: bold;
padding: 10px lh() 8px;
border: none;
font-style: normal;
@media screen and (max-width: 1020px) {
padding: 10px lh(.7) 8px;
......@@ -125,10 +125,10 @@ div.header-wrapper {
ul {
li {
padding: auto;
display: table-cell;
width: 16.6666666667%;
padding: auto;
text-align: center;
width: 16.6666666667%;
}
}
}
......
......@@ -2,11 +2,11 @@ html {
margin-top: 0;
body {
background: #f4f4f4; //#f3f1e5
color: $dark-gray;
font: $body-font-size $body-font-family;
text-align: center;
margin: 0;
background: #f4f4f4; //#f3f1e5
text-align: center;
section.main-content {
@extend .clearfix;
......@@ -17,7 +17,7 @@ html {
@include box-shadow(0 0 4px #dfdfdf);
@include box-sizing(border-box);
margin-top: 3px;
// overflow: hidden;
overflow: hidden;
@media print {
border-bottom: 0;
......@@ -32,12 +32,13 @@ html {
div.qtip {
div.ui-tooltip-content {
border: none;
background: #000;
background: rgba(#000, .8);
border: none;
color: #fff;
font: 12px $body-font-family;
margin-top: -30px;
margin-right: -20px;
margin-top: -30px;
}
}
......
#lean_overlay {
background: #000;
display: none;
height:100%;
left: 0px;
position: fixed;
z-index:100;
top: 0px;
left: 0px;
height:100%;
width:100%;
background: #000;
display: none;
z-index:100;
}
div.leanModal_box {
......@@ -31,8 +31,8 @@ div.leanModal_box {
z-index: 2;
&:hover{
text-decoration: none;
color: $mit-red;
text-decoration: none;
}
}
......@@ -55,8 +55,8 @@ div.leanModal_box {
li {
&.terms, &.honor-code {
width: auto;
float: none;
width: auto;
}
div.tip {
......@@ -118,8 +118,8 @@ div.leanModal_box {
}
&.honor-code {
width: auto;
float: none;
width: auto;
}
label {
......@@ -128,8 +128,8 @@ div.leanModal_box {
}
#{$all-text-inputs}, textarea {
width: 100%;
@include box-sizing(border-box);
width: 100%;
}
input[type="checkbox"] {
......@@ -176,15 +176,15 @@ div#login {
ol {
li {
width: auto;
float: none;
width: auto;
}
}
}
div.lost-password {
text-align: left;
margin-top: lh();
text-align: left;
a {
color: #999;
......@@ -218,9 +218,9 @@ div#deactivate-account {
margin-bottom: lh(.5);
textarea, #{$all-text-inputs} {
@include box-sizing(border-box);
display: block;
width: 100%;
@include box-sizing(border-box);
}
textarea {
......
......@@ -2,5 +2,5 @@
@import "base/reset", "base/font-face", "base/functions";
// pages
@import "index/variables", "index/extends", "index/base", "index/header", "index/footer", "index/index";
@import "marketing/variables", "marketing/extends", "marketing/base", "marketing/header", "marketing/footer", "marketing/index";
@import "layout/leanmodal";
form#wiki_revision {
float: left;
width: flex-grid(6, 9);
margin-right: flex-gutter(9);
width: flex-grid(6, 9);
label {
display: block;
margin-bottom: 7px ;
}
.CodeMirror-scroll {
min-height: 550px;
width: 100%;
}
.CodeMirror {
@extend textarea;
@include box-sizing(border-box);
font-family: monospace;
margin-bottom: 20px;
}
textarea {
@include box-sizing(border-box);
margin-bottom: 20px;
......@@ -32,25 +33,25 @@ form#wiki_revision {
}
#submit_delete {
@include box-shadow(none);
background: none;
border: none;
@include box-shadow(none);
color: #999;
float: right;
text-decoration: underline;
font-weight: normal;
text-decoration: underline;
}
input[type="submit"] {
margin-top: 20px;
}
}
#wiki_edit_instructions {
color: #666;
float: left;
width: flex-grid(3, 9);
margin-top: lh();
color: #666;
width: flex-grid(3, 9);
&:hover {
color: #333;
......@@ -58,16 +59,14 @@ form#wiki_revision {
.markdown-example {
background-color: #e3e3e3;
text-shadow: 0 1px 0 #fff;
line-height: 1.0;
margin: 5px 0 7px;
padding: {
top: 5px;
right: 2px;
bottom: 5px;
left: 5px;
}
margin: 5px 0 7px;
line-height: 1.0;
text-shadow: 0 1px 0 #fff;
}
}
......@@ -3,20 +3,20 @@ div#wiki_panel {
overflow: auto;
h2 {
padding: lh(.5) lh();
@extend .bottom-border;
font-size: 18px;
margin: 0 ;
@extend .bottom-border;
padding: lh(.5) lh();
}
input[type="button"] {
@extend h3;
@include transition();
color: lighten($text-color, 10%);
font-size: $body-font-size;
margin: 0 !important;
padding: 7px lh();
text-align: left;
@include transition();
width: 100%;
&:hover {
......@@ -28,8 +28,8 @@ div#wiki_panel {
ul {
li {
&.search {
@include box-shadow(0 1px 0 #eee);
border-bottom: 1px solid #d3d3d3;
@include box-shadow(0 1px 0 #eee);
padding: 7px lh();
label {
......@@ -49,15 +49,15 @@ div#wiki_panel {
div#wiki_create_form {
@extend .clearfix;
padding: 15px;
background: #d6d6d6;
border-bottom: 1px solid #bbb;
padding: 15px;
input[type="text"] {
margin-bottom: 6px;
@include box-sizing(border-box);
display: block;
margin-bottom: 6px;
width: 100%;
@include box-sizing(border-box);
}
ul {
......
......@@ -7,15 +7,14 @@ div.wiki-wrapper {
@extend .content;
position: relative;
header {
@extend .topbar;
height:46px;
@include box-shadow(inset 0 1px 0 white);
height:46px;
&:empty {
display: none !important;
border-bottom: 0;
display: none !important;
}
a {
......@@ -23,10 +22,10 @@ div.wiki-wrapper {
}
p {
float: left;
margin-bottom: 0;
color: darken($cream, 55%);
float: left;
line-height: 46px;
margin-bottom: 0;
padding-left: lh();
}
......@@ -48,8 +47,8 @@ div.wiki-wrapper {
@include box-shadow(inset 1px 0 0 lighten(#f6efd4, 5%));
color: darken($cream, 80%);
display: block;
font-weight: normal;
font-size: 12px;
font-weight: normal;
letter-spacing: 1px;
line-height: 46px;
margin: 0;
......@@ -89,15 +88,15 @@ div.wiki-wrapper {
width: flex-grid(2.5, 9);
@media screen and (max-width:900px) {
border-right: 0;
display: block;
width: auto;
border-right: 0;
}
@media print {
border-right: 0;
display: block;
width: auto;
border-right: 0;
}
}
......@@ -106,9 +105,9 @@ div.wiki-wrapper {
}
section.results {
border-left: 1px dashed #ddd;
@include box-sizing(border-box);
display: inline-block;
border-left: 1px dashed #ddd;
float: left;
padding-left: 10px;
width: flex-grid(6.5, 9);
......@@ -123,8 +122,8 @@ div.wiki-wrapper {
@media print {
display: block;
width: auto;
padding: 0;
width: auto;
canvas, img {
page-break-inside: avoid;
......@@ -140,14 +139,15 @@ div.wiki-wrapper {
}
li {
border-bottom: 1px solid #eee;
list-style: none;
margin: 0;
padding: 10px 0;
border-bottom: 1px solid #eee;
&:last-child {
border-bottom: 0;
}
h3 {
font-size: 18px;
font-weight: normal;
......@@ -155,6 +155,5 @@ div.wiki-wrapper {
}
}
}
}
}
......@@ -2,7 +2,7 @@
var ${ id }contents=["",
%for t in items:
${t['content']} ,
${t['content']} ,
%endfor
""
];
......@@ -16,7 +16,7 @@ var ${ id }types=["",
var ${ id }init_functions=["",
%for t in items:
function(){ ${t['init_js']} },
function(){ ${t['init_js']} },
%endfor
""];
......@@ -24,12 +24,12 @@ var ${ id }titles=${titles};
var ${ id }destroy_functions=["",
%for t in items:
function(){ ${t['destroy_js']} },
function(){ ${t['destroy_js']} },
%endfor
""];
var ${ id }loc = -1;
function disablePrev() {
function disablePrev() {
var i=${ id }loc-1;
log_event("seq_prev", {'old':${id}loc, 'new':i,'id':'${id}'});
if (i < 1 ) {
......@@ -39,7 +39,7 @@ function disablePrev() {
};
}
function disableNext() {
function disableNext() {
var i=${ id }loc+1;
log_event("seq_next", {'old':${id}loc, 'new':i,'id':'${id}'});
......@@ -77,11 +77,11 @@ function ${ id }goto(i) {
function ${ id }setup_click(i) {
$('#tt_'+i).click(function(eo) { ${ id }goto(i);});
$('#tt_'+i).addClass("seq_"+${ id }types[i]+"_inactive");
$('#tt_'+i).parent().append("<p>" + ${ id }titles[i-1] + "</p>");
$('#tt_'+i).append("<p>" + ${ id }titles[i-1] + "</p>");
}
function ${ id }next() {
function ${ id }next() {
var i=${ id }loc+1;
log_event("seq_next", {'old':${id}loc, 'new':i,'id':'${id}'});
if(i > ${ len(items) } ) {
......@@ -92,7 +92,7 @@ function ${ id }next() {
}
function ${ id }prev() {
function ${ id }prev() {
var i=${ id }loc-1;
log_event("seq_prev", {'old':${id}loc, 'new':i,'id':'${id}'});
if (i < 1 ) {
......@@ -105,7 +105,7 @@ function ${ id }prev() {
$(function() {
var i;
var i;
for(i=1; i<${ len(items)+1 }; i++) {
${ id }setup_click(i);
}
......
......@@ -69,6 +69,12 @@ if settings.COURSEWARE_ENABLED:
url(r'^calculate$', 'util.views.calculate'),
)
if settings.ENABLE_MULTICOURSE:
urlpatterns += (url(r'^mitxhome$', 'util.views.mitxhome'),)
if settings.QUICKEDIT:
urlpatterns += (url(r'^quickedit/(?P<id>[^/]*)$', 'courseware.views.quickedit'),)
if settings.ASKBOT_ENABLED:
urlpatterns += (url(r'^%s' % settings.ASKBOT_URL, include('askbot.urls')), \
url(r'^admin/', include(admin.site.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