Drop remaining coffee use

This basically commits the transpiled CoffeeScript JS (with minor
cleanup) and removes coffee build support.

A tiny amount of support for xblocks exists, because external users
may have xblocks with coffee. But no coffee in our tree anyway.
This commit is contained in:
Michael Terry
2018-03-23 15:30:20 -04:00
committed by Michael Terry
parent 2abbd1eb83
commit a34c8c8233
107 changed files with 3346 additions and 2502 deletions

View File

@@ -20,13 +20,6 @@ test_root/staticfiles
common/static/xmodule common/static/xmodule
# Coffeescript directories (don't lint autogenerated files)
cms/static/coffee
lms/static/coffee
common/static/coffee
common/lib/capa/capa/tests/test_files/js
# Symlinks into common/lib/xmodule/xmodule/js # Symlinks into common/lib/xmodule/xmodule/js
cms/static/xmodule_js cms/static/xmodule_js
lms/static/xmodule_js lms/static/xmodule_js
@@ -36,27 +29,40 @@ lms/static/xmodule_js
cms/djangoapps/pipeline_js/templates cms/djangoapps/pipeline_js/templates
# This directory is about half Coffee and half JS, things get messy here so just ignore all existing coffee paths # These are es2015 spec files that used to be in an ignored path.
# Now they live with the rest of the code, but we want to ignore them
# until the surrounding code is es2015 and we have a chance to clean them.
# We need to ignore them here, because es2015 will cause a parse error
# even if we add an eslint-disable line to the file.
cms/static/js/spec/models/course_spec.js
cms/static/js/spec/models/metadata_spec.js
cms/static/js/spec/models/section_spec.js
cms/static/js/spec/models/settings_course_grader_spec.js
cms/static/js/spec/models/settings_grading_spec.js
cms/static/js/spec/models/textbook_spec.js
cms/static/js/spec/models/upload_spec.js
cms/static/js/spec/views/assets_squire_spec.js
cms/static/js/spec/views/course_info_spec.js
cms/static/js/spec/views/metadata_edit_spec.js
cms/static/js/spec/views/textbook_spec.js
cms/static/js/spec/views/upload_spec.js
common/lib/capa/capa/tests/test_files/js/test_problem_display.js
common/lib/capa/capa/tests/test_files/js/test_problem_generator.js
common/lib/capa/capa/tests/test_files/js/test_problem_grader.js
common/lib/capa/capa/tests/test_files/js/xproblem.js
common/lib/xmodule/xmodule/js/spec/annotatable/display_spec.js common/lib/xmodule/xmodule/js/spec/annotatable/display_spec.js
common/lib/xmodule/xmodule/js/spec/capa/display_spec.js common/lib/xmodule/xmodule/js/spec/capa/display_spec.js
common/lib/xmodule/xmodule/js/spec/html/edit_spec.js common/lib/xmodule/xmodule/js/spec/html/edit_spec.js
common/lib/xmodule/xmodule/js/spec/problem/edit_spec.js
common/lib/xmodule/xmodule/js/spec/problem/edit_spec_hint.js common/lib/xmodule/xmodule/js/spec/problem/edit_spec_hint.js
common/lib/xmodule/xmodule/js/spec/problem/edit_spec.js
common/lib/xmodule/xmodule/js/spec/tabs/edit.js common/lib/xmodule/xmodule/js/spec/tabs/edit.js
lms/static/js/spec/calculator_spec.js
lms/static/js/spec/courseware_spec.js
lms/static/js/spec/feedback_form_spec.js
lms/static/js/spec/helper.js
lms/static/js/spec/histogram_spec.js
lms/static/js/spec/modules/tab_spec.js
lms/static/js/spec/requirejs_spec.js
common/lib/xmodule/xmodule/js/src/annotatable/display.js
common/lib/xmodule/xmodule/js/src/conditional/display.js
common/lib/xmodule/xmodule/js/src/discussion/display.js
common/lib/xmodule/xmodule/js/src/html/display.js
common/lib/xmodule/xmodule/js/src/html/edit.js
common/lib/xmodule/xmodule/js/src/raw/edit/json.js
common/lib/xmodule/xmodule/js/src/raw/edit/metadata-only.js
common/lib/xmodule/xmodule/js/src/raw/edit/xml.js
common/lib/xmodule/xmodule/js/src/sequence/edit.js
common/lib/xmodule/xmodule/js/src/tabs/tabs-aggregator.js
common/lib/xmodule/xmodule/js/src/vertical/edit.js
# This file is responsible for almost half of the repo's total issues.
common/lib/xmodule/xmodule/js/src/capa/schematic.js
!**/.eslintrc.js !**/.eslintrc.js

View File

@@ -810,10 +810,7 @@ PIPELINE_JS = {
}, },
} }
PIPELINE_COMPILERS = ( PIPELINE_COMPILERS = ()
'pipeline.compilers.coffee.CoffeeScriptCompiler',
)
PIPELINE_CSS_COMPRESSOR = None PIPELINE_CSS_COMPRESSOR = None
PIPELINE_JS_COMPRESSOR = None PIPELINE_JS_COMPRESSOR = None
@@ -829,10 +826,6 @@ STATICFILES_IGNORE_PATTERNS = (
"sass/*/*.scss", "sass/*/*.scss",
"sass/*/*/*.scss", "sass/*/*/*.scss",
"sass/*/*/*/*.scss", "sass/*/*/*/*.scss",
"coffee/*.coffee",
"coffee/*/*.coffee",
"coffee/*/*/*.coffee",
"coffee/*/*/*/*.coffee",
# Ignore tests # Ignore tests
"spec", "spec",

View File

@@ -104,7 +104,7 @@
'jquery.fileupload-validate': 'js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate', 'jquery.fileupload-validate': 'js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate',
'jquery.iframe-transport': 'js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', 'jquery.iframe-transport': 'js/vendor/jQuery-File-Upload/js/jquery.iframe-transport',
'jquery.inputnumber': 'js/vendor/html5-input-polyfills/number-polyfill', 'jquery.inputnumber': 'js/vendor/html5-input-polyfills/number-polyfill',
'jquery.immediateDescendents': 'coffee/src/jquery.immediateDescendents', 'jquery.immediateDescendents': 'js/src/jquery.immediateDescendents',
'datepair': 'js/vendor/timepicker/datepair', 'datepair': 'js/vendor/timepicker/datepair',
'date': 'js/vendor/date', 'date': 'js/vendor/date',
moment: 'common/js/vendor/moment-with-locales', moment: 'common/js/vendor/moment-with-locales',
@@ -304,11 +304,11 @@
deps: ['xblock/core'] deps: ['xblock/core']
}, },
'cms/js/main': { 'cms/js/main': {
deps: ['coffee/src/ajax_prefix'] deps: ['js/src/ajax_prefix']
}, },
'js/src/logger': { 'js/src/logger': {
exports: 'Logger', exports: 'Logger',
deps: ['coffee/src/ajax_prefix'] deps: ['js/src/ajax_prefix']
}, },
// the following are all needed for annotation tools // the following are all needed for annotation tools

View File

@@ -44,7 +44,7 @@
'jquery.fileupload-validate': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate', // eslint-disable-line max-len 'jquery.fileupload-validate': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate', // eslint-disable-line max-len
'jquery.iframe-transport': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', // eslint-disable-line max-len 'jquery.iframe-transport': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', // eslint-disable-line max-len
'jquery.inputnumber': 'xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill', 'jquery.inputnumber': 'xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill',
'jquery.immediateDescendents': 'xmodule_js/common_static/coffee/src/jquery.immediateDescendents', 'jquery.immediateDescendents': 'xmodule_js/common_static/js/src/jquery.immediateDescendents',
'jquery.simulate': 'xmodule_js/common_static/js/vendor/jquery.simulate', 'jquery.simulate': 'xmodule_js/common_static/js/vendor/jquery.simulate',
'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair', 'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair',
'date': 'xmodule_js/common_static/js/vendor/date', 'date': 'xmodule_js/common_static/js/vendor/date',
@@ -72,7 +72,7 @@
'mock-ajax': 'xmodule_js/common_static/js/vendor/mock-ajax', 'mock-ajax': 'xmodule_js/common_static/js/vendor/mock-ajax',
mathjax: '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_SVG&delayStartupUntil=configured', // eslint-disable-line max-len mathjax: '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_SVG&delayStartupUntil=configured', // eslint-disable-line max-len
'youtube': '//www.youtube.com/player_api?noext', 'youtube': '//www.youtube.com/player_api?noext',
'coffee/src/ajax_prefix': 'xmodule_js/common_static/coffee/src/ajax_prefix', 'js/src/ajax_prefix': 'xmodule_js/common_static/js/src/ajax_prefix',
'js/spec/test_utils': 'js/spec/test_utils' 'js/spec/test_utils': 'js/spec/test_utils'
}, },
shim: { shim: {
@@ -218,9 +218,9 @@
deps: ['jquery'] deps: ['jquery']
}, },
'cms/js/main': { 'cms/js/main': {
deps: ['coffee/src/ajax_prefix'] deps: ['js/src/ajax_prefix']
}, },
'coffee/src/ajax_prefix': { 'js/src/ajax_prefix': {
deps: ['jquery'] deps: ['jquery']
} }
} }
@@ -231,17 +231,17 @@
testFiles = [ testFiles = [
'cms/js/spec/main_spec', 'cms/js/spec/main_spec',
'cms/js/spec/xblock/cms.runtime.v1_spec', 'cms/js/spec/xblock/cms.runtime.v1_spec',
'coffee/spec/models/course_spec', 'js/spec/models/course_spec',
'coffee/spec/models/metadata_spec', 'js/spec/models/metadata_spec',
'coffee/spec/models/section_spec', 'js/spec/models/section_spec',
'coffee/spec/models/settings_course_grader_spec', 'js/spec/models/settings_course_grader_spec',
'coffee/spec/models/settings_grading_spec', 'js/spec/models/settings_grading_spec',
'coffee/spec/models/textbook_spec', 'js/spec/models/textbook_spec',
'coffee/spec/models/upload_spec', 'js/spec/models/upload_spec',
'coffee/spec/views/course_info_spec', 'js/spec/views/course_info_spec',
'coffee/spec/views/metadata_edit_spec', 'js/spec/views/metadata_edit_spec',
'coffee/spec/views/textbook_spec', 'js/spec/views/textbook_spec',
'coffee/spec/views/upload_spec', 'js/spec/views/upload_spec',
'js/spec/video/transcripts/utils_spec', 'js/spec/video/transcripts/utils_spec',
'js/spec/video/transcripts/editor_spec', 'js/spec/video/transcripts/editor_spec',
'js/spec/video/transcripts/videolist_spec', 'js/spec/video/transcripts/videolist_spec',

View File

@@ -27,7 +27,7 @@
'jquery.fileupload-validate': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate', // eslint-disable-line max-len 'jquery.fileupload-validate': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate', // eslint-disable-line max-len
'jquery.iframe-transport': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', // eslint-disable-line max-len 'jquery.iframe-transport': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', // eslint-disable-line max-len
'jquery.inputnumber': 'xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill', 'jquery.inputnumber': 'xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill',
'jquery.immediateDescendents': 'xmodule_js/common_static/coffee/src/jquery.immediateDescendents', 'jquery.immediateDescendents': 'xmodule_js/common_static/js/src/jquery.immediateDescendents',
'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair', 'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair',
'date': 'xmodule_js/common_static/js/vendor/date', 'date': 'xmodule_js/common_static/js/vendor/date',
'text': 'xmodule_js/common_static/js/vendor/requirejs/text', 'text': 'xmodule_js/common_static/js/vendor/requirejs/text',
@@ -49,7 +49,7 @@
'URI': 'xmodule_js/common_static/js/vendor/URI.min', 'URI': 'xmodule_js/common_static/js/vendor/URI.min',
mathjax: '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_SVG&delayStartupUntil=configured', // eslint-disable-line max-len mathjax: '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_SVG&delayStartupUntil=configured', // eslint-disable-line max-len
'youtube': '//www.youtube.com/player_api?noext', 'youtube': '//www.youtube.com/player_api?noext',
'coffee/src/ajax_prefix': 'xmodule_js/common_static/coffee/src/ajax_prefix' 'js/src/ajax_prefix': 'xmodule_js/common_static/js/src/ajax_prefix'
}, },
shim: { shim: {
'gettext': { 'gettext': {
@@ -174,9 +174,9 @@
deps: ['xblock/core'] deps: ['xblock/core']
}, },
'cms/js/main': { 'cms/js/main': {
deps: ['coffee/src/ajax_prefix'] deps: ['js/src/ajax_prefix']
}, },
'coffee/src/ajax_prefix': { 'js/src/ajax_prefix': {
deps: ['jquery'] deps: ['jquery']
} }
} }
@@ -185,7 +185,7 @@
jasmine.getFixtures().fixturesPath = '/base/templates'; jasmine.getFixtures().fixturesPath = '/base/templates';
testFiles = [ testFiles = [
'coffee/spec/views/assets_spec', 'js/spec/views/assets_squire_spec',
'js/spec/video/translations_editor_spec', 'js/spec/video/translations_editor_spec',
'js/spec/video/file_uploader_editor_spec', 'js/spec/video/file_uploader_editor_spec',
'js/spec/models/group_configuration_spec' 'js/spec/models/group_configuration_spec'

View File

@@ -1,7 +1,7 @@
define(['backbone'], function(Backbone) { define(['backbone'], function(Backbone) {
/** /**
* Model used for metadata setting editors. This model does not do its own saving, * Model used for metadata setting editors. This model does not do its own saving,
* as that is done by module_edit.coffee. * as that is done by module_edit.js.
*/ */
var Metadata = Backbone.Model.extend({ var Metadata = Backbone.Model.extend({
defaults: { defaults: {

View File

@@ -22,13 +22,11 @@ var options = {
// Otherwise Istanbul which is used for coverage tracking will cause tests to not run. // Otherwise Istanbul which is used for coverage tracking will cause tests to not run.
sourceFiles: [ sourceFiles: [
{pattern: 'cms/**/!(*spec|djangojs).js'}, {pattern: 'cms/**/!(*spec|djangojs).js'},
{pattern: 'coffee/src/**/!(*spec).js'},
{pattern: 'js/**/!(*spec|djangojs).js'} {pattern: 'js/**/!(*spec|djangojs).js'}
], ],
specFiles: [ specFiles: [
{pattern: 'cms/**/*spec.js'}, {pattern: 'cms/**/*spec.js'},
{pattern: 'coffee/spec/**/*spec.js'},
{pattern: 'js/certificates/spec/**/*spec.js'}, {pattern: 'js/certificates/spec/**/*spec.js'},
{pattern: 'js/spec/**/*spec.js'} {pattern: 'js/spec/**/*spec.js'}
], ],

View File

@@ -21,18 +21,15 @@ var options = {
// Make sure the patterns in sourceFiles and specFiles do not match the same file. // Make sure the patterns in sourceFiles and specFiles do not match the same file.
// Otherwise Istanbul which is used for coverage tracking will cause tests to not run. // Otherwise Istanbul which is used for coverage tracking will cause tests to not run.
sourceFiles: [ sourceFiles: [
{pattern: 'coffee/src/**/!(*spec).js'},
{pattern: 'cms/js/**/!(*spec|djangojs).js'}, {pattern: 'cms/js/**/!(*spec|djangojs).js'},
{pattern: 'js/**/!(*spec|djangojs).js'} {pattern: 'js/**/!(*spec|djangojs).js'}
], ],
specFiles: [ specFiles: [
{pattern: 'coffee/spec/**/*spec.js'},
{pattern: 'js/spec/**/*spec.js'} {pattern: 'js/spec/**/*spec.js'}
], ],
fixtureFiles: [ fixtureFiles: [
{pattern: 'coffee/fixtures/**/*.*'},
{pattern: 'templates/**/*.*'} {pattern: 'templates/**/*.*'}
], ],

View File

@@ -64,6 +64,7 @@
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
*/ */
/* eslint-disable */
var MersenneTwister = function(seed) { var MersenneTwister = function(seed) {
if (seed == undefined) { if (seed == undefined) {
seed = new Date().getTime(); seed = new Date().getTime();

View File

@@ -47,11 +47,9 @@ class AnnotatableFields(object):
class AnnotatableModule(AnnotatableFields, XModule): class AnnotatableModule(AnnotatableFields, XModule):
js = { js = {
'coffee': [
resource_string(__name__, 'js/src/html/display.coffee'),
resource_string(__name__, 'js/src/annotatable/display.coffee'),
],
'js': [ 'js': [
resource_string(__name__, 'js/src/html/display.js'),
resource_string(__name__, 'js/src/annotatable/display.js'),
resource_string(__name__, 'js/src/javascript_loader.js'), resource_string(__name__, 'js/src/javascript_loader.js'),
resource_string(__name__, 'js/src/collapsible.js'), resource_string(__name__, 'js/src/collapsible.js'),
] ]

View File

@@ -116,10 +116,8 @@ class ConditionalModule(ConditionalFields, XModule, StudioEditableModule):
""" """
js = { js = {
'coffee': [
resource_string(__name__, 'js/src/conditional/display.coffee'),
],
'js': [ 'js': [
resource_string(__name__, 'js/src/conditional/display.js'),
resource_string(__name__, 'js/src/javascript_loader.js'), resource_string(__name__, 'js/src/javascript_loader.js'),
resource_string(__name__, 'js/src/collapsible.js'), resource_string(__name__, 'js/src/collapsible.js'),
] ]

View File

@@ -58,8 +58,8 @@ class TabsEditingDescriptor(EditingFields, MakoModuleDescriptor):
""" """
mako_template = "widgets/tabs-aggregator.html" mako_template = "widgets/tabs-aggregator.html"
css = {'scss': [resource_string(__name__, 'css/tabs/tabs.scss')]} css = {'scss': [resource_string(__name__, 'css/tabs/tabs.scss')]}
js = {'coffee': [resource_string( js = {'js': [resource_string(
__name__, 'js/src/tabs/tabs-aggregator.coffee')]} __name__, 'js/src/tabs/tabs-aggregator.js')]}
js_module_name = "TabsEditingDescriptor" js_module_name = "TabsEditingDescriptor"
tabs = [] tabs = []
@@ -93,7 +93,7 @@ class XMLEditingDescriptor(EditingDescriptor):
css = {'scss': [resource_string(__name__, 'css/codemirror/codemirror.scss')]} css = {'scss': [resource_string(__name__, 'css/codemirror/codemirror.scss')]}
js = {'coffee': [resource_string(__name__, 'js/src/raw/edit/xml.coffee')]} js = {'js': [resource_string(__name__, 'js/src/raw/edit/xml.js')]}
js_module_name = "XMLEditingDescriptor" js_module_name = "XMLEditingDescriptor"
@@ -103,7 +103,7 @@ class MetadataOnlyEditingDescriptor(EditingDescriptor):
not expose a UI for editing the module data not expose a UI for editing the module data
""" """
js = {'coffee': [resource_string(__name__, 'js/src/raw/edit/metadata-only.coffee')]} js = {'js': [resource_string(__name__, 'js/src/raw/edit/metadata-only.js')]}
js_module_name = "MetadataOnlyEditingDescriptor" js_module_name = "MetadataOnlyEditingDescriptor"
mako_template = "widgets/metadata-only-edit.html" mako_template = "widgets/metadata-only-edit.html"
@@ -117,5 +117,5 @@ class JSONEditingDescriptor(EditingDescriptor):
css = {'scss': [resource_string(__name__, 'css/codemirror/codemirror.scss')]} css = {'scss': [resource_string(__name__, 'css/codemirror/codemirror.scss')]}
js = {'coffee': [resource_string(__name__, 'js/src/raw/edit/json.coffee')]} js = {'js': [resource_string(__name__, 'js/src/raw/edit/json.js')]}
js_module_name = "JSONEditingDescriptor" js_module_name = "JSONEditingDescriptor"

View File

@@ -107,10 +107,8 @@ class HtmlModuleMixin(HtmlBlock, XModule):
Attributes and methods used by HtmlModules internally. Attributes and methods used by HtmlModules internally.
""" """
js = { js = {
'coffee': [
resource_string(__name__, 'js/src/html/display.coffee'),
],
'js': [ 'js': [
resource_string(__name__, 'js/src/html/display.js'),
resource_string(__name__, 'js/src/javascript_loader.js'), resource_string(__name__, 'js/src/javascript_loader.js'),
resource_string(__name__, 'js/src/collapsible.js'), resource_string(__name__, 'js/src/collapsible.js'),
resource_string(__name__, 'js/src/html/imageModal.js'), resource_string(__name__, 'js/src/html/imageModal.js'),
@@ -139,7 +137,7 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di
template_dir_name = "html" template_dir_name = "html"
show_in_read_only_mode = True show_in_read_only_mode = True
js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]} js = {'js': [resource_string(__name__, 'js/src/html/edit.js')]}
js_module_name = "HTMLEditingDescriptor" js_module_name = "HTMLEditingDescriptor"
css = {'scss': [resource_string(__name__, 'css/editor/edit.scss'), resource_string(__name__, 'css/html/edit.scss')]} css = {'scss': [resource_string(__name__, 'css/editor/edit.scss'), resource_string(__name__, 'css/html/edit.scss')]}

View File

@@ -91,11 +91,9 @@ class AnnotatableFields(object):
class ImageAnnotationModule(AnnotatableFields, XModule): class ImageAnnotationModule(AnnotatableFields, XModule):
'''Image Annotation Module''' '''Image Annotation Module'''
js = { js = {
'coffee': [
resource_string(__name__, 'js/src/html/display.coffee'),
resource_string(__name__, 'js/src/annotatable/display.coffee'),
],
'js': [ 'js': [
resource_string(__name__, 'js/src/html/display.js'),
resource_string(__name__, 'js/src/annotatable/display.js'),
resource_string(__name__, 'js/src/javascript_loader.js'), resource_string(__name__, 'js/src/javascript_loader.js'),
resource_string(__name__, 'js/src/collapsible.js'), resource_string(__name__, 'js/src/collapsible.js'),
] ]

View File

@@ -18,7 +18,7 @@ var options = {
// Avoid adding files to this list. Use RequireJS. // Avoid adding files to this list. Use RequireJS.
libraryFilesToInclude: [ libraryFilesToInclude: [
// Load the core JavaScript dependencies // Load the core JavaScript dependencies
{pattern: 'common_static/coffee/src/ajax_prefix.js', included: true}, {pattern: 'common_static/js/src/ajax_prefix.js', included: true},
{pattern: 'common_static/common/js/vendor/underscore.js', included: true}, {pattern: 'common_static/common/js/vendor/underscore.js', included: true},
{pattern: 'common_static/common/js/vendor/backbone.js', included: true}, {pattern: 'common_static/common/js/vendor/backbone.js', included: true},
{pattern: 'common_static/js/vendor/CodeMirror/codemirror.js', included: true}, {pattern: 'common_static/js/vendor/CodeMirror/codemirror.js', included: true},

View File

@@ -19,7 +19,7 @@ describe('Problem', function() {
spyOn(SR, 'readText'); spyOn(SR, 'readText');
spyOn(SR, 'readTexts'); spyOn(SR, 'readTexts');
// Load this function from spec/helper.coffee // Load this function from spec/helper.js
// Note that if your test fails with a message like: // Note that if your test fails with a message like:
// 'External request attempted for blah, which is not defined.' // 'External request attempted for blah, which is not defined.'
// this msg is coming from the stubRequests function else clause. // this msg is coming from the stubRequests function else clause.

View File

@@ -885,8 +885,6 @@ hint
); );
describe('Markdown to xml extended hint with tricky syntax cases', function() { describe('Markdown to xml extended hint with tricky syntax cases', function() {
// I'm entering this as utf-8 in this file.
// I cannot find a way to set the encoding for .coffee files but it seems to work.
it('produces xml with unicode', function() { it('produces xml with unicode', function() {
const data = MarkdownEditingDescriptor.markdownToXml(`\ const data = MarkdownEditingDescriptor.markdownToXml(`\
>>á and Ø<< >>á and Ø<<

View File

@@ -1,16 +0,0 @@
# Ignore .js files in this folder as they are compiled from coffeescript
# For each of the xmodules subdirectories, add a .gitignore file that
# will version any *.js file that is specifically written, not compiled.
*.js
# Video are written in pure JavaScript.
!video/*.js
!video/transcripts/*.js
# Converted to JS from CoffeeScript.
!time.js
!collapsible.js
!xmodule.js
!javascript_loader.js

View File

@@ -1,261 +1,443 @@
class @Annotatable // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
_debug: false /* eslint-disable */
// TODO: Examine all of the xss-lint exceptions (https://openedx.atlassian.net/browse/PLAT-2084)
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
# selectors for the annotatable xmodule this.Annotatable = (function() {
wrapperSelector: '.annotatable-wrapper' Annotatable.prototype._debug = false;
toggleAnnotationsSelector: '.annotatable-toggle-annotations'
toggleInstructionsSelector: '.annotatable-toggle-instructions'
instructionsSelector: '.annotatable-instructions'
sectionSelector: '.annotatable-section'
spanSelector: '.annotatable-span'
replySelector: '.annotatable-reply'
# these selectors are for responding to events from the annotation capa problem type
problemXModuleSelector: '.xmodule_CapaModule'
problemSelector: 'div.problem'
problemInputSelector: 'div.problem .annotation-input'
problemReturnSelector: 'div.problem .annotation-return'
constructor: (el) -> /*
console.log 'loaded Annotatable' if @_debug selectors for the annotatable xmodule
@el = el */
@$el = $(el)
@init()
$: (selector) -> Annotatable.prototype.wrapperSelector = '.annotatable-wrapper';
$(selector, @el)
init: () -> Annotatable.prototype.toggleAnnotationsSelector = '.annotatable-toggle-annotations';
@initEvents()
@initTips()
initEvents: () -> Annotatable.prototype.toggleInstructionsSelector = '.annotatable-toggle-instructions';
# Initialize toggle handlers for the instructions and annotations sections
[@annotationsHidden, @instructionsHidden] = [false, false]
@$(@toggleAnnotationsSelector).bind 'click', @onClickToggleAnnotations
@$(@toggleInstructionsSelector).bind 'click', @onClickToggleInstructions
# Initialize handler for 'reply to annotation' events that scroll to Annotatable.prototype.instructionsSelector = '.annotatable-instructions';
# the associated problem. The reply buttons are part of the tooltip
# content. It's important that the tooltips be configured to render
# as descendants of the annotation module and *not* the document.body.
@$el.on 'click', @replySelector, @onClickReply
# Initialize handler for 'return to annotation' events triggered from problems. Annotatable.prototype.sectionSelector = '.annotatable-section';
# 1) There are annotationinput capa problems rendered on the page
# 2) Each one has an embedded return link (see annotation capa problem template).
# Since the capa problem injects HTML content via AJAX, the best we can do is
# is let the click events bubble up to the body and handle them there.
$(document).on 'click', @problemReturnSelector, @onClickReturn
initTips: () -> Annotatable.prototype.spanSelector = '.annotatable-span';
# tooltips are used to display annotations for highlighted text spans
@$(@spanSelector).each (index, el) =>
$(el).qtip(@getSpanTipOptions el)
getSpanTipOptions: (el) -> Annotatable.prototype.replySelector = '.annotatable-reply';
content:
title:
text: @makeTipTitle(el)
text: @makeTipContent(el)
position:
my: 'bottom center' # of tooltip
at: 'top center' # of target
target: $(el) # where the tooltip was triggered (i.e. the annotation span)
container: @$(@wrapperSelector)
adjust:
y: -5
show:
event: 'click mouseenter'
solo: true
hide:
event: 'click mouseleave'
delay: 500,
fixed: true # don't hide the tooltip if it is moused over
style:
classes: 'ui-tooltip-annotatable'
events:
show: @onShowTip
move: @onMoveTip
onClickToggleAnnotations: (e) => @toggleAnnotations()
onClickToggleInstructions: (e) => @toggleInstructions() /*
these selectors are for responding to events from the annotation capa problem type
*/
onClickReply: (e) => @replyTo(e.currentTarget) Annotatable.prototype.problemXModuleSelector = '.xmodule_CapaModule';
onClickReturn: (e) => @returnFrom(e.currentTarget) Annotatable.prototype.problemSelector = 'div.problem';
onShowTip: (event, api) => Annotatable.prototype.problemInputSelector = 'div.problem .annotation-input';
event.preventDefault() if @annotationsHidden
onMoveTip: (event, api, position) => Annotatable.prototype.problemReturnSelector = 'div.problem .annotation-return';
###
This method handles a vertical positioning bug in Firefox as
well as an edge case in which a tooltip is displayed above a
non-overlapping span like this:
(( TOOLTIP )) function Annotatable(el) {
\/ this.onMoveTip = bind(this.onMoveTip, this);
text text text ... text text text ...... <span span span> this.onShowTip = bind(this.onShowTip, this);
<span span span> this.onClickReturn = bind(this.onClickReturn, this);
this.onClickReply = bind(this.onClickReply, this);
this.onClickToggleInstructions = bind(this.onClickToggleInstructions, this);
this.onClickToggleAnnotations = bind(this.onClickToggleAnnotations, this);
if (this._debug) {
console.log('loaded Annotatable');
}
this.el = el;
this.$el = $(el);
this.init();
}
The problem is that the tooltip looks disconnected from both spans, so Annotatable.prototype.$ = function(selector) {
we should re-position the tooltip to appear above the span. return $(selector, this.el);
### };
tip = api.elements.tooltip Annotatable.prototype.init = function() {
adjust_y = api.options.position?.adjust?.y || 0 this.initEvents();
container = api.options.position?.container || $('body') return this.initTips();
target = api.elements.target };
rects = $(target).get(0).getClientRects() Annotatable.prototype.initEvents = function() {
is_non_overlapping = (rects?.length == 2 and rects[0].left > rects[1].right)
if is_non_overlapping /*
# we want to choose the largest of the two non-overlapping spans and display Initialize toggle handlers for the instructions and annotations sections
# the tooltip above the center of it (see api.options.position settings) */
focus_rect = (if rects[0].width > rects[1].width then rects[0] else rects[1]) var ref;
else ref = [false, false], this.annotationsHidden = ref[0], this.instructionsHidden = ref[1];
# always compute the new position because Firefox doesn't this.$(this.toggleAnnotationsSelector).bind('click', this.onClickToggleAnnotations);
# properly vertically position the tooltip this.$(this.toggleInstructionsSelector).bind('click', this.onClickToggleInstructions);
focus_rect = rects[0]
rect_center = focus_rect.left + (focus_rect.width / 2) /*
rect_top = focus_rect.top Initialize handler for 'reply to annotation' events that scroll to
tip_width = $(tip).width() the associated problem. The reply buttons are part of the tooltip
tip_height = $(tip).height() content. It's important that the tooltips be configured to render
as descendants of the annotation module and *not* the document.body.
*/
this.$el.on('click', this.replySelector, this.onClickReply);
# tooltip is positioned relative to its container, so we need to factor in offsets /*
container_offset = $(container).offset() Initialize handler for 'return to annotation' events triggered from problems.
offset_left = -container_offset.left 1) There are annotationinput capa problems rendered on the page
offset_top = $(document).scrollTop() - container_offset.top 2) Each one has an embedded return link (see annotation capa problem template).
Since the capa problem injects HTML content via AJAX, the best we can do is
is let the click events bubble up to the body and handle them there.
*/
return $(document).on('click', this.problemReturnSelector, this.onClickReturn);
};
tip_left = offset_left + rect_center - (tip_width / 2) Annotatable.prototype.initTips = function() {
tip_top = offset_top + rect_top - tip_height + adjust_y
# make sure the new tip position doesn't clip the edges of the screen /*
win_width = $(window).width() tooltips are used to display annotations for highlighted text spans
if tip_left < offset_left */
tip_left = offset_left return this.$(this.spanSelector).each((function(_this) {
else if tip_left + tip_width > win_width + offset_left return function(index, el) {
tip_left = win_width + offset_left - tip_width return $(el).qtip(_this.getSpanTipOptions(el));
};
})(this));
};
# final step: update the position object (used by qtip2 to show the tip after the move event) Annotatable.prototype.getSpanTipOptions = function(el) {
$.extend position, 'left': tip_left, 'top': tip_top return {
content: {
title: {
text: this.makeTipTitle(el)
},
text: this.makeTipContent(el)
},
position: {
getSpanForProblemReturn: (el) -> /*
problem_id = $(@problemReturnSelector).index(el) of tooltip
@$(@spanSelector).filter("[data-problem-id='#{problem_id}']") */
my: 'bottom center',
getProblem: (el) -> /*
problem_id = @getProblemId(el) of target
$(@problemInputSelector).eq(problem_id) */
at: 'top center',
getProblemId: (el) -> /*
$(el).data('problem-id') where the tooltip was triggered (i.e. the annotation span)
*/
target: $(el),
container: this.$(this.wrapperSelector),
adjust: {
y: -5
}
},
show: {
event: 'click mouseenter',
solo: true
},
hide: {
event: 'click mouseleave',
delay: 500,
toggleAnnotations: () -> /*
hide = (@annotationsHidden = not @annotationsHidden) don't hide the tooltip if it is moused over
@toggleAnnotationButtonText hide */
@toggleSpans hide fixed: true
@toggleTips hide },
style: {
classes: 'ui-tooltip-annotatable'
},
events: {
show: this.onShowTip,
move: this.onMoveTip
}
};
};
toggleTips: (hide) -> Annotatable.prototype.onClickToggleAnnotations = function(e) {
visible = @findVisibleTips() return this.toggleAnnotations();
@hideTips visible };
toggleAnnotationButtonText: (hide) -> Annotatable.prototype.onClickToggleInstructions = function(e) {
if hide return this.toggleInstructions();
buttonText = gettext('Show Annotations') };
else
buttonText = gettext('Hide Annotations')
@$(@toggleAnnotationsSelector).text(buttonText)
toggleInstructions: () -> Annotatable.prototype.onClickReply = function(e) {
hide = (@instructionsHidden = not @instructionsHidden) return this.replyTo(e.currentTarget);
@toggleInstructionsButton hide };
@toggleInstructionsText hide
toggleInstructionsButton: (hide) -> Annotatable.prototype.onClickReturn = function(e) {
if hide return this.returnFrom(e.currentTarget);
txt = gettext('Expand Instructions') };
else
txt = gettext('Collapse Instructions')
cls = (if hide then ['expanded', 'collapsed'] else ['collapsed','expanded'])
@$(@toggleInstructionsSelector).text(txt).removeClass(cls[0]).addClass(cls[1])
toggleInstructionsText: (hide) -> Annotatable.prototype.onShowTip = function(event, api) {
slideMethod = (if hide then 'slideUp' else 'slideDown') if (this.annotationsHidden) {
@$(@instructionsSelector)[slideMethod]() return event.preventDefault();
}
};
toggleSpans: (hide) -> Annotatable.prototype.onMoveTip = function(event, api, position) {
@$(@spanSelector).toggleClass 'hide', hide, 250
replyTo: (buttonEl) -> /*
offset = -20 This method handles a vertical positioning bug in Firefox as
el = @getProblem buttonEl well as an edge case in which a tooltip is displayed above a
if el.length > 0 non-overlapping span like this:
@scrollTo(el, @afterScrollToProblem, offset)
else (( TOOLTIP ))
console.log('problem not found. event: ', e) if @_debug \/
text text text ... text text text ...... <span span span>
<span span span>
The problem is that the tooltip looks disconnected from both spans, so
we should re-position the tooltip to appear above the span.
*/
var adjust_y, container, container_offset, focus_rect, is_non_overlapping, offset_left, offset_top, rect_center, rect_top, rects, ref, ref1, ref2, target, tip, tip_height, tip_left, tip_top, tip_width, win_width;
tip = api.elements.tooltip;
adjust_y = ((ref = api.options.position) != null ? (ref1 = ref.adjust) != null ? ref1.y : void 0 : void 0) || 0;
container = ((ref2 = api.options.position) != null ? ref2.container : void 0) || $('body');
target = api.elements.target;
rects = $(target).get(0).getClientRects();
is_non_overlapping = (rects != null ? rects.length : void 0) === 2 && rects[0].left > rects[1].right;
if (is_non_overlapping) {
returnFrom: (buttonEl) -> /*
offset = -200 we want to choose the largest of the two non-overlapping spans and display
el = @getSpanForProblemReturn buttonEl the tooltip above the center of it (see api.options.position settings)
if el.length > 0 */
@scrollTo(el, @afterScrollToSpan, offset) focus_rect = (rects[0].width > rects[1].width ? rects[0] : rects[1]);
else } else {
console.log('span not found. event:', e) if @_debug
scrollTo: (el, after, offset = -20) -> /*
$('html,body').scrollTo(el, { always compute the new position because Firefox doesn't
duration: 500 properly vertically position the tooltip
onAfter: @_once => after?.call this, el */
offset: offset focus_rect = rects[0];
}) if $(el).length > 0 }
rect_center = focus_rect.left + (focus_rect.width / 2);
rect_top = focus_rect.top;
tip_width = $(tip).width();
tip_height = $(tip).height();
afterScrollToProblem: (problem_el) -> /*
problem_el.effect 'highlight', {}, 500 tooltip is positioned relative to its container, so we need to factor in offsets
*/
container_offset = $(container).offset();
offset_left = -container_offset.left;
offset_top = $(document).scrollTop() - container_offset.top;
tip_left = offset_left + rect_center - (tip_width / 2);
tip_top = offset_top + rect_top - tip_height + adjust_y;
afterScrollToSpan: (span_el) -> /*
span_el.addClass 'selected', 400, 'swing', -> make sure the new tip position doesn't clip the edges of the screen
span_el.removeClass 'selected', 400, 'swing' */
win_width = $(window).width();
if (tip_left < offset_left) {
tip_left = offset_left;
} else if (tip_left + tip_width > win_width + offset_left) {
tip_left = win_width + offset_left - tip_width;
}
makeTipContent: (el) -> /*
(api) => final step: update the position object (used by qtip2 to show the tip after the move event)
text = $(el).data('comment-body') */
comment = @createComment(text) return $.extend(position, {
problem_id = @getProblemId(el) 'left': tip_left,
reply = @createReplyLink(problem_id) 'top': tip_top
$(comment).add(reply) });
};
makeTipTitle: (el) -> Annotatable.prototype.getSpanForProblemReturn = function(el) {
(api) => var problem_id;
title = $(el).data('comment-title') problem_id = $(this.problemReturnSelector).index(el);
(if title then title else gettext('Commentary')) return this.$(this.spanSelector).filter("[data-problem-id='" + problem_id + "']");
};
createComment: (text) -> Annotatable.prototype.getProblem = function(el) {
$("<div class=\"annotatable-comment\">#{text}</div>") var problem_id;
problem_id = this.getProblemId(el);
return $(this.problemInputSelector).eq(problem_id);
};
createReplyLink: (problem_id) -> Annotatable.prototype.getProblemId = function(el) {
linktxt = gettext('Reply to Annotation') return $(el).data('problem-id');
$("<a class=\"annotatable-reply\" href=\"javascript:void(0);\" data-problem-id=\"#{problem_id}\">#{linktxt}</a>") };
findVisibleTips: () -> Annotatable.prototype.toggleAnnotations = function() {
visible = [] var hide;
@$(@spanSelector).each (index, el) -> hide = (this.annotationsHidden = !this.annotationsHidden);
api = $(el).qtip('api') this.toggleAnnotationButtonText(hide);
tip = $(api?.elements.tooltip) this.toggleSpans(hide);
if tip.is(':visible') return this.toggleTips(hide);
visible.push el };
visible
hideTips: (elements) -> Annotatable.prototype.toggleTips = function(hide) {
$(elements).qtip('hide') var visible;
visible = this.findVisibleTips();
return this.hideTips(visible);
};
_once: (fn) -> Annotatable.prototype.toggleAnnotationButtonText = function(hide) {
done = false var buttonText;
return => if (hide) {
fn.call this unless done buttonText = gettext('Show Annotations');
done = true } else {
buttonText = gettext('Hide Annotations');
}
return this.$(this.toggleAnnotationsSelector).text(buttonText);
};
Annotatable.prototype.toggleInstructions = function() {
var hide;
hide = (this.instructionsHidden = !this.instructionsHidden);
this.toggleInstructionsButton(hide);
return this.toggleInstructionsText(hide);
};
Annotatable.prototype.toggleInstructionsButton = function(hide) {
var cls, txt;
if (hide) {
txt = gettext('Expand Instructions');
} else {
txt = gettext('Collapse Instructions');
}
cls = (hide ? ['expanded', 'collapsed'] : ['collapsed', 'expanded']);
return this.$(this.toggleInstructionsSelector).text(txt).removeClass(cls[0]).addClass(cls[1]);
};
Annotatable.prototype.toggleInstructionsText = function(hide) {
var slideMethod;
slideMethod = (hide ? 'slideUp' : 'slideDown');
return this.$(this.instructionsSelector)[slideMethod]();
};
Annotatable.prototype.toggleSpans = function(hide) {
return this.$(this.spanSelector).toggleClass('hide', hide, 250);
};
Annotatable.prototype.replyTo = function(buttonEl) {
var el, offset;
offset = -20;
el = this.getProblem(buttonEl);
if (el.length > 0) {
return this.scrollTo(el, this.afterScrollToProblem, offset);
} else {
if (this._debug) {
return console.log('problem not found. event: ', e);
}
}
};
Annotatable.prototype.returnFrom = function(buttonEl) {
var el, offset;
offset = -200;
el = this.getSpanForProblemReturn(buttonEl);
if (el.length > 0) {
return this.scrollTo(el, this.afterScrollToSpan, offset);
} else {
if (this._debug) {
return console.log('span not found. event:', e);
}
}
};
Annotatable.prototype.scrollTo = function(el, after, offset) {
if (offset == null) {
offset = -20;
}
if ($(el).length > 0) {
return $('html,body').scrollTo(el, {
duration: 500,
onAfter: this._once((function(_this) {
return function() {
return after != null ? after.call(_this, el) : void 0;
};
})(this)),
offset: offset
});
}
};
Annotatable.prototype.afterScrollToProblem = function(problem_el) {
return problem_el.effect('highlight', {}, 500);
};
Annotatable.prototype.afterScrollToSpan = function(span_el) {
return span_el.addClass('selected', 400, 'swing', function() {
return span_el.removeClass('selected', 400, 'swing');
});
};
Annotatable.prototype.makeTipContent = function(el) {
return (function(_this) {
return function(api) {
var comment, problem_id, reply, text;
text = $(el).data('comment-body');
comment = _this.createComment(text);
problem_id = _this.getProblemId(el);
reply = _this.createReplyLink(problem_id);
return $(comment).add(reply);
};
})(this);
};
Annotatable.prototype.makeTipTitle = function(el) {
return (function(_this) {
return function(api) {
var title;
title = $(el).data('comment-title');
if (title) {
return title;
} else {
return gettext('Commentary');
}
};
})(this);
};
Annotatable.prototype.createComment = function(text) {
return $("<div class=\"annotatable-comment\">" + text + "</div>"); // xss-lint: disable=javascript-concat-html
};
Annotatable.prototype.createReplyLink = function(problem_id) {
var linktxt;
linktxt = gettext('Reply to Annotation');
return $("<a class=\"annotatable-reply\" href=\"javascript:void(0);\" data-problem-id=\"" + problem_id + "\">" + linktxt + "</a>"); // xss-lint: disable=javascript-concat-html
};
Annotatable.prototype.findVisibleTips = function() {
var visible;
visible = [];
this.$(this.spanSelector).each(function(index, el) {
var api, tip;
api = $(el).qtip('api');
tip = $(api != null ? api.elements.tooltip : void 0);
if (tip.is(':visible')) {
return visible.push(el);
}
});
return visible;
};
Annotatable.prototype.hideTips = function(elements) {
return $(elements).qtip('hide');
};
Annotatable.prototype._once = function(fn) {
var done;
done = false;
return (function(_this) {
return function() {
if (!done) {
fn.call(_this);
}
return done = true;
};
})(this);
};
return Annotatable;
})();
}).call(this);

View File

@@ -1,3 +1,5 @@
/* eslint-disable */
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// Circuit simulator // Circuit simulator

View File

@@ -1,38 +1,60 @@
class @Conditional // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
(function() {
this.Conditional = (function() {
function Conditional(element, callerElId) {
var dependencies;
this.el = $(element).find('.conditional-wrapper');
this.callerElId = callerElId;
if (callerElId !== void 0) {
dependencies = this.el.data('depends');
if ((typeof dependencies === 'string') && (dependencies.length > 0) && (dependencies.indexOf(callerElId) === -1)) {
return;
}
}
this.url = this.el.data('url');
if (this.url) {
this.render(element);
}
}
constructor: (element, callerElId) -> Conditional.prototype.render = function(element) {
@el = $(element).find('.conditional-wrapper') return $.postWithPrefix(this.url + "/conditional_get", (function(_this) {
return function(response) {
var i, j, len, parentEl, parentId, ref;
_this.el.html('');
ref = response.html;
for (j = 0, len = ref.length; j < len; j++) {
i = ref[j];
_this.el.append(i);
}
parentEl = $(element).parent();
parentId = parentEl.attr('id');
if (response.message === false) {
if (parentEl.hasClass('vert')) {
parentEl.hide();
} else {
$(element).hide();
}
} else {
if (parentEl.hasClass('vert')) {
parentEl.show();
} else {
$(element).show();
}
}
@callerElId = callerElId /*
The children are rendered with a new request, so they have a different request-token.
Use that token instead of @requestToken by simply not passing a token into initializeBlocks.
*/
return XBlock.initializeBlocks(_this.el);
};
})(this));
};
if callerElId isnt undefined return Conditional;
dependencies = @el.data('depends')
if (typeof dependencies is 'string') and (dependencies.length > 0) and (dependencies.indexOf(callerElId) is -1)
return
@url = @el.data('url') })();
if @url
@render(element)
render: (element) -> }).call(this);
$.postWithPrefix "#{@url}/conditional_get", (response) =>
@el.html ''
@el.append(i) for i in response.html
parentEl = $(element).parent()
parentId = parentEl.attr 'id'
if response.message is false
if parentEl.hasClass('vert')
parentEl.hide()
else
$(element).hide()
else
if parentEl.hasClass('vert')
parentEl.show()
else
$(element).show()
# The children are rendered with a new request, so they have a different request-token.
# Use that token instead of @requestToken by simply not passing a token into initializeBlocks.
XBlock.initializeBlocks(@el)

View File

@@ -1,4 +1,21 @@
class @InlineDiscussion extends XModule.Descriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
constructor: (element) -> /* eslint-disable */
@el = $(element).find('.discussion-module') (function() {
@view = new DiscussionInlineView(el: @el) var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
this.InlineDiscussion = (function(superClass) {
extend(InlineDiscussion, superClass);
function InlineDiscussion(element) {
this.el = $(element).find('.discussion-module');
this.view = new DiscussionInlineView({
el: this.el
});
}
return InlineDiscussion;
})(XModule.Descriptor);
}).call(this);

View File

@@ -1,13 +1,26 @@
class @HTMLModule // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
(function() {
this.HTMLModule = (function() {
function HTMLModule(element) {
this.element = element;
this.el = $(this.element);
JavascriptLoader.executeModuleScripts(this.el);
Collapsible.setCollapsibles(this.el);
if (typeof MathJax !== "undefined" && MathJax !== null) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.el[0]]);
}
if (typeof setupFullScreenModal !== "undefined" && setupFullScreenModal !== null) {
setupFullScreenModal();
}
}
constructor: (@element) -> HTMLModule.prototype.$ = function(selector) {
@el = $(@element) return $(selector, this.el);
JavascriptLoader.executeModuleScripts(@el) };
Collapsible.setCollapsibles(@el)
if MathJax?
MathJax.Hub.Queue ["Typeset", MathJax.Hub, @el[0]]
if setupFullScreenModal?
setupFullScreenModal()
$: (selector) -> return HTMLModule;
$(selector, @el)
})();
}).call(this);

View File

@@ -1,92 +1,141 @@
class @HTMLEditingDescriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
# custom fonts are prepended to font selection dropdown this.HTMLEditingDescriptor = (function() {
CUSTOM_FONTS = "Default='Open Sans', Verdana, Arial, Helvetica, sans-serif;"
# list of standard tinyMCE fonts: http://www.tinymce.com/wiki.php/Configuration:font_formats /*
STANDARD_FONTS = "Andale Mono=andale mono,times;"+ custom fonts are prepended to font selection dropdown
"Arial=arial,helvetica,sans-serif;"+ */
"Arial Black=arial black,avant garde;"+ var CUSTOM_FONTS, STANDARD_FONTS, _getFonts;
"Book Antiqua=book antiqua,palatino;"+
"Comic Sans MS=comic sans ms,sans-serif;"+
"Courier New=courier new,courier;"+
"Georgia=georgia,palatino;"+
"Helvetica=helvetica;"+
"Impact=impact,chicago;"+
"Symbol=symbol;"+
"Tahoma=tahoma,arial,helvetica,sans-serif;"+
"Terminal=terminal,monaco;"+
"Times New Roman=times new roman,times;"+
"Trebuchet MS=trebuchet ms,geneva;"+
"Verdana=verdana,geneva;"+
"Webdings=webdings;"+
"Wingdings=wingdings,zapf dingbats"
_getFonts = () -> CUSTOM_FONTS = "Default='Open Sans', Verdana, Arial, Helvetica, sans-serif;";
CUSTOM_FONTS + STANDARD_FONTS
constructor: (element) -> /*
@element = element list of standard tinyMCE fonts: http://www.tinymce.com/wiki.php/Configuration:font_formats
@base_asset_url = @element.find("#editor-tab").data('base-asset-url') */
@editor_choice = @element.find("#editor-tab").data('editor') STANDARD_FONTS = "Andale Mono=andale mono,times;" +
@new_image_modal = window.STUDIO_FRONTEND_IN_CONTEXT_IMAGE_SELECTION "Arial=arial,helvetica,sans-serif;" +
if @base_asset_url == undefined "Arial Black=arial black,avant garde;" +
@base_asset_url = null "Book Antiqua=book antiqua,palatino;" +
"Comic Sans MS=comic sans ms,sans-serif;" +
"Courier New=courier new,courier;" +
"Georgia=georgia,palatino;" +
"Helvetica=helvetica;" +
"Impact=impact,chicago;" +
"Symbol=symbol;" +
"Tahoma=tahoma,arial,helvetica,sans-serif;" +
"Terminal=terminal,monaco;" +
"Times New Roman=times new roman,times;" +
"Trebuchet MS=trebuchet ms,geneva;" +
"Verdana=verdana,geneva;" +
"Webdings=webdings;" +
"Wingdings=wingdings,zapf dingbats";
# We always create the "raw editor" so we can get the text out of it if necessary on save. _getFonts = function() {
@advanced_editor = CodeMirror.fromTextArea($(".edit-box", @element)[0], { return CUSTOM_FONTS + STANDARD_FONTS;
mode: "text/html" };
lineNumbers: true
function HTMLEditingDescriptor(element) {
this.initInstanceCallback = bind(this.initInstanceCallback, this);
this.saveCodeEditor = bind(this.saveCodeEditor, this);
this.showCodeEditor = bind(this.showCodeEditor, this);
this.saveLink = bind(this.saveLink, this);
this.editLink = bind(this.editLink, this);
this.editImageSubmit = bind(this.editImageSubmit, this);
this.saveImageFromModal = bind(this.saveImageFromModal, this);
this.closeImageModal = bind(this.closeImageModal, this);
this.openImageModal = bind(this.openImageModal, this);
this.saveImage = bind(this.saveImage, this);
this.editImage = bind(this.editImage, this);
this.setupTinyMCE = bind(this.setupTinyMCE, this);
var tiny_mce_css_links;
this.element = element;
this.base_asset_url = this.element.find("#editor-tab").data('base-asset-url');
this.editor_choice = this.element.find("#editor-tab").data('editor');
this.new_image_modal = window.STUDIO_FRONTEND_IN_CONTEXT_IMAGE_SELECTION;
if (this.base_asset_url === void 0) {
this.base_asset_url = null;
}
/*
We always create the "raw editor" so we can get the text out of it if necessary on save.
*/
this.advanced_editor = CodeMirror.fromTextArea($(".edit-box", this.element)[0], {
mode: "text/html",
lineNumbers: true,
lineWrapping: true lineWrapping: true
}) });
if (this.editor_choice === 'visual') {
this.$advancedEditorWrapper = $(this.advanced_editor.getWrapperElement());
this.$advancedEditorWrapper.addClass('is-inactive');
if @editor_choice == 'visual' /*
@$advancedEditorWrapper = $(@advanced_editor.getWrapperElement()) Create an array of all content CSS links to use in and pass to Tiny MCE.
@$advancedEditorWrapper.addClass('is-inactive') We create this dynamically in order to support hashed files from our Django pipeline.
# Create an array of all content CSS links to use in and pass to Tiny MCE. CSS files that are to be used by Tiny MCE should contain the string "tinymce" so
# We create this dynamically in order to support hashed files from our Django pipeline. they can be found by the search below.
# CSS files that are to be used by Tiny MCE should contain the string "tinymce" so We filter for only those files that are "content" files (as opposed to "skin" files).
# they can be found by the search below. */
# We filter for only those files that are "content" files (as opposed to "skin" files). tiny_mce_css_links = [];
tiny_mce_css_links = [] $("link[rel=stylesheet][href*='tinymce']").filter("[href*='content']").each(function() {
$("link[rel=stylesheet][href*='tinymce']").filter("[href*='content']").each -> tiny_mce_css_links.push($(this).attr("href"));
tiny_mce_css_links.push $(this).attr("href") });
return
# This is a workaround for the fact that tinyMCE's baseURL property is not getting correctly set on AWS /*
# instances (like sandbox). It is not necessary to explicitly set baseURL when running locally. This is a workaround for the fact that tinyMCE's baseURL property is not getting correctly set on AWS
tinyMCE.baseURL = "#{baseUrl}/js/vendor/tinymce/js/tinymce" instances (like sandbox). It is not necessary to explicitly set baseURL when running locally.
# This is necessary for the LMS bulk e-mail acceptance test. In that particular scenario, */
# tinyMCE incorrectly decides that the suffix should be "", which means it fails to load files. tinyMCE.baseURL = baseUrl + "/js/vendor/tinymce/js/tinymce";
tinyMCE.suffix = ".min"
@tiny_mce_textarea = $(".tiny-mce", @element).tinymce({ /*
script_url : "#{baseUrl}/js/vendor/tinymce/js/tinymce/tinymce.full.min.js", This is necessary for the LMS bulk e-mail acceptance test. In that particular scenario,
font_formats : _getFonts(), tinyMCE incorrectly decides that the suffix should be "", which means it fails to load files.
theme : "modern", */
skin: 'studio-tmce4', tinyMCE.suffix = ".min";
schema: "html5", this.tiny_mce_textarea = $(".tiny-mce", this.element).tinymce({
# Necessary to preserve relative URLs to our images. script_url: baseUrl + "/js/vendor/tinymce/js/tinymce/tinymce.full.min.js",
convert_urls : false, font_formats: _getFonts(),
# Sniff UI direction from `.wrapper-view` in studio or `.window-wrap` in LMS theme: "modern",
directionality: $(".wrapper-view, .window-wrap").prop('dir'), skin: 'studio-tmce4',
content_css : tiny_mce_css_links.join(", "), schema: "html5",
formats : {
# tinyMCE does block level for code by default /*
code: {inline: 'code'} Necessary to preserve relative URLs to our images.
}, */
# Disable visual aid on borderless table. convert_urls: false,
visual: false,
plugins: "textcolor, link, image, codemirror", /*
codemirror: { Sniff UI direction from `.wrapper-view` in studio or `.window-wrap` in LMS
path: "#{baseUrl}/js/vendor" */
}, directionality: $(".wrapper-view, .window-wrap").prop('dir'),
image_advtab: true, content_css: tiny_mce_css_links.join(", "),
# We may want to add "styleselect" when we collect all styles used throughout the LMS formats: {
toolbar: "formatselect | fontselect | bold italic underline forecolor wrapAsCode | " + // tinyMCE does block level for code by default
"alignleft aligncenter alignright alignjustify | " + code: {
"bullist numlist outdent indent blockquote | link unlink " + inline: 'code'
"#{if @new_image_modal then 'insertImage' else 'image'} | code", }
block_formats: interpolate("%(paragraph)s=p;%(preformatted)s=pre;%(heading3)s=h3;%(heading4)s=h4;%(heading5)s=h5;%(heading6)s=h6", { },
/*
Disable visual aid on borderless table.
*/
visual: false,
plugins: "textcolor, link, image, codemirror",
codemirror: {
path: baseUrl + "/js/vendor"
},
image_advtab: true,
/*
We may want to add "styleselect" when we collect all styles used throughout the LMS
*/
toolbar: "formatselect | fontselect | bold italic underline forecolor wrapAsCode | " +
"alignleft aligncenter alignright alignjustify | " +
"bullist numlist outdent indent blockquote | link unlink " +
((this.new_image_modal ? 'insertImage' : 'image') + " | code"),
block_formats: interpolate("%(paragraph)s=p;%(preformatted)s=pre;%(heading3)s=h3;%(heading4)s=h4;%(heading5)s=h5;%(heading6)s=h6", {
paragraph: gettext("Paragraph"), paragraph: gettext("Paragraph"),
preformatted: gettext("Preformatted"), preformatted: gettext("Preformatted"),
heading3: gettext("Heading 3"), heading3: gettext("Heading 3"),
@@ -94,961 +143,1246 @@ class @HTMLEditingDescriptor
heading5: gettext("Heading 5"), heading5: gettext("Heading 5"),
heading6: gettext("Heading 6") heading6: gettext("Heading 6")
}, true), }, true),
width: '100%', width: '100%',
height: '400px', height: '400px',
menubar: false, menubar: false,
statusbar: false, statusbar: false,
# Necessary to avoid stripping of style tags. /*
valid_children : "+body[style]", Necessary to avoid stripping of style tags.
*/
valid_children: "+body[style]",
# Allow any elements to be used, e.g. link, script, math /*
valid_elements: "*[*]", Allow any elements to be used, e.g. link, script, math
extended_valid_elements: "*[*]", */
invalid_elements: "", valid_elements: "*[*]",
extended_valid_elements: "*[*]",
invalid_elements: "",
setup: this.setupTinyMCE,
setup: @setupTinyMCE, /*
# Cannot get access to tinyMCE Editor instance (for focusing) until after it is rendered. Cannot get access to tinyMCE Editor instance (for focusing) until after it is rendered.
# The tinyMCE callback passes in the editor as a parameter. The tinyMCE callback passes in the editor as a parameter.
init_instance_callback: @initInstanceCallback, */
init_instance_callback: this.initInstanceCallback,
browser_spellcheck: true
});
tinymce.addI18n('en', {
browser_spellcheck: true /*
}) Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
tinymce.addI18n('en', { */
### "Add to Dictionary": gettext("Add to Dictionary"),
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Add to Dictionary": gettext("Add to Dictionary"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Advanced": gettext("Advanced"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Align center": gettext("Align center"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Align left": gettext("Align left"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Align right": gettext("Align right"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Alignment": gettext("Alignment"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Alternative source": gettext("Alternative source"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Anchor": gettext("Anchor"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Anchors": gettext("Anchors"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Author": gettext("Author"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Background color": gettext("Background color"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Blockquote": gettext("Blockquote"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Blocks": gettext("Blocks"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Body": gettext("Body"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Bold": gettext("Bold"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Border color": gettext("Border color"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Border": gettext("Border"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Bottom": gettext("Bottom"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Bullet list": gettext("Bullet list"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cancel": gettext("Cancel"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Caption": gettext("Caption"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cell padding": gettext("Cell padding"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cell properties": gettext("Cell properties"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cell spacing": gettext("Cell spacing"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cell type": gettext("Cell type"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cell": gettext("Cell"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Center": gettext("Center"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Circle": gettext("Circle"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Clear formatting": gettext("Clear formatting"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Close": gettext("Close"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Code block": gettext("Code block"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Code": gettext("Code"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Color": gettext("Color"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cols": gettext("Cols"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Column group": gettext("Column group"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Column": gettext("Column"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Constrain proportions": gettext("Constrain proportions"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Copy row": gettext("Copy row"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Copy": gettext("Copy"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Could not find the specified string.": gettext("Could not find the specified string."),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Custom color": gettext("Custom color"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Custom...": gettext("Custom..."),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cut row": gettext("Cut row"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Cut": gettext("Cut"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Decrease indent": gettext("Decrease indent"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Default": gettext("Default"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Delete column": gettext("Delete column"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Delete row": gettext("Delete row"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Delete table": gettext("Delete table"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Description": gettext("Description"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Dimensions": gettext("Dimensions"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Disc": gettext("Disc"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Div": gettext("Div"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Document properties": gettext("Document properties"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Edit HTML": gettext("Edit HTML"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Edit": gettext("Edit"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Embed": gettext("Embed"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Emoticons": gettext("Emoticons"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Encoding": gettext("Encoding"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"File": gettext("File"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Find and replace": gettext("Find and replace"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Find next": gettext("Find next"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Find previous": gettext("Find previous"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Find": gettext("Find"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Finish": gettext("Finish"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Font Family": gettext("Font Family"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Font Sizes": gettext("Font Sizes"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Footer": gettext("Footer"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Format": gettext("Format"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Formats": gettext("Formats"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Fullscreen": gettext("Fullscreen"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"General": gettext("General"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"H Align": gettext("H Align"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header 1": gettext("Header 1"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header 2": gettext("Header 2"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header 3": gettext("Header 3"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header 4": gettext("Header 4"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header 5": gettext("Header 5"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header 6": gettext("Header 6"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header cell": gettext("Header cell"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Header": gettext("Header"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Headers": gettext("Headers"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Heading 1": gettext("Heading 1"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Heading 2": gettext("Heading 2"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Heading 3": gettext("Heading 3"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Heading 4": gettext("Heading 4"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Heading 5": gettext("Heading 5"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Heading 6": gettext("Heading 6"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Headings": gettext("Headings"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Height": gettext("Height"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Horizontal line": gettext("Horizontal line"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Horizontal space": gettext("Horizontal space"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"HTML source code": gettext("HTML source code"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Ignore all": gettext("Ignore all"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Ignore": gettext("Ignore"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Image description": gettext("Image description"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Increase indent": gettext("Increase indent"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Inline": gettext("Inline"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert column after": gettext("Insert column after"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert column before": gettext("Insert column before"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert date/time": gettext("Insert date/time"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert image": gettext("Insert image"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert link": gettext("Insert link"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert row after": gettext("Insert row after"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert row before": gettext("Insert row before"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert table": gettext("Insert table"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert template": gettext("Insert template"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert video": gettext("Insert video"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert": gettext("Insert"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert/edit image": gettext("Insert/edit image"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert/edit link": gettext("Insert/edit link"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Insert/edit video": gettext("Insert/edit video"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Italic": gettext("Italic"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Justify": gettext("Justify"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Keywords": gettext("Keywords"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Left to right": gettext("Left to right"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Left": gettext("Left"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Lower Alpha": gettext("Lower Alpha"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Lower Greek": gettext("Lower Greek"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Lower Roman": gettext("Lower Roman"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Match case": gettext("Match case"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Merge cells": gettext("Merge cells"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Middle": gettext("Middle"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Name": gettext("Name"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"New document": gettext("New document"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"New window": gettext("New window"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Next": gettext("Next"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"No color": gettext("No color"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Nonbreaking space": gettext("Nonbreaking space"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"None": gettext("None"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Numbered list": gettext("Numbered list"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Ok": gettext("Ok"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"OK": gettext("OK"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Page break": gettext("Page break"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Paragraph": gettext("Paragraph"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Paste as text": gettext("Paste as text"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": gettext("Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off."),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Paste row after": gettext("Paste row after"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Paste row before": gettext("Paste row before"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Paste your embed code below:": gettext("Paste your embed code below:"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Paste": gettext("Paste"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Poster": gettext("Poster"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Pre": gettext("Pre"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Prev": gettext("Prev"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Preview": gettext("Preview"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Print": gettext("Print"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Redo": gettext("Redo"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Remove link": gettext("Remove link"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Replace all": gettext("Replace all"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Replace all": gettext("Replace all"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Replace with": gettext("Replace with"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Replace": gettext("Replace"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Replace": gettext("Replace"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Restore last draft": gettext("Restore last draft"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": gettext("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Right to left": gettext("Right to left"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Right": gettext("Right"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Robots": gettext("Robots"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Row group": gettext("Row group"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Row properties": gettext("Row properties"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Row type": gettext("Row type"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Row": gettext("Row"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Rows": gettext("Rows"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Save": gettext("Save"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Scope": gettext("Scope"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Select all": gettext("Select all"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Show blocks": gettext("Show blocks"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Show invisible characters": gettext("Show invisible characters"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Source code": gettext("Source code"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Source": gettext("Source"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Special character": gettext("Special character"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Spellcheck": gettext("Spellcheck"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Split cell": gettext("Split cell"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Square": gettext("Square"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Start search": gettext("Start search"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Strikethrough": gettext("Strikethrough"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Style": gettext("Style"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Subscript": gettext("Subscript"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Superscript": gettext("Superscript"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Table properties": gettext("Table properties"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Table": gettext("Table"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Target": gettext("Target"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Templates": gettext("Templates"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Text color": gettext("Text color"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Text to display": gettext("Text to display"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": gettext("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"The URL you entered seems to be an external link. Do you want to add the required http:// prefix?": gettext("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Title": gettext("Title"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Tools": gettext("Tools"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Top": gettext("Top"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Underline": gettext("Underline"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Undo": gettext("Undo"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Upper Alpha": gettext("Upper Alpha"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Upper Roman": gettext("Upper Roman"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Url": gettext("Url"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"V Align": gettext("V Align"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Vertical space": gettext("Vertical space"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"View": gettext("View"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Visual aids": gettext("Visual aids"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Whole words": gettext("Whole words"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Width": gettext("Width"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Words: {0}": gettext("Words: {0}"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"You have unsaved changes are you sure you want to navigate away?": gettext("You have unsaved changes are you sure you want to navigate away?"),
###
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
###
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.": gettext("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead."),
})
setupTinyMCE: (ed) => /*
ed.addButton('wrapAsCode', { Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
### */
Translators: this is a toolbar button tooltip from the raw HTML editor displayed in the browser when a user needs to edit HTML "Advanced": gettext("Advanced"),
###
title : gettext('Code block'),
image : "#{baseUrl}/images/ico-tinymce-code.png",
onclick : () ->
ed.formatter.toggle('code')
})
ed.addButton('insertImage', { /*
### Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
Translators: this is a toolbar button tooltip from the raw HTML editor displayed in the browser when a user needs to edit HTML */
### "Align center": gettext("Align center"),
title : gettext('Insert/Edit Image'),
icon: 'image',
onclick : @openImageModal
})
@visualEditor = ed /*
@imageModal = $('#edit-image-modal .modal') Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Align left": gettext("Align left"),
# These events were added to the plugin code as the TinyMCE PluginManager /*
# does not fire any events when plugins are opened or closed. Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
ed.on('SaveImage', @saveImage) */
ed.on('EditImage', @editImage) "Align right": gettext("Align right"),
ed.on('SaveLink', @saveLink)
ed.on('EditLink', @editLink)
ed.on('ShowCodeEditor', @showCodeEditor)
ed.on('SaveCodeEditor', @saveCodeEditor)
@imageModal.on('submitForm', @editImageSubmit) /*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Alignment": gettext("Alignment"),
editImage: (data) => /*
# Called when the image plugin will be shown. Input arg is the JSON version of the image data. Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
if data['src'] */
data['src'] = rewriteStaticLinks(data['src'], @base_asset_url, '/static/') "Alternative source": gettext("Alternative source"),
saveImage: (data) => /*
# Called when the image plugin is saved. Input arg is the JSON version of the image data. Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
if data['src'] */
data['src'] = rewriteStaticLinks(data['src'], '/static/', @base_asset_url) "Anchor": gettext("Anchor"),
openImageModal: () => /*
img = $(@visualEditor.selection.getNode()) Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
imgAttrs = */
baseAssetUrl: @base_asset_url "Anchors": gettext("Anchors"),
if img && img.is('img')
imgAttrs['src'] = rewriteStaticLinks(img.attr('src'), @base_asset_url, '/static/')
imgAttrs['alt'] = img.attr('alt')
imgAttrs['width'] = parseInt(img.attr('width'), 10) || img[0].naturalWidth
imgAttrs['height'] = parseInt(img.attr('height'), 10) || img[0].naturalHeight
imgAttrs['style'] = img.attr('style')
@imageModal[0].dispatchEvent(new CustomEvent('openModal', {bubbles: true, detail: imgAttrs}))
closeImageModal: () => /*
@imageModal[0].dispatchEvent(new CustomEvent('closeModal', {bubbles: true})) Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Author": gettext("Author"),
saveImageFromModal: (data) => /*
# Insert img node from studio-frontend modal form data passed as a javascript object Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
if data['src'] */
data['src'] = rewriteStaticLinks(data['src'], '/static/', @base_asset_url) "Background color": gettext("Background color"),
@visualEditor.insertContent(@visualEditor.dom.createHTML('img', data)) /*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Blockquote": gettext("Blockquote"),
editImageSubmit: (event) => /*
if event.detail Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
@saveImageFromModal(event.detail) */
"Blocks": gettext("Blocks"),
editLink: (data) => /*
# Called when the link plugin will be shown. Input arg is the JSON version of the link data. Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
if data['href'] */
data['href'] = rewriteStaticLinks(data['href'], @base_asset_url, '/static/') "Body": gettext("Body"),
saveLink: (data) => /*
# Called when the link plugin is saved. Input arg is the JSON version of the link data. Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
if data['href'] */
data['href'] = rewriteStaticLinks(data['href'], '/static/', @base_asset_url) "Bold": gettext("Bold"),
showCodeEditor: (source) => /*
# Called when the CodeMirror Editor is displayed to convert links to show static prefix. Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
# The input argument is a dict with the text content. */
content = rewriteStaticLinks(source.content, @base_asset_url, '/static/') "Border color": gettext("Border color"),
source.content = content
saveCodeEditor: (source) => /*
# Called when the CodeMirror Editor is saved to convert links back to the full form. Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
# The input argument is a dict with the text content. */
content = rewriteStaticLinks(source.content, '/static/', @base_asset_url) "Border": gettext("Border"),
source.content = content
initInstanceCallback: (visualEditor) => /*
visualEditor.setContent(rewriteStaticLinks(visualEditor.getContent({no_events: 1}), '/static/', @base_asset_url)) Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
# Unfortunately, just setting visualEditor.isNortDirty = true is not enough to convince TinyMCE we */
# haven't dirtied the Editor. Store the raw content so we can compare it later. "Bottom": gettext("Bottom"),
@starting_content = visualEditor.getContent({format:"raw", no_events: 1})
visualEditor.focus()
getVisualEditor: () -> /*
### Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
Returns the instance of TinyMCE. */
"Bullet list": gettext("Bullet list"),
Pulled out as a helper method for unit test. /*
### Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
return @visualEditor */
"Cancel": gettext("Cancel"),
save: -> /*
text = undefined Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
if @editor_choice == 'visual' */
visualEditor = @getVisualEditor() "Caption": gettext("Caption"),
raw_content = visualEditor.getContent({format:"raw", no_events: 1})
if @starting_content != raw_content
text = rewriteStaticLinks(visualEditor.getContent({no_events: 1}), @base_asset_url, '/static/')
if text == undefined /*
text = @advanced_editor.getValue() Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cell padding": gettext("Cell padding"),
data: text /*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cell properties": gettext("Cell properties"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cell spacing": gettext("Cell spacing"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cell type": gettext("Cell type"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cell": gettext("Cell"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Center": gettext("Center"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Circle": gettext("Circle"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Clear formatting": gettext("Clear formatting"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Close": gettext("Close"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Code block": gettext("Code block"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Code": gettext("Code"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Color": gettext("Color"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cols": gettext("Cols"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Column group": gettext("Column group"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Column": gettext("Column"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Constrain proportions": gettext("Constrain proportions"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Copy row": gettext("Copy row"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Copy": gettext("Copy"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Could not find the specified string.": gettext("Could not find the specified string."),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Custom color": gettext("Custom color"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Custom...": gettext("Custom..."),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cut row": gettext("Cut row"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Cut": gettext("Cut"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Decrease indent": gettext("Decrease indent"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Default": gettext("Default"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Delete column": gettext("Delete column"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Delete row": gettext("Delete row"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Delete table": gettext("Delete table"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Description": gettext("Description"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Dimensions": gettext("Dimensions"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Disc": gettext("Disc"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Div": gettext("Div"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Document properties": gettext("Document properties"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Edit HTML": gettext("Edit HTML"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Edit": gettext("Edit"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Embed": gettext("Embed"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Emoticons": gettext("Emoticons"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Encoding": gettext("Encoding"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"File": gettext("File"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Find and replace": gettext("Find and replace"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Find next": gettext("Find next"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Find previous": gettext("Find previous"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Find": gettext("Find"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Finish": gettext("Finish"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Font Family": gettext("Font Family"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Font Sizes": gettext("Font Sizes"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Footer": gettext("Footer"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Format": gettext("Format"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Formats": gettext("Formats"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Fullscreen": gettext("Fullscreen"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"General": gettext("General"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"H Align": gettext("H Align"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header 1": gettext("Header 1"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header 2": gettext("Header 2"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header 3": gettext("Header 3"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header 4": gettext("Header 4"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header 5": gettext("Header 5"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header 6": gettext("Header 6"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header cell": gettext("Header cell"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Header": gettext("Header"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Headers": gettext("Headers"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Heading 1": gettext("Heading 1"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Heading 2": gettext("Heading 2"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Heading 3": gettext("Heading 3"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Heading 4": gettext("Heading 4"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Heading 5": gettext("Heading 5"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Heading 6": gettext("Heading 6"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Headings": gettext("Headings"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Height": gettext("Height"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Horizontal line": gettext("Horizontal line"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Horizontal space": gettext("Horizontal space"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"HTML source code": gettext("HTML source code"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Ignore all": gettext("Ignore all"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Ignore": gettext("Ignore"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Image description": gettext("Image description"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Increase indent": gettext("Increase indent"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Inline": gettext("Inline"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert column after": gettext("Insert column after"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert column before": gettext("Insert column before"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert date/time": gettext("Insert date/time"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert image": gettext("Insert image"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert link": gettext("Insert link"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert row after": gettext("Insert row after"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert row before": gettext("Insert row before"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert table": gettext("Insert table"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert template": gettext("Insert template"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert video": gettext("Insert video"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert": gettext("Insert"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert/edit image": gettext("Insert/edit image"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert/edit link": gettext("Insert/edit link"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Insert/edit video": gettext("Insert/edit video"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Italic": gettext("Italic"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Justify": gettext("Justify"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Keywords": gettext("Keywords"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Left to right": gettext("Left to right"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Left": gettext("Left"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Lower Alpha": gettext("Lower Alpha"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Lower Greek": gettext("Lower Greek"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Lower Roman": gettext("Lower Roman"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Match case": gettext("Match case"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Merge cells": gettext("Merge cells"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Middle": gettext("Middle"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Name": gettext("Name"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"New document": gettext("New document"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"New window": gettext("New window"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Next": gettext("Next"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"No color": gettext("No color"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Nonbreaking space": gettext("Nonbreaking space"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"None": gettext("None"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Numbered list": gettext("Numbered list"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Ok": gettext("Ok"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"OK": gettext("OK"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Page break": gettext("Page break"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Paragraph": gettext("Paragraph"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Paste as text": gettext("Paste as text"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": gettext("Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off."),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Paste row after": gettext("Paste row after"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Paste row before": gettext("Paste row before"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Paste your embed code below:": gettext("Paste your embed code below:"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Paste": gettext("Paste"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Poster": gettext("Poster"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Pre": gettext("Pre"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Prev": gettext("Prev"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Preview": gettext("Preview"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Print": gettext("Print"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Redo": gettext("Redo"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Remove link": gettext("Remove link"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Replace all": gettext("Replace all"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Replace all": gettext("Replace all"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Replace with": gettext("Replace with"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Replace": gettext("Replace"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Replace": gettext("Replace"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Restore last draft": gettext("Restore last draft"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": gettext("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Right to left": gettext("Right to left"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Right": gettext("Right"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Robots": gettext("Robots"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Row group": gettext("Row group"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Row properties": gettext("Row properties"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Row type": gettext("Row type"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Row": gettext("Row"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Rows": gettext("Rows"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Save": gettext("Save"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Scope": gettext("Scope"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Select all": gettext("Select all"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Show blocks": gettext("Show blocks"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Show invisible characters": gettext("Show invisible characters"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Source code": gettext("Source code"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Source": gettext("Source"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Special character": gettext("Special character"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Spellcheck": gettext("Spellcheck"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Split cell": gettext("Split cell"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Square": gettext("Square"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Start search": gettext("Start search"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Strikethrough": gettext("Strikethrough"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Style": gettext("Style"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Subscript": gettext("Subscript"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Superscript": gettext("Superscript"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Table properties": gettext("Table properties"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Table": gettext("Table"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Target": gettext("Target"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Templates": gettext("Templates"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Text color": gettext("Text color"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Text to display": gettext("Text to display"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": gettext("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"The URL you entered seems to be an external link. Do you want to add the required http:// prefix?": gettext("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Title": gettext("Title"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Tools": gettext("Tools"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Top": gettext("Top"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Underline": gettext("Underline"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Undo": gettext("Undo"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Upper Alpha": gettext("Upper Alpha"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Upper Roman": gettext("Upper Roman"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Url": gettext("Url"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"V Align": gettext("V Align"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Vertical space": gettext("Vertical space"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"View": gettext("View"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Visual aids": gettext("Visual aids"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Whole words": gettext("Whole words"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Width": gettext("Width"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Words: {0}": gettext("Words: {0}"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"You have unsaved changes are you sure you want to navigate away?": gettext("You have unsaved changes are you sure you want to navigate away?"),
/*
Translators: this is a message from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.": gettext("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.")
});
}
}
HTMLEditingDescriptor.prototype.setupTinyMCE = function(ed) {
ed.addButton('wrapAsCode', {
/*
Translators: this is a toolbar button tooltip from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
title: gettext('Code block'),
image: baseUrl + "/images/ico-tinymce-code.png",
onclick: function() {
return ed.formatter.toggle('code');
}
});
ed.addButton('insertImage', {
/*
Translators: this is a toolbar button tooltip from the raw HTML editor displayed in the browser when a user needs to edit HTML
*/
title: gettext('Insert/Edit Image'),
icon: 'image',
onclick: this.openImageModal
});
this.visualEditor = ed;
this.imageModal = $('#edit-image-modal .modal');
/*
These events were added to the plugin code as the TinyMCE PluginManager
does not fire any events when plugins are opened or closed.
*/
ed.on('SaveImage', this.saveImage);
ed.on('EditImage', this.editImage);
ed.on('SaveLink', this.saveLink);
ed.on('EditLink', this.editLink);
ed.on('ShowCodeEditor', this.showCodeEditor);
ed.on('SaveCodeEditor', this.saveCodeEditor);
return this.imageModal.on('submitForm', this.editImageSubmit);
};
HTMLEditingDescriptor.prototype.editImage = function(data) {
/*
Called when the image plugin will be shown. Input arg is the JSON version of the image data.
*/
if (data['src']) {
return data['src'] = rewriteStaticLinks(data['src'], this.base_asset_url, '/static/');
}
};
HTMLEditingDescriptor.prototype.saveImage = function(data) {
/*
Called when the image plugin is saved. Input arg is the JSON version of the image data.
*/
if (data['src']) {
return data['src'] = rewriteStaticLinks(data['src'], '/static/', this.base_asset_url);
}
};
HTMLEditingDescriptor.prototype.openImageModal = function() {
var img, imgAttrs;
img = $(this.visualEditor.selection.getNode());
imgAttrs = {
baseAssetUrl: this.base_asset_url
};
if (img && img.is('img')) {
imgAttrs['src'] = rewriteStaticLinks(img.attr('src'), this.base_asset_url, '/static/');
imgAttrs['alt'] = img.attr('alt');
imgAttrs['width'] = parseInt(img.attr('width'), 10) || img[0].naturalWidth;
imgAttrs['height'] = parseInt(img.attr('height'), 10) || img[0].naturalHeight;
imgAttrs['style'] = img.attr('style');
}
return this.imageModal[0].dispatchEvent(new CustomEvent('openModal', {
bubbles: true,
detail: imgAttrs
}));
};
HTMLEditingDescriptor.prototype.closeImageModal = function() {
return this.imageModal[0].dispatchEvent(new CustomEvent('closeModal', {
bubbles: true
}));
};
HTMLEditingDescriptor.prototype.saveImageFromModal = function(data) {
/*
Insert img node from studio-frontend modal form data passed as a javascript object
*/
if (data['src']) {
data['src'] = rewriteStaticLinks(data['src'], '/static/', this.base_asset_url);
}
return this.visualEditor.insertContent(this.visualEditor.dom.createHTML('img', data));
};
HTMLEditingDescriptor.prototype.editImageSubmit = function(event) {
if (event.detail) {
return this.saveImageFromModal(event.detail);
}
};
HTMLEditingDescriptor.prototype.editLink = function(data) {
/*
Called when the link plugin will be shown. Input arg is the JSON version of the link data.
*/
if (data['href']) {
return data['href'] = rewriteStaticLinks(data['href'], this.base_asset_url, '/static/');
}
};
HTMLEditingDescriptor.prototype.saveLink = function(data) {
/*
Called when the link plugin is saved. Input arg is the JSON version of the link data.
*/
if (data['href']) {
return data['href'] = rewriteStaticLinks(data['href'], '/static/', this.base_asset_url);
}
};
HTMLEditingDescriptor.prototype.showCodeEditor = function(source) {
/*
Called when the CodeMirror Editor is displayed to convert links to show static prefix.
The input argument is a dict with the text content.
*/
var content;
content = rewriteStaticLinks(source.content, this.base_asset_url, '/static/');
return source.content = content;
};
HTMLEditingDescriptor.prototype.saveCodeEditor = function(source) {
/*
Called when the CodeMirror Editor is saved to convert links back to the full form.
The input argument is a dict with the text content.
*/
var content;
content = rewriteStaticLinks(source.content, '/static/', this.base_asset_url);
return source.content = content;
};
HTMLEditingDescriptor.prototype.initInstanceCallback = function(visualEditor) {
visualEditor.setContent(rewriteStaticLinks(visualEditor.getContent({
no_events: 1
}), '/static/', this.base_asset_url));
/*
Unfortunately, just setting visualEditor.isNortDirty = true is not enough to convince TinyMCE we
haven't dirtied the Editor. Store the raw content so we can compare it later.
*/
this.starting_content = visualEditor.getContent({
format: "raw",
no_events: 1
});
return visualEditor.focus();
};
HTMLEditingDescriptor.prototype.getVisualEditor = function() {
/*
Returns the instance of TinyMCE.
Pulled out as a helper method for unit test.
*/
return this.visualEditor;
};
HTMLEditingDescriptor.prototype.save = function() {
var raw_content, text, visualEditor;
text = void 0;
if (this.editor_choice === 'visual') {
visualEditor = this.getVisualEditor();
raw_content = visualEditor.getContent({
format: "raw",
no_events: 1
});
if (this.starting_content !== raw_content) {
text = rewriteStaticLinks(visualEditor.getContent({
no_events: 1
}), this.base_asset_url, '/static/');
}
}
if (text === void 0) {
text = this.advanced_editor.getValue();
}
return {
data: text
};
};
return HTMLEditingDescriptor;
})();
}).call(this);

View File

@@ -1,10 +1,32 @@
class @JSONEditingDescriptor extends XModule.Descriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
constructor: (@element) -> /* eslint-disable */
@edit_box = CodeMirror.fromTextArea($(".edit-box", @element)[0], { (function() {
mode: { name: "javascript", json: true } var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
lineNumbers: true hasProp = {}.hasOwnProperty;
lineWrapping: true
})
save: -> this.JSONEditingDescriptor = (function(superClass) {
data: JSON.parse @edit_box.getValue() extend(JSONEditingDescriptor, superClass);
function JSONEditingDescriptor(element) {
this.element = element;
this.edit_box = CodeMirror.fromTextArea($(".edit-box", this.element)[0], {
mode: {
name: "javascript",
json: true
},
lineNumbers: true,
lineWrapping: true
});
}
JSONEditingDescriptor.prototype.save = function() {
return {
data: JSON.parse(this.edit_box.getValue())
};
};
return JSONEditingDescriptor;
})(XModule.Descriptor);
}).call(this);

View File

@@ -1,5 +1,24 @@
class @MetadataOnlyEditingDescriptor extends XModule.Descriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
constructor: (@element) -> /* eslint-disable */
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
save: -> this.MetadataOnlyEditingDescriptor = (function(superClass) {
data: null extend(MetadataOnlyEditingDescriptor, superClass);
function MetadataOnlyEditingDescriptor(element) {
this.element = element;
}
MetadataOnlyEditingDescriptor.prototype.save = function() {
return {
data: null
};
};
return MetadataOnlyEditingDescriptor;
})(XModule.Descriptor);
}).call(this);

View File

@@ -1,10 +1,29 @@
class @XMLEditingDescriptor extends XModule.Descriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
constructor: (@element) -> /* eslint-disable */
@edit_box = CodeMirror.fromTextArea($(".edit-box", @element)[0], { (function() {
mode: "xml" var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
lineNumbers: true hasProp = {}.hasOwnProperty;
lineWrapping: true
})
save: -> this.XMLEditingDescriptor = (function(superClass) {
data: @edit_box.getValue() extend(XMLEditingDescriptor, superClass);
function XMLEditingDescriptor(element) {
this.element = element;
this.edit_box = CodeMirror.fromTextArea($(".edit-box", this.element)[0], {
mode: "xml",
lineNumbers: true,
lineWrapping: true
});
}
XMLEditingDescriptor.prototype.save = function() {
return {
data: this.edit_box.getValue()
};
};
return XMLEditingDescriptor;
})(XModule.Descriptor);
}).call(this);

View File

@@ -1,2 +1,18 @@
class @SequenceDescriptor extends XModule.Descriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
this.SequenceDescriptor = (function(superClass) {
extend(SequenceDescriptor, superClass);
function SequenceDescriptor() {
return SequenceDescriptor.__super__.constructor.apply(this, arguments);
}
return SequenceDescriptor;
})(XModule.Descriptor);
}).call(this);

View File

@@ -1,142 +1,190 @@
class @TabsEditingDescriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
@isInactiveClass : "is-inactive" /* eslint-disable */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
constructor: (element) -> this.TabsEditingDescriptor = (function() {
@element = element; TabsEditingDescriptor.isInactiveClass = "is-inactive";
###
Not tested on syncing of multiple editors of same type in tabs
(Like many CodeMirrors).
###
@$tabs = $(".tab", @element) function TabsEditingDescriptor(element) {
@$content = $(".component-tab", @element) this.onSwitchEditor = bind(this.onSwitchEditor, this);
var currentTab;
this.element = element;
@element.find('.editor-tabs .tab').each (index, value) => /*
$(value).on('click', @onSwitchEditor) Not tested on syncing of multiple editors of same type in tabs
(Like many CodeMirrors).
*/
this.$tabs = $(".tab", this.element);
this.$content = $(".component-tab", this.element);
this.element.find('.editor-tabs .tab').each((function(_this) {
return function(index, value) {
return $(value).on('click', _this.onSwitchEditor);
};
})(this));
# If default visible tab is not setted or if were marked as current /*
# more than 1 tab just first tab will be shown If default visible tab is not setted or if were marked as current
currentTab = @$tabs.filter('.current') more than 1 tab just first tab will be shown
currentTab = @$tabs.first() if currentTab.length isnt 1 */
@html_id = @$tabs.closest('.wrapper-comp-editor').data('html_id') currentTab = this.$tabs.filter('.current');
currentTab.trigger("click", [true, @html_id]) if (currentTab.length !== 1) {
currentTab = this.$tabs.first();
}
this.html_id = this.$tabs.closest('.wrapper-comp-editor').data('html_id');
currentTab.trigger("click", [true, this.html_id]);
}
onSwitchEditor: (e, firstTime, html_id) => TabsEditingDescriptor.prototype.onSwitchEditor = function(e, firstTime, html_id) {
e.preventDefault() var $currentTarget, content_id, isInactiveClass, onSwitchFunction, previousTab;
e.preventDefault();
isInactiveClass = TabsEditingDescriptor.isInactiveClass;
$currentTarget = $(e.currentTarget);
if (!$currentTarget.hasClass('current') || firstTime === true) {
previousTab = null;
this.$tabs.each(function(index, value) {
if ($(value).hasClass('current')) {
return previousTab = $(value).data('tab_name');
}
});
isInactiveClass = TabsEditingDescriptor.isInactiveClass /*
$currentTarget = $(e.currentTarget) init and save data from previous tab
*/
TabsEditingDescriptor.Model.updateValue(this.html_id, previousTab);
if not $currentTarget.hasClass('current') or firstTime is true /*
previousTab = null Save data from editor in previous tab to editor in current tab here.
(to be implemented when there is a use case for this functionality)
*/
@$tabs.each( (index, value) -> // call onswitch
if $(value).hasClass('current') onSwitchFunction = TabsEditingDescriptor.Model.modules[this.html_id].tabSwitch[$currentTarget.data('tab_name')];
previousTab = $(value).data('tab_name') if ($.isFunction(onSwitchFunction)) {
) onSwitchFunction();
}
this.$tabs.removeClass('current');
$currentTarget.addClass('current');
# init and save data from previous tab /*
TabsEditingDescriptor.Model.updateValue(@html_id, previousTab) Tabs are implemeted like anchors. Therefore we can use hash to find
corresponding content
*/
content_id = $currentTarget.attr('href');
return this.$content.addClass(isInactiveClass).filter(content_id).removeClass(isInactiveClass);
}
};
# Save data from editor in previous tab to editor in current tab here. TabsEditingDescriptor.prototype.save = function() {
# (to be implemented when there is a use case for this functionality) var current_tab;
this.element.off('click', '.editor-tabs .tab', this.onSwitchEditor);
current_tab = this.$tabs.filter('.current').data('tab_name');
return {
data: TabsEditingDescriptor.Model.getValue(this.html_id, current_tab)
};
};
# call onswitch TabsEditingDescriptor.prototype.setMetadataEditor = function(metadataEditor) {
onSwitchFunction = TabsEditingDescriptor.Model.modules[@html_id].tabSwitch[$currentTarget.data('tab_name')] return TabsEditingDescriptor.setMetadataEditor.apply(TabsEditingDescriptor, arguments);
onSwitchFunction() if $.isFunction(onSwitchFunction) };
@$tabs.removeClass('current') TabsEditingDescriptor.prototype.getStorage = function() {
$currentTarget.addClass('current') return TabsEditingDescriptor.getStorage();
};
# Tabs are implemeted like anchors. Therefore we can use hash to find TabsEditingDescriptor.prototype.addToStorage = function(id, data) {
# corresponding content return TabsEditingDescriptor.addToStorage.apply(TabsEditingDescriptor, arguments);
content_id = $currentTarget.attr('href') };
@$content TabsEditingDescriptor.Model = {
.addClass(isInactiveClass) addModelUpdate: function(id, tabName, modelUpdateFunction) {
.filter(content_id)
.removeClass(isInactiveClass)
save: -> /*
@element.off('click', '.editor-tabs .tab', @onSwitchEditor) Function that registers 'modelUpdate' functions of every tab.
current_tab = @$tabs.filter('.current').data('tab_name') These functions are used to update value, which will be returned
data: TabsEditingDescriptor.Model.getValue(@html_id, current_tab) by calling save on component.
*/
this.initialize(id);
return this.modules[id].modelUpdate[tabName] = modelUpdateFunction;
},
addOnSwitch: function(id, tabName, onSwitchFunction) {
setMetadataEditor : (metadataEditor) -> /*
TabsEditingDescriptor.setMetadataEditor.apply(TabsEditingDescriptor, arguments) Function that registers functions invoked when switching
to particular tab.
*/
this.initialize(id);
return this.modules[id].tabSwitch[tabName] = onSwitchFunction;
},
updateValue: function(id, tabName) {
getStorage : () -> /*
TabsEditingDescriptor.getStorage() Function that invokes when switching tabs.
It ensures that data from previous tab is stored.
If new tab need this data, it should retrieve it from
stored value.
*/
var modelUpdateFunction;
this.initialize(id);
modelUpdateFunction = this.modules[id]['modelUpdate'][tabName];
if ($.isFunction(modelUpdateFunction)) {
return this.modules[id]['value'] = modelUpdateFunction();
}
},
getValue: function(id, tabName) {
addToStorage : (id, data) -> /*
TabsEditingDescriptor.addToStorage.apply(TabsEditingDescriptor, arguments) Retrieves stored data on component save.
1. When we switching tabs - previous tab data is always saved to @[id].value
2. If current tab have registered 'modelUpdate' method, it should be invoked 1st.
(If we have edited in 1st tab, then switched to 2nd, 2nd tab should
care about getting data from @[id].value in onSwitch.)
*/
if (!this.modules[id]) {
return null;
}
if ($.isFunction(this.modules[id]['modelUpdate'][tabName])) {
return this.modules[id]['modelUpdate'][tabName]();
} else {
if (typeof this.modules[id]['value'] === 'undefined') {
return null;
} else {
return this.modules[id]['value'];
}
}
},
@Model : /*
addModelUpdate : (id, tabName, modelUpdateFunction) -> html_id's of descriptors will be stored in modules variable as
### containers for callbacks.
Function that registers 'modelUpdate' functions of every tab. */
These functions are used to update value, which will be returned modules: {},
by calling save on component. Storage: {},
### initialize: function(id) {
@initialize(id)
@modules[id].modelUpdate[tabName] = modelUpdateFunction
addOnSwitch : (id, tabName, onSwitchFunction) -> /*
### Initialize objects per id. Id is html_id of descriptor.
Function that registers functions invoked when switching */
to particular tab. this.modules[id] = this.modules[id] || {};
### this.modules[id].tabSwitch = this.modules[id]['tabSwitch'] || {};
@initialize(id) return this.modules[id].modelUpdate = this.modules[id]['modelUpdate'] || {};
@modules[id].tabSwitch[tabName] = onSwitchFunction }
};
updateValue : (id, tabName) -> TabsEditingDescriptor.setMetadataEditor = function(metadataEditor) {
### return TabsEditingDescriptor.Model.Storage['MetadataEditor'] = metadataEditor;
Function that invokes when switching tabs. };
It ensures that data from previous tab is stored.
If new tab need this data, it should retrieve it from
stored value.
###
@initialize(id)
modelUpdateFunction = @modules[id]['modelUpdate'][tabName]
@modules[id]['value'] = modelUpdateFunction() if $.isFunction(modelUpdateFunction)
getValue : (id, tabName) -> TabsEditingDescriptor.addToStorage = function(id, data) {
### return TabsEditingDescriptor.Model.Storage[id] = data;
Retrieves stored data on component save. };
1. When we switching tabs - previous tab data is always saved to @[id].value
2. If current tab have registered 'modelUpdate' method, it should be invoked 1st.
(If we have edited in 1st tab, then switched to 2nd, 2nd tab should
care about getting data from @[id].value in onSwitch.)
###
if not @modules[id]
return null
if $.isFunction(@modules[id]['modelUpdate'][tabName])
return @modules[id]['modelUpdate'][tabName]()
else
if typeof @modules[id]['value'] is 'undefined'
return null
else
return @modules[id]['value']
# html_id's of descriptors will be stored in modules variable as TabsEditingDescriptor.getStorage = function() {
# containers for callbacks. return TabsEditingDescriptor.Model.Storage;
modules: {} };
Storage: {}
initialize : (id) -> return TabsEditingDescriptor;
###
Initialize objects per id. Id is html_id of descriptor.
###
@modules[id] = @modules[id] or {}
@modules[id].tabSwitch = @modules[id]['tabSwitch'] or {}
@modules[id].modelUpdate = @modules[id]['modelUpdate'] or {}
@setMetadataEditor : (metadataEditor) -> })();
TabsEditingDescriptor.Model.Storage['MetadataEditor'] = metadataEditor
@addToStorage : (id, data) ->
TabsEditingDescriptor.Model.Storage[id] = data
@getStorage : () ->
TabsEditingDescriptor.Model.Storage
}).call(this);

View File

@@ -1,2 +1,18 @@
class @VerticalDescriptor extends XModule.Descriptor // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
this.VerticalDescriptor = (function(superClass) {
extend(VerticalDescriptor, superClass);
function VerticalDescriptor() {
return VerticalDescriptor.__super__.constructor.apply(this, arguments);
}
return VerticalDescriptor;
})(XModule.Descriptor);
}).call(this);

View File

@@ -409,7 +409,7 @@ class LibraryContentDescriptor(LibraryContentFields, MakoModuleDescriptor, XmlDe
module_class = LibraryContentModule module_class = LibraryContentModule
mako_template = 'widgets/metadata-edit.html' mako_template = 'widgets/metadata-edit.html'
js = {'coffee': [resource_string(__name__, 'js/src/vertical/edit.coffee')]} js = {'js': [resource_string(__name__, 'js/src/vertical/edit.js')]}
js_module_name = "VerticalDescriptor" js_module_name = "VerticalDescriptor"
show_in_read_only_mode = True show_in_read_only_mode = True

View File

@@ -642,7 +642,7 @@ class SequenceDescriptor(SequenceFields, ProctoringFields, MakoModuleDescriptor,
show_in_read_only_mode = True show_in_read_only_mode = True
js = { js = {
'coffee': [resource_string(__name__, 'js/src/sequence/edit.coffee')], 'js': [resource_string(__name__, 'js/src/sequence/edit.js')],
} }
js_module_name = "SequenceDescriptor" js_module_name = "SequenceDescriptor"

View File

@@ -92,8 +92,7 @@ class AnnotatableFields(object):
class TextAnnotationModule(AnnotatableFields, XModule): class TextAnnotationModule(AnnotatableFields, XModule):
''' Text Annotation Module ''' ''' Text Annotation Module '''
js = {'coffee': [], js = {'js': []}
'js': []}
css = {'scss': [resource_string(__name__, 'css/annotatable/display.scss')]} css = {'scss': [resource_string(__name__, 'css/annotatable/display.scss')]}
icon_class = 'textannotation' icon_class = 'textannotation'

View File

@@ -85,11 +85,9 @@ class AnnotatableFields(object):
class VideoAnnotationModule(AnnotatableFields, XModule): class VideoAnnotationModule(AnnotatableFields, XModule):
'''Video Annotation Module''' '''Video Annotation Module'''
js = { js = {
'coffee': [
resource_string(__name__, 'js/src/html/display.coffee'),
resource_string(__name__, 'js/src/annotatable/display.coffee'),
],
'js': [ 'js': [
resource_string(__name__, 'js/src/html/display.js'),
resource_string(__name__, 'js/src/annotatable/display.js'),
resource_string(__name__, 'js/src/javascript_loader.js'), resource_string(__name__, 'js/src/javascript_loader.js'),
resource_string(__name__, 'js/src/collapsible.js'), resource_string(__name__, 'js/src/collapsible.js'),
] ]

View File

@@ -1 +0,0 @@
*.js

View File

@@ -65,7 +65,6 @@ var commonFiles = {
{pattern: 'common/js/vendor/**/*.js'}, {pattern: 'common/js/vendor/**/*.js'},
{pattern: 'edx-pattern-library/js/**/*.js'}, {pattern: 'edx-pattern-library/js/**/*.js'},
{pattern: 'edx-ui-toolkit/js/**/*.js'}, {pattern: 'edx-ui-toolkit/js/**/*.js'},
{pattern: 'xmodule_js/common_static/coffee/src/**/!(*spec).js'},
{pattern: 'xmodule_js/common_static/common/js/**/!(*spec).js'}, {pattern: 'xmodule_js/common_static/common/js/**/!(*spec).js'},
{pattern: 'xmodule_js/common_static/js/**/!(*spec).js'}, {pattern: 'xmodule_js/common_static/js/**/!(*spec).js'},
{pattern: 'xmodule_js/src/**/*.js'} {pattern: 'xmodule_js/src/**/*.js'}

View File

@@ -26,7 +26,7 @@
'jquery.fileupload': 'js/vendor/jQuery-File-Upload/js/jquery.fileupload', 'jquery.fileupload': 'js/vendor/jQuery-File-Upload/js/jquery.fileupload',
'jquery.iframe-transport': 'js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', 'jquery.iframe-transport': 'js/vendor/jQuery-File-Upload/js/jquery.iframe-transport',
'jquery.inputnumber': 'js/vendor/html5-input-polyfills/number-polyfill', 'jquery.inputnumber': 'js/vendor/html5-input-polyfills/number-polyfill',
'jquery.immediateDescendents': 'coffee/src/jquery.immediateDescendents', 'jquery.immediateDescendents': 'js/src/jquery.immediateDescendents',
'jquery.simulate': 'js/vendor/jquery.simulate', 'jquery.simulate': 'js/vendor/jquery.simulate',
'jquery.url': 'js/vendor/url.min', 'jquery.url': 'js/vendor/url.min',
'sinon': 'common/js/vendor/sinon', 'sinon': 'common/js/vendor/sinon',

View File

@@ -3,7 +3,7 @@
padding: 10px; padding: 10px;
background-color: #fff; background-color: #fff;
/* keep font-family in sync with CUSTOM_FONTS constant in Html editor XModule /* keep font-family in sync with CUSTOM_FONTS constant in Html editor XModule
* (edx-platform/common/lib/xmodule/xmodule/js/src/html/edit.coffee) * (edx-platform/common/lib/xmodule/xmodule/js/src/html/edit.js)
* and with acceptance tests in cms/djangoapps/contentstore/features/html-editor.feature * and with acceptance tests in cms/djangoapps/contentstore/features/html-editor.feature
*/ */
font-family: 'Open Sans', Verdana, Arial, Helvetica, sans-serif; font-family: 'Open Sans', Verdana, Arial, Helvetica, sans-serif;

View File

@@ -1,16 +1,24 @@
@AjaxPrefix = // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
addAjaxPrefix: (jQuery, prefix) -> /* eslint-disable */
jQuery.postWithPrefix = (url, data, callback, type) -> (function() {
$.post("#{prefix()}#{url}", data, callback, type) this.AjaxPrefix = {
addAjaxPrefix: function(jQuery, prefix) {
jQuery.getWithPrefix = (url, data, callback, type) -> jQuery.postWithPrefix = function(url, data, callback, type) {
$.get("#{prefix()}#{url}", data, callback, type) return $.post("" + (prefix()) + url, data, callback, type);
};
jQuery.ajaxWithPrefix = (url, settings) -> jQuery.getWithPrefix = function(url, data, callback, type) {
if settings? return $.get("" + (prefix()) + url, data, callback, type);
$.ajax("#{prefix()}#{url}", settings) };
else return jQuery.ajaxWithPrefix = function(url, settings) {
settings = url if (settings != null) {
settings.url = "#{prefix()}#{settings.url}" return $.ajax("" + (prefix()) + url, settings);
$.ajax settings } else {
settings = url;
settings.url = "" + (prefix()) + settings.url;
return $.ajax(settings);
}
};
}
};
}).call(this);

View File

@@ -1,11 +1,23 @@
# Find all the children of an element that match the selector, but only // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
# the first instance found down any path. For example, we'll find all /* eslint-disable */
# the ".xblock" elements below us, but not the ones that are themselves
# contained somewhere inside ".xblock" elements. /*
jQuery.fn.immediateDescendents = (selector) -> Find all the children of an element that match the selector, but only
@children().map -> the first instance found down any path. For example, we'll find all
elem = jQuery(this) the ".xblock" elements below us, but not the ones that are themselves
if elem.is(selector) contained somewhere inside ".xblock" elements.
this */
else (function() {
elem.immediateDescendents(selector).get() jQuery.fn.immediateDescendents = function(selector) {
return this.children().map(function() {
var elem;
elem = jQuery(this);
if (elem.is(selector)) {
return this;
} else {
return elem.immediateDescendents(selector).get();
}
});
};
}).call(this);

View File

@@ -1,47 +1,76 @@
class XProblemGenerator // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
constructor: (seed, @parameters={}) -> (function() {
var XProblemDisplay, XProblemGenerator, XProblemGrader, root;
@random = new MersenneTwister(seed) XProblemGenerator = (function() {
function XProblemGenerator(seed, parameters) {
this.parameters = parameters != null ? parameters : {};
this.random = new MersenneTwister(seed);
this.problemState = {};
}
@problemState = {} XProblemGenerator.prototype.generate = function() {
return console.error("Abstract method called: XProblemGenerator.generate");
};
generate: () -> return XProblemGenerator;
console.error("Abstract method called: XProblemGenerator.generate") })();
class XProblemDisplay XProblemDisplay = (function() {
function XProblemDisplay(state, submission, evaluation, container, submissionField, parameters) {
this.state = state;
this.submission = submission;
this.evaluation = evaluation;
this.container = container;
this.submissionField = submissionField;
this.parameters = parameters != null ? parameters : {};
}
constructor: (@state, @submission, @evaluation, @container, @submissionField, @parameters={}) -> XProblemDisplay.prototype.render = function() {
return console.error("Abstract method called: XProblemDisplay.render");
};
render: () -> XProblemDisplay.prototype.updateSubmission = function() {
return this.submissionField.val(JSON.stringify(this.getCurrentSubmission()));
};
console.error("Abstract method called: XProblemDisplay.render") XProblemDisplay.prototype.getCurrentSubmission = function() {
return console.error("Abstract method called: XProblemDisplay.getCurrentSubmission");
};
updateSubmission: () -> return XProblemDisplay;
@submissionField.val(JSON.stringify(@getCurrentSubmission())) })();
getCurrentSubmission: () -> XProblemGrader = (function() {
console.error("Abstract method called: XProblemDisplay.getCurrentSubmission") function XProblemGrader(submission, problemState, parameters) {
this.submission = submission;
this.problemState = problemState;
this.parameters = parameters != null ? parameters : {};
this.solution = null;
this.evaluation = {};
}
class XProblemGrader XProblemGrader.prototype.solve = function() {
return console.error("Abstract method called: XProblemGrader.solve");
};
constructor: (@submission, @problemState, @parameters={}) -> XProblemGrader.prototype.grade = function() {
return console.error("Abstract method called: XProblemGrader.grade");
};
@solution = null return XProblemGrader;
@evaluation = {}
solve: () -> })();
console.error("Abstract method called: XProblemGrader.solve") root = typeof exports !== "undefined" && exports !== null ? exports : this;
grade: () -> root.XProblemGenerator = XProblemGenerator;
console.error("Abstract method called: XProblemGrader.grade") root.XProblemDisplay = XProblemDisplay;
root = exports ? this root.XProblemGrader = XProblemGrader;
root.XProblemGenerator = XProblemGenerator }).call(this);
root.XProblemDisplay = XProblemDisplay
root.XProblemGrader = XProblemGrader

View File

@@ -16,11 +16,11 @@ var options = {
// Avoid adding files to this list. Use RequireJS. // Avoid adding files to this list. Use RequireJS.
libraryFilesToInclude: [ libraryFilesToInclude: [
{pattern: 'coffee/src/ajax_prefix.js', included: true}, {pattern: 'js/src/ajax_prefix.js', included: true},
{pattern: 'js/vendor/draggabilly.js', included: true}, {pattern: 'js/vendor/draggabilly.js', included: true},
{pattern: 'common/js/vendor/jquery.js', included: true}, {pattern: 'common/js/vendor/jquery.js', included: true},
{pattern: 'common/js/vendor/jquery-migrate.js', included: true}, {pattern: 'common/js/vendor/jquery-migrate.js', included: true},
{pattern: 'coffee/src/jquery.immediateDescendents.js', included: true}, {pattern: 'js/src/jquery.immediateDescendents.js', included: true},
{pattern: 'js/vendor/jquery.leanModal.js', included: true}, {pattern: 'js/vendor/jquery.leanModal.js', included: true},
{pattern: 'js/vendor/jquery.timeago.js', included: true}, {pattern: 'js/vendor/jquery.timeago.js', included: true},
{pattern: 'js/vendor/jquery.truncate.js', included: true}, {pattern: 'js/vendor/jquery.truncate.js', included: true},
@@ -49,7 +49,6 @@ var options = {
// Make sure the patterns in sourceFiles and specFiles do not match the same file. // Make sure the patterns in sourceFiles and specFiles do not match the same file.
// Otherwise Istanbul which is used for coverage tracking will cause tests to not run. // Otherwise Istanbul which is used for coverage tracking will cause tests to not run.
sourceFiles: [ sourceFiles: [
{pattern: 'coffee/src/**/*.js', included: true},
{pattern: 'common/js/xblock/core.js', included: true}, {pattern: 'common/js/xblock/core.js', included: true},
{pattern: 'common/js/xblock/runtime.v1.js', included: true}, {pattern: 'common/js/xblock/runtime.v1.js', included: true},
{pattern: 'common/js/discussion/**/*.js', included: true}, {pattern: 'common/js/discussion/**/*.js', included: true},
@@ -58,7 +57,6 @@ var options = {
], ],
specFiles: [ specFiles: [
{pattern: 'coffee/spec/**/*.js', included: true},
{pattern: 'common/js/spec/xblock/*.js', included: true}, {pattern: 'common/js/spec/xblock/*.js', included: true},
{pattern: 'common/js/spec/discussion/**/*spec.js', included: true}, {pattern: 'common/js/spec/discussion/**/*spec.js', included: true},
{pattern: 'js/**/*spec.js', included: true} {pattern: 'js/**/*spec.js', included: true}

View File

@@ -16,7 +16,6 @@ var options = {
}, },
libraryFiles: [ libraryFiles: [
{pattern: 'coffee/src/**/*.js'},
{pattern: 'js/libs/**/*.js'}, {pattern: 'js/libs/**/*.js'},
{pattern: 'js/test/**/*.js'}, {pattern: 'js/test/**/*.js'},
{pattern: 'js/vendor/**/*.js'} {pattern: 'js/vendor/**/*.js'}

View File

@@ -66,9 +66,8 @@ out when files are missing (added when we started dynamically scanning XBlocks
for assets). for assets).
The ``django-pipeline`` config is aware of CSS files for the purposes of The ``django-pipeline`` config is aware of CSS files for the purposes of
concatenation, but it does *not* know about the source Sass files (or handful of concatenation, but it does *not* know about the source Sass files.
remaining CoffeeScript files). Those are processed with paver tasks before Those are processed with paver tasks before ``django-pipeline`` ever sees them.
``django-pipeline`` ever sees them.
We also have the following custom extensions to Django's builtin ``STATICFILES`` We also have the following custom extensions to Django's builtin ``STATICFILES``
mechanism: mechanism:
@@ -168,8 +167,7 @@ also be responsible for the optimization/minification of JavaScript assets, but
those optimized assets would only appear under the ``/webpack`` directory. Third those optimized assets would only appear under the ``/webpack`` directory. Third
party assets that Webpack is not aware of may have hash suffixes applied to them party assets that Webpack is not aware of may have hash suffixes applied to them
by the Django collectstatic layer, but will not otherwise be processed or by the Django collectstatic layer, but will not otherwise be processed or
optimized in any way -- so no coffeescript/sass compilation, no uglifyjs optimized in any way -- so no sass compilation, no uglifyjs minification, etc.
minification, etc.
The django-pipeline dependency should be removed altogether. The django-pipeline dependency should be removed altogether.

View File

@@ -92,10 +92,10 @@ Test Locations
- Javascript unit tests: Located in ``spec`` folders. For example, - Javascript unit tests: Located in ``spec`` folders. For example,
``common/lib/xmodule/xmodule/js/spec`` and ``common/lib/xmodule/xmodule/js/spec`` and
``{cms,lms}/static/coffee/spec`` For consistency, you should use the ``{cms,lms}/static/js/spec`` For consistency, you should use the
same directory structure for implementation and test. For example, same directory structure for implementation and test. For example,
the test for ``src/views/module.coffee`` should be written in the test for ``src/views/module.js`` should be written in
``spec/views/module_spec.coffee``. ``spec/views/module_spec.js``.
- UI acceptance tests: - UI acceptance tests:
@@ -151,8 +151,8 @@ For example, this command runs all the python test scripts::
paver test_python paver test_python
It also runs ``collectstatic``, which prepares the It also runs ``collectstatic``, which prepares the
static files used by the site (for example, compiling CoffeeScript to static files used by the site (for example, compiling Sass to
JavaScript). CSS).
You can re-run all failed python tests by running this command (see note at end of You can re-run all failed python tests by running this command (see note at end of
section):: section)::
@@ -332,7 +332,6 @@ To run a specific set of JavaScript tests and print the results to the
console, run these commands:: console, run these commands::
paver test_js_run -s lms paver test_js_run -s lms
paver test_js_run -s lms-coffee
paver test_js_run -s cms paver test_js_run -s cms
paver test_js_run -s cms-squire paver test_js_run -s cms-squire
paver test_js_run -s xmodule paver test_js_run -s xmodule
@@ -342,7 +341,6 @@ console, run these commands::
To run JavaScript tests in a browser, run these commands:: To run JavaScript tests in a browser, run these commands::
paver test_js_dev -s lms paver test_js_dev -s lms
paver test_js_dev -s lms-coffee
paver test_js_dev -s cms paver test_js_dev -s cms
paver test_js_dev -s cms-squire paver test_js_dev -s cms-squire
paver test_js_dev -s xmodule paver test_js_dev -s xmodule

View File

@@ -1241,7 +1241,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
] ]
# Provide human-friendly and translatable names for these features. These names # Provide human-friendly and translatable names for these features. These names
# will be displayed in the table generated in data_download.coffee. It is not (yet) # will be displayed in the table generated in data_download.js. It is not (yet)
# used as the header row in the CSV, but could be in the future. # used as the header row in the CSV, but could be in the future.
query_features_names = { query_features_names = {
'id': _('User ID'), 'id': _('User ID'),

View File

@@ -48,7 +48,7 @@ lms/djangoapps/notes:
Also requires: Also requires:
* lms/static/coffee/src/notes.coffee -- wrapper around annotator.js * lms/static/js/notes.js -- wrapper around annotator.js
* lms/templates/notes.html -- used by views.py to display the notes * lms/templates/notes.html -- used by views.py to display the notes
Interacts with: Interacts with:

View File

@@ -448,7 +448,6 @@ system_node_path = os.environ.get("NODE_PATH", NODE_MODULES_ROOT)
node_paths = [ node_paths = [
COMMON_ROOT / "static/js/vendor", COMMON_ROOT / "static/js/vendor",
COMMON_ROOT / "static/coffee/src",
system_node_path, system_node_path,
] ]
NODE_PATH = ':'.join(node_paths) NODE_PATH = ':'.join(node_paths)
@@ -1163,10 +1162,6 @@ EDXNOTES_READ_TIMEOUT = 1.5 # time in seconds
# if parental consent is never required. # if parental consent is never required.
PARENTAL_CONSENT_AGE_LIMIT = 13 PARENTAL_CONSENT_AGE_LIMIT = 13
################################# Jasmine ##################################
JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee'
######################### Branded Footer ################################### ######################### Branded Footer ###################################
# Constants for the footer used on the site and shared with other sites # Constants for the footer used on the site and shared with other sites
# (such as marketing and the blog) via the branding API. # (such as marketing and the blog) via the branding API.
@@ -1340,14 +1335,13 @@ PIPELINE_UGLIFYJS_BINARY = 'node_modules/.bin/uglifyjs'
from openedx.core.lib.rooted_paths import rooted_glob from openedx.core.lib.rooted_paths import rooted_glob
courseware_js = ( courseware_js = [
[ 'js/ajax-error.js',
'coffee/src/' + pth + '.js' 'js/courseware.js',
for pth in ['courseware', 'histogram', 'navigation'] 'js/histogram.js',
] + 'js/navigation.js',
['js/' + pth + '.js' for pth in ['ajax-error']] + 'js/modules/tab.js',
sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/modules/**/*.js')) ]
)
proctoring_js = ( proctoring_js = (
[ [
@@ -1428,9 +1422,9 @@ dashboard_js = (
) )
discussion_js = ( discussion_js = (
rooted_glob(COMMON_ROOT / 'static', 'common/js/discussion/mathjax_include.js') + rooted_glob(COMMON_ROOT / 'static', 'common/js/discussion/mathjax_include.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/customwmd.js') + rooted_glob(PROJECT_ROOT / 'static', 'js/customwmd.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/mathjax_accessible.js') + rooted_glob(PROJECT_ROOT / 'static', 'js/mathjax_accessible.js') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/mathjax_delay_renderer.js') + rooted_glob(PROJECT_ROOT / 'static', 'js/mathjax_delay_renderer.js') +
sorted(rooted_glob(COMMON_ROOT / 'static', 'common/js/discussion/**/*.js')) sorted(rooted_glob(COMMON_ROOT / 'static', 'common/js/discussion/**/*.js'))
) )
@@ -1445,7 +1439,7 @@ discussion_vendor_js = [
'js/split.js' 'js/split.js'
] ]
notes_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/notes/**/*.js')) notes_js = ['js/notes.js']
instructor_dash_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/instructor_dashboard/**/*.js')) instructor_dash_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/instructor_dashboard/**/*.js'))
verify_student_js = [ verify_student_js = [
@@ -1682,15 +1676,21 @@ PIPELINE_CSS = {
}, },
} }
common_js = [
separately_bundled_js = set(courseware_js + discussion_js + notes_js + instructor_dash_js) 'js/src/ajax_prefix.js',
common_js = sorted(set(rooted_glob(COMMON_ROOT / 'static', 'coffee/src/**/*.js')) - separately_bundled_js) 'js/src/jquery.immediateDescendents.js',
'js/src/xproblem.js',
]
xblock_runtime_js = [ xblock_runtime_js = [
'common/js/xblock/core.js', 'common/js/xblock/core.js',
'common/js/xblock/runtime.v1.js', 'common/js/xblock/runtime.v1.js',
'lms/js/xblock/lms.runtime.v1.js', 'lms/js/xblock/lms.runtime.v1.js',
] ]
lms_application_js = sorted(set(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/**/*.js')) - separately_bundled_js) lms_application_js = [
'js/calculator.js',
'js/feedback_form.js',
'js/main.js',
]
PIPELINE_JS = { PIPELINE_JS = {
'base_application': { 'base_application': {
@@ -1796,10 +1796,6 @@ STATICFILES_IGNORE_PATTERNS = (
"sass/*/*.scss", "sass/*/*.scss",
"sass/*/*/*.scss", "sass/*/*/*.scss",
"sass/*/*/*/*.scss", "sass/*/*/*/*.scss",
"coffee/*.coffee",
"coffee/*/*.coffee",
"coffee/*/*/*.coffee",
"coffee/*/*/*/*.coffee",
# Ignore tests # Ignore tests
"spec", "spec",

View File

@@ -1,44 +0,0 @@
CoffeeScript
============
This folder contains the CoffeeScript file that will be compiled to the static
directory. By default, we're compile and merge all the files ending `.coffee`
into `static/js/application.js`.
Install the Compiler
--------------------
CoffeeScript compiler are written in JavaScript. You'll need to install Node and
npm (Node Package Manager) to be able to install the CoffeeScript compiler.
### Mac OS X
Install Node via Homebrew, then use npm:
$ brew install node
$ curl http://npmjs.org/install.sh | sh
$ npm install -g git://github.com/jashkenas/coffee-script.git
(Note that we're using the edge version of CoffeeScript for now, as there was
some issue with directory watching in 1.3.1.)
Try to run `coffee` and make sure you get a coffee prompt.
### Debian/Ubuntu
Conveniently, you can install Node via `apt-get`, then use npm:
$ sudo apt-get install nodejs npm &&
$ sudo npm install -g git://github.com/jashkenas/coffee-script.git
Compiling
---------
CoffeeScript is compiled when you update assets using the command:
$ paver update_assets
Testing
-------
We use Jasmine to unit-test the JavaScript files. See `docs/en_us/internal/testing.rst` for details.

View File

@@ -1 +0,0 @@
*.js

View File

@@ -1,224 +1,274 @@
# Keyboard Support // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
# If focus is on the hint button: /*
# * Enter: Open or close hint popup. Select last focused hint item if opening Keyboard Support
# * Space: Open or close hint popup. Select last focused hint item if opening
# If focus is on a hint item: If focus is on the hint button:
# * Left arrow: Select previous hint item * Enter: Open or close hint popup. Select last focused hint item if opening
# * Up arrow: Select previous hint item * Space: Open or close hint popup. Select last focused hint item if opening
# * Right arrow: Select next hint item
# * Down arrow: Select next hint item
If focus is on a hint item:
* Left arrow: Select previous hint item
* Up arrow: Select previous hint item
* Right arrow: Select next hint item
* Down arrow: Select next hint item
*/
class @Calculator (function() {
constructor: -> this.Calculator = (function() {
@hintButton = $('#calculator_hint') function Calculator() {
@calcInput = $('#calculator_input') this.hintButton = $('#calculator_hint');
@hintPopup = $('.help') this.calcInput = $('#calculator_input');
@hintsList = @hintPopup.find('.hint-item') this.hintPopup = $('.help');
@selectHint($('#' + @hintPopup.attr('data-calculator-hint'))); this.hintsList = this.hintPopup.find('.hint-item');
this.selectHint($('#' + this.hintPopup.attr('data-calculator-hint')));
$('.calc').click(this.toggle);
$('form#calculator').submit(this.calculate).submit(function(e) {
return e.preventDefault();
});
this.hintButton.click($.proxy(this.handleClickOnHintButton, this));
this.hintPopup.click($.proxy(this.handleClickOnHintPopup, this));
this.hintPopup.keydown($.proxy(this.handleKeyDownOnHint, this));
$('#calculator_wrapper').keyup($.proxy(this.handleKeyUpOnHint, this));
this.handleClickOnDocument = $.proxy(this.handleClickOnDocument, this);
this.calcInput.focus($.proxy(this.inputClickHandler, this));
}
$('.calc').click @toggle Calculator.prototype.KEY = {
$('form#calculator').submit(@calculate).submit (e) -> TAB: 9,
e.preventDefault() ENTER: 13,
ESC: 27,
SPACE: 32,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40
};
@hintButton Calculator.prototype.toggle = function(event) {
.click(($.proxy(@handleClickOnHintButton, @))) var $calc, $calcWrapper, icon, isExpanded, text;
event.preventDefault();
$calc = $('.calc');
$calcWrapper = $('#calculator_wrapper');
text = gettext('Open Calculator');
isExpanded = false;
icon = 'fa-calculator';
$('.calc-main').toggleClass('open');
if ($calc.hasClass('closed')) {
$calcWrapper.attr('aria-hidden', 'true');
} else {
text = gettext('Close Calculator');
icon = 'fa-close';
isExpanded = true;
$calcWrapper.attr('aria-hidden', 'false');
@hintPopup /*
.click(($.proxy(@handleClickOnHintPopup, @))) TODO: Investigate why doing this without the timeout causes it to jump
down to the bottom of the page. I suspect it's because it's putting the
@hintPopup focus on the text field before it transitions onto the page.
.keydown($.proxy(@handleKeyDownOnHint, @)) */
setTimeout((function() {
$('#calculator_wrapper') return $calcWrapper.find('#calculator_input').focus();
.keyup($.proxy(@handleKeyUpOnHint, @)) }), 100);
}
@handleClickOnDocument = $.proxy(@handleClickOnDocument, @) $calc.attr({
'title': text,
@calcInput
.focus(($.proxy(@inputClickHandler, @)))
KEY:
TAB : 9
ENTER : 13
ESC : 27
SPACE : 32
LEFT : 37
UP : 38
RIGHT : 39
DOWN : 40
toggle: (event) ->
event.preventDefault()
$calc = $('.calc')
$calcWrapper = $('#calculator_wrapper')
text = gettext('Open Calculator')
isExpanded = false
icon = 'fa-calculator'
$('.calc-main').toggleClass 'open'
if $calc.hasClass('closed')
$calcWrapper
.attr('aria-hidden', 'true')
else
text = gettext('Close Calculator')
icon = 'fa-close'
isExpanded = true
$calcWrapper
.attr('aria-hidden', 'false')
# TODO: Investigate why doing this without the timeout causes it to jump
# down to the bottom of the page. I suspect it's because it's putting the
# focus on the text field before it transitions onto the page.
setTimeout (-> $calcWrapper.find('#calculator_input').focus()), 100
$calc
.attr
'title': text
'aria-expanded': isExpanded 'aria-expanded': isExpanded
.find('.utility-control-label').text text }).find('.utility-control-label').text(text);
$calc.find('.icon').removeClass('fa-calculator').removeClass('fa-close').addClass(icon);
$calc return $calc.toggleClass('closed');
.find('.icon') };
.removeClass('fa-calculator')
.removeClass('fa-close')
.addClass(icon)
$calc.toggleClass 'closed' Calculator.prototype.inputClickHandler = function() {
return $('#calculator_output').removeClass('has-result');
inputClickHandler: -> };
$('#calculator_output').removeClass('has-result')
showHint: -> Calculator.prototype.showHint = function() {
@hintPopup this.hintPopup.addClass('shown').attr('aria-hidden', false);
.addClass('shown') $('#calculator_output').removeClass('has-result');
.attr('aria-hidden', false) return $(document).on('click', this.handleClickOnDocument);
};
$('#calculator_output').removeClass('has-result') Calculator.prototype.hideHint = function() {
this.hintPopup.removeClass('shown').attr('aria-hidden', true);
$('#calculator_output').removeClass('has-result');
return $(document).off('click', this.handleClickOnDocument);
};
$(document).on('click', @handleClickOnDocument) Calculator.prototype.selectHint = function(element) {
if (!element || (element && element.length === 0)) {
element = this.hintsList.first();
}
this.activeHint = element;
this.activeHint.focus();
return this.hintPopup.attr('data-calculator-hint', element.attr('id'));
};
hideHint: -> Calculator.prototype.prevHint = function() {
@hintPopup
.removeClass('shown')
.attr('aria-hidden', true)
$('#calculator_output').removeClass('has-result')
$(document).off('click', @handleClickOnDocument) /*
the previous hint
*/
var prev;
prev = this.activeHint.prev();
selectHint: (element) -> /*
if not element or (element and element.length == 0) if this was the first item
element = @hintsList.first() select the last one in the group.
*/
if (this.activeHint.index() === 0) {
prev = this.hintsList.last();
}
@activeHint = element; /*
@activeHint.focus(); select the previous hint
@hintPopup.attr('data-calculator-hint', element.attr('id')); */
return this.selectHint(prev);
};
prevHint: () -> Calculator.prototype.nextHint = function() {
prev = @activeHint.prev(); # the previous hint
# if this was the first item
# select the last one in the group.
if @activeHint.index() == 0
prev = @hintsList.last()
# select the previous hint
@selectHint(prev)
nextHint: () -> /*
next = @activeHint.next(); # the next hint the next hint
# if this was the last item, */
# select the first one in the group. var next;
if @activeHint.index() == @hintsList.length - 1 next = this.activeHint.next();
next = @hintsList.first()
# give the next hint focus
@selectHint(next)
handleKeyDown: (e) -> /*
if e.altKey if this was the last item,
# do nothing select the first one in the group.
return true */
if (this.activeHint.index() === this.hintsList.length - 1) {
next = this.hintsList.first();
}
if e.keyCode == @KEY.ENTER or e.keyCode == @KEY.SPACE /*
if @hintPopup.hasClass 'shown' give the next hint focus
@hideHint() */
else return this.selectHint(next);
@showHint() };
@activeHint.focus()
e.preventDefault() Calculator.prototype.handleKeyDown = function(e) {
return false if (e.altKey) {
# allow the event to propagate /*
return true do nothing
*/
return true;
}
if (e.keyCode === this.KEY.ENTER || e.keyCode === this.KEY.SPACE) {
if (this.hintPopup.hasClass('shown')) {
this.hideHint();
} else {
this.showHint();
this.activeHint.focus();
}
e.preventDefault();
return false;
}
handleKeyDownOnHint: (e) -> /*
if e.altKey allow the event to propagate
# do nothing */
return true return true;
};
switch e.keyCode Calculator.prototype.handleKeyDownOnHint = function(e) {
if (e.altKey) {
when @KEY.ESC /*
# hide popup with hints do nothing
@hideHint() */
@hintButton.focus() return true;
}
switch (e.keyCode) {
case this.KEY.ESC:
e.stopPropagation() /*
return false hide popup with hints
*/
this.hideHint();
this.hintButton.focus();
e.stopPropagation();
return false;
case this.KEY.LEFT:
case this.KEY.UP:
if (e.shiftKey) {
when @KEY.LEFT, @KEY.UP /*
if e.shiftKey do nothing
# do nothing */
return true }
return true;
this.prevHint();
e.stopPropagation();
return false;
case this.KEY.RIGHT:
case this.KEY.DOWN:
if (e.shiftKey) {
@prevHint() /*
do nothing
*/
return true;
}
this.nextHint();
e.stopPropagation();
return false;
}
e.stopPropagation() /*
return false allow the event to propagate
*/
return true;
};
when @KEY.RIGHT, @KEY.DOWN Calculator.prototype.handleKeyUpOnHint = function(e) {
if e.shiftKey switch (e.keyCode) {
# do nothing case this.KEY.TAB:
return true
@nextHint() /*
move focus to hint links and hide hint once focus is out of hint pop up
*/
this.active_element = document.activeElement;
if (!$(this.active_element).parents().is(this.hintPopup)) {
return this.hideHint();
}
}
};
e.stopPropagation() Calculator.prototype.handleClickOnDocument = function(e) {
return false return this.hideHint();
};
# allow the event to propagate Calculator.prototype.handleClickOnHintButton = function(e) {
return true e.preventDefault();
e.stopPropagation();
if (this.hintPopup.hasClass('shown')) {
this.hideHint();
return this.hintButton.attr('aria-expanded', false);
} else {
this.showHint();
this.hintButton.attr('aria-expanded', true);
return this.activeHint.focus();
}
};
handleKeyUpOnHint: (e) -> Calculator.prototype.handleClickOnHintPopup = function(e) {
switch e.keyCode return e.stopPropagation();
when @KEY.TAB };
# move focus to hint links and hide hint once focus is out of hint pop up
@active_element = document.activeElement
if not $(@active_element).parents().is(@hintPopup)
@hideHint()
handleClickOnDocument: (e) -> Calculator.prototype.calculate = function() {
@hideHint() return $.getWithPrefix('/calculate', {
equation: $('#calculator_input').val()
}, function(data) {
return $('#calculator_output').val(data.result).addClass('has-result').focus();
});
};
handleClickOnHintButton: (e) -> return Calculator;
e.preventDefault()
e.stopPropagation()
if @hintPopup.hasClass 'shown'
@hideHint()
@hintButton.attr('aria-expanded', false)
else
@showHint()
@hintButton.attr('aria-expanded', true)
@activeHint.focus()
handleClickOnHintPopup: (e) -> })();
e.stopPropagation()
calculate: -> }).call(this);
$.getWithPrefix '/calculate', { equation: $('#calculator_input').val() }, (data) ->
$('#calculator_output')
.val(data.result)
.addClass('has-result')
.focus()

View File

@@ -1,21 +1,38 @@
class @Courseware // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
@prefix: '' /* eslint-disable */
(function() {
this.Courseware = (function() {
Courseware.prefix = '';
constructor: -> function Courseware() {
Logger.bind() Logger.bind();
@render() this.render();
}
@start: -> Courseware.start = function() {
new Courseware return new Courseware;
};
render: -> Courseware.prototype.render = function() {
XBlock.initializeBlocks($('.course-content')) XBlock.initializeBlocks($('.course-content'));
$('.course-content .histogram').each -> return $('.course-content .histogram').each(function() {
id = $(this).attr('id').replace(/histogram_/, '') var error, histg, id;
try id = $(this).attr('id').replace(/histogram_/, '');
histg = new Histogram id, $(this).data('histogram') try {
catch error histg = new Histogram(id, $(this).data('histogram'));
histg = error } catch (_error) {
if console? error = _error;
console.log(error) histg = error;
return histg if (typeof console !== "undefined" && console !== null) {
console.log(error);
}
}
return histg;
});
};
return Courseware;
})();
}).call(this);

View File

@@ -1,181 +1,247 @@
# Mostly adapted from math.stackexchange.com: http://cdn.sstatic.net/js/mathjax-editing-new.js // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
// TODO: Examine all of the xss-lint exceptions (https://openedx.atlassian.net/browse/PLAT-2084)
class MathJaxProcessor /*
Mostly adapted from math.stackexchange.com: http://cdn.sstatic.net/js/mathjax-editing-new.js
*/
(function() {
var MathJaxProcessor;
MATHSPLIT = /// ( MathJaxProcessor = (function() {
\$\$? # normal inline or display delimiter
| \\(?:begin|end)\{[a-z]*\*?\} # \begin{} \end{} style
| \\[\\{}$]
| [{}]
| (?:\n\s*)+ # only treat as math when there's single new line
| @@\d+@@ # delimiter similar to the one used internally
) ///i
CODESPAN = /// var CODESPAN, MATHSPLIT;
(^|[^\\]) # match beginning or any previous character other than escape delimiter ('/')
(`+) # code span starts
([^\n]*?[^`\n]) # code content
\2 # code span ends
(?!`)
///gm
constructor: (inlineMark, displayMark) -> /*
@inlineMark = inlineMark || "$" \$\$? # normal inline or display delimiter
@displayMark = displayMark || "$$" | \\(?:begin|end)\{[a-z]*\*?\} # \begin{} \end{} style
@math = null | \\[\\{}$]
@blocks = null | [{}]
| (?:\n\s*)+ # only treat as math when there's single new line
| @@\d+@@ # delimiter similar to the one used internally
*/
MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
processMath: (start, last, preProcess) ->
block = @blocks.slice(start, last + 1).join("").replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
if MathJax.Hub.Browser.isMSIE
block = block.replace /(%[^\n]*)\n/g, "$1<br/>\n"
@blocks[i] = "" for i in [start+1..last]
@blocks[start] = "@@#{@math.length}@@"
block = preProcess(block) if preProcess
@math.push block
removeMath: (text) -> /*
(^|[^\\]) # match beginning or any previous character other than escape delimiter ('/')
(`+) # code span starts
([^\n]*?[^`\n]) # code content
\2 # code span ends
(?!`)
*/
CODESPAN = /(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm;
text = text || "" function MathJaxProcessor(inlineMark, displayMark) {
@math = [] this.inlineMark = inlineMark || "$";
start = end = last = null this.displayMark = displayMark || "$$";
braces = 0 this.math = null;
this.blocks = null;
}
hasCodeSpans = /`/.test text MathJaxProcessor.prototype.processMath = function(start, last, preProcess) {
if hasCodeSpans var block, i, j, ref, ref1;
text = text.replace(/~/g, "~T").replace CODESPAN, ($0) -> # replace dollar sign in code span temporarily block = this.blocks.slice(start, last + 1).join("").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
$0.replace /\$/g, "~D" if (MathJax.Hub.Browser.isMSIE) {
deTilde = (text) -> block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n");
text.replace /~([TD])/g, ($0, $1) -> }
{T: "~", D: "$"}[$1] for (i = j = ref = start + 1, ref1 = last; ref <= ref1 ? j <= ref1 : j >= ref1; i = ref <= ref1 ? ++j : --j) {
else this.blocks[i] = "";
deTilde = (text) -> text }
this.blocks[start] = "@@" + this.math.length + "@@";
if (preProcess) {
block = preProcess(block);
}
return this.math.push(block);
};
@blocks = _split(text.replace(/\r\n?/g, "\n"), MATHSPLIT) MathJaxProcessor.prototype.removeMath = function(text) {
var block, braces, current, deTilde, end, hasCodeSpans, j, last, ref, start;
text = text || "";
this.math = [];
start = end = last = null;
braces = 0;
hasCodeSpans = /`/.test(text);
if (hasCodeSpans) {
for current in [1...@blocks.length] by 2 /*
block = @blocks[current] replace dollar sign in code span temporarily
if block.charAt(0) == "@" */
@blocks[current] = "@@#{@math.length}@@" text = text.replace(/~/g, "~T").replace(CODESPAN, function($0) {
@math.push block return $0.replace(/\$/g, "~D");
else if start });
if block == end deTilde = function(text) {
if braces return text.replace(/~([TD])/g, function($0, $1) {
last = current return {
else T: "~",
@processMath(start, current, deTilde) D: "$"
start = end = last = null }[$1];
else if block.match /\n.*\n/ });
if last };
current = last } else {
@processMath(start, current, deTilde) deTilde = function(text) {
start = end = last = null return text;
braces = 0 };
else if block == "{" }
++braces this.blocks = _split(text.replace(/\r\n?/g, "\n"), MATHSPLIT);
else if block == "}" and braces for (current = j = 1, ref = this.blocks.length; j < ref; current = j += 2) {
--braces block = this.blocks[current];
else if (block.charAt(0) === "@") {
if block == @inlineMark or block == @displayMark this.blocks[current] = "@@" + this.math.length + "@@";
start = current this.math.push(block);
end = block } else if (start) {
braces = 0 if (block === end) {
else if block.substr(1, 5) == "begin" if (braces) {
start = current last = current;
end = "\\end" + block.substr(6) } else {
braces = 0 this.processMath(start, current, deTilde);
start = end = last = null;
}
} else if (block.match(/\n.*\n/)) {
if (last) {
current = last;
this.processMath(start, current, deTilde);
}
start = end = last = null;
braces = 0;
} else if (block === "{") {
++braces;
} else if (block === "}" && braces) {
--braces;
}
} else {
if (block === this.inlineMark || block === this.displayMark) {
start = current;
end = block;
braces = 0;
} else if (block.substr(1, 5) === "begin") {
start = current;
end = "\\end" + block.substr(6);
braces = 0;
}
}
}
if (last) {
this.processMath(start, last, deTilde);
start = end = last = null;
}
return deTilde(this.blocks.join(""));
};
if last MathJaxProcessor.removeMathWrapper = function(_this) {
@processMath(start, last, deTilde) return function(text) {
start = end = last = null return _this.removeMath(text);
};
};
deTilde(@blocks.join("")) MathJaxProcessor.prototype.replaceMath = function(text) {
text = text.replace(/@@(\d+)@@/g, (function(_this) {
return function($0, $1) {
return _this.math[$1];
};
})(this));
this.math = null;
return text;
};
@removeMathWrapper: (_this) -> MathJaxProcessor.replaceMathWrapper = function(_this) {
(text) -> _this.removeMath(text) return function(text) {
return _this.replaceMath(text);
};
};
replaceMath: (text) -> return MathJaxProcessor;
text = text.replace /@@(\d+)@@/g, ($0, $1) => @math[$1]
@math = null
text
@replaceMathWrapper: (_this) -> })();
(text) -> _this.replaceMath(text)
if Markdown? if (typeof Markdown !== "undefined" && Markdown !== null) {
Markdown.getMathCompatibleConverter = function(postProcessor) {
var converter, processor;
postProcessor || (postProcessor = (function(text) {
return text;
}));
converter = Markdown.getSanitizingConverter();
if (typeof MathJax !== "undefined" && MathJax !== null) {
processor = new MathJaxProcessor();
converter.hooks.chain("preConversion", MathJaxProcessor.removeMathWrapper(processor));
converter.hooks.chain("postConversion", function(text) {
return postProcessor(MathJaxProcessor.replaceMathWrapper(processor)(text));
});
}
return converter;
};
Markdown.makeWmdEditor = function(elem, appended_id, imageUploadUrl, postProcessor) {
var $elem, $wmdPanel, $wmdPreviewContainer, _append, ajaxFileUpload, converter, delayRenderer, editor, imageUploadHandler, initialText, wmdInputId;
$elem = $(elem);
if (!$elem.length) {
console.log("warning: elem for makeWmdEditor doesn't exist");
return;
}
if (!$elem.find(".wmd-panel").length) {
initialText = $elem.html();
$elem.empty();
_append = appended_id || "";
wmdInputId = "wmd-input" + _append;
$wmdPreviewContainer = $("<div>").addClass("wmd-preview-container").attr("role", "region").attr("aria-label", gettext("HTML preview of post")).append($("<div>").addClass("wmd-preview-label").text(gettext("Preview"))).append($("<div>").attr("id", "wmd-preview" + _append).addClass("wmd-panel wmd-preview"));
$wmdPanel = $("<div>").addClass("wmd-panel").append($("<div>").attr("id", "wmd-button-bar" + _append)).append($("<label>").addClass("sr").attr("for", wmdInputId).text(gettext("Your question or idea (required)"))).append($("<textarea>").addClass("wmd-input").attr("id", wmdInputId).html(initialText)).append($wmdPreviewContainer); // xss-lint: disable=javascript-jquery-html
$elem.append($wmdPanel);
}
converter = Markdown.getMathCompatibleConverter(postProcessor);
ajaxFileUpload = function(imageUploadUrl, input, startUploadHandler) {
$("#loading").ajaxStart(function() {
return $(this).show();
}).ajaxComplete(function() {
return $(this).hide();
});
$("#upload").ajaxStart(function() {
return $(this).hide();
}).ajaxComplete(function() {
return $(this).show();
});
return $.ajaxFileUpload({
url: imageUploadUrl,
secureuri: false,
fileElementId: 'file-upload',
dataType: 'json',
success: function(data, status) {
var error, fileURL;
fileURL = data['result']['file_url'];
error = data['result']['error'];
if (error !== '') {
alert(error);
if (startUploadHandler) {
$('#file-upload').unbind('change').change(startUploadHandler);
}
return console.log(error);
} else {
return $(input).attr('value', fileURL);
}
},
error: function(data, status, e) {
alert(e);
if (startUploadHandler) {
return $('#file-upload').unbind('change').change(startUploadHandler);
}
}
});
};
imageUploadHandler = function(elem, input) {
return ajaxFileUpload(imageUploadUrl, input, imageUploadHandler);
};
editor = new Markdown.Editor(converter,
appended_id, // idPostfix
null, // help handler
imageUploadHandler);
delayRenderer = new MathJaxDelayRenderer();
editor.hooks.chain("onPreviewPush", function(text, previewSet) {
return delayRenderer.render({
text: text,
previewSetter: previewSet
});
});
editor.run();
return editor;
};
}
Markdown.getMathCompatibleConverter = (postProcessor) -> }).call(this);
postProcessor ||= ((text) -> text)
converter = Markdown.getSanitizingConverter()
if MathJax?
processor = new MathJaxProcessor()
converter.hooks.chain "preConversion", MathJaxProcessor.removeMathWrapper(processor)
converter.hooks.chain "postConversion", (text) ->
postProcessor(MathJaxProcessor.replaceMathWrapper(processor)(text))
converter
Markdown.makeWmdEditor = (elem, appended_id, imageUploadUrl, postProcessor) ->
$elem = $(elem)
if not $elem.length
console.log "warning: elem for makeWmdEditor doesn't exist"
return
if not $elem.find(".wmd-panel").length
initialText = $elem.html()
$elem.empty()
_append = appended_id || ""
wmdInputId = "wmd-input#{_append}"
$wmdPreviewContainer = $("<div>").addClass("wmd-preview-container")
.attr("role", "region")
.attr("aria-label", gettext("HTML preview of post"))
.append($("<div>").addClass("wmd-preview-label").text(gettext("Preview")))
.append($("<div>").attr("id", "wmd-preview#{_append}").addClass("wmd-panel wmd-preview"))
$wmdPanel = $("<div>").addClass("wmd-panel")
.append($("<div>").attr("id", "wmd-button-bar#{_append}"))
.append($("<label>").addClass("sr").attr("for", wmdInputId).text(gettext("Your question or idea (required)")))
.append($("<textarea>").addClass("wmd-input").attr("id", wmdInputId).html(initialText))
.append($wmdPreviewContainer)
$elem.append($wmdPanel)
converter = Markdown.getMathCompatibleConverter(postProcessor)
ajaxFileUpload = (imageUploadUrl, input, startUploadHandler) ->
$("#loading").ajaxStart(-> $(this).show()).ajaxComplete(-> $(this).hide())
$("#upload").ajaxStart(-> $(this).hide()).ajaxComplete(-> $(this).show())
$.ajaxFileUpload
url: imageUploadUrl
secureuri: false
fileElementId: 'file-upload'
dataType: 'json'
success: (data, status) ->
fileURL = data['result']['file_url']
error = data['result']['error']
if error != ''
alert error
if startUploadHandler
$('#file-upload').unbind('change').change(startUploadHandler)
console.log error
else
$(input).attr('value', fileURL)
error: (data, status, e) ->
alert(e)
if startUploadHandler
$('#file-upload').unbind('change').change(startUploadHandler)
imageUploadHandler = (elem, input) ->
ajaxFileUpload(imageUploadUrl, input, imageUploadHandler)
editor = new Markdown.Editor(
converter,
appended_id, # idPostfix
null, # help handler
imageUploadHandler
)
delayRenderer = new MathJaxDelayRenderer()
editor.hooks.chain "onPreviewPush", (text, previewSet) ->
delayRenderer.render
text: text
previewSetter: previewSet
editor.run()
editor

View File

@@ -1,10 +1,23 @@
class @FeedbackForm // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
constructor: -> /* eslint-disable */
$('#feedback_button').click -> (function() {
data = this.FeedbackForm = (function() {
subject: $('#feedback_subject').val() function FeedbackForm() {
message: $('#feedback_message').val() $('#feedback_button').click(function() {
url: window.location.href var data;
$.postWithPrefix '/send_feedback', data, -> data = {
$('#feedback_div').html 'Feedback submitted. Thank you' subject: $('#feedback_subject').val(),
,'json' message: $('#feedback_message').val(),
url: window.location.href
};
return $.postWithPrefix('/send_feedback', data, function() {
return $('#feedback_div').html('Feedback submitted. Thank you'); // xss-lint: disable=javascript-jquery-html
}, 'json');
});
}
return FeedbackForm;
})();
}).call(this);

View File

@@ -1,36 +1,68 @@
class @Histogram // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
constructor: (@id, @rawData) -> /* eslint-disable */
@xTicks = [] (function() {
@yTicks = [] this.Histogram = (function() {
@data = [] function Histogram(id, rawData) {
@calculate() this.id = id;
@render() this.rawData = rawData;
this.xTicks = [];
this.yTicks = [];
this.data = [];
this.calculate();
this.render();
}
calculate: -> Histogram.prototype.calculate = function() {
for [score, count] in @rawData var count, i, len, log_count, ref, ref1, results, score;
continue if score == null ref = this.rawData;
log_count = Math.log(count + 1) results = [];
@data.push [score, log_count] for (i = 0, len = ref.length; i < len; i++) {
@xTicks.push [score, score.toString()] ref1 = ref[i], score = ref1[0], count = ref1[1];
@yTicks.push [log_count, count.toString()] if (score === null) {
continue;
}
log_count = Math.log(count + 1);
this.data.push([score, log_count]);
this.xTicks.push([score, score.toString()]);
results.push(this.yTicks.push([log_count, count.toString()]));
}
return results;
};
render: -> Histogram.prototype.render = function() {
$.plot $("#histogram_#{@id}"), [ return $.plot($("#histogram_" + this.id), [
data: @data {
bars: data: this.data,
show: true bars: {
align: 'center' show: true,
lineWidth: 0 align: 'center',
fill: 1.0 lineWidth: 0,
color: "#b72121" fill: 1.0
], },
xaxis: color: "#b72121"
min: -1 }
max: Math.max.apply Math, $.map(@xTicks, (data) -> data[0] + 1) ], {
ticks: @xTicks xaxis: {
tickLength: 0 min: -1,
yaxis: max: Math.max.apply(Math, $.map(this.xTicks, function(data) {
min: 0.0 return data[0] + 1;
max: Math.max.apply Math, $.map(@yTicks, (data) -> data[0] * 1.1) })),
ticks: @yTicks ticks: this.xTicks,
labelWidth: 50 tickLength: 0
},
yaxis: {
min: 0.0,
max: Math.max.apply(Math, $.map(this.yTicks, function(data) {
return data[0] * 1.1;
})),
ticks: this.yTicks,
labelWidth: 50
}
});
};
return Histogram;
})();
}).call(this);

View File

@@ -6,7 +6,7 @@ The instructor dashboard is broken into sections.
Only one section is visible at a time, Only one section is visible at a time,
and is responsible for its own functionality. and is responsible for its own functionality.
NOTE: plantTimeout (which is just setTimeout from util.coffee) NOTE: plantTimeout (which is just setTimeout from util.js)
is used frequently in the instructor dashboard to isolate is used frequently in the instructor dashboard to isolate
failures. If one piece of code under a plantTimeout fails failures. If one piece of code under a plantTimeout fails
then it will not crash the rest of the dashboard. then it will not crash the rest of the dashboard.
@@ -19,7 +19,7 @@ NOTE: Server endpoints in the dashboard are stored in
The urls are rendered there by a template. The urls are rendered there by a template.
NOTE: For an example of what a section object should look like NOTE: For an example of what a section object should look like
see course_info.coffee see course_info.js
imports from other modules imports from other modules
wrap in (-> ... apply) to defer evaluation wrap in (-> ... apply) to defer evaluation

View File

@@ -1,38 +1,64 @@
AjaxPrefix.addAjaxPrefix(jQuery, -> $("meta[name='path_prefix']").attr('content')) // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
/* eslint-disable */
(function() {
AjaxPrefix.addAjaxPrefix(jQuery, function() {
return $("meta[name='path_prefix']").attr('content');
});
$ -> $(function() {
$.ajaxSetup $.ajaxSetup({
headers : { 'X-CSRFToken': $.cookie 'csrftoken' } headers: {
dataType: 'json' 'X-CSRFToken': $.cookie('csrftoken')
},
dataType: 'json'
});
window.onTouchBasedDevice = function() {
return navigator.userAgent.match(/iPhone|iPod|iPad|Android/i);
};
if (onTouchBasedDevice()) {
$('body').addClass('touch-based-device');
}
window.onTouchBasedDevice = -> /*
navigator.userAgent.match /iPhone|iPod|iPad|Android/i $("a[rel*=leanModal]").leanModal()
*/
$('#csrfmiddlewaretoken').attr('value', $.cookie('csrftoken'));
new Calculator;
new FeedbackForm;
if ($('body').hasClass('courseware')) {
Courseware.start();
}
window.postJSON = function(url, data, callback) {
return $.postWithPrefix(url, data, callback);
};
$('#login').click(function() {
$('#login_form input[name="email"]').focus();
return false;
});
$('#signup').click(function() {
$('#signup-modal input[name="email"]').focus();
return false;
});
$('body').addClass 'touch-based-device' if onTouchBasedDevice() /*
fix for ie
*/
if (!Array.prototype.indexOf) {
return Array.prototype.indexOf = function(obj, start) {
var ele, i, j, len, ref;
if (start == null) {
start = 0;
}
ref = this.slice(start);
for (i = j = 0, len = ref.length; j < len; i = ++j) {
ele = ref[i];
if (ele === obj) {
return i + start;
}
return -1;
}
};
}
});
# $("a[rel*=leanModal]").leanModal() }).call(this);
$('#csrfmiddlewaretoken').attr 'value', $.cookie('csrftoken')
new Calculator
new FeedbackForm
if $('body').hasClass('courseware')
Courseware.start()
window.postJSON = (url, data, callback) ->
$.postWithPrefix url, data, callback
$('#login').click ->
$('#login_form input[name="email"]').focus()
false
$('#signup').click ->
$('#signup-modal input[name="email"]').focus()
false
# fix for ie
if !Array::indexOf
Array::indexOf = (obj, start = 0) ->
for ele, i in this[start..]
if ele is obj
return i + start
return -1

View File

@@ -1,21 +1,37 @@
$ -> // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
if window.navigator.appName is "Microsoft Internet Explorer" /* eslint-disable */
isMPInstalled = (boolean) -> (function() {
# check if MathPlayer is installed $(function() {
# (from http://www.dessci.com/en/products/mathplayer/check.htm) var isMPInstalled;
try if (window.navigator.appName === "Microsoft Internet Explorer") {
oMP = new ActiveXObject("MathPlayer.Factory.1") isMPInstalled = function(boolean) {
true var e, oMP;
catch e /*
false check if MathPlayer is installed
(from http://www.dessci.com/en/products/mathplayer/check.htm)
*/
try {
oMP = new ActiveXObject("MathPlayer.Factory.1");
return true;
} catch (_error) {
e = _error;
return false;
}
};
# detect if there is mathjax on the page /*
# if not, set 'aria-hidden' to 'true' detect if there is mathjax on the page
if MathJax? and not isMPInstalled() if not, set 'aria-hidden' to 'true'
$("#mathjax-accessibility-message").attr("aria-hidden", "false") */
if ((typeof MathJax !== "undefined" && MathJax !== null) && !isMPInstalled()) {
$("#mathjax-accessibility-message").attr("aria-hidden", "false");
}
if ((typeof MathJax !== "undefined" && MathJax !== null) && $("#mathplayer-browser-message").length > 0) {
return $("#mathplayer-browser-message").attr("aria-hidden", "false");
} else {
return $("#mathjax-accessibility-message").attr("aria-hidden", "true");
}
}
});
if MathJax? and $("#mathplayer-browser-message").length > 0 }).call(this);
$("#mathplayer-browser-message").attr("aria-hidden", "false")
else
$("#mathjax-accessibility-message").attr("aria-hidden", "true")

View File

@@ -1,73 +1,109 @@
getTime = -> // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
new Date().getTime() /* eslint-disable */
// TODO: Examine all of the xss-lint exceptions (https://openedx.atlassian.net/browse/PLAT-2084)
(function() {
var getTime;
class @MathJaxDelayRenderer getTime = function() {
return new Date().getTime();
};
maxDelay: 3000 this.MathJaxDelayRenderer = (function() {
mathjaxRunning: false var bufferId, numBuffers;
elapsedTime: 0
mathjaxDelay: 0
mathjaxTimeout: undefined
bufferId = "mathjax_delay_buffer"
numBuffers = 0
constructor: (params) -> MathJaxDelayRenderer.prototype.maxDelay = 3000;
params = params || {}
@maxDelay = params["maxDelay"] || @maxDelay
@bufferId = params["bufferId"] || (bufferId + numBuffers)
numBuffers += 1
@$buffer = $("<div>").attr("id", @bufferId).css("display", "none").appendTo($("body"))
# render: (params) -> MathJaxDelayRenderer.prototype.mathjaxRunning = false;
# params:
# elem: jquery element to be rendered
# text: text to be rendered & put into the element;
# if blank, then just render the current text in the element
# preprocessor: pre-process the text before rendering using MathJax
# if text is blank, it will pre-process the html in the element
# previewSetter: if provided, will pass text back to it instead of
# directly setting the element
render: (params) -> MathJaxDelayRenderer.prototype.elapsedTime = 0;
elem = params["element"] MathJaxDelayRenderer.prototype.mathjaxDelay = 0;
previewSetter = params["previewSetter"]
text = params["text"]
if not text?
text = $(elem).html()
preprocessor = params["preprocessor"]
if params["delay"] == false MathJaxDelayRenderer.prototype.mathjaxTimeout = void 0;
if preprocessor?
text = preprocessor(text) bufferId = "mathjax_delay_buffer";
$(elem).html(text)
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $(elem).attr("id")] numBuffers = 0;
else
if @mathjaxTimeout function MathJaxDelayRenderer(params) {
window.clearTimeout(@mathjaxTimeout) params = params || {};
@mathjaxTimeout = undefined this.maxDelay = params["maxDelay"] || this.maxDelay;
delay = Math.min @elapsedTime + @mathjaxDelay, @maxDelay this.bufferId = params["bufferId"] || (bufferId + numBuffers);
numBuffers += 1;
renderer = => this.$buffer = $("<div>").attr("id", this.bufferId).css("display", "none").appendTo($("body")); // xss-lint: disable=javascript-jquery-insert-into-target
if @mathjaxRunning }
return
prevTime = getTime()
if preprocessor? /*
text = preprocessor(text) render: (params) ->
@$buffer.html(text) params:
curTime = getTime() elem: jquery element to be rendered
@elapsedTime = curTime - prevTime text: text to be rendered & put into the element;
if MathJax? if blank, then just render the current text in the element
prevTime = getTime() preprocessor: pre-process the text before rendering using MathJax
@mathjaxRunning = true if text is blank, it will pre-process the html in the element
MathJax.Hub.Queue ["Typeset", MathJax.Hub, @$buffer.attr("id")], => previewSetter: if provided, will pass text back to it instead of
@mathjaxRunning = false directly setting the element
curTime = getTime() */
@mathjaxDelay = curTime - prevTime
if previewSetter MathJaxDelayRenderer.prototype.render = function(params) {
previewSetter($(@$buffer).html()) var delay, elem, preprocessor, previewSetter, renderer, text;
else elem = params["element"];
$(elem).html($(@$buffer).html()) previewSetter = params["previewSetter"];
else text = params["text"];
@mathjaxDelay = 0 if (text == null) {
@mathjaxTimeout = window.setTimeout(renderer, delay) text = $(elem).html();
}
preprocessor = params["preprocessor"];
if (params["delay"] === false) {
if (preprocessor != null) {
text = preprocessor(text);
}
$(elem).html(text); // xss-lint: disable=javascript-jquery-html
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, $(elem).attr("id")]);
} else {
if (this.mathjaxTimeout) {
window.clearTimeout(this.mathjaxTimeout);
this.mathjaxTimeout = void 0;
}
delay = Math.min(this.elapsedTime + this.mathjaxDelay, this.maxDelay);
renderer = (function(_this) {
return function() {
var curTime, prevTime;
if (_this.mathjaxRunning) {
return;
}
prevTime = getTime();
if (preprocessor != null) {
text = preprocessor(text);
}
_this.$buffer.html(text); // xss-lint: disable=javascript-jquery-html
curTime = getTime();
_this.elapsedTime = curTime - prevTime;
if (typeof MathJax !== "undefined" && MathJax !== null) {
prevTime = getTime();
_this.mathjaxRunning = true;
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, _this.$buffer.attr("id")], function() {
_this.mathjaxRunning = false;
curTime = getTime();
_this.mathjaxDelay = curTime - prevTime;
if (previewSetter) {
return previewSetter($(_this.$buffer).html());
} else {
return $(elem).html($(_this.$buffer).html()); // xss-lint: disable=javascript-jquery-html
}
});
} else {
return _this.mathjaxDelay = 0;
}
};
})(this);
return this.mathjaxTimeout = window.setTimeout(renderer, delay);
}
};
return MathJaxDelayRenderer;
})();
}).call(this);

View File

@@ -1,23 +1,52 @@
class @Tab // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
constructor: (@id, @items) -> /* eslint-disable */
@el = $("#tab_#{id}") // TODO: Examine all of the xss-lint exceptions (https://openedx.atlassian.net/browse/PLAT-2084)
@render() (function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
$: (selector) -> this.Tab = (function() {
$(selector, @el) function Tab(id1, items) {
this.id = id1;
this.items = items;
this.onShow = bind(this.onShow, this);
this.el = $("#tab_" + id);
this.render();
}
render: -> Tab.prototype.$ = function(selector) {
$.each @items, (index, item) => return $(selector, this.el);
tab = $('<a>').attr(href: "##{@tabId(index)}").html(item.title) };
@$('.navigation').append($('<li>').append(tab))
@el.append($('<section>').attr(id: @tabId(index)))
@el.tabs
show: @onShow
onShow: (element, ui) => Tab.prototype.render = function() {
@$('section.ui-tabs-hide').html('') $.each(this.items, (function(_this) {
@$("##{@tabId(ui.index)}").html(@items[ui.index]['content']) return function(index, item) {
@el.trigger 'contentChanged' var tab;
tab = $('<a>').attr({
href: "#" + (_this.tabId(index))
}).html(item.title); // xss-lint: disable=javascript-jquery-html
_this.$('.navigation').append($('<li>').append(tab)); // xss-lint: disable=javascript-jquery-append
return _this.el.append($('<section>').attr({
id: _this.tabId(index)
}));
};
})(this));
return this.el.tabs({
show: this.onShow
});
};
tabId: (index) -> Tab.prototype.onShow = function(element, ui) {
"tab-#{@id}-#{index}" this.$('section.ui-tabs-hide').html('');
this.$("#" + (this.tabId(ui.index))).html(this.items[ui.index]['content']); // xss-lint: disable=javascript-jquery-html
return this.el.trigger('contentChanged');
};
Tab.prototype.tabId = function(index) {
return "tab-" + this.id + "-" + index;
};
return Tab;
})();
}).call(this);

View File

@@ -1,5 +1,5 @@
// This file is intentionally blank as it was removed because it was not // This file is intentionally blank as it was removed because it was no
// longer necessary, but the pipeline requires it for whatever reason. // longer necessary, but the pipeline requires it for whatever reason.
// Until the pipeline issue is resolved this file is here. It shouldn't // Until the pipeline issue is resolved this file is here. It shouldn't
// conflict with anything. // conflict with anything.
var nothingtoseehere; var nothingtoseehere;

View File

@@ -1,97 +1,168 @@
class StudentNotes // Once generated by CoffeeScript 1.9.3, but now lives as pure JS
_debug: false /* eslint-disable */
(function() {
var StudentNotes,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
targets: [] # holds elements with annotator() instances StudentNotes = (function() {
StudentNotes.prototype._debug = false;
# Adds a listener for "notes" events that may bubble up from descendants.
constructor: ($, el) ->
console.log 'student notes init', arguments, this if @_debug
if not $(el).data('notes-instance') /*
events = 'notes:init': @onInitNotes holds elements with annotator() instances
$(el).delegate('*', events) */
$(el).data('notes-instance', @)
# Initializes annotations on a container element in response to an init event. StudentNotes.prototype.targets = [];
onInitNotes: (event, uri=null, storage_url=null, token=null) =>
event.stopPropagation()
found = @targets.some (target) -> target is event.target
# Get uri /*
unless uri.substring(0, 4) is "http" Adds a listener for "notes" events that may bubble up from descendants.
uri_root = (window.location.href.split(/#|\?/).shift() or "") */
uri = uri_root + uri.substring(1)
parts = window.location.href.split("/")
courseid = parts[4] + "/" + parts[5] + "/" + parts[6]
# Get id and name user function StudentNotes($, el) {
idUdiv = $(event.target).parent().find(".idU")[0] this.onInitNotes = bind(this.onInitNotes, this);
idDUdiv = $(event.target).parent().find(".idDU")[0] var events;
idUdiv = (if typeof idUdiv isnt "undefined" then idUdiv.innerHTML else "") if (this._debug) {
idDUdiv = (if typeof idDUdiv isnt "undefined" then idDUdiv.innerHTML else "") console.log('student notes init', arguments, this);
}
options = if (!$(el).data('notes-instance')) {
optionsAnnotator: events = {
permissions: 'notes:init': this.onInitNotes
user: };
id: idUdiv $(el).delegate('*', events);
name: idDUdiv $(el).data('notes-instance', this);
}
}
userString: (user) ->
return user.name if user and user.name
user
userId: (user) -> /*
return user.id if user and user.id Initializes annotations on a container element in response to an init event.
user */
auth:
token: token
store: StudentNotes.prototype.onInitNotes = function(event, uri, storage_url, token) {
prefix: storage_url var courseid, found, idDUdiv, idUdiv, options, ova, parts, uri_root;
if (uri == null) {
uri = null;
}
if (storage_url == null) {
storage_url = null;
}
if (token == null) {
token = null;
}
event.stopPropagation();
found = this.targets.some(function(target) {
return target === event.target;
});
annotationData: uri:uri /*
Get uri
*/
if (uri.substring(0, 4) !== "http") {
uri_root = window.location.href.split(/#|\?/).shift() || "";
uri = uri_root + uri.substring(1);
}
parts = window.location.href.split("/");
courseid = parts[4] + "/" + parts[5] + "/" + parts[6];
urls: /*
create: '/create', Get id and name user
read: '/read/:id', */
update: '/update/:id', idUdiv = $(event.target).parent().find(".idU")[0];
destroy: '/delete/:id', idDUdiv = $(event.target).parent().find(".idDU")[0];
search: '/search' idUdiv = (typeof idUdiv !== "undefined" ? idUdiv.innerHTML : "");
idDUdiv = (typeof idDUdiv !== "undefined" ? idDUdiv.innerHTML : "");
options = {
optionsAnnotator: {
permissions: {
user: {
id: idUdiv,
name: idDUdiv
},
userString: function(user) {
if (user && user.name) {
return user.name;
}
return user;
},
userId: function(user) {
if (user && user.id) {
return user.id;
}
return user;
}
},
auth: {
token: token
},
store: {
prefix: storage_url,
annotationData: {
uri: uri
},
urls: {
create: '/create',
read: '/read/:id',
update: '/update/:id',
destroy: '/delete/:id',
search: '/search'
},
loadFromSearch: {
limit: 10000,
uri: uri,
user: idUdiv
}
}
},
optionsVideoJS: {
techOrder: ["html5", "flash", "youtube"],
customControlsOnMobile: true
},
optionsOVA: {
posBigNew: 'none',
NumAnnotations: 20
},
optionsRichText: {
tinymce: {
selector: "li.annotator-item textarea",
plugins: "media image insertdatetime link code",
menubar: false,
toolbar_items_size: 'small',
extended_valid_elements: "iframe[src|frameborder|style|scrolling|class|width|height|name|align|id]",
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media rubric | code "
}
}
};
if (found) {
if (Annotator._instances.length !== 0) {
$(event.target).annotator("destroy");
}
return ova = new OpenVideoAnnotation.Annotator($(event.target), options);
} else {
if (event.target.id === "annotator-viewer") {
return ova = new OpenVideoAnnotation.Annotator($(event.target), options);
} else {
return this.targets.push(event.target);
}
}
};
loadFromSearch: return StudentNotes;
limit:10000
uri: uri
user:idUdiv
optionsVideoJS: techOrder: ["html5","flash","youtube"],customControlsOnMobile: true
optionsOVA:
posBigNew:'none'
NumAnnotations:20
optionsRichText:
tinymce:
selector: "li.annotator-item textarea"
plugins: "media image insertdatetime link code"
menubar: false
toolbar_items_size: 'small'
extended_valid_elements : "iframe[src|frameborder|style|scrolling|class|width|height|name|align|id]"
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media rubric | code "
if found
$(event.target).annotator "destroy" unless Annotator._instances.length is 0
ova = new OpenVideoAnnotation.Annotator($(event.target), options)
else
if event.target.id is "annotator-viewer"
ova = new OpenVideoAnnotation.Annotator($(event.target), options)
else
@targets.push(event.target)
# Enable notes by default on the document root. })();
# To initialize annotations on a container element in the document:
#
# $('#myElement').trigger('notes:init');
#
# Comment this line to disable notes.
$(document).ready ($) -> new StudentNotes $, @
/*
Enable notes by default on the document root.
To initialize annotations on a container element in the document:
$('#myElement').trigger('notes:init');
Comment this line to disable notes.
*/
$(document).ready(function($) {
return new StudentNotes($, this);
});
}).call(this);

View File

@@ -26,7 +26,6 @@ var options = {
// Make sure the patterns in sourceFiles and specFiles do not match the same file. // Make sure the patterns in sourceFiles and specFiles do not match the same file.
// Otherwise Istanbul which is used for coverage tracking will cause tests to not run. // Otherwise Istanbul which is used for coverage tracking will cause tests to not run.
sourceFiles: [ sourceFiles: [
{pattern: 'coffee/src/**/!(*spec).js'},
{pattern: 'course_bookmarks/**/!(*spec).js'}, {pattern: 'course_bookmarks/**/!(*spec).js'},
{pattern: 'course_search/**/!(*spec).js'}, {pattern: 'course_search/**/!(*spec).js'},
{pattern: 'discussion/js/**/!(*spec).js'}, {pattern: 'discussion/js/**/!(*spec).js'},

View File

@@ -1,71 +0,0 @@
// Karma config for lms-coffee suite.
// Docs and troubleshooting tips in common/static/common/js/karma.common.conf.js
/* eslint-env node */
'use strict';
var path = require('path');
var configModule = require(path.join(__dirname, '../../common/static/common/js/karma.common.conf.js'));
var options = {
useRequireJs: false,
includeCommonFiles: true,
// Avoid adding files to this list. Use RequireJS.
libraryFilesToInclude: [
{pattern: 'common/js/vendor/require.js', included: true},
{pattern: 'js/RequireJS-namespace-undefine.js', included: true},
{pattern: 'common/js/vendor/jquery.js', included: true},
{pattern: 'common/js/vendor/jquery-migrate.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/jquery.event.drag-2.2.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/slick.core.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/slick.grid.js', included: true},
{pattern: 'xmodule_js/common_static/coffee/src/ajax_prefix.js', included: true},
{pattern: 'common/js/vendor/jquery.js', included: true},
{pattern: 'common/js/vendor/jquery-migrate.js', included: true},
{pattern: 'common/js/vendor/underscore.js', included: true},
{pattern: 'common/js/xblock/*.js', included: true},
{pattern: 'xmodule_js/common_static/js/src/logger.js', included: true},
{pattern: 'xmodule_js/common_static/js/test/i18n.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/jquery.cookie.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/flot/jquery.flot.js', included: true},
{pattern: 'xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/jquery-ui.min.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/URI.min.js', included: true},
{pattern: 'common/js/vendor/hls.js', included: true},
{pattern: 'xmodule_js/src/capa/*.js', included: true},
{pattern: 'xmodule_js/src/video/*.js', included: true},
{pattern: 'xmodule_js/src/xmodule.js', included: true},
{pattern: 'xmodule_js/common_static/js/vendor/jasmine-imagediff.js', included: true},
{pattern: 'common/js/spec_helpers/jasmine-extensions.js', included: true},
{pattern: 'lms/js/spec/main_requirejs_coffee.js', included: true}
],
libraryFiles: [
{pattern: 'xmodule_js/common_static/js/vendor/**/*.js'}
],
// Make sure the patterns in sourceFiles and specFiles do not match the same file.
// Otherwise Istanbul which is used for coverage tracking will cause tests to not run.
sourceFiles: [
{pattern: 'coffee/src/**/*.js', included: true}
],
specFiles: [
{pattern: 'coffee/spec/**/*.js', included: true}
],
fixtureFiles: [
{pattern: 'coffee/fixtures/**/*.*', included: true}
]
};
module.exports = function(config) {
configModule.configure(config, options);
};

View File

@@ -69,7 +69,7 @@
*/ */
paths: { paths: {
gettext: 'empty:', gettext: 'empty:',
'coffee/src/ajax_prefix': 'empty:', 'js/src/ajax_prefix': 'empty:',
jquery: 'empty:', jquery: 'empty:',
'jquery-migrate': 'empty:', 'jquery-migrate': 'empty:',
'jquery.cookie': 'empty:', 'jquery.cookie': 'empty:',

View File

@@ -31,7 +31,7 @@
'jquery.fileupload': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload', 'jquery.fileupload': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload',
'jquery.iframe-transport': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', // eslint-disable-line max-len 'jquery.iframe-transport': 'xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport', // eslint-disable-line max-len
'jquery.inputnumber': 'xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill', 'jquery.inputnumber': 'xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill',
'jquery.immediateDescendents': 'xmodule_js/common_static/coffee/src/jquery.immediateDescendents', 'jquery.immediateDescendents': 'xmodule_js/common_static/js/src/jquery.immediateDescendents',
'jquery.simulate': 'xmodule_js/common_static/js/vendor/jquery.simulate', 'jquery.simulate': 'xmodule_js/common_static/js/vendor/jquery.simulate',
'jquery.timeago': 'xmodule_js/common_static/js/vendor/jquery.timeago', 'jquery.timeago': 'xmodule_js/common_static/js/vendor/jquery.timeago',
'jquery.url': 'xmodule_js/common_static/js/vendor/url.min', 'jquery.url': 'xmodule_js/common_static/js/vendor/url.min',
@@ -58,7 +58,7 @@
'domReady': 'xmodule_js/common_static/js/vendor/domReady', 'domReady': 'xmodule_js/common_static/js/vendor/domReady',
mathjax: '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_SVG&delayStartupUntil=configured', // eslint-disable-line max-len mathjax: '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_SVG&delayStartupUntil=configured', // eslint-disable-line max-len
'youtube': '//www.youtube.com/player_api?noext', 'youtube': '//www.youtube.com/player_api?noext',
'coffee/src/ajax_prefix': 'xmodule_js/common_static/coffee/src/ajax_prefix', 'js/src/ajax_prefix': 'xmodule_js/common_static/js/src/ajax_prefix',
'js/instructor_dashboard/student_admin': 'js/instructor_dashboard/student_admin', 'js/instructor_dashboard/student_admin': 'js/instructor_dashboard/student_admin',
'xmodule_js/common_static/js/test/add_ajax_prefix': 'xmodule_js/common_static/js/test/add_ajax_prefix', 'xmodule_js/common_static/js/test/add_ajax_prefix': 'xmodule_js/common_static/js/test/add_ajax_prefix',
'xblock/lms.runtime.v1': 'lms/js/xblock/lms.runtime.v1', 'xblock/lms.runtime.v1': 'lms/js/xblock/lms.runtime.v1',
@@ -71,8 +71,8 @@
'Markdown.Editor': 'js/Markdown.Editor', 'Markdown.Editor': 'js/Markdown.Editor',
'Markdown.Sanitizer': 'js/Markdown.Sanitizer', 'Markdown.Sanitizer': 'js/Markdown.Sanitizer',
'_split': 'js/split', '_split': 'js/split',
'mathjax_delay_renderer': 'coffee/src/mathjax_delay_renderer', 'mathjax_delay_renderer': 'js/mathjax_delay_renderer',
'MathJaxProcessor': 'coffee/src/customwmd', 'MathJaxProcessor': 'js/customwmd',
'picturefill': 'common/js/vendor/picturefill', 'picturefill': 'common/js/vendor/picturefill',
'bootstrap': 'common/js/vendor/bootstrap.bundle', 'bootstrap': 'common/js/vendor/bootstrap.bundle',
'draggabilly': 'xmodule_js/common_static/js/vendor/draggabilly', 'draggabilly': 'xmodule_js/common_static/js/vendor/draggabilly',
@@ -282,7 +282,7 @@
}, },
'xmodule_js/common_static/js/test/add_ajax_prefix': { 'xmodule_js/common_static/js/test/add_ajax_prefix': {
exports: 'AjaxPrefix', exports: 'AjaxPrefix',
deps: ['coffee/src/ajax_prefix'] deps: ['js/src/ajax_prefix']
}, },
'js/instructor_dashboard/util': { 'js/instructor_dashboard/util': {
exports: 'js/instructor_dashboard/util', exports: 'js/instructor_dashboard/util',

View File

@@ -16,9 +16,9 @@ from openedx.core.djangolib.markup import HTML
## add in a definition of 'xxx_url' in the right section_data for whatever page your ## add in a definition of 'xxx_url' in the right section_data for whatever page your
## feature is on. ## feature is on.
## 3. Add a url() entry in api_urls.py ## 3. Add a url() entry in api_urls.py
## 4. Over in lms/static/coffee/src/instructor_dashboard/ there there are .coffee files ## 4. Over in lms/static/js/instructor_dashboard/ there are .js files for each page.
## for each page which define the .js. Edit this to make your input do something ## Edit this to make your input do something when clicked. The .js files use the
## when clicked. The .coffee files use the name=xx to pick out inputs, not id= ## name=xx to pick out inputs, not id=
## 5. Implement your standard django/python in lms/djangoapps/instructor/views/api.py ## 5. Implement your standard django/python in lms/djangoapps/instructor/views/api.py
## 6. And tests go in lms/djangoapps/instructor/tests/ ## 6. And tests go in lms/djangoapps/instructor/tests/
@@ -110,7 +110,7 @@ from openedx.core.djangolib.markup import HTML
</div> </div>
## links which are tied to idash-sections below. ## links which are tied to idash-sections below.
## the links are activated and handled in instructor_dashboard.coffee ## the links are activated and handled in instructor_dashboard.js
## when the javascript loads, it clicks on the first section ## when the javascript loads, it clicks on the first section
<ul class="instructor-nav"> <ul class="instructor-nav">
% for section_data in sections: % for section_data in sections:

25
package-lock.json generated
View File

@@ -2091,31 +2091,6 @@
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
}, },
"coffee-loader": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/coffee-loader/-/coffee-loader-0.7.3.tgz",
"integrity": "sha1-+tvG79b8fsyIxbMEaiwpIGa8tUo=",
"requires": {
"loader-utils": "1.1.0"
},
"dependencies": {
"loader-utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"requires": {
"big.js": "3.2.0",
"emojis-list": "2.1.0",
"json5": "0.5.1"
}
}
}
},
"coffee-script": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.1.tgz",
"integrity": "sha1-NLVd7bCc7zbRseKQgwLgV/HiYGg="
},
"collapse-white-space": { "collapse-white-space": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz",

View File

@@ -19,8 +19,6 @@
"bootstrap": "4.0.0-beta.2", "bootstrap": "4.0.0-beta.2",
"camelize": "1.0.0", "camelize": "1.0.0",
"classnames": "2.2.5", "classnames": "2.2.5",
"coffee-loader": "0.7.3",
"coffee-script": "1.6.1",
"css-loader": "0.28.8", "css-loader": "0.28.8",
"edx-pattern-library": "0.18.1", "edx-pattern-library": "0.18.1",
"edx-ui-toolkit": "1.5.2", "edx-ui-toolkit": "1.5.2",

View File

@@ -28,7 +28,6 @@ from .utils.timer import timed
# setup baseline paths # setup baseline paths
ALL_SYSTEMS = ['lms', 'studio'] ALL_SYSTEMS = ['lms', 'studio']
COFFEE_DIRS = ['lms', 'cms', 'common']
LMS = 'lms' LMS = 'lms'
CMS = 'cms' CMS = 'cms'
@@ -301,32 +300,6 @@ def debounce(seconds=1):
return decorator return decorator
class CoffeeScriptWatcher(PatternMatchingEventHandler):
"""
Watches for coffeescript changes
"""
ignore_directories = True
patterns = ['*.coffee']
def register(self, observer):
"""
register files with observer
"""
dirnames = set()
for filename in sh(coffeescript_files(), capture=True).splitlines():
dirnames.add(path(filename).dirname())
for dirname in dirnames:
observer.schedule(self, dirname)
@debounce()
def on_any_event(self, event):
print('\tCHANGED:', event.src_path)
try:
compile_coffeescript(event.src_path)
except Exception: # pylint: disable=broad-except
traceback.print_exc()
class SassWatcher(PatternMatchingEventHandler): class SassWatcher(PatternMatchingEventHandler):
""" """
Watches for sass file changes Watches for sass file changes
@@ -403,28 +376,6 @@ class XModuleAssetsWatcher(PatternMatchingEventHandler):
restart_django_servers() restart_django_servers()
def coffeescript_files():
"""
return find command for paths containing coffee files
"""
dirs = " ".join(Env.REPO_ROOT / coffee_dir for coffee_dir in COFFEE_DIRS)
return cmd('find', dirs, '-type f', '-name \"*.coffee\"')
@task
@no_help
@timed
def compile_coffeescript(*files):
"""
Compile CoffeeScript to JavaScript.
"""
if not files:
files = ["`{}`".format(coffeescript_files())]
sh(cmd(
"node_modules/.bin/coffee", "--compile", *files
))
@task @task
@no_help @no_help
@cmdopts([ @cmdopts([
@@ -736,7 +687,6 @@ def collect_assets(systems, settings, **kwargs):
# We compile these out, don't need the source files in staticfiles # We compile these out, don't need the source files in staticfiles
"sass", "sass",
"*.coffee",
] ]
ignore_args = " ".join( ignore_args = " ".join(
@@ -906,7 +856,6 @@ def watch_assets(options):
sass_directories = get_watcher_dirs(theme_dirs, themes) sass_directories = get_watcher_dirs(theme_dirs, themes)
observer = Observer(timeout=wait) observer = Observer(timeout=wait)
CoffeeScriptWatcher().register(observer)
SassWatcher().register(observer, sass_directories) SassWatcher().register(observer, sass_directories)
XModuleSassWatcher().register(observer, ['common/lib/xmodule/']) XModuleSassWatcher().register(observer, ['common/lib/xmodule/'])
XModuleAssetsWatcher().register(observer) XModuleAssetsWatcher().register(observer)
@@ -936,7 +885,7 @@ def watch_assets(options):
@timed @timed
def update_assets(args): def update_assets(args):
""" """
Compile CoffeeScript and Sass, then collect static assets. Compile Sass, then collect static assets.
""" """
parser = argparse.ArgumentParser(prog='paver update_assets') parser = argparse.ArgumentParser(prog='paver update_assets')
parser.add_argument( parser.add_argument(
@@ -980,7 +929,6 @@ def update_assets(args):
process_xmodule_assets() process_xmodule_assets()
process_npm_assets() process_npm_assets()
compile_coffeescript()
# Build Webpack # Build Webpack
call_task('pavelib.assets.webpack', options={'settings': args.settings}) call_task('pavelib.assets.webpack', options={'settings': args.settings})

View File

@@ -25,7 +25,6 @@ DEFAULT_SETTINGS = Env.DEVSTACK_SETTINGS
@needs( @needs(
"pavelib.prereqs.install_prereqs", "pavelib.prereqs.install_prereqs",
"pavelib.i18n.i18n_validate_gettext", "pavelib.i18n.i18n_validate_gettext",
"pavelib.assets.compile_coffeescript",
) )
@cmdopts([ @cmdopts([
("verbose", "v", "Sets 'verbose' to True"), ("verbose", "v", "Sets 'verbose' to True"),

View File

@@ -18,10 +18,6 @@ class TestPaverJavaScriptTestTasks(PaverTestCase):
EXPECTED_DELETE_JAVASCRIPT_REPORT_COMMAND = u'find {platform_root}/reports/javascript -type f -delete' EXPECTED_DELETE_JAVASCRIPT_REPORT_COMMAND = u'find {platform_root}/reports/javascript -type f -delete'
EXPECTED_INSTALL_NPM_ASSETS_COMMAND = u'install npm_assets' EXPECTED_INSTALL_NPM_ASSETS_COMMAND = u'install npm_assets'
EXPECTED_COFFEE_COMMAND = (
u'node_modules/.bin/coffee --compile `find {platform_root}/lms {platform_root}/cms '
u'{platform_root}/common -type f -name "*.coffee"`'
)
EXPECTED_KARMA_OPTIONS = ( EXPECTED_KARMA_OPTIONS = (
u"{config_file} " u"{config_file} "
u"--single-run={single_run} " u"--single-run={single_run} "
@@ -120,7 +116,6 @@ class TestPaverJavaScriptTestTasks(PaverTestCase):
platform_root=self.platform_root platform_root=self.platform_root
)) ))
expected_messages.append(self.EXPECTED_INSTALL_NPM_ASSETS_COMMAND) expected_messages.append(self.EXPECTED_INSTALL_NPM_ASSETS_COMMAND)
expected_messages.append(self.EXPECTED_COFFEE_COMMAND.format(platform_root=self.platform_root))
for suite in suites: for suite in suites:
# Karma test command # Karma test command

View File

@@ -6,10 +6,6 @@ from paver.easy import call_task
from ..utils.envs import Env from ..utils.envs import Env
from .utils import PaverTestCase from .utils import PaverTestCase
EXPECTED_COFFEE_COMMAND = (
u"node_modules/.bin/coffee --compile `find {platform_root}/lms "
u"{platform_root}/cms {platform_root}/common -type f -name \"*.coffee\"`"
)
EXPECTED_SASS_COMMAND = ( EXPECTED_SASS_COMMAND = (
u"libsass {sass_directory}" u"libsass {sass_directory}"
) )
@@ -33,7 +29,7 @@ EXPECTED_COLLECT_STATIC_COMMAND = (
u'python manage.py {system} --settings={asset_settings} collectstatic ' u'python manage.py {system} --settings={asset_settings} collectstatic '
u'--ignore "fixtures" --ignore "karma_*.js" --ignore "spec" ' u'--ignore "fixtures" --ignore "karma_*.js" --ignore "spec" '
u'--ignore "spec_helpers" --ignore "spec-helpers" --ignore "xmodule_js" ' u'--ignore "spec_helpers" --ignore "spec-helpers" --ignore "xmodule_js" '
u'--ignore "geoip" --ignore "sass" --ignore "*.coffee" ' u'--ignore "geoip" --ignore "sass" '
u'--noinput {log_string}' u'--noinput {log_string}'
) )
EXPECTED_CELERY_COMMAND = ( EXPECTED_CELERY_COMMAND = (
@@ -247,7 +243,6 @@ class TestPaverServerTasks(PaverTestCase):
if not is_fast: if not is_fast:
expected_messages.append(u"xmodule_assets common/static/xmodule") expected_messages.append(u"xmodule_assets common/static/xmodule")
expected_messages.append(u"install npm_assets") expected_messages.append(u"install npm_assets")
expected_messages.append(EXPECTED_COFFEE_COMMAND.format(platform_root=self.platform_root))
expected_messages.extend( expected_messages.extend(
[c.format(settings=expected_asset_settings) for c in EXPECTED_PRINT_SETTINGS_COMMAND] [c.format(settings=expected_asset_settings) for c in EXPECTED_PRINT_SETTINGS_COMMAND]
) )
@@ -293,7 +288,6 @@ class TestPaverServerTasks(PaverTestCase):
if not is_fast: if not is_fast:
expected_messages.append(u"xmodule_assets common/static/xmodule") expected_messages.append(u"xmodule_assets common/static/xmodule")
expected_messages.append(u"install npm_assets") expected_messages.append(u"install npm_assets")
expected_messages.append(EXPECTED_COFFEE_COMMAND.format(platform_root=self.platform_root))
expected_messages.extend( expected_messages.extend(
[c.format(settings=expected_asset_settings) for c in EXPECTED_PRINT_SETTINGS_COMMAND] [c.format(settings=expected_asset_settings) for c in EXPECTED_PRINT_SETTINGS_COMMAND]
) )

View File

@@ -182,7 +182,6 @@ class Env(object):
REPO_ROOT / 'cms/static/karma_cms.conf.js', REPO_ROOT / 'cms/static/karma_cms.conf.js',
REPO_ROOT / 'cms/static/karma_cms_squire.conf.js', REPO_ROOT / 'cms/static/karma_cms_squire.conf.js',
REPO_ROOT / 'lms/static/karma_lms.conf.js', REPO_ROOT / 'lms/static/karma_lms.conf.js',
REPO_ROOT / 'lms/static/karma_lms_coffee.conf.js',
REPO_ROOT / 'common/lib/xmodule/xmodule/js/karma_xmodule.conf.js', REPO_ROOT / 'common/lib/xmodule/xmodule/js/karma_xmodule.conf.js',
REPO_ROOT / 'common/static/karma_common.conf.js', REPO_ROOT / 'common/static/karma_common.conf.js',
REPO_ROOT / 'common/static/karma_common_requirejs.conf.js', REPO_ROOT / 'common/static/karma_common_requirejs.conf.js',
@@ -192,7 +191,6 @@ class Env(object):
'cms', 'cms',
'cms-squire', 'cms-squire',
'lms', 'lms',
'lms-coffee',
'xmodule', 'xmodule',
'common', 'common',
'common-requirejs' 'common-requirejs'

View File

@@ -39,7 +39,6 @@ class JsTestSuite(TestSuite):
test_utils.clean_dir(self.report_dir) test_utils.clean_dir(self.report_dir)
assets.process_npm_assets() assets.process_npm_assets()
assets.compile_coffeescript("`find lms cms common -type f -name \"*.coffee\"`")
@property @property
def _default_subsuites(self): def _default_subsuites(self):
@@ -51,7 +50,7 @@ class JsTestSuite(TestSuite):
class JsTestSubSuite(TestSuite): class JsTestSubSuite(TestSuite):
""" """
Class for JS suites like cms, cms-squire, lms, lms-coffee, common, Class for JS suites like cms, cms-squire, lms, common,
common-requirejs and xmodule common-requirejs and xmodule
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

Some files were not shown because too many files have changed in this diff Show More