diff --git a/lang/en/form.php b/lang/en/form.php index d926cd28c0573..d5f2c6c6b2aa6 100644 --- a/lang/en/form.php +++ b/lang/en/form.php @@ -41,6 +41,7 @@ $string['err_required'] = 'You must supply a value here.'; $string['general'] = 'General'; $string['hideadvanced'] = 'Hide advanced'; +$string['hideeditortoolbar'] = 'Hide editing tools'; $string['hour'] = 'Hour'; $string['minute'] = 'Minute'; $string['miscellaneoussettings'] = 'Miscellaneous settings'; @@ -57,6 +58,7 @@ $string['selectallornone'] = 'Select all/none'; $string['selected'] = 'Selected'; $string['showadvanced'] = 'Show advanced'; +$string['showeditortoolbar'] = 'Show editing tools'; $string['somefieldsrequired'] = 'There are required fields in this form marked {$a}.'; $string['time'] = 'Time'; $string['timeunit'] = 'Time unit'; diff --git a/lib/editor/tinymce/editor_styles.css b/lib/editor/tinymce/editor_styles.css deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/lib/editor/tinymce/lib.php b/lib/editor/tinymce/lib.php index ede90a5c7919d..bf286caa7253f 100644 --- a/lib/editor/tinymce/lib.php +++ b/lib/editor/tinymce/lib.php @@ -106,6 +106,7 @@ public function use_editor($elementid, array $options=null, $fpoptions=null) { if ($fpoptions) { $PAGE->requires->js_init_call('M.editor_tinymce.init_filepicker', array($elementid, $fpoptions), true); } + $this->initialise_collapse_js(); } protected function get_init_params($elementid, array $options=null) { @@ -274,4 +275,27 @@ public function get_tinymce_base_url() { global $CFG; return new moodle_url("$CFG->httpswwwroot/lib/editor/tinymce/tiny_mce/$this->version/"); } + + /** + * Initialise javascript form elements + * @return void + */ + public function initialise_collapse_js() { + global $CFG, $PAGE, $OUTPUT; + + // This method is called for every editor instance. Ensure it's only run once. + // Static is a clunky solution but the best we could find to keep everything simple and encapsulated. + static $is_initialised; + + if ($is_initialised) { + return; + } + + // Initialise language strings. + $PAGE->requires->strings_for_js(array('hideeditortoolbar', 'showeditortoolbar'), + 'form'); + + $PAGE->requires->yui_module('moodle-editor_tinymce-collapse', 'M.editor_collapse.init'); + $is_initialised = true; + } } diff --git a/lib/editor/tinymce/styles.css b/lib/editor/tinymce/styles.css new file mode 100644 index 0000000000000..f6214988d87a2 --- /dev/null +++ b/lib/editor/tinymce/styles.css @@ -0,0 +1,3 @@ +.mform .felement.feditor .toggle_editor_toolbar {background: #EEEEEE;border-color: #BBBBBB;border-radius: 4px 4px 0 0;border-style: solid solid none;border-width: 1px 1px 0;display: inline-block;font-size: 0.7em;padding: 3px 6px;width: 9em;} +.mform .felement.feditor .mceStatusbar, +.mform .felement.feditor iframe {min-width: 35em;} \ No newline at end of file diff --git a/lib/editor/tinymce/yui/collapse/collapse.js b/lib/editor/tinymce/yui/collapse/collapse.js new file mode 100644 index 0000000000000..ea68c0d693c4f --- /dev/null +++ b/lib/editor/tinymce/yui/collapse/collapse.js @@ -0,0 +1,166 @@ +YUI.add('moodle-editor_tinymce-collapse', function(Y) { + + var COLLAPSE = function() { + COLLAPSE.superclass.constructor.apply(this, arguments); + }; + + Y.extend(COLLAPSE, Y.Base, { + + toggleNodeTemplate : null, + /** + * Set up basic values for static access. + */ + init : function() { + this.initialise_toggles(10); + }, + + /** + * Has TinyMCE been loaded and the editors been initialised? + * Designed mainly for IE + * @return bool + */ + editors_initialised : function() { + return typeof tinyMCE !== 'undefined'; + }, + + initialise_toggles : function(refreshes) { + var editors_initialised = this.editors_initialised(), self = this, editor; + if (!editors_initialised && refreshes) { + setTimeout(function() { + self.initialise_toggles(refreshes - 1); + }, 100); + return; + } + + // Create the toggle template for use later + this.toggleNodeTemplate = Y.Node.create(''); + this.toggleNodeTemplate.setContent(M.util.get_string('showeditortoolbar', 'form')); + + // Delegate clicks of the toggle_editor_toolbar + Y.one('body').delegate('click', this.toggle_collapse_from_event, 'a.toggle_editor_toolbar', this); + + // Set up editors which have already been created + for (editor in tinyMCE.editors) { + this.setup_collapse(tinyMCE.editors[editor]); + } + + // Set up for future editors. + // I haven't yet found a way of directly delegating the editor.onInit event. Instead we have to listen for the + // tinyMCE.onAddEditor event, and then add a further event listener to the editor's onInit event. + // onAddEditor is triggered before the editor has been created. + // We use Y.Bind to ensure that context is maintained. + tinyMCE.onAddEditor.add(Y.bind(this.add_setup_collapse_listener, this)); + + }, + + /** + * Setup a listener for a new editor which will actually set the editor up + * @param {Manager} mgr + * @param {Editor} ed + */ + add_setup_collapse_listener : function (mgr, ed) { + // Bind the editor.onInit function to set this editor up. This ensures we maintain our context (this) + ed.onInit.add(Y.bind(this.setup_collapse, this)); + }, + + /** + * Setup the toggle system for the provided editor + * + * @param {Editor} ed The TinyMCE editor instance + */ + setup_collapse : function(ed) { + var textarea = Y.Node(ed.getElement()), + editortable = Y.Node(ed.getContainer()).one('> table'), + thisToggleNode; + + // Does this text area support collapsing at all? + if (!textarea.hasClass('collapsible')) { + return; + } + + // Did we find an appropriate table to work with + if (!editortable) { + return; + } + + // Add toggle button. + thisToggleNode = this.toggleNodeTemplate.cloneNode(true); + editortable.get('parentNode').insert(thisToggleNode, editortable); + + // Toggle the toolbars initially. + if (Y.Node(ed.getElement()).hasClass('collapsed')) { + this.toggle_collapse(thisToggleNode, editortable, 0); + } else { + this.toggle_collapse(thisToggleNode, editortable, 1); + } + }, + + /** + * Toggle the specified editor toolbars. + * + * @param {Node} button The toggle button which we have to change the text for + * @param {Node} editortable The table which the tinyMCE editor is in + * @param {Boolean} newstate The intended toggle state + */ + toggle_collapse : function(button, editortable, newstate) { + var toolbar = editortable.one('td.mceToolbar').ancestor('tr'), + statusbar = editortable.one('.mceStatusbar').ancestor('tr'), + editor, iframe, size; + + // Check whether we have a state already. + if (typeof newstate === 'undefined') { + if (toolbar.getStyle('display') === 'none') { + newstate = 1; + } else { + newstate = 0; + } + } + + // Toggle the various states and update the button text to suit + if (newstate === 0) { + toolbar.hide(); + statusbar.hide(); + button.setContent(M.util.get_string('showeditortoolbar', 'form')); + } else { + toolbar.show(); + statusbar.show(); + button.setContent(M.util.get_string('hideeditortoolbar', 'form')); + } + + // TinyMCE renders the toolbar and path bar as part of the textarea. So toggling these items + // changes the required size of the rendered textarea. Frustrating but it's the way it's built. + // So we get TinyMCE to resize itself for us. Clunky but it works. + + // Get the tinyMCE editor object for this text area. + editorid = editortable.ancestor('div').one('textarea').get('id'); + editor = tinyMCE.getInstanceById(editorid); + + // Somehow, this editor did not exist. + if (!editor) { + return; + } + + // Resize editor to reflect presence of toolbar and path bar.. + iframe = editor.getBody(); + if (iframe) { + size = tinymce.DOM.getSize(iframe); + // If objects exist resize editor. + if (size) { + editor.theme.resizeTo(size.w, size.h); + } + } + }, + + toggle_collapse_from_event : function(thisevent) { + var button = thisevent.target.ancestor('a', true), + editortable = thisevent.target.ancestor('span', true).one('table.mceLayout'); + this.toggle_collapse(button, editortable); + } + }); + + M.editor_collapse = M.editor_collapse || {}; + M.editor_collapse.init = function(params) { + return new COLLAPSE(params); + }; + +}, '@VERSION@', {requires:['base', 'node', 'dom']}); \ No newline at end of file diff --git a/lib/form/editor.php b/lib/form/editor.php index bd04b222f3601..baf0c326f615f 100644 --- a/lib/form/editor.php +++ b/lib/form/editor.php @@ -53,7 +53,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element { /** @var array options provided to initalize filepicker */ protected $_options = array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 0, 'changeformat' => 0, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED, 'context' => null, 'noclean' => 0, 'trusttext' => 0, - 'return_types' => 7); + 'return_types' => 7, 'collapsible'=>0, 'collapsed' => 0); // $_options['return_types'] = FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE /** @var array values for editor */ @@ -383,7 +383,17 @@ function toHtml() { if (!is_null($this->getAttribute('onblur')) && !is_null($this->getAttribute('onchange'))) { $editorrules = ' onblur="'.htmlspecialchars($this->getAttribute('onblur')).'" onchange="'.htmlspecialchars($this->getAttribute('onchange')).'"'; } - $str .= '
'; @@ -439,4 +449,5 @@ function getFrozenHtml() { return ''; } + } diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php index da8ad92d77e27..270f32700886f 100644 --- a/question/type/edit_question_form.php +++ b/question/type/edit_question_form.php @@ -104,7 +104,7 @@ public function __construct($submiturl, $question, $category, $contexts, $formed $this->context = context::instance_by_id($record->contextid); $this->editoroptions = array('subdirs' => 1, 'maxfiles' => EDITOR_UNLIMITED_FILES, - 'context' => $this->context); + 'context' => $this->context, 'collapsed' => 1); $this->fileoptions = array('subdirs' => 1, 'maxfiles' => -1, 'maxbytes' => -1); $this->category = $category; @@ -186,7 +186,7 @@ protected function definition() { $mform->addRule('name', null, 'required', null, 'client'); $mform->addElement('editor', 'questiontext', get_string('questiontext', 'question'), - array('rows' => 15), $this->editoroptions); + array('rows' => 15), $this->get_non_collabsible_editor_options()); $mform->setType('questiontext', PARAM_RAW); $mform->addElement('text', 'defaultmark', get_string('defaultmark', 'question'), @@ -196,7 +196,7 @@ protected function definition() { $mform->addRule('defaultmark', null, 'required', null, 'client'); $mform->addElement('editor', 'generalfeedback', get_string('generalfeedback', 'question'), - array('rows' => 10), $this->editoroptions); + array('rows' => 10), $this->get_non_collabsible_editor_options()); $mform->setType('generalfeedback', PARAM_RAW); $mform->addHelpButton('generalfeedback', 'generalfeedback', 'question'); @@ -673,4 +673,8 @@ public function validation($fromform, $files) { * in the question type class. */ public abstract function qtype(); + + protected function get_non_collabsible_editor_options() { + return array_merge($this->editoroptions, array('collapsed' => 0)); + } }