first import book
This commit is contained in:
parent
6be2701fd8
commit
dfd9c869d5
233 changed files with 47797 additions and 0 deletions
1
book/modules/__init__.py
Normal file
1
book/modules/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
1
book/modules/gatech/__init__.py
Normal file
1
book/modules/gatech/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
3
book/modules/gatech/parsons/__init__.py
Normal file
3
book/modules/gatech/parsons/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
from .parsons import *
|
||||
|
||||
237
book/modules/gatech/parsons/parsons.py
Normal file
237
book/modules/gatech/parsons/parsons.py
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
# Copyright (C) 2012 Michael Hewner
|
||||
#
|
||||
# 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__ = 'hewner'
|
||||
|
||||
import re
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst import Directive
|
||||
from luther.sphinx.assess import Assessment
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('parsonsprob',ParsonsProblem)
|
||||
|
||||
app.add_stylesheet('parsons.css')
|
||||
app.add_stylesheet('lib/prettify.css')
|
||||
|
||||
# includes parsons specific javascript headers
|
||||
# parsons-noconflict reverts underscore and
|
||||
# jquery to their original versions
|
||||
app.add_javascript('lib/jquery.min.js')
|
||||
app.add_javascript('lib/jquery-ui.min.js')
|
||||
app.add_javascript('lib/prettify.js')
|
||||
app.add_javascript('lib/underscore-min.js')
|
||||
app.add_javascript('lib/lis.js')
|
||||
app.add_javascript('parsons.js')
|
||||
app.add_javascript('parsons-noconflict.js')
|
||||
|
||||
|
||||
class ParsonsProblem(Assessment):
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
option_spec = {}
|
||||
has_content = True
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
|
||||
Instructions for solving the problem should be written and then a line with -----
|
||||
signals the beginning of the code. If you want more than one line in a single
|
||||
code block, seperate your code blocks with =====.
|
||||
|
||||
Both the instructions sections and code blocks are optional. If you don't include any
|
||||
=====, the code will assume you want each line to be its own code block.
|
||||
|
||||
Example:
|
||||
|
||||
.. parsonsprob:: unqiue_problem_id_here
|
||||
|
||||
Solve my really cool parsons problem...if you can.
|
||||
-----
|
||||
def findmax(alist):
|
||||
=====
|
||||
if len(alist) == 0:
|
||||
return None
|
||||
=====
|
||||
curmax = alist[0]
|
||||
for item in alist:
|
||||
=====
|
||||
if item > curmax:
|
||||
=====
|
||||
curmax = item
|
||||
=====
|
||||
return curmax
|
||||
|
||||
|
||||
"""
|
||||
|
||||
template_values = {}
|
||||
template_values['qnumber'] = self.getNumber()
|
||||
template_values['unique_id'] = self.lineno
|
||||
template_values['instructions'] = ""
|
||||
code = self.content
|
||||
|
||||
if '-----' in self.content:
|
||||
index = self.content.index('-----')
|
||||
template_values['instructions'] = "\n".join(self.content[:index])
|
||||
code = self.content[index + 1:]
|
||||
|
||||
if '=====' in code:
|
||||
template_values['code'] = self.parse_multiline_parsons(code);
|
||||
else:
|
||||
template_values['code'] = "\n".join(code)
|
||||
|
||||
template_values['divid'] = self.arguments[0]
|
||||
|
||||
TEMPLATE = '''
|
||||
<div class='parsons alert alert-warning'>
|
||||
%(qnumber)s: %(instructions)s<br /><br />
|
||||
<div style="clear:left;"></div>
|
||||
<div id="parsons-orig-%(unique_id)s" style="display:none;">%(code)s</div>
|
||||
<div id="parsons-sortableTrash-%(unique_id)s" class="sortable-code"></div>
|
||||
<div id="parsons-sortableCode-%(unique_id)s" class="sortable-code"></div>
|
||||
<div style="clear:left;"></div>
|
||||
<input type="button" class='btn btn-success' id="checkMe%(unique_id)s" value="Check Me"/>
|
||||
<input type="button" class='btn btn-default' id="reset%(unique_id)s" value="Reset"/>
|
||||
<div id="parsons-message-%(unique_id)s"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$pjQ(document).ready(function(){
|
||||
var msgBox = $("#parsons-message-%(unique_id)s");
|
||||
msgBox.hide();
|
||||
var displayErrors = function (fb) {
|
||||
if(fb.errors.length > 0) {
|
||||
var hash = pp_%(unique_id)s.getHash("#ul-parsons-sortableCode-%(unique_id)s");
|
||||
msgBox.fadeIn(500);
|
||||
msgBox.attr('class','alert alert-danger');
|
||||
msgBox.html(fb.errors[0]);
|
||||
logBookEvent({'event':'parsons', 'act':hash, 'div_id':'%(divid)s'});
|
||||
|
||||
} else {
|
||||
logBookEvent({'event':'parsons', 'act':'yes', 'div_id':'%(divid)s'});
|
||||
msgBox.fadeIn(100);
|
||||
msgBox.attr('class','alert alert-success');
|
||||
msgBox.html("Perfect!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$(window).load(function() {
|
||||
// set min width and height
|
||||
var sortableul = $("#ul-parsons-sortableCode-%(unique_id)s");
|
||||
var trashul = $("#ul-parsons-sortableTrash-%(unique_id)s");
|
||||
var sortableHeight = sortableul.height();
|
||||
var sortableWidth = sortableul.width();
|
||||
var trashWidth = trashul.width();
|
||||
var trashHeight = trashul.height();
|
||||
var minHeight = Math.max(trashHeight,sortableHeight);
|
||||
var minWidth = Math.max(trashWidth, sortableWidth);
|
||||
trashul.css("min-height",minHeight + "px");
|
||||
sortableul.css("min-height",minHeight + "px");
|
||||
sortableul.height(minHeight);
|
||||
trashul.css("min-width",minWidth + "px");
|
||||
sortableul.css("min-width",minWidth + "px");
|
||||
});
|
||||
|
||||
|
||||
var pp_%(unique_id)s = new ParsonsWidget({
|
||||
'sortableId': 'parsons-sortableCode-%(unique_id)s',
|
||||
'trashId': 'parsons-sortableTrash-%(unique_id)s',
|
||||
'max_wrong_lines': 1,
|
||||
'solution_label': 'Drop blocks here',
|
||||
'feedback_cb' : displayErrors
|
||||
});
|
||||
pp_%(unique_id)s.init($pjQ("#parsons-orig-%(unique_id)s").text());
|
||||
pp_%(unique_id)s.shuffleLines();
|
||||
|
||||
if(localStorage.getItem('%(divid)s') && localStorage.getItem('%(divid)s-trash')) {
|
||||
try {
|
||||
var solution = localStorage.getItem('%(divid)s');
|
||||
var trash = localStorage.getItem('%(divid)s-trash');
|
||||
pp_%(unique_id)s.createHTMLFromHashes(solution,trash);
|
||||
pp_%(unique_id)s.getFeedback();
|
||||
} catch(err) {
|
||||
var text = "An error occured restoring old %(divid)s state. Error: ";
|
||||
console.log(text + err.message);
|
||||
}
|
||||
|
||||
}
|
||||
$pjQ("#reset%(unique_id)s").click(function(event){
|
||||
event.preventDefault();
|
||||
pp_%(unique_id)s.shuffleLines();
|
||||
|
||||
// set min width and height
|
||||
var sortableul = $("#ul-parsons-sortableCode-%(unique_id)s");
|
||||
var trashul = $("#ul-parsons-sortableTrash-%(unique_id)s");
|
||||
var sortableHeight = sortableul.height();
|
||||
var sortableWidth = sortableul.width();
|
||||
var trashWidth = trashul.width();
|
||||
var trashHeight = trashul.height();
|
||||
var minHeight = Math.max(trashHeight,sortableHeight);
|
||||
var minWidth = Math.max(trashWidth, sortableWidth);
|
||||
trashul.css("min-height",minHeight + "px");
|
||||
sortableul.css("min-height",minHeight + "px");
|
||||
trashul.css("min-width",minWidth + "px");
|
||||
sortableul.css("min-width",minWidth + "px");
|
||||
msgBox.hide();
|
||||
});
|
||||
|
||||
$pjQ("#checkMe%(unique_id)s").click(function(event){
|
||||
event.preventDefault();
|
||||
var hash = pp_%(unique_id)s.getHash("#ul-parsons-sortableCode-%(unique_id)s");
|
||||
localStorage.setItem('%(divid)s',hash);
|
||||
hash = pp_%(unique_id)s.getHash("#ul-parsons-sortableTrash-%(unique_id)s");
|
||||
localStorage.setItem('%(divid)s-trash',hash);
|
||||
|
||||
pp_%(unique_id)s.getFeedback();
|
||||
msgBox.fadeIn(100);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
'''
|
||||
|
||||
|
||||
self.assert_has_content()
|
||||
return [nodes.raw('',TEMPLATE % template_values, format='html')]
|
||||
|
||||
|
||||
def parse_multiline_parsons(self, lines):
|
||||
current_block = []
|
||||
results = []
|
||||
for line in lines:
|
||||
if(line == '====='):
|
||||
results.append(self.convert_leading_whitespace_for_block(current_block))
|
||||
current_block = []
|
||||
else:
|
||||
current_block.append(line)
|
||||
results.append(self.convert_leading_whitespace_for_block(current_block))
|
||||
return "\n".join(results)
|
||||
|
||||
def convert_leading_whitespace_for_block(self, block):
|
||||
whitespaceMatcher = re.compile("^\s*")
|
||||
initialWhitespace = whitespaceMatcher.match(block[0]).end()
|
||||
result = block[0]
|
||||
for line in block[1:]:
|
||||
result += '\\n' # not a line break...the literal characters \n
|
||||
result += line[initialWhitespace:]
|
||||
return result
|
||||
0
book/modules/luther/__init__.py
Normal file
0
book/modules/luther/__init__.py
Normal file
0
book/modules/luther/sphinx/__init__.py
Normal file
0
book/modules/luther/sphinx/__init__.py
Normal file
1
book/modules/luther/sphinx/activecode/__init__.py
Normal file
1
book/modules/luther/sphinx/activecode/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .activecode import *
|
||||
290
book/modules/luther/sphinx/activecode/activecode.py
Normal file
290
book/modules/luther/sphinx/activecode/activecode.py
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
# 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()
|
||||
2
book/modules/luther/sphinx/animation/__init__.py
Normal file
2
book/modules/luther/sphinx/animation/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from .animation import *
|
||||
105
book/modules/luther/sphinx/animation/animation.py
Normal file
105
book/modules/luther/sphinx/animation/animation.py
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# 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
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('animation',Animation)
|
||||
# app.add_stylesheet('video.css')
|
||||
app.add_javascript('animationbase.js')
|
||||
|
||||
|
||||
SRC = '''
|
||||
<div id="%(divid)s">
|
||||
<canvas id="%(divid)s_canvas" width="400" height="400" style="border:4px solid blue"></canvas>
|
||||
<br />
|
||||
<button onclick="%(divid)s_anim = %(divid)s_init('%(divid)s')">Initialize</button>
|
||||
<button onclick="%(divid)s_anim.run('%(divid)s_anim')">Run</button>
|
||||
<button onclick="%(divid)s_anim.stop()">Stop</button> </br>
|
||||
<button onclick="%(divid)s_anim.begin()">Beginning</button>
|
||||
<button onclick="%(divid)s_anim.forward()">Step Forward</button>
|
||||
<button onclick="%(divid)s_anim.backward()">Step Backward</button>
|
||||
<button onclick="%(divid)s_anim.end()">End</button>
|
||||
|
||||
<script type="text/javascript">
|
||||
%(divid)s_init = function(divid)
|
||||
{
|
||||
var a = new Animator(new %(model)s(), new %(viewer)s(), divid)
|
||||
a.init()
|
||||
return a
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
'''
|
||||
|
||||
SCRIPTTAG = '''<script type="text/javascript" src="../_static/%s"></script>\n'''
|
||||
|
||||
class Animation(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
has_content = False
|
||||
option_spec = {'modelfile':directives.unchanged,
|
||||
'viewerfile':directives.unchanged,
|
||||
'model':directives.unchanged,
|
||||
'viewer':directives.unchanged
|
||||
}
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
process the video directive and generate html for output.
|
||||
:param self:
|
||||
:return:
|
||||
"""
|
||||
|
||||
res = ''
|
||||
self.options['divid'] = self.arguments[0]
|
||||
|
||||
if 'modelfile' in self.options:
|
||||
res = res + SCRIPTTAG % self.options['modelfile']
|
||||
if 'viewerfile' in self.options:
|
||||
res = res + SCRIPTTAG % self.options['viewerfile']
|
||||
|
||||
|
||||
res = res + SRC % self.options
|
||||
return [nodes.raw('',res , format='html')]
|
||||
|
||||
|
||||
source = '''
|
||||
.. animation:: testanim
|
||||
:modelfile: sortmodels.js
|
||||
:viewerfile: sortviewers.js
|
||||
:model: SortModel
|
||||
:viewer: BarViewer
|
||||
|
||||
'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
from docutils.core import publish_parts
|
||||
|
||||
directives.register_directive('animation',Animation)
|
||||
|
||||
doc_parts = publish_parts(source,
|
||||
settings_overrides={'output_encoding': 'utf8',
|
||||
'initial_header_level': 2},
|
||||
writer_name="html")
|
||||
|
||||
print doc_parts['html_body']
|
||||
204
book/modules/luther/sphinx/animation/animationbase.js
Normal file
204
book/modules/luther/sphinx/animation/animationbase.js
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
Animator = function(m, v, divid)
|
||||
{
|
||||
this.model = m
|
||||
this.viewer = v
|
||||
this.timer = null
|
||||
|
||||
this.cursor = -1
|
||||
|
||||
this.sc = document.getElementById(divid+"_canvas")
|
||||
this.ctx = this.sc.getContext("2d")
|
||||
this.sc.width = this.sc.width
|
||||
this.speed = 75
|
||||
|
||||
this.script = this.model.init() //does the animation and sends script back
|
||||
this.viewer.init(this.ctx)
|
||||
}
|
||||
|
||||
Animator.prototype.getContext=function()
|
||||
{
|
||||
return this.ctx
|
||||
}
|
||||
|
||||
Animator.prototype.incCursor=function()
|
||||
{
|
||||
if (this.cursor < this.script.length-1)
|
||||
this.cursor = this.cursor + 1
|
||||
}
|
||||
|
||||
Animator.prototype.decCursor=function()
|
||||
{
|
||||
if (this.cursor > 0)
|
||||
this.cursor = this.cursor -1
|
||||
}
|
||||
|
||||
Animator.prototype.getCursor=function()
|
||||
{
|
||||
return this.cursor
|
||||
}
|
||||
|
||||
Animator.prototype.setCursor=function(newc)
|
||||
{
|
||||
this.cursor = newc
|
||||
}
|
||||
|
||||
Animator.prototype.run = function(animobj)
|
||||
{
|
||||
if (this.timer == null)
|
||||
this.timer = setInterval(animobj+".forward()",this.speed)
|
||||
}
|
||||
|
||||
Animator.prototype.stop = function()
|
||||
{
|
||||
clearInterval(this.timer)
|
||||
this.timer=null
|
||||
}
|
||||
|
||||
Animator.prototype.forward = function()
|
||||
{
|
||||
this.incCursor()
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
if (this.getCursor() == this.script.length-1 && this.timer != null)
|
||||
{
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
}
|
||||
|
||||
Animator.prototype.backward = function()
|
||||
{
|
||||
this.decCursor()
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
}
|
||||
|
||||
Animator.prototype.end = function()
|
||||
{
|
||||
this.setCursor(this.script.length-1)
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
|
||||
}
|
||||
|
||||
Animator.prototype.begin = function()
|
||||
{
|
||||
this.setCursor(0)
|
||||
this.sc.width=this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
}
|
||||
|
||||
Animator.prototype.init = function()
|
||||
{
|
||||
this.setCursor(0)
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[0])
|
||||
}
|
||||
|
||||
init1 = function()
|
||||
{
|
||||
a = new Animator(new BubbleSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init2 = function()
|
||||
{
|
||||
a = new Animator(new BubbleSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init3 = function()
|
||||
{
|
||||
a = new Animator(new BubbleSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init4 = function()
|
||||
{
|
||||
a = new Animator(new SelectionSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init5 = function()
|
||||
{
|
||||
a = new Animator(new SelectionSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init6 = function()
|
||||
{
|
||||
a = new Animator(new SelectionSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init7 = function()
|
||||
{
|
||||
a = new Animator(new InsertionSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init8 = function()
|
||||
{
|
||||
a = new Animator(new InsertionSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init9 = function()
|
||||
{
|
||||
a = new Animator(new InsertionSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init10 = function()
|
||||
{
|
||||
a = new Animator(new ShellSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init11 = function()
|
||||
{
|
||||
a = new Animator(new ShellSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init12 = function()
|
||||
{
|
||||
a = new Animator(new ShellSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init13 = function()
|
||||
{
|
||||
a = new Animator(new MergeSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init14 = function()
|
||||
{
|
||||
a = new Animator(new MergeSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init15 = function()
|
||||
{
|
||||
a = new Animator(new MergeSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init16 = function()
|
||||
{
|
||||
a = new Animator(new QuickSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init17 = function()
|
||||
{
|
||||
a = new Animator(new QuickSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init18 = function()
|
||||
{
|
||||
a = new Animator(new QuickSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
38
book/modules/luther/sphinx/animation/animationrefactor.html
Normal file
38
book/modules/luther/sphinx/animation/animationrefactor.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<script type "text/javascript" src="animationrefactor.js"></script>
|
||||
|
||||
<html>
|
||||
<body onload="">
|
||||
|
||||
<div id="ancan">
|
||||
<canvas id="ancan_canvas" width="400" height="400" style="border:4px solid blue"></canvas>
|
||||
<br />
|
||||
<button onclick="ancan_anim = init1('ancan')">Initialize with BarView</button><button onclick="ancan_anim = init2('ancan')">Initialize with ListView</button><br/>
|
||||
<button onclick="ancan_anim.run('ancan_anim')">Run</button>
|
||||
<button onclick="ancan_anim.stop()">Stop</button> </br>
|
||||
<button onclick="ancan_anim.begin()">Beginning</button>
|
||||
<button onclick="ancan_anim.forward()">Step Forward</button>
|
||||
<button onclick="ancan_anim.backward()">Step Backward</button>
|
||||
<button onclick="ancan_anim.end()">End</button>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
init1 = function(divid)
|
||||
{
|
||||
var a = new Animator(new SortModel(), new BarViewer(), divid)
|
||||
a.init()
|
||||
return a
|
||||
}
|
||||
|
||||
init2 = function(divid)
|
||||
{
|
||||
var a = new Animator(new SortModel(), new ListViewer(), divid)
|
||||
a.init()
|
||||
return a
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
256
book/modules/luther/sphinx/animation/animationrefactor.js
Normal file
256
book/modules/luther/sphinx/animation/animationrefactor.js
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
DataItem = function(pos, h, col)
|
||||
{
|
||||
this.height = h
|
||||
this.position = pos
|
||||
this.color = col
|
||||
}
|
||||
|
||||
DataItem.prototype.clone=function()
|
||||
{
|
||||
var newitem = new DataItem(this.position,this.height,this.color) //make a copy
|
||||
return newitem
|
||||
}
|
||||
|
||||
DataItem.prototype.getHeight=function()
|
||||
{
|
||||
return this.height
|
||||
}
|
||||
|
||||
DataItem.prototype.getColor=function()
|
||||
{
|
||||
return this.color
|
||||
}
|
||||
|
||||
DataItem.prototype.getPosition=function()
|
||||
{
|
||||
return this.position
|
||||
}
|
||||
|
||||
DataItem.prototype.setHeight=function(newh)
|
||||
{
|
||||
this.height = newh
|
||||
}
|
||||
|
||||
DataItem.prototype.setPosition=function(newp)
|
||||
{
|
||||
this.position = newp
|
||||
}
|
||||
|
||||
DataItem.prototype.setColor=function(newc)
|
||||
{
|
||||
this.color = newc
|
||||
}
|
||||
|
||||
SortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
SortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 50
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
for (var passnum=this.valuelist.length-1; passnum>0; passnum = passnum-1)
|
||||
{
|
||||
for (var i=0; i<passnum; i=i+1)
|
||||
{
|
||||
this.valuelist[i].setColor("red")
|
||||
this.valuelist[i+1].setColor("red")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
if (this.valuelist[i].getHeight() > this.valuelist[i+1].getHeight())
|
||||
{
|
||||
|
||||
var temp = this.valuelist[i]
|
||||
this.valuelist[i] = this.valuelist[i+1]
|
||||
this.valuelist[i+1] = temp
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
}
|
||||
|
||||
this.valuelist[i].setColor("black")
|
||||
this.valuelist[i+1].setColor("black")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
}
|
||||
|
||||
return this.script
|
||||
}
|
||||
|
||||
SortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BarViewer = function() //construct the view
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BarViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
BarViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillRect(p*7 + 2,
|
||||
this.ctx.canvas.height-ascene[p].height,
|
||||
3,
|
||||
ascene[p].height)
|
||||
}
|
||||
}
|
||||
|
||||
ListViewer = function() //contruct a list of numbers view
|
||||
{
|
||||
}
|
||||
|
||||
ListViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
ListViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillText(ascene[p].height, p*7 + 2,
|
||||
this.ctx.canvas.height-ascene[p].height)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Animator = function(m, v, divid)
|
||||
{
|
||||
this.model = m
|
||||
this.viewer = v
|
||||
this.timer = null
|
||||
|
||||
this.cursor = -1
|
||||
|
||||
this.sc = document.getElementById(divid+"_canvas")
|
||||
this.ctx = this.sc.getContext("2d")
|
||||
this.sc.width = this.sc.width
|
||||
this.speed = 75
|
||||
|
||||
this.script = this.model.init() //does the sort and sends script back
|
||||
this.viewer.init(this.ctx)
|
||||
}
|
||||
|
||||
Animator.prototype.getContext=function()
|
||||
{
|
||||
return this.ctx
|
||||
}
|
||||
|
||||
Animator.prototype.incCursor=function()
|
||||
{
|
||||
if (this.cursor < this.script.length-1)
|
||||
this.cursor = this.cursor + 1
|
||||
}
|
||||
|
||||
Animator.prototype.decCursor=function()
|
||||
{
|
||||
if (this.cursor > 0)
|
||||
this.cursor = this.cursor -1
|
||||
}
|
||||
|
||||
Animator.prototype.getCursor=function()
|
||||
{
|
||||
return this.cursor
|
||||
}
|
||||
|
||||
Animator.prototype.setCursor=function(newc)
|
||||
{
|
||||
this.cursor = newc
|
||||
}
|
||||
|
||||
Animator.prototype.run = function(animobj)
|
||||
{
|
||||
if (this.timer == null)
|
||||
this.timer = setInterval(animobj+".forward()",this.speed)
|
||||
}
|
||||
|
||||
Animator.prototype.stop = function()
|
||||
{
|
||||
clearInterval(this.timer)
|
||||
this.timer=null
|
||||
}
|
||||
|
||||
Animator.prototype.forward = function()
|
||||
{
|
||||
this.incCursor()
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
if (this.getCursor() == this.script.length-1 && this.timer != null)
|
||||
{
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
}
|
||||
|
||||
Animator.prototype.backward = function()
|
||||
{
|
||||
this.decCursor()
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
}
|
||||
|
||||
Animator.prototype.end = function()
|
||||
{
|
||||
this.setCursor(this.script.length-1)
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
|
||||
}
|
||||
|
||||
Animator.prototype.begin = function()
|
||||
{
|
||||
this.setCursor(0)
|
||||
this.sc.width=this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
}
|
||||
|
||||
Animator.prototype.init = function()
|
||||
{
|
||||
this.setCursor(0)
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[0])
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
51
book/modules/luther/sphinx/animation/chart.html
Normal file
51
book/modules/luther/sphinx/animation/chart.html
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>charts</title>
|
||||
<!-- <script type="text/javascript" src="https://www.google.com/jsapi"></script> -->
|
||||
<!-- <script type="text/javascript" src="chart.js"></script> -->
|
||||
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="jqchart/jquery.gchart.js"></script>
|
||||
<script type="text/javascript" src="jqchart/jquery.gchart.graphviz.js"></script>
|
||||
<style>
|
||||
#visualization { width: 800px; height: 400px; }
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="visualization"></div>
|
||||
<script type="text/javascript">
|
||||
// $("#visualization").gchart({type: 'graphviz', series: [$.gchart.series([20, 50, 30])]});
|
||||
// label: '<f0> left | <f1> middle | <f2> right'
|
||||
|
||||
$('#visualization').gchart($.gchart.graphviz(true,
|
||||
{
|
||||
struct1: {label: '<f0> left |<f1> middle |<f2> right'},
|
||||
struct2: {label: '<f0> one|<f1> two'},
|
||||
struct3: {label: 'hello\nworld |{ b |{c|<here> d|e}| f}| g | h'}
|
||||
},
|
||||
{
|
||||
'struct1:f1': ['struct2:f0'],
|
||||
'struct2:f2': ['struct3:here']
|
||||
},
|
||||
{
|
||||
node: {shape: 'record'}
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
// $('#visualization').gchart($.gchart.graphviz(true,
|
||||
// {struct1: {label: "<f0> left |<f1> middle |<f2> right"},
|
||||
// struct2: {label: "<f0> one|<f1> two"},
|
||||
// struct3: {label: "hello\ world |{ b |{c|<here> d|e}| f}| g | h"}
|
||||
// },
|
||||
// {'struct1:f1': ['struct2:f0'],
|
||||
// 'struct2:f2': ['struct3:here']},
|
||||
// {node: {shape: 'record'}}))
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<title>jQuery Google Chart</title>
|
||||
<style type="text/css">
|
||||
#basicGChart { width: 450px; height: 300px }
|
||||
</style>
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="jquery.gchart.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('#basicGChart').gchart({type: 'line', maxValue: 40,
|
||||
title: 'Weather for|Brisbane, Australia', titleColor: 'green',
|
||||
backgroundColor: $.gchart.gradient('horizontal', 'ccffff', 'ccffff00'),
|
||||
series: [$.gchart.series('Max', [29.1, 28.9, 28.1, 26.3,
|
||||
23.5, 21.2, 20.6, 21.7, 23.8, 25.6, 27.3, 28.6], 'red', 'ffcccc'),
|
||||
$.gchart.series('Min', [20.9, 20.8, 19.5, 16.9,
|
||||
13.8, 10.9, 9.5, 10.0, 12.5, 15.6, 18.0, 19.8], 'green'),
|
||||
$.gchart.series('Rainfall', [157.7, 174.6, 138.5, 90.4,
|
||||
98.8, 71.2, 62.6, 42.7, 34.9, 94.4, 96.5, 126.2], 'blue', 0, 200)],
|
||||
axes: [$.gchart.axis('bottom', ['Jan', 'Feb', 'Mar', 'Apr',
|
||||
'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 'black'),
|
||||
$.gchart.axis('left', 0, 40, 'red', 'right'),
|
||||
$.gchart.axis('left', ['C'], [50], 'red', 'right'),
|
||||
$.gchart.axis('right', 0, 200, 50, 'blue', 'left'),
|
||||
$.gchart.axis('right', ['mm'], [50], 'blue', 'left')],
|
||||
legend: 'right'});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>jQuery Google Chart Basics</h1>
|
||||
<p>This page demonstrates the very basics of the
|
||||
<a href="http://keith-wood.name/gChart.html">jQuery Google Chart plugin</a>.
|
||||
It contains the minimum requirements for using the plugin and
|
||||
can be used as the basis for your own experimentation.</p>
|
||||
<p>For more detail see the <a href="http://keith-wood.name/gChartRef.html">documentation reference</a> page.</p>
|
||||
<div id="basicGChart"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
/* http://keith-wood.name/gChart.html
|
||||
Google Chart interface extensions for jQuery v1.4.3.
|
||||
See API details at http://code.google.com/apis/chart/.
|
||||
Written by Keith Wood (kbwood{at}iinet.com.au) September 2008.
|
||||
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
Please attribute the author if you use it. */
|
||||
|
||||
(function($) { // Hide scope, no $ conflict
|
||||
|
||||
$.extend($.gchart._defaults, {
|
||||
// Maps -------------------
|
||||
mapLatLong: false, // True to use lat/long coords in mapArea
|
||||
mapArea: null, // New maps: (number) pixel border all around or
|
||||
// (number[4]) individual pixel borders or lat/long
|
||||
// Original maps: the general area to show:
|
||||
// world, africa, asia, europe, middle_east, south_america, usa
|
||||
mapRegions: [], // List of country/state codes to plot
|
||||
mapDefaultColor: 'bebebe', // The colour for non-plotted countries/states
|
||||
mapColors: ['blue', 'red'], // The colour range for plotted countries/states
|
||||
// QR Code ----------------
|
||||
qrECLevel: null, // Error correction level: low, medium, quarter, high
|
||||
qrMargin: null // Margin (squares) around QR code, default is 4
|
||||
});
|
||||
|
||||
// New chart types: formula, map, mapOriginal, meter, qrCode, scatter, venn
|
||||
$.extend($.gchart._chartTypes, {formula: 'tx', map: 'map', mapOriginal: 't',
|
||||
meter: 'gom', qrCode: 'qr', scatter: 's', venn: 'v',
|
||||
gom: 'gom', qr: 'qr', s: 's', t: 't', tx: 'tx', v: 'v'});
|
||||
|
||||
$.extend($.gchart._typeOptions, {map: 'map', qr: 'qr', t: 'map', tx: 'no'});
|
||||
|
||||
$.extend($.gchart._prototype.prototype, {
|
||||
|
||||
/* Latitude and longitude coordinates for the continents. */
|
||||
mapAfrica: [-35, -20, 40, 55],
|
||||
mapAsia: [-15, 40, 75, 180],
|
||||
mapAustralia: [-45, 110, -10, 155],
|
||||
mapEurope: [33, -25, 73, 50],
|
||||
mapNorthAmerica: [5, -175, 75, -50],
|
||||
mapSouthAmerica: [-55, -85, 15, -35],
|
||||
|
||||
/* Prepare options for a scatter chart.
|
||||
@param values (number[][2/3]) the coordinates of the points: [0] is the x-coord,
|
||||
[1] is the y-coord, [2] (optional) is the percentage size
|
||||
@param minMax (number[2/4]) any minimum and maximum values for the axes (optional)
|
||||
@param labels (string[]) the labels for the groups (optional)
|
||||
@param colours (string[]) the colours for the labels (optional)
|
||||
@param options (object) additional settings (optional)
|
||||
@return (object) the configured options object */
|
||||
scatter: function(values, minMax, labels, colours, options) {
|
||||
if (!$.isArray(minMax)) {
|
||||
options = minMax;
|
||||
colours = null;
|
||||
labels = null;
|
||||
minMax = null;
|
||||
}
|
||||
else if (typeof minMax[0] != 'number') {
|
||||
options = colours;
|
||||
colours = labels;
|
||||
labels = minMax;
|
||||
minMax = null;
|
||||
}
|
||||
if (labels && !$.isArray(labels)) {
|
||||
options = labels;
|
||||
colours = null;
|
||||
labels = null;
|
||||
}
|
||||
var series = [[], [], []];
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
series[0][i] = values[i][0];
|
||||
series[1][i] = values[i][1];
|
||||
series[2][i] = values[i][2] || 100;
|
||||
}
|
||||
minMax = minMax || [];
|
||||
options = options || {};
|
||||
if (labels) {
|
||||
options.extension = {chdl: labels.join('|')};
|
||||
}
|
||||
if (colours) {
|
||||
colours = $.map(colours, function(v, i) {
|
||||
return $.gchart.color(v);
|
||||
});
|
||||
$.extend(options.extension, {chco: colours.join('|')});
|
||||
}
|
||||
return $.extend({}, options,
|
||||
{type: 'scatter', encoding: (minMax.length >= 2 ? 'scaled' : 'text'), series: [
|
||||
(minMax.length >= 2 ? $.gchart.series(series[0], minMax[0], minMax[1]) :
|
||||
$.gchart.series(series[0])),
|
||||
(minMax.length >= 4 ? $.gchart.series(series[1],
|
||||
(minMax[2] != null ? minMax[2] : minMax[0]), (minMax[3] != null ? minMax[3] : minMax[1])) :
|
||||
$.gchart.series(series[1])), $.gchart.series(series[2])]});
|
||||
},
|
||||
|
||||
/* Prepare options for a Venn diagram.
|
||||
@param size1 (number) the relative size of the first circle
|
||||
@param size2 (number) the relative size of the second circle
|
||||
@param size3 (number) the relative size of the third circle
|
||||
@param overlap12 (number) the overlap between circles 1 and 2
|
||||
@param overlap13 (number) the overlap between circles 1 and 3
|
||||
@param overlap23 (number) the overlap between circles 2 and 3
|
||||
@param overlap123 (number) the overlap between all circles
|
||||
@param options (object) additional settings (optional)
|
||||
@return (object) the configured options object */
|
||||
venn: function(size1, size2, size3, overlap12, overlap13, overlap23, overlap123, options) {
|
||||
return $.extend({}, options || {}, {type: 'venn', series:
|
||||
[$.gchart.series([size1, size2, size3, overlap12, overlap13, overlap23, overlap123])]});
|
||||
},
|
||||
|
||||
/* Prepare options for a Google meter.
|
||||
@param text (string or string[]) the text to show on the arrow (optional)
|
||||
@param values (number or number[] or [] of these) the position(s) of the arrow(s)
|
||||
@param maxValue (number) the maximum value for the meter (optional, default 100)
|
||||
@param colours (string[]) the colours to use for the band (optional)
|
||||
@param labels (string[]) labels appearing beneath the meter (optional)
|
||||
@param styles (number[][4]) the styles of each series' arrows:
|
||||
width, dash, space, arrow size (optional)
|
||||
@param options (object) additional settings (optional)
|
||||
@return (object) the configured options object */
|
||||
meter: function(text, values, maxValue, colours, labels, styles, options) {
|
||||
if (typeof text != 'string' && !$.isArray(text)) {
|
||||
options = styles;
|
||||
styles = labels;
|
||||
labels = colours;
|
||||
colours = maxValue;
|
||||
maxValue = values;
|
||||
values = text;
|
||||
text = '';
|
||||
}
|
||||
if (typeof maxValue != 'number') {
|
||||
options = styles;
|
||||
styles = labels;
|
||||
labels = colours;
|
||||
colours = maxValue;
|
||||
maxValue = null;
|
||||
}
|
||||
if (!$.isArray(colours)) {
|
||||
options = styles;
|
||||
styles = labels;
|
||||
labels = colours;
|
||||
colours = null;
|
||||
}
|
||||
if (!$.isArray(labels)) {
|
||||
options = styles;
|
||||
styles = labels;
|
||||
labels = null;
|
||||
}
|
||||
if (!$.isArray(styles)) {
|
||||
options = styles;
|
||||
styles = null;
|
||||
}
|
||||
values = ($.isArray(values) ? values : [values]);
|
||||
var multi = false;
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
multi = multi || $.isArray(values[i]);
|
||||
}
|
||||
var ss = (multi ? [] : [$.gchart.series(values)]);
|
||||
if (multi) {
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
ss.push($.gchart.series($.isArray(values[i]) ? values[i] : [values[i]]));
|
||||
}
|
||||
}
|
||||
values = ss;
|
||||
if (colours) {
|
||||
var cs = '';
|
||||
$.each(colours, function(i, v) {
|
||||
cs += ',' + $.gchart.color(v);
|
||||
});
|
||||
colours = cs.substr(1);
|
||||
}
|
||||
if (styles) {
|
||||
var ls = ['', ''];
|
||||
$.each(styles, function(i, v) {
|
||||
v = ($.isArray(v) ? v : [v]);
|
||||
ls[0] += '|' + $.gchart.color(v.slice(0, 3).join(','));
|
||||
ls[1] += '|' + (v[3] || 15);
|
||||
});
|
||||
styles = ls[0].substr(1) + ls[1];
|
||||
}
|
||||
var axis = (labels && labels.length ? $.gchart.axis('y', labels) : null);
|
||||
return $.extend({}, options || {}, {type: 'meter',
|
||||
maxValue: maxValue || 100, series: values,
|
||||
dataLabels: ($.isArray(text) ? text : [text || ''])},
|
||||
(colours ? {extension: {chco: colours}} : {}),
|
||||
(axis ? {axes: [axis]} : {}),
|
||||
(styles ? {extension: {chls: styles}} : {}));
|
||||
},
|
||||
|
||||
/* Prepare options for a map chart.
|
||||
@param latLongArea (boolean) true to specify the area via latitude/longitude (optional)
|
||||
@param mapArea (string) the region of the world to show (original map style) or
|
||||
(number[4]) the pixel zoom or lat/long coordinates to show or
|
||||
(number) all around pixel zoom (optional)
|
||||
@param values (object) the countries/states to plot -
|
||||
attributes are country/state codes and values
|
||||
@param defaultColour (string) the colour for regions without values (optional)
|
||||
@param colour (string or string[]) the starting colour or
|
||||
gradient colours for rendering values (optional)
|
||||
@param endColour (string) the ending colour for rendering values (optional)
|
||||
@param options (object) additional settings (optional)
|
||||
@return (object) the configured options object */
|
||||
map: function(latLongArea, mapArea, values, defaultColour, colour, endColour, options) {
|
||||
if (typeof latLongArea != 'boolean') {
|
||||
options = endColour;
|
||||
endColour = colour;
|
||||
colour = defaultColour;
|
||||
defaultColour = values;
|
||||
values = mapArea;
|
||||
mapArea = latLongArea;
|
||||
latLongArea = false;
|
||||
}
|
||||
if (typeof mapArea == 'object' && !$.isArray(mapArea)) { // Optional mapArea
|
||||
options = endColour;
|
||||
endColour = colour;
|
||||
colour = defaultColour;
|
||||
defaultColour = values;
|
||||
values = mapArea;
|
||||
mapArea = null;
|
||||
}
|
||||
if (typeof defaultColour == 'object') {
|
||||
options = defaultColour;
|
||||
endColour = null;
|
||||
colour = null;
|
||||
defaultColour = null;
|
||||
}
|
||||
else if (typeof colour == 'object' && !$.isArray(colour)) {
|
||||
options = colour;
|
||||
endColour = null;
|
||||
colour = null;
|
||||
}
|
||||
else if (typeof endColour == 'object') {
|
||||
options = endColour;
|
||||
endColour = null;
|
||||
}
|
||||
var mapRegions = [];
|
||||
var data = [];
|
||||
var i = 0;
|
||||
for (var name in values) {
|
||||
mapRegions[i] = name.replace(/_/g, '-');
|
||||
data[i] = values[name];
|
||||
i++;
|
||||
}
|
||||
if (typeof mapArea == 'number') {
|
||||
mapArea = [mapArea, mapArea, mapArea, mapArea];
|
||||
}
|
||||
return $.extend({}, options || {},
|
||||
{type: (typeof mapArea == 'string' ? 'mapOriginal' : 'map'),
|
||||
mapLatLong: latLongArea, mapArea: mapArea, mapRegions: mapRegions,
|
||||
mapDefaultColor: defaultColour || $.gchart._defaults.mapDefaultColor,
|
||||
mapColors: ($.isArray(colour) ? colour : [colour || $.gchart._defaults.mapColors[0],
|
||||
endColour || $.gchart._defaults.mapColors[1]]),
|
||||
series: [$.gchart.series('', data)]});
|
||||
},
|
||||
|
||||
/* Prepare options for generating a QR Code.
|
||||
@param text (object) the QR code settings or
|
||||
(string) the text to encode
|
||||
@param encoding (string) the encoding scheme (optional)
|
||||
@param ecLevel (string) the error correction level: l, m, q, h (optional)
|
||||
@param margin (number) the margin around the code (optional)
|
||||
@return (object) the configured options object */
|
||||
qrCode: function(text, encoding, ecLevel, margin) {
|
||||
var options = {};
|
||||
if (typeof text == 'object') {
|
||||
options = text;
|
||||
}
|
||||
else { // Individual fields
|
||||
options = {dataLabels: [text], encoding: encoding,
|
||||
qrECLevel: ecLevel, qrMargin: margin};
|
||||
}
|
||||
options.type = 'qrCode';
|
||||
if (options.text) {
|
||||
options.dataLabels = [options.text];
|
||||
options.text = null;
|
||||
}
|
||||
return options;
|
||||
},
|
||||
|
||||
/* Generate standard options for map charts.
|
||||
@param options (object) the chart settings
|
||||
@param labels (string) the concatenated labels for the chart
|
||||
@return (string) the standard map chart options */
|
||||
mapOptions: function(options, labels) {
|
||||
var encoding = this['_' + options.encoding + 'Encoding'] || this['_textEncoding'];
|
||||
var colours = '';
|
||||
for (var i = 0; i < options.mapColors.length; i++) {
|
||||
colours += ',' + $.gchart.color(options.mapColors[i]);
|
||||
}
|
||||
return (typeof options.mapArea == 'string' ? '&chtm=' + options.mapArea :
|
||||
(options.mapArea ? (options.mapLatLong ? ':fixed=' : ':auto=') +
|
||||
($.isArray(options.mapArea) ? options.mapArea.join(',') :
|
||||
options.mapArea + ',' + options.mapArea + ',' + options.mapArea + ',' + options.mapArea) : '')) +
|
||||
'&chd=' + encoding.apply($.gchart, [options]) +
|
||||
(options.mapRegions && options.mapRegions.length ?
|
||||
'&chld=' + options.mapRegions.join(typeof options.mapArea == 'string' ? '' : '|') : '') +
|
||||
'&chco=' + $.gchart.color(options.mapDefaultColor) + colours;
|
||||
},
|
||||
|
||||
/* Generate standard options for QR Code charts.
|
||||
@param options (object) the chart settings
|
||||
@param labels (string) the concatenated labels for the chart
|
||||
@return (string) the standard QR Code chart options */
|
||||
qrOptions: function(options, labels) {
|
||||
return $.gchart._include('&choe=', options.encoding) +
|
||||
(options.qrECLevel || options.qrMargin ?
|
||||
'&chld=' + (options.qrECLevel ? options.qrECLevel.charAt(0) : 'l') +
|
||||
(options.qrMargin != null ? '|' + options.qrMargin : '') : '') +
|
||||
(labels ? '&chl=' + labels.substr(1) : '');
|
||||
},
|
||||
|
||||
/* Generate standard options for charts that aren't really charts.
|
||||
@param options (object) the chart settings
|
||||
@param labels (string) the concatenated labels for the chart
|
||||
@return (string) the standard non-chart options */
|
||||
noOptions: function(options, labels) {
|
||||
return '&chl=' + labels.substr(1);
|
||||
},
|
||||
|
||||
/* Generate the options for chart size, including restriction for maps.
|
||||
@param type (string) the encoded chart type
|
||||
@param options (object) the chart settings
|
||||
@return (string) the chart size options */
|
||||
addSize: function(type, options) {
|
||||
var maxSize = (type == 'map' || type == 't' ? 600 : 1000);
|
||||
options.width = Math.max(10, Math.min(options.width, maxSize));
|
||||
options.height = Math.max(10, Math.min(options.height, maxSize));
|
||||
if (options.width * options.height > 300000) {
|
||||
options.height = Math.floor(300000 / options.width);
|
||||
}
|
||||
return 'chs=' + options.width + 'x' + options.height;
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.ext.min.js
vendored
Normal file
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.ext.min.js
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/* http://keith-wood.name/gChart.html
|
||||
Google Chart interface extensions for jQuery v1.4.3.
|
||||
See API details at http://code.google.com/apis/chart/.
|
||||
Written by Keith Wood (kbwood{at}iinet.com.au) September 2008.
|
||||
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
Please attribute the author if you use it. */
|
||||
(function($){$.extend($.gchart._defaults,{mapLatLong:false,mapArea:null,mapRegions:[],mapDefaultColor:'bebebe',mapColors:['blue','red'],qrECLevel:null,qrMargin:null});$.extend($.gchart._chartTypes,{formula:'tx',map:'map',mapOriginal:'t',meter:'gom',qrCode:'qr',scatter:'s',venn:'v',gom:'gom',qr:'qr',s:'s',t:'t',tx:'tx',v:'v'});$.extend($.gchart._typeOptions,{map:'map',qr:'qr',t:'map',tx:'no'});$.extend($.gchart._prototype.prototype,{mapAfrica:[-35,-20,40,55],mapAsia:[-15,40,75,180],mapAustralia:[-45,110,-10,155],mapEurope:[33,-25,73,50],mapNorthAmerica:[5,-175,75,-50],mapSouthAmerica:[-55,-85,15,-35],scatter:function(a,b,c,d,e){if(!$.isArray(b)){e=b;d=null;c=null;b=null}else if(typeof b[0]!='number'){e=d;d=c;c=b;b=null}if(c&&!$.isArray(c)){e=c;d=null;c=null}var f=[[],[],[]];for(var i=0;i<a.length;i++){f[0][i]=a[i][0];f[1][i]=a[i][1];f[2][i]=a[i][2]||100}b=b||[];e=e||{};if(c){e.extension={chdl:c.join('|')}}if(d){d=$.map(d,function(v,i){return $.gchart.color(v)});$.extend(e.extension,{chco:d.join('|')})}return $.extend({},e,{type:'scatter',encoding:(b.length>=2?'scaled':'text'),series:[(b.length>=2?$.gchart.series(f[0],b[0],b[1]):$.gchart.series(f[0])),(b.length>=4?$.gchart.series(f[1],(b[2]!=null?b[2]:b[0]),(b[3]!=null?b[3]:b[1])):$.gchart.series(f[1])),$.gchart.series(f[2])]})},venn:function(a,b,c,d,e,f,g,h){return $.extend({},h||{},{type:'venn',series:[$.gchart.series([a,b,c,d,e,f,g])]})},meter:function(a,b,c,d,e,f,g){if(typeof a!='string'&&!$.isArray(a)){g=f;f=e;e=d;d=c;c=b;b=a;a=''}if(typeof c!='number'){g=f;f=e;e=d;d=c;c=null}if(!$.isArray(d)){g=f;f=e;e=d;d=null}if(!$.isArray(e)){g=f;f=e;e=null}if(!$.isArray(f)){g=f;f=null}b=($.isArray(b)?b:[b]);var h=false;for(var i=0;i<b.length;i++){h=h||$.isArray(b[i])}var j=(h?[]:[$.gchart.series(b)]);if(h){for(var i=0;i<b.length;i++){j.push($.gchart.series($.isArray(b[i])?b[i]:[b[i]]))}}b=j;if(d){var k='';$.each(d,function(i,v){k+=','+$.gchart.color(v)});d=k.substr(1)}if(f){var l=['',''];$.each(f,function(i,v){v=($.isArray(v)?v:[v]);l[0]+='|'+$.gchart.color(v.slice(0,3).join(','));l[1]+='|'+(v[3]||15)});f=l[0].substr(1)+l[1]}var m=(e&&e.length?$.gchart.axis('y',e):null);return $.extend({},g||{},{type:'meter',maxValue:c||100,series:b,dataLabels:($.isArray(a)?a:[a||''])},(d?{extension:{chco:d}}:{}),(m?{axes:[m]}:{}),(f?{extension:{chls:f}}:{}))},map:function(a,b,c,d,e,f,g){if(typeof a!='boolean'){g=f;f=e;e=d;d=c;c=b;b=a;a=false}if(typeof b=='object'&&!$.isArray(b)){g=f;f=e;e=d;d=c;c=b;b=null}if(typeof d=='object'){g=d;f=null;e=null;d=null}else if(typeof e=='object'&&!$.isArray(e)){g=e;f=null;e=null}else if(typeof f=='object'){g=f;f=null}var h=[];var j=[];var i=0;for(var k in c){h[i]=k.replace(/_/g,'-');j[i]=c[k];i++}if(typeof b=='number'){b=[b,b,b,b]}return $.extend({},g||{},{type:(typeof b=='string'?'mapOriginal':'map'),mapLatLong:a,mapArea:b,mapRegions:h,mapDefaultColor:d||$.gchart._defaults.mapDefaultColor,mapColors:($.isArray(e)?e:[e||$.gchart._defaults.mapColors[0],f||$.gchart._defaults.mapColors[1]]),series:[$.gchart.series('',j)]})},qrCode:function(a,b,c,d){var e={};if(typeof a=='object'){e=a}else{e={dataLabels:[a],encoding:b,qrECLevel:c,qrMargin:d}}e.type='qrCode';if(e.text){e.dataLabels=[e.text];e.text=null}return e},mapOptions:function(a,b){var c=this['_'+a.encoding+'Encoding']||this['_textEncoding'];var d='';for(var i=0;i<a.mapColors.length;i++){d+=','+$.gchart.color(a.mapColors[i])}return(typeof a.mapArea=='string'?'&chtm='+a.mapArea:(a.mapArea?(a.mapLatLong?':fixed=':':auto=')+($.isArray(a.mapArea)?a.mapArea.join(','):a.mapArea+','+a.mapArea+','+a.mapArea+','+a.mapArea):''))+'&chd='+c.apply($.gchart,[a])+(a.mapRegions&&a.mapRegions.length?'&chld='+a.mapRegions.join(typeof a.mapArea=='string'?'':'|'):'')+'&chco='+$.gchart.color(a.mapDefaultColor)+d},qrOptions:function(a,b){return $.gchart._include('&choe=',a.encoding)+(a.qrECLevel||a.qrMargin?'&chld='+(a.qrECLevel?a.qrECLevel.charAt(0):'l')+(a.qrMargin!=null?'|'+a.qrMargin:''):'')+(b?'&chl='+b.substr(1):'')},noOptions:function(a,b){return'&chl='+b.substr(1)},addSize:function(a,b){var c=(a=='map'||a=='t'?600:1000);b.width=Math.max(10,Math.min(b.width,c));b.height=Math.max(10,Math.min(b.height,c));if(b.width*b.height>300000){b.height=Math.floor(300000/b.width)}return'chs='+b.width+'x'+b.height}})})(jQuery);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* http://keith-wood.name/gChart.html
|
||||
Google Chart interface extensions for jQuery v1.4.3.
|
||||
See API details at http://code.google.com/apis/chart/.
|
||||
Written by Keith Wood (kbwood{at}iinet.com.au) September 2008.
|
||||
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
Please attribute the author if you use it. */
|
||||
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(o($){$.w($.7.O,{X:Y,r:6,C:[],P:\'1o\',B:[\'1p\',\'1q\'],D:6,E:6});$.w($.7.1r,{1s:\'Q\',z:\'z\',19:\'t\',Z:\'11\',12:\'F\',13:\'s\',14:\'v\',11:\'11\',F:\'F\',s:\'s\',t:\'t\',Q:\'Q\',v:\'v\'});$.w($.7.1t,{z:\'z\',F:\'F\',t:\'z\',Q:\'1u\'});$.w($.7.1v.1w,{1x:[-1a,-20,1b,1c],1y:[-15,1b,1d,1z],1A:[-1B,1C,-10,1D],1E:[1F,-25,1G,1e],1H:[5,-1I,1d,-1e],1J:[-1c,-1K,15,-1a],13:o(a,b,c,d,e){8(!$.n(b)){e=b;d=6;c=6;b=6}R 8(p b[0]!=\'16\'){e=d;d=c;c=b;b=6}8(c&&!$.n(c)){e=c;d=6;c=6}9 f=[[],[],[]];G(9 i=0;i<a.A;i++){f[0][i]=a[i][0];f[1][i]=a[i][1];f[2][i]=a[i][2]||1f}b=b||[];e=e||{};8(c){e.S={1L:c.H(\'|\')}}8(d){d=$.z(d,o(v,i){u $.7.I(v)});$.w(e.S,{17:d.H(\'|\')})}u $.w({},e,{J:\'13\',T:(b.A>=2?\'1M\':\'U\'),q:[(b.A>=2?$.7.q(f[0],b[0],b[1]):$.7.q(f[0])),(b.A>=4?$.7.q(f[1],(b[2]!=6?b[2]:b[0]),(b[3]!=6?b[3]:b[1])):$.7.q(f[1])),$.7.q(f[2])]})},14:o(a,b,c,d,e,f,g,h){u $.w({},h||{},{J:\'14\',q:[$.7.q([a,b,c,d,e,f,g])]})},Z:o(a,b,c,d,e,f,g){8(p a!=\'V\'&&!$.n(a)){g=f;f=e;e=d;d=c;c=b;b=a;a=\'\'}8(p c!=\'16\'){g=f;f=e;e=d;d=c;c=6}8(!$.n(d)){g=f;f=e;e=d;d=6}8(!$.n(e)){g=f;f=e;e=6}8(!$.n(f)){g=f;f=6}b=($.n(b)?b:[b]);9 h=Y;G(9 i=0;i<b.A;i++){h=h||$.n(b[i])}9 j=(h?[]:[$.7.q(b)]);8(h){G(9 i=0;i<b.A;i++){j.1N($.7.q($.n(b[i])?b[i]:[b[i]]))}}b=j;8(d){9 k=\'\';$.1g(d,o(i,v){k+=\',\'+$.7.I(v)});d=k.W(1)}8(f){9 l=[\'\',\'\'];$.1g(f,o(i,v){v=($.n(v)?v:[v]);l[0]+=\'|\'+$.7.I(v.1O(0,3).H(\',\'));l[1]+=\'|\'+(v[3]||15)});f=l[0].W(1)+l[1]}9 m=(e&&e.A?$.7.1P(\'y\',e):6);u $.w({},g||{},{J:\'Z\',1Q:c||1f,q:b,18:($.n(a)?a:[a||\'\'])},(d?{S:{17:d}}:{}),(m?{1R:[m]}:{}),(f?{S:{1S:f}}:{}))},z:o(a,b,c,d,e,f,g){8(p a!=\'1T\'){g=f;f=e;e=d;d=c;c=b;b=a;a=Y}8(p b==\'K\'&&!$.n(b)){g=f;f=e;e=d;d=c;c=b;b=6}8(p d==\'K\'){g=d;f=6;e=6;d=6}R 8(p e==\'K\'&&!$.n(e)){g=e;f=6;e=6}R 8(p f==\'K\'){g=f;f=6}9 h=[];9 j=[];9 i=0;G(9 k 1U c){h[i]=k.1V(/1h/g,\'-\');j[i]=c[k];i++}8(p b==\'16\'){b=[b,b,b,b]}u $.w({},g||{},{J:(p b==\'V\'?\'19\':\'z\'),X:a,r:b,C:h,P:d||$.7.O.P,B:($.n(e)?e:[e||$.7.O.B[0],f||$.7.O.B[1]]),q:[$.7.q(\'\',j)]})},12:o(a,b,c,d){9 e={};8(p a==\'K\'){e=a}R{e={18:[a],T:b,D:c,E:d}}e.J=\'12\';8(e.U){e.18=[e.U];e.U=6}u e},1W:o(a,b){9 c=1i[\'1h\'+a.T+\'1X\']||1i[\'1Y\'];9 d=\'\';G(9 i=0;i<a.B.A;i++){d+=\',\'+$.7.I(a.B[i])}u(p a.r==\'V\'?\'&1Z=\'+a.r:(a.r?(a.X?\':21=\':\':22=\')+($.n(a.r)?a.r.H(\',\'):a.r+\',\'+a.r+\',\'+a.r+\',\'+a.r):\'\'))+\'&23=\'+c.24($.7,[a])+(a.C&&a.C.A?\'&1j=\'+a.C.H(p a.r==\'V\'?\'\':\'|\'):\'\')+\'&17=\'+$.7.I(a.P)+d},26:o(a,b){u $.7.27(\'&28=\',a.T)+(a.D||a.E?\'&1j=\'+(a.D?a.D.29(0):\'l\')+(a.E!=6?\'|\'+a.E:\'\'):\'\')+(b?\'&1k=\'+b.W(1):\'\')},2a:o(a,b){u\'&1k=\'+b.W(1)},2b:o(a,b){9 c=(a==\'z\'||a==\'t\'?2c:2d);b.L=M.1l(10,M.1m(b.L,c));b.N=M.1l(10,M.1m(b.N,c));8(b.L*b.N>1n){b.N=M.2e(1n/b.L)}u\'2f=\'+b.L+\'x\'+b.N}})})(2g);',62,141,'||||||null|gchart|if|var||||||||||||||isArray|function|typeof|series|mapArea|||return||extend|||map|length|mapColors|mapRegions|qrECLevel|qrMargin|qr|for|join|color|type|object|width|Math|height|_defaults|mapDefaultColor|tx|else|extension|encoding|text|string|substr|mapLatLong|false|meter||gom|qrCode|scatter|venn||number|chco|dataLabels|mapOriginal|35|40|55|75|50|100|each|_|this|chld|chl|max|min|300000|bebebe|blue|red|_chartTypes|formula|_typeOptions|no|_prototype|prototype|mapAfrica|mapAsia|180|mapAustralia|45|110|155|mapEurope|33|73|mapNorthAmerica|175|mapSouthAmerica|85|chdl|scaled|push|slice|axis|maxValue|axes|chls|boolean|in|replace|mapOptions|Encoding|_textEncoding|chtm||fixed|auto|chd|apply||qrOptions|_include|choe|charAt|noOptions|addSize|600|1000|floor|chs|jQuery'.split('|'),0,{}))
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/* http://keith-wood.name/gChart.html
|
||||
Google Chart GraphViz extension for jQuery v1.4.3.
|
||||
See API details at http://code.google.com/apis/chart/.
|
||||
Written by Keith Wood (kbwood{at}iinet.com.au) September 2008.
|
||||
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
Please attribute the author if you use it. */
|
||||
|
||||
(function($) { // Hide scope, no $ conflict
|
||||
|
||||
// New chart types: graphviz
|
||||
$.extend($.gchart._chartTypes, {graphviz: 'gv', gv: 'gv'});
|
||||
|
||||
$.extend($.gchart._typeOptions, {gv: 'no'});
|
||||
|
||||
$.extend($.gchart._prototype.prototype, {
|
||||
|
||||
/* Prepare options for a GraphViz chart.
|
||||
@param engine (string, optional) the graphing engine to use:
|
||||
dot (default), neato, twopi, circo, fdp
|
||||
@param options (object, optional) other options for the chart
|
||||
@param directed (boolean, optional) true for directed graph, false for normal
|
||||
@param nodes (string) the DOT representation of the nodes to graph or
|
||||
(object) the graph nodes and their settings
|
||||
@param edges (object, optional) the graph edges keyed from, with array of to
|
||||
@param attrs (object, optional) other settings for the graph
|
||||
@return (object) the configured options object */
|
||||
graphviz: function(engine, options, directed, nodes, edges, attrs) {
|
||||
if (arguments.length == 1) {
|
||||
nodes = engine;
|
||||
engine = 'dot';
|
||||
}
|
||||
var hadEngine = typeof engine == 'string';
|
||||
if (!hadEngine) {
|
||||
attrs = edges;
|
||||
edges = nodes;
|
||||
nodes = directed;
|
||||
directed = options;
|
||||
options = engine;
|
||||
engine = 'dot';
|
||||
}
|
||||
if ((options && typeof options != 'object') || arguments.length == 2 ||
|
||||
(arguments.length == 3 && hadEngine)) {
|
||||
attrs = edges;
|
||||
edges = nodes;
|
||||
nodes = directed;
|
||||
directed = options;
|
||||
options = {};
|
||||
}
|
||||
if (typeof directed != 'boolean' && arguments.length > 1) {
|
||||
attrs = edges;
|
||||
edges = nodes;
|
||||
nodes = directed;
|
||||
directed = false;
|
||||
}
|
||||
options = options || {};
|
||||
options.type = 'gv' + (engine != 'dot' ? ':' + engine : '');
|
||||
options.dataLabels = [typeof nodes == 'string' ? nodes :
|
||||
this._genGraph(directed, nodes, edges, attrs)];
|
||||
return options;
|
||||
},
|
||||
|
||||
/* Generate a graph definition.
|
||||
@param directed (boolean, optional) true for directed graph, false for normal
|
||||
@param nodes (object) the graph nodes and their settings
|
||||
@param edges (object) the graph edges keyed from, with array of to
|
||||
@param attrs (object, optional) other settings for the graph
|
||||
@return (string) the graph definition */
|
||||
_genGraph: function(directed, nodes, edges, attrs) {
|
||||
attrs = attrs || {};
|
||||
var gdef = (directed ? 'digraph' : 'graph') + '{';
|
||||
var sep = '';
|
||||
for (var n in attrs) {
|
||||
gdef += sep + n;
|
||||
var sep2 = '[';
|
||||
for (var n2 in attrs[n]) {
|
||||
gdef += sep2 + n2 + '=' + attrs[n][n2];
|
||||
sep2 = ','
|
||||
}
|
||||
gdef += (sep2 != '[' ? ']' : '');
|
||||
sep = ';';
|
||||
}
|
||||
for (var node in nodes || {}) {
|
||||
gdef += sep + node;
|
||||
var sep2 = '[';
|
||||
for (var n in nodes[node]) {
|
||||
gdef += sep2 + n + '=' + nodes[node][n];
|
||||
sep2 = ','
|
||||
}
|
||||
gdef += (sep2 != '[' ? ']' : '');
|
||||
sep = ';';
|
||||
}
|
||||
for (var edge in edges || {}) {
|
||||
for (var n in edges[edge]) {
|
||||
gdef += sep + edge + (directed ? '->' : '--') + edges[edge][n];
|
||||
}
|
||||
sep = ';';
|
||||
}
|
||||
gdef += '}';
|
||||
return gdef;
|
||||
},
|
||||
|
||||
/* Generate standard options for charts that aren't really charts.
|
||||
@param options (object) the chart settings
|
||||
@param labels (string) the concatenated labels for the chart
|
||||
@return (string) the standard non-chart options */
|
||||
noOptions: function(options, labels) {
|
||||
return '&chl=' + labels.substr(1);
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.graphviz.min.js
vendored
Normal file
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.graphviz.min.js
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/* http://keith-wood.name/gChart.html
|
||||
Google Chart GraphViz extension for jQuery v1.4.3.
|
||||
See API details at http://code.google.com/apis/chart/.
|
||||
Written by Keith Wood (kbwood{at}iinet.com.au) September 2008.
|
||||
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
Please attribute the author if you use it. */
|
||||
(function($){$.extend($.gchart._chartTypes,{graphviz:'gv',gv:'gv'});$.extend($.gchart._typeOptions,{gv:'no'});$.extend($.gchart._prototype.prototype,{graphviz:function(a,b,c,d,e,f){if(arguments.length==1){d=a;a='dot'}var g=typeof a=='string';if(!g){f=e;e=d;d=c;c=b;b=a;a='dot'}if((b&&typeof b!='object')||arguments.length==2||(arguments.length==3&&g)){f=e;e=d;d=c;c=b;b={}}if(typeof c!='boolean'&&arguments.length>1){f=e;e=d;d=c;c=false}b=b||{};b.type='gv'+(a!='dot'?':'+a:'');b.dataLabels=[typeof d=='string'?d:this._genGraph(c,d,e,f)];return b},_genGraph:function(a,b,c,d){d=d||{};var e=(a?'digraph':'graph')+'{';var f='';for(var n in d){e+=f+n;var g='[';for(var h in d[n]){e+=g+h+'='+d[n][h];g=','}e+=(g!='['?']':'');f=';'}for(var i in b||{}){e+=f+i;var g='[';for(var n in b[i]){e+=g+n+'='+b[i][n];g=','}e+=(g!='['?']':'');f=';'}for(var j in c||{}){for(var n in c[j]){e+=f+j+(a?'->':'--')+c[j][n]}f=';'}e+='}';return e},noOptions:function(a,b){return'&chl='+b.substr(1)}})})(jQuery);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* http://keith-wood.name/gChart.html
|
||||
Google Chart GraphViz extension for jQuery v1.4.3.
|
||||
See API details at http://code.google.com/apis/chart/.
|
||||
Written by Keith Wood (kbwood{at}iinet.com.au) September 2008.
|
||||
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
Please attribute the author if you use it. */
|
||||
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(7($){$.m($.o.u,{r:\'6\',6:\'6\'});$.m($.o.v,{6:\'w\'});$.m($.o.x.y,{r:7(a,b,c,d,e,f){8(9.k==1){d=a;a=\'p\'}0 g=l a==\'s\';8(!g){f=e;e=d;d=c;c=b;b=a;a=\'p\'}8((b&&l b!=\'z\')||9.k==2||(9.k==3&&g)){f=e;e=d;d=c;c=b;b={}}8(l c!=\'A\'&&9.k>1){f=e;e=d;d=c;c=B}b=b||{};b.C=\'6\'+(a!=\'p\'?\':\'+a:\'\');b.D=[l d==\'s\'?d:E.t(c,d,e,f)];q b},t:7(a,b,c,d){d=d||{};0 e=(a?\'F\':\'G\')+\'{\';0 f=\'\';4(0 n 5 d){e+=f+n;0 g=\'[\';4(0 h 5 d[n]){e+=g+h+\'=\'+d[n][h];g=\',\'}e+=(g!=\'[\'?\']\':\'\');f=\';\'}4(0 i 5 b||{}){e+=f+i;0 g=\'[\';4(0 n 5 b[i]){e+=g+n+\'=\'+b[i][n];g=\',\'}e+=(g!=\'[\'?\']\':\'\');f=\';\'}4(0 j 5 c||{}){4(0 n 5 c[j]){e+=f+j+(a?\'->\':\'--\')+c[j][n]}f=\';\'}e+=\'}\';q e},H:7(a,b){q\'&I=\'+b.J(1)}})})(K);',47,47,'var||||for|in|gv|function|if|arguments|||||||||||length|typeof|extend||gchart|dot|return|graphviz|string|_genGraph|_chartTypes|_typeOptions|no|_prototype|prototype|object|boolean|false|type|dataLabels|this|digraph|graph|noOptions|chl|substr|jQuery'.split('|'),0,{}))
|
||||
1279
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.icons.js
Normal file
1279
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.icons.js
Normal file
File diff suppressed because it is too large
Load diff
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.icons.min.js
vendored
Normal file
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.icons.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1661
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.js
Normal file
1661
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.js
Normal file
File diff suppressed because it is too large
Load diff
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.min.js
vendored
Normal file
8
book/modules/luther/sphinx/animation/jqchart/jquery.gchart.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
138
book/modules/luther/sphinx/animation/searchmodels.js
Normal file
138
book/modules/luther/sphinx/animation/searchmodels.js
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
DataItem = function(pos, h, col)
|
||||
{
|
||||
this.value = h
|
||||
this.position = pos
|
||||
this.color = col
|
||||
}
|
||||
|
||||
DataItem.prototype.clone=function()
|
||||
{
|
||||
var newitem = new DataItem(this.position,this.value,this.color) //make a copy
|
||||
return newitem
|
||||
}
|
||||
|
||||
DataItem.prototype.getValue=function()
|
||||
{
|
||||
return this.value
|
||||
}
|
||||
|
||||
DataItem.prototype.getColor=function()
|
||||
{
|
||||
return this.color
|
||||
}
|
||||
|
||||
DataItem.prototype.getPosition=function()
|
||||
{
|
||||
return this.position
|
||||
}
|
||||
|
||||
DataItem.prototype.setValue=function(newh)
|
||||
{
|
||||
this.value = newh
|
||||
}
|
||||
|
||||
DataItem.prototype.setPosition=function(newp)
|
||||
{
|
||||
this.position = newp
|
||||
}
|
||||
|
||||
DataItem.prototype.setColor=function(newc)
|
||||
{
|
||||
this.color = newc
|
||||
}
|
||||
|
||||
BinarySearchModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
BinarySearchModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 25
|
||||
var initvalues=[25,30,46,55,60,78,90,95,101,110,122,134,145,150,166,175,187,200,205,213,240,255,267,299]
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var item = new DataItem(i,initvalues[i],"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
//this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
this.binarySearch(this.valuelist,200)
|
||||
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
return this.script
|
||||
}
|
||||
|
||||
BinarySearchModel.prototype.binarySearch=function(alist, item)
|
||||
{
|
||||
var first = 0
|
||||
var last = alist.length-1
|
||||
var found = false
|
||||
var oldmidpoint = null
|
||||
|
||||
while (first<=last && !found)
|
||||
{ this.chunkcolor(first,last,"black")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
if (oldmidpoint != null)
|
||||
{alist[oldmidpoint].setColor("black")
|
||||
this.script.push(this.makescene(this.valuelist))}
|
||||
var midpoint = Math.floor((first + last)/2)
|
||||
alist[midpoint].setColor("blue")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
//alist[midpoint].setColor("black")
|
||||
//this.script.push(this.makescene(this.valuelist))
|
||||
if (alist[midpoint].getValue() == item)
|
||||
{
|
||||
found = true
|
||||
alist[midpoint].setColor("green")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
}
|
||||
else
|
||||
if (item < alist[midpoint].getValue())
|
||||
{
|
||||
last = midpoint-1
|
||||
this.chunkcolor(first,midpoint-1,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
oldmidpoint = midpoint
|
||||
//alist[midpoint].setColor("black")
|
||||
//this.script.push(this.makescene(this.valuelist))
|
||||
}
|
||||
else
|
||||
{
|
||||
first = midpoint+1
|
||||
this.chunkcolor(midpoint+1,last,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
oldmidpoint = midpoint
|
||||
//alist[midpoint].setColor("black")
|
||||
//this.script.push(this.makescene(this.valuelist))
|
||||
}
|
||||
|
||||
}
|
||||
return found
|
||||
|
||||
}
|
||||
|
||||
|
||||
BinarySearchModel.prototype.chunkcolor=function(start,end,c)
|
||||
{
|
||||
for (var clearidx=start; clearidx<=end; clearidx++)
|
||||
this.valuelist[clearidx].setColor(c)
|
||||
}
|
||||
|
||||
|
||||
BinarySearchModel.prototype.makescene = function(somearray)
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<somearray.length; idx++)
|
||||
{
|
||||
var item = somearray[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
41
book/modules/luther/sphinx/animation/simpletree.html
Normal file
41
book/modules/luther/sphinx/animation/simpletree.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="animationrefactor.js"></script>
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="jqchart/jquery.gchart.js"></script>
|
||||
<script type="text/javascript" src="jqchart/jquery.gchart.graphviz.js"></script>
|
||||
<script type="text/javascript" src="animationrefactor.js"></script>
|
||||
<script type="text/javascript" src="simpletree.js"></script>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<style>
|
||||
#ancan_div { width: 800px; height: 400px; }
|
||||
</style>
|
||||
|
||||
<div id="ancan">
|
||||
<canvas id="ancan_canvas" width="400" height="400" style="border:4px solid blue"></canvas>
|
||||
<div id="ancan_div" width="400" height="400"> </div>
|
||||
<br />
|
||||
<button onclick="ancan_anim = init1('ancan')">Initialize</button>
|
||||
<button onclick="ancan_anim.run('ancan_anim')">Run</button>
|
||||
<button onclick="ancan_anim.stop()">Stop</button> </br>
|
||||
<button onclick="ancan_anim.begin()">Beginning</button>
|
||||
<button onclick="ancan_anim.forward()">Step Forward</button>
|
||||
<button onclick="ancan_anim.backward()">Step Backward</button>
|
||||
<button onclick="ancan_anim.end()">End</button>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
init1 = function(divid)
|
||||
{
|
||||
var a = new Animator(new SimpleTreeModel(), new TreeViewer(), divid)
|
||||
a.init()
|
||||
return a
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
63
book/modules/luther/sphinx/animation/simpletree.js
Normal file
63
book/modules/luther/sphinx/animation/simpletree.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
SimpleTreeModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
SimpleTreeModel.prototype.init = function(ctl)
|
||||
{
|
||||
|
||||
var model = [
|
||||
{ nodelist: {C_0: {color: 'blue', style: 'filled'},
|
||||
H_0: {type: 's', shape: 'record', color: 'blue', label: 'foo'},
|
||||
H_1: {type: 's'}, H_2: {type: 's'},
|
||||
C_1: {type: 's'}, H_3: {type: 's'},
|
||||
H_4: {type: 's'}, H_5: {type: 's'}},
|
||||
edgelist: {C_0: ['H_0:f1', 'H_1', 'H_2', 'C_1'], C_1: ['H_3', 'H_4', 'H_5']},
|
||||
params: {node: {shape: 'circle', color: 'red'}, edge: {color: 'blue'}}},
|
||||
|
||||
{ nodelist: {C_0: {},
|
||||
H_0: {type: 's', shape: 'record', color: 'blue', label: 'foo', style: 'filled'},
|
||||
H_1: {type: 's'}, H_2: {type: 's'},
|
||||
C_1: {type: 's'}, H_3: {type: 's'},
|
||||
H_4: {type: 's'}, H_5: {type: 's'}},
|
||||
edgelist: {C_0: ['H_0:f1', 'H_1', 'H_2', 'C_1'], C_1: ['H_3', 'H_4', 'H_5']},
|
||||
params: {node: {shape: 'circle', color: 'red'}, edge: {color: 'blue'}}},
|
||||
|
||||
{ nodelist: {C_0: {},
|
||||
H_0: {type: 's', shape: 'record', label: 'foo'},
|
||||
H_1: {type: 's', style: 'filled', color: 'blue'}, H_2: {type: 's'},
|
||||
C_1: {type: 's'}, H_3: {type: 's'},
|
||||
H_4: {type: 's'}, H_5: {type: 's'}},
|
||||
edgelist: {C_0: ['H_0:f1', 'H_1', 'H_2', 'C_1'], C_1: ['H_3', 'H_4', 'H_5']},
|
||||
params: {node: {shape: 'circle', color: 'red'}, edge: {color: 'blue'}}},
|
||||
|
||||
{ nodelist: {C_0: {},
|
||||
H_0: {type: 's', shape: 'record', label: 'foo'},
|
||||
H_1: {type: 's', style: 'filled', color: 'blue'}, H_2: {type: 's'},
|
||||
C_1: {type: 's'}, H_3: {type: 's'},
|
||||
H_4: {type: 's'}, H_5: {type: 's'}, B_1: {type: 's', color: 'blue', style: 'filled'}},
|
||||
edgelist: {C_0: ['H_0:f1', 'H_1', 'H_2', 'C_1'], C_1: ['H_3', 'H_4', 'H_5'], H_1: ['B_1']},
|
||||
params: {node: {shape: 'circle', color: 'red'}, edge: {color: 'blue'}}},
|
||||
];
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
|
||||
TreeViewer = function() //construct the view
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TreeViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
TreeViewer.prototype.render = function(ascene)
|
||||
{
|
||||
$('#ancan_div').attr('class','none')
|
||||
$('#ancan_div').gchart($.gchart.graphviz(true, ascene.nodelist,
|
||||
ascene.edgelist, ascene.params ))
|
||||
|
||||
}
|
||||
|
||||
35
book/modules/luther/sphinx/animation/sortingbase.html
Normal file
35
book/modules/luther/sphinx/animation/sortingbase.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="sortmodels.js"></script>
|
||||
<script type="text/javascript" src="sortviewers.js"></script>
|
||||
<script type="text/javascript" src="animationbase.js"></script>
|
||||
|
||||
|
||||
<html>
|
||||
<body onload="">
|
||||
|
||||
<canvas id="ancan" width="400" height="400" style="border:4px solid blue"></canvas>
|
||||
<br />
|
||||
<button onclick="init1()">BubbleSort with BarView</button><button onclick="init2()">BubbleSort with ScatterView</button>
|
||||
<button onclick="init3()">BubbleSort with BoxView</button><br/>
|
||||
<button onclick="init4()">SelectionSort with BarView</button><button onclick="init5()">SelectionSort with ScatterView</button>
|
||||
<button onclick="init6()">SelectionSort with BoxView</button><br/>
|
||||
<button onclick="init7()">InsertionSort with BarView</button><button onclick="init8()">InsertionSort with ScatterView</button>
|
||||
<button onclick="init9()">InsertionSort with BoxView</button><br/>
|
||||
<button onclick="init10()">ShellSort with BarView</button><button onclick="init11()">ShellSort with ScatterView</button>
|
||||
<button onclick="init12()">ShellSort with BoxView</button><br/>
|
||||
<button onclick="init13()">MergeSort with BarView</button><button onclick="init14()">MergeSort with ScatterView</button>
|
||||
<button onclick="init15()">MergeSort with BoxView</button><br/>
|
||||
<button onclick="init16()">QuickSort with BarView</button><button onclick="init17()">QuickSort with ScatterView</button>
|
||||
<button onclick="init18()">QuickSort with BoxView</button><br/>
|
||||
<button onclick="a.run()">Run</button>
|
||||
<button onclick="a.stop()">Stop</button> </br>
|
||||
<button onclick="a.begin()">Beginning</button>
|
||||
<button onclick="a.forward()">Step Forward</button>
|
||||
<button onclick="a.backward()">Step Backward</button>
|
||||
<button onclick="a.end()">End</button>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
22
book/modules/luther/sphinx/animation/sortingdemo.html
Normal file
22
book/modules/luther/sphinx/animation/sortingdemo.html
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<script type "text/javascript" src="sortingdemo.js"></script>
|
||||
|
||||
<html>
|
||||
<body onload="">
|
||||
|
||||
<canvas id="sortingcanvas" width="400" height="400" style="border:4px solid blue">
|
||||
</canvas>
|
||||
<br />
|
||||
<button onclick="init()">Initialize</button><br/>
|
||||
<button onclick="run()">Run</button>
|
||||
<button onclick="stop()">Stop</button> </br>
|
||||
<button onclick="begin()">Beginning</button>
|
||||
<button onclick="forward()">Step Forward</button>
|
||||
<button onclick="backward()">Step Backward</button>
|
||||
<button onclick="end()">End</button>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
268
book/modules/luther/sphinx/animation/sortingdemo.js
Normal file
268
book/modules/luther/sphinx/animation/sortingdemo.js
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
BarList = function(hm)
|
||||
{
|
||||
this.howmany = hm
|
||||
this.bars = new Array()
|
||||
|
||||
for (var i=0; i<this.howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
abar = new Bar(i,y,"black")
|
||||
this.bars.push(abar)
|
||||
}
|
||||
}
|
||||
|
||||
BarList.prototype.size = function()
|
||||
{
|
||||
return this.howmany
|
||||
}
|
||||
|
||||
BarList.prototype.show = function(c)
|
||||
{
|
||||
for (var idx=0; idx<this.howmany; idx++)
|
||||
{
|
||||
this.bars[idx].show(this.c)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bar = function(pos, h, col)
|
||||
{
|
||||
this.height = h
|
||||
this.position = pos
|
||||
this.color = col
|
||||
|
||||
}
|
||||
|
||||
Bar.prototype.clone=function()
|
||||
{
|
||||
var newbar = new Bar() //make a copy
|
||||
newbar.setHeight(this.getHeight())
|
||||
newbar.setPosition(this.getPosition())
|
||||
newbar.setColor(this.getColor())
|
||||
return newbar
|
||||
}
|
||||
|
||||
Bar.prototype.getHeight=function()
|
||||
{
|
||||
return this.height
|
||||
}
|
||||
|
||||
Bar.prototype.setHeight=function(newh)
|
||||
{
|
||||
this.height = newh
|
||||
}
|
||||
|
||||
Bar.prototype.getColor=function()
|
||||
{
|
||||
return this.color
|
||||
}
|
||||
|
||||
Bar.prototype.getPosition=function()
|
||||
{
|
||||
return this.position
|
||||
}
|
||||
|
||||
Bar.prototype.setPosition=function(newp)
|
||||
{
|
||||
this.position = newp
|
||||
}
|
||||
|
||||
Bar.prototype.show = function(c,p)
|
||||
{
|
||||
c.fillStyle=this.color
|
||||
c.fillRect(p*7 + 2, c.canvas.height-this.height, 3, this.height)
|
||||
}
|
||||
|
||||
Bar.prototype.unshow = function(c,p)
|
||||
{
|
||||
c.clearRect(p*7 + 2, c.canvas.height-this.height, 3, this.height)
|
||||
}
|
||||
|
||||
Bar.prototype.setColor=function(newc)
|
||||
{
|
||||
this.color = newc
|
||||
}
|
||||
|
||||
|
||||
|
||||
SortingAnimation = function() //Insertion Sort Demo
|
||||
{
|
||||
this.timer = null
|
||||
this.framelist = new Array()
|
||||
this.cursor = -1
|
||||
|
||||
this.sc = document.getElementById("sortingcanvas")
|
||||
this.ctx = this.sc.getContext("2d")
|
||||
this.sc.width = this.sc.width
|
||||
this.speed = 75
|
||||
|
||||
|
||||
|
||||
//commented out code does insertion sort, code below does bubble sort
|
||||
/* for (var index=1; index < this.barlist.bars.length; index = index+1)
|
||||
{
|
||||
this.barlist.bars[index].setColor("blue")
|
||||
this.snapshot()
|
||||
this.barlist.bars[index].setColor("black")
|
||||
this.snapshot()
|
||||
var currentvalue = this.barlist.bars[index].clone()
|
||||
var position = index
|
||||
while (position>0 && (this.barlist.bars[position-1].getHeight() > currentvalue.getHeight()))
|
||||
{
|
||||
this.barlist.bars[position-1].setColor("red")
|
||||
this.snapshot()
|
||||
this.barlist.bars[position-1].setColor("black")
|
||||
|
||||
this.barlist.bars[position] = this.barlist.bars[position-1].clone()
|
||||
//this.barlist.bars[position-1] = currentvalue
|
||||
this.barlist.bars[position-1].setHeight(0)
|
||||
|
||||
|
||||
this.snapshot()
|
||||
|
||||
position = position-1
|
||||
|
||||
}
|
||||
|
||||
this.barlist.bars[position] = currentvalue
|
||||
this.barlist.bars[position].setColor("blue")
|
||||
this.snapshot()
|
||||
this.barlist.bars[position].setColor("black")
|
||||
}
|
||||
this.snapshot()*/
|
||||
|
||||
this.barlist = new BarList(50)
|
||||
this.snapshot()
|
||||
for (var passnum=this.barlist.bars.length-1; passnum>0; passnum = passnum-1)
|
||||
{
|
||||
for (var i=0; i<passnum; i=i+1)
|
||||
{
|
||||
this.barlist.bars[i].setColor("red")
|
||||
this.barlist.bars[i+1].setColor("red")
|
||||
|
||||
this.snapshot()
|
||||
|
||||
if (this.barlist.bars[i].getHeight() > this.barlist.bars[i+1].getHeight())
|
||||
{
|
||||
|
||||
var temp = this.barlist.bars[i]
|
||||
this.barlist.bars[i] = this.barlist.bars[i+1]
|
||||
this.barlist.bars[i+1] = temp
|
||||
|
||||
this.snapshot()
|
||||
|
||||
}
|
||||
|
||||
this.barlist.bars[i].setColor("black")
|
||||
this.barlist.bars[i+1].setColor("black")
|
||||
|
||||
this.snapshot()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SortingAnimation.prototype.incCursor=function()
|
||||
{
|
||||
if (this.cursor < this.framelist.length-1)
|
||||
this.cursor = this.cursor + 1
|
||||
}
|
||||
|
||||
SortingAnimation.prototype.decCursor=function()
|
||||
{
|
||||
if (this.cursor > 0)
|
||||
this.cursor = this.cursor -1
|
||||
}
|
||||
|
||||
SortingAnimation.prototype.getCursor=function()
|
||||
{
|
||||
return this.cursor
|
||||
}
|
||||
|
||||
SortingAnimation.prototype.setCursor=function(newc)
|
||||
{
|
||||
this.cursor = newc
|
||||
}
|
||||
|
||||
|
||||
SortingAnimation.prototype.render = function(framenum)
|
||||
{
|
||||
var currentframe = this.framelist[framenum]
|
||||
this.sc.width = this.sc.width
|
||||
|
||||
for (var idx=0; idx<currentframe.length; idx++)
|
||||
{
|
||||
currentframe[idx].show(this.ctx,idx)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SortingAnimation.prototype.snapshot = function()
|
||||
{
|
||||
var newframe = new Array()
|
||||
for (var idx=0; idx<this.barlist.bars.length; idx++)
|
||||
{
|
||||
var newbar = new Bar() //make a copy
|
||||
newbar.setHeight(this.barlist.bars[idx].getHeight())
|
||||
newbar.setPosition(this.barlist.bars[idx].getPosition())
|
||||
newbar.setColor(this.barlist.bars[idx].getColor())
|
||||
|
||||
newframe.push(newbar)
|
||||
}
|
||||
|
||||
this.framelist.push(newframe)
|
||||
}
|
||||
|
||||
|
||||
|
||||
run = function()
|
||||
{
|
||||
if (sa.timer == null)
|
||||
sa.timer = setInterval("forward()",sa.speed)
|
||||
}
|
||||
|
||||
stop = function()
|
||||
{
|
||||
clearInterval(sa.timer)
|
||||
sa.timer=null
|
||||
}
|
||||
|
||||
forward = function()
|
||||
{
|
||||
sa.incCursor()
|
||||
sa.render(sa.getCursor())
|
||||
if (sa.getCursor() == sa.framelist.length-1 && sa.timer != null)
|
||||
{
|
||||
clearInterval(sa.timer)
|
||||
sa.timer = null
|
||||
}
|
||||
}
|
||||
|
||||
backward = function()
|
||||
{
|
||||
sa.decCursor()
|
||||
sa.render(sa.getCursor())
|
||||
}
|
||||
|
||||
end = function()
|
||||
{
|
||||
sa.setCursor(sa.framelist.length-1)
|
||||
sa.render(sa.getCursor())
|
||||
|
||||
}
|
||||
|
||||
begin = function()
|
||||
{
|
||||
sa.setCursor(0)
|
||||
sa.render(sa.getCursor())
|
||||
}
|
||||
|
||||
init = function()
|
||||
{
|
||||
sa = new SortingAnimation()
|
||||
sa.snapshot()
|
||||
sa.render(0)
|
||||
}
|
||||
32
book/modules/luther/sphinx/animation/sortingpackage.html
Normal file
32
book/modules/luther/sphinx/animation/sortingpackage.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<script type "text/javascript" src="sortingpackage.js"></script>
|
||||
|
||||
<html>
|
||||
<body onload="">
|
||||
|
||||
<canvas id="ancan" width="400" height="400" style="border:4px solid blue"></canvas>
|
||||
<br />
|
||||
<button onclick="init1()">BubbleSort with BarView</button><button onclick="init2()">BubbleSort with ScatterView</button>
|
||||
<button onclick="init3()">BubbleSort with BoxView</button><br/>
|
||||
<button onclick="init4()">SelectionSort with BarView</button><button onclick="init5()">SelectionSort with ScatterView</button>
|
||||
<button onclick="init6()">SelectionSort with BoxView</button><br/>
|
||||
<button onclick="init7()">InsertionSort with BarView</button><button onclick="init8()">InsertionSort with ScatterView</button>
|
||||
<button onclick="init9()">InsertionSort with BoxView</button><br/>
|
||||
<button onclick="init10()">ShellSort with BarView</button><button onclick="init11()">ShellSort with ScatterView</button>
|
||||
<button onclick="init12()">ShellSort with BoxView</button><br/>
|
||||
<button onclick="init13()">MergeSort with BarView</button><button onclick="init14()">MergeSort with ScatterView</button>
|
||||
<button onclick="init15()">MergeSort with BoxView</button><br/>
|
||||
<button onclick="init16()">QuickSort with BarView</button><button onclick="init17()">QuickSort with ScatterView</button>
|
||||
<button onclick="init18()">QuickSort with BoxView</button><br/>
|
||||
<button onclick="a.run()">Run</button>
|
||||
<button onclick="a.stop()">Stop</button> </br>
|
||||
<button onclick="a.begin()">Beginning</button>
|
||||
<button onclick="a.forward()">Step Forward</button>
|
||||
<button onclick="a.backward()">Step Backward</button>
|
||||
<button onclick="a.end()">End</button>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
870
book/modules/luther/sphinx/animation/sortingpackage.js
Normal file
870
book/modules/luther/sphinx/animation/sortingpackage.js
Normal file
|
|
@ -0,0 +1,870 @@
|
|||
DataItem = function(pos, h, col)
|
||||
{
|
||||
this.height = h
|
||||
this.position = pos
|
||||
this.color = col
|
||||
}
|
||||
|
||||
DataItem.prototype.clone=function()
|
||||
{
|
||||
var newitem = new DataItem(this.position,this.height,this.color) //make a copy
|
||||
return newitem
|
||||
}
|
||||
|
||||
DataItem.prototype.getHeight=function()
|
||||
{
|
||||
return this.height
|
||||
}
|
||||
|
||||
DataItem.prototype.getColor=function()
|
||||
{
|
||||
return this.color
|
||||
}
|
||||
|
||||
DataItem.prototype.getPosition=function()
|
||||
{
|
||||
return this.position
|
||||
}
|
||||
|
||||
DataItem.prototype.setHeight=function(newh)
|
||||
{
|
||||
this.height = newh
|
||||
}
|
||||
|
||||
DataItem.prototype.setPosition=function(newp)
|
||||
{
|
||||
this.position = newp
|
||||
}
|
||||
|
||||
DataItem.prototype.setColor=function(newc)
|
||||
{
|
||||
this.color = newc
|
||||
}
|
||||
|
||||
BubbleSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
BubbleSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
for (var passnum=this.valuelist.length-1; passnum>0; passnum = passnum-1)
|
||||
{
|
||||
for (var i=0; i<passnum; i=i+1)
|
||||
{
|
||||
this.valuelist[i].setColor("red")
|
||||
this.valuelist[i+1].setColor("red")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
if (this.valuelist[i].getHeight() > this.valuelist[i+1].getHeight())
|
||||
{
|
||||
|
||||
var temp = this.valuelist[i]
|
||||
this.valuelist[i] = this.valuelist[i+1]
|
||||
this.valuelist[i+1] = temp
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
}
|
||||
|
||||
this.valuelist[i].setColor("black")
|
||||
this.valuelist[i+1].setColor("black")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
}
|
||||
|
||||
return this.script
|
||||
}
|
||||
|
||||
BubbleSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
InsertionSortModel = function()
|
||||
{
|
||||
}
|
||||
|
||||
InsertionSortModel.prototype.init=function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
for (var index=1; index < this.valuelist.length; index = index+1)
|
||||
{
|
||||
this.valuelist[index].setColor("blue")
|
||||
this.script.push(this.makescene())
|
||||
this.valuelist[index].setColor("black")
|
||||
this.script.push(this.makescene())
|
||||
var currentvalue = this.valuelist[index].clone()
|
||||
var position = index
|
||||
while (position>0 && (this.valuelist[position-1].getHeight() > currentvalue.getHeight()))
|
||||
{
|
||||
this.valuelist[position-1].setColor("red")
|
||||
this.script.push(this.makescene())
|
||||
this.valuelist[position-1].setColor("black")
|
||||
|
||||
this.valuelist[position] = this.valuelist[position-1].clone()
|
||||
//this.barlist.bars[position-1] = currentvalue
|
||||
this.valuelist[position-1].setHeight(0)
|
||||
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
position = position-1
|
||||
|
||||
}
|
||||
|
||||
this.valuelist[position] = currentvalue
|
||||
this.valuelist[position].setColor("blue")
|
||||
this.script.push(this.makescene())
|
||||
this.valuelist[position].setColor("black")
|
||||
}
|
||||
|
||||
this.script.push(this.makescene())
|
||||
return this.script
|
||||
}
|
||||
|
||||
|
||||
InsertionSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
return newscene
|
||||
}
|
||||
|
||||
SelectionSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
SelectionSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
|
||||
for (var fillslot=this.valuelist.length-1; fillslot>0; fillslot = fillslot-1)
|
||||
{ var positionOfMax=0
|
||||
this.valuelist[positionOfMax].setColor("yellow")
|
||||
this.valuelist[fillslot].setColor("blue")
|
||||
this.script.push(this.makescene())
|
||||
|
||||
for (var i=1; i<fillslot+1; i=i+1)
|
||||
{
|
||||
this.valuelist[i].setColor("red")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
if (this.valuelist[i].getHeight() > this.valuelist[positionOfMax].getHeight())
|
||||
{
|
||||
this.valuelist[positionOfMax].setColor("black")
|
||||
positionOfMax = i
|
||||
this.valuelist[i].setColor("yellow")
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
else
|
||||
{
|
||||
this.valuelist[i].setColor("black")
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var temp = this.valuelist[fillslot]
|
||||
this.valuelist[fillslot] = this.valuelist[positionOfMax]
|
||||
this.valuelist[positionOfMax] = temp
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
this.valuelist[fillslot].setColor("black")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
|
||||
return this.script
|
||||
}
|
||||
|
||||
SelectionSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
ShellSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
ShellSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
var sublistcount = Math.floor(this.valuelist.length/2)
|
||||
while (sublistcount > 0)
|
||||
{
|
||||
for (var startposition = 0; startposition < sublistcount;
|
||||
startposition = startposition+1)
|
||||
{
|
||||
var gap = sublistcount
|
||||
var start = startposition
|
||||
this.valuelist[start].setColor("red")
|
||||
for (var i=start+gap; i<this.valuelist.length; i = i + gap)
|
||||
{
|
||||
currentvalue = this.valuelist[i].clone()
|
||||
currentvalue.setColor("red")
|
||||
this.script.push(this.makescene())
|
||||
position = i
|
||||
while (position>=gap && this.valuelist[position-gap].getHeight()>currentvalue.getHeight())
|
||||
{
|
||||
this.valuelist[position] = this.valuelist[position-gap].clone()
|
||||
this.valuelist[position-gap].setHeight(0)
|
||||
position = position-gap
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
this.valuelist[position]=currentvalue
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
for (var clearidx=0; clearidx<this.valuelist.length; clearidx++)
|
||||
this.valuelist[clearidx].setColor("black")
|
||||
this.script.push(this.makescene())
|
||||
|
||||
}
|
||||
this.script.push(this.makescene())
|
||||
sublistcount = Math.floor(sublistcount/2)
|
||||
}
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
return this.script
|
||||
}
|
||||
|
||||
ShellSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
MergeSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
this.domergesort(0,this.valuelist.length-1)
|
||||
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
return this.script
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.chunkcolor=function(start,end,c)
|
||||
{
|
||||
for (var clearidx=start; clearidx<=end; clearidx++)
|
||||
this.valuelist[clearidx].setColor(c)
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.domergesort = function(start,end)
|
||||
{ len = end-start + 1
|
||||
if (len>1)
|
||||
{
|
||||
var mid = start + Math.floor(len/2)
|
||||
|
||||
this.chunkcolor(start,mid-1,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(start,mid-1,"black")
|
||||
this.domergesort(start,mid-1)
|
||||
|
||||
this.chunkcolor(mid,end,"blue")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(mid,end,"black")
|
||||
this.domergesort(mid,end)
|
||||
|
||||
var i=start
|
||||
var j=mid
|
||||
|
||||
var newlist = Array()
|
||||
while (i<mid && j<=end)
|
||||
{
|
||||
if (this.valuelist[i].getHeight()<this.valuelist[j].getHeight())
|
||||
{
|
||||
newlist.push(this.valuelist[i])
|
||||
i=i+1
|
||||
}
|
||||
else
|
||||
{
|
||||
newlist.push(this.valuelist[j])
|
||||
j=j+1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (i<mid)
|
||||
{
|
||||
newlist.push(this.valuelist[i])
|
||||
i=i+1
|
||||
}
|
||||
|
||||
while (j<=end)
|
||||
{
|
||||
newlist.push(this.valuelist[j])
|
||||
j=j+1
|
||||
}
|
||||
this.copyback(newlist,start)
|
||||
this.chunkcolor(start,end,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(start,end,"black")
|
||||
}
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.copyback = function(original,i,j) //make copy from i to j excl
|
||||
{
|
||||
var newcopy = new Array()
|
||||
for (var idx=0; idx<original.length; idx++)
|
||||
{
|
||||
var item = original[idx].clone() //make a copy
|
||||
this.valuelist[i] = item
|
||||
i=i+1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MergeSortModel.prototype.makecopy = function(original,i) //make copy to i
|
||||
{
|
||||
|
||||
for (var idx=0; idx<original.length; idx++)
|
||||
{
|
||||
var item = original[idx].clone() //make a copy
|
||||
this.valuelist[i] = item
|
||||
i++
|
||||
}
|
||||
|
||||
return newcopy
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.makescene = function(somearray)
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<somearray.length; idx++)
|
||||
{
|
||||
var item = somearray[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
QuickSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
this.quickSort(this.valuelist)
|
||||
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
return this.script
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.quickSort=function(alist)
|
||||
{
|
||||
this.quickSortHelper(alist,0,alist.length-1)
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.quickSortHelper=function(alist,first,last)
|
||||
{
|
||||
if (first<last)
|
||||
{
|
||||
var splitpoint = this.partition(alist,first,last)
|
||||
|
||||
this.chunkcolor(first,splitpoint-1,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(first,splitpoint-1,"black")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
this.chunkcolor(splitpoint+1,last,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(splitpoint+1,last,"black")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.quickSortHelper(alist,first,splitpoint-1)
|
||||
this.quickSortHelper(alist,splitpoint+1,last)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QuickSortModel.prototype.partition = function(alist,first,last)
|
||||
{
|
||||
var pivotvalue = alist[first].getHeight()
|
||||
alist[first].setColor("red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
|
||||
var leftmark = first+1
|
||||
var rightmark = last
|
||||
alist[leftmark].setColor("blue")
|
||||
alist[rightmark].setColor("blue")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
|
||||
var done = false
|
||||
while (! done)
|
||||
{
|
||||
|
||||
while (leftmark <= rightmark && alist[leftmark].getHeight() <= pivotvalue)
|
||||
{
|
||||
alist[leftmark].setColor("black")
|
||||
leftmark = leftmark + 1
|
||||
if (leftmark <= rightmark)
|
||||
{
|
||||
alist[leftmark].setColor("blue")
|
||||
this.script.push(this.makescene(this.valuelist))}
|
||||
}
|
||||
while (alist[rightmark].getHeight() >= pivotvalue && rightmark >= leftmark)
|
||||
{
|
||||
alist[rightmark].setColor("black")
|
||||
rightmark = rightmark - 1
|
||||
if (rightmark >= leftmark)
|
||||
{
|
||||
alist[rightmark].setColor("blue")
|
||||
this.script.push(this.makescene(this.valuelist))}
|
||||
}
|
||||
|
||||
if (rightmark < leftmark)
|
||||
done = true
|
||||
else
|
||||
{
|
||||
temp = alist[leftmark]
|
||||
alist[leftmark] = alist[rightmark]
|
||||
alist[rightmark] = temp
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
alist[leftmark].setColor("black")
|
||||
alist[rightmark].setColor("black")
|
||||
}
|
||||
}
|
||||
|
||||
var temp = alist[first]
|
||||
alist[first] = alist[rightmark]
|
||||
alist[rightmark] = temp
|
||||
|
||||
alist[first].setColor("black")
|
||||
alist[rightmark].setColor("red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(0,this.valuelist.length-1,"black")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
|
||||
return rightmark
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.chunkcolor=function(start,end,c)
|
||||
{
|
||||
for (var clearidx=start; clearidx<=end; clearidx++)
|
||||
this.valuelist[clearidx].setColor(c)
|
||||
}
|
||||
|
||||
|
||||
QuickSortModel.prototype.makescene = function(somearray)
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<somearray.length; idx++)
|
||||
{
|
||||
var item = somearray[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BarViewer = function() //construct the view
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BarViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
BarViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillRect(p*7 + 2,
|
||||
this.ctx.canvas.height-ascene[p].height,
|
||||
3,
|
||||
ascene[p].height)
|
||||
}
|
||||
}
|
||||
|
||||
ScatterViewer = function() //contruct a list of numbers view
|
||||
{
|
||||
}
|
||||
|
||||
ScatterViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
ScatterViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillText(ascene[p].height, p*7 + 2,
|
||||
this.ctx.canvas.height-ascene[p].height)
|
||||
}
|
||||
}
|
||||
|
||||
BoxViewer = function() //contruct an array of boxes view
|
||||
{
|
||||
}
|
||||
|
||||
BoxViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
BoxViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillText(ascene[p].height, p*25 + 3, 200)
|
||||
this.ctx.strokeStyle = ascene[p].color
|
||||
this.ctx.strokeRect(p*25+2,185,25,25)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Animator = function(m, v)
|
||||
{
|
||||
this.model = m
|
||||
this.viewer = v
|
||||
this.timer = null
|
||||
|
||||
this.cursor = -1
|
||||
|
||||
this.sc = document.getElementById("ancan")
|
||||
this.ctx = this.sc.getContext("2d")
|
||||
this.sc.width = this.sc.width
|
||||
this.speed = 75
|
||||
|
||||
this.script = this.model.init() //does the sort and sends script back
|
||||
this.viewer.init(this.ctx)
|
||||
}
|
||||
|
||||
Animator.prototype.getContext=function()
|
||||
{
|
||||
return this.ctx
|
||||
}
|
||||
|
||||
Animator.prototype.incCursor=function()
|
||||
{
|
||||
if (this.cursor < this.script.length-1)
|
||||
this.cursor = this.cursor + 1
|
||||
}
|
||||
|
||||
Animator.prototype.decCursor=function()
|
||||
{
|
||||
if (this.cursor > 0)
|
||||
this.cursor = this.cursor -1
|
||||
}
|
||||
|
||||
Animator.prototype.getCursor=function()
|
||||
{
|
||||
return this.cursor
|
||||
}
|
||||
|
||||
Animator.prototype.setCursor=function(newc)
|
||||
{
|
||||
this.cursor = newc
|
||||
}
|
||||
|
||||
Animator.prototype.run = function()
|
||||
{
|
||||
if (this.timer == null)
|
||||
this.timer = setInterval("a.forward()",this.speed)
|
||||
}
|
||||
|
||||
Animator.prototype.stop = function()
|
||||
{
|
||||
clearInterval(this.timer)
|
||||
this.timer=null
|
||||
}
|
||||
|
||||
Animator.prototype.forward = function()
|
||||
{
|
||||
this.incCursor()
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
if (this.getCursor() == this.script.length-1 && this.timer != null)
|
||||
{
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
}
|
||||
|
||||
Animator.prototype.backward = function()
|
||||
{
|
||||
this.decCursor()
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
}
|
||||
|
||||
Animator.prototype.end = function()
|
||||
{
|
||||
this.setCursor(this.script.length-1)
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
|
||||
}
|
||||
|
||||
Animator.prototype.begin = function()
|
||||
{
|
||||
this.setCursor(0)
|
||||
this.sc.width=this.sc.width
|
||||
this.viewer.render(this.script[this.getCursor()])
|
||||
}
|
||||
|
||||
Animator.prototype.init = function()
|
||||
{
|
||||
this.setCursor(0)
|
||||
this.sc.width = this.sc.width
|
||||
this.viewer.render(this.script[0])
|
||||
}
|
||||
|
||||
init1 = function()
|
||||
{
|
||||
a = new Animator(new BubbleSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init2 = function()
|
||||
{
|
||||
a = new Animator(new BubbleSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init3 = function()
|
||||
{
|
||||
a = new Animator(new BubbleSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init4 = function()
|
||||
{
|
||||
a = new Animator(new SelectionSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init5 = function()
|
||||
{
|
||||
a = new Animator(new SelectionSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init6 = function()
|
||||
{
|
||||
a = new Animator(new SelectionSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init7 = function()
|
||||
{
|
||||
a = new Animator(new InsertionSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init8 = function()
|
||||
{
|
||||
a = new Animator(new InsertionSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init9 = function()
|
||||
{
|
||||
a = new Animator(new InsertionSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init10 = function()
|
||||
{
|
||||
a = new Animator(new ShellSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init11 = function()
|
||||
{
|
||||
a = new Animator(new ShellSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init12 = function()
|
||||
{
|
||||
a = new Animator(new ShellSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init13 = function()
|
||||
{
|
||||
a = new Animator(new MergeSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init14 = function()
|
||||
{
|
||||
a = new Animator(new MergeSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init15 = function()
|
||||
{
|
||||
a = new Animator(new MergeSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init16 = function()
|
||||
{
|
||||
a = new Animator(new QuickSortModel(), new BarViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init17 = function()
|
||||
{
|
||||
a = new Animator(new QuickSortModel(), new ScatterViewer())
|
||||
a.init()
|
||||
}
|
||||
|
||||
init18 = function()
|
||||
{
|
||||
a = new Animator(new QuickSortModel(), new BoxViewer())
|
||||
a.init()
|
||||
}
|
||||
601
book/modules/luther/sphinx/animation/sortmodels.js
Normal file
601
book/modules/luther/sphinx/animation/sortmodels.js
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
DataItem = function(pos, h, col)
|
||||
{
|
||||
this.value = h
|
||||
this.position = pos
|
||||
this.color = col
|
||||
}
|
||||
|
||||
DataItem.prototype.clone=function()
|
||||
{
|
||||
var newitem = new DataItem(this.position,this.value,this.color) //make a copy
|
||||
return newitem
|
||||
}
|
||||
|
||||
DataItem.prototype.getValue=function()
|
||||
{
|
||||
return this.value
|
||||
}
|
||||
|
||||
DataItem.prototype.getColor=function()
|
||||
{
|
||||
return this.color
|
||||
}
|
||||
|
||||
DataItem.prototype.getPosition=function()
|
||||
{
|
||||
return this.position
|
||||
}
|
||||
|
||||
DataItem.prototype.setValue=function(newh)
|
||||
{
|
||||
this.value = newh
|
||||
}
|
||||
|
||||
DataItem.prototype.setPosition=function(newp)
|
||||
{
|
||||
this.position = newp
|
||||
}
|
||||
|
||||
DataItem.prototype.setColor=function(newc)
|
||||
{
|
||||
this.color = newc
|
||||
}
|
||||
|
||||
BubbleSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
BubbleSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
for (var passnum=this.valuelist.length-1; passnum>0; passnum = passnum-1)
|
||||
{
|
||||
for (var i=0; i<passnum; i=i+1)
|
||||
{
|
||||
this.valuelist[i].setColor("red")
|
||||
this.valuelist[i+1].setColor("red")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
if (this.valuelist[i].getValue() > this.valuelist[i+1].getValue())
|
||||
{
|
||||
|
||||
var temp = this.valuelist[i]
|
||||
this.valuelist[i] = this.valuelist[i+1]
|
||||
this.valuelist[i+1] = temp
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
}
|
||||
|
||||
this.valuelist[i].setColor("black")
|
||||
this.valuelist[i+1].setColor("black")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
}
|
||||
|
||||
return this.script
|
||||
}
|
||||
|
||||
BubbleSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
InsertionSortModel = function()
|
||||
{
|
||||
}
|
||||
|
||||
InsertionSortModel.prototype.init=function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
for (var index=1; index < this.valuelist.length; index = index+1)
|
||||
{
|
||||
this.valuelist[index].setColor("blue")
|
||||
this.script.push(this.makescene())
|
||||
this.valuelist[index].setColor("black")
|
||||
this.script.push(this.makescene())
|
||||
var currentvalue = this.valuelist[index].clone()
|
||||
var position = index
|
||||
while (position>0 && (this.valuelist[position-1].getValue() > currentvalue.getValue()))
|
||||
{
|
||||
this.valuelist[position-1].setColor("red")
|
||||
this.script.push(this.makescene())
|
||||
this.valuelist[position-1].setColor("black")
|
||||
|
||||
this.valuelist[position] = this.valuelist[position-1].clone()
|
||||
//this.barlist.bars[position-1] = currentvalue
|
||||
this.valuelist[position-1].setValue(0)
|
||||
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
position = position-1
|
||||
|
||||
}
|
||||
|
||||
this.valuelist[position] = currentvalue
|
||||
this.valuelist[position].setColor("blue")
|
||||
this.script.push(this.makescene())
|
||||
this.valuelist[position].setColor("black")
|
||||
}
|
||||
|
||||
this.script.push(this.makescene())
|
||||
return this.script
|
||||
}
|
||||
|
||||
|
||||
InsertionSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
return newscene
|
||||
}
|
||||
|
||||
SelectionSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
SelectionSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
|
||||
for (var fillslot=this.valuelist.length-1; fillslot>0; fillslot = fillslot-1)
|
||||
{ var positionOfMax=0
|
||||
this.valuelist[positionOfMax].setColor("yellow")
|
||||
this.valuelist[fillslot].setColor("blue")
|
||||
this.script.push(this.makescene())
|
||||
|
||||
for (var i=1; i<fillslot+1; i=i+1)
|
||||
{
|
||||
this.valuelist[i].setColor("red")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
if (this.valuelist[i].getValue() > this.valuelist[positionOfMax].getValue())
|
||||
{
|
||||
this.valuelist[positionOfMax].setColor("black")
|
||||
positionOfMax = i
|
||||
this.valuelist[i].setColor("yellow")
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
else
|
||||
{
|
||||
this.valuelist[i].setColor("black")
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var temp = this.valuelist[fillslot]
|
||||
this.valuelist[fillslot] = this.valuelist[positionOfMax]
|
||||
this.valuelist[positionOfMax] = temp
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
this.valuelist[fillslot].setColor("black")
|
||||
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
|
||||
return this.script
|
||||
}
|
||||
|
||||
SelectionSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
ShellSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
ShellSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene())
|
||||
|
||||
var sublistcount = Math.floor(this.valuelist.length/2)
|
||||
while (sublistcount > 0)
|
||||
{
|
||||
for (var startposition = 0; startposition < sublistcount;
|
||||
startposition = startposition+1)
|
||||
{
|
||||
var gap = sublistcount
|
||||
var start = startposition
|
||||
this.valuelist[start].setColor("red")
|
||||
for (var i=start+gap; i<this.valuelist.length; i = i + gap)
|
||||
{
|
||||
currentvalue = this.valuelist[i].clone()
|
||||
currentvalue.setColor("red")
|
||||
this.script.push(this.makescene())
|
||||
position = i
|
||||
while (position>=gap && this.valuelist[position-gap].getValue()>currentvalue.getValue())
|
||||
{
|
||||
this.valuelist[position] = this.valuelist[position-gap].clone()
|
||||
this.valuelist[position-gap].setValue(0)
|
||||
position = position-gap
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
this.valuelist[position]=currentvalue
|
||||
this.script.push(this.makescene())
|
||||
}
|
||||
for (var clearidx=0; clearidx<this.valuelist.length; clearidx++)
|
||||
this.valuelist[clearidx].setColor("black")
|
||||
this.script.push(this.makescene())
|
||||
|
||||
}
|
||||
this.script.push(this.makescene())
|
||||
sublistcount = Math.floor(sublistcount/2)
|
||||
}
|
||||
|
||||
this.script.push(this.makescene())
|
||||
|
||||
return this.script
|
||||
}
|
||||
|
||||
ShellSortModel.prototype.makescene = function()
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<this.valuelist.length; idx++)
|
||||
{
|
||||
var item = this.valuelist[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
MergeSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
this.domergesort(0,this.valuelist.length-1)
|
||||
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
return this.script
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.chunkcolor=function(start,end,c)
|
||||
{
|
||||
for (var clearidx=start; clearidx<=end; clearidx++)
|
||||
this.valuelist[clearidx].setColor(c)
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.domergesort = function(start,end)
|
||||
{ len = end-start + 1
|
||||
if (len>1)
|
||||
{
|
||||
var mid = start + Math.floor(len/2)
|
||||
|
||||
this.chunkcolor(start,mid-1,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(start,mid-1,"black")
|
||||
this.domergesort(start,mid-1)
|
||||
|
||||
this.chunkcolor(mid,end,"blue")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(mid,end,"black")
|
||||
this.domergesort(mid,end)
|
||||
|
||||
var i=start
|
||||
var j=mid
|
||||
|
||||
var newlist = Array()
|
||||
while (i<mid && j<=end)
|
||||
{
|
||||
if (this.valuelist[i].getValue()<this.valuelist[j].getValue())
|
||||
{
|
||||
newlist.push(this.valuelist[i])
|
||||
i=i+1
|
||||
}
|
||||
else
|
||||
{
|
||||
newlist.push(this.valuelist[j])
|
||||
j=j+1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (i<mid)
|
||||
{
|
||||
newlist.push(this.valuelist[i])
|
||||
i=i+1
|
||||
}
|
||||
|
||||
while (j<=end)
|
||||
{
|
||||
newlist.push(this.valuelist[j])
|
||||
j=j+1
|
||||
}
|
||||
this.copyback(newlist,start)
|
||||
this.chunkcolor(start,end,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(start,end,"black")
|
||||
}
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.copyback = function(original,i,j) //make copy from i to j excl
|
||||
{
|
||||
var newcopy = new Array()
|
||||
for (var idx=0; idx<original.length; idx++)
|
||||
{
|
||||
var item = original[idx].clone() //make a copy
|
||||
this.valuelist[i] = item
|
||||
i=i+1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MergeSortModel.prototype.makecopy = function(original,i) //make copy to i
|
||||
{
|
||||
|
||||
for (var idx=0; idx<original.length; idx++)
|
||||
{
|
||||
var item = original[idx].clone() //make a copy
|
||||
this.valuelist[i] = item
|
||||
i++
|
||||
}
|
||||
|
||||
return newcopy
|
||||
}
|
||||
|
||||
MergeSortModel.prototype.makescene = function(somearray)
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<somearray.length; idx++)
|
||||
{
|
||||
var item = somearray[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
|
||||
QuickSortModel = function() //construct the model
|
||||
{
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.init = function(ctl)
|
||||
{
|
||||
this.mycontroller = ctl
|
||||
|
||||
this.valuelist = new Array()
|
||||
var howmany = 15
|
||||
|
||||
for (var i=0; i<howmany; i++)
|
||||
{
|
||||
var min = 5
|
||||
var max = 300
|
||||
var y = Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
var item = new DataItem(i,y,"black")
|
||||
this.valuelist.push(item)
|
||||
}
|
||||
|
||||
this.script = new Array()
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
this.quickSort(this.valuelist)
|
||||
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
return this.script
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.quickSort=function(alist)
|
||||
{
|
||||
this.quickSortHelper(alist,0,alist.length-1)
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.quickSortHelper=function(alist,first,last)
|
||||
{
|
||||
if (first<last)
|
||||
{
|
||||
var splitpoint = this.partition(alist,first,last)
|
||||
|
||||
this.chunkcolor(first,splitpoint-1,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(first,splitpoint-1,"black")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
this.chunkcolor(splitpoint+1,last,"red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(splitpoint+1,last,"black")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.quickSortHelper(alist,first,splitpoint-1)
|
||||
this.quickSortHelper(alist,splitpoint+1,last)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QuickSortModel.prototype.partition = function(alist,first,last)
|
||||
{
|
||||
var pivotvalue = alist[first].getValue()
|
||||
alist[first].setColor("red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
|
||||
var leftmark = first+1
|
||||
var rightmark = last
|
||||
alist[leftmark].setColor("blue")
|
||||
alist[rightmark].setColor("blue")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
|
||||
var done = false
|
||||
while (! done)
|
||||
{
|
||||
|
||||
while (leftmark <= rightmark && alist[leftmark].getValue() <= pivotvalue)
|
||||
{
|
||||
alist[leftmark].setColor("black")
|
||||
leftmark = leftmark + 1
|
||||
if (leftmark <= rightmark)
|
||||
{
|
||||
alist[leftmark].setColor("blue")
|
||||
this.script.push(this.makescene(this.valuelist))}
|
||||
}
|
||||
while (alist[rightmark].getValue() >= pivotvalue && rightmark >= leftmark)
|
||||
{
|
||||
alist[rightmark].setColor("black")
|
||||
rightmark = rightmark - 1
|
||||
if (rightmark >= leftmark)
|
||||
{
|
||||
alist[rightmark].setColor("blue")
|
||||
this.script.push(this.makescene(this.valuelist))}
|
||||
}
|
||||
|
||||
if (rightmark < leftmark)
|
||||
done = true
|
||||
else
|
||||
{
|
||||
temp = alist[leftmark]
|
||||
alist[leftmark] = alist[rightmark]
|
||||
alist[rightmark] = temp
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
alist[leftmark].setColor("black")
|
||||
alist[rightmark].setColor("black")
|
||||
}
|
||||
}
|
||||
|
||||
var temp = alist[first]
|
||||
alist[first] = alist[rightmark]
|
||||
alist[rightmark] = temp
|
||||
|
||||
alist[first].setColor("black")
|
||||
alist[rightmark].setColor("red")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
this.chunkcolor(0,this.valuelist.length-1,"black")
|
||||
this.script.push(this.makescene(this.valuelist))
|
||||
|
||||
|
||||
return rightmark
|
||||
}
|
||||
|
||||
QuickSortModel.prototype.chunkcolor=function(start,end,c)
|
||||
{
|
||||
for (var clearidx=start; clearidx<=end; clearidx++)
|
||||
this.valuelist[clearidx].setColor(c)
|
||||
}
|
||||
|
||||
|
||||
QuickSortModel.prototype.makescene = function(somearray)
|
||||
{
|
||||
var newscene = new Array()
|
||||
for (var idx=0; idx<somearray.length; idx++)
|
||||
{
|
||||
var item = somearray[idx].clone() //make a copy
|
||||
newscene.push(item)
|
||||
}
|
||||
|
||||
return newscene
|
||||
}
|
||||
|
||||
61
book/modules/luther/sphinx/animation/sortviewers.js
Normal file
61
book/modules/luther/sphinx/animation/sortviewers.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
BarViewer = function() //construct the view
|
||||
{
|
||||
}
|
||||
|
||||
BarViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
BarViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillRect(p*7 + 2,
|
||||
this.ctx.canvas.height-ascene[p].getValue(),
|
||||
3,
|
||||
ascene[p].getValue())
|
||||
}
|
||||
}
|
||||
|
||||
ScatterViewer = function() //contruct a list of numbers view
|
||||
{
|
||||
}
|
||||
|
||||
ScatterViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
ScatterViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillText(ascene[p].getValue(), p*7 + 2,
|
||||
this.ctx.canvas.height-ascene[p].getValue())
|
||||
}
|
||||
}
|
||||
|
||||
BoxViewer = function() //contruct an array of boxes view
|
||||
{
|
||||
}
|
||||
|
||||
BoxViewer.prototype.init = function(c)
|
||||
{
|
||||
this.ctx = c
|
||||
}
|
||||
|
||||
BoxViewer.prototype.render = function(ascene)
|
||||
{
|
||||
for (var p=0; p<ascene.length; p++)
|
||||
{
|
||||
this.ctx.fillStyle=ascene[p].color
|
||||
this.ctx.fillText(ascene[p].getValue(), p*25 + 3, 200)
|
||||
this.ctx.strokeStyle = ascene[p].color
|
||||
this.ctx.strokeRect(p*25+2,185,25,25)
|
||||
}
|
||||
}
|
||||
|
||||
2
book/modules/luther/sphinx/assess/__init__.py
Normal file
2
book/modules/luther/sphinx/assess/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from .assess import *
|
||||
112
book/modules/luther/sphinx/assess/assess.py
Normal file
112
book/modules/luther/sphinx/assess/assess.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# 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
|
||||
from assessbase import Assessment
|
||||
from multiplechoice import *
|
||||
from textfield import *
|
||||
from blankfill import *
|
||||
import json
|
||||
import random
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('mchoicemf',MChoiceMF)
|
||||
app.add_directive('mchoicema',MChoiceMA)
|
||||
app.add_directive('fillintheblank',FillInTheBlank)
|
||||
app.add_directive('mcmfrandom',MChoiceRandomMF)
|
||||
app.add_directive('addbutton',AddButton)
|
||||
app.add_directive('qnum',QuestionNumber)
|
||||
app.add_role('textfield',textfield_role)
|
||||
|
||||
|
||||
app.add_javascript('assess.js')
|
||||
|
||||
app.add_node(MChoiceNode, html=(visit_mc_node, depart_mc_node))
|
||||
app.add_node(FITBNode, html=(visit_fitb_node, depart_fitb_node))
|
||||
|
||||
class AddButton(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
:param self:
|
||||
:return:
|
||||
.. addbutton:: bname
|
||||
|
||||
...
|
||||
"""
|
||||
|
||||
TEMPLATE_START = '''
|
||||
<div id="%(divid)s" class="alert alert-warning">
|
||||
<form name="%(divid)s_form" method="get" action="" onsubmit="return false;">
|
||||
'''
|
||||
|
||||
TEMPLATE_END = '''
|
||||
<button class='btn btn-inverse' name="reset" onclick="resetPage('%(divid)s')">Forget My Answers</button>
|
||||
</form>
|
||||
</div>
|
||||
'''
|
||||
|
||||
self.options['divid'] = self.arguments[0]
|
||||
|
||||
res = ""
|
||||
res = TEMPLATE_START % self.options
|
||||
|
||||
res += TEMPLATE_END % self.options
|
||||
return [nodes.raw('',res , format='html')]
|
||||
|
||||
|
||||
class QuestionNumber(Directive):
|
||||
"""Set Parameters for Question Numbering"""
|
||||
required_arguments = 0
|
||||
optional_arguments = 3
|
||||
has_content = False
|
||||
option_spec = { 'prefix': directives.unchanged,
|
||||
'suffix': directives.unchanged,
|
||||
'start': directives.positive_int
|
||||
}
|
||||
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
|
||||
if 'start' in self.options:
|
||||
env.assesscounter = self.options['start'] - 1
|
||||
|
||||
if 'prefix' in self.options:
|
||||
env.assessprefix = self.options['prefix']
|
||||
|
||||
if 'suffix' in self.options:
|
||||
env.assesssuffix = self.options['suffix']
|
||||
|
||||
return []
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#####################
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
95
book/modules/luther/sphinx/assess/assessbase.py
Normal file
95
book/modules/luther/sphinx/assess/assessbase.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# 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
|
||||
|
||||
|
||||
_base_js_escapes = (
|
||||
('\\', r'\u005C'),
|
||||
('\'', r'\u0027'),
|
||||
('"', r'\u0022'),
|
||||
("'", r'\u0027'),
|
||||
('>', r'\u003E'),
|
||||
('<', r'\u003C'),
|
||||
('&', r'\u0026'),
|
||||
('=', r'\u003D'),
|
||||
('-', r'\u002D'),
|
||||
(';', r'\u003B'),
|
||||
(u'\u2028', r'\u2028'),
|
||||
(u'\u2029', r'\u2029')
|
||||
)
|
||||
|
||||
# Escape every ASCII character with a value less than 32.
|
||||
_js_escapes = (_base_js_escapes +
|
||||
tuple([('%c' % z, '\\u%04X' % z) for z in range(32)]))
|
||||
|
||||
# escapejs from Django: https://www.djangoproject.com/
|
||||
def escapejs(value):
|
||||
"""Hex encodes characters for use in JavaScript strings."""
|
||||
if not isinstance(value, basestring):
|
||||
value = str(value)
|
||||
|
||||
for bad, good in _js_escapes:
|
||||
value = value.replace(bad, good)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class Assessment(Directive):
|
||||
"""Base Class for assessments"""
|
||||
|
||||
def getNumber(self):
|
||||
env = self.state.document.settings.env
|
||||
if not hasattr(env,'assesscounter'):
|
||||
env.assesscounter = 0
|
||||
env.assesscounter += 1
|
||||
|
||||
res = "Q-%d"
|
||||
|
||||
if hasattr(env,'assessprefix'):
|
||||
res = env.assessprefix + "%d"
|
||||
|
||||
res = res % env.assesscounter
|
||||
|
||||
if hasattr(env, 'assesssuffix'):
|
||||
res += env.assesssuffix
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def run(self):
|
||||
|
||||
self.options['qnumber'] = self.getNumber()
|
||||
|
||||
self.options['divid'] = self.arguments[0]
|
||||
|
||||
if self.content[0][:2] == '..': # first line is a directive
|
||||
self.content[0] = self.options['qnumber'] + ': \n\n' + self.content[0]
|
||||
else:
|
||||
self.content[0] = self.options['qnumber'] + ': ' + self.content[0]
|
||||
|
||||
if self.content:
|
||||
if 'iscode' in self.options:
|
||||
self.options['bodytext'] = '<pre>' + "\n".join(self.content) + '</pre>'
|
||||
else:
|
||||
self.options['bodytext'] = "\n".join(self.content)
|
||||
else:
|
||||
self.options['bodytext'] = '\n'
|
||||
|
||||
|
||||
123
book/modules/luther/sphinx/assess/blankfill.py
Normal file
123
book/modules/luther/sphinx/assess/blankfill.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# Copyright (C) 2013 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
|
||||
from assessbase import *
|
||||
import json
|
||||
import random
|
||||
|
||||
|
||||
|
||||
class FITBNode(nodes.General, nodes.Element):
|
||||
def __init__(self,content):
|
||||
"""
|
||||
|
||||
Arguments:
|
||||
- `self`:
|
||||
- `content`:
|
||||
"""
|
||||
super(FITBNode,self).__init__()
|
||||
self.fitb_options = content
|
||||
|
||||
|
||||
def visit_fitb_node(self,node):
|
||||
res = node.template_start % node.fitb_options
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
|
||||
def depart_fitb_node(self,node):
|
||||
fbl = []
|
||||
for k in sorted(node.fitb_options.keys()):
|
||||
if 'feedback' in k:
|
||||
pair = eval(node.fitb_options[k])
|
||||
p1 = escapejs(pair[1])
|
||||
newpair = (pair[0],p1)
|
||||
fbl.append(newpair)
|
||||
|
||||
if 'casei' in node.fitb_options:
|
||||
node.fitb_options['casei'] = 'true'
|
||||
else:
|
||||
node.fitb_options['casei'] = 'false'
|
||||
node.fitb_options['fbl'] = json.dumps(fbl).replace('"',"'")
|
||||
res = ""
|
||||
|
||||
res += node.template_end % node.fitb_options
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
|
||||
class FillInTheBlank(Assessment):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {'correct':directives.unchanged,
|
||||
'feedback':directives.unchanged,
|
||||
'feedback1':directives.unchanged,
|
||||
'feedback2':directives.unchanged,
|
||||
'feedback3':directives.unchanged,
|
||||
'feedback4':directives.unchanged,
|
||||
'blankid':directives.unchanged,
|
||||
'iscode':directives.flag,
|
||||
'casei':directives.flag # case insensitive matching
|
||||
}
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
process the fillintheblank directive and generate html for output.
|
||||
:param self:
|
||||
:return:
|
||||
.. fillintheblank:: qname
|
||||
:iscode: boolean
|
||||
:correct: somestring
|
||||
:feedback: -- displayed if wrong
|
||||
:feedback: ('.*', 'this is the message')
|
||||
Question text
|
||||
...
|
||||
"""
|
||||
|
||||
TEMPLATE_START = '''
|
||||
<div id="%(divid)s" class="alert alert-warning">
|
||||
'''
|
||||
|
||||
TEMPLATE_END = '''
|
||||
<script>
|
||||
$(document).ready(function(){checkPreviousFIB('%(divid)s');});
|
||||
</script>
|
||||
<button class='btn btn-success' name="do answer" onclick="checkFIBStorage('%(divid)s', '%(blankid)s', '%(correct)s',%(fbl)s, %(casei)s)">Check Me</button>
|
||||
<button class='btn btn-default' id="%(divid)s_bcomp" disabled name="compare" onclick="compareFITBAnswers('%(divid)s');">Compare Me</button>
|
||||
<br />
|
||||
<br />
|
||||
<div id="%(divid)s_feedback">
|
||||
</div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
super(FillInTheBlank,self).run()
|
||||
|
||||
fitbNode = FITBNode(self.options)
|
||||
fitbNode.template_start = TEMPLATE_START
|
||||
fitbNode.template_end = TEMPLATE_END
|
||||
|
||||
self.state.nested_parse(self.content, self.content_offset, fitbNode)
|
||||
|
||||
return [fitbNode]
|
||||
|
||||
|
||||
1
book/modules/luther/sphinx/assess/min/assess-ck.js
Normal file
1
book/modules/luther/sphinx/assess/min/assess-ck.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
var checkMe=function(a,b,c){var d,e=document.forms[a+"_form"].elements.group1;for(var f=e.length-1;f>=0;f--)e[f].checked&&(d=e[f].value);feedBack("#"+a+"_feedback",d==b,c)},feedBack=function(a,b,c){b?$(a).html("You are Correct!"):$(a).html("Inorrect. "+c)};
|
||||
352
book/modules/luther/sphinx/assess/multiplechoice.py
Normal file
352
book/modules/luther/sphinx/assess/multiplechoice.py
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
# Copyright (C) 2013 Bradley N. Miller, Barabara Ericson
|
||||
#
|
||||
# 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
|
||||
from assessbase import *
|
||||
import json
|
||||
import random
|
||||
|
||||
|
||||
|
||||
class MChoiceNode(nodes.General, nodes.Element):
|
||||
def __init__(self,content):
|
||||
"""
|
||||
|
||||
Arguments:
|
||||
- `self`:
|
||||
- `content`:
|
||||
"""
|
||||
super(MChoiceNode,self).__init__()
|
||||
self.mc_options = content
|
||||
|
||||
|
||||
def visit_mc_node(self,node):
|
||||
res = ""
|
||||
res = node.template_start % node.mc_options
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
|
||||
def depart_mc_node(self,node):
|
||||
res = node.template_form_start % node.mc_options
|
||||
feedbackStr = "["
|
||||
currFeedback = ""
|
||||
# Add all of the possible answers
|
||||
okeys = node.mc_options.keys()
|
||||
okeys.sort()
|
||||
for k in okeys:
|
||||
if 'answer_' in k:
|
||||
x,label = k.split('_')
|
||||
node.mc_options['alabel'] = label
|
||||
node.mc_options['atext'] = node.mc_options[k]
|
||||
res += node.template_option % node.mc_options
|
||||
currFeedback = "feedback_" + label
|
||||
feedbackStr = feedbackStr + "'" + escapejs(node.mc_options[currFeedback]) + "', "
|
||||
|
||||
# store the feedback array with key feedback minus last comma
|
||||
node.mc_options['feedback'] = feedbackStr[0:-2] + "]"
|
||||
|
||||
res += node.template_end % node.mc_options
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
|
||||
|
||||
|
||||
#####################
|
||||
# multiple choice question with multiple feedback
|
||||
# author - Barb Ericson
|
||||
# author - Anusha
|
||||
class MChoiceMF(Assessment):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {'answer_a':directives.unchanged,
|
||||
'answer_b':directives.unchanged,
|
||||
'answer_c':directives.unchanged,
|
||||
'answer_d':directives.unchanged,
|
||||
'answer_e':directives.unchanged,
|
||||
'correct':directives.unchanged,
|
||||
'feedback_a':directives.unchanged,
|
||||
'feedback_b':directives.unchanged,
|
||||
'feedback_c':directives.unchanged,
|
||||
'feedback_d':directives.unchanged,
|
||||
'feedback_e':directives.unchanged,
|
||||
'iscode':directives.flag
|
||||
}
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
process the multiplechoice directive and generate html for output.
|
||||
:param self:
|
||||
:return:
|
||||
.. mcmfstorage:: qname
|
||||
:iscode: boolean
|
||||
:answer_a: possible answer -- what follows _ is label
|
||||
:answer_b: possible answer
|
||||
...
|
||||
:answer_e: possible answer
|
||||
:correct: leter of correct answer
|
||||
:feedback_a: displayed if a is picked
|
||||
:feedback_b: displayed if b is picked
|
||||
:feedback_c: displayed if c is picked
|
||||
:feedback_d: displayed if d is picked
|
||||
:feedback_e: displayed if e is picked
|
||||
|
||||
Question text
|
||||
...
|
||||
"""
|
||||
TEMPLATE_START = '''
|
||||
<div id="%(divid)s" class="alert alert-warning">
|
||||
'''
|
||||
|
||||
OPTION = '''
|
||||
<input type="radio" name="group1" value="%(alabel)s" id="%(divid)s_opt_%(alabel)s" />
|
||||
<label for= "%(divid)s_opt_%(alabel)s"> %(alabel)s) %(atext)s</label><br />
|
||||
'''
|
||||
|
||||
TEMPLATE_END = '''
|
||||
|
||||
<script>
|
||||
$(document).ready(function(){checkRadio('%(divid)s');});
|
||||
</script>
|
||||
<button class='btn btn-success' name="do answer" onclick="checkMCMFStorage('%(divid)s','%(correct)s',%(feedback)s)">Check Me</button>
|
||||
<button class='btn btn-default' id="%(divid)s_bcomp" disabled name="compare" onclick="compareAnswers('%(divid)s');">Compare Me</button>
|
||||
</form><br />
|
||||
<div id="%(divid)s_feedback">
|
||||
</div>
|
||||
</div>
|
||||
'''
|
||||
super(MChoiceMF,self).run()
|
||||
|
||||
|
||||
|
||||
|
||||
mcNode = MChoiceNode(self.options)
|
||||
mcNode.template_start = TEMPLATE_START
|
||||
mcNode.template_form_start = '''<form name="%(divid)s_form" method="get" action="" onsubmit="return false;">'''
|
||||
mcNode.template_option = OPTION
|
||||
mcNode.template_end = TEMPLATE_END
|
||||
|
||||
self.state.nested_parse(self.content, self.content_offset, mcNode)
|
||||
|
||||
return [mcNode]
|
||||
|
||||
|
||||
#####################
|
||||
# multiple choice question with multiple correct answers
|
||||
# author - Barb Ericson
|
||||
|
||||
class MChoiceMA(Assessment):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {'answer_a':directives.unchanged,
|
||||
'answer_b':directives.unchanged,
|
||||
'answer_c':directives.unchanged,
|
||||
'answer_d':directives.unchanged,
|
||||
'answer_e':directives.unchanged,
|
||||
'correct':directives.unchanged,
|
||||
'feedback_a':directives.unchanged,
|
||||
'feedback_b':directives.unchanged,
|
||||
'feedback_c':directives.unchanged,
|
||||
'feedback_d':directives.unchanged,
|
||||
'feedback_e':directives.unchanged,
|
||||
'iscode':directives.flag
|
||||
}
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
process the multiplechoice directive and generate html for output.
|
||||
:param self:
|
||||
:return:
|
||||
.. mchoicemf:: qname
|
||||
:iscode: boolean
|
||||
:answer_a: possible answer -- what follows _ is label
|
||||
:answer_b: possible answer
|
||||
...
|
||||
:answer_e: possible answer
|
||||
:correct: comma seperated list of correct values a, b, c
|
||||
:feedback_a: displayed if a is picked
|
||||
:feedback_b: displayed if b is picked
|
||||
:feedback_c: displayed if c is picked
|
||||
:feedback_d: displayed if d is picked
|
||||
:feedback_e: displayed if e is picked
|
||||
|
||||
Question text
|
||||
...
|
||||
"""
|
||||
TEMPLATE_START = '''
|
||||
<div id="%(divid)s" class="alert alert-warning">
|
||||
'''
|
||||
|
||||
OPTION = '''
|
||||
<input type="checkbox" name="group1" value="%(alabel)s" id="%(divid)s_opt_%(alabel)s" />
|
||||
<label for= "%(divid)s_opt_%(alabel)s"> %(alabel)s) %(atext)s</label><br />
|
||||
'''
|
||||
|
||||
TEMPLATE_END = '''
|
||||
<script>
|
||||
$(document).ready(function(){checkMultipleSelect('%(divid)s');});
|
||||
</script>
|
||||
<button class='btn btn-success' name="do answer" onclick="checkMCMAStorage('%(divid)s','%(correct)s',%(feedback)s)">Check Me</button>
|
||||
<button class='btn btn-default' id="%(divid)s_bcomp" disabled name="compare" onclick="compareAnswers('%(divid)s');">Compare Me</button>
|
||||
</form><br />
|
||||
<div id="%(divid)s_feedback">
|
||||
</div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
|
||||
super(MChoiceMA,self).run()
|
||||
|
||||
mcNode = MChoiceNode(self.options)
|
||||
mcNode.template_start = TEMPLATE_START
|
||||
mcNode.template_form_start = '''<form name="%(divid)s_form" method="get" action="" onsubmit="return false;">'''
|
||||
mcNode.template_option = OPTION
|
||||
mcNode.template_end = TEMPLATE_END
|
||||
|
||||
self.state.nested_parse(self.content, self.content_offset, mcNode)
|
||||
|
||||
return [mcNode]
|
||||
|
||||
|
||||
|
||||
################################
|
||||
|
||||
|
||||
#####################
|
||||
# display a multiple choice question with feedback that randomizes the answers
|
||||
class MChoiceRandomMF(Assessment):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {'answer_a':directives.unchanged,
|
||||
'answer_b':directives.unchanged,
|
||||
'answer_c':directives.unchanged,
|
||||
'answer_d':directives.unchanged,
|
||||
'answer_e':directives.unchanged,
|
||||
'correct':directives.unchanged,
|
||||
'feedback_a':directives.unchanged,
|
||||
'feedback_b':directives.unchanged,
|
||||
'feedback_c':directives.unchanged,
|
||||
'feedback_d':directives.unchanged,
|
||||
'feedback_e':directives.unchanged,
|
||||
'iscode':directives.flag
|
||||
}
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
process the multiplechoice directive and generate html for output.
|
||||
:param self:
|
||||
:return:
|
||||
.. mcmfrandom:: qname
|
||||
:iscode: boolean
|
||||
:answer_a: possible answer -- what follows _ is label
|
||||
:answer_b: possible answer
|
||||
...
|
||||
:answer_e: possible answer
|
||||
:correct: leter of correct answer
|
||||
:feedback_a: displayed if a is picked
|
||||
:feedback_b: displayed if b is picked
|
||||
:feedback_c: displayed if c is picked
|
||||
:feedback_d: displayed if d is picked
|
||||
:feedback_e: displayed if e is picked
|
||||
|
||||
Question text
|
||||
...
|
||||
"""
|
||||
TEMPLATE_START = '''
|
||||
<div id="%(divid)s" class="alert alert-warning">
|
||||
<p>%(qnumber)s: %(bodytext)s</p>
|
||||
<form name="%(divid)s_form" method="get" action="" onsubmit="return true;">
|
||||
'''
|
||||
|
||||
OPTION = '''
|
||||
<div id="%(divid)s_op%(opi)s"></div>
|
||||
'''
|
||||
|
||||
TEMPLATE_END = '''
|
||||
<div id="%(divid)s_bt"></div>
|
||||
|
||||
</form>
|
||||
<div id="%(divid)s_feedback">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function(){createHTML_MCMFRandom("%(divid)s","%(a)s","%(f)s","%(corr)s");});
|
||||
</script>
|
||||
</div>
|
||||
'''
|
||||
|
||||
|
||||
super(MChoiceRandomMF,self).run()
|
||||
|
||||
res = ""
|
||||
res = TEMPLATE_START % self.options
|
||||
feedbackStr = "["
|
||||
currFeedback = ""
|
||||
# Add all of the possible answers
|
||||
okeys = self.options.keys()
|
||||
okeys.sort()
|
||||
|
||||
|
||||
answ=""
|
||||
feed=""
|
||||
ansArr=[]
|
||||
feedArray=[]
|
||||
for k in okeys:
|
||||
if 'answer_' in k:
|
||||
ansArr.append(k)
|
||||
for f in ansArr:
|
||||
t,flabel=f.split("_")
|
||||
feedArray.append(flabel)
|
||||
|
||||
i=0
|
||||
for k in okeys:
|
||||
if 'answer_' in k:
|
||||
answ=answ+self.options[ansArr[i]]+"*separator*"
|
||||
feed=feed+self.options["feedback_"+feedArray[i]]+"*separator*"
|
||||
self.options['opi']=i+1
|
||||
res += OPTION % self.options
|
||||
i=i+1
|
||||
|
||||
# Store the Answer and Feedback arrays
|
||||
self.options['a']=answ
|
||||
self.options['f']=feed
|
||||
|
||||
op=self.options['correct']
|
||||
|
||||
if(op=='a'):
|
||||
index=0
|
||||
elif(op=='b'):
|
||||
index=1
|
||||
elif(op=='c'):
|
||||
index=2
|
||||
elif(op=='d'):
|
||||
index=3
|
||||
elif(op=='e'):
|
||||
index=4
|
||||
self.options['corr']=self.options[ansArr[index]]
|
||||
|
||||
res += TEMPLATE_END % self.options
|
||||
return [nodes.raw('',res , format='html')]
|
||||
53
book/modules/luther/sphinx/assess/prototype.html
Normal file
53
book/modules/luther/sphinx/assess/prototype.html
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>trial</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript" ></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="prototype">
|
||||
<p>Evaluate the following boolean expression: True or False</p>
|
||||
<form name="prototype_form" method="get" action="" onsubmit="return false;">
|
||||
<input type="radio" name="group1" value="a" id="prototype_opt_1" />
|
||||
<label for= "prototype_opt_1"> a) True</label><br />
|
||||
<input type="radio" name="group1" value="b" id="prototype_opt_2" />
|
||||
<label for="prototype_opt_2"> b) False</label><br />
|
||||
<input type="radio" name="group1" value="c" id="prototype_opt_3">
|
||||
<label for="prototype_opt_3"> c) Unknown</label><br />
|
||||
|
||||
<input type="button" name="do answer"
|
||||
value="Check Me" onclick="checkMe('prototype','a','try again')"/>
|
||||
</form>
|
||||
<div id="prototype_feedback">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript"> // can go in assessfuncs??
|
||||
var checkMe = function(divid, expected,feedback) {
|
||||
var given;
|
||||
var buttonObjs = document.forms[divid+"_form"].elements['group1']
|
||||
for (var i = buttonObjs.length - 1; i >= 0; i--) {
|
||||
if (buttonObjs[i].checked) {
|
||||
given = buttonObjs[i].value;
|
||||
}
|
||||
};
|
||||
// update number of trials??
|
||||
// log this to the db
|
||||
feedBack('#'+divid+'_feedback',given == expected, feedback)
|
||||
}
|
||||
var feedBack = function(divid,correct,feedbackText) {
|
||||
if (correct) {
|
||||
$(divid).html('You are Correct!');
|
||||
} else {
|
||||
$(divid).html("Inorrect. Here's something to think about: " + feedbackText );
|
||||
}
|
||||
}
|
||||
|
||||
// for each form in the div
|
||||
// get the id of the form
|
||||
// call checkMe on the form... -- need metadata what kind of question what parms etc
|
||||
// hidden fields for meta data??? each form defines a checkme function with no parameters
|
||||
// that calls the actual function that checks the answer properly??
|
||||
// summarize
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
65
book/modules/luther/sphinx/assess/textfield.py
Normal file
65
book/modules/luther/sphinx/assess/textfield.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# 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 random
|
||||
|
||||
# setup is called in assess.py
|
||||
|
||||
# app.add_node(MChoiceNode, html=(visit_mc_node, depart_mc_node))
|
||||
# app.add_node(FITBNode, html=(visit_fitb_node, depart_fitb_node))
|
||||
|
||||
|
||||
|
||||
def textfield_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
'''
|
||||
Usage:
|
||||
|
||||
In your document you can write :textfield:`myid:myvalue:width`
|
||||
|
||||
This will translate to:
|
||||
<input type='text' id='myid' class="form-control input-small" style="display:inline; width:width;" value='myvalue'></input>
|
||||
|
||||
where width can be specified in pixels or percentage of page width (standard CSS syntax).
|
||||
Width can also be specified using relative sizes:
|
||||
mini, small, medium, large, xlarge, and xxlarge
|
||||
|
||||
|
||||
|
||||
'''
|
||||
iid, value, width = text.split(':')
|
||||
|
||||
if 'mini' in width:
|
||||
width = '60px'
|
||||
elif 'small' in width:
|
||||
width = '90px'
|
||||
elif 'medium' in width:
|
||||
width = '150px'
|
||||
elif 'large' in width:
|
||||
width = '210px'
|
||||
elif 'xlarge' in width:
|
||||
width = '270px'
|
||||
elif 'xxlarge' in width:
|
||||
width = '530px'
|
||||
|
||||
res = '''<input type='text' id='%s' class="form-control" style="display:inline; width: %s;" value="%s"></input>''' % (iid,width,value)
|
||||
|
||||
return [nodes.raw('',res, format='html')],[]
|
||||
|
||||
1
book/modules/luther/sphinx/codelens/__init__.py
Normal file
1
book/modules/luther/sphinx/codelens/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .visualizer import *
|
||||
256
book/modules/luther/sphinx/codelens/pg_encoder.py
Normal file
256
book/modules/luther/sphinx/codelens/pg_encoder.py
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
# Online Python Tutor
|
||||
# https://github.com/pgbovine/OnlinePythonTutor/
|
||||
#
|
||||
# Copyright (C) 2010-2012 Philip J. Guo (philip@pgbovine.net)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# Thanks to John DeNero for making the encoder work on both Python 2 and 3
|
||||
|
||||
|
||||
# Given an arbitrary piece of Python data, encode it in such a manner
|
||||
# that it can be later encoded into JSON.
|
||||
# http://json.org/
|
||||
#
|
||||
# We use this function to encode run-time traces of data structures
|
||||
# to send to the front-end.
|
||||
#
|
||||
# Format:
|
||||
# Primitives:
|
||||
# * None, int, long, float, str, bool - unchanged
|
||||
# (json.dumps encodes these fine verbatim)
|
||||
#
|
||||
# Compound objects:
|
||||
# * list - ['LIST', elt1, elt2, elt3, ..., eltN]
|
||||
# * tuple - ['TUPLE', elt1, elt2, elt3, ..., eltN]
|
||||
# * set - ['SET', elt1, elt2, elt3, ..., eltN]
|
||||
# * dict - ['DICT', [key1, value1], [key2, value2], ..., [keyN, valueN]]
|
||||
# * instance - ['INSTANCE', class name, [attr1, value1], [attr2, value2], ..., [attrN, valueN]]
|
||||
# * class - ['CLASS', class name, [list of superclass names], [attr1, value1], [attr2, value2], ..., [attrN, valueN]]
|
||||
# * function - ['FUNCTION', function name, parent frame ID (for nested functions)]
|
||||
# * module - ['module', module name]
|
||||
# * other - [<type name>, string representation of object]
|
||||
# * compound object reference - ['REF', target object's unique_id]
|
||||
#
|
||||
# the unique_id is derived from id(), which allows us to capture aliasing
|
||||
|
||||
|
||||
# number of significant digits for floats
|
||||
FLOAT_PRECISION = 4
|
||||
|
||||
|
||||
import re, types
|
||||
import sys
|
||||
typeRE = re.compile("<type '(.*)'>")
|
||||
classRE = re.compile("<class '(.*)'>")
|
||||
|
||||
import inspect
|
||||
|
||||
is_python3 = (sys.version_info[0] == 3)
|
||||
if is_python3:
|
||||
long = None # Avoid NameError when evaluating "long"
|
||||
|
||||
|
||||
def is_class(dat):
|
||||
"""Return whether dat is a class."""
|
||||
if is_python3:
|
||||
return isinstance(dat, type)
|
||||
else:
|
||||
return type(dat) in (types.ClassType, types.TypeType)
|
||||
|
||||
|
||||
def is_instance(dat):
|
||||
"""Return whether dat is an instance of a class."""
|
||||
if is_python3:
|
||||
return isinstance(type(dat), type) and not isinstance(dat, type)
|
||||
else:
|
||||
# ugh, classRE match is a bit of a hack :(
|
||||
return type(dat) == types.InstanceType or classRE.match(str(type(dat)))
|
||||
|
||||
|
||||
def get_name(obj):
|
||||
"""Return the name of an object."""
|
||||
return obj.__name__ if hasattr(obj, '__name__') else get_name(type(obj))
|
||||
|
||||
|
||||
# Note that this might BLOAT MEMORY CONSUMPTION since we're holding on
|
||||
# to every reference ever created by the program without ever releasing
|
||||
# anything!
|
||||
class ObjectEncoder:
|
||||
def __init__(self):
|
||||
# Key: canonicalized small ID
|
||||
# Value: encoded (compound) heap object
|
||||
self.encoded_heap_objects = {}
|
||||
|
||||
self.id_to_small_IDs = {}
|
||||
self.cur_small_ID = 1
|
||||
|
||||
|
||||
def get_heap(self):
|
||||
return self.encoded_heap_objects
|
||||
|
||||
|
||||
def reset_heap(self):
|
||||
# VERY IMPORTANT to reassign to an empty dict rather than just
|
||||
# clearing the existing dict, since get_heap() could have been
|
||||
# called earlier to return a reference to a previous heap state
|
||||
self.encoded_heap_objects = {}
|
||||
|
||||
def set_function_parent_frame_ID(self, ref_obj, enclosing_frame_id):
|
||||
assert ref_obj[0] == 'REF'
|
||||
func_obj = self.encoded_heap_objects[ref_obj[1]]
|
||||
assert func_obj[0] == 'FUNCTION'
|
||||
func_obj[-1] = enclosing_frame_id
|
||||
|
||||
|
||||
# return either a primitive object or an object reference;
|
||||
# and as a side effect, update encoded_heap_objects
|
||||
def encode(self, dat, get_parent):
|
||||
"""Encode a data value DAT using the GET_PARENT function for parent ids."""
|
||||
# primitive type
|
||||
if type(dat) in (int, long, float, str, bool, type(None)):
|
||||
if type(dat) is float:
|
||||
return round(dat, FLOAT_PRECISION)
|
||||
else:
|
||||
return dat
|
||||
|
||||
# compound type - return an object reference and update encoded_heap_objects
|
||||
else:
|
||||
my_id = id(dat)
|
||||
|
||||
try:
|
||||
my_small_id = self.id_to_small_IDs[my_id]
|
||||
except KeyError:
|
||||
my_small_id = self.cur_small_ID
|
||||
self.id_to_small_IDs[my_id] = self.cur_small_ID
|
||||
self.cur_small_ID += 1
|
||||
|
||||
del my_id # to prevent bugs later in this function
|
||||
|
||||
ret = ['REF', my_small_id]
|
||||
|
||||
# punt early if you've already encoded this object
|
||||
if my_small_id in self.encoded_heap_objects:
|
||||
return ret
|
||||
|
||||
|
||||
# major side-effect!
|
||||
new_obj = []
|
||||
self.encoded_heap_objects[my_small_id] = new_obj
|
||||
|
||||
typ = type(dat)
|
||||
|
||||
if typ == list:
|
||||
new_obj.append('LIST')
|
||||
for e in dat:
|
||||
new_obj.append(self.encode(e, get_parent))
|
||||
elif typ == tuple:
|
||||
new_obj.append('TUPLE')
|
||||
for e in dat:
|
||||
new_obj.append(self.encode(e, get_parent))
|
||||
elif typ == set:
|
||||
new_obj.append('SET')
|
||||
for e in dat:
|
||||
new_obj.append(self.encode(e, get_parent))
|
||||
elif typ == dict:
|
||||
new_obj.append('DICT')
|
||||
for (k, v) in dat.items():
|
||||
# don't display some built-in locals ...
|
||||
if k not in ('__module__', '__return__', '__locals__'):
|
||||
new_obj.append([self.encode(k, get_parent), self.encode(v, get_parent)])
|
||||
elif typ in (types.FunctionType, types.MethodType):
|
||||
if is_python3:
|
||||
argspec = inspect.getfullargspec(dat)
|
||||
else:
|
||||
argspec = inspect.getargspec(dat)
|
||||
|
||||
printed_args = [e for e in argspec.args]
|
||||
if argspec.varargs:
|
||||
printed_args.append('*' + argspec.varargs)
|
||||
|
||||
if is_python3:
|
||||
if argspec.varkw:
|
||||
printed_args.append('**' + argspec.varkw)
|
||||
if argspec.kwonlyargs:
|
||||
printed_args.extend(argspec.kwonlyargs)
|
||||
else:
|
||||
if argspec.keywords:
|
||||
printed_args.append('**' + argspec.keywords)
|
||||
|
||||
func_name = get_name(dat)
|
||||
pretty_name = func_name + '(' + ', '.join(printed_args) + ')'
|
||||
encoded_val = ['FUNCTION', pretty_name, None]
|
||||
if get_parent:
|
||||
enclosing_frame_id = get_parent(dat)
|
||||
encoded_val[2] = enclosing_frame_id
|
||||
new_obj.extend(encoded_val)
|
||||
elif typ is types.BuiltinFunctionType:
|
||||
pretty_name = get_name(dat) + '(...)'
|
||||
new_obj.extend(['FUNCTION', pretty_name, None])
|
||||
elif is_class(dat) or is_instance(dat):
|
||||
self.encode_class_or_instance(dat, new_obj)
|
||||
elif typ is types.ModuleType:
|
||||
new_obj.extend(['module', dat.__name__])
|
||||
else:
|
||||
typeStr = str(typ)
|
||||
m = typeRE.match(typeStr)
|
||||
|
||||
if not m:
|
||||
m = classRE.match(typeStr)
|
||||
|
||||
assert m, typ
|
||||
new_obj.extend([m.group(1), str(dat)])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def encode_class_or_instance(self, dat, new_obj):
|
||||
"""Encode dat as a class or instance."""
|
||||
if is_instance(dat):
|
||||
if hasattr(dat, '__class__'):
|
||||
# common case ...
|
||||
class_name = get_name(dat.__class__)
|
||||
else:
|
||||
# super special case for something like
|
||||
# "from datetime import datetime_CAPI" in Python 3.2,
|
||||
# which is some weird 'PyCapsule' type ...
|
||||
# http://docs.python.org/release/3.1.5/c-api/capsule.html
|
||||
class_name = get_name(type(dat))
|
||||
|
||||
new_obj.extend(['INSTANCE', class_name])
|
||||
# don't traverse inside modules, or else risk EXPLODING the visualization
|
||||
if class_name == 'module':
|
||||
return
|
||||
else:
|
||||
superclass_names = [e.__name__ for e in dat.__bases__ if e is not object]
|
||||
new_obj.extend(['CLASS', get_name(dat), superclass_names])
|
||||
|
||||
# traverse inside of its __dict__ to grab attributes
|
||||
# (filter out useless-seeming ones):
|
||||
hidden = ('__doc__', '__module__', '__return__', '__dict__',
|
||||
'__locals__', '__weakref__')
|
||||
if hasattr(dat, '__dict__'):
|
||||
user_attrs = sorted([e for e in dat.__dict__ if e not in hidden])
|
||||
else:
|
||||
user_attrs = []
|
||||
|
||||
for attr in user_attrs:
|
||||
new_obj.append([self.encode(attr, None), self.encode(dat.__dict__[attr], None)])
|
||||
|
||||
872
book/modules/luther/sphinx/codelens/pg_logger.py
Normal file
872
book/modules/luther/sphinx/codelens/pg_logger.py
Normal file
|
|
@ -0,0 +1,872 @@
|
|||
# Online Python Tutor
|
||||
# https://github.com/pgbovine/OnlinePythonTutor/
|
||||
#
|
||||
# Copyright (C) 2010-2012 Philip J. Guo (philip@pgbovine.net)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
# This is the meat of the Online Python Tutor back-end. It implements a
|
||||
# full logger for Python program execution (based on pdb, the standard
|
||||
# Python debugger imported via the bdb module), printing out the values
|
||||
# of all in-scope data structures after each executed instruction.
|
||||
|
||||
|
||||
|
||||
import sys
|
||||
import bdb # the KEY import here!
|
||||
import re
|
||||
import traceback
|
||||
import types
|
||||
|
||||
is_python3 = (sys.version_info[0] == 3)
|
||||
|
||||
if is_python3:
|
||||
import io as cStringIO
|
||||
else:
|
||||
import cStringIO
|
||||
import pg_encoder
|
||||
|
||||
|
||||
# TODO: not threadsafe:
|
||||
|
||||
# upper-bound on the number of executed lines, in order to guard against
|
||||
# infinite loops
|
||||
MAX_EXECUTED_LINES = 500
|
||||
|
||||
#DEBUG = False
|
||||
DEBUG = False
|
||||
|
||||
|
||||
# simple sandboxing scheme:
|
||||
#
|
||||
# - use resource.setrlimit to deprive this process of ANY file descriptors
|
||||
# (which will cause file read/write and subprocess shell launches to fail)
|
||||
# - restrict user builtins and module imports
|
||||
# (beware that this is NOT foolproof at all ... there are known flaws!)
|
||||
#
|
||||
# ALWAYS use defense-in-depth and don't just rely on these simple mechanisms
|
||||
try:
|
||||
import resource
|
||||
resource_module_loaded = True
|
||||
except ImportError:
|
||||
# Google App Engine doesn't seem to have the 'resource' module
|
||||
resource_module_loaded = False
|
||||
|
||||
|
||||
# ugh, I can't figure out why in Python 2, __builtins__ seems to
|
||||
# be a dict, but in Python 3, __builtins__ seems to be a module,
|
||||
# so just handle both cases ... UGLY!
|
||||
if type(__builtins__) is dict:
|
||||
BUILTIN_IMPORT = __builtins__['__import__']
|
||||
else:
|
||||
assert type(__builtins__) is types.ModuleType
|
||||
BUILTIN_IMPORT = __builtins__.__import__
|
||||
|
||||
|
||||
# whitelist of module imports
|
||||
ALLOWED_MODULE_IMPORTS = ('math', 'random', 'datetime',
|
||||
'functools', 'operator', 'string',
|
||||
'collections', 're', 'json',
|
||||
'heapq', 'bisect')
|
||||
|
||||
# PREEMPTIVELY import all of these modules, so that when the user's
|
||||
# script imports them, it won't try to do a file read (since they've
|
||||
# already been imported and cached in memory). Remember that when
|
||||
# the user's code runs, resource.setrlimit(resource.RLIMIT_NOFILE, (0, 0))
|
||||
# will already be in effect, so no more files can be opened.
|
||||
for m in ALLOWED_MODULE_IMPORTS:
|
||||
__import__(m)
|
||||
|
||||
|
||||
# Restrict imports to a whitelist
|
||||
def __restricted_import__(*args):
|
||||
if args[0] in ALLOWED_MODULE_IMPORTS:
|
||||
return BUILTIN_IMPORT(*args)
|
||||
else:
|
||||
raise ImportError('{0} not supported'.format(args[0]))
|
||||
|
||||
|
||||
# blacklist of builtins
|
||||
BANNED_BUILTINS = ('reload', 'input', 'apply', 'open', 'compile',
|
||||
'file', 'eval', 'exec', 'execfile',
|
||||
'exit', 'quit', 'raw_input', 'help',
|
||||
'dir', 'globals', 'locals', 'vars')
|
||||
|
||||
|
||||
IGNORE_VARS = set(('__user_stdout__', '__builtins__', '__name__', '__exception__', '__doc__', '__package__'))
|
||||
|
||||
def get_user_stdout(frame):
|
||||
return frame.f_globals['__user_stdout__'].getvalue()
|
||||
|
||||
def get_user_globals(frame):
|
||||
d = filter_var_dict(frame.f_globals)
|
||||
# also filter out __return__ for globals only, but NOT for locals
|
||||
if '__return__' in d:
|
||||
del d['__return__']
|
||||
return d
|
||||
|
||||
def get_user_locals(frame):
|
||||
return filter_var_dict(frame.f_locals)
|
||||
|
||||
def filter_var_dict(d):
|
||||
ret = {}
|
||||
for (k,v) in d.items():
|
||||
if k not in IGNORE_VARS:
|
||||
ret[k] = v
|
||||
return ret
|
||||
|
||||
|
||||
# yield all function objects locally-reachable from frame,
|
||||
# making sure to traverse inside all compound objects ...
|
||||
def visit_all_locally_reachable_function_objs(frame):
|
||||
for (k, v) in get_user_locals(frame).items():
|
||||
for e in visit_function_obj(v, set()):
|
||||
if e: # only non-null if it's a function object
|
||||
assert type(e) in (types.FunctionType, types.MethodType)
|
||||
yield e
|
||||
|
||||
|
||||
# TODO: this might be slow if we're traversing inside lots of objects:
|
||||
def visit_function_obj(v, ids_seen_set):
|
||||
v_id = id(v)
|
||||
|
||||
# to prevent infinite loop
|
||||
if v_id in ids_seen_set:
|
||||
yield None
|
||||
else:
|
||||
ids_seen_set.add(v_id)
|
||||
|
||||
typ = type(v)
|
||||
|
||||
# simple base case
|
||||
if typ in (types.FunctionType, types.MethodType):
|
||||
yield v
|
||||
|
||||
# recursive cases
|
||||
elif typ in (list, tuple, set):
|
||||
for child in v:
|
||||
for child_res in visit_function_obj(child, ids_seen_set):
|
||||
yield child_res
|
||||
|
||||
elif typ == dict or pg_encoder.is_class(v) or pg_encoder.is_instance(v):
|
||||
contents_dict = None
|
||||
|
||||
if typ == dict:
|
||||
contents_dict = v
|
||||
# warning: some classes or instances don't have __dict__ attributes
|
||||
elif hasattr(v, '__dict__'):
|
||||
contents_dict = v.__dict__
|
||||
|
||||
if contents_dict:
|
||||
for (key_child, val_child) in contents_dict.items():
|
||||
for key_child_res in visit_function_obj(key_child, ids_seen_set):
|
||||
yield key_child_res
|
||||
for val_child_res in visit_function_obj(val_child, ids_seen_set):
|
||||
yield val_child_res
|
||||
|
||||
# degenerate base case
|
||||
yield None
|
||||
|
||||
|
||||
class PGLogger(bdb.Bdb):
|
||||
|
||||
def __init__(self, cumulative_mode, finalizer_func, disable_security_checks=False):
|
||||
bdb.Bdb.__init__(self)
|
||||
self.mainpyfile = ''
|
||||
self._wait_for_mainpyfile = 0
|
||||
|
||||
self.disable_security_checks = disable_security_checks
|
||||
|
||||
# a function that takes the output trace as a parameter and
|
||||
# processes it
|
||||
self.finalizer_func = finalizer_func
|
||||
|
||||
# if True, then displays ALL stack frames that have ever existed
|
||||
# rather than only those currently on the stack (and their
|
||||
# lexical parents)
|
||||
self.cumulative_mode = cumulative_mode
|
||||
|
||||
# each entry contains a dict with the information for a single
|
||||
# executed line
|
||||
self.trace = []
|
||||
|
||||
#http://stackoverflow.com/questions/2112396/in-python-in-google-app-engine-how-do-you-capture-output-produced-by-the-print
|
||||
self.GAE_STDOUT = sys.stdout
|
||||
|
||||
# Key: function object
|
||||
# Value: parent frame
|
||||
self.closures = {}
|
||||
|
||||
# set of function objects that were defined in the global scope
|
||||
self.globally_defined_funcs = set()
|
||||
|
||||
# Key: frame object
|
||||
# Value: monotonically increasing small ID, based on call order
|
||||
self.frame_ordered_ids = {}
|
||||
self.cur_frame_id = 1
|
||||
|
||||
# List of frames to KEEP AROUND after the function exits.
|
||||
# If cumulative_mode is True, then keep ALL frames in
|
||||
# zombie_frames; otherwise keep only frames where
|
||||
# nested functions were defined within them.
|
||||
self.zombie_frames = []
|
||||
|
||||
# set of elements within zombie_frames that are also
|
||||
# LEXICAL PARENTS of other frames
|
||||
self.parent_frames_set = set()
|
||||
|
||||
# all globals that ever appeared in the program, in the order in
|
||||
# which they appeared. note that this might be a superset of all
|
||||
# the globals that exist at any particular execution point,
|
||||
# since globals might have been deleted (using, say, 'del')
|
||||
self.all_globals_in_order = []
|
||||
|
||||
# very important for this single object to persist throughout
|
||||
# execution, or else canonical small IDs won't be consistent.
|
||||
self.encoder = pg_encoder.ObjectEncoder()
|
||||
|
||||
self.executed_script = None # Python script to be executed!
|
||||
|
||||
|
||||
def get_frame_id(self, cur_frame):
|
||||
return self.frame_ordered_ids[cur_frame]
|
||||
|
||||
# Returns the (lexical) parent of a function value.
|
||||
def get_parent_of_function(self, val):
|
||||
if val not in self.closures:
|
||||
return None
|
||||
return self.get_frame_id(self.closures[val])
|
||||
|
||||
|
||||
# Returns the (lexical) parent frame of the function that was called
|
||||
# to create the stack frame 'frame'.
|
||||
#
|
||||
# OKAY, this is a SUPER hack, but I don't see a way around it
|
||||
# since it's impossible to tell exactly which function
|
||||
# ('closure') object was called to create 'frame'.
|
||||
#
|
||||
# The Python interpreter doesn't maintain this information,
|
||||
# so unless we hack the interpreter, we will simply have
|
||||
# to make an educated guess based on the contents of local
|
||||
# variables inherited from possible parent frame candidates.
|
||||
def get_parent_frame(self, frame):
|
||||
for (func_obj, parent_frame) in self.closures.items():
|
||||
# ok, there's a possible match, but let's compare the
|
||||
# local variables in parent_frame to those of frame
|
||||
# to make sure. this is a hack that happens to work because in
|
||||
# Python, each stack frame inherits ('inlines') a copy of the
|
||||
# variables from its (lexical) parent frame.
|
||||
if func_obj.__code__ == frame.f_code:
|
||||
all_matched = True
|
||||
for k in frame.f_locals:
|
||||
# Do not try to match local names
|
||||
if k in frame.f_code.co_varnames:
|
||||
continue
|
||||
if k != '__return__' and k in parent_frame.f_locals:
|
||||
if parent_frame.f_locals[k] != frame.f_locals[k]:
|
||||
all_matched = False
|
||||
break
|
||||
|
||||
if all_matched:
|
||||
return parent_frame
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def lookup_zombie_frame_by_id(self, frame_id):
|
||||
# TODO: kinda inefficient
|
||||
for e in self.zombie_frames:
|
||||
if self.get_frame_id(e) == frame_id:
|
||||
return e
|
||||
assert False # should never get here
|
||||
|
||||
|
||||
# unused ...
|
||||
#def reset(self):
|
||||
# bdb.Bdb.reset(self)
|
||||
# self.forget()
|
||||
|
||||
|
||||
def forget(self):
|
||||
self.lineno = None
|
||||
self.stack = []
|
||||
self.curindex = 0
|
||||
self.curframe = None
|
||||
|
||||
def setup(self, f, t):
|
||||
self.forget()
|
||||
self.stack, self.curindex = self.get_stack(f, t)
|
||||
self.curframe = self.stack[self.curindex][0]
|
||||
|
||||
|
||||
# Override Bdb methods
|
||||
|
||||
def user_call(self, frame, argument_list):
|
||||
"""This method is called when there is the remote possibility
|
||||
that we ever need to stop in this function."""
|
||||
if self._wait_for_mainpyfile:
|
||||
return
|
||||
if self.stop_here(frame):
|
||||
# delete __return__ so that on subsequent calls to
|
||||
# a generator function, the OLD yielded (returned)
|
||||
# value gets deleted from the frame ...
|
||||
try:
|
||||
del frame.f_locals['__return__']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
self.interaction(frame, None, 'call')
|
||||
|
||||
def user_line(self, frame):
|
||||
"""This function is called when we stop or break at this line."""
|
||||
if self._wait_for_mainpyfile:
|
||||
if (self.canonic(frame.f_code.co_filename) != "<string>" or
|
||||
frame.f_lineno <= 0):
|
||||
return
|
||||
self._wait_for_mainpyfile = 0
|
||||
self.interaction(frame, None, 'step_line')
|
||||
|
||||
def user_return(self, frame, return_value):
|
||||
"""This function is called when a return trap is set here."""
|
||||
frame.f_locals['__return__'] = return_value
|
||||
self.interaction(frame, None, 'return')
|
||||
|
||||
def user_exception(self, frame, exc_info):
|
||||
exc_type, exc_value, exc_traceback = exc_info
|
||||
"""This function is called if an exception occurs,
|
||||
but only if we are to stop at or just below this level."""
|
||||
frame.f_locals['__exception__'] = exc_type, exc_value
|
||||
if type(exc_type) == type(''):
|
||||
exc_type_name = exc_type
|
||||
else: exc_type_name = exc_type.__name__
|
||||
self.interaction(frame, exc_traceback, 'exception')
|
||||
|
||||
|
||||
# General interaction function
|
||||
|
||||
def interaction(self, frame, traceback, event_type):
|
||||
self.setup(frame, traceback)
|
||||
tos = self.stack[self.curindex]
|
||||
top_frame = tos[0]
|
||||
lineno = tos[1]
|
||||
|
||||
# don't trace inside of ANY functions that aren't user-written code
|
||||
# (e.g., those from imported modules -- e.g., random, re -- or the
|
||||
# __restricted_import__ function in this file)
|
||||
#
|
||||
# empirically, it seems like the FIRST entry in self.stack is
|
||||
# the 'run' function from bdb.py, but everything else on the
|
||||
# stack is the user program's "real stack"
|
||||
for (cur_frame, cur_line) in self.stack[1:]:
|
||||
# it seems like user-written code has a filename of '<string>',
|
||||
# but maybe there are false positives too?
|
||||
if self.canonic(cur_frame.f_code.co_filename) != '<string>':
|
||||
return
|
||||
# also don't trace inside of the magic "constructor" code
|
||||
if cur_frame.f_code.co_name == '__new__':
|
||||
return
|
||||
# or __repr__, which is often called when running print statements
|
||||
if cur_frame.f_code.co_name == '__repr__':
|
||||
return
|
||||
|
||||
|
||||
# debug ...
|
||||
#print('===', file=sys.stderr)
|
||||
#for (e,ln) in self.stack:
|
||||
# print(e.f_code.co_name + ' ' + e.f_code.co_filename + ' ' + str(ln), file=sys.stderr)
|
||||
#print('', file=sys.stderr)
|
||||
|
||||
|
||||
# don't trace inside of our __restricted_import__ helper function
|
||||
# (this check is now subsumed by the above check)
|
||||
#if top_frame.f_code.co_name == '__restricted_import__':
|
||||
# return
|
||||
|
||||
self.encoder.reset_heap() # VERY VERY VERY IMPORTANT,
|
||||
# or else we won't properly capture heap object mutations in the trace!
|
||||
|
||||
if event_type == 'call':
|
||||
# Don't be so strict about this assertion because it FAILS
|
||||
# when you're calling a generator (not for the first time),
|
||||
# since that frame has already previously been on the stack ...
|
||||
#assert top_frame not in self.frame_ordered_ids
|
||||
|
||||
self.frame_ordered_ids[top_frame] = self.cur_frame_id
|
||||
self.cur_frame_id += 1
|
||||
|
||||
if self.cumulative_mode:
|
||||
self.zombie_frames.append(top_frame)
|
||||
|
||||
|
||||
# only render zombie frames that are NO LONGER on the stack
|
||||
cur_stack_frames = [e[0] for e in self.stack]
|
||||
zombie_frames_to_render = [e for e in self.zombie_frames if e not in cur_stack_frames]
|
||||
|
||||
|
||||
# each element is a pair of (function name, ENCODED locals dict)
|
||||
encoded_stack_locals = []
|
||||
|
||||
|
||||
# returns a dict with keys: function name, frame id, id of parent frame, encoded_locals dict
|
||||
def create_encoded_stack_entry(cur_frame):
|
||||
ret = {}
|
||||
|
||||
|
||||
parent_frame_id_list = []
|
||||
|
||||
f = cur_frame
|
||||
while True:
|
||||
p = self.get_parent_frame(f)
|
||||
if p:
|
||||
pid = self.get_frame_id(p)
|
||||
assert pid
|
||||
parent_frame_id_list.append(pid)
|
||||
f = p
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
cur_name = cur_frame.f_code.co_name
|
||||
|
||||
if cur_name == '':
|
||||
cur_name = 'unnamed function'
|
||||
|
||||
# encode in a JSON-friendly format now, in order to prevent ill
|
||||
# effects of aliasing later down the line ...
|
||||
encoded_locals = {}
|
||||
|
||||
for (k, v) in get_user_locals(cur_frame).items():
|
||||
is_in_parent_frame = False
|
||||
|
||||
# don't display locals that appear in your parents' stack frames,
|
||||
# since that's redundant
|
||||
for pid in parent_frame_id_list:
|
||||
parent_frame = self.lookup_zombie_frame_by_id(pid)
|
||||
if k in parent_frame.f_locals:
|
||||
# ignore __return__, which is never copied
|
||||
if k != '__return__':
|
||||
# these values SHOULD BE ALIASES
|
||||
# (don't do an 'is' check since it might not fire for primitives)
|
||||
if parent_frame.f_locals[k] == v:
|
||||
is_in_parent_frame = True
|
||||
|
||||
if is_in_parent_frame and k not in cur_frame.f_code.co_varnames:
|
||||
continue
|
||||
|
||||
# don't display some built-in locals ...
|
||||
if k == '__module__':
|
||||
continue
|
||||
|
||||
encoded_val = self.encoder.encode(v, self.get_parent_of_function)
|
||||
encoded_locals[k] = encoded_val
|
||||
|
||||
|
||||
# order the variable names in a sensible way:
|
||||
|
||||
# Let's start with co_varnames, since it (often) contains all
|
||||
# variables in this frame, some of which might not exist yet.
|
||||
ordered_varnames = []
|
||||
for e in cur_frame.f_code.co_varnames:
|
||||
if e in encoded_locals:
|
||||
ordered_varnames.append(e)
|
||||
|
||||
# sometimes co_varnames doesn't contain all of the true local
|
||||
# variables: e.g., when executing a 'class' definition. in that
|
||||
# case, iterate over encoded_locals and push them onto the end
|
||||
# of ordered_varnames in alphabetical order
|
||||
for e in sorted(encoded_locals.keys()):
|
||||
if e != '__return__' and e not in ordered_varnames:
|
||||
ordered_varnames.append(e)
|
||||
|
||||
# finally, put __return__ at the very end
|
||||
if '__return__' in encoded_locals:
|
||||
ordered_varnames.append('__return__')
|
||||
|
||||
# doctor Python 3 initializer to look like a normal function (denero)
|
||||
if '__locals__' in encoded_locals:
|
||||
ordered_varnames.remove('__locals__')
|
||||
local = encoded_locals.pop('__locals__')
|
||||
if encoded_locals.get('__return__', True) is None:
|
||||
encoded_locals['__return__'] = local
|
||||
|
||||
# crucial sanity checks!
|
||||
assert len(ordered_varnames) == len(encoded_locals)
|
||||
for e in ordered_varnames:
|
||||
assert e in encoded_locals
|
||||
|
||||
return dict(func_name=cur_name,
|
||||
is_parent=(cur_frame in self.parent_frames_set),
|
||||
frame_id=self.get_frame_id(cur_frame),
|
||||
parent_frame_id_list=parent_frame_id_list,
|
||||
encoded_locals=encoded_locals,
|
||||
ordered_varnames=ordered_varnames)
|
||||
|
||||
|
||||
i = self.curindex
|
||||
|
||||
# look for whether a nested function has been defined during
|
||||
# this particular call:
|
||||
if i > 1: # i == 1 implies that there's only a global scope visible
|
||||
for v in visit_all_locally_reachable_function_objs(top_frame):
|
||||
if (v not in self.closures and \
|
||||
v not in self.globally_defined_funcs):
|
||||
|
||||
# Look for the presence of the code object (v.func_code
|
||||
# for Python 2 or v.__code__ for Python 3) in the
|
||||
# constant pool (f_code.co_consts) of an enclosing
|
||||
# stack frame, and set that frame as your parent.
|
||||
#
|
||||
# This technique properly handles lambdas passed as
|
||||
# function parameters. e.g., this example:
|
||||
#
|
||||
# def foo(x):
|
||||
# bar(lambda y: x + y)
|
||||
# def bar(a):
|
||||
# print a(20)
|
||||
# foo(10)
|
||||
chosen_parent_frame = None
|
||||
for (my_frame, my_lineno) in self.stack:
|
||||
if chosen_parent_frame:
|
||||
break
|
||||
|
||||
for frame_const in my_frame.f_code.co_consts:
|
||||
if frame_const is (v.__code__ if is_python3 else v.func_code):
|
||||
chosen_parent_frame = my_frame
|
||||
break
|
||||
|
||||
assert chosen_parent_frame # I hope this always passes :0
|
||||
|
||||
# this condition should be False for functions declared in global scope ...
|
||||
if chosen_parent_frame in self.frame_ordered_ids:
|
||||
self.closures[v] = chosen_parent_frame
|
||||
self.parent_frames_set.add(chosen_parent_frame) # unequivocally add to this set!!!
|
||||
if not chosen_parent_frame in self.zombie_frames:
|
||||
self.zombie_frames.append(chosen_parent_frame)
|
||||
else:
|
||||
# if there is only a global scope visible ...
|
||||
for (k, v) in get_user_globals(top_frame).items():
|
||||
if (type(v) in (types.FunctionType, types.MethodType) and \
|
||||
v not in self.closures):
|
||||
self.globally_defined_funcs.add(v)
|
||||
|
||||
|
||||
# climb up until you find '<module>', which is (hopefully) the global scope
|
||||
while True:
|
||||
cur_frame = self.stack[i][0]
|
||||
cur_name = cur_frame.f_code.co_name
|
||||
if cur_name == '<module>':
|
||||
break
|
||||
|
||||
encoded_stack_locals.append(create_encoded_stack_entry(cur_frame))
|
||||
i -= 1
|
||||
|
||||
zombie_encoded_stack_locals = [create_encoded_stack_entry(e) for e in zombie_frames_to_render]
|
||||
|
||||
|
||||
# encode in a JSON-friendly format now, in order to prevent ill
|
||||
# effects of aliasing later down the line ...
|
||||
encoded_globals = {}
|
||||
for (k, v) in get_user_globals(tos[0]).items():
|
||||
encoded_val = self.encoder.encode(v, self.get_parent_of_function)
|
||||
encoded_globals[k] = encoded_val
|
||||
|
||||
if k not in self.all_globals_in_order:
|
||||
self.all_globals_in_order.append(k)
|
||||
|
||||
# filter out globals that don't exist at this execution point
|
||||
# (because they've been, say, deleted with 'del')
|
||||
ordered_globals = [e for e in self.all_globals_in_order if e in encoded_globals]
|
||||
assert len(ordered_globals) == len(encoded_globals)
|
||||
|
||||
|
||||
# merge zombie_encoded_stack_locals and encoded_stack_locals
|
||||
# into one master ordered list using some simple rules for
|
||||
# making it look aesthetically pretty
|
||||
stack_to_render = [];
|
||||
|
||||
# first push all regular stack entries
|
||||
if encoded_stack_locals:
|
||||
for e in encoded_stack_locals:
|
||||
e['is_zombie'] = False
|
||||
e['is_highlighted'] = False
|
||||
stack_to_render.append(e)
|
||||
|
||||
# highlight the top-most active stack entry
|
||||
stack_to_render[0]['is_highlighted'] = True
|
||||
|
||||
|
||||
# now push all zombie stack entries
|
||||
for e in zombie_encoded_stack_locals:
|
||||
# don't display return value for zombie frames
|
||||
# TODO: reconsider ...
|
||||
'''
|
||||
try:
|
||||
e['ordered_varnames'].remove('__return__')
|
||||
except ValueError:
|
||||
pass
|
||||
'''
|
||||
|
||||
e['is_zombie'] = True
|
||||
e['is_highlighted'] = False # never highlight zombie entries
|
||||
|
||||
stack_to_render.append(e)
|
||||
|
||||
# now sort by frame_id since that sorts frames in "chronological
|
||||
# order" based on the order they were invoked
|
||||
stack_to_render.sort(key=lambda e: e['frame_id'])
|
||||
|
||||
|
||||
|
||||
# create a unique hash for this stack entry, so that the
|
||||
# frontend can uniquely identify it when doing incremental
|
||||
# rendering. the strategy is to use a frankenstein-like mix of the
|
||||
# relevant fields to properly disambiguate closures and recursive
|
||||
# calls to the same function
|
||||
for e in stack_to_render:
|
||||
hash_str = e['func_name']
|
||||
# frame_id is UNIQUE, so it can disambiguate recursive calls
|
||||
hash_str += '_f' + str(e['frame_id'])
|
||||
|
||||
# needed to refresh GUI display ...
|
||||
if e['is_parent']:
|
||||
hash_str += '_p'
|
||||
|
||||
# TODO: this is no longer needed, right? (since frame_id is unique)
|
||||
#if e['parent_frame_id_list']:
|
||||
# hash_str += '_p' + '_'.join([str(i) for i in e['parent_frame_id_list']])
|
||||
if e['is_zombie']:
|
||||
hash_str += '_z'
|
||||
|
||||
e['unique_hash'] = hash_str
|
||||
|
||||
|
||||
trace_entry = dict(line=lineno,
|
||||
event=event_type,
|
||||
func_name=tos[0].f_code.co_name,
|
||||
globals=encoded_globals,
|
||||
ordered_globals=ordered_globals,
|
||||
stack_to_render=stack_to_render,
|
||||
heap=self.encoder.get_heap(),
|
||||
stdout=get_user_stdout(tos[0]))
|
||||
|
||||
# if there's an exception, then record its info:
|
||||
if event_type == 'exception':
|
||||
# always check in f_locals
|
||||
exc = frame.f_locals['__exception__']
|
||||
trace_entry['exception_msg'] = exc[0].__name__ + ': ' + str(exc[1])
|
||||
|
||||
self.trace.append(trace_entry)
|
||||
|
||||
|
||||
# sanity check to make sure the state of the world at a 'call' instruction
|
||||
# is identical to that at the instruction immediately following it ...
|
||||
'''
|
||||
if len(self.trace) > 1:
|
||||
cur = self.trace[-1]
|
||||
prev = self.trace[-2]
|
||||
if prev['event'] == 'call':
|
||||
assert cur['globals'] == prev['globals']
|
||||
for (s1, s2) in zip(cur['stack_to_render'], prev['stack_to_render']):
|
||||
assert s1 == s2
|
||||
assert cur['heap'] == prev['heap']
|
||||
assert cur['stdout'] == prev['stdout']
|
||||
'''
|
||||
|
||||
|
||||
if len(self.trace) >= MAX_EXECUTED_LINES:
|
||||
self.trace.append(dict(event='instruction_limit_reached', exception_msg='(stopped after ' + str(MAX_EXECUTED_LINES) + ' steps to prevent possible infinite loop)'))
|
||||
self.force_terminate()
|
||||
|
||||
self.forget()
|
||||
|
||||
|
||||
def _runscript(self, script_str):
|
||||
self.executed_script = script_str
|
||||
|
||||
# When bdb sets tracing, a number of call and line events happens
|
||||
# BEFORE debugger even reaches user's code (and the exact sequence of
|
||||
# events depends on python version). So we take special measures to
|
||||
# avoid stopping before we reach the main script (see user_line and
|
||||
# user_call for details).
|
||||
self._wait_for_mainpyfile = 1
|
||||
|
||||
|
||||
# ok, let's try to sorta 'sandbox' the user script by not
|
||||
# allowing certain potentially dangerous operations.
|
||||
user_builtins = {}
|
||||
|
||||
# ugh, I can't figure out why in Python 2, __builtins__ seems to
|
||||
# be a dict, but in Python 3, __builtins__ seems to be a module,
|
||||
# so just handle both cases ... UGLY!
|
||||
if type(__builtins__) is dict:
|
||||
builtin_items = __builtins__.items()
|
||||
else:
|
||||
assert type(__builtins__) is types.ModuleType
|
||||
builtin_items = []
|
||||
for k in dir(__builtins__):
|
||||
builtin_items.append((k, getattr(__builtins__, k)))
|
||||
|
||||
for (k, v) in builtin_items:
|
||||
if k in BANNED_BUILTINS:
|
||||
continue
|
||||
elif k == '__import__':
|
||||
user_builtins[k] = __restricted_import__
|
||||
else:
|
||||
user_builtins[k] = v
|
||||
|
||||
|
||||
user_stdout = cStringIO.StringIO()
|
||||
|
||||
sys.stdout = user_stdout
|
||||
user_globals = {"__name__" : "__main__",
|
||||
"__builtins__" : user_builtins,
|
||||
"__user_stdout__" : user_stdout}
|
||||
|
||||
try:
|
||||
# enforce resource limits RIGHT BEFORE running script_str
|
||||
|
||||
# set ~200MB virtual memory limit AND a 5-second CPU time
|
||||
# limit (tuned for Webfaction shared hosting) to protect against
|
||||
# memory bombs such as:
|
||||
# x = 2
|
||||
# while True: x = x*x
|
||||
if resource_module_loaded and (not self.disable_security_checks):
|
||||
resource.setrlimit(resource.RLIMIT_AS, (200000000, 200000000))
|
||||
resource.setrlimit(resource.RLIMIT_CPU, (5, 5))
|
||||
|
||||
# protect against unauthorized filesystem accesses ...
|
||||
resource.setrlimit(resource.RLIMIT_NOFILE, (0, 0)) # no opened files allowed
|
||||
|
||||
# VERY WEIRD. If you activate this resource limitation, it
|
||||
# ends up generating an EMPTY trace for the following program:
|
||||
# "x = 0\nfor i in range(10):\n x += 1\n print x\n x += 1\n"
|
||||
# (at least on my Webfaction hosting with Python 2.7)
|
||||
#resource.setrlimit(resource.RLIMIT_FSIZE, (0, 0)) # (redundancy for paranoia)
|
||||
|
||||
# sys.modules contains an in-memory cache of already-loaded
|
||||
# modules, so if you delete modules from here, they will
|
||||
# need to be re-loaded from the filesystem.
|
||||
#
|
||||
# Thus, as an extra precaution, remove these modules so that
|
||||
# they can't be re-imported without opening a new file,
|
||||
# which is disallowed by resource.RLIMIT_NOFILE
|
||||
#
|
||||
# Of course, this isn't a foolproof solution by any means,
|
||||
# and it might lead to UNEXPECTED FAILURES later in execution.
|
||||
del sys.modules['os']
|
||||
del sys.modules['sys']
|
||||
|
||||
self.run(script_str, user_globals, user_globals)
|
||||
# sys.exit ...
|
||||
except SystemExit:
|
||||
#sys.exit(0)
|
||||
raise bdb.BdbQuit
|
||||
except:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
|
||||
trace_entry = dict(event='uncaught_exception')
|
||||
|
||||
(exc_type, exc_val, exc_tb) = sys.exc_info()
|
||||
if hasattr(exc_val, 'lineno'):
|
||||
trace_entry['line'] = exc_val.lineno
|
||||
if hasattr(exc_val, 'offset'):
|
||||
trace_entry['offset'] = exc_val.offset
|
||||
|
||||
trace_entry['exception_msg'] = type(exc_val).__name__ + ": " + str(exc_val)
|
||||
|
||||
# SUPER SUBTLE! if this exact same exception has already been
|
||||
# recorded by the program, then DON'T record it again as an
|
||||
# uncaught_exception
|
||||
already_caught = False
|
||||
for e in self.trace:
|
||||
if e['event'] == 'exception' and e['exception_msg'] == trace_entry['exception_msg']:
|
||||
already_caught = True
|
||||
break
|
||||
|
||||
if not already_caught:
|
||||
self.trace.append(trace_entry)
|
||||
|
||||
raise bdb.BdbQuit # need to forceably STOP execution
|
||||
|
||||
|
||||
def force_terminate(self):
|
||||
#self.finalize()
|
||||
raise bdb.BdbQuit # need to forceably STOP execution
|
||||
|
||||
|
||||
def finalize(self):
|
||||
sys.stdout = self.GAE_STDOUT # very important!
|
||||
|
||||
assert len(self.trace) <= (MAX_EXECUTED_LINES + 1)
|
||||
|
||||
# don't do this anymore ...
|
||||
'''
|
||||
# filter all entries after 'return' from '<module>', since they
|
||||
# seem extraneous:
|
||||
res = []
|
||||
for e in self.trace:
|
||||
res.append(e)
|
||||
if e['event'] == 'return' and e['func_name'] == '<module>':
|
||||
break
|
||||
'''
|
||||
|
||||
res = self.trace
|
||||
|
||||
# if the SECOND to last entry is an 'exception'
|
||||
# and the last entry is return from <module>, then axe the last
|
||||
# entry, for aesthetic reasons :)
|
||||
if len(res) >= 2 and \
|
||||
res[-2]['event'] == 'exception' and \
|
||||
res[-1]['event'] == 'return' and res[-1]['func_name'] == '<module>':
|
||||
res.pop()
|
||||
|
||||
self.trace = res
|
||||
|
||||
return self.finalizer_func(self.executed_script, self.trace)
|
||||
|
||||
|
||||
|
||||
# the MAIN meaty function!!!
|
||||
def exec_script_str(script_str, cumulative_mode, finalizer_func):
|
||||
logger = PGLogger(cumulative_mode, finalizer_func)
|
||||
|
||||
try:
|
||||
logger._runscript(script_str)
|
||||
except bdb.BdbQuit:
|
||||
pass
|
||||
finally:
|
||||
logger.finalize()
|
||||
|
||||
|
||||
# disables security check and returns the result of finalizer_func
|
||||
# WARNING: ONLY RUN THIS LOCALLY and never over the web, since
|
||||
# security checks are disabled
|
||||
def exec_script_str_local(script_str, cumulative_mode, finalizer_func):
|
||||
logger = PGLogger(cumulative_mode, finalizer_func, disable_security_checks=True)
|
||||
|
||||
try:
|
||||
logger._runscript(script_str)
|
||||
except bdb.BdbQuit:
|
||||
pass
|
||||
finally:
|
||||
return logger.finalize()
|
||||
|
||||
210
book/modules/luther/sphinx/codelens/visualizer.py
Normal file
210
book/modules/luther/sphinx/codelens/visualizer.py
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
# 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
|
||||
from pg_logger import exec_script_str_local
|
||||
import json
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('codelens',Codelens)
|
||||
app.add_stylesheet('css/pytutor.css')
|
||||
app.add_stylesheet('css/basic.css')
|
||||
|
||||
app.add_javascript('js/d3.v2.min.js')
|
||||
app.add_javascript('jquery-migrate-1.2.1.min.js') # needed so that ba-bbq can use the latest jQuery that we've included
|
||||
app.add_javascript('js/jquery.ba-bbq.min.js')
|
||||
app.add_javascript('js/jquery.jsPlumb-1.3.10-all-min.js')
|
||||
app.add_javascript('js/pytutor.js')
|
||||
|
||||
|
||||
|
||||
VIS = '''
|
||||
<div id="%(divid)s"></div>
|
||||
<p class="cl_caption"><span class="cl_caption_text">%(caption)s (%(divid)s)</span> </p>'''
|
||||
|
||||
QUESTION = '''
|
||||
<div id="%(divid)s_modal" class="modal fade codelens-modal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Check your understanding</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>%(question)s</p>
|
||||
<input id="%(divid)s_textbox" type="textbox" class="form-control" style="width:200px;" />
|
||||
<br />
|
||||
<button id="%(divid)s_tracecheck" class='btn btn-default tracecheck' onclick="traceQCheckMe('%(divid)s_textbox','%(divid)s','%(correct)s')">
|
||||
Check Me
|
||||
</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Continue</button>
|
||||
<br />
|
||||
<p id="%(divid)s_feedbacktext" class="feedbacktext alert alert-warning"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
'''
|
||||
|
||||
DATA = '''
|
||||
<script type="text/javascript">
|
||||
%(tracedata)s
|
||||
var %(divid)s_vis;
|
||||
|
||||
$(document).ready(function() {
|
||||
%(divid)s_vis = new ExecutionVisualizer('%(divid)s',%(divid)s_trace,
|
||||
{embeddedMode: %(embedded)s,
|
||||
verticalStack: true,
|
||||
heightChangeCallback: redrawAllVisualizerArrows,
|
||||
codeDivWidth: 500
|
||||
});
|
||||
attachLoggers(%(divid)s_vis,'%(divid)s');
|
||||
allVisualizers.push(%(divid)s_vis);
|
||||
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#%(divid)s_tracecheck").click(function() {
|
||||
logBookEvent({'event':'codelens', 'act': 'check', 'div_id':'%(divid)s'});
|
||||
});
|
||||
});
|
||||
|
||||
if (allVisualizers === undefined) {
|
||||
var allVisualizers = [];
|
||||
}
|
||||
|
||||
|
||||
$(window).resize(function() {
|
||||
%(divid)s_vis.redrawConnectors();
|
||||
});
|
||||
</script>
|
||||
'''
|
||||
|
||||
|
||||
# Some documentation to help the author.
|
||||
# Here's and example of a single stack frame.
|
||||
# you might ask a qestion about the value of a global variable
|
||||
# in which case the correct answer is expressed as:
|
||||
#
|
||||
# globals.a
|
||||
#
|
||||
# You could ask about a value on the heap
|
||||
#
|
||||
# heap.variable
|
||||
#
|
||||
# You could ask about a local variable -- not shown here.
|
||||
#
|
||||
# locals.variable
|
||||
#
|
||||
# You could even ask about what line is going to be executed next
|
||||
#
|
||||
# line
|
||||
# {
|
||||
# "ordered_globals": [
|
||||
# "a",
|
||||
# "b"
|
||||
# ],
|
||||
# "stdout": "1\n",
|
||||
# "func_name": "<module>",
|
||||
# "stack_to_render": [],
|
||||
# "globals": {
|
||||
# "a": 1,
|
||||
# "b": 1
|
||||
# },
|
||||
# "heap": {},
|
||||
# "line": 5,
|
||||
# "event": "return"
|
||||
# }
|
||||
|
||||
|
||||
class Codelens(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
option_spec = {
|
||||
'tracedata':directives.unchanged,
|
||||
'caption':directives.unchanged,
|
||||
'showoutput':directives.flag,
|
||||
'question':directives.unchanged,
|
||||
'correct':directives.unchanged,
|
||||
'feedback':directives.unchanged,
|
||||
'breakline':directives.nonnegative_int
|
||||
}
|
||||
|
||||
has_content = True
|
||||
|
||||
def run(self):
|
||||
|
||||
self.JS_VARNAME = ""
|
||||
self.JS_VARVAL = ""
|
||||
|
||||
def raw_dict(input_code, output_trace):
|
||||
ret = dict(code=input_code, trace=output_trace)
|
||||
return ret
|
||||
|
||||
def js_var_finalizer(input_code, output_trace):
|
||||
global JS_VARNAME
|
||||
ret = dict(code=input_code, trace=output_trace)
|
||||
json_output = json.dumps(ret, indent=None)
|
||||
return "var %s = %s;" % (self.JS_VARNAME, json_output)
|
||||
|
||||
|
||||
self.options['divid'] = self.arguments[0]
|
||||
if self.content:
|
||||
source = "\n".join(self.content)
|
||||
else:
|
||||
source = '\n'
|
||||
|
||||
CUMULATIVE_MODE=False
|
||||
self.JS_VARNAME = self.options['divid']+'_trace'
|
||||
if 'showoutput' not in self.options:
|
||||
self.options['embedded'] = 'true' # to set embeddedmode to true
|
||||
else:
|
||||
self.options['embedded'] = 'false'
|
||||
|
||||
|
||||
|
||||
if 'question' in self.options:
|
||||
curTrace = exec_script_str_local(source, CUMULATIVE_MODE, raw_dict)
|
||||
self.inject_questions(curTrace)
|
||||
json_output = json.dumps(curTrace, indent=None)
|
||||
self.options['tracedata'] = "var %s = %s;" % (self.JS_VARNAME, json_output)
|
||||
else:
|
||||
self.options['tracedata'] = exec_script_str_local(source, CUMULATIVE_MODE, js_var_finalizer)
|
||||
|
||||
res = VIS
|
||||
if 'caption' not in self.options:
|
||||
self.options['caption'] = ''
|
||||
if 'question' in self.options:
|
||||
res += QUESTION
|
||||
if 'tracedata' in self.options:
|
||||
res += DATA
|
||||
return [nodes.raw('',res % self.options,format='html')]
|
||||
|
||||
def inject_questions(self,curTrace):
|
||||
if 'breakline' not in self.options:
|
||||
raise RuntimeError('Must have breakline option')
|
||||
breakline = self.options['breakline']
|
||||
for frame in curTrace['trace']:
|
||||
if frame['line'] == breakline:
|
||||
frame['question'] = dict(text=self.options['question'],
|
||||
correct = self.options['correct'],
|
||||
div = self.options['divid']+'_modal',
|
||||
feedback = self.options['feedback'] )
|
||||
127
book/modules/luther/sphinx/datafile/__init__.py
Normal file
127
book/modules/luther/sphinx/datafile/__init__.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# 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
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('datafile',DataFile)
|
||||
app.add_javascript('bookfuncs.js')
|
||||
app.add_javascript('skulpt/dist/skulpt.js')
|
||||
app.add_javascript('skulpt/dist/builtin.js')
|
||||
|
||||
app.add_node(DataFileNode, html=(visit_df_node, depart_df_node))
|
||||
|
||||
app.connect('doctree-resolved',process_datafile_nodes)
|
||||
app.connect('env-purge-doc', purge_datafiles)
|
||||
|
||||
|
||||
PRE = '''
|
||||
<pre id="%(divid)s" style="display: %(hide)s;">
|
||||
%(filecontent)s
|
||||
</pre>
|
||||
'''
|
||||
|
||||
TEXTA = '''
|
||||
<textarea id="%(divid)s" rows="%(rows)d" cols="%(cols)d">
|
||||
%(filecontent)s
|
||||
</textarea>
|
||||
'''
|
||||
|
||||
class DataFileNode(nodes.General, nodes.Element):
|
||||
def __init__(self,content):
|
||||
"""
|
||||
Arguments:
|
||||
- `self`:
|
||||
- `content`:
|
||||
"""
|
||||
super(DataFileNode,self).__init__()
|
||||
self.df_content = 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_df_node(self,node):
|
||||
if node.df_content['edit'] == True:
|
||||
res = TEXTA
|
||||
else:
|
||||
res = PRE
|
||||
res = res % node.df_content
|
||||
|
||||
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_df_node(self,node):
|
||||
''' This is called at the start of processing an datafile node. If datafile 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_datafile_nodes(app,env,docname):
|
||||
pass
|
||||
|
||||
|
||||
def purge_datafiles(app,env,docname):
|
||||
pass
|
||||
|
||||
|
||||
class DataFile(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 2
|
||||
has_content = True
|
||||
option_spec = {
|
||||
'hide':directives.flag,
|
||||
'edit':directives.flag,
|
||||
'rows':directives.positive_int,
|
||||
'cols':directives.positive_int
|
||||
}
|
||||
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
|
||||
if not hasattr(env,'datafilecounter'):
|
||||
env.datafilecounter = 0
|
||||
env.datafilecounter += 1
|
||||
|
||||
if 'cols' not in self.options:
|
||||
self.options['cols'] = min(65,max([len(x) for x in self.content]))
|
||||
if 'rows'not in self.options:
|
||||
self.options['rows'] = 20
|
||||
|
||||
self.options['divid'] = self.arguments[0]
|
||||
if self.content:
|
||||
source = "\n".join(self.content)
|
||||
else:
|
||||
source = '\n'
|
||||
self.options['filecontent'] = source
|
||||
|
||||
if 'hide' not in self.options:
|
||||
self.options['hide'] = 'block'
|
||||
else:
|
||||
self.options['hide'] = 'none'
|
||||
|
||||
if 'edit' not in self.options:
|
||||
self.options['edit'] = False
|
||||
|
||||
return [DataFileNode(self.options)]
|
||||
|
||||
2
book/modules/luther/sphinx/disqus/__init__.py
Normal file
2
book/modules/luther/sphinx/disqus/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from .disqus import *
|
||||
115
book/modules/luther/sphinx/disqus/disqus.py
Normal file
115
book/modules/luther/sphinx/disqus/disqus.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# 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__ = 'isaacdontjelindell'
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst import Directive
|
||||
|
||||
|
||||
DISQUS_BOX = """\
|
||||
<script type="text/javascript">
|
||||
function %(identifier)s_disqus(source) {
|
||||
if (window.DISQUS) {
|
||||
|
||||
$('#disqus_thread').insertAfter(source); //put the DIV for the Disqus box after the link
|
||||
|
||||
//if Disqus exists, call it's reset method with new parameters
|
||||
DISQUS.reset({
|
||||
reload: true,
|
||||
config: function () {
|
||||
this.page.identifier = '%(identifier)s_' + eBookConfig.course;
|
||||
this.page.url = 'http://%(identifier)s_'+eBookConfig.course+'.interactivepython.com/#!';
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
//insert a wrapper in HTML after the relevant "show comments" link
|
||||
$('<div id="disqus_thread"></div>').insertAfter(source);
|
||||
|
||||
// set Disqus required vars
|
||||
disqus_shortname = '%(shortname)s';
|
||||
disqus_identifier = '%(identifier)s_' + eBookConfig.course;
|
||||
disqus_url = 'http://%(identifier)s_'+eBookConfig.course+'.interactivepython.com/#!';
|
||||
|
||||
//append the Disqus embed script to HTML
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
|
||||
$('head').append(dsq);
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
"""
|
||||
|
||||
DISQUS_LINK = """
|
||||
<a href="#disqus_thread" class='disqus_thread_link' data-disqus-identifier="%(identifier)s" onclick="%(identifier)s_disqus(this);">Show Comments</a>
|
||||
<script type='text/javascript'>
|
||||
$("a[data-disqus-identifier='%(identifier)s']").attr('data-disqus-identifier', '%(identifier)s_' + eBookConfig.course);
|
||||
</script>
|
||||
"""
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('disqus', DisqusDirective)
|
||||
|
||||
app.add_node(DisqusNode, html=(visit_disqus_node, depart_disqus_node))
|
||||
app.connect('doctree-resolved' ,process_disqus_nodes)
|
||||
app.connect('env-purge-doc', purge_disqus_nodes)
|
||||
|
||||
class DisqusNode(nodes.General, nodes.Element):
|
||||
def __init__(self,content):
|
||||
super(DisqusNode,self).__init__()
|
||||
self.disqus_components = content
|
||||
|
||||
|
||||
def visit_disqus_node(self, node):
|
||||
res = DISQUS_BOX
|
||||
res += DISQUS_LINK
|
||||
|
||||
res = res % node.disqus_components
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
def depart_disqus_node(self,node):
|
||||
pass
|
||||
|
||||
def process_disqus_nodes(app, env, docname):
|
||||
pass
|
||||
|
||||
def purge_disqus_nodes(app, env, docname):
|
||||
pass
|
||||
|
||||
|
||||
class DisqusDirective(Directive):
|
||||
required_arguments = 0
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
has_content = False
|
||||
option_spec = {'shortname':directives.unchanged_required,
|
||||
'identifier':directives.unchanged_required
|
||||
}
|
||||
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
generate html to include Disqus box.
|
||||
:param self:
|
||||
:return:
|
||||
"""
|
||||
|
||||
return [DisqusNode(self.options)]
|
||||
|
||||
2
book/modules/luther/sphinx/meta/__init__.py
Normal file
2
book/modules/luther/sphinx/meta/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from .meta import *
|
||||
62
book/modules/luther/sphinx/meta/meta.py
Normal file
62
book/modules/luther/sphinx/meta/meta.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# 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
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('shortname',Meta)
|
||||
app.add_directive('description',Meta)
|
||||
|
||||
|
||||
class Meta(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 50
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
process the video directive and generate html for output.
|
||||
:param self:
|
||||
:return:
|
||||
"""
|
||||
return [nodes.raw('','', format='html')]
|
||||
|
||||
|
||||
|
||||
source = """\
|
||||
This is some text.
|
||||
|
||||
.. shortname:: divid
|
||||
.. description:: foo bar baz
|
||||
|
||||
This is some more text.
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
from docutils.core import publish_parts
|
||||
|
||||
directives.register_directive('shortname',Meta)
|
||||
directives.register_directive('description',Meta)
|
||||
|
||||
doc_parts = publish_parts(source,
|
||||
settings_overrides={'output_encoding': 'utf8',
|
||||
'initial_header_level': 2},
|
||||
writer_name="html")
|
||||
|
||||
print doc_parts['html_body']
|
||||
1
book/modules/luther/sphinx/poll/__init__.py
Normal file
1
book/modules/luther/sphinx/poll/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .poll import *
|
||||
149
book/modules/luther/sphinx/poll/poll.py
Normal file
149
book/modules/luther/sphinx/poll/poll.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# 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__ = 'isaacdontjelindell'
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst import Directive
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('poll', PollDirective)
|
||||
|
||||
app.add_node(PollNode, html=(visit_poll_node, depart_poll_node))
|
||||
|
||||
app.add_javascript('poll.js')
|
||||
app.add_stylesheet('poll.css')
|
||||
|
||||
|
||||
BEGIN = """ <div id='%(divid)s' class='poll alert alert-warning'> """
|
||||
|
||||
BEGIN_FORM = """
|
||||
<form id='%(divid)s_poll' name='%(divid)s_poll' action="">
|
||||
<fieldset>
|
||||
<legend>Poll</legend>
|
||||
<div class='poll-question'>%(content)s</div>
|
||||
<div id='%(divid)s_poll_input'>
|
||||
<div class='poll-options'>
|
||||
"""
|
||||
|
||||
POLL_ELEMENT = """
|
||||
<label class='radio-inline'>
|
||||
<input type='radio' name='%(divid)s_opt' id='%(divid)s_%(value)s' value='%(value)s'>
|
||||
%(value)s
|
||||
</label>
|
||||
"""
|
||||
|
||||
END_POLL_OPTIONS = """ </div> """
|
||||
|
||||
COMMENT = """
|
||||
<br />
|
||||
<input type='text' class='form-control' style='width:300px;' name='%(divid)s_comment' placeholder='Any comments?'>
|
||||
<br />
|
||||
"""
|
||||
|
||||
END_POLL_INPUT = """
|
||||
<button type='button' id='%(divid)s_submit' class='btn btn-success' onclick="submitPoll('%(divid)s');">Submit</button>
|
||||
</div>
|
||||
"""
|
||||
|
||||
END_FORM = """
|
||||
</fieldset>
|
||||
</form>
|
||||
"""
|
||||
|
||||
RESULTS_DIV = """ <div id='%(divid)s_results'></div> """
|
||||
|
||||
|
||||
|
||||
END = """
|
||||
<script type='text/javascript'>
|
||||
// check if the user has already answered this poll
|
||||
$(function() {
|
||||
var len = localStorage.length;
|
||||
if (len > 0) {
|
||||
for (var i = 0; i < len; i++) {
|
||||
var key = localStorage.key(i);
|
||||
if (key === '%(divid)s') {
|
||||
var ex = localStorage.getItem(key);
|
||||
if(ex === "true") {
|
||||
// hide the poll inputs
|
||||
$("#%(divid)s_poll_input").hide();
|
||||
|
||||
// show the results of the poll
|
||||
var data = {};
|
||||
data.div_id = '%(divid)s';
|
||||
data.course = eBookConfig.course;
|
||||
jQuery.get(eBookConfig.ajaxURL+'getpollresults', data, showPollResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
"""
|
||||
|
||||
class PollNode(nodes.General, nodes.Element):
|
||||
def __init__(self, options):
|
||||
super(PollNode, self).__init__()
|
||||
self.pollnode_components = options
|
||||
|
||||
def visit_poll_node(self, node):
|
||||
res = BEGIN
|
||||
res += BEGIN_FORM
|
||||
|
||||
for i in range(1, node.pollnode_components['scale']+1):
|
||||
res += POLL_ELEMENT % {'divid':node.pollnode_components['divid'], 'value':i}
|
||||
|
||||
res += END_POLL_OPTIONS
|
||||
|
||||
if 'allowcomment' in node.pollnode_components:
|
||||
res += COMMENT
|
||||
|
||||
res += END_POLL_INPUT
|
||||
res += END_FORM
|
||||
res += RESULTS_DIV
|
||||
res += END
|
||||
|
||||
res = res % node.pollnode_components
|
||||
self.body.append(res)
|
||||
|
||||
def depart_poll_node(self,node):
|
||||
pass
|
||||
|
||||
|
||||
class PollDirective(Directive):
|
||||
required_arguments = 1 # the div id
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {'scale':directives.positive_int,
|
||||
'allowcomment': directives.flag}
|
||||
|
||||
node_class = PollNode
|
||||
|
||||
def run(self):
|
||||
# Raise an error if the directive does not have contents.
|
||||
self.assert_has_content()
|
||||
|
||||
self.options['divid'] = self.arguments[0]
|
||||
self.options['content'] = "<p>".join(self.content)
|
||||
poll_node = PollNode(self.options)
|
||||
|
||||
return [poll_node]
|
||||
|
||||
|
||||
1
book/modules/luther/sphinx/reveal/__init__.py
Normal file
1
book/modules/luther/sphinx/reveal/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .reveal import *
|
||||
64
book/modules/luther/sphinx/reveal/reveal.py
Normal file
64
book/modules/luther/sphinx/reveal/reveal.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
__author__ = 'isaacdontjelindell'
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst import Directive
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('reveal', RevealDirective)
|
||||
|
||||
app.add_node(RevealNode, html=(visit_reveal_node, depart_reveal_node))
|
||||
|
||||
|
||||
BEGIN = """
|
||||
<button type='button' id='%(divid)s_show' class='btn btn-default' style='margin-bottom:10px;' onclick="$(this).hide();$('#%(divid)s').show();$('#%(divid)s_hide').show();$('#%(divid)s').find('.CodeMirror').each(function(i, el){el.CodeMirror.refresh();});">
|
||||
%(showtitle)s
|
||||
</button>
|
||||
<button type='button' id='%(divid)s_hide' class='btn btn-default' onclick="$(this).hide();$('#%(divid)s').hide();$('#%(divid)s_show').show();" style='display:none'>%(hidetitle)s</button>
|
||||
<div id='%(divid)s' style='display:none'>
|
||||
"""
|
||||
|
||||
END = """
|
||||
</div>
|
||||
"""
|
||||
|
||||
class RevealNode(nodes.General, nodes.Element):
|
||||
def __init__(self,content):
|
||||
super(RevealNode,self).__init__()
|
||||
self.reveal_components = content
|
||||
|
||||
|
||||
def visit_reveal_node(self, node):
|
||||
res = BEGIN % node.reveal_components
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
def depart_reveal_node(self,node):
|
||||
res = END % node.reveal_components
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
class RevealDirective(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {"showtitle":directives.unchanged,
|
||||
"hidetitle":directives.unchanged}
|
||||
|
||||
def run(self):
|
||||
self.assert_has_content() # an empty reveal block isn't very useful...
|
||||
|
||||
if not 'showtitle' in self.options:
|
||||
self.options['showtitle'] = "Show"
|
||||
if not 'hidetitle' in self.options:
|
||||
self.options['hidetitle'] = "Hide"
|
||||
|
||||
self.options['divid'] = self.arguments[0]
|
||||
|
||||
reveal_node = RevealNode(self.options)
|
||||
|
||||
self.state.nested_parse(self.content, self.content_offset, reveal_node)
|
||||
|
||||
return [reveal_node]
|
||||
|
||||
1
book/modules/luther/sphinx/tabbedStuff/__init__.py
Normal file
1
book/modules/luther/sphinx/tabbedStuff/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .tabbedStuff import *
|
||||
178
book/modules/luther/sphinx/tabbedStuff/tabbedStuff.py
Normal file
178
book/modules/luther/sphinx/tabbedStuff/tabbedStuff.py
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
# 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__ = 'isaacdontjelindell'
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst import Directive
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('tabbed', TabbedStuffDirective)
|
||||
app.add_directive('tab', TabDirective)
|
||||
|
||||
app.add_node(TabNode, html=(visit_tab_node, depart_tab_node))
|
||||
app.add_node(TabbedStuffNode, html=(visit_tabbedstuff_node, depart_tabbedstuff_node))
|
||||
|
||||
app.add_stylesheet('tabbedstuff.css')
|
||||
|
||||
|
||||
BEGIN = """<div id='%(divid)s' class='alert alert-warning'>"""
|
||||
|
||||
TABLIST_BEGIN = """<ul class='nav nav-tabs' id='%(divid)s_tab'>"""
|
||||
|
||||
TABLIST_ELEMENT = """
|
||||
<li>
|
||||
<a data-toggle='tab' href='#%(divid)s-%(tabname)s'><span>%(tabfriendlyname)s</span></a>
|
||||
</li>
|
||||
"""
|
||||
|
||||
TABLIST_END = """</ul>"""
|
||||
|
||||
TABCONTENT_BEGIN = """<div class='tab-content'>"""
|
||||
TABCONTENT_END = """</div>"""
|
||||
|
||||
TABDIV_BEGIN = """<div class='tab-pane' id='%(divid)s-%(tabname)s'>"""
|
||||
|
||||
TABDIV_END = """</div>"""
|
||||
|
||||
END = """
|
||||
</div>
|
||||
<script type='text/javascript'>
|
||||
$('#%(divid)s .nav-tabs a').click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).tab('show');
|
||||
})
|
||||
|
||||
// activate the first tab
|
||||
var el = $('#%(divid)s .nav-tabs a')[0];
|
||||
$(el).tab('show');
|
||||
|
||||
$('#%(divid)s .nav-tabs a').on('shown.bs.tab', function (e) {
|
||||
var content_div = $(e.target.attributes.href.value);
|
||||
content_div.find('.disqus_thread_link').each(function() {
|
||||
$(this).click();
|
||||
});
|
||||
|
||||
content_div.find('.CodeMirror').each(function(i, el) {
|
||||
el.CodeMirror.refresh();
|
||||
});
|
||||
})
|
||||
</script>
|
||||
"""
|
||||
|
||||
class TabNode(nodes.General, nodes.Element):
|
||||
def __init__(self, content):
|
||||
super(TabNode, self).__init__()
|
||||
self.tabnode_components = content
|
||||
self.tabname = content['tabname']
|
||||
|
||||
def visit_tab_node(self, node):
|
||||
divid = node.parent.divid
|
||||
tabname = node.tabname
|
||||
|
||||
# remove spaces from tabname to allow it to be used as the div id.
|
||||
res = TABDIV_BEGIN % {'divid':divid,
|
||||
'tabname':tabname.replace(" ", "")}
|
||||
self.body.append(res)
|
||||
|
||||
def depart_tab_node(self,node):
|
||||
self.body.append(TABDIV_END)
|
||||
|
||||
class TabbedStuffNode(nodes.General, nodes.Element):
|
||||
'''A TabbedStuffNode contains one or more TabNodes'''
|
||||
def __init__(self,content):
|
||||
super(TabbedStuffNode,self).__init__()
|
||||
self.tabbed_stuff_components = content
|
||||
self.divid = content['divid']
|
||||
|
||||
def visit_tabbedstuff_node(self, node):
|
||||
divid = node.divid
|
||||
|
||||
# this is all the child tab nodes
|
||||
tabs = node.traverse(include_self=False, descend=True, condition=TabNode)
|
||||
|
||||
res = BEGIN % {'divid':divid}
|
||||
res += TABLIST_BEGIN
|
||||
|
||||
# make the tab list (<ul>).
|
||||
# tabfriendlyname can contain spaces and will be displayed as the name of the tab.
|
||||
# tabname is the same as tabfriendlyname but with spaces removed, so it can be
|
||||
# used as the div id.
|
||||
for tab in tabs:
|
||||
res += TABLIST_ELEMENT % {'divid':divid,
|
||||
'tabfriendlyname':tab.tabname,
|
||||
'tabname':tab.tabname.replace(" ", "")}
|
||||
|
||||
res += TABLIST_END # </ul>
|
||||
res += TABCONTENT_BEGIN
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
|
||||
def depart_tabbedstuff_node(self,node):
|
||||
divid = node.divid
|
||||
|
||||
# close the tab plugin div and init the Bootstrap tabs
|
||||
res = TABCONTENT_END
|
||||
res += END
|
||||
|
||||
res = res % {'divid':divid}
|
||||
|
||||
self.body.append(res)
|
||||
|
||||
|
||||
|
||||
class TabDirective(Directive):
|
||||
required_arguments = 1 # the name of the tab
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {}
|
||||
|
||||
node_class = TabNode
|
||||
|
||||
def run(self):
|
||||
# Raise an error if the directive does not have contents.
|
||||
self.assert_has_content()
|
||||
|
||||
# Create the node, to be populated by "nested_parse".
|
||||
self.options['tabname'] = self.arguments[0]
|
||||
tab_node = TabNode(self.options)
|
||||
|
||||
# Parse the child nodes (content of the tab)
|
||||
self.state.nested_parse(self.content, self.content_offset, tab_node)
|
||||
return [tab_node]
|
||||
|
||||
class TabbedStuffDirective(Directive):
|
||||
required_arguments = 1 # the div to put the tabbed exhibit in
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
|
||||
def run(self):
|
||||
# Raise an error if the directive does not have contents.
|
||||
self.assert_has_content()
|
||||
|
||||
self.options['divid'] = self.arguments[0]
|
||||
|
||||
# Create the node, to be populated by "nested_parse".
|
||||
tabbedstuff_node = TabbedStuffNode(self.options)
|
||||
|
||||
# Parse the directive contents (should be 1 or more tab directives)
|
||||
self.state.nested_parse(self.content, self.content_offset, tabbedstuff_node)
|
||||
return [tabbedstuff_node]
|
||||
|
||||
2
book/modules/luther/sphinx/video/__init__.py
Normal file
2
book/modules/luther/sphinx/video/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from .video import *
|
||||
134
book/modules/luther/sphinx/video/video.py
Normal file
134
book/modules/luther/sphinx/video/video.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
# 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
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('video',Video)
|
||||
app.add_stylesheet('video.css')
|
||||
|
||||
CODE = """\
|
||||
<a id="%(divid)s_thumb" style='position:relative;'>
|
||||
<img src="%(thumb)s" />
|
||||
<div class='video-play-overlay'></div>
|
||||
</a>
|
||||
<div id="%(divid)s" class="video_popup" >
|
||||
<video %(controls)s %(preload)s %(loop)s >
|
||||
%(sources)s
|
||||
No supported video types
|
||||
</video>
|
||||
</div>
|
||||
"""
|
||||
|
||||
POPUP = """\
|
||||
<script>
|
||||
jQuery(function ($) {
|
||||
$('#%(divid)s_thumb').click(function (e) {
|
||||
$('#%(divid)s').modal();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
"""
|
||||
|
||||
INLINE = """\
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
$('#%(divid)s_thumb').click(function(e) {
|
||||
$('#%(divid)s').show();
|
||||
$('#%(divid)s_thumb').hide();
|
||||
logBookEvent({'event':'video','act':'play','div_id': '%(divid)s'});
|
||||
// Log the run event
|
||||
});
|
||||
});
|
||||
</script>
|
||||
"""
|
||||
SOURCE = """<source src="%s" type="video/%s"></source>"""
|
||||
|
||||
|
||||
class Video(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
option_spec = {'controls':directives.flag,
|
||||
'loop': directives.flag,
|
||||
'thumb': directives.uri,
|
||||
'preload': directives.flag
|
||||
}
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
process the video directive and generate html for output.
|
||||
:param self:
|
||||
:return:
|
||||
"""
|
||||
mimeMap = {'mov':'mp4','webm':'webm', 'm4v':'m4v'}
|
||||
|
||||
sources = [SOURCE % (directives.uri(line),mimeMap[line[line.rindex(".")+1:]]) for line in self.content]
|
||||
self.options['divid'] = self.arguments[0]
|
||||
if 'controls' in self.options:
|
||||
self.options['controls'] = 'controls'
|
||||
if 'loop' in self.options:
|
||||
self.options['loop'] = 'loop'
|
||||
else:
|
||||
self.options['loop'] = ''
|
||||
|
||||
if 'preload' in self.options:
|
||||
self.options['preload'] = 'preload="auto"'
|
||||
else:
|
||||
self.options['preload'] = 'preload="none"'
|
||||
|
||||
self.options['sources'] = "\n ".join(sources)
|
||||
res = CODE % self.options
|
||||
if 'popup' in self.options:
|
||||
res += POPUP % self.options
|
||||
else:
|
||||
res += INLINE % self.options
|
||||
return [nodes.raw('',res , format='html')]
|
||||
|
||||
|
||||
|
||||
source = """\
|
||||
This is some text.
|
||||
|
||||
.. video:: divid
|
||||
:controls:
|
||||
:thumb: _static/turtlestill.png
|
||||
:loop:
|
||||
|
||||
http://knuth.luther.edu/~bmiller/foo.mov
|
||||
http://knuth.luther.edu/~bmiller/foo.webm
|
||||
|
||||
This is some more text.
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
from docutils.core import publish_parts
|
||||
|
||||
directives.register_directive('video',Video)
|
||||
|
||||
doc_parts = publish_parts(source,
|
||||
settings_overrides={'output_encoding': 'utf8',
|
||||
'initial_header_level': 2},
|
||||
writer_name="html")
|
||||
|
||||
print doc_parts['html_body']
|
||||
Loading…
Add table
Add a link
Reference in a new issue