290 lines
8.8 KiB
Python
290 lines
8.8 KiB
Python
# Copyright (C) 2011 Bradley N. Miller
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
__author__ = 'bmiller'
|
|
|
|
from docutils import nodes
|
|
from docutils.parsers.rst import directives
|
|
from docutils.parsers.rst import Directive
|
|
import json
|
|
import os
|
|
|
|
# try:
|
|
# import conf
|
|
# version = conf.version
|
|
# staticserver = conf.staticserver
|
|
# except:
|
|
# version = '2.1.0'
|
|
# staticserver = 'runestonestatic.appspot.com'
|
|
|
|
def setup(app):
|
|
app.add_directive('activecode',ActiveCode)
|
|
app.add_directive('actex',ActiveExercise)
|
|
app.add_stylesheet('codemirror.css')
|
|
app.add_stylesheet('activecode.css')
|
|
|
|
app.add_javascript('jquery.highlight.js' )
|
|
app.add_javascript('bookfuncs.js' )
|
|
app.add_javascript('codemirror.js' )
|
|
app.add_javascript('python.js' )
|
|
app.add_javascript('javascript.js' )
|
|
app.add_javascript('activecode.js')
|
|
app.add_javascript('skulpt.min.js' )
|
|
app.add_javascript('skulpt-stdlib.js')
|
|
|
|
app.add_node(ActivcodeNode, html=(visit_ac_node, depart_ac_node))
|
|
|
|
app.connect('doctree-resolved',process_activcode_nodes)
|
|
app.connect('env-purge-doc', purge_activecodes)
|
|
|
|
|
|
|
|
START = '''
|
|
<div id="%(divid)s" lang="%(language)s" >
|
|
'''
|
|
|
|
|
|
EDIT1 = '''
|
|
<br/>
|
|
<div id="%(divid)s_code_div" style="display: %(hidecode)s">
|
|
<textarea cols="50" rows="12" id="%(divid)s_code" class="active_code", prefixcode="%(include)s" lang="%(language)s">
|
|
%(initialcode)s
|
|
</textarea>
|
|
</div>
|
|
<p class="ac_caption"><span class="ac_caption_text">%(caption)s (%(divid)s)</span> </p>
|
|
|
|
<button class='btn btn-success' id="%(divid)s_runb" onclick="runit('%(divid)s',this, %(include)s);">Run</button>
|
|
'''
|
|
|
|
UNHIDE='''
|
|
<button class='btn btn-default' id="%(divid)s_showb" onclick="$('#%(divid)s_code_div').toggle();cm_editors['%(divid)s_code'].refresh();$('#%(divid)s_saveb').toggle();$('#%(divid)s_loadb').toggle()">Show/Hide Code</button>
|
|
'''
|
|
|
|
GRADES = '''
|
|
<input type="button" class='btn btn-default ' id="gradeb" name="Show Feedback" value="Show Feedback" onclick="createGradeSummary('%(divid)s')"/>
|
|
'''
|
|
|
|
AUDIO = '''
|
|
<input type="button" class='btn btn-default ' id="audiob" name="Play Audio" value="Start Audio Tour" onclick="createAudioTourHTML('%(divid)s','%(argu)s','%(no_of_buttons)s','%(ctext)s')"/>
|
|
'''
|
|
|
|
EDIT2 = '''
|
|
<div id="cont"></div>
|
|
|
|
<button class="ac_opt btn btn-default" style="display: inline-block" id="%(divid)s_saveb" onclick="saveEditor('%(divid)s');">Save</button>
|
|
<button class="ac_opt btn btn-default" style="display: inline-block" id="%(divid)s_loadb" onclick="requestCode('%(divid)s');">Load</button>
|
|
|
|
<script>
|
|
if ('%(hidecode)s' == 'none') {
|
|
// a hack to preserve the inline-block display style. Toggle() will use display: block
|
|
// (instead of inline-block) if the previous display style was 'none'
|
|
$('#%(divid)s_saveb').toggle();
|
|
$('#%(divid)s_loadb').toggle();
|
|
}
|
|
</script>
|
|
|
|
'''
|
|
|
|
CANVAS = '''
|
|
<div style="text-align: center">
|
|
<canvas id="%(divid)s_canvas" class="ac-canvas" height="400" width="400" style="border-style: solid; display: none; text-align: center"></canvas>
|
|
</div>
|
|
'''
|
|
|
|
SUFF = '''<pre id="%(divid)s_suffix" style="display:none">%(suffix)s</pre>'''
|
|
|
|
PRE = '''
|
|
<pre id="%(divid)s_pre" class="active_out">
|
|
|
|
</pre>
|
|
|
|
'''
|
|
|
|
END = '''
|
|
</div>
|
|
|
|
'''
|
|
|
|
AUTO = '''
|
|
<script type="text/javascript">
|
|
$(document).ready(function() {
|
|
$(window).load(function() {
|
|
var runb = document.getElementById("%(divid)s_runb");
|
|
runit('%(divid)s',runb, %(include)s);
|
|
});
|
|
});
|
|
</script>
|
|
'''
|
|
|
|
#'
|
|
class ActivcodeNode(nodes.General, nodes.Element):
|
|
def __init__(self,content):
|
|
"""
|
|
|
|
Arguments:
|
|
- `self`:
|
|
- `content`:
|
|
"""
|
|
super(ActivcodeNode,self).__init__()
|
|
self.ac_components = content
|
|
|
|
# self for these functions is an instance of the writer class. For example
|
|
# in html, self is sphinx.writers.html.SmartyPantsHTMLTranslator
|
|
# The node that is passed as a parameter is an instance of our node class.
|
|
def visit_ac_node(self,node):
|
|
#print self.settings.env.activecodecounter
|
|
res = START
|
|
if 'above' in node.ac_components:
|
|
res += CANVAS
|
|
res += EDIT1
|
|
if 'tour_1' not in node.ac_components:
|
|
res += EDIT2
|
|
else:
|
|
res += AUDIO + EDIT2
|
|
if 'above' not in node.ac_components:
|
|
if 'nocanvas' not in node.ac_components:
|
|
res += CANVAS
|
|
if 'hidecode' not in node.ac_components:
|
|
node.ac_components['hidecode'] = 'block'
|
|
if node.ac_components['hidecode'] == 'none':
|
|
res += UNHIDE
|
|
if 'gradebutton' in node.ac_components:
|
|
res += GRADES
|
|
if 'suffix' in node.ac_components:
|
|
res += SUFF
|
|
if 'nopre' not in node.ac_components:
|
|
res += PRE
|
|
if 'autorun' in node.ac_components:
|
|
res += AUTO
|
|
res += END
|
|
res = res % node.ac_components
|
|
res = res.replace("u'","'") # hack: there must be a better way to include the list and avoid unicode strings
|
|
|
|
self.body.append(res)
|
|
|
|
def depart_ac_node(self,node):
|
|
''' This is called at the start of processing an activecode node. If activecode had recursive nodes
|
|
etc and did not want to do all of the processing in visit_ac_node any finishing touches could be
|
|
added here.
|
|
'''
|
|
pass
|
|
|
|
|
|
def process_activcode_nodes(app,env,docname):
|
|
pass
|
|
|
|
|
|
def purge_activecodes(app,env,docname):
|
|
pass
|
|
|
|
|
|
class ActiveCode(Directive):
|
|
required_arguments = 1
|
|
optional_arguments = 1
|
|
has_content = True
|
|
option_spec = {
|
|
'nocanvas':directives.flag,
|
|
'nopre':directives.flag,
|
|
'above':directives.flag, # put the canvas above the code
|
|
'autorun':directives.flag,
|
|
'caption':directives.unchanged,
|
|
'include':directives.unchanged,
|
|
'hidecode':directives.flag,
|
|
'language':directives.unchanged,
|
|
'tour_1':directives.unchanged,
|
|
'tour_2':directives.unchanged,
|
|
'tour_3':directives.unchanged,
|
|
'tour_4':directives.unchanged,
|
|
'tour_5':directives.unchanged
|
|
}
|
|
|
|
def run(self):
|
|
env = self.state.document.settings.env
|
|
# keep track of how many activecodes we have.... could be used to automatically make a unique id for them.
|
|
if not hasattr(env,'activecodecounter'):
|
|
env.activecodecounter = 0
|
|
env.activecodecounter += 1
|
|
|
|
self.options['divid'] = self.arguments[0]
|
|
|
|
if self.content:
|
|
if '====' in self.content:
|
|
idx = self.content.index('====')
|
|
source = "\n".join(self.content[:idx])
|
|
suffix = "\n".join(self.content[idx+1:])
|
|
else:
|
|
source = "\n".join(self.content)
|
|
suffix = "\n"
|
|
else:
|
|
source = '\n'
|
|
suffix = '\n'
|
|
|
|
self.options['initialcode'] = source
|
|
self.options['suffix'] = suffix
|
|
str=source.replace("\n","*nline*")
|
|
str0=str.replace("\"","*doubleq*")
|
|
str1=str0.replace("(","*open*")
|
|
str2=str1.replace(")","*close*")
|
|
str3=str2.replace("'","*singleq*")
|
|
self.options['argu']=str3
|
|
|
|
complete=""
|
|
no_of_buttons=0
|
|
okeys = self.options.keys()
|
|
for k in okeys:
|
|
if '_' in k:
|
|
x,label = k.split('_')
|
|
no_of_buttons=no_of_buttons+1
|
|
complete=complete+self.options[k]+"*atype*"
|
|
|
|
newcomplete=complete.replace("\"","*doubleq*")
|
|
self.options['ctext'] = newcomplete
|
|
self.options['no_of_buttons'] = no_of_buttons
|
|
|
|
if 'caption' not in self.options:
|
|
self.options['caption'] = ''
|
|
|
|
if 'include' not in self.options:
|
|
self.options['include'] = 'undefined'
|
|
else:
|
|
lst = self.options['include'].split(',')
|
|
lst = [x.strip() for x in lst]
|
|
self.options['include'] = lst
|
|
|
|
if 'hidecode' in self.options:
|
|
self.options['hidecode'] = 'none'
|
|
else:
|
|
self.options['hidecode'] = 'block'
|
|
|
|
if 'language' not in self.options:
|
|
self.options['language'] = 'python'
|
|
|
|
return [ActivcodeNode(self.options)]
|
|
|
|
|
|
class ActiveExercise(ActiveCode):
|
|
required_arguments = 1
|
|
optional_arguments = 0
|
|
has_content = True
|
|
|
|
def run(self):
|
|
self.options['hidecode'] = True
|
|
self.options['gradebutton'] = True
|
|
return super(ActiveExercise,self).run()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
a = ActiveCode()
|