aapssfc/book/modules/luther/sphinx/activecode/activecode.py
2015-03-03 15:56:44 +01:00

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