From 6d1f553bad868876ef72feb2b580162f8d5d68d4 Mon Sep 17 00:00:00 2001 From: vyshane Date: Fri, 8 Dec 2006 04:44:24 +0000 Subject: [PATCH] Added language drop down to tag text as different languages for screen readers and to tag text as different languages for the multilang filter. MDL-7437. --- lib/editor/htmlarea/htmlarea.php | 263 ++++++++++++++++++++++++------- 1 file changed, 202 insertions(+), 61 deletions(-) diff --git a/lib/editor/htmlarea/htmlarea.php b/lib/editor/htmlarea/htmlarea.php index c6a2908012..be136994d9 100644 --- a/lib/editor/htmlarea/htmlarea.php +++ b/lib/editor/htmlarea/htmlarea.php @@ -1,5 +1,6 @@ dirroot.'/lib/languages.php'); $id = optional_param('id', 0, PARAM_INT); @@ -29,6 +30,8 @@ $strnormal = get_string("normal", "editor"); $straddress = get_string("address", "editor"); $strpreformatted = get_string("preformatted", "editor"); + $strlang = get_string('lang', 'editor'); + $strmulti = get_string('multi', 'editor'); ?> // htmlArea v3.0 - Copyright (c) 2002, 2003 interactivetools.com, inc. @@ -127,7 +130,7 @@ HTMLArea.Config = function () { this.fullPage = false; // style included in the iframe document - this.pageStyle = "body { background-color: #fff; font-family: 'Times New Roman', Times; }"; + this.pageStyle = "body { background-color: #fff; font-family: 'Times New Roman', Times; } \n .lang { background-color: #dee; }"; // set to true if you want Word code to be cleaned upon Paste this.killWordOnPaste = true; @@ -145,9 +148,10 @@ HTMLArea.Config = function () { [ "fontname", "space", "fontsize", "space", "formatblock", "space", + "language", "space", "bold", "italic", "underline", "strikethrough", "separator", "subscript", "superscript", "separator", - "copy", "cut", "paste","clean", "separator", "undo", "redo" ], + "clean", "separator", "undo", "redo" ], [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator", "lefttoright", "righttoleft", "separator", @@ -196,10 +200,29 @@ HTMLArea.Config = function () { "": "pre" }; + this.language = { + "":"", + $name) { + $key = str_replace('_', '-', $key); + $strlangarray .= '"'.$key.'": "'.$key.'",'; + } + $strlangarray .= '"'.$strmulti.'": "multi",'; + + foreach ($LANGUAGES as $key => $name) { + $key = str_replace('_', '-', $key); + $strlangarray .= '"'.$key.' ": "'.$key.'_ML",'; + } + $strlangarray = substr($strlangarray, 0, -1); + echo $strlangarray; + ?> + }; + this.customSelects = {}; function cut_copy_paste(e, cmd, obj) { - e.execCommand(cmd); + e.execCommand(cmd); }; this.btnList = { @@ -232,9 +255,6 @@ HTMLArea.Config = function () { showhelp: [ "Help using editor", "ed_help.gif", true, function(e) {e.execCommand("showhelp");} ], undo: [ "Undoes your last action", "ed_undo.gif", false, function(e) {e.execCommand("undo");} ], redo: [ "Redoes your last action", "ed_redo.gif", false, function(e) {e.execCommand("redo");} ], - cut: [ "Cut selection", "ed_cut.gif", false, cut_copy_paste ], - copy: [ "Copy selection", "ed_copy.gif", false, cut_copy_paste ], - paste: [ "Paste from clipboard", "ed_paste.gif", false, cut_copy_paste ], clean: [ "Clean Word HTML", "ed_wordclean.gif", false, function(e) {e.execCommand("killword"); }], lefttoright: [ "Direction left to right", "ed_left_to_right.gif", false, function(e) {e.execCommand("lefttoright");} ], righttoleft: [ "Direction right to left", "ed_right_to_left.gif", false, function(e) {e.execCommand("righttoleft");} ], @@ -383,6 +403,7 @@ HTMLArea.prototype._createToolbar = function () { case "fontsize": case "fontname": case "formatblock": + case "language": options = editor.config[txt]; cmd = txt; break; @@ -1198,57 +1219,84 @@ HTMLArea.prototype.updateToolbar = function(noStatus) { case "fontname": case "fontsize": case "formatblock": - if (!text) try { - var value = ("" + doc.queryCommandValue(cmd)).toLowerCase(); - if (!value) { - // FIXME: what do we do here? - break; - } - var options = this.config[cmd]; - var k = 0; - // btn.element.selectedIndex = 0; - for (var j in options) { - // FIXME: the following line is scary. - if ((j.toLowerCase() == value) || - (options[j].substr(0, value.length).toLowerCase() == value)) { - btn.element.selectedIndex = k; - break; - } - ++k; - } - } catch(e) {}; - break; + if (!text) try { + var value = ("" + doc.queryCommandValue(cmd)).toLowerCase(); + if (!value) { + // FIXME: what do we do here? + break; + } + var options = this.config[cmd]; + var k = 0; + // btn.element.selectedIndex = 0; + for (var j in options) { + // FIXME: the following line is scary. + if ((j.toLowerCase() == value) || + (options[j].substr(0, value.length).toLowerCase() == value)) { + btn.element.selectedIndex = k; + break; + } + ++k; + } + } catch(e) {}; + break; + case "language": + if (!text) try { + var value; + parentEl = this.getParentElement(); + if (parentEl.getAttribute('lang')) { + // A language was previously defined for the block. + if (parentEl.getAttribute('class') == 'multilang') { + value = parentEl.getAttribute('lang')+'_ML'; + } else { + value = parentEl.getAttribute('lang'); + } + } else { + value = ''; + } + var options = this.config[cmd]; + var k = 0; + for (var j in options) { + // FIXME: the following line is scary. + if ((j.toLowerCase() == value) || + (options[j].substr(0, value.length).toLowerCase() == value)) { + btn.element.selectedIndex = k; + break; + } + ++k; + } + } catch(e) {}; + break; case "textindicator": - if (!text) { - try {with (btn.element.style) { - backgroundColor = HTMLArea._makeColor( - doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor")); - if (/transparent/i.test(backgroundColor)) { - // Mozilla - backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor")); - } - color = HTMLArea._makeColor(doc.queryCommandValue("forecolor")); - fontFamily = doc.queryCommandValue("fontname"); - fontWeight = doc.queryCommandState("bold") ? "bold" : "normal"; - fontStyle = doc.queryCommandState("italic") ? "italic" : "normal"; - }} catch (e) { - // alert(e + "\n\n" + cmd); - } - } - break; + if (!text) { + try {with (btn.element.style) { + backgroundColor = HTMLArea._makeColor( + doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor")); + if (/transparent/i.test(backgroundColor)) { + // Mozilla + backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor")); + } + color = HTMLArea._makeColor(doc.queryCommandValue("forecolor")); + fontFamily = doc.queryCommandValue("fontname"); + fontWeight = doc.queryCommandState("bold") ? "bold" : "normal"; + fontStyle = doc.queryCommandState("italic") ? "italic" : "normal"; + }} catch (e) { + // alert(e + "\n\n" + cmd); + } + } + break; case "htmlmode": btn.state("active", text); break; case "lefttoright": case "righttoleft": - var el = this.getParentElement(); - while (el && !HTMLArea.isBlockElement(el)) - el = el.parentNode; - if (el) - btn.state("active", (el.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr"))); - break; + var el = this.getParentElement(); + while (el && !HTMLArea.isBlockElement(el)) + el = el.parentNode; + if (el) + btn.state("active", (el.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr"))); + break; default: - try { - btn.state("active", (!text && doc.queryCommandState(cmd))); - } catch (e) {} + try { + btn.state("active", (!text && doc.queryCommandState(cmd))); + } catch (e) {} } } // take undo snapshots @@ -1829,10 +1877,13 @@ HTMLArea.prototype._comboSelected = function(el, txt) { switch (txt) { case "fontname": case "fontsize": this.execCommand(txt, false, value); break; + case "language": + this.setLang(value); + break; case "formatblock": - (HTMLArea.is_ie) && (value = "<" + value + ">"); - this.execCommand(txt, false, value); - break; + (HTMLArea.is_ie) && (value = "<" + value + ">"); + this.execCommand(txt, false, value); + break; default: // try to look it up in the registered dropdowns var dropdown = this.config.customSelects[txt]; @@ -1844,6 +1895,93 @@ HTMLArea.prototype._comboSelected = function(el, txt) { } }; + +/** + * Used to set the language for the selected content. + * We use the content format for + * content that should be marked for multilang filter use, and + * content for normal content for which we want to + * set the language (for screen reader usage, for example). + */ +HTMLArea.prototype.setLang = function(lang) { + + if (lang == 'multi') { + // This is just the separator in the dropdown. Does nothing. + return; + } + + var editor = this; + var selectedHTML = editor.getSelectedHTML(); + var multiLang = false; + + var re = new RegExp('_ML', 'g'); + if (lang.match(re)) { + multiLang = true; + lang = lang.replace(re, ''); + } + + // Remove all lang attributes from span tags in selected html. + selectedHTML = selectedHTML.replace(/(]*)lang="[^"]*"([^>]*>)/, "$1$2"); + selectedHTML = selectedHTML.replace(/(]*)class="multilang"([^>]*>)/, "$1$2"); + + // If a span tag is now empty, delete it. + selectedHTML = selectedHTML.replace(/(.*?)<\/span>/, "$1"); + + + var parentEl = this.getParentElement(); + var insertNewSpan = false; + + if (parentEl.nodeName == 'SPAN' && parentEl.getAttribute('lang')) { + // A language was previously defined for the current block. + // Check whether the selected text makes up the whole of the block + // contents. + var re = new RegExp(parentEl.innerHTML); + + if (selectedHTML.match(re)) { + // The selected text makes up the whole of the span block. + if (lang != '') { + parentEl.setAttribute('lang', lang); + if (multiLang) { + parentEl.setAttribute('class', 'multilang'); + } + } else { + parentEl.removeAttribute('lang'); + + var classAttr = parentEl.getAttribute('class'); + if (classAttr) { + classAttr = classAttr.replace(/multilang/, '').trim(); + } + if (classAttr == '') { + parentEl.removeAttribute('class'); + } + if (parentEl.attributes.length == 0) { + // The span is no longer needed. + for (i=0; i