From 4f35baee040edf65191011aee929257bc7a20e41 Mon Sep 17 00:00:00 2001 From: Manuel Cagigas Date: Thu, 12 Jul 2018 12:48:25 +0200 Subject: [PATCH] 7.4.0 stable version --- VERSION | 2 +- configuration.ini.dist | 4 - core/core.js | 5348 ++++++++------- core/core.src.js | 5915 +++++++++++++++++ core/editor.css | 69 - core/editor.html | 80 - core/editor.js | 496 -- core/iframe.html | 61 - core/modal.css | 218 +- core/package.json | 14 + lib.php | 7 +- tests/behat/edit_formula.feature | 4 +- thirdpartylibs.xml | 10 + version.php | 6 +- wirisplugin-generic.js | 2 +- .../moodle-atto_wiris-button-debug.js | 12 +- .../moodle-atto_wiris-button-min.js | 2 +- .../moodle-atto_wiris-button.js | 12 +- yui/src/button/js/button.js | 12 +- 19 files changed, 9032 insertions(+), 3242 deletions(-) create mode 100644 core/core.src.js delete mode 100644 core/editor.css delete mode 100644 core/editor.html delete mode 100644 core/editor.js delete mode 100644 core/iframe.html create mode 100644 core/package.json create mode 100644 thirdpartylibs.xml diff --git a/VERSION b/VERSION index c288b8fd..217cf8d7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.3.0.1389 +7.4.0.1390 diff --git a/configuration.ini.dist b/configuration.ini.dist index 0357c08d..fbafd7d5 100644 --- a/configuration.ini.dist +++ b/configuration.ini.dist @@ -114,10 +114,6 @@ #wiriseditorsetsize = false -## Specifies if editor should be opened on a modal window or in a pop-up window. true by default. - -#wiriseditormodalwindow = true - ## Specifies if the modal window should be opened in full-screen mode. false by default. #wiriseditormodalwindowfullscreen = false diff --git a/core/core.js b/core/core.js index 90f36a82..33e11eb2 100644 --- a/core/core.js +++ b/core/core.js @@ -1,65 +1,44 @@ -var _wrs_popupWindow; +"use strict"; -/** - * StringManager class to use strings in code correctly with control. - * @ignore - */ -function StringManager() { - // Strings are empty when it creates, it set when calls load method. - this.strings = null; - this.stringsLoaded = false; -} +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var _wrs_popupWindow; /** - * This method return a string passing a key. - * @param {string} key of array strings that you want. - * @return string A text that you want or key if it doesn't exist. - * @ignore - */ -StringManager.prototype.getString = function(key) { - // Wait 200ms and recall this method if strings aren't load. - if (!this.stringsLoaded) { - setTimeout(this.getString.bind(this, key), 100); - return; - } - if (key in this.strings) { - return this.strings[key]; - } - return key; -} -/** - * This method load all strings to the manager and unset it for prevent bad usage. - * @param {array} String array of language - * @ignore + * Global variables (strict mode) */ -StringManager.prototype.loadStrings = function(langStrings) { - if (!this.stringsLoaded) { - this.strings = langStrings; - // Activate variable to unlock getStrings - this.stringsLoaded = true; - } -} +var _wrs_conf_createimagePath; +var _wrs_conf_showimagePath; +var _wrs_conf_editorPath; +var _wrs_conf_CASPath; +var _wrs_conf_createcasimagePath; +var _wrs_conf_getmathmlPath; +var _wrs_conf_servicePath; +var _wrs_conf_path; +var _wrs_conf_editor; -wrs_addEvent(window, 'message', function (e) { - if (e.source = _wrs_popupWindow && typeof e.wrs_processed == 'undefined' && typeof e.data.isWirisMessage != 'undefined') { - e.wrs_processed = true; - var postVariable = {}; - postVariable.id = e.data.id; - if (e.data.hasOwnProperty('methodName')) { - var object = (e.data.objectName == null) ? this : window[e.data.objectName]; - postVariable.value = object[e.data.methodName].apply(object, e.data.arguments); - } else { - var postVariables = {}; - e.data.varNames.forEach(function(varName){ - postVariables[varName] = window[varName]; - }); - postVariable.value = postVariables; - } - if (typeof(e.source) != 'undefined') { // Avoid sent message when popupWindows has been closed. - e.source.postMessage(postVariable, _wrs_conf_path); +var _wrs_temporalRange; + +function wrs_getCorePath() { + var scriptName = "core/core.js"; + var col = document.getElementsByTagName("script"); + for (var i = 0; i < col.length; i++) { + var d; + var src; + d = col[i]; + src = d.src; + var j = src.lastIndexOf(scriptName); + if (j >= 0) { + // That's my script! + return src.substr(0, j - 1); } } -}); +} + +var _wrs_corePath = wrs_getCorePath(); /** * Fires an element event. @@ -68,41 +47,40 @@ wrs_addEvent(window, 'message', function (e) { * @ignore */ function wrs_fireEvent(element, event) { - if (document.createEvent){ + if (document.createEvent) { var eventObject = document.createEvent('HTMLEvents'); eventObject.initEvent(event, true, true); return !element.dispatchEvent(eventObject); } var eventObject = document.createEventObject(); - return element.fireEvent('on' + event, eventObject) + return element.fireEvent('on' + event, eventObject); } -wrs_addEvent(window, 'mouseup', function (e) { - - if (typeof(_wrs_modalWindow) !== 'undefined' && _wrs_modalWindow != null) { - if (_wrs_modalWindow.properties.state != "maximized") { - _wrs_modalWindow.overlayDiv.style.display = 'none'; - } - _wrs_modalWindow.fireEditorEvent('mouseup'); - } -}); - // Translated languages. var _wrs_languages = 'ar,ca,cs,da,de,en,es,et,eu,fi,fr,gl,he,hr,hu,it,ja,ko,nl,no,pl,pt,pt_br,ru,sv,tr,zh,el'; // Load lang file at first to replace some variables -wrs_loadLangFile() +wrs_loadLangFile(); // Vars. -var _wrs_stringManager = new StringManager(); +var _wrs_stringManager; var _wrs_currentPath = window.location.toString().substr(0, window.location.toString().lastIndexOf('/') + 1); var _wrs_editMode = typeof _wrs_editMode != 'undefined' ? _wrs_editMode : undefined; -var _wrs_isNewElement = typeof _wrs_isNewElement != 'undefined' ? _wrs_isNewElement : true; +var _wrs_isNewElement = typeof _wrs_isNewElement !== 'undefined' ? _wrs_isNewElement : true; var _wrs_temporalImage; var _wrs_temporalFocusElement; var _wrs_range; -var _wrs_latex_formula_name = _wrs_stringManager.getString('latex_name_label'); +// TODO: String Manager not loaded. +function loadStringLatex() { + if (_wrs_conf_core_loaded) { + var _wrs_latex_formula_name = _wrs_stringManager.getString('latex_name_label'); + } else { + setTimeout(loadStringLatex, 100); + } +} +loadStringLatex(); + var _wrs_latex_formula_number = 1; // Tags used for LaTeX formulas. @@ -119,19 +97,19 @@ var _wrs_int_nonLatexCache = {}; var _wrs_int_AccessibleCache = {}; var _wrs_xmlCharacters = { - 'tagOpener': '<', // Hex: \x3C. - 'tagCloser': '>', // Hex: \x3E. - 'doubleQuote': '"', // Hex: \x22. - 'ampersand': '&', // Hex: \x26. - 'quote': '\'' // Hex: \x27. + 'tagOpener': '<', // Hex: \x3C. + 'tagCloser': '>', // Hex: \x3E. + 'doubleQuote': '"', // Hex: \x22. + 'ampersand': '&', // Hex: \x26. + 'quote': '\'' // Hex: \x27. }; var _wrs_safeXmlCharacters = { - 'tagOpener': '«', // Hex: \xAB. - 'tagCloser': '»', // Hex: \xBB. - 'doubleQuote': '¨', // Hex: \xA8. - 'ampersand': '§', // Hex: \xA7. - 'quote': '`', // Hex: \x60. + 'tagOpener': '«', // Hex: \xAB. + 'tagCloser': '»', // Hex: \xBB. + 'doubleQuote': '¨', // Hex: \xA8. + 'ampersand': '§', // Hex: \xA7. + 'quote': '`', // Hex: \x60. 'realDoubleQuote': '¨' }; @@ -140,46 +118,45 @@ var _wrs_safeXmlCharactersEntities = { 'tagCloser': '»', 'doubleQuote': '¨', 'realDoubleQuote': '"' -} +}; var _wrs_safeBadBlackboardCharacters = { 'ltElement': '«mo»<«/mo»', 'gtElement': '«mo»>«/mo»', 'ampElement': '«mo»&«/mo»' -} +}; var _wrs_safeGoodBlackboardCharacters = { 'ltElement': '«mo»§lt;«/mo»', 'gtElement': '«mo»§gt;«/mo»', 'ampElement': '«mo»§amp;«/mo»' -} +}; var _wrs_staticNodeLengths = { 'IMG': 1, 'BR': 1 -} -// Backwards compatibily. + // Backwards compatibily. -if (!(window._wrs_conf_imageClassName)) { - _wrs_conf_imageClassName = 'Wirisformula'; +};if (!window._wrs_conf_imageClassName) { + var _wrs_conf_imageClassName = 'Wirisformula'; } -if (!(window._wrs_conf_CASClassName)) { - _wrs_conf_CASClassName = 'Wiriscas'; +if (!window._wrs_conf_CASClassName) { + var _wrs_conf_CASClassName = 'Wiriscas'; } // Mutation observers to avoid wiris image formulas class be removed. if (typeof MutationObserver != 'undefined') { - var wrs_observer = new MutationObserver(function(mutations) { - mutations.forEach(function(mutation) { - if (mutation.oldValue == _wrs_conf_imageClassName && mutation.attributeName == 'class' && mutation.target.className.indexOf(_wrs_conf_imageClassName) == -1 ) { + var wrs_observer = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.oldValue == _wrs_conf_imageClassName && mutation.attributeName == 'class' && mutation.target.className.indexOf(_wrs_conf_imageClassName) == -1) { mutation.target.className = _wrs_conf_imageClassName; } }); }); - var wrs_observer_config = { attributes: true, attributeOldValue:true }; + var wrs_observer_config = { attributes: true, attributeOldValue: true }; } // Plugin listeners for custom callbacks. This variable @@ -206,7 +183,7 @@ var _wrs_parseXml = true; function wrs_addElementEvents(target, doubleClickHandler, mousedownHandler, mouseupHandler) { if (doubleClickHandler) { wrs_addEvent(target, 'dblclick', function (event) { - var realEvent = (event) ? event : window.event; + var realEvent = event ? event : window.event; var element = realEvent.srcElement ? realEvent.srcElement : realEvent.target; doubleClickHandler(target, element, realEvent); }); @@ -214,7 +191,7 @@ function wrs_addElementEvents(target, doubleClickHandler, mousedownHandler, mous if (mousedownHandler) { wrs_addEvent(target, 'mousedown', function (event) { - var realEvent = (event) ? event : window.event; + var realEvent = event ? event : window.event; var element = realEvent.srcElement ? realEvent.srcElement : realEvent.target; _wrs_temporalFocusElement = element; mousedownHandler(target, element, realEvent); @@ -223,7 +200,7 @@ function wrs_addElementEvents(target, doubleClickHandler, mousedownHandler, mous if (mouseupHandler) { wrs_addEvent(target, 'mouseup', function (event) { - var realEvent = (event) ? event : window.event; + var realEvent = event ? event : window.event; var element = realEvent.srcElement ? realEvent.srcElement : realEvent.target; mouseupHandler(target, element, realEvent); }); @@ -240,8 +217,7 @@ function wrs_addElementEvents(target, doubleClickHandler, mousedownHandler, mous function wrs_addEvent(element, event, func) { if (element.addEventListener) { element.addEventListener(event, func, true); - } - else if (element.attachEvent) { + } else if (element.attachEvent) { element.attachEvent('on' + event, func); } } @@ -256,17 +232,13 @@ function wrs_addEvent(element, event, func) { */ function wrs_addIframeEvents(iframe, doubleClickHandler, mousedownHandler, mouseupHandler) { wrs_initSetSize(); - wrs_addElementEvents(iframe.contentWindow.document, - function (target, element, event) { - doubleClickHandler(iframe, element, event); - }, - function (target, element, event) { - mousedownHandler(iframe, element, event); - }, - function (target, element, event) { - mouseupHandler(iframe, element, event); - } - ); + wrs_addElementEvents(iframe.contentWindow.document, function (target, element, event) { + doubleClickHandler(iframe, element, event); + }, function (target, element, event) { + mousedownHandler(iframe, element, event); + }, function (target, element, event) { + mouseupHandler(iframe, element, event); + }); } /** @@ -278,7 +250,7 @@ function wrs_addIframeEvents(iframe, doubleClickHandler, mousedownHandler, mouse function wrs_addTextareaEvents(textarea, clickHandler) { if (clickHandler) { wrs_addEvent(textarea, 'click', function (event) { - var realEvent = (event) ? event : window.event; + var realEvent = event ? event : window.event; clickHandler(textarea, realEvent); }); } @@ -371,7 +343,7 @@ function wrs_removeClass(element, className) { var classes = element.className.split(" "); for (var i = 0; i < classes.length; i++) { - if(classes[i] != className) { + if (classes[i] != className) { newClassName += classes[i] + " "; } } @@ -384,7 +356,7 @@ function wrs_removeClass(element, className) { * @return {string} String with the safeXml charaters parsed. * @ignore */ -function wrs_convertOldXmlinitialtextAttribute(text){ +function wrs_convertOldXmlinitialtextAttribute(text) { // Used to fix a bug with Cas imported from Moodle 1.9 to Moodle 2.x. // This could be removed in future. var val = 'value='; @@ -444,8 +416,7 @@ function wrs_createElement(elementName, attributes, creator) { html += '>'; element = creator.createElement(html); - } - catch (e) { + } catch (e) { element = creator.createElement(elementName); for (var attributeName in attributes) { @@ -472,13 +443,10 @@ function wrs_createHttpRequest() { try { return new ActiveXObject('Msxml2.XMLHTTP'); - } - catch (e) { + } catch (e) { try { return new ActiveXObject('Microsoft.XMLHTTP'); - } - catch (oc) { - } + } catch (oc) {} } return false; @@ -525,7 +493,7 @@ function wrs_createImageSrc(mathml, data) { } function wrs_createShowImageSrc(mathml, data, language) { - var dataMd5 = [] + var dataMd5 = []; var renderParams = 'mml,color,centerbaseline,zoom,dpi,fontSize,fontFamily,defaultStretchy,backgroundColor,format'; var renderParamsArray = renderParams.split(','); for (var key in renderParamsArray) { @@ -543,7 +511,7 @@ function wrs_createShowImageSrc(mathml, data, language) { } } dataObject.formula = com.wiris.js.JsPluginTools.md5encode(wrs_propertiesToString(dataMd5)); - dataObject.lang = (typeof language == 'undefined') ? 'en' : language; + dataObject.lang = typeof language == 'undefined' ? 'en' : language; dataObject.version = _wrs_conf_version; var result = wrs_getContent(_wrs_conf_showimagePath + '?' + wrs_httpBuildQuery(dataObject)); @@ -562,10 +530,10 @@ function wrs_createObject(objectCode, creator) { } // Internet Explorer can't include "param" tag when is setting an innerHTML property. - objectCode = objectCode.split('').join('').split('').join(''); - objectCode = objectCode.split('').join('
').split('').join('
'); var container = wrs_createElement('div', {}, creator); @@ -591,8 +559,7 @@ function wrs_createObject(objectCode, creator) { param.removeAttribute('wirisObject'); object.parentNode.replaceChild(param, object); - } - else if (object.getAttribute && object.getAttribute('wirisObject') == 'WirisApplet') { + } else if (object.getAttribute && object.getAttribute('wirisObject') == 'WirisApplet') { var attributesParsed = {}; for (var i = 0; i < object.attributes.length; ++i) { @@ -609,13 +576,12 @@ function wrs_createObject(objectCode, creator) { if (object.childNodes[i].nodeName.toLowerCase() == 'param') { applet.appendChild(object.childNodes[i]); - --i; // When we insert the object child into the applet, object loses one child. + --i; // When we insert the object child into the applet, object loses one child. } } object.parentNode.replaceChild(applet, object); - } - else { + } else { for (var i = 0; i < object.childNodes.length; ++i) { recursiveParamsFix(object.childNodes[i]); } @@ -639,7 +605,8 @@ function wrs_createObjectCode(object) { return; } - if (object.nodeType == 1) { // ELEMENT_NODE. + if (object.nodeType == 1) { + // ELEMENT_NODE. var output = '<' + object.tagName; for (var i = 0; i < object.attributes.length; ++i) { @@ -656,18 +623,17 @@ function wrs_createObjectCode(object) { } output += ''; - } - else if (object.nodeName == 'DIV' || object.nodeName == 'SCRIPT') { + } else if (object.nodeName == 'DIV' || object.nodeName == 'SCRIPT') { output += '>'; - } - else { + } else { output += '/>'; } return output; } - if (object.nodeType == 3) { // TEXT_NODE. + if (object.nodeType == 3) { + // TEXT_NODE. return wrs_htmlentities(object.nodeValue); } @@ -690,7 +656,7 @@ function wrs_endParse(code, wirisProperties, language) { function wrs_regexpIndexOf(input, regexp, start) { var index = input.substring(start || 0).search(regexp); - return (index >= 0) ? (index + (start || 0)) : index; + return index >= 0 ? index + (start || 0) : index; } /** @@ -720,8 +686,7 @@ function wrs_endParseEditMode(code, wirisProperties, language) { var mathml = wrs_getMathMLFromLatex(latex, true); output += mathml; endPosition += 2; - } - else { + } else { output += '$$'; endPosition = startPosition + 2; } @@ -746,19 +711,18 @@ function wrs_endParseEditMode(code, wirisProperties, language) { var i = formulaPosition; var startTagFound = false; - while (i >= 0 && !startTagFound) { // Going backwards until the start tag '<' is found. + while (i >= 0 && !startTagFound) { + // Going backwards until the start tag '<' is found. var character = code.charAt(i); if (character == '"' || character == '\'') { var characterNextPosition = code.lastIndexOf(character, i); - i = (characterNextPosition == -1) ? -1 : characterNextPosition; - } - else if (character == '<') { + i = characterNextPosition == -1 ? -1 : characterNextPosition; + } else if (character == '<') { startPosition = i; startTagFound = true; - } - else if (character == '>') { - i = -1; // Break: we are inside a text node. + } else if (character == '>') { + i = -1; // Break: we are inside a text node. } --i; @@ -778,9 +742,8 @@ function wrs_endParseEditMode(code, wirisProperties, language) { if (character == '"' || character == '\'') { var characterNextPosition = code.indexOf(character, i); - i = (characterNextPosition == -1) ? code.length : characterNextPosition; - } - else if (character == '<') { + i = characterNextPosition == -1 ? code.length : characterNextPosition; + } else if (character == '<') { if (i + 1 < code.length && code.charAt(i + 1) == '/') { --counter; @@ -789,15 +752,13 @@ function wrs_endParseEditMode(code, wirisProperties, language) { if (endPosition == -1) { // End tag stripped. - counter = -1; // to be != 0 and to break the loop. + counter = -1; // to be != 0 and to break the loop. } } - } - else { + } else { ++counter; } - } - else if (character == '>' && code.charAt(i - 1) == '/') { + } else if (character == '>' && code.charAt(i - 1) == '/') { --counter; if (counter == 0) { @@ -819,14 +780,12 @@ function wrs_endParseEditMode(code, wirisProperties, language) { var imgObject = wrs_mathmlToImgObject(document, mathml, wirisProperties, language); output += wrs_createObjectCode(imgObject); - } - else { + } else { // Start tag found but no end tag found. No process is done. A character is appended to avoid infinite loop in the next search. output += code.charAt(formulaPosition); endPosition = formulaPosition + 1; } - } - else { + } else { // No start tag is found. No process is done. A character is appended to avoid infinite loop in the next search. output += code.charAt(formulaPosition); endPosition = formulaPosition + 1; @@ -858,12 +817,10 @@ function wrs_endParseSaveMode(code) { convertToXml = true; convertToSafeXml = true; code = wrs_codeImgTransform(code, 'img2mathml'); - } - else if (_wrs_conf_saveMode == 'xml') { + } else if (_wrs_conf_saveMode == 'xml') { convertToXml = true; code = wrs_codeImgTransform(code, 'img2mathml'); - } - else if (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'image') { + } else if (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'image') { code = wrs_codeImgTransform(code, 'img264'); } } @@ -896,21 +853,18 @@ function wrs_getContent(url, postVariables) { var httpRequest = wrs_createHttpRequest(); if (httpRequest) { - if (typeof postVariables === undefined || typeof postVariables == 'undefined') { + if ((typeof postVariables === "undefined" ? "undefined" : _typeof(postVariables)) === undefined || typeof postVariables == 'undefined') { httpRequest.open('GET', url, false); - } - else if (url.substr(0, 1) == '/' || url.substr(0, 7) == 'http://' || url.substr(0, 8) == 'https://') { + } else if (url.substr(0, 1) == '/' || url.substr(0, 7) == 'http://' || url.substr(0, 8) == 'https://') { httpRequest.open('POST', url, false); - } - else { + } else { httpRequest.open('POST', _wrs_currentPath + url, false); } if (postVariables !== undefined) { httpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8'); httpRequest.send(wrs_httpBuildQuery(postVariables)); - } - else { + } else { httpRequest.send(null); } @@ -918,9 +872,7 @@ function wrs_getContent(url, postVariables) { } alert(_wrs_stringManager.getString('browser_no_compatible')); - } - catch (e) { - } + } catch (e) {} return ''; } @@ -980,7 +932,8 @@ function wrs_getLatexFromTextNode(textNode, caretPosition, latexTags) { // Looking for the first textNode. var startNode = textNode; - while (startNode.previousSibling && startNode.previousSibling.nodeType == 3) { // TEXT_NODE. + while (startNode.previousSibling && startNode.previousSibling.nodeType == 3) { + // TEXT_NODE. startNode = startNode.previousSibling; } @@ -1007,7 +960,8 @@ function wrs_getLatexFromTextNode(textNode, caretPosition, latexTags) { while (position == -1) { currentNode = currentNode.nextSibling; - if (!currentNode || currentNode.nodeType != 3) { // TEXT_NODE. + if (!currentNode || currentNode.nodeType != 3) { + // TEXT_NODE. return null; // Not found. } @@ -1022,14 +976,14 @@ function wrs_getLatexFromTextNode(textNode, caretPosition, latexTags) { function isPrevious(node, position, endNode, endPosition) { if (node == endNode) { - return (position <= endPosition); + return position <= endPosition; } while (node && node != endNode) { node = node.nextSibling; } - return (node == endNode); + return node == endNode; } var start; @@ -1063,8 +1017,7 @@ function wrs_getLatexFromTextNode(textNode, caretPosition, latexTags) { if (start.node == end.node) { latex = start.node.nodeValue.substring(start.position + tagLength, end.position - tagLength); - } - else { + } else { latex = start.node.nodeValue.substring(start.position + tagLength, start.node.nodeValue.length); var currentNode = start.node; @@ -1073,8 +1026,7 @@ function wrs_getLatexFromTextNode(textNode, caretPosition, latexTags) { if (currentNode == end.node) { latex += end.node.nodeValue.substring(0, end.position - tagLength); - } - else { + } else { latex += currentNode.nodeValue; } } while (currentNode != end.node); @@ -1131,11 +1083,13 @@ function wrs_getMathMLFromLatex(latex, includeLatexOnSemantics) { * @ignore */ function wrs_getNodeLength(node) { - if (node.nodeType == 3) { // TEXT_NODE. + if (node.nodeType == 3) { + // TEXT_NODE. return node.nodeValue.length; } - if (node.nodeType == 1) { // ELEMENT_NODE. + if (node.nodeType == 1) { + // ELEMENT_NODE. var length = _wrs_staticNodeLengths[node.nodeName.toUpperCase()]; if (length === undefined) { @@ -1161,7 +1115,7 @@ function wrs_getNodeLength(node) { function wrs_getQueryParams(windowObject) { var data = {}; var start = windowObject.location.search.indexOf('?'); - start = (start == -1) ? 0 : start + 1; + start = start == -1 ? 0 : start + 1; var queryStringParts = windowObject.location.search.substr(start).split('&'); for (var i = 0; i < queryStringParts.length; ++i) { @@ -1187,8 +1141,7 @@ function wrs_getSelectedItem(target, isIframe, forceGetSelection) { if (isIframe) { windowTarget = target.contentWindow; windowTarget.focus(); - } - else { + } else { windowTarget = window; target.focus(); } @@ -1217,15 +1170,15 @@ function wrs_getSelectedItem(target, isIframe, forceGetSelection) { var node; var caretPosition; - if (temporalObject.nextSibling && temporalObject.nextSibling.nodeType == 3) { // TEXT_NODE. + if (temporalObject.nextSibling && temporalObject.nextSibling.nodeType == 3) { + // TEXT_NODE. node = temporalObject.nextSibling; caretPosition = 0; - } - else if (temporalObject.previousSibling && temporalObject.previousSibling.nodeType == 3) { // TEXT_NODE. + } else if (temporalObject.previousSibling && temporalObject.previousSibling.nodeType == 3) { + // TEXT_NODE. node = temporalObject.previousSibling; caretPosition = node.nodeValue.length; - } - else { + } else { node = windowTarget.document.createTextNode(''); temporalObject.parentNode.insertBefore(node, temporalObject); caretPosition = 0; @@ -1253,14 +1206,14 @@ function wrs_getSelectedItem(target, isIframe, forceGetSelection) { try { var range = selection.getRangeAt(0); - } - catch (e) { + } catch (e) { var range = windowTarget.document.createRange(); } var node = range.startContainer; - if (node.nodeType == 3) { // TEXT_NODE. + if (node.nodeType == 3) { + // TEXT_NODE. if (range.startOffset != range.endOffset) { return null; } @@ -1275,7 +1228,8 @@ function wrs_getSelectedItem(target, isIframe, forceGetSelection) { return null; } - if (node.nodeType == 1) { // ELEMENT_NODE. + if (node.nodeType == 1) { + // ELEMENT_NODE. var position = range.startOffset; if (node.childNodes[position]) { @@ -1299,7 +1253,7 @@ function wrs_getSelectedItemOnTextarea(textarea) { var textNode = document.createTextNode(textarea.value); var textNodeWithLatex = wrs_getLatexFromTextNode(textNode, textarea.selectionStart); if (textNodeWithLatex == null) { - return null + return null; }; // Try to get latex mathml from cache @@ -1352,8 +1306,7 @@ function wrs_getWIRISImageOutput(imgCode, convertToXml, convertToSafeXml) { } return xmlCode; - } - else if (imgObject.className == _wrs_conf_CASClassName) { + } else if (imgObject.className == _wrs_conf_CASClassName) { var appletCode = imgObject.getAttribute(_wrs_conf_CASMathmlAttribute); appletCode = wrs_mathmlDecode(appletCode); var appletObject = wrs_createObject(appletCode); @@ -1401,7 +1354,7 @@ function wrs_htmlentitiesDecode(input) { function wrs_httpBuildQuery(properties) { var result = ''; - for (i in properties) { + for (var i in properties) { if (properties[i] != null) { result += wrs_urlencode(i) + '=' + wrs_urlencode(properties[i]) + '&'; } @@ -1455,8 +1408,7 @@ function wrs_initParseImgToIframes(windowTarget) { var iframe = wrs_mathmlToIframeObject(windowTarget, wrs_mathmlDecode(mathml)); imgList[i].parentNode.replaceChild(iframe, imgList[i]); - } - else { + } else { ++i; } } @@ -1473,7 +1425,7 @@ function wrs_initParseEditMode(code) { if (window._wrs_conf_parseModes !== undefined && wrs_arrayContains(_wrs_conf_parseModes, 'latex') != -1) { var imgList = wrs_getElementsByNameFromString(code, 'img', true); var token = 'encoding="LaTeX">'; - var carry = 0; // While replacing images with latex, the indexes of the found images changes respecting the original code, so this carry is needed. + var carry = 0; // While replacing images with latex, the indexes of the found images changes respecting the original code, so this carry is needed. for (var i = 0; i < imgList.length; ++i) { var imgCode = code.substring(imgList[i].start + carry, imgList[i].end + carry); @@ -1537,25 +1489,25 @@ function wrs_initParseSaveMode(code, language) { } var appletList = wrs_getElementsByNameFromString(code, 'applet', false); - var carry = 0; // While replacing applets with images, the indexes of the found applets changes respecting the original code, so this carry is needed. + var carry = 0; // While replacing applets with images, the indexes of the found applets changes respecting the original code, so this carry is needed. for (var i = 0; i < appletList.length; ++i) { var appletCode = code.substring(appletList[i].start + carry, appletList[i].end + carry); // The second control in the if is used to find WIRIS applet which don't have Wiriscas class (as it was in old CAS applets). if (appletCode.indexOf(' class="' + _wrs_conf_CASClassName + '"') != -1 || appletCode.toUpperCase().indexOf('WIRIS') != -1) { - if (appletCode.indexOf(' src="') != -1){ + if (appletCode.indexOf(' src="') != -1) { var srcStart = appletCode.indexOf(' src="') + ' src="'.length; var srcEnd = appletCode.indexOf('"', srcStart); var src = appletCode.substring(srcStart, srcEnd); - } else{ + } else { // This should happen only with old CAS imported from Moodle 1 to Moodle 2. - if (typeof(_wrs_conf_pluginBasePath) != 'undefined'){ + if (typeof _wrs_conf_pluginBasePath != 'undefined') { var src = _wrs_conf_pluginBasePath + '/integration/showcasimage.php?formula=noimage'; } else { var src = ''; } - if (appletCode.indexOf(' class="' + _wrs_conf_CASClassName + '"') == -1){ + if (appletCode.indexOf(' class="' + _wrs_conf_CASClassName + '"') == -1) { var closeSymbol = appletCode.indexOf('>'); var appletTag = appletCode.substring(0, closeSymbol); var newAppletTag = appletTag.split(' width=').join(' class="Wiriscas" width='); @@ -1591,13 +1543,13 @@ function wrs_getElementsByNameFromString(code, name, autoClosed) { name = name.toLowerCase(); var start = code.indexOf('<' + name + ' '); - while (start != -1) { // Look for nodes. + while (start != -1) { + // Look for nodes. var endString; if (autoClosed) { endString = '>'; - } - else { + } else { endString = ''; } @@ -1610,8 +1562,7 @@ function wrs_getElementsByNameFromString(code, name, autoClosed) { 'start': start, 'end': end }); - } - else { + } else { end = start + 1; } @@ -1636,40 +1587,40 @@ function wrs_insertElementOnSelection(element, focusElement, windowTarget) { // For example, on CKEditor calls CKEditorInstance.focus() method. // With this method we can call proper focus methods which in some scenarios // help's MathType to focus properly on the current editor window. - if (typeof wrs_int_insertElementOnSelection != 'undefined') { + if (typeof wrs_int_insertElementOnSelection !== 'undefined') { wrs_int_insertElementOnSelection(); } - if(typeof focusElement.frameElement != 'undefined'){ - function get_browser(){ + if (typeof focusElement.frameElement !== 'undefined') { + var get_browser = function get_browser() { var ua = navigator.userAgent; - if(ua.search("Edge/") >= 0){ + if (ua.search("Edge/") >= 0) { return "EDGE"; - }else if(ua.search("Chrome/") >= 0){ + } else if (ua.search("Chrome/") >= 0) { return "CHROME"; - }else if(ua.search("Trident/") >= 0){ + } else if (ua.search("Trident/") >= 0) { return "IE"; - }else if(ua.search("Firefox/") >= 0){ + } else if (ua.search("Firefox/") >= 0) { return "FIREFOX"; - }else if(ua.search("Safari/") >= 0){ + } else if (ua.search("Safari/") >= 0) { return "SAFARI"; } - } + }; + var browserName = get_browser(); // Iexplorer, Edge and Safari can't focus into iframe if (browserName == 'SAFARI' || browserName == 'IE' || browserName == 'EDGE') { focusElement.focus(); - }else{ + } else { focusElement.frameElement.focus(); } - }else{ + } else { focusElement.focus(); } if (_wrs_isNewElement) { if (focusElement.type == "textarea") { wrs_updateTextarea(focusElement, element.textContent); - } - else if (document.selection && document.getSelection == 0) { + } else if (document.selection && document.getSelection == 0) { var range = windowTarget.document.selection.createRange(); windowTarget.document.execCommand('InsertImage', false, element.src); @@ -1684,26 +1635,22 @@ function wrs_insertElementOnSelection(element, focusElement, windowTarget) { if (temporalObject.nodeName.toUpperCase() == 'IMG') { temporalObject.parentNode.replaceChild(element, temporalObject); - } - else { + } else { // IE9 fix: parentNode() does not return the IMG node, returns the parent DIV node. In IE < 9, pasteHTML does not work well. range.pasteHTML(wrs_createObjectCode(element)); } } - } - else { + } else { var selection = windowTarget.getSelection(); // We have use wrs_range beacuse IExplorer delete selection when select another part of text. if (_wrs_range) { var range = _wrs_range; _wrs_range = null; - } - else { + } else { try { var range = selection.getRangeAt(0); - } - catch (e) { + } catch (e) { var range = windowTarget.document.createRange(); } } @@ -1714,19 +1661,18 @@ function wrs_insertElementOnSelection(element, focusElement, windowTarget) { var node = range.startContainer; var position = range.startOffset; - if (node.nodeType == 3) { // TEXT_NODE. + if (node.nodeType == 3) { + // TEXT_NODE. node = node.splitText(position); node.parentNode.insertBefore(element, node); node = node.parentNode; - } - else if (node.nodeType == 1) { // ELEMENT_NODE. + } else if (node.nodeType == 1) { + // ELEMENT_NODE. node.insertBefore(element, node.childNodes[position]); } // Fix to set the caret after the inserted image. range.selectNode(element); - position = range.endOffset; - selection.collapse(node, position); - // Integration function + // Integration function. // If wrs_int_setCaretPosition function exists on // integration script can call caret method from the editor instance. // With this method we can call proper specific editor methods which in some scenarios @@ -1734,37 +1680,32 @@ function wrs_insertElementOnSelection(element, focusElement, windowTarget) { if (typeof wrs_int_selectRange != 'undefined') { wrs_int_selectRange(range); } + // Selection collapse must have to do it after the function 'wrs_int_selectRange' because + // can be that the range was changed and the selection needs to be updated. + position = range.endOffset; + selection.collapse(node, position); } - } - else if (_wrs_temporalRange) { + } else if (_wrs_temporalRange) { if (document.selection && document.getSelection == 0) { _wrs_isNewElement = true; _wrs_temporalRange.select(); wrs_insertElementOnSelection(element, focusElement, windowTarget); - } - else { + } else { var parentNode = _wrs_temporalRange.startContainer; _wrs_temporalRange.deleteContents(); _wrs_temporalRange.insertNode(element); } - } - else if (focusElement.type == "textarea") { + } else if (focusElement.type == "textarea") { var item; // Wrapper for some integrations that can have special behaviours to show latex. if (typeof wrs_int_getSelectedItem != 'undefined') { item = wrs_int_getSelectedItem(focusElement, false); - } - else { + } else { item = wrs_getSelectedItemOnTextarea(focusElement); } wrs_updateExistingFormulaOnTextarea(focusElement, element.textContent, item.startPosition, item.endPosition); - } - else { - if (!element) { // Editor empty, formula has been erased on edit. - _wrs_temporalImage.parentNode.removeChild(_wrs_temporalImage); - } - _wrs_temporalImage.parentNode.replaceChild(element, _wrs_temporalImage); - function placeCaretAfterNode(node) { + } else { + var placeCaretAfterNode = function placeCaretAfterNode(node) { if (typeof window.getSelection != "undefined") { var range = windowTarget.document.createRange(); range.setStartAfter(node); @@ -1773,12 +1714,17 @@ function wrs_insertElementOnSelection(element, focusElement, windowTarget) { selection.removeAllRanges(); selection.addRange(range); } + }; + + if (!element) { + // Editor empty, formula has been erased on edit. + _wrs_temporalImage.parentNode.removeChild(_wrs_temporalImage); } + _wrs_temporalImage.parentNode.replaceChild(element, _wrs_temporalImage); + placeCaretAfterNode(element); } - } - catch (e) { - } + } catch (e) {} } /** @@ -1790,11 +1736,11 @@ function wrs_insertElementOnSelection(element, focusElement, windowTarget) { */ function wrs_isMathmlInAttribute(content, i) { // Regex = '^[\'"][\\s]*=[\\s]*[\\w-]+([\\s]*("[^"]*"|\'[^\']*\')[\\s]*=[\\s]*[\\w-]+[\\s]*)*[\\s]+gmi<'; - var math_att = '[\'"][\\s]*=[\\s]*[\\w-]+'; // "=att OR '=att - var att_content = '"[^"]*"|\'[^\']*\''; // "blabla" OR 'blabla' - var att = '[\\s]*(' + att_content + ')[\\s]*=[\\s]*[\\w-]+[\\s]*'; // "blabla"=att OR 'blabla'=att - var atts = '(' + att + ')*'; // "blabla"=att1 "blabla"=att2 - var regex = '^' + math_att + atts + '[\\s]+gmi<'; // "=att "blabla"=att1 "blabla"=att2 gmi< . + var math_att = '[\'"][\\s]*=[\\s]*[\\w-]+'; // "=att OR '=att + var att_content = '"[^"]*"|\'[^\']*\''; // "blabla" OR 'blabla' + var att = '[\\s]*(' + att_content + ')[\\s]*=[\\s]*[\\w-]+[\\s]*'; // "blabla"=att OR 'blabla'=att + var atts = '(' + att + ')*'; // "blabla"=att1 "blabla"=att2 + var regex = '^' + math_att + atts + '[\\s]+gmi<'; // "=att "blabla"=att1 "blabla"=att2 gmi< . var expression = new RegExp(regex); var actual_content = content.substring(0, i); @@ -1820,14 +1766,13 @@ function wrs_mathmlDecode(input) { input = input.split(_wrs_safeXmlCharactersEntities.realDoubleQuote).join(_wrs_safeXmlCharacters.realDoubleQuote); // Blackboard. - if ('_wrs_blackboard' in window && window._wrs_blackboard){ + if ('_wrs_blackboard' in window && window._wrs_blackboard) { input = input.split(_wrs_safeBadBlackboardCharacters.ltElement).join(_wrs_safeGoodBlackboardCharacters.ltElement); input = input.split(_wrs_safeBadBlackboardCharacters.gtElement).join(_wrs_safeGoodBlackboardCharacters.gtElement); input = input.split(_wrs_safeBadBlackboardCharacters.ampElement).join(_wrs_safeGoodBlackboardCharacters.ampElement); /*var regex = /«mtext».*[<>&].*«\/mtext»/; - - var result = regex.exec(input); + var result = regex.exec(input); while(result){ var changedResult = result[0].split(_wrs_xmlCharacters.tagOpener).join('§lt;'); changedResult = changedResult.split(_wrs_xmlCharacters.tagCloser).join('§gt;'); @@ -1854,20 +1799,17 @@ function wrs_mathmlDecode(input) { if (currentEntity == null) { if (character == '$') { currentEntity = ''; - } - else { + } else { returnValue += character; } - } - else { + } else { if (character == ';') { returnValue += '&' + currentEntity + ';'; currentEntity = null; - } - else if (character.match(/([a-zA-Z0-9#._-] | '-')/)) { // Character is part of an entity. + } else if (character.match(/([a-zA-Z0-9#._-] | '-')/)) { + // Character is part of an entity. currentEntity += character; - } - else { + } else { returnValue += '$' + currentEntity; // Is not an entity. currentEntity = null; --i; // Parse again the current character. @@ -1910,26 +1852,23 @@ function wrs_mathmlEntities(mathml) { // Parsing > 128 characters. if (mathml.codePointAt(i) > 128) { - toReturn += '&#' + mathml.codePointAt(i) + ';' + toReturn += '&#' + mathml.codePointAt(i) + ';'; // For UTF-32 characters we need to move the index one position. if (mathml.codePointAt(i) > 0xffff) { i++; } - } - else if (character == '&') { + } else if (character == '&') { var end = mathml.indexOf(';', i + 1); if (end >= 0) { var container = document.createElement('span'); container.innerHTML = mathml.substring(i, end + 1); - toReturn += '&#' + wrs_fixedCharCodeAt((container.innerText || container.textContent),0) + ';'; + toReturn += '&#' + wrs_fixedCharCodeAt(container.innerText || container.textContent, 0) + ';'; i = end; - } - else { + } else { toReturn += character; } - } - else { + } else { toReturn += character; } } @@ -1948,9 +1887,9 @@ function wrs_mathmlAddEditorAttribute(mathml) { var toReturn = ''; var start = mathml.indexOf(''); - if (mathml.indexOf("class") == -1 ) { + if (start == 0) { + var end = mathml.indexOf('>'); + if (mathml.indexOf("class") == -1) { // Adding custom editor type. toReturn = mathml.substr(start, end) + ' class="wrs_' + wrs_int_getCustomEditorEnabled().toolbar + '">'; toReturn += mathml.substr(end + 1, mathml.length); @@ -1958,7 +1897,6 @@ function wrs_mathmlAddEditorAttribute(mathml) { } } return mathml; - } /** @@ -1983,10 +1921,11 @@ function wrs_fixedCharCodeAt(str, idx) { if (isNaN(low)) { throw _wrs_stringManager.getString('exception_high_surrogate'); } - return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; + return (hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000; } - if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate. + if (0xDC00 <= code && code <= 0xDFFF) { + // Low surrogate. /* We return false to allow loops to skip this iteration since should have already handled high surrogate above in the previous iteration. */ return false; @@ -2006,21 +1945,18 @@ function wrs_mathmlToAccessible(mathml, language, data) { if (_wrs_int_AccessibleCache.hasOwnProperty(mathml)) { accessibleText = _wrs_int_AccessibleCache[mathml]; - } - else { + } else { data['service'] = 'mathml2accessible'; data['lang'] = _wrs_int_langCode; var accesibleJsonResponse = JSON.parse(wrs_getContent(_wrs_conf_servicePath, data)); if (accesibleJsonResponse.status != 'error') { accessibleText = accesibleJsonResponse.result.text; - } - else { + } else { accessibleText = _wrs_stringManager.getString('error_convert_accessibility'); } } return accessibleText; - } /** @@ -2032,6 +1968,33 @@ function wrs_mathmlToAccessible(mathml, language, data) { */ function wrs_mathmlToIframeObject(windowTarget, mathml) { if (window.navigator.userAgent.toLowerCase().indexOf('webkit') != -1) { + var waitForViewer = function waitForViewer() { + if (windowTarget.com && windowTarget.com.wiris) { + var _prepareDiv = function _prepareDiv() { + if (windowTarget._wrs_viewer.isReady()) { + container.style.height = formulaContainer.style.height; + container.style.width = formulaContainer.style.width; + container.style.verticalAlign = formulaContainer.style.verticalAlign; + } else { + setTimeout(_prepareDiv, 100); + } + }; + + if (!('_wrs_viewer' in windowTarget)) { + windowTarget._wrs_viewer = new windowTarget.com.wiris.jsEditor.JsViewerMain(_wrs_conf_pluginBasePath + '/integration/editor'); + windowTarget._wrs_viewer.insertCSS(null, windowTarget.document); + } + + windowTarget._wrs_viewer.paintFormulaOnContainer(mathml, formulaContainer, null); + + ; + + _prepareDiv(); + } else { + setTimeout(waitForViewer, 100); + } + }; + // In WebKit, the formula is represented by a div instead of an iframe. var container = windowTarget.document.createElement('span'); container.className = _wrs_conf_imageClassName; @@ -2047,33 +2010,6 @@ function wrs_mathmlToIframeObject(windowTarget, mathml) { formulaContainer.style.display = 'inline'; container.appendChild(formulaContainer); - function waitForViewer() { - if (windowTarget.com && windowTarget.com.wiris) { - if (!('_wrs_viewer' in windowTarget)) { - windowTarget._wrs_viewer = new windowTarget.com.wiris.jsEditor.JsViewerMain(_wrs_conf_pluginBasePath + '/integration/editor'); - windowTarget._wrs_viewer.insertCSS(null, windowTarget.document); - } - - windowTarget._wrs_viewer.paintFormulaOnContainer(mathml, formulaContainer, null); - - function prepareDiv() { - if (windowTarget._wrs_viewer.isReady()) { - container.style.height = formulaContainer.style.height; - container.style.width = formulaContainer.style.width; - container.style.verticalAlign = formulaContainer.style.verticalAlign; - } - else { - setTimeout(prepareDiv, 100); - } - }; - - prepareDiv(); - } - else { - setTimeout(waitForViewer, 100); - } - } - if (!('_wrs_viewerAppended' in windowTarget)) { var viewerScript = windowTarget.document.createElement('script'); viewerScript.src = _wrs_conf_pluginBasePath + '/integration/editor/viewer.js'; @@ -2121,7 +2057,7 @@ function wrs_mathmlToImgObject(creator, mathml, wirisProperties, language) { var imgObject = creator.createElement('img'); imgObject.align = 'middle'; imgObject.style.maxWidth = 'none'; - var data = (wirisProperties) ? wirisProperties : {}; + var data = wirisProperties ? wirisProperties : {}; if (window._wrs_conf_useDigestInsteadOfMathml && _wrs_conf_useDigestInsteadOfMathml) { data['returnDigest'] = 'true'; @@ -2147,13 +2083,15 @@ function wrs_mathmlToImgObject(creator, mathml, wirisProperties, language) { // TODO Custom Editors: class="wrs_toolbar" should be given by the editor // so the first condition shouldn't be longer necessary. + var customEditor; if (customEditor = wrs_int_getCustomEditorEnabled()) { imgObject.setAttribute('data-custom-editor', customEditor.toolbar); - } else if (mathml.indexOf('class="') != -1) { // We check here if the mathmnl has been created from a customEditor (such chemistry) + } else if (mathml.indexOf('class="') != -1) { + // We check here if the mathmnl has been created from a customEditor (such chemistry) // to add data-custom-editor attribute to img object (if necessary). - mathmlSubstring = mathml.substring(mathml.indexOf('class="') + 'class="'.length, mathml.length); + var mathmlSubstring = mathml.substring(mathml.indexOf('class="') + 'class="'.length, mathml.length); mathmlSubstring = mathmlSubstring.substring(0, mathmlSubstring.indexOf('"')); - mathmlSubstring = mathmlSubstring.substring(4,mathmlSubstring.length); + mathmlSubstring = mathmlSubstring.substring(4, mathmlSubstring.length); imgObject.setAttribute('data-custom-editor', mathmlSubstring); } @@ -2166,8 +2104,7 @@ function wrs_mathmlToImgObject(creator, mathml, wirisProperties, language) { // if the mathml is malformed, this function will throw an exception. try { result = JSON.parse(wrs_getContent(_wrs_conf_showimagePath, data)); - } - catch (e) { + } catch (e) { return; } } @@ -2186,24 +2123,21 @@ function wrs_mathmlToImgObject(creator, mathml, wirisProperties, language) { if (typeof result.alt == 'undefined') { imgObject.alt = wrs_mathmlToAccessible(mathml, language, data); wrs_populateAccessibleCache(mathml, imgObject.alt); - } - else { + } else { imgObject.alt = result.alt; } } - } - else { + } else { var result = wrs_createImageSrc(mathml, data); if (window._wrs_conf_useDigestInsteadOfMathml && _wrs_conf_useDigestInsteadOfMathml) { var parts = result.split(':', 2); imgObject.setAttribute(_wrs_conf_imageMathmlAttribute, parts[0]); imgObject.src = parts[1]; - } - else { + } else { imgObject.setAttribute(_wrs_conf_imageMathmlAttribute, wrs_mathmlEncode(mathml)); imgObject.src = result; if (_wrs_conf_setSize) { - wrs_setImgSize(imgObject,result, (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default') ? true : false); + wrs_setImgSize(imgObject, result, _wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default' ? true : false); } } if (window._wrs_conf_enableAccessibility && _wrs_conf_enableAccessibility) { @@ -2211,6 +2145,19 @@ function wrs_mathmlToImgObject(creator, mathml, wirisProperties, language) { wrs_populateAccessibleCache(mathml, imgObject.alt); } } + /* if (_wrs_conf_setSize) { + var ar = wrs_urlToAssArray(result); + width = ar['cw']; + height = ar['ch']; + baseline = ar['cb']; + dpi = ar['dpi']; + if (dpi) { + width = width * 96/dpi; + height = height * 96/dpi; + baseline = baseline * 96/dpi; + } + // result = wrs_assArrayToUrl(ar); + }*/ if (typeof wrs_observer != 'undefined') { wrs_observer.observe(imgObject, wrs_observer_config); @@ -2266,9 +2213,9 @@ function wrs_openCASWindow(target, isIframe, language) { function wrs_openEditorWindow(language, target, isIframe) { var ua = navigator.userAgent.toLowerCase(); var isAndroid = ua.indexOf("android") > -1; - var isIOS = ((ua.indexOf("ipad") > -1) || (ua.indexOf("iphone") > -1)); + var isIOS = ua.indexOf("ipad") > -1 || ua.indexOf("iphone") > -1; - if(isAndroid || isIOS) { + if (isAndroid || isIOS) { _wrs_conf_modalWindow = true; // Conf property must be overrided on tablet/phone devices. } @@ -2276,13 +2223,11 @@ function wrs_openEditorWindow(language, target, isIframe) { if (isIframe) { var selection = target.contentWindow.getSelection(); _wrs_range = selection.getRangeAt(0); - } - else { + } else { var selection = getSelection(); _wrs_range = selection.getRangeAt(0); } - } - catch (e) { + } catch (e) { _wrs_range = null; } @@ -2305,14 +2250,14 @@ function wrs_openEditorWindow(language, target, isIframe) { path = wrs_addArgument(path, "v", _wrs_plugin_version); var availableDirs = new Array('rtl', 'ltr'); - if (typeof _wrs_int_directionality != 'undefined' && wrs_arrayContains(availableDirs, _wrs_int_directionality) != -1){ - path = wrs_addArgument(path,"dir",_wrs_int_directionality); + if (typeof _wrs_int_directionality != 'undefined' && wrs_arrayContains(availableDirs, _wrs_int_directionality) != -1) { + path = wrs_addArgument(path, "dir", _wrs_int_directionality); } // Cross Domain Policy. wrs_addArgument(path, 'host', 'localhost'); - _wrs_editMode = (window._wrs_conf_defaultEditMode) ? _wrs_conf_defaultEditMode : 'images'; + _wrs_editMode = window._wrs_conf_defaultEditMode ? _wrs_conf_defaultEditMode : 'images'; _wrs_temporalRange = null; if (target) { @@ -2328,16 +2273,14 @@ function wrs_openEditorWindow(language, target, isIframe) { if (wrs_containsClass(selectedItem.node, _wrs_conf_imageClassName)) { if (selectedItem.node.nodeName.toUpperCase() == 'IMG') { _wrs_editMode = 'images'; - } - else if (selectedItem.node.nodeName.toUpperCase() == 'IFRAME') { + } else if (selectedItem.node.nodeName.toUpperCase() == 'IFRAME') { _wrs_editMode = 'iframes'; } _wrs_temporalImage = selectedItem.node; _wrs_isNewElement = false; } - } - else { + } else { var latexResult = wrs_getLatexFromTextNode(selectedItem.node, selectedItem.caretPosition); if (latexResult != null) { @@ -2348,7 +2291,7 @@ function wrs_openEditorWindow(language, target, isIframe) { _wrs_temporalImage = document.createElement('img'); _wrs_temporalImage.setAttribute(_wrs_conf_imageMathmlAttribute, wrs_mathmlEncode(mathml)); - var windowTarget = (isIframe) ? target.contentWindow : window; + var windowTarget = isIframe ? target.contentWindow : window; if (document.selection) { var leftOffset = 0; @@ -2363,8 +2306,7 @@ function wrs_openEditorWindow(language, target, isIframe) { _wrs_temporalRange.moveToElementText(latexResult.startNode.parentNode); _wrs_temporalRange.move('character', leftOffset + latexResult.startPosition); _wrs_temporalRange.moveEnd('character', latexResult.latex.length + 4); // Plus 4 for the '$$' characters. - } - else { + } else { _wrs_temporalRange = windowTarget.document.createRange(); _wrs_temporalRange.setStart(latexResult.startNode, latexResult.startPosition); _wrs_temporalRange.setEnd(latexResult.endNode, latexResult.endPosition); @@ -2373,29 +2315,37 @@ function wrs_openEditorWindow(language, target, isIframe) { } } } + // Parse atributes of editor into object + var splitterEditorAtributes = _wrs_conf_editorAttributes.split(", "); + var resultEditorAtributes = {}; + for (var i = 0, len = splitterEditorAtributes.length; i < len; i++) { + var tempAtribute = splitterEditorAtributes[i].split('='); + var key = tempAtribute[0]; + var value = tempAtribute[1]; + resultEditorAtributes[key] = value; + } + resultEditorAtributes.language = _wrs_int_langCode; var title = wrs_int_getCustomEditorEnabled() != null ? wrs_int_getCustomEditorEnabled().title : _wrs_stringManager.getString('mathtype'); - if (typeof _wrs_conf_modalWindow != 'undefined' && _wrs_conf_modalWindow === false) { - _wrs_popupWindow = window.open(path, title, _wrs_conf_editorAttributes); - return _wrs_popupWindow; + if (_wrs_modalWindow == null) { + _wrs_modalWindow = new ModalWindow(_wrs_conf_editorAttributes); + _wrs_modalWindow.setContentManager(new contentManager(resultEditorAtributes)); } - else { - if (_wrs_modalWindow == null) { - _wrs_modalWindow = new ModalWindow(path, _wrs_conf_editorAttributes); - } - if (!_wrs_css_loaded) { - var fileref = document.createElement("link"); - fileref.setAttribute("rel", "stylesheet"); - fileref.setAttribute("type", "text/css"); - fileref.setAttribute("href", wrs_concatenateUrl(_wrs_conf_path, '/core/modal.css')); - document.getElementsByTagName("head")[0].appendChild(fileref); - _wrs_css_loaded = true; - } - _wrs_modalWindow.open(); + if (!_wrs_css_loaded) { + var fileref = document.createElement("link"); + fileref.setAttribute("rel", "stylesheet"); + fileref.setAttribute("type", "text/css"); + fileref.setAttribute("href", wrs_concatenateUrl(_wrs_conf_path, '/core/modal.css')); + document.getElementsByTagName("head")[0].appendChild(fileref); + _wrs_css_loaded = true; } + // TODO: setMathML logic here. + _wrs_modalWindow.open(); } - +function Core() { + this.init = true; +} /** * Converts all occurrences of mathml code to LATEX. The MathML code should containg to be converted. @@ -2404,7 +2354,7 @@ function wrs_openEditorWindow(language, target, isIframe) { * @return {string} String with all MathML annotated occurrences replaced by the corresponding LaTeX code. * @ignore */ -function wrs_parseMathmlToLatex(content, characters){ +function wrs_parseMathmlToLatex(content, characters) { var output = ''; var mathTagBegin = characters.tagOpener + 'math'; var mathTagEnd = characters.tagOpener + '/math' + characters.tagCloser; @@ -2420,15 +2370,14 @@ function wrs_parseMathmlToLatex(content, characters){ if (end == -1) { end = content.length - 1; - } - else { + } else { end += mathTagEnd.length; } mathml = content.substring(start, end); startAnnotation = mathml.indexOf(openTarget); - if (startAnnotation != -1){ + if (startAnnotation != -1) { startAnnotation += openTarget.length; closeAnnotation = mathml.indexOf(closeTarget); var latex = mathml.substring(startAnnotation, closeAnnotation); @@ -2438,7 +2387,7 @@ function wrs_parseMathmlToLatex(content, characters){ output += '$$' + latex + '$$'; // Populate latex into cache. wrs_populateLatexCache(latex, mathml); - }else{ + } else { output += mathml; } @@ -2467,7 +2416,7 @@ function wrs_parseMathmlToImg(content, characters, language) { while (start != -1) { output += content.substring(end, start); // Avoid WIRIS images to be parsed. - imageMathmlAtrribute = content.indexOf(_wrs_conf_imageMathmlAttribute); + var imageMathmlAtrribute = content.indexOf(_wrs_conf_imageMathmlAttribute); end = content.indexOf(mathTagEnd, start); if (end == -1) { @@ -2476,17 +2425,15 @@ function wrs_parseMathmlToImg(content, characters, language) { // First close tag of img attribute // If a mathmlAttribute exists should be inside a img tag. end += content.indexOf("/>", start); - } - else { + } else { end += mathTagEnd.length; } - if (!wrs_isMathmlInAttribute(content, start) && imageMathmlAtrribute == -1){ + if (!wrs_isMathmlInAttribute(content, start) && imageMathmlAtrribute == -1) { var mathml = content.substring(start, end); - mathml = (characters == _wrs_safeXmlCharacters) ? wrs_mathmlDecode(mathml) : wrs_mathmlEntities(mathml); + mathml = characters == _wrs_safeXmlCharacters ? wrs_mathmlDecode(mathml) : wrs_mathmlEntities(mathml); output += wrs_createObjectCode(wrs_mathmlToImgObject(document, mathml, null, language)); - } - else { + } else { output += content.substring(start, end); } @@ -2518,8 +2465,7 @@ function wrs_parseSafeAppletsToObjects(content) { if (end == -1) { end = content.length - 1; - } - else { + } else { end += appletTagEnd.length; } @@ -2543,8 +2489,7 @@ function wrs_parseSafeAppletsToObjects(content) { function wrs_removeEvent(element, event, func) { if (element.removeEventListener) { element.removeEventListener(event, func, true); - } - else if (element.detachEvent) { + } else if (element.detachEvent) { element.detachEvent('on' + event, func); } } @@ -2599,18 +2544,18 @@ function wrs_updateCAS(focusElement, windowTarget, appletCode, image, imageWidth wrs_insertElementOnSelection(imgObject, focusElement, windowTarget); } -var wrs_PluginEvent = function () { +var wrs_PluginEvent = function wrs_PluginEvent() { this.cancelled = false; this.defaultPrevented = false; -} +}; wrs_PluginEvent.prototype.cancel = function () { this.cancelled = true; -} +}; wrs_PluginEvent.prototype.preventDefault = function () { this.defaultPrevented = true; -} +}; /** * Fires MathType event listeners @@ -2681,25 +2626,21 @@ function wrs_updateFormula(focusElement, windowTarget, mathml, wirisProperties, if (mathml.length == 0) { wrs_insertElementOnSelection(null, focusElement, windowTarget); - } - else if (editMode == 'latex') { + } else if (editMode == 'latex') { e.latex = wrs_getLatexFromMathML(mathml); // wrs_int_getNonLatexNode is an integration wrapper to have special behaviours for nonLatex. // Not all the integrations have special behaviours for nonLatex. if (typeof wrs_int_getNonLatexNode != 'undefined' && (typeof e.latex == 'undefined' || e.latex == null)) { wrs_int_getNonLatexNode(e, windowTarget, mathml); - } - else { + } else { e.node = windowTarget.document.createTextNode('$$' + e.latex + '$$'); wrs_populateLatexCache(e.latex, mathml); } wrs_insertElementOnSelection(e.node, focusElement, windowTarget); - } - else if (editMode == 'iframes') { + } else if (editMode == 'iframes') { var iframe = wrs_mathmlToIframeObject(windowTarget, mathml); wrs_insertElementOnSelection(iframe, focusElement, windowTarget); - } - else { + } else { e.node = wrs_mathmlToImgObject(windowTarget.document, mathml, wirisProperties, language); wrs_insertElementOnSelection(e.node, focusElement, windowTarget); } @@ -2723,8 +2664,7 @@ function wrs_updateTextarea(textarea, text) { var selectionEnd = textarea.selectionEnd; textarea.value = textarea.value.substring(0, textarea.selectionStart) + text + textarea.value.substring(textarea.selectionEnd, textarea.value.length); textarea.selectionEnd = selectionEnd + text.length; - } - else { + } else { var selection = document.selection.createRange(); selection.text = text; } @@ -2768,7 +2708,8 @@ function wrs_urlencode(clearString) { return output; } -function wrs_addArgument(path,key,value) { +function wrs_addArgument(path, key, value) { + var sep; if (path.indexOf("?") > 0) { sep = "&"; } else { @@ -2782,7 +2723,7 @@ function wrs_urlToAssArray(url) { i = url.indexOf("?"); if (i > 0) { var query = url.substring(i + 1); - var ss = query.split("&"); + var ss = query.split("&"); var h = new Object(); for (i = 0; i < ss.length; i++) { var s = ss[i]; @@ -2807,7 +2748,7 @@ function wrs_setImgSize(img, url, json) { if (_wrs_conf_saveMode != 'base64') { var ar = getMetricsFromSvgString(url); } else { - var base64String = img.src.substr( img.src.indexOf('base64,') + 7, img.src.length); + var base64String = img.src.substr(img.src.indexOf('base64,') + 7, img.src.length); var svgString = ''; var bytes = wrs_b64ToByteArray(base64String, base64String.length); for (var i = 0; i < bytes.length; i++) { @@ -2817,7 +2758,7 @@ function wrs_setImgSize(img, url, json) { } // PNG format: we store all metrics information in the first 88 bytes. } else { - var base64String = img.src.substr( img.src.indexOf('base64,') + 7, img.src.length); + var base64String = img.src.substr(img.src.indexOf('base64,') + 7, img.src.length); var bytes = wrs_b64ToByteArray(base64String, 88); var ar = wrs_getMetricsFromBytes(bytes); } @@ -2852,23 +2793,22 @@ function wrs_fixAfterResize(img) { if (img.src.indexOf("data:image") != -1) { if (_wrs_conf_imageFormat == 'svg') { // ...data:image/svg+xml;charset=utf8, = 32. - var svg = wrs_urldecode(img.src.substring(32, img.src.length)) + var svg = wrs_urldecode(img.src.substring(32, img.src.length)); wrs_setImgSize(img, svg, true); } else { // ...data:image/png;base64, == 22. - var base64 = img.src.substring(22,img.src.length); + var base64 = img.src.substring(22, img.src.length); wrs_setImgSize(img, base64, true); } } else { - wrs_setImgSize(img,img.src); + wrs_setImgSize(img, img.src); } } } function wrs_initSetSize() { // Override _wrs_conf_setSize to align formulas when xml or safeXml mode are enabled. - _wrs_conf_setSize = _wrs_conf_setSize || _wrs_conf_saveMode == 'xml' || _wrs_conf_saveMode == 'safeXml' || (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default') - || (_wrs_conf_saveMode == 'image' && _wrs_conf_imageFormat == 'svg'); + _wrs_conf_setSize = _wrs_conf_setSize || _wrs_conf_saveMode == 'xml' || _wrs_conf_saveMode == 'safeXml' || _wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default' || _wrs_conf_saveMode == 'image' && _wrs_conf_imageFormat == 'svg'; } /** @@ -2879,7 +2819,7 @@ function wrs_initSetSize() { */ function wrs_loadConfiguration() { if (typeof _wrs_conf_path == 'undefined') { - _wrs_conf_path = wrs_getCorePath(); + _wrs_conf_path = _wrs_corePath; } var httpRequest = typeof XMLHttpRequest != 'undefined' ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); @@ -2901,7 +2841,7 @@ function wrs_loadConfiguration() { wrs_loadServicePaths(configUrl); // End configuration. - _wrs_conf_configuration_loaded = true; + var _wrs_conf_configuration_loaded = true; if (typeof _wrs_conf_core_loaded != 'undefined') { _wrs_conf_plugin_loaded = true; } @@ -2918,55 +2858,45 @@ function wrs_loadServicePaths(url) { _wrs_conf_servicePath = url.replace('configurationjs', 'service'); } -_wrs_conf_plugin_loaded = true; +var _wrs_conf_plugin_loaded = true; -function wrs_getCorePath() { - var scriptName = "core/core.js"; - var col = document.getElementsByTagName("script"); - for (var i = 0; i < col.length; i++) { - var d; - var src; - d = col[i]; - src = d.src; - var j = src.lastIndexOf(scriptName); - if (j >= 0) { - // That's my script! - return src.substr(0, j - 1); +function wrs_loadLangFile() { + if (_wrs_conf_core_loaded) { + _wrs_stringManager = new StringManager(); + // When a language is not defined, put english (en) as default. + if (typeof _wrs_int_langCode == 'undefined' || _wrs_int_langCode == null) { + _wrs_int_langCode = 'en'; } - } -} -function wrs_loadLangFile() { - // When a language is not defined, put english (en) as default. - if (typeof _wrs_int_langCode == 'undefined' || _wrs_int_langCode == null) { - _wrs_int_langCode = 'en'; - } + var langArray = _wrs_languages.split(','); - langArray = _wrs_languages.split(','); + if (langArray.indexOf(_wrs_int_langCode) == -1) { + _wrs_int_langCode = _wrs_int_langCode.substr(0, 2); + } - if (langArray.indexOf(_wrs_int_langCode) == -1) { - _wrs_int_langCode = _wrs_int_langCode.substr(0,2); - } + if (langArray.indexOf(_wrs_int_langCode) == -1) { + _wrs_int_langCode = 'en'; + } - if (langArray.indexOf(_wrs_int_langCode) == -1) { - _wrs_int_langCode = 'en'; - } + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = _wrs_corePath + "/lang/" + _wrs_int_langCode + "/strings.js"; + // When strings are loaded, it loads into stringManager + script.onload = function () { - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = wrs_getCorePath() + "/lang/" + _wrs_int_langCode + "/strings.js"; - // When strings are loaded, it loads into stringManager - script.onload = function() { - _wrs_stringManager.loadStrings(wrs_strings); - // Unseting global language strings array to prevent access. - wrs_strings = null; - }; - document.getElementsByTagName('head')[0].appendChild(script); + _wrs_stringManager.loadStrings(wrs_strings); + // Unseting global language strings array to prevent access. + wrs_strings = null; + }; + document.getElementsByTagName('head')[0].appendChild(script); + } else { + setTimeout(wrs_loadLangFile, 100); + } } function wrs_concatenateUrl(path1, path2) { var separator = ""; - if ((path1.indexOf("/") != path1.length) && (path2.indexOf("/") != 0)) { + if (path1.indexOf("/") != path1.length && path2.indexOf("/") != 0) { separator = "/"; } return (path1 + separator + path2).replace(/([^:]\/)\/+/g, "$1"); @@ -2982,200 +2912,6 @@ if (typeof _wrs_conf_configuration_loaded == 'undefined') { _wrs_conf_plugin_loaded = true; } -/** - * Create modal window with embebbed iframe - * - * @param {string} title Modal window title - * @param {object} iframeParams iframe attributes - * @param {object} deviceProperties device properties like orientation, OS.. - * @param {object} modalProperites modal properties (like draggable). - * @ignore - */ - -function wrs_createModalWindow() { - // Adding css stylesheet. - if (!_wrs_css_loaded) { - var fileref = document.createElement("link"); - fileref.setAttribute("rel", "stylesheet"); - fileref.setAttribute("type", "text/css"); - fileref.setAttribute("href", wrs_concatenateUrl(_wrs_conf_path, '/core/modal.css')); - document.getElementsByTagName("head")[0].appendChild(fileref); - _wrs_css_loaded = true; - } - - _wrs_modalWindow.open(); -} - -/** - * Closes modal window - * @ignore - */ -function wrs_closeModalWindow() { - // We avoid to close window when it's closed - if(_wrs_modalWindow.isOpen()) { - wrs_int_disableCustomEditors(); - wrs_int_notifyWindowClosed(); - _wrs_editMode = (window._wrs_conf_defaultEditMode) ? _wrs_conf_defaultEditMode : 'images'; - _wrs_modalWindow.close(); - } -} - -/** - * Check content of editor before close action - * @ignore - */ -function wrs_showPopUpMessage() { - if (_wrs_modalWindow.properties.state == 'minimized') { - _wrs_modalWindow.stackModalWindow(); - } - _wrs_modalWindow.popup.show(); -} - -/** - * Create modal dialog for non mobile android devices. - * @param {modalDiv} modal overlay div. - * @param {containerDiv} modal window div. - * @param {iframe} embedded iframe. - * @param {iframeParams} embedded iframe params (height, width). - * @ignore - */ - -function wrs_createModalWindowAndroid() { - _wrs_modalWindowProperties.device = 'android'; - wrs_addClass(_wrs_modalWindow.iframeContainer, 'wrs_modal_android'); - _wrs_modalWindow.overlayDiv.className = _wrs_modalWindow.overlayDiv.className + " wrs_modal_android"; - _wrs_modalWindow.containerDiv.className = _wrs_modalWindow.containerDiv.className + " wrs_modal_android"; - _wrs_modalWindow.iframe.className = _wrs_modalWindow.iframe.className + " wrs_modal_android"; -} - -/** - * Create modal dialog for non mobile iOS devices. - * @param {modalDiv} modal overlay div. - * @param {containerDiv} modal window div. - * @param {iframe} embedded iframe. - * @param {iframeParams} embedded iframe params (height, width). - * @ignore - */ -function wrs_createModalWindowIos() { - wrs_addClass(iframeContainer, 'wrs_modal_ios'); - _wrs_modalWindow.overlayDiv.className = _wrs_modalWindow.overlayDiv.className + " wrs_modal_ios"; - if (typeof _wrs_isMoodle24 != 'undefined') { - _wrs_modalWindow.overlayDiv.className = _wrs_modalWindow.overlayDiv.className + " moodle"; - } - - _wrs_modalWindow.containerDiv.className = _wrs_modalWindow.containerDiv.className + " wrs_modal_ios"; - _wrs_modalWindow.iframe.className = _wrs_modalWindow.iframe.className + " wrs_modal_ios"; -} - -/** - * Create modal dialog for mobile devices. - * - * @param {modalDiv} modal overlay div. - * @param {containerDiv} modal window div. - * @param {iframe} embedded iframe. - * @param {iframeParams} embedded iframe params (height, width). - * @ignore - */ - -function wrs_createModalWindowMobile(modalDiv, containerDiv, iframe, iframeParams, iframeContainer) { - - wrs_addClass(_wrs_modalWindow.iframeContainer, 'wrs_modal_mobile'); - _wrs_modalWindow.overlayDiv.className = _wrs_modalWindow.overlayDiv.className + " wrs_modal_mobile"; - _wrs_modalWindow.containerDiv.className = _wrs_modalWindow.containerDiv.className + " wrs_modal_mobile"; - _wrs_modalWindow.iframe.className = _wrs_modalWindow.iframe.className + " wrs_modal_mobile"; - - wrs_addMetaViewport("device-width", 1.0, 1.0, 1.0); - - var modalTitleBar = document.getElementsByClassName('wrs_modal_title')[0] - - if (modalTitleBar) { - document.removeChild(modalTitleBar); - } -} - -/** - * Create modal dialog for Androir mobile devices with an old stock browser (<=4.3). - * - * @param {modalDiv} modal overlay div. - * @param {containerDiv} modal window div. - * @param {iframe} embedded iframe. - * @param {iframeParams} embedded iframe params (height, width). - * @ignore - */ -function wrs_createModalWindowBadStockAndroid(modalDiv, containerDiv, iframe, iframeParams) { - _wrs_modalWindow.overlayDiv.className = _wrs_modalWindow.overlayDiv.className + " wrs_modal_badStock"; - _wrs_modalWindow.containerDiv.className = _wrs_modalWindow.containerDiv.className + " wrs_modal_badStock"; - _wrs_modalWindow.iframe.className = _wrs_modalWindow.iframe.className + " wrs_modal_badStock"; - - if (window.outerWidth < parseInt(_wrs_modalWindowProperties.iframeAttributes['width'])) { - var modalWidth = parseInt(_wrs_modalWindowProperties.iframeAttributes['width']) + 10; - _wrs_modalWindow.containerDiv.style.width = _wrs_modalWindowProperties.iframeAttributes['width'] + 'px'; - _wrs_modalWindow.iframe.style.width = _wrs_modalWindowProperties.iframeAttributes['width'] + 'px'; - } - - window.addEventListener('orientationchange', function() { - if (window.outerWidth > parseInt(_wrs_modalWindowProperties.iframeAttributes['width']) + 10) { - var modalWidth = parseInt(_wrs_modalWindowProperties.iframeAttributes['width']) + 10; - _wrs_modalWindow.containerDiv.style.width = modalWidth + 'px'; - _wrs_modalWindow.iframe.style.width = _wrs_modalWindowProperties.iframeAttributes['width'] + 'px'; - } else { - _wrs_modalWindow.containerDiv.style.width = null; - _wrs_modalWindow.iframe.style.width = null; - } - }); - - wrs_addMetaViewport("device-width", 1.0, 1.0, 1.0); -} - -/** - * Add viewport header for scale control. - * - * @param {int} width width of the layout viewport. - * @param {int} initialScale Sets the initial zoom of the page and the width of the layout viewport. - * @param {int} minimumScale Sets the minimum zoom level (i.e. how much the user can zoom out). - * @param {int} maximumScale Sets the maximum zoom level (i.e. how much the user can zoom in). - * @ignore - */ -function wrs_addMetaViewport(width, initialScale, minimumScale, maximumScale) { - _wrs_originalMetaViewport = document.querySelector('meta[name=viewport]') ? document.querySelector('meta[name=viewport]').content : null; - if (_wrs_originalMetaViewport) { - document.querySelector('meta[name=viewport]').content = "width=" + width + ", initial-scale=" + initialScale + ", minimum-scale=" + minimumScale + ", maximum-scale=" + maximumScale; - } else { - var attributes = {}; - attributes['name'] = 'viewport'; - attributes['content'] = "width=" + width + ", initial-scale=" + initialScale + ", minimum-scale=" + minimumScale + ", maximum-scale=" + maximumScale; - var meta = wrs_createElement('meta', attributes); - document.getElementsByTagName("head")[0].appendChild(meta); - } -} - - -/** - * Android stock browser test - * http://stackoverflow.com/questions/24926221/distinguish-android-chrome-from-stock-browser-stock-browsers-user-agent-contai - * - * @return {Boolean} true if user agent is from an Android stock browser (<= 4.3) - * @ignore - */ -function wrs_isBadStockAndroid () { - var userAgent = window.navigator.userAgent; - // Android stock browser test derived from - // http://stackoverflow.com/questions/24926221/distinguish-android-chrome-from-stock-browser-stock-browsers-user-agent-contai. - var isAndroid = userAgent.indexOf(' Android ') > -1; - if (!isAndroid) { - return false; - } - - var isStockAndroid = userAgent.indexOf('Version/') > -1; - if (!isStockAndroid) { - return false; - } - - var versionNumber = parseFloat((userAgent.match('Android ([0-9.]+)') || [])[1]); - // Anything below 4.4 uses WebKit without *any* viewport support. - return versionNumber <= 4.3; -} - /** * Populates LaTeX cache into _wrs_int_LatexCache global variable. * @@ -3184,7 +2920,7 @@ function wrs_isBadStockAndroid () { * @ignore */ function wrs_populateLatexCache(latex, mathml) { - if (mathml.indexOf('semantics') == -1 && mathml.indexOf('annotation') == -1 ) { + if (mathml.indexOf('semantics') == -1 && mathml.indexOf('annotation') == -1) { mathml = wrs_insertSemanticsMathml(mathml, latex); } if (!_wrs_int_LatexCache.hasOwnProperty(latex)) { @@ -3247,7 +2983,7 @@ function wrs_insertSemanticsMathml(mathml, latex) { var indexMathEnd = mathml.indexOf(mathTagEnd); var mathBeginExists = mathml.substring(mathml.indexOf('<'), mathml.indexOf('>')).indexOf('math'); - if (indexMathBegin != -1 && indexMathEnd != -1 && mathBeginExists) { + if (indexMathBegin != -1 && indexMathEnd != -1 && mathBeginExists) { var mathmlContent = mathml.substring(indexMathBegin + 1, indexMathEnd); if (mathmlContent.indexOf(mrowOpen) != 0) { var mathmlContentSemantics = openSemantics + mrowOpen + mathmlContent + mrowClose + openTarget + latex + closeTarget + closeSemantics; @@ -3258,7 +2994,6 @@ function wrs_insertSemanticsMathml(mathml, latex) { } else { return mathml; } - } /** @@ -3291,7 +3026,7 @@ function wrs_removeSemanticsMathml(mathml) { * @ignore */ function wrs_codeImgTransform(code, mode) { - output = ''; + var output = ''; var endPosition = 0; var pattern = /') { + } else if (character == '>') { endPosition = i + 1; } ++i; } - if (endPosition < startPosition) { // The img tag is stripped. + if (endPosition < startPosition) { + // The img tag is stripped. output += code.substring(startPosition, code.length); return output; } var imgCode = code.substring(startPosition, endPosition); var imgObject = wrs_createObject(imgCode); var xmlCode = imgObject.getAttribute(_wrs_conf_imageMathmlAttribute); + var convertToXml; + var convertToSafeXml; if (mode == 'base642showimage') { if (xmlCode == null) { @@ -3343,8 +3079,7 @@ function wrs_codeImgTransform(code, mode) { if (_wrs_conf_saveMode == 'safeXml') { convertToXml = true; convertToSafeXml = true; - } - else if (_wrs_conf_saveMode == 'xml') { + } else if (_wrs_conf_saveMode == 'xml') { convertToXml = true; convertToSafeXml = false; } @@ -3359,7 +3094,7 @@ function wrs_codeImgTransform(code, mode) { var properties = {}; properties['base64'] = 'true'; - imgCode = wrs_mathmlToImgObject(document, xmlCode, properties, null) + imgCode = wrs_mathmlToImgObject(document, xmlCode, properties, null); // Metrics. wrs_setImgSize(imgCode, imgCode.src, true); @@ -3391,20 +3126,20 @@ function wrs_decode64(el) { if (code === PLUS || code === PLUS_URL_SAFE) { return 62; // Char '+'. } - if (code === SLASH || code === SLASH_URL_SAFE){ - return 63 // Char '/'. + if (code === SLASH || code === SLASH_URL_SAFE) { + return 63; // Char '/'. } - if (code < NUMBER){ - return -1 // No match. + if (code < NUMBER) { + return -1; // No match. } - if (code < NUMBER + 10){ - return code - NUMBER + 26 + 26 + if (code < NUMBER + 10) { + return code - NUMBER + 26 + 26; } - if (code < UPPER + 26){ - return code - UPPER + if (code < UPPER + 26) { + return code - UPPER; } - if (code < LOWER + 26){ - return code - LOWER + 26 + if (code < LOWER + 26) { + return code - LOWER + 26; } } @@ -3420,13 +3155,14 @@ function wrs_b64ToByteArray(b64String, len) { var tmp; if (b64String.length % 4 > 0) { - throw new Error(_wrs_stringManager.getString('exception_string_length')); // Tipped base64. Length is fixed. + throw new Error('Invalid string. Length must be a multiple of 4'); // Tipped base64. Length is fixed. } - var arr = new Array() + var arr = new Array(); - if (!len) { // All b64String string. - var placeHolders = b64String.charAt(b64String.length - 2) === '=' ? 2 : b64String.charAt(b64String.length - 1) === '=' ? 1 : 0 + if (!len) { + // All b64String string. + var placeHolders = b64String.charAt(b64String.length - 2) === '=' ? 2 : b64String.charAt(b64String.length - 1) === '=' ? 1 : 0; var l = placeHolders > 0 ? b64String.length - 4 : b64String.length; } else { var l = len; @@ -3436,10 +3172,10 @@ function wrs_b64ToByteArray(b64String, len) { // Ignoring code checker standards (bitewise operators). // See https://tracker.moodle.org/browse/CONTRIB-5862 for further information. // @codingStandardsIgnoreStart - tmp = (wrs_decode64(b64String.charAt(i)) << 18) | (wrs_decode64(b64String.charAt(i + 1)) << 12) | (wrs_decode64(b64String.charAt(i + 2)) << 6) | wrs_decode64(b64String.charAt(i + 3)); + tmp = wrs_decode64(b64String.charAt(i)) << 18 | wrs_decode64(b64String.charAt(i + 1)) << 12 | wrs_decode64(b64String.charAt(i + 2)) << 6 | wrs_decode64(b64String.charAt(i + 3)); - arr.push((tmp >> 16) & 0xFF); - arr.push((tmp >> 8) & 0xFF); + arr.push(tmp >> 16 & 0xFF); + arr.push(tmp >> 8 & 0xFF); arr.push(tmp & 0xFF); // @codingStandardsIgnoreEnd } @@ -3448,17 +3184,17 @@ function wrs_b64ToByteArray(b64String, len) { if (placeHolders === 2) { // Ignoring code checker standards (bitewise operators). // @codingStandardsIgnoreStart - tmp = (wrs_decode64(b64String.charAt(i)) << 2) | (wrs_decode64(b64String.charAt(i + 1)) >> 4); - arr.push(tmp & 0xFF) + tmp = wrs_decode64(b64String.charAt(i)) << 2 | wrs_decode64(b64String.charAt(i + 1)) >> 4; + arr.push(tmp & 0xFF); } else if (placeHolders === 1) { - tmp = (wrs_decode64(b64String.charAt(i)) << 10) | (wrs_decode64(b64String.charAt(i + 1)) << 4) | (wrs_decode64(b64String.charAt(i + 2)) >> 2) - arr.push((tmp >> 8) & 0xFF); + tmp = wrs_decode64(b64String.charAt(i)) << 10 | wrs_decode64(b64String.charAt(i + 1)) << 4 | wrs_decode64(b64String.charAt(i + 2)) >> 2; + arr.push(tmp >> 8 & 0xFF); arr.push(tmp & 0xFF); // @codingStandardsIgnoreEnd } } - return arr + return arr; } /** @@ -3471,9 +3207,9 @@ function wrs_readInt32(bytes) { if (bytes.length < 4) { return false; } - var int32 = bytes.splice(0,4); + var int32 = bytes.splice(0, 4); // @codingStandardsIgnoreStart - return (int32[0] << 24 | int32[1] << 16 | int32[2] << 8 | int32[3] << 0); + return int32[0] << 24 | int32[1] << 16 | int32[2] << 8 | int32[3] << 0; // @codingStandardsIgnoreEnd } @@ -3487,7 +3223,6 @@ function wrs_readByte(bytes) { // @codingStandardsIgnoreStart return bytes.shift() << 0; // @codingStandardsIgnoreEnd - } /** @@ -3510,22 +3245,24 @@ function wrs_readBytes(bytes, pos, len) { */ function wrs_getMetricsFromBytes(bytes) { wrs_readBytes(bytes, 0, 8); - alloc = 10; - i = 0; + var alloc = 10; + var i = 0; while (bytes.length >= 4) { - len = wrs_readInt32(bytes); - typ = wrs_readInt32(bytes); + var len = wrs_readInt32(bytes); + var typ = wrs_readInt32(bytes); if (typ == 0x49484452) { - width = wrs_readInt32(bytes); - height = wrs_readInt32(bytes); + var width = wrs_readInt32(bytes); + var height = wrs_readInt32(bytes); // Read 5 bytes. wrs_readInt32(bytes); wrs_readByte(bytes); - } else if (typ == 0x62615345) { // Baseline: 'baSE'. - baseline = wrs_readInt32(bytes); - } else if (typ == 0x70485973) { // Dpis: 'pHYs'. - dpi = wrs_readInt32(bytes); - dpi = (Math.round(dpi / 39.37)); + } else if (typ == 0x62615345) { + // Baseline: 'baSE'. + var baseline = wrs_readInt32(bytes); + } else if (typ == 0x70485973) { + // Dpis: 'pHYs'. + var dpi = wrs_readInt32(bytes); + dpi = Math.round(dpi / 39.37); wrs_readInt32(bytes); wrs_readByte(bytes); } @@ -3547,28 +3284,27 @@ function wrs_getMetricsFromBytes(bytes) { function getMetricsFromSvgString(svgString) { var first = svgString.indexOf('height="'); - var last = svgString.indexOf('"',first + 8, svgString.length); + var last = svgString.indexOf('"', first + 8, svgString.length); var height = svgString.substring(first + 8, last); first = svgString.indexOf('width="'); - last = svgString.indexOf('"',first + 7, svgString.length); + last = svgString.indexOf('"', first + 7, svgString.length); var width = svgString.substring(first + 7, last); first = svgString.indexOf('wrs:baseline="'); - last = svgString.indexOf('"',first + 14, svgString.length); + last = svgString.indexOf('"', first + 14, svgString.length); var baseline = svgString.substring(first + 14, last); - if (typeof(width != 'undefined')) { + if (_typeof(width != 'undefined')) { var arr = new Array(); arr['cw'] = width; arr['ch'] = height; if (typeof baseline != 'undefined') { - arr['cb'] = baseline + arr['cb'] = baseline; } return arr; } - } /** @@ -3587,14 +3323,13 @@ function wrs_int_getCustomEditorEnabled() { return customEditorEnabled; } - /** * Disable all custom editors * @ignore */ -function wrs_int_disableCustomEditors(){ - Object.keys(_wrs_int_customEditors).forEach(function(key) { - _wrs_int_customEditors[key].enabled = false; +function wrs_int_disableCustomEditors() { + Object.keys(_wrs_int_customEditors).forEach(function (key) { + _wrs_int_customEditors[key].enabled = false; }); } @@ -3619,7 +3354,7 @@ function wrs_int_enableCustomEditor(editor) { */ function wrs_propertiesToString(h) { // 1. Sort keys. We sort the keys because we want a deterministic output. - var keys = [] + var keys = []; for (var key in h) { if (h.hasOwnProperty(key)) { keys.push(key); @@ -3631,7 +3366,7 @@ function wrs_propertiesToString(h) { for (var j = i + 1; j < n; j++) { var s1 = keys[i]; var s2 = keys[j]; - if (wrs_compareStrings(s1,s2) > 0) { + if (wrs_compareStrings(s1, s2) > 0) { // Swap. keys[i] = s2; keys[j] = s1; @@ -3664,44 +3399,39 @@ function wrs_propertiesToString(h) { * @return {int} the int difference between a and b * @ignore */ -function wrs_compareStrings(a, b){ +function wrs_compareStrings(a, b) { var i; var an = a.length; var bn = b.length; - var n = (an > bn) ? bn : an; - for(i = 0; i < n; i++){ - var c = wrs_fixedCharCodeAt(a,i) - wrs_fixedCharCodeAt(b,i); - if(c != 0) { + var n = an > bn ? bn : an; + for (i = 0; i < n; i++) { + var c = wrs_fixedCharCodeAt(a, i) - wrs_fixedCharCodeAt(b, i); + if (c != 0) { return c; } } - return a.length - b.length; + return a.length - b.length; } // Polyfills. if (!Object.keys) { - Object.keys = (function () { + Object.keys = function () { 'use strict'; + var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; + hasDontEnumBug = !{ toString: null }.propertyIsEnumerable('toString'), + dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'], + dontEnumsLength = dontEnums.length; return function (obj) { - if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { - throw new TypeError(_wrs_stringManager.getString('exception_key_nonobject')); + if ((typeof obj === "undefined" ? "undefined" : _typeof(obj)) !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); } - var result = [], prop, i; + var result = [], + prop, + i; for (prop in obj) { if (hasOwnProperty.call(obj, prop)) { @@ -3718,14 +3448,15 @@ if (!Object.keys) { } return result; }; - }()); + }(); } /*! http://mths.be/codepointat v0.1.0 by @mathias */ if (!String.prototype.codePointAt) { - (function() { + (function () { 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` - var codePointAt = function(position) { + + var codePointAt = function codePointAt(position) { if (this == null) { throw TypeError(); } @@ -3733,7 +3464,8 @@ if (!String.prototype.codePointAt) { var size = string.length; // `ToInteger` var index = position ? Number(position) : 0; - if (index != index) { // better `isNaN` + if (index != index) { + // better `isNaN` index = 0; } // Account for out-of-bounds indices: @@ -3744,15 +3476,16 @@ if (!String.prototype.codePointAt) { var first = string.charCodeAt(index); var second; if ( // check if it’s the start of a surrogate pair - first >= 0xD800 && first <= 0xDBFF && // high surrogate - size > index + 1 // there is a next code unit + first >= 0xD800 && first <= 0xDBFF && // high surrogate + size > index + 1 // there is a next code unit ) { - second = string.charCodeAt(index + 1); - if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate - // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae - return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + second = string.charCodeAt(index + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { + // low surrogate + // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } } - } return first; }; if (Object.defineProperty) { @@ -3764,7 +3497,7 @@ if (!String.prototype.codePointAt) { } else { String.prototype.codePointAt = codePointAt; } - }()); + })(); } /** @@ -3776,24 +3509,12 @@ function wrs_addPluginListener(listener) { wrs_pluginListeners.push(listener); } -/** - * For now its not possible comunicate directly between editor.js and ModalWindow object. - * We need to use this method to call ModalWindow prototype from editor.js - * @param {object} editor - * @ignore - */ -function wrs_setModalWindowEditor(editor) { - if (_wrs_conf_modalWindow) { - _wrs_modalWindow.setEditor(editor); - } -} - /** * Get the base URL (i.e the URL on core.js lives). * @ignore */ function wrs_getServerPath() { - url = wrs_getCorePath(); + var url = _wrs_corePath; var hostNameIndex = url.indexOf("/", url.indexOf("/") + 2); return url.substr(0, hostNameIndex); } @@ -3805,10 +3526,10 @@ function wrs_getServerPath() { */ function wrs_updateContextPath() { if (typeof _wrs_conf_plugin_loaded == 'undefined') { - setTimeout(wrs_updateContextPath, 100); + setTimeout(wrs_updateContextPath, 100); } else { if (_wrs_conf_showimagePath.indexOf("/") == 0) { - serverPath = wrs_getServerPath() + var serverPath = wrs_getServerPath(); _wrs_conf_showimagePath = serverPath + _wrs_conf_showimagePath; _wrs_conf_editorPath = serverPath + _wrs_conf_editorPath; _wrs_conf_CASPath = serverPath + _wrs_conf_CASPath; @@ -3826,12 +3547,12 @@ wrs_updateContextPath(); // Reference: http://es5.github.io/#x15.4.4.18. if (!Array.prototype.forEach) { - Array.prototype.forEach = function(callback, thisArg) { + Array.prototype.forEach = function (callback, thisArg) { var T, k; if (this == null) { - throw new TypeError(_wrs_stringManager.getString('exception_null_or_undefined')); + throw new TypeError(' this is null or not defined'); } // 1. Let O be the result of calling ToObject passing the |this| value as the argument. @@ -3847,7 +3568,7 @@ if (!Array.prototype.forEach) { // 4. If IsCallable(callback) is false, throw a TypeError exception. // See: http://es5.github.com/#x9.11 . if (typeof callback !== "function") { - throw new TypeError(callback + _wrs_stringManager.getString('exception_not_function')); + throw new TypeError(callback + ' is not a function'); } // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. @@ -3864,9 +3585,9 @@ if (!Array.prototype.forEach) { var kValue; // A. Let Pk be ToString(k). - // This is implicit for LHS operands of the in operator + // This is implicit for LHS operands of the in operator // B. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk - // This step can be combined with c + // This step can be combined with c // C. If kPresent is true. if (k in O) { @@ -3883,1879 +3604,2512 @@ if (!Array.prototype.forEach) { // 8. return undefined. }; } -/** - * EditorListener constructor - * @ignore - */ -function EditorListener(){ - this.isContentChanged = false; - this.waitingForChanges = false; -} -/** - * EditorListener method set if content is changed - * @ignore - */ -EditorListener.prototype.setIsContentChanged = function(value){ - this.isContentChanged = value; -} -/** - * EditorListener method to get if content is changed - * @ignore - */ -EditorListener.prototype.getIsContentChanged = function(value){ - return this.isContentChanged; -} -/** - * EditorListener method to wait changes - * @ignore - */ -EditorListener.prototype.setWaitingForChanges = function(value){ - this.waitingForChanges = value; -} -/** - * EditorListener method to overwrite - * @ignore - */ -EditorListener.prototype.caretPositionChanged = function(editor){}; -/** - * EditorListener method to overwrite - * @ignore - */ -EditorListener.prototype.clipboardChanged = function(editor){}; -/** - * EditorListener method to set if content is changed - * @ignore - */ -EditorListener.prototype.contentChanged = function(editor){ - if(this.waitingForChanges === true && this.isContentChanged === false){ - this.isContentChanged = true; +// @codingStandardsIgnoreStart +(function () { + var HxOverrides = function HxOverrides() {}; + HxOverrides.__name__ = true; + HxOverrides.dateStr = function (date) { + var m = date.getMonth() + 1; + var d = date.getDate(); + var h = date.getHours(); + var mi = date.getMinutes(); + var s = date.getSeconds(); + return date.getFullYear() + "-" + (m < 10 ? "0" + m : "" + m) + "-" + (d < 10 ? "0" + d : "" + d) + " " + (h < 10 ? "0" + h : "" + h) + ":" + (mi < 10 ? "0" + mi : "" + mi) + ":" + (s < 10 ? "0" + s : "" + s); + }; + HxOverrides.strDate = function (s) { + switch (s.length) { + case 8: + var k = s.split(":"); + var d = new Date(); + d.setTime(0); + d.setUTCHours(k[0]); + d.setUTCMinutes(k[1]); + d.setUTCSeconds(k[2]); + return d; + case 10: + var k = s.split("-"); + return new Date(k[0], k[1] - 1, k[2], 0, 0, 0); + case 19: + var k = s.split(" "); + var y = k[0].split("-"); + var t = k[1].split(":"); + return new Date(y[0], y[1] - 1, y[2], t[0], t[1], t[2]); + default: + throw "Invalid date format : " + s; + } + }; + HxOverrides.cca = function (s, index) { + var x = s.charCodeAt(index); + if (x != x) return undefined; + return x; + }; + HxOverrides.substr = function (s, pos, len) { + if (pos != null && pos != 0 && len != null && len < 0) return ""; + if (len == null) len = s.length; + if (pos < 0) { + pos = s.length + pos; + if (pos < 0) pos = 0; + } else if (len < 0) len = s.length + len - pos; + return s.substr(pos, len); + }; + HxOverrides.remove = function (a, obj) { + var i = 0; + var l = a.length; + while (i < l) { + if (a[i] == obj) { + a.splice(i, 1); + return true; + } + i++; + } + return false; + }; + HxOverrides.iter = function (a) { + return { cur: 0, arr: a, hasNext: function hasNext() { + return this.cur < this.arr.length; + }, next: function next() { + return this.arr[this.cur++]; + } }; + }; + var IntIter = function IntIter(min, max) { + this.min = min; + this.max = max; + }; + IntIter.__name__ = true; + IntIter.prototype = { + next: function next() { + return this.min++; + }, + hasNext: function hasNext() { + return this.min < this.max; + }, + __class__: IntIter + }; + var Std = function Std() {}; + Std.__name__ = true; + Std["is"] = function (v, t) { + return js.Boot.__instanceof(v, t); + }; + Std.string = function (s) { + return js.Boot.__string_rec(s, ""); + }; + Std["int"] = function (x) { + return x | 0; + }; + Std.parseInt = function (x) { + var v = parseInt(x, 10); + if (v == 0 && (HxOverrides.cca(x, 1) == 120 || HxOverrides.cca(x, 1) == 88)) v = parseInt(x); + if (isNaN(v)) return null; + return v; + }; + Std.parseFloat = function (x) { + return parseFloat(x); + }; + Std.random = function (x) { + return Math.floor(Math.random() * x); + }; + var com = com || {}; + if (!com.wiris) com.wiris = {}; + if (!com.wiris.js) com.wiris.js = {}; + com.wiris.js.JsPluginTools = function () { + this.tryReady(); + }; + com.wiris.js.JsPluginTools.__name__ = true; + com.wiris.js.JsPluginTools.main = function () { + var ev; + ev = com.wiris.js.JsPluginTools.getInstance(); + haxe.Timer.delay($bind(ev, ev.tryReady), 100); + }; + com.wiris.js.JsPluginTools.getInstance = function () { + if (com.wiris.js.JsPluginTools.instance == null) com.wiris.js.JsPluginTools.instance = new com.wiris.js.JsPluginTools(); + return com.wiris.js.JsPluginTools.instance; + }; + com.wiris.js.JsPluginTools.bypassEncapsulation = function () { + if (window.com == null) window.com = {}; + if (window.com.wiris == null) window.com.wiris = {}; + if (window.com.wiris.js == null) window.com.wiris.js = {}; + if (window.com.wiris.js.JsPluginTools == null) window.com.wiris.js.JsPluginTools = com.wiris.js.JsPluginTools.getInstance(); + }; + com.wiris.js.JsPluginTools.prototype = { + md5encode: function md5encode(content) { + return haxe.Md5.encode(content); + }, + doLoad: function doLoad() { + this.ready = true; + com.wiris.js.JsPluginTools.instance = this; + com.wiris.js.JsPluginTools.bypassEncapsulation(); + }, + tryReady: function tryReady() { + this.ready = false; + if (js.Lib.document.readyState) { + this.doLoad(); + this.ready = true; + } + if (!this.ready) haxe.Timer.delay($bind(this, this.tryReady), 100); + }, + __class__: com.wiris.js.JsPluginTools + }; + var haxe = haxe || {}; + haxe.Log = function () {}; + haxe.Log.__name__ = true; + haxe.Log.trace = function (v, infos) { + js.Boot.__trace(v, infos); + }; + haxe.Log.clear = function () { + js.Boot.__clear_trace(); + }; + haxe.Md5 = function () {}; + haxe.Md5.__name__ = true; + haxe.Md5.encode = function (s) { + return new haxe.Md5().doEncode(s); + }; + haxe.Md5.prototype = { + doEncode: function doEncode(str) { + var x = this.str2blks(str); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var step; + var i = 0; + while (i < x.length) { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + step = 0; + a = this.ff(a, b, c, d, x[i], 7, -680876936); + d = this.ff(d, a, b, c, x[i + 1], 12, -389564586); + c = this.ff(c, d, a, b, x[i + 2], 17, 606105819); + b = this.ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = this.ff(a, b, c, d, x[i + 4], 7, -176418897); + d = this.ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = this.ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = this.ff(b, c, d, a, x[i + 7], 22, -45705983); + a = this.ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = this.ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = this.ff(c, d, a, b, x[i + 10], 17, -42063); + b = this.ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = this.ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = this.ff(d, a, b, c, x[i + 13], 12, -40341101); + c = this.ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = this.ff(b, c, d, a, x[i + 15], 22, 1236535329); + a = this.gg(a, b, c, d, x[i + 1], 5, -165796510); + d = this.gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = this.gg(c, d, a, b, x[i + 11], 14, 643717713); + b = this.gg(b, c, d, a, x[i], 20, -373897302); + a = this.gg(a, b, c, d, x[i + 5], 5, -701558691); + d = this.gg(d, a, b, c, x[i + 10], 9, 38016083); + c = this.gg(c, d, a, b, x[i + 15], 14, -660478335); + b = this.gg(b, c, d, a, x[i + 4], 20, -405537848); + a = this.gg(a, b, c, d, x[i + 9], 5, 568446438); + d = this.gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = this.gg(c, d, a, b, x[i + 3], 14, -187363961); + b = this.gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = this.gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = this.gg(d, a, b, c, x[i + 2], 9, -51403784); + c = this.gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = this.gg(b, c, d, a, x[i + 12], 20, -1926607734); + a = this.hh(a, b, c, d, x[i + 5], 4, -378558); + d = this.hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = this.hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = this.hh(b, c, d, a, x[i + 14], 23, -35309556); + a = this.hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = this.hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = this.hh(c, d, a, b, x[i + 7], 16, -155497632); + b = this.hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = this.hh(a, b, c, d, x[i + 13], 4, 681279174); + d = this.hh(d, a, b, c, x[i], 11, -358537222); + c = this.hh(c, d, a, b, x[i + 3], 16, -722521979); + b = this.hh(b, c, d, a, x[i + 6], 23, 76029189); + a = this.hh(a, b, c, d, x[i + 9], 4, -640364487); + d = this.hh(d, a, b, c, x[i + 12], 11, -421815835); + c = this.hh(c, d, a, b, x[i + 15], 16, 530742520); + b = this.hh(b, c, d, a, x[i + 2], 23, -995338651); + a = this.ii(a, b, c, d, x[i], 6, -198630844); + d = this.ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = this.ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = this.ii(b, c, d, a, x[i + 5], 21, -57434055); + a = this.ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = this.ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = this.ii(c, d, a, b, x[i + 10], 15, -1051523); + b = this.ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = this.ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = this.ii(d, a, b, c, x[i + 15], 10, -30611744); + c = this.ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = this.ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = this.ii(a, b, c, d, x[i + 4], 6, -145523070); + d = this.ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = this.ii(c, d, a, b, x[i + 2], 15, 718787259); + b = this.ii(b, c, d, a, x[i + 9], 21, -343485551); + a = this.addme(a, olda); + b = this.addme(b, oldb); + c = this.addme(c, oldc); + d = this.addme(d, oldd); + i += 16; + } + return this.rhex(a) + this.rhex(b) + this.rhex(c) + this.rhex(d); + }, + ii: function ii(a, b, c, d, x, s, t) { + return this.cmn(this.bitXOR(c, this.bitOR(b, ~d)), a, b, x, s, t); + }, + hh: function hh(a, b, c, d, x, s, t) { + return this.cmn(this.bitXOR(this.bitXOR(b, c), d), a, b, x, s, t); + }, + gg: function gg(a, b, c, d, x, s, t) { + return this.cmn(this.bitOR(this.bitAND(b, d), this.bitAND(c, ~d)), a, b, x, s, t); + }, + ff: function ff(a, b, c, d, x, s, t) { + return this.cmn(this.bitOR(this.bitAND(b, c), this.bitAND(~b, d)), a, b, x, s, t); + }, + cmn: function cmn(q, a, b, x, s, t) { + return this.addme(this.rol(this.addme(this.addme(a, q), this.addme(x, t)), s), b); + }, + rol: function rol(num, cnt) { + return num << cnt | num >>> 32 - cnt; + }, + str2blks: function str2blks(str) { + var nblk = (str.length + 8 >> 6) + 1; + var blks = new Array(); + var _g1 = 0, + _g = nblk * 16; + while (_g1 < _g) { + var i = _g1++; + blks[i] = 0; + } + var i = 0; + while (i < str.length) { + blks[i >> 2] |= HxOverrides.cca(str, i) << (str.length * 8 + i) % 4 * 8; + i++; + } + blks[i >> 2] |= 128 << (str.length * 8 + i) % 4 * 8; + var l = str.length * 8; + var k = nblk * 16 - 2; + blks[k] = l & 255; + blks[k] |= (l >>> 8 & 255) << 8; + blks[k] |= (l >>> 16 & 255) << 16; + blks[k] |= (l >>> 24 & 255) << 24; + return blks; + }, + rhex: function rhex(num) { + var str = ""; + var hex_chr = "0123456789abcdef"; + var _g = 0; + while (_g < 4) { + var j = _g++; + str += hex_chr.charAt(num >> j * 8 + 4 & 15) + hex_chr.charAt(num >> j * 8 & 15); + } + return str; + }, + addme: function addme(x, y) { + var lsw = (x & 65535) + (y & 65535); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return msw << 16 | lsw & 65535; + }, + bitAND: function bitAND(a, b) { + var lsb = a & 1 & (b & 1); + var msb31 = a >>> 1 & b >>> 1; + return msb31 << 1 | lsb; + }, + bitXOR: function bitXOR(a, b) { + var lsb = a & 1 ^ b & 1; + var msb31 = a >>> 1 ^ b >>> 1; + return msb31 << 1 | lsb; + }, + bitOR: function bitOR(a, b) { + var lsb = a & 1 | b & 1; + var msb31 = a >>> 1 | b >>> 1; + return msb31 << 1 | lsb; + }, + __class__: haxe.Md5 + }; + haxe.Timer = function (time_ms) { + var me = this; + this.id = window.setInterval(function () { + me.run(); + }, time_ms); + }; + haxe.Timer.__name__ = true; + haxe.Timer.delay = function (f, time_ms) { + var t = new haxe.Timer(time_ms); + t.run = function () { + t.stop(); + f(); + }; + return t; + }; + haxe.Timer.measure = function (f, pos) { + var t0 = haxe.Timer.stamp(); + var r = f(); + haxe.Log.trace(haxe.Timer.stamp() - t0 + "s", pos); + return r; + }; + haxe.Timer.stamp = function () { + return new Date().getTime() / 1000; + }; + haxe.Timer.prototype = { + run: function run() {}, + stop: function stop() { + if (this.id == null) return; + window.clearInterval(this.id); + this.id = null; + }, + __class__: haxe.Timer + }; + var js = js || {}; + js.Boot = function () {}; + js.Boot.__name__ = true; + js.Boot.__unhtml = function (s) { + return s.split("&").join("&").split("<").join("<").split(">").join(">"); + }; + js.Boot.__trace = function (v, i) { + var msg = i != null ? i.fileName + ":" + i.lineNumber + ": " : ""; + msg += js.Boot.__string_rec(v, ""); + var d; + if (typeof document != "undefined" && (d = document.getElementById("haxe:trace")) != null) d.innerHTML += js.Boot.__unhtml(msg) + "
";else if (typeof console != "undefined" && console.log != null) console.log(msg); + }; + js.Boot.__clear_trace = function () { + var d = document.getElementById("haxe:trace"); + if (d != null) d.innerHTML = ""; + }; + js.Boot.isClass = function (o) { + return o.__name__; + }; + js.Boot.isEnum = function (e) { + return e.__ename__; + }; + js.Boot.getClass = function (o) { + return o.__class__; + }; + js.Boot.__string_rec = function (o, s) { + if (o == null) return "null"; + if (s.length >= 5) return "<...>"; + var t = typeof o === "undefined" ? "undefined" : _typeof(o); + if (t == "function" && (o.__name__ || o.__ename__)) t = "object"; + switch (t) { + case "object": + if (o instanceof Array) { + if (o.__enum__) { + if (o.length == 2) return o[0]; + var str = o[0] + "("; + s += "\t"; + var _g1 = 2, + _g = o.length; + while (_g1 < _g) { + var i = _g1++; + if (i != 2) str += "," + js.Boot.__string_rec(o[i], s);else str += js.Boot.__string_rec(o[i], s); + } + return str + ")"; + } + var l = o.length; + var i; + var str = "["; + s += "\t"; + var _g = 0; + while (_g < l) { + var i1 = _g++; + str += (i1 > 0 ? "," : "") + js.Boot.__string_rec(o[i1], s); + } + str += "]"; + return str; + } + var tostr; + try { + tostr = o.toString; + } catch (e) { + return "???"; + } + if (tostr != null && tostr != Object.toString) { + var s2 = o.toString(); + if (s2 != "[object Object]") return s2; + } + var k = null; + var str = "{\n"; + s += "\t"; + var hasp = o.hasOwnProperty != null; + for (var k in o) { + ; + if (hasp && !o.hasOwnProperty(k)) { + continue; + } + if (k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") { + continue; + } + if (str.length != 2) str += ", \n"; + str += s + k + " : " + js.Boot.__string_rec(o[k], s); + } + s = s.substring(1); + str += "\n" + s + "}"; + return str; + case "function": + return ""; + case "string": + return o; + default: + return String(o); + } + }; + js.Boot.__interfLoop = function (cc, cl) { + if (cc == null) return false; + if (cc == cl) return true; + var intf = cc.__interfaces__; + if (intf != null) { + var _g1 = 0, + _g = intf.length; + while (_g1 < _g) { + var i = _g1++; + var i1 = intf[i]; + if (i1 == cl || js.Boot.__interfLoop(i1, cl)) return true; + } + } + return js.Boot.__interfLoop(cc.__super__, cl); + }; + js.Boot.__instanceof = function (o, cl) { + try { + if (o instanceof cl) { + if (cl == Array) return o.__enum__ == null; + return true; + } + if (js.Boot.__interfLoop(o.__class__, cl)) return true; + } catch (e) { + if (cl == null) return false; + } + switch (cl) { + case Int: + return Math.ceil(o % 2147483648.0) === o; + case Float: + return typeof o == "number"; + case Bool: + return o === true || o === false; + case String: + return typeof o == "string"; + case Dynamic: + return true; + default: + if (o == null) return false; + if (cl == Class && o.__name__ != null) return true;else null; + if (cl == Enum && o.__ename__ != null) return true;else null; + return o.__enum__ == cl; + } + }; + js.Boot.__cast = function (o, t) { + if (js.Boot.__instanceof(o, t)) return o;else throw "Cannot cast " + Std.string(o) + " to " + Std.string(t); + }; + js.Lib = function () {}; + js.Lib.__name__ = true; + js.Lib.debug = function () { + debugger; + }; + js.Lib.alert = function (v) { + alert(js.Boot.__string_rec(v, "")); + }; + js.Lib.eval = function (code) { + return eval(code); + }; + js.Lib.setErrorHandler = function (f) { + js.Lib.onerror = f; + }; + var $_; + function $bind(o, m) { + var f = function f() { + return f.method.apply(f.scope, arguments); + };f.scope = o;f.method = m;return f; + }; + if (Array.prototype.indexOf) HxOverrides.remove = function (a, o) { + var i = a.indexOf(o); + if (i == -1) return false; + a.splice(i, 1); + return true; + };else null; + Math.__name__ = ["Math"]; + Math.NaN = Number.NaN; + Math.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; + Math.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; + Math.isFinite = function (i) { + return isFinite(i); + }; + Math.isNaN = function (i) { + return isNaN(i); + }; + String.prototype.__class__ = String; + String.__name__ = true; + Array.prototype.__class__ = Array; + Array.__name__ = true; + Date.prototype.__class__ = Date; + Date.__name__ = ["Date"]; + var Int = { __name__: ["Int"] }; + var Dynamic = { __name__: ["Dynamic"] }; + var Float = Number; + Float.__name__ = ["Float"]; + var Bool = Boolean; + Bool.__ename__ = ["Bool"]; + var Class = { __name__: ["Class"] }; + var Enum = {}; + var Void = { __ename__: ["Void"] }; + if (typeof document != "undefined") js.Lib.document = document; + if (typeof window != "undefined") { + js.Lib.window = window; + js.Lib.window.onerror = function (msg, url, line) { + var f = js.Lib.onerror; + if (f == null) return false; + return f(msg, [url + ":" + line]); + }; } -} + com.wiris.js.JsPluginTools.main(); + delete Array.prototype.__class__; +})(); +// @codingStandardsIgnoreEnd /** - * EditorListener method to overwrite + * Modal window constructor + * @param {Object} editorAttributes Editor attributes (width, height)... * @ignore */ -EditorListener.prototype.styleChanged = function(editor){} -/** - * EditorListener method to overwrite - * @ignore - */ -EditorListener.prototype.transformationReceived = function(editor){} -// @codingStandardsIgnoreStart -(function(){ -var HxOverrides = function() { } -HxOverrides.__name__ = true; -HxOverrides.dateStr = function(date) { - var m = date.getMonth() + 1; - var d = date.getDate(); - var h = date.getHours(); - var mi = date.getMinutes(); - var s = date.getSeconds(); - return date.getFullYear() + "-" + (m < 10?"0" + m:"" + m) + "-" + (d < 10?"0" + d:"" + d) + " " + (h < 10?"0" + h:"" + h) + ":" + (mi < 10?"0" + mi:"" + mi) + ":" + (s < 10?"0" + s:"" + s); -} -HxOverrides.strDate = function(s) { - switch(s.length) { - case 8: - var k = s.split(":"); - var d = new Date(); - d.setTime(0); - d.setUTCHours(k[0]); - d.setUTCMinutes(k[1]); - d.setUTCSeconds(k[2]); - return d; - case 10: - var k = s.split("-"); - return new Date(k[0],k[1] - 1,k[2],0,0,0); - case 19: - var k = s.split(" "); - var y = k[0].split("-"); - var t = k[1].split(":"); - return new Date(y[0],y[1] - 1,y[2],t[0],t[1],t[2]); - default: - throw "Invalid date format : " + s; - } -} -HxOverrides.cca = function(s,index) { - var x = s.charCodeAt(index); - if(x != x) return undefined; - return x; -} -HxOverrides.substr = function(s,pos,len) { - if(pos != null && pos != 0 && len != null && len < 0) return ""; - if(len == null) len = s.length; - if(pos < 0) { - pos = s.length + pos; - if(pos < 0) pos = 0; - } else if(len < 0) len = s.length + len - pos; - return s.substr(pos,len); -} -HxOverrides.remove = function(a,obj) { - var i = 0; - var l = a.length; - while(i < l) { - if(a[i] == obj) { - a.splice(i,1); - return true; - } - i++; - } - return false; -} -HxOverrides.iter = function(a) { - return { cur : 0, arr : a, hasNext : function() { - return this.cur < this.arr.length; - }, next : function() { - return this.arr[this.cur++]; - }}; -} -var IntIter = function(min,max) { - this.min = min; - this.max = max; -}; -IntIter.__name__ = true; -IntIter.prototype = { - next: function() { - return this.min++; - } - ,hasNext: function() { - return this.min < this.max; - } - ,__class__: IntIter -} -var Std = function() { } -Std.__name__ = true; -Std["is"] = function(v,t) { - return js.Boot.__instanceof(v,t); -} -Std.string = function(s) { - return js.Boot.__string_rec(s,""); -} -Std["int"] = function(x) { - return x | 0; -} -Std.parseInt = function(x) { - var v = parseInt(x,10); - if(v == 0 && (HxOverrides.cca(x,1) == 120 || HxOverrides.cca(x,1) == 88)) v = parseInt(x); - if(isNaN(v)) return null; - return v; -} -Std.parseFloat = function(x) { - return parseFloat(x); -} -Std.random = function(x) { - return Math.floor(Math.random() * x); -} -var com = com || {} -if(!com.wiris) com.wiris = {} -if(!com.wiris.js) com.wiris.js = {} -com.wiris.js.JsPluginTools = function() { - this.tryReady(); -}; -com.wiris.js.JsPluginTools.__name__ = true; -com.wiris.js.JsPluginTools.main = function() { - var ev; - ev = com.wiris.js.JsPluginTools.getInstance(); - haxe.Timer.delay($bind(ev,ev.tryReady),100); -} -com.wiris.js.JsPluginTools.getInstance = function() { - if(com.wiris.js.JsPluginTools.instance == null) com.wiris.js.JsPluginTools.instance = new com.wiris.js.JsPluginTools(); - return com.wiris.js.JsPluginTools.instance; -} -com.wiris.js.JsPluginTools.bypassEncapsulation = function() { - if(window.com == null) window.com = { }; - if(window.com.wiris == null) window.com.wiris = { }; - if(window.com.wiris.js == null) window.com.wiris.js = { }; - if(window.com.wiris.js.JsPluginTools == null) window.com.wiris.js.JsPluginTools = com.wiris.js.JsPluginTools.getInstance(); -} -com.wiris.js.JsPluginTools.prototype = { - md5encode: function(content) { - return haxe.Md5.encode(content); - } - ,doLoad: function() { - this.ready = true; - com.wiris.js.JsPluginTools.instance = this; - com.wiris.js.JsPluginTools.bypassEncapsulation(); - } - ,tryReady: function() { - this.ready = false; - if(js.Lib.document.readyState) { - this.doLoad(); - this.ready = true; - } - if(!this.ready) haxe.Timer.delay($bind(this,this.tryReady),100); - } - ,__class__: com.wiris.js.JsPluginTools -} -var haxe = haxe || {} -haxe.Log = function() { } -haxe.Log.__name__ = true; -haxe.Log.trace = function(v,infos) { - js.Boot.__trace(v,infos); -} -haxe.Log.clear = function() { - js.Boot.__clear_trace(); -} -haxe.Md5 = function() { -}; -haxe.Md5.__name__ = true; -haxe.Md5.encode = function(s) { - return new haxe.Md5().doEncode(s); -} -haxe.Md5.prototype = { - doEncode: function(str) { - var x = this.str2blks(str); - var a = 1732584193; - var b = -271733879; - var c = -1732584194; - var d = 271733878; - var step; - var i = 0; - while(i < x.length) { - var olda = a; - var oldb = b; - var oldc = c; - var oldd = d; - step = 0; - a = this.ff(a,b,c,d,x[i],7,-680876936); - d = this.ff(d,a,b,c,x[i + 1],12,-389564586); - c = this.ff(c,d,a,b,x[i + 2],17,606105819); - b = this.ff(b,c,d,a,x[i + 3],22,-1044525330); - a = this.ff(a,b,c,d,x[i + 4],7,-176418897); - d = this.ff(d,a,b,c,x[i + 5],12,1200080426); - c = this.ff(c,d,a,b,x[i + 6],17,-1473231341); - b = this.ff(b,c,d,a,x[i + 7],22,-45705983); - a = this.ff(a,b,c,d,x[i + 8],7,1770035416); - d = this.ff(d,a,b,c,x[i + 9],12,-1958414417); - c = this.ff(c,d,a,b,x[i + 10],17,-42063); - b = this.ff(b,c,d,a,x[i + 11],22,-1990404162); - a = this.ff(a,b,c,d,x[i + 12],7,1804603682); - d = this.ff(d,a,b,c,x[i + 13],12,-40341101); - c = this.ff(c,d,a,b,x[i + 14],17,-1502002290); - b = this.ff(b,c,d,a,x[i + 15],22,1236535329); - a = this.gg(a,b,c,d,x[i + 1],5,-165796510); - d = this.gg(d,a,b,c,x[i + 6],9,-1069501632); - c = this.gg(c,d,a,b,x[i + 11],14,643717713); - b = this.gg(b,c,d,a,x[i],20,-373897302); - a = this.gg(a,b,c,d,x[i + 5],5,-701558691); - d = this.gg(d,a,b,c,x[i + 10],9,38016083); - c = this.gg(c,d,a,b,x[i + 15],14,-660478335); - b = this.gg(b,c,d,a,x[i + 4],20,-405537848); - a = this.gg(a,b,c,d,x[i + 9],5,568446438); - d = this.gg(d,a,b,c,x[i + 14],9,-1019803690); - c = this.gg(c,d,a,b,x[i + 3],14,-187363961); - b = this.gg(b,c,d,a,x[i + 8],20,1163531501); - a = this.gg(a,b,c,d,x[i + 13],5,-1444681467); - d = this.gg(d,a,b,c,x[i + 2],9,-51403784); - c = this.gg(c,d,a,b,x[i + 7],14,1735328473); - b = this.gg(b,c,d,a,x[i + 12],20,-1926607734); - a = this.hh(a,b,c,d,x[i + 5],4,-378558); - d = this.hh(d,a,b,c,x[i + 8],11,-2022574463); - c = this.hh(c,d,a,b,x[i + 11],16,1839030562); - b = this.hh(b,c,d,a,x[i + 14],23,-35309556); - a = this.hh(a,b,c,d,x[i + 1],4,-1530992060); - d = this.hh(d,a,b,c,x[i + 4],11,1272893353); - c = this.hh(c,d,a,b,x[i + 7],16,-155497632); - b = this.hh(b,c,d,a,x[i + 10],23,-1094730640); - a = this.hh(a,b,c,d,x[i + 13],4,681279174); - d = this.hh(d,a,b,c,x[i],11,-358537222); - c = this.hh(c,d,a,b,x[i + 3],16,-722521979); - b = this.hh(b,c,d,a,x[i + 6],23,76029189); - a = this.hh(a,b,c,d,x[i + 9],4,-640364487); - d = this.hh(d,a,b,c,x[i + 12],11,-421815835); - c = this.hh(c,d,a,b,x[i + 15],16,530742520); - b = this.hh(b,c,d,a,x[i + 2],23,-995338651); - a = this.ii(a,b,c,d,x[i],6,-198630844); - d = this.ii(d,a,b,c,x[i + 7],10,1126891415); - c = this.ii(c,d,a,b,x[i + 14],15,-1416354905); - b = this.ii(b,c,d,a,x[i + 5],21,-57434055); - a = this.ii(a,b,c,d,x[i + 12],6,1700485571); - d = this.ii(d,a,b,c,x[i + 3],10,-1894986606); - c = this.ii(c,d,a,b,x[i + 10],15,-1051523); - b = this.ii(b,c,d,a,x[i + 1],21,-2054922799); - a = this.ii(a,b,c,d,x[i + 8],6,1873313359); - d = this.ii(d,a,b,c,x[i + 15],10,-30611744); - c = this.ii(c,d,a,b,x[i + 6],15,-1560198380); - b = this.ii(b,c,d,a,x[i + 13],21,1309151649); - a = this.ii(a,b,c,d,x[i + 4],6,-145523070); - d = this.ii(d,a,b,c,x[i + 11],10,-1120210379); - c = this.ii(c,d,a,b,x[i + 2],15,718787259); - b = this.ii(b,c,d,a,x[i + 9],21,-343485551); - a = this.addme(a,olda); - b = this.addme(b,oldb); - c = this.addme(c,oldc); - d = this.addme(d,oldd); - i += 16; - } - return this.rhex(a) + this.rhex(b) + this.rhex(c) + this.rhex(d); - } - ,ii: function(a,b,c,d,x,s,t) { - return this.cmn(this.bitXOR(c,this.bitOR(b,~d)),a,b,x,s,t); - } - ,hh: function(a,b,c,d,x,s,t) { - return this.cmn(this.bitXOR(this.bitXOR(b,c),d),a,b,x,s,t); - } - ,gg: function(a,b,c,d,x,s,t) { - return this.cmn(this.bitOR(this.bitAND(b,d),this.bitAND(c,~d)),a,b,x,s,t); - } - ,ff: function(a,b,c,d,x,s,t) { - return this.cmn(this.bitOR(this.bitAND(b,c),this.bitAND(~b,d)),a,b,x,s,t); - } - ,cmn: function(q,a,b,x,s,t) { - return this.addme(this.rol(this.addme(this.addme(a,q),this.addme(x,t)),s),b); - } - ,rol: function(num,cnt) { - return num << cnt | num >>> 32 - cnt; - } - ,str2blks: function(str) { - var nblk = (str.length + 8 >> 6) + 1; - var blks = new Array(); - var _g1 = 0, _g = nblk * 16; - while(_g1 < _g) { - var i = _g1++; - blks[i] = 0; - } - var i = 0; - while(i < str.length) { - blks[i >> 2] |= HxOverrides.cca(str,i) << (str.length * 8 + i) % 4 * 8; - i++; - } - blks[i >> 2] |= 128 << (str.length * 8 + i) % 4 * 8; - var l = str.length * 8; - var k = nblk * 16 - 2; - blks[k] = l & 255; - blks[k] |= (l >>> 8 & 255) << 8; - blks[k] |= (l >>> 16 & 255) << 16; - blks[k] |= (l >>> 24 & 255) << 24; - return blks; - } - ,rhex: function(num) { - var str = ""; - var hex_chr = "0123456789abcdef"; - var _g = 0; - while(_g < 4) { - var j = _g++; - str += hex_chr.charAt(num >> j * 8 + 4 & 15) + hex_chr.charAt(num >> j * 8 & 15); - } - return str; - } - ,addme: function(x,y) { - var lsw = (x & 65535) + (y & 65535); - var msw = (x >> 16) + (y >> 16) + (lsw >> 16); - return msw << 16 | lsw & 65535; - } - ,bitAND: function(a,b) { - var lsb = a & 1 & (b & 1); - var msb31 = a >>> 1 & b >>> 1; - return msb31 << 1 | lsb; - } - ,bitXOR: function(a,b) { - var lsb = a & 1 ^ b & 1; - var msb31 = a >>> 1 ^ b >>> 1; - return msb31 << 1 | lsb; - } - ,bitOR: function(a,b) { - var lsb = a & 1 | b & 1; - var msb31 = a >>> 1 | b >>> 1; - return msb31 << 1 | lsb; - } - ,__class__: haxe.Md5 -} -haxe.Timer = function(time_ms) { - var me = this; - this.id = window.setInterval(function() { - me.run(); - },time_ms); -}; -haxe.Timer.__name__ = true; -haxe.Timer.delay = function(f,time_ms) { - var t = new haxe.Timer(time_ms); - t.run = function() { - t.stop(); - f(); - }; - return t; -} -haxe.Timer.measure = function(f,pos) { - var t0 = haxe.Timer.stamp(); - var r = f(); - haxe.Log.trace(haxe.Timer.stamp() - t0 + "s",pos); - return r; -} -haxe.Timer.stamp = function() { - return new Date().getTime() / 1000; -} -haxe.Timer.prototype = { - run: function() { - } - ,stop: function() { - if(this.id == null) return; - window.clearInterval(this.id); - this.id = null; - } - ,__class__: haxe.Timer -} -var js = js || {} -js.Boot = function() { } -js.Boot.__name__ = true; -js.Boot.__unhtml = function(s) { - return s.split("&").join("&").split("<").join("<").split(">").join(">"); -} -js.Boot.__trace = function(v,i) { - var msg = i != null?i.fileName + ":" + i.lineNumber + ": ":""; - msg += js.Boot.__string_rec(v,""); - var d; - if(typeof(document) != "undefined" && (d = document.getElementById("haxe:trace")) != null) d.innerHTML += js.Boot.__unhtml(msg) + "
"; else if(typeof(console) != "undefined" && console.log != null) console.log(msg); -} -js.Boot.__clear_trace = function() { - var d = document.getElementById("haxe:trace"); - if(d != null) d.innerHTML = ""; -} -js.Boot.isClass = function(o) { - return o.__name__; -} -js.Boot.isEnum = function(e) { - return e.__ename__; -} -js.Boot.getClass = function(o) { - return o.__class__; -} -js.Boot.__string_rec = function(o,s) { - if(o == null) return "null"; - if(s.length >= 5) return "<...>"; - var t = typeof(o); - if(t == "function" && (o.__name__ || o.__ename__)) t = "object"; - switch(t) { - case "object": - if(o instanceof Array) { - if(o.__enum__) { - if(o.length == 2) return o[0]; - var str = o[0] + "("; - s += "\t"; - var _g1 = 2, _g = o.length; - while(_g1 < _g) { - var i = _g1++; - if(i != 2) str += "," + js.Boot.__string_rec(o[i],s); else str += js.Boot.__string_rec(o[i],s); - } - return str + ")"; - } - var l = o.length; - var i; - var str = "["; - s += "\t"; - var _g = 0; - while(_g < l) { - var i1 = _g++; - str += (i1 > 0?",":"") + js.Boot.__string_rec(o[i1],s); - } - str += "]"; - return str; - } - var tostr; - try { - tostr = o.toString; - } catch( e ) { - return "???"; - } - if(tostr != null && tostr != Object.toString) { - var s2 = o.toString(); - if(s2 != "[object Object]") return s2; - } - var k = null; - var str = "{\n"; - s += "\t"; - var hasp = o.hasOwnProperty != null; - for( var k in o ) { ; - if(hasp && !o.hasOwnProperty(k)) { - continue; - } - if(k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") { - continue; - } - if(str.length != 2) str += ", \n"; - str += s + k + " : " + js.Boot.__string_rec(o[k],s); - } - s = s.substring(1); - str += "\n" + s + "}"; - return str; - case "function": - return ""; - case "string": - return o; - default: - return String(o); - } -} -js.Boot.__interfLoop = function(cc,cl) { - if(cc == null) return false; - if(cc == cl) return true; - var intf = cc.__interfaces__; - if(intf != null) { - var _g1 = 0, _g = intf.length; - while(_g1 < _g) { - var i = _g1++; - var i1 = intf[i]; - if(i1 == cl || js.Boot.__interfLoop(i1,cl)) return true; - } - } - return js.Boot.__interfLoop(cc.__super__,cl); -} -js.Boot.__instanceof = function(o,cl) { - try { - if(o instanceof cl) { - if(cl == Array) return o.__enum__ == null; - return true; - } - if(js.Boot.__interfLoop(o.__class__,cl)) return true; - } catch( e ) { - if(cl == null) return false; - } - switch(cl) { - case Int: - return Math.ceil(o%2147483648.0) === o; - case Float: - return typeof(o) == "number"; - case Bool: - return o === true || o === false; - case String: - return typeof(o) == "string"; - case Dynamic: - return true; - default: - if(o == null) return false; - if(cl == Class && o.__name__ != null) return true; else null; - if(cl == Enum && o.__ename__ != null) return true; else null; - return o.__enum__ == cl; - } -} -js.Boot.__cast = function(o,t) { - if(js.Boot.__instanceof(o,t)) return o; else throw "Cannot cast " + Std.string(o) + " to " + Std.string(t); -} -js.Lib = function() { } -js.Lib.__name__ = true; -js.Lib.debug = function() { - debugger; -} -js.Lib.alert = function(v) { - alert(js.Boot.__string_rec(v,"")); -} -js.Lib.eval = function(code) { - return eval(code); -} -js.Lib.setErrorHandler = function(f) { - js.Lib.onerror = f; -} -var $_; -function $bind(o,m) { var f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; return f; }; -if(Array.prototype.indexOf) HxOverrides.remove = function(a,o) { - var i = a.indexOf(o); - if(i == -1) return false; - a.splice(i,1); - return true; -}; else null; -Math.__name__ = ["Math"]; -Math.NaN = Number.NaN; -Math.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; -Math.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; -Math.isFinite = function(i) { - return isFinite(i); -}; -Math.isNaN = function(i) { - return isNaN(i); -}; -String.prototype.__class__ = String; -String.__name__ = true; -Array.prototype.__class__ = Array; -Array.__name__ = true; -Date.prototype.__class__ = Date; -Date.__name__ = ["Date"]; -var Int = { __name__ : ["Int"]}; -var Dynamic = { __name__ : ["Dynamic"]}; -var Float = Number; -Float.__name__ = ["Float"]; -var Bool = Boolean; -Bool.__ename__ = ["Bool"]; -var Class = { __name__ : ["Class"]}; -var Enum = { }; -var Void = { __ename__ : ["Void"]}; -if(typeof document != "undefined") js.Lib.document = document; -if(typeof window != "undefined") { - js.Lib.window = window; - js.Lib.window.onerror = function(msg,url,line) { - var f = js.Lib.onerror; - if(f == null) return false; - return f(msg,[url + ":" + line]); - }; -} -com.wiris.js.JsPluginTools.main(); -delete Array.prototype.__class__; }()); -// @codingStandardsIgnoreEnd -/** - * Modal window constructor - * @param {string} path Iframe src - * @param {string} title Modal window title - * @param {Object} editorAttributes Editor attributes (width, height)... - * @ignore - */ -function ModalWindow(path, editorAttributes) { - var ua = navigator.userAgent.toLowerCase(); - var isAndroid = ua.indexOf("android") > -1; - var isIOS = ((ua.indexOf("ipad") > -1) || (ua.indexOf("iphone") > -1)); - this.iosSoftkeyboardOpened = false; - this.iosMeasureUnit = ua.indexOf("crios") == -1 ? "%" : "vh"; - this.iosDivHeight = "100" + this.iosMeasureUnit; - - var deviceWidth = window.outerWidth; - var deviceHeight = window.outerHeight; - - var landscape = deviceWidth > deviceHeight; - var portrait = deviceWidth < deviceHeight; - - var iframeAttributes = {}; - iframeAttributes['width'] = editorAttributes.split(' ').join('').split(',')[0].split("=")[1]; - iframeAttributes['height'] = editorAttributes.split(' ').join('').split(',')[1].split("=")[1]; - iframeAttributes['src'] = path; - - var isMobile = (landscape && iframeAttributes['height'] > deviceHeight) || (portrait && iframeAttributes['width'] > deviceWidth) ? true : false; - - // Device object properties. - - var deviceProperties = {}; - deviceProperties['orientation'] = landscape ? 'landscape' : 'portait'; - deviceProperties['isAndroid'] = isAndroid ? true : false; - deviceProperties['isIOS'] = isIOS ? true : false; - deviceProperties['isMobile'] = isMobile; - deviceProperties['isDesktop'] = !isMobile && !isIOS && !isAndroid; - - this.deviceProperties = deviceProperties; - this.properties = { - created : false, - state : '', - previousState : '', - deviceProperties: deviceProperties, - position : {bottom: 0, right: 10}, - size : {height: 338, width: 580} - } - - this.properties.iframeAttributes = iframeAttributes; - this.modalHeight = parseInt(this.properties.iframeAttributes['height']); - this.modalWidth = parseInt(this.properties.iframeAttributes['width']); - - this.title = ''; - - var attributes = {}; - - attributes['class'] = 'wrs_modal_overlay'; - var modalOverlayDiv = wrs_createElement('div', attributes); - this.overlayDiv = modalOverlayDiv; - - attributes['class'] = 'wrs_modal_title_bar'; - var barModalDiv = wrs_createElement('div', attributes); - this.titleBardDiv = barModalDiv; - - attributes = {}; - attributes['class'] = 'wrs_modal_title'; - var titleModalDiv = wrs_createElement('div', attributes); - titleModalDiv.innerHTML = this.title; - this.titleDiv = titleModalDiv; - - attributes = {}; - attributes['class'] = 'wrs_modal_close_button'; - attributes['title'] = _wrs_stringManager.getString('close'); - var closeModalDiv = wrs_createElement('a', attributes); - closeModalDiv.setAttribute('role','button'); - this.closeDiv = closeModalDiv; - - attributes = {}; - attributes['class'] = 'wrs_modal_stack_button'; - attributes['title'] = _wrs_stringManager.getString('exit_fullscreen'); - var stackModalDiv = wrs_createElement('a', attributes); - stackModalDiv.setAttribute('role','button'); - this.stackDiv = stackModalDiv; - - attributes = {}; - attributes['class'] = 'wrs_modal_maximize_button'; - attributes['title'] = _wrs_stringManager.getString('fullscreen'); - var maximizeModalDiv = wrs_createElement('a', attributes); - maximizeModalDiv.setAttribute('role','button'); - this.maximizeDiv = maximizeModalDiv; - - attributes = {}; - attributes['class'] = 'wrs_modal_minimize_button'; - attributes['title'] = _wrs_stringManager.getString('minimize'); - var minimizeModalDiv = wrs_createElement('a', attributes); - minimizeModalDiv.setAttribute('role','button'); - this.minimizeDiv = minimizeModalDiv; - - attributes = {}; - attributes['class'] = 'wrs_modal_dialogContainer'; - var containerDiv = wrs_createElement('div', attributes); - this.containerDiv = containerDiv; - - attributes = {}; - attributes['id'] = 'wrs_modal_iframe_id'; - attributes['class'] = 'wrs_modal_iframe'; - attributes['title'] = _wrs_stringManager.getString('mathtype'); - attributes['src'] = iframeAttributes['src']; - attributes['frameBorder'] = "0"; - var iframeModal = wrs_createElement('iframe', attributes); - this.iframe = iframeModal; - - attributes = {}; - attributes['class'] = 'wrs_modal_iframeContainer'; - var iframeModalContainer = wrs_createElement('div', attributes); - this.iframeContainer = iframeModalContainer; - - // We create iframe inside _wrs_conf_path origin. - this.iframeOrigin = this.getOriginFromUrl(_wrs_conf_path); - - this.lastImageWasNew = true; - - this.toolbar = null; -} +var ModalWindow = function () { + function ModalWindow(editorAttributes) { + _classCallCheck(this, ModalWindow); -ModalWindow.prototype.create = function() { - this.titleBardDiv.appendChild(this.closeDiv); - this.titleBardDiv.appendChild(this.stackDiv); - this.titleBardDiv.appendChild(this.maximizeDiv); - this.titleBardDiv.appendChild(this.minimizeDiv); - this.titleBardDiv.appendChild(this.titleDiv); - this.iframeContainer.appendChild(this.iframe); + // EditorAtributes + // TODO: Remove the argument: we need to find a way to detect mobile devices + // without known anything about editor. + this.editorAttributes = editorAttributes; - wrs_addEvent(this.overlayDiv, 'mouseup', function (e) { - if (typeof(_wrs_modalWindow) !== 'undefined' && _wrs_modalWindow != null) { - _wrs_modalWindow.fireEditorEvent('mouseup'); - } - }); + // Metrics + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + var isIOS = ua.indexOf("ipad") > -1 || ua.indexOf("iphone") > -1; + this.iosSoftkeyboardOpened = false; + this.iosMeasureUnit = ua.indexOf("crios") == -1 ? "%" : "vh"; + this.iosDivHeight = "100" + this.iosMeasureUnit; - if (this.deviceProperties['isDesktop']) { - this.containerDiv.appendChild(this.titleBardDiv); - } - this.containerDiv.appendChild(this.iframeContainer); - // Check if browser has scrollBar before modal has modified. - this.recalculateScrollBar(); + var deviceWidth = window.outerWidth; + var deviceHeight = window.outerHeight; - document.body.appendChild(this.containerDiv); - document.body.appendChild(this.overlayDiv); + var landscape = deviceWidth > deviceHeight; + var portrait = deviceWidth < deviceHeight; - wrs_addEvent(this.closeDiv, 'click', function(){ - _wrs_popupWindow.postMessage({'objectName' : 'checkCloseCondition'}, this.iframeOrigin); - }.bind(this)); + var editorWidth = editorAttributes.split(' ').join('').split(',')[0].split("=")[1]; + var editorHeight = editorAttributes.split(' ').join('').split(',')[1].split("=")[1]; - if (this.deviceProperties['isDesktop']) { // Desktop. - this.maximizeDiv.addEventListener('click', this.maximizeModalWindow.bind(this), true); - this.stackDiv.addEventListener('click', this.stackModalWindow.bind(this), true); - this.minimizeDiv.addEventListener('click', this.minimizeModalWindow.bind(this), true); - this.createModalWindowDesktop(); - this.createResizeButtons(); - if (_wrs_conf_modalWindow) { - this.addListeners(); - } - } - else if (this.deviceProperties['isAndroid']) { - this.createModalWindowAndroid(); - } - else if (this.deviceProperties['isIOS'] && !this.deviceProperties['isMobile']) { - this.createModalWindowIos(); - } - _wrs_popupWindow = this.iframe.contentWindow; - this.properties.open = true; - this.properties.created = true; + // TODO: Detect isMobile without using editor metrics. + var isMobile = landscape && editorHeight > deviceHeight || portrait && editorWidth > deviceWidth ? true : false; - // Maximize window only when the configuration is set and the device is not ios or android. - if (this.deviceProperties['isDesktop'] && typeof _wrs_conf_modalWindow != "undefined" && _wrs_conf_modalWindow && _wrs_conf_modalWindowFullScreen) { - this.maximizeModalWindow(); - } + // Device object properties. - var popUpAtributes = {'cancelString' : _wrs_stringManager.getString('cancel'), 'submitString' : _wrs_stringManager.getString('close'), 'message' : _wrs_stringManager.getString('close_modal_warning')}; - this.popup = new PopUpMessage(popUpAtributes); -} + this.deviceProperties = { + orientation: landscape ? 'landscape' : 'portait', + isAndroid: isAndroid ? true : false, + isIOS: isIOS ? true : false, + isMobile: isMobile, + isDesktop: !isMobile && !isIOS && !isAndroid + }; -/** - * Method to create resize buttons in modal DOM - * @ignore - */ -ModalWindow.prototype.createResizeButtons = function() { - // This is a definition of Resize Button Bottom-Right - this.resizerBR = document.createElement('div'); - this.resizerBR.className = 'wrs_bottom_right_resizer'; - this.resizerBR.innerHTML = '◢'; - // This is a definition of Resize Button Top-Left - this.resizerTL = document.createElement('div'); - this.resizerTL.className = 'wrs_bottom_left_resizer'; - // Append resize buttons to modal - this.containerDiv.appendChild(this.resizerBR); - this.titleBardDiv.appendChild(this.resizerTL); - // Add events to resize on click and drag - wrs_addEvent(this.resizerBR, 'mousedown', this.activateResizeStateBR.bind(this)); - wrs_addEvent(this.resizerTL, 'mousedown', this.activateResizeStateTL.bind(this)); -} -/** - * Method to initialize variables for Bottom-Right resize button - * @param {event} ev mouse - * @ignore - */ -ModalWindow.prototype.activateResizeStateBR = function(ev) { - this.initializeResizeProperties(ev, false); -} + this.properties = { + created: false, + state: '', + previousState: '', + position: { bottom: 0, right: 10 }, + size: { height: 338, width: 580 } + }; -/** - * Method to initialize variables for Top-Left resize button - * @param {event} ev mouse - * @ignore - */ -ModalWindow.prototype.activateResizeStateTL = function(ev) { - this.initializeResizeProperties(ev, true); -} + var attributes = {}; + attributes.class = 'wrs_modal_overlay'; + attributes.id = attributes.class + '_id'; + this.overlay = wrs_createElement('div', attributes); -/** - * Common method to initialize variables at resize - * @param {event} ev mouse - * @ignore - */ -ModalWindow.prototype.initializeResizeProperties = function(ev, leftOption) { - // Apply class for disable involuntary select text when drag. - wrs_addClass(document.body, 'wrs_noselect'); - this.resizeDataObject = { - x: this.eventClient(ev).X, - y: this.eventClient(ev).Y - }; - // Save Initial state of modal to compare on drag and obtain the difference. - this.initialWidth = parseInt(this.containerDiv.style.width); - this.initialHeight = parseInt(this.containerDiv.style.height); - if (!leftOption) { - this.initialRight = parseInt(this.containerDiv.style.right); - this.initialBottom = parseInt(this.containerDiv.style.bottom); - } else { - this.leftScale = true; - } - if (!this.initialRight){ - this.initialRight = 0; - } - if (!this.initialBottom){ - this.initialBottom = 0; - } - // Disable mouse events on editor when we start to drag modal. - this.iframe.style['pointer-events'] = 'none'; - document.body.style['user-select'] = 'none'; - // Needed for IE11 for apply disabled mouse events on editor because iexplorer need a dinamic object to apply this property. - if (this.isIE11()) { - this.iframe.style['position'] = 'relative'; - } - // Prevent lost mouse events into other iframes - // Activate overlay div to prevent mouse events behind modal - if (_wrs_modalWindow.properties.state != "maximized") { - this.overlayDiv.style.display = ""; - } -} + attributes = {}; + attributes.class = 'wrs_modal_title_bar'; + attributes.id = attributes.class + '_id'; + this.titleBar = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_title'; + attributes.id = attributes.class + '_id'; + this.title = wrs_createElement('div', attributes); + this.title.innerHTML = ''; + + attributes = {}; + attributes.class = 'wrs_modal_close_button'; + attributes.id = attributes.class + '_id'; + attributes.title = _wrs_stringManager.getString('close'); + this.closeDiv = wrs_createElement('a', attributes);; + this.closeDiv.setAttribute('role', 'button'); + + attributes = {}; + attributes.class = 'wrs_modal_stack_button'; + attributes.id = attributes.class + '_id'; + attributes.title = "Exit full-screen"; + this.stackDiv = wrs_createElement('a', attributes); + this.stackDiv.setAttribute('role', 'button'); + + attributes = {}; + attributes.class = 'wrs_modal_maximize_button'; + attributes.id = attributes.class + '_id'; + attributes.title = _wrs_stringManager.getString('fullscreen'); + this.maximizeDiv = wrs_createElement('a', attributes); + this.maximizeDiv.setAttribute('role', 'button'); + + attributes = {}; + attributes.class = 'wrs_modal_minimize_button'; + attributes.id = attributes.class + '_id'; + attributes.title = _wrs_stringManager.getString('minimise'); + this.minimizeDiv = wrs_createElement('a', attributes); + this.minimizeDiv.setAttribute('role', 'button'); + + attributes = {}; + attributes.class = 'wrs_modal_dialogContainer'; + attributes.id = attributes.class + '_id'; + this.container = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_wrapper'; + attributes.id = attributes.class + '_id'; + this.wrapper = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_content_container'; + attributes.id = attributes.class + '_id'; + this.contentContainer = wrs_createElement('div', attributes); -ModalWindow.prototype.open = function() { + attributes = {}; + attributes.class = 'wrs_modal_controls'; + attributes.id = attributes.class + '_id'; + this.controls = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_buttons_container'; + attributes.id = attributes.class + '_id'; + this.buttonContainer = wrs_createElement('div', attributes); + + // Buttons: all button must be created using createSubmitButton method. + this.submitButton = this.createSubmitButton({ + class: 'wrs_modal_button_accept', + innerHTML: _wrs_stringManager.getString('accept') + }, this.submitAction.bind(this)); + + this.cancelButton = this.createSubmitButton({ + class: 'wrs_modal_button_cancel', + innerHTML: _wrs_stringManager.getString('cancel') + }, this.cancelAction.bind(this)); + + this.lastImageWasNew = true; + + this.contentManager = null; + + // Overlay popup. + var popupStrings = { + 'cancelString': _wrs_stringManager.getString('cancel'), + 'submitString': _wrs_stringManager.getString('close'), + 'message': _wrs_stringManager.getString('close_modal_warning') + }; + + var callbacks = { + 'closeCallback': function () { + this.close(); + }.bind(this), + 'cancelCallback': function () { + this.focus(); + }.bind(this) + }; - if (this.deviceProperties['isIOS'] || this.deviceProperties['isAndroid'] || this.deviceProperties['isMobile']) { - // Due to editor wait we need to wait until editor focus. - setTimeout(function() { _wrs_modalWindow.hideKeyboard() }, 300); + var popupupProperties = { + 'overlayElement': this.container, + 'callbacks': callbacks, + 'strings': popupStrings + }; + + this.popup = new PopUpMessage(popupupProperties); } + /** + * This method sets the contentElement object. A contentElement object + * manages the logic of modal object content: submit, update, close and changes. + * @param {object} contentManager + * @ignore + */ - if (this.properties.open == true || this.properties.created) { - if (this.properties.open == true) { - this.updateToolbar(); - if (_wrs_isNewElement) { - this.updateMathMLContent(); - this.lastImageWasNew = true; + _createClass(ModalWindow, [{ + key: "setContentManager", + value: function setContentManager(contentManager) { + this.contentManager = contentManager; + } + + /** + * Returns the modal contentElement object. + *@returns {object} + *@ignore + */ + + }, { + key: "getContentManager", + value: function getContentManager() { + return this.contentManager; + } + + /** + * This method is called when the modal object has been submited. Calls + * contentElement submitAction method - if exists - and closes the modal + * object. No logic about the content should be placed here, + * contentElement.submitAction is the responsible of the content logic. + * @ignore + */ + + }, { + key: "submitAction", + value: function submitAction() { + if (typeof this.contentManager.submitAction !== 'undefined') { + this.contentManager.submitAction(); } - else { - this.setMathMLWithCallback(wrs_mathmlDecode(_wrs_temporalImage.getAttribute(_wrs_conf_imageMathmlAttribute))); - this.lastImageWasNew = false; + this.close(); + } + + /** + * This method is called when the modal object has been cancelled. If + * contentElement has implemented hasChanges method, a confirm popup + * will be shown if hasChanges returns true. + * @ignore + */ + + }, { + key: "cancelAction", + value: function cancelAction() { + if (typeof this.contentManager.hasChanges === 'undefined') { + this.close(); + } else if (!this.contentManager.hasChanges()) { + this.close(); + } else { + this.showPopUpMessage(); } } - else { - this.containerDiv.style.visibility = ''; - this.containerDiv.style.opacity = ''; - this.containerDiv.style.display = ''; - this.overlayDiv.style.visibility = ''; - this.overlayDiv.style.display = ''; + + /** + * Returns a submit button. The properties argument admits the following: + * properties { + * 'class' : '', // button class. + * 'innerHTML : '' // button innerHTML. + * } + * @param {Object} properties input button properties. + * @param {Function} callback callback function associated to click event. + * @ignore + */ + + }, { + key: "createSubmitButton", + value: function createSubmitButton(properties, callback) { + function SubmitButton(properties, callback) { + this.element = document.createElement('button'); + this.element.id = properties.class + '_id'; + this.element.className = properties.class; + this.element.innerHTML = properties.innerHTML; + wrs_addEvent(this.element, 'click', callback); + } + + SubmitButton.prototype.getElement = function () { + return this.element; + }; + + return new SubmitButton(properties, callback).getElement(); + } + + /** + * Creates the modal window object inserting a contentElement object. + * @ignore + */ + + }, { + key: "create", + value: function create() { + + /*Modal Window Structure + _____________________________________________________________________________________ + |wrs_modal_dialog_Container | + | _________________________________________________________________________________ | + | |title_bar minimize_button stack_button close_button | | + | |_______________________________________________________________________________| | + | |wrapper | | + | | _____________________________________________________________________________ | | + | | |content | | | + | | | | | | + | | | | | | + | | |___________________________________________________________________________| | | + | | _____________________________________________________________________________ | | + | | |controls | | | + | | | ___________________________________ | | | + | | | |buttonContainer | | | | + | | | | _______________________________ | | | | + | | | | |button_accept | button_cancel| | | | | + | | | |_|_____________ |______________|_| | | | + | | |___________________________________________________________________________| | | + | |_______________________________________________________________________________| | + |___________________________________________________________________________________|*/ + + this.titleBar.appendChild(this.closeDiv); + this.titleBar.appendChild(this.stackDiv); + this.titleBar.appendChild(this.maximizeDiv); + this.titleBar.appendChild(this.minimizeDiv); + this.titleBar.appendChild(this.title); + + if (this.deviceProperties['isDesktop']) { + this.container.appendChild(this.titleBar); + } + + this.wrapper.appendChild(this.contentContainer); + this.wrapper.appendChild(this.controls); + + this.controls.appendChild(this.buttonContainer); + + this.buttonContainer.appendChild(this.submitButton); + this.buttonContainer.appendChild(this.cancelButton); + + this.container.appendChild(this.wrapper); + + // Check if browser has scrollBar before modal has modified. + this.recalculateScrollBar(); + + document.body.appendChild(this.container); + document.body.appendChild(this.overlay); + + if (this.deviceProperties['isDesktop']) { + // Desktop. + this.createModalWindowDesktop(); + this.createResizeButtons(); + + this.addListeners(); + // Maximize window only when the configuration is set and the device is not iOS or Android. + if (_wrs_conf_modalWindowFullScreen) { + this.maximize(); + } + } else if (this.deviceProperties['isAndroid']) { + this.createModalWindowAndroid(); + } else if (this.deviceProperties['isIOS'] && !this.deviceProperties['isMobile']) { + this.createModalWindowIos(); + } + + if (this.contentManager != null) { + this.contentManager.insert(this); + } this.properties.open = true; + this.properties.created = true; + } + + /** + * Creates a button in the modal object to resize it. + * @ignore + */ + + }, { + key: "createResizeButtons", + value: function createResizeButtons() { + // This is a definition of Resize Button Bottom-Right + this.resizerBR = document.createElement('div'); + this.resizerBR.className = 'wrs_bottom_right_resizer'; + this.resizerBR.innerHTML = '◢'; + // This is a definition of Resize Button Top-Left + this.resizerTL = document.createElement('div'); + this.resizerTL.className = 'wrs_bottom_left_resizer'; + // Append resize buttons to modal + this.container.appendChild(this.resizerBR); + this.titleBar.appendChild(this.resizerTL); + // Add events to resize on click and drag + wrs_addEvent(this.resizerBR, 'mousedown', this.activateResizeStateBR.bind(this)); + wrs_addEvent(this.resizerTL, 'mousedown', this.activateResizeStateTL.bind(this)); + } + /** + * Method to initialize variables for Bottom-Right resize button + * @param {event} ev mouse + * @ignore + */ + + }, { + key: "activateResizeStateBR", + value: function activateResizeStateBR(ev) { + this.initializeResizeProperties(ev, false); + } + + /** + * Method to initialize variables for Top-Left resize button + * @param {event} ev mouse + * @ignore + */ + + }, { + key: "activateResizeStateTL", + value: function activateResizeStateTL(ev) { + this.initializeResizeProperties(ev, true); + } + + /** + * Common method to initialize variables at resize + * @param {event} ev mouse + * @ignore + */ + + }, { + key: "initializeResizeProperties", + value: function initializeResizeProperties(ev, leftOption) { + // Apply class for disable involuntary select text when drag. + wrs_addClass(document.body, 'wrs_noselect'); + wrs_addClass(this.overlay, 'wrs_overlay_active'); + this.resizeDataObject = { + x: this.eventClient(ev).X, + y: this.eventClient(ev).Y + }; + // Save Initial state of modal to compare on drag and obtain the difference. + this.initialWidth = parseInt(this.container.style.width); + this.initialHeight = parseInt(this.container.style.height); + if (!leftOption) { + this.initialRight = parseInt(this.container.style.right); + this.initialBottom = parseInt(this.container.style.bottom); + } else { + this.leftScale = true; + } + if (!this.initialRight) { + this.initialRight = 0; + } + if (!this.initialBottom) { + this.initialBottom = 0; + } + // Disable mouse events on editor when we start to drag modal. + document.body.style['user-select'] = 'none'; + } + + /** + * This method opens the modal window, restoring the previous state, position and metrics, + * if exists. + * By default the modal object opens in stack mode. + * @ignore + */ + + }, { + key: "open", + value: function open() { + //Removing close class. + this.removeClass('wrs_closed'); + // Hiding keyboard for mobile devices. + if (this.deviceProperties['isIOS'] || this.deviceProperties['isAndroid'] || this.deviceProperties['isMobile']) { + // Due to editor wait we need to wait until editor focus. + setTimeout(function () { + this.hideKeyboard(); + }.bind(this), 400); + } - this.updateToolbar(); + // New modal window. He need to create the whole object. + if (!this.properties.created) { + this.create(); + } else { + // Previous state closed. Open method can be called even the previous state is open, + // for exmample updating the content of the modal object. + if (!this.properties.open) { + this.properties.open = true; + + // Restoring the previous open state: if the modal object has been closed + // re-open it should preserve the state and the metrics. + if (!this.deviceProperties.isAndroid && !this.deviceProperties.isIOS) { + this.restoreState(); + } + } - if (_wrs_isNewElement) { - this.updateMathMLContent(); - this.lastImageWasNew = true; + // Maximize window only when the configuration is set and the device is not iOs or Android. + if (this.deviceProperties['isDesktop'] && _wrs_conf_modalWindowFullScreen) { + this.maximize(); + } + + // In iOS we need to recalculate the size of the modal object because + // iOS keyboard is a float div which can overlay the modal object. + if (this.deviceProperties['isIOS']) { + this.iosSoftkeyboardOpened = false; + this.setContainerHeight("100" + this.iosMeasureUnit); + } + + // Updating content element. + if (typeof this.contentManager != 'undefined') { + this.contentManager.onOpen(this); + } + } + } + + /** + * Closes modal window and restores viewport header. + * @ignore + */ + + }, { + key: "close", + value: function close() { + this.removeClass('wrs_maximized'); + this.removeClass('wrs_minimized'); + this.removeClass('wrs_stack'); + this.addClass('wrs_closed'); + this.saveModalProperties(); + this.properties.open = false; + } + + /** + * Util function to kwnow if browser is internet explorer 11. + * @return {boolean} return true if browser is iexplorer v11 or false in others. + * @ignore + */ + + }, { + key: "isIE11", + value: function isIE11() { + if (navigator.userAgent.search("Msie/") >= 0 || navigator.userAgent.search("Trident/") >= 0 || navigator.userAgent.search("Edge/") >= 0) { + return true; + } + return false; + } + + /** + * Adds a class to all modal DOM elements. + * @param {string} cls + * @ignore + */ + + }, { + key: "addClass", + value: function addClass(cls) { + wrs_addClass(this.overlay, cls); + wrs_addClass(this.titleBar, cls); + wrs_addClass(this.overlay, cls); + wrs_addClass(this.container, cls); + wrs_addClass(this.contentContainer, cls); + wrs_addClass(this.stackDiv, cls); + wrs_addClass(this.minimizeDiv, cls); + wrs_addClass(this.maximizeDiv, cls); + wrs_addClass(this.wrapper, cls); + } + + /** + * Remove a calss from all modal DOM elements. + * @param {string} cls + * @ignore + */ + + }, { + key: "removeClass", + value: function removeClass(cls) { + wrs_removeClass(this.overlay, cls); + wrs_removeClass(this.titleBar, cls); + wrs_removeClass(this.overlay, cls); + wrs_removeClass(this.container, cls); + wrs_removeClass(this.contentContainer, cls); + wrs_removeClass(this.stackDiv, cls); + wrs_removeClass(this.minimizeDiv, cls); + wrs_removeClass(this.maximizeDiv, cls); + wrs_removeClass(this.wrapper, cls); + } + + /** + * Create modal dialog for desktop. + * @ignore + */ + + }, { + key: "createModalWindowDesktop", + value: function createModalWindowDesktop() { + this.addClass('wrs_modal_desktop'); + this.stack(); + } + + /** + * Create modal dialog for non mobile android devices. + * @ignore + */ + + }, { + key: "createModalWindowAndroid", + value: function createModalWindowAndroid() { + this.addClass('wrs_modal_android'); + window.addEventListener('resize', this.orientationChangeAndroidSoftkeyboard.bind(this)); + } + + /** + * Create modal dialog for iOS devices. + * @ignore + */ + + }, { + key: "createModalWindowIos", + value: function createModalWindowIos() { + this.addClass('wrs_modal_ios'); + // Refresh the size when the orientation is changed + window.addEventListener('resize', this.orientationChangeIosSoftkeyboard.bind(this)); + } + + /** + * Restore previous state, position and size of previous stacked modal window + * @ignore + */ + + }, { + key: "restoreState", + value: function restoreState() { + if (this.properties.state == 'maximized') { + // Reseting states for prevent return to stack state. + this.maximize(); + } else if (this.properties.state == 'minimized') { + // Reseting states for prevent return to stack state. + this.properties.state = this.properties.previousState; + this.properties.previousState = ''; + this.minimize(); } else { - this.setMathMLWithCallback(wrs_mathmlDecode(_wrs_temporalImage.getAttribute(_wrs_conf_imageMathmlAttribute))); - this.lastImageWasNew = false; + this.stack(); } + } + + /** + * Stacks the modal object. + * @ignore + */ + + }, { + key: "stack", + value: function stack() { + this.properties.previousState = this.properties.state; + this.properties.state = 'stack'; + this.removeClass('wrs_maximized'); + this.minimizeDiv.title = "Minimise"; + this.removeClass('wrs_minimized'); + this.addClass('wrs_stack'); + + this.restoreModalProperties(); - if (!this.properties.deviceProperties.isAndroid && !this.properties.deviceProperties.isIOS) { - this.restoreStateModalWindow(); + if (typeof this.resizerBR !== 'undefined' && typeof this.resizerTL !== 'undefined') { + this.setResizeButtonsVisibility(); + } + + // Need recalculate position of actual modal because window can was changed in fullscreenmode + this.recalculateScrollBar(); + this.recalculatePosition(); + this.recalculateScale(); + this.focus(); + } + + /** + * Minimizes the modal object + * @ignore + */ + + }, { + key: "minimize", + value: function minimize() { + // Saving width, height, top and bottom parameters to restore when open + this.saveModalProperties(); + if (this.properties.state == 'minimized' && this.properties.previousState == 'stack') { + this.stack(); + } else if (this.properties.state == 'minimized' && this.properties.previousState == 'maximized') { + this.maximize(); + } else { + // Setting css to prevent important tag into css style + this.container.style.height = "30px"; + this.container.style.width = "250px"; + this.container.style.bottom = "0px"; + this.container.style.right = "10px"; + + this.removeListeners(); + this.properties.previousState = this.properties.state; + this.properties.state = "minimized"; + this.setResizeButtonsVisibility(); + this.minimizeDiv.title = "Maximise"; + + if (wrs_containsClass(this.overlay, 'wrs_stack')) { + this.removeClass('wrs_stack'); + } else { + this.removeClass('wrs_maximized'); + } + this.addClass('wrs_minimized'); } } - // Maximize window only when the configuration is set and the device is not ios or android. - if (this.deviceProperties['isDesktop'] && typeof _wrs_conf_modalWindow != "undefined" && _wrs_conf_modalWindow && _wrs_conf_modalWindowFullScreen) { - this.maximizeModalWindow(); + /** + * Maximizes the modal object. + * @ignore + */ + + }, { + key: "maximize", + value: function maximize() { + // Saving width, height, top and bottom parameters to restore when open + this.saveModalProperties(); + if (this.properties.state != 'maximized') { + this.properties.previousState = this.properties.state; + this.properties.state = 'maximized'; + } + // Don't permit resize on maximize mode. + this.setResizeButtonsVisibility(); + + if (wrs_containsClass(this.overlay, 'wrs_minimized')) { + this.minimizeDiv.title = "Minimise"; + this.removeClass('wrs_minimized'); + } else if (wrs_containsClass(this.overlay, 'wrs_stack')) { + this.container.style.left = null; + this.container.style.top = null; + this.removeClass('wrs_stack'); + } + + this.addClass('wrs_maximized'); + + // Set size to 80% screen with a max size. + this.setSize(parseInt(window.innerHeight * 0.8), parseInt(window.innerWidth * 0.8)); + var sizeModificated = false; + if (this.container.clientHeight > 700) { + this.container.style.height = '700px'; + sizeModificated = true; + } + if (this.container.clientWidth > 1200) { + this.container.style.width = '1200px'; + sizeModificated = true; + } + + // Setting modal position in center on screen. + this.setPosition(window.innerHeight / 2 - this.container.offsetHeight / 2, window.innerWidth / 2 - this.container.offsetWidth / 2); + this.recalculateScale(); + this.recalculatePosition(); + this.recalculateSize(); + this.focus(); + } + + /** + * Sets modal size. + * @param {integer} height set a height of modal with an integer + * @param {integer} width set a width of modal with an integer + * @ignore + */ + + }, { + key: "setSize", + value: function setSize(height, width) { + this.container.style.height = height + 'px'; + this.container.style.width = width + 'px'; + this.recalculateSize(); + } + + /** + * Sets modal position. + * @param {integer} bottom set a bottom of div modal with an integer + * @param {integer} right set a right of div modal with an integer + * @ignore + */ + + }, { + key: "setPosition", + value: function setPosition(bottom, right) { + this.container.style.bottom = bottom + 'px'; + this.container.style.right = right + 'px'; + } + + /** + * Saves actual parameters of open modal object (like size and position...) to restore it + * once the modal object is re-opened. + * @ignore + */ + + }, { + key: "saveModalProperties", + value: function saveModalProperties() { + // Saving values of modal only when modal is in stack state. + if (this.properties.state == 'stack') { + this.properties.position.bottom = parseInt(this.container.style.bottom); + this.properties.position.right = parseInt(this.container.style.right); + this.properties.size.width = parseInt(this.container.style.width); + this.properties.size.height = parseInt(this.container.style.height); + } } - if (this.deviceProperties['isIOS']) { - this.iosSoftkeyboardOpened = false; - this.setIframeContainerHeight("100" + this.iosMeasureUnit); + /** + * Restore previous parameters values of closed modal (like size and position) and apply this parameters in actual modal. + * @ignore + */ + + }, { + key: "restoreModalProperties", + value: function restoreModalProperties() { + if (this.properties.state == 'stack') { + // Restoring Bottom and Right values from last modal + this.setPosition(this.properties.position.bottom, this.properties.position.right); + // Restoring Height and Left values from last modal + this.setSize(this.properties.size.height, this.properties.size.width); + } } - } else { - var title = wrs_int_getCustomEditorEnabled() != null ? wrs_int_getCustomEditorEnabled().title : _wrs_stringManager.getString('mathtype'); - _wrs_modalWindow.setTitle(title); - this.create(); - } -} + /** + * Set modal size. + * @ignore + */ -/** - * It put correct toolbar depending if exist other custom toolbars at the same time (e.g: Chemistry) - * @ignore - */ -ModalWindow.prototype.updateToolbar = function() { - if (customEditor = wrs_int_getCustomEditorEnabled()) { - var toolbar = customEditor.toolbar ? customEditor.toolbar : _wrs_int_wirisProperties['toolbar']; - _wrs_modalWindow.setTitle(customEditor.title); - if (this.toolbar == null || this.toolbar != toolbar) { - this.setToolbar(toolbar); + }, { + key: "recalculateSize", + value: function recalculateSize() { + this.wrapper.style.width = this.container.clientWidth - 12 + 'px'; + this.wrapper.style.height = this.container.clientHeight - 38 + 'px'; + this.contentContainer.style.height = parseInt(this.wrapper.offsetHeight - 50) + 'px'; } - } else { - var toolbar = this.checkToolbar(); - _wrs_modalWindow.setTitle(_wrs_stringManager.getString('mathtype')); - if (this.toolbar == null || this.toolbar != toolbar) { - this.setToolbar(toolbar); - wrs_int_disableCustomEditors(); + + /** + * Active and disable visibility of resize buttons in modal window depend on state. + * @ignore + */ + + }, { + key: "setResizeButtonsVisibility", + value: function setResizeButtonsVisibility() { + if (this.properties.state == 'stack') { + this.resizerTL.style.visibility = 'visible'; + this.resizerBR.style.visibility = 'visible'; + } else { + this.resizerTL.style.visibility = 'hidden'; + this.resizerBR.style.visibility = 'hidden'; + } } - } -} -/** - * It returns correct toolbar depending on the configuration local or serverside. - * @ignore - */ -ModalWindow.prototype.checkToolbar = function() { - var toolbar = (typeof _wrs_conf_editorParameters == 'undefined' || typeof _wrs_conf_editorParameters['toolbar'] == 'undefined') ? 'general' : _wrs_conf_editorParameters['toolbar']; - if(toolbar == 'general'){ - toolbar = (typeof _wrs_int_wirisProperties == 'undefined' || typeof _wrs_int_wirisProperties['toolbar'] == 'undefined') ? 'general' : _wrs_int_wirisProperties['toolbar']; - } - return toolbar; -} + /** + * Makes an object draggable adding mouse and touch events. + * @ignore + */ + + }, { + key: "addListeners", + value: function addListeners() { + // Button events (maximize, minimize, stack and close). + this.maximizeDiv.addEventListener('click', this.maximize.bind(this), true); + this.stackDiv.addEventListener('click', this.stack.bind(this), true); + this.minimizeDiv.addEventListener('click', this.minimize.bind(this), true); + this.closeDiv.addEventListener('click', this.cancelAction.bind(this)); + + // Mouse events. + wrs_addEvent(window, 'mousedown', this.startDrag.bind(this)); + wrs_addEvent(window, 'mouseup', this.stopDrag.bind(this)); + wrs_addEvent(window, 'mousemove', this.drag.bind(this)); + wrs_addEvent(window, 'resize', this.onWindowResize.bind(this)); + // Key events. + wrs_addEvent(window, 'keydown', this.onKeyDown.bind(this)); + } + + /** + * Removes draggable events from an object. + * @ignore + */ + + }, { + key: "removeListeners", + value: function removeListeners() { + // Mouse events. + wrs_removeEvent(window, 'mousedown', this.startDrag); + wrs_removeEvent(window, 'mouseup', this.stopDrag); + wrs_removeEvent(window, 'mousemove', this.drag); + wrs_removeEvent(window, 'resize', this.onWindowResize); + // Key events + wrs_removeEvent(window, 'keydown', this.onKeyDown); + } + + /** + * Returns mouse or touch coordinates (on touch events ev.ClientX doesn't exists) + * @param {event} ev mouse or touch event + * @return {object} with the X and Y coordinates. + * @ignore + */ + + }, { + key: "eventClient", + value: function eventClient(ev) { + if (typeof ev.clientX == 'undefined' && ev.changedTouches) { + var client = { + X: ev.changedTouches[0].clientX, + Y: ev.changedTouches[0].clientY + }; + return client; + } else { + client = { + X: ev.clientX, + Y: ev.clientY + }; + return client; + } + } -/** - * It controls cases where is needed to set an empty mathml or copy the current mathml value. - * @ignore - */ -ModalWindow.prototype.updateMathMLContent = function() { - if (this.properties.deviceProperties.isAndroid || this.properties.deviceProperties.isIOS) { - this.setMathMLWithCallback('[]"'); - } else { - this.setMathMLWithCallback(''); - } -} + /** + * Start drag function: set the object dragDataObject with the draggable object offsets coordinates. + * when drag starts (on touchstart or mousedown events). + * + * @param {event} ev touchstart or mousedown event. + * @ignore + */ -ModalWindow.prototype.isOpen = function() { - return this.properties.open; -} + }, { + key: "startDrag", + value: function startDrag(ev) { + if (this.properties.state == 'minimized') { + return; + } + if (ev.target.className == 'wrs_modal_title') { + if (typeof this.dragDataObject === 'undefined' || this.dragDataObject === null) { + ev = ev || event; + // Save first click mouse point on screen + this.dragDataObject = { + x: this.eventClient(ev).X, + y: this.eventClient(ev).Y + }; + // Reset last drag position when start drag + this.lastDrag = { + x: "0px", + y: "0px" + }; + // Init right and bottom values for window modal if it isn't exist. + if (this.container.style.right == '') { + this.container.style.right = "0px"; + } + if (this.container.style.bottom == '') { + this.container.style.bottom = "0px"; + } -/** - * Closes modal window and restores viewport header. - * @ignore - */ -ModalWindow.prototype.close = function() { - this.saveModalProperties(); - // Set disabled focus to prevent lost focus. - this.setMathML('',true); - this.overlayDiv.style.visibility = 'hidden'; - this.containerDiv.style.visibility = 'hidden'; - this.containerDiv.style.display = 'none'; - this.containerDiv.style.opacity = '0'; - this.overlayDiv.style.display = 'none'; - this.properties.open = false; - wrs_int_disableCustomEditors(); - setTimeout( - function() { - if (typeof _wrs_currentEditor != 'undefined' && _wrs_currentEditor) { - _wrs_currentEditor.focus(); + // Needed for IE11 for apply disabled mouse events on editor because iexplorer need a dinamic object to apply this property. + if (this.isIE11()) {} + // this.iframe.style['position'] = 'relative'; + + // Apply class for disable involuntary select text when drag. + wrs_addClass(document.body, 'wrs_noselect'); + wrs_addClass(this.overlay, 'wrs_overlay_active'); + // Obtain screen limits for prevent overflow. + this.limitWindow = this.getLimitWindow(); + } } - }, 100); - _wrs_popupWindow.postMessage({'objectName' : 'editorClose'}, this.iframeOrigin); -} + } -/** - * Util function for know if user is using iexplorer v11. - * @return {boolean} return true if browser is iexplorer v11 or false in others. - * @ignore - */ -ModalWindow.prototype.isIE11 = function() { - if (navigator.userAgent.search("Msie/") >= 0 || navigator.userAgent.search("Trident/") >= 0 || navigator.userAgent.search("Edge/") >= 0 ) { - return true; - } - return false; -} + /** + * UpdatesdragDataObject with the draggable object coordinates when the draggable object is being moved. + * + * @param {event} ev touchmouve or mousemove events. + * @ignore + */ -ModalWindow.prototype.addClass = function(cls) { - wrs_addClass(this.overlayDiv, cls); - wrs_addClass(this.titleBardDiv, cls); - wrs_addClass(this.overlayDiv, cls); - wrs_addClass(this.containerDiv, cls); - wrs_addClass(this.iframeContainer, cls); - wrs_addClass(this.iframe, cls); - wrs_addClass(this.stackDiv, cls); - wrs_addClass(this.minimizeDiv, cls); - wrs_addClass(this.maximizeDiv, cls); -} + }, { + key: "drag", + value: function drag(ev) { + if (this.dragDataObject) { + ev.preventDefault(); + ev = ev || event; + // Calculate max and min between actual mouse position and limit of screeen. It restric the movement of modal into window. + var limitY = Math.min(this.eventClient(ev).Y + window.pageYOffset, this.limitWindow.minPointer.y + window.pageYOffset); + limitY = Math.max(this.limitWindow.maxPointer.y + window.pageYOffset, limitY); + var limitX = Math.min(this.eventClient(ev).X + window.pageXOffset, this.limitWindow.minPointer.x + window.pageXOffset); + limitX = Math.max(this.limitWindow.maxPointer.x + window.pageXOffset, limitX); + // Substract limit with first position to obtain relative pixels increment to the anchor point. + var dragX = limitX - this.dragDataObject.x + "px"; + var dragY = limitY - this.dragDataObject.y + "px"; + // Save last valid position of modal before window overflow. + this.lastDrag = { + x: dragX, + y: dragY + }; + // This move modal with hadware acceleration. + this.container.style.transform = "translate3d(" + dragX + "," + dragY + ",0)"; + this.container.style.position = 'absolute'; + } + if (this.resizeDataObject) { + var limitX = Math.min(this.eventClient(ev).X, window.innerWidth - this.scrollbarWidth - 7); + var limitY = Math.min(this.eventClient(ev).Y, window.innerHeight - 7); + if (limitX < 0) { + limitX = 0; + } -ModalWindow.prototype.removeClass = function(cls) { - wrs_removeClass(this.overlayDiv, cls); - wrs_removeClass(this.titleBardDiv, cls); - wrs_removeClass(this.overlayDiv, cls); - wrs_removeClass(this.containerDiv, cls); - wrs_removeClass(this.iframeContainer, cls); - wrs_removeClass(this.iframe, cls); - wrs_removeClass(this.stackDiv, cls); - wrs_removeClass(this.minimizeDiv, cls); - wrs_removeClass(this.maximizeDiv, cls); -} + if (limitY < 0) { + limitY = 0; + } -ModalWindow.prototype.setTitle = function(title) { - this.titleDiv.innerHTML = title; - this.title = title; + var scaleMultiplier; + if (this.leftScale) { + scaleMultiplier = -1; + } else { + scaleMultiplier = 1; + } + this.container.style.width = this.initialWidth + scaleMultiplier * (limitX - this.resizeDataObject.x) + 'px'; + this.container.style.height = this.initialHeight + scaleMultiplier * (limitY - this.resizeDataObject.y) + 'px'; + if (!this.leftScale) { + if (this.resizeDataObject.x - limitX - this.initialWidth < -580) { + this.container.style.right = this.initialRight - (limitX - this.resizeDataObject.x) + 'px'; + } else { + this.container.style.right = this.initialRight + this.initialWidth - 580 + "px"; + this.container.style.width = "580px"; + } + if (this.resizeDataObject.y - limitY < this.initialHeight - 338) { + this.container.style.bottom = this.initialBottom - (limitY - this.resizeDataObject.y) + 'px'; + } else { + this.container.style.bottom = this.initialBottom + this.initialHeight - 338 + "px"; + this.container.style.height = "338px"; + } + } + this.recalculateScale(); + this.recalculatePosition(); + } + } + /** + * Get limits of actual window to limit modal movement + * @return {Object} Object containing mouseX and mouseY are coordinates of actual mouse on screen. + * @ignore + */ + + }, { + key: "getLimitWindow", + value: function getLimitWindow() { + // Obtain dimentions of window page. + var maxWidth = window.innerWidth; + var maxHeight = window.innerHeight; + + // Calculate relative position of mouse point into window. + var offSetToolbarY = this.container.offsetHeight + parseInt(this.container.style.bottom) - (maxHeight - (this.dragDataObject.y - window.pageXOffset)); + var offSetToolbarX = maxWidth - this.scrollbarWidth - (this.dragDataObject.x - window.pageXOffset) - parseInt(this.container.style.right); + + // Calculate limits with sizes of window, modal and mouse position. + var minPointerY = maxHeight - this.container.offsetHeight + offSetToolbarY; + var maxPointerY = this.title.offsetHeight - (this.title.offsetHeight - offSetToolbarY); + var minPointerX = maxWidth - offSetToolbarX - this.scrollbarWidth; + var maxPointerX = this.container.offsetWidth - offSetToolbarX; + var minPointer = { x: minPointerX, y: minPointerY }; + var maxPointer = { x: maxPointerX, y: maxPointerY }; + return { minPointer: minPointer, maxPointer: maxPointer }; + } + /** + * Get Scrollbar width size of browser + * @ignore + */ + + }, { + key: "getScrollBarWidth", + value: function getScrollBarWidth() { + // Create a paragraph with full width of page. + var inner = document.createElement('p'); + inner.style.width = "100%"; + inner.style.height = "200px"; + + // Create a hidden div to compare sizes. + var outer = document.createElement('div'); + outer.style.position = "absolute"; + outer.style.top = "0px"; + outer.style.left = "0px"; + outer.style.visibility = "hidden"; + outer.style.width = "200px"; + outer.style.height = "150px"; + outer.style.overflow = "hidden"; + outer.appendChild(inner); + + document.body.appendChild(outer); + var widthOuter = inner.offsetWidth; + + // Change type overflow of paragraph for measure scrollbar. + outer.style.overflow = 'scroll'; + var widthInner = inner.offsetWidth; + + // If measure is the same, we compare with internal div. + if (widthOuter == widthInner) { + widthInner = outer.clientWidth; + } + document.body.removeChild(outer); + + return widthOuter - widthInner; + } + + /** + * Set the dragDataObject to null when the drag finish (touchend or mouseup events). + * + * @param {event} ev touchend or mouseup event. + * @ignore + */ + + }, { + key: "stopDrag", + value: function stopDrag(ev) { + // Due to we have multiple events that call this function, we need only to execute the next modifiers one time, + // when the user stops to drag and dragDataObject is not null (the object to drag is attached). + if (this.dragDataObject || this.resizeDataObject) { + // If modal doesn't change, it's not necessary to set position with interpolation + if (this.container.style.position != 'fixed') { + this.container.style.position = 'fixed'; + // Fixed position makes the coords relative to the main window. So that, we need to transform + // the absolute coords to relative. + this.container.style.transform = ''; + if (this.dragDataObject) { + this.container.style.right = parseInt(this.container.style.right) - parseInt(this.lastDrag.x) + pageXOffset + "px"; + this.container.style.bottom = parseInt(this.container.style.bottom) - parseInt(this.lastDrag.y) + pageYOffset + "px"; + } + } + // We make focus on editor after drag modal windows to prevent lose focus. + this.focus(); + // Restore mouse events on iframe + // this.iframe.style['pointer-events'] = 'auto'; + document.body.style['user-select'] = ''; + // Restore static state of iframe if we use iexplorer + if (this.isIE11()) {} + // this.iframe.style['position'] = null; + + // Active text select event + wrs_removeClass(document.body, 'wrs_noselect'); + wrs_removeClass(this.overlay, 'wrs_overlay_active'); + } + this.dragDataObject = null; + this.resizeDataObject = null; + this.initialWidth = null; + this.leftScale = null; + } + + /** + * Recalculating scale for modal when resize browser window * + * @ignore + */ + + }, { + key: "onWindowResize", + value: function onWindowResize() { + this.recalculateScrollBar(); + this.recalculatePosition(); + this.recalculateScale(); + } + + /** + * Keydown events: + * Esc key close modal window. + * Tab key tab to submit button. + * @param {event} ev + */ + + }, { + key: "onKeyDown", + value: function onKeyDown(ev) { + if (ev.key !== undefined && ev.repeat === false) { + // Code for detect Esc event + if (ev.key === "Escape" || ev.key === 'Esc') { + if (this.properties.open) { + this.cancelAction(); + } + } + // Code for detect Tab event + if (ev.key === "Tab") { + this.submitButton.focus(); + ev.preventDefault(); + } + } + } + /** + * Recalculating position for modal when resize browser window + * @ignore + */ + + }, { + key: "recalculatePosition", + value: function recalculatePosition() { + this.container.style.right = Math.min(parseInt(this.container.style.right), window.innerWidth - this.scrollbarWidth - this.container.offsetWidth) + "px"; + if (parseInt(this.container.style.right) < 0) { + this.container.style.right = "0px"; + } + this.container.style.bottom = Math.min(parseInt(this.container.style.bottom), window.innerHeight - this.container.offsetHeight) + "px"; + if (parseInt(this.container.style.bottom) < 0) { + this.container.style.bottom = "0px"; + } + } -} -/** - * Create modal dialog for desktop OS. - * @ignore - */ -ModalWindow.prototype.createModalWindowDesktop = function() { - this.addClass('wrs_modal_desktop'); - this.stackModalWindow(); -} + /** + * Recalculating scale for modal when resize browser window + * @ignore + */ -/** - * Create modal dialog for non mobile android devices. - * @ignore - */ + }, { + key: "recalculateScale", + value: function recalculateScale() { + var sizeModificated = false; + if (parseInt(this.container.style.width) > 580) { + this.container.style.width = Math.min(parseInt(this.container.style.width), window.innerWidth - this.scrollbarWidth) + "px"; + sizeModificated = true; + } else { + this.container.style.width = "580px"; + sizeModificated = true; + } + if (parseInt(this.container.style.height) > 338) { + this.container.style.height = Math.min(parseInt(this.container.style.height), window.innerHeight) + "px"; + sizeModificated = true; + } else { + this.container.style.height = "338px"; + sizeModificated = true; + } + if (sizeModificated) { + this.recalculateSize(); + } + } + + /** + * Recalculating width of scrollBar browser + * @ignore + */ -ModalWindow.prototype.createModalWindowAndroid = function() { - this.addClass('wrs_modal_android'); - window.addEventListener('resize', function (e) { - if (_wrs_conf_modalWindow) { - _wrs_modalWindow.orientationChangeAndroidSoftkeyboard(); + }, { + key: "recalculateScrollBar", + value: function recalculateScrollBar() { + this.hasScrollBar = window.innerWidth > document.documentElement.clientWidth; + if (this.hasScrollBar) { + this.scrollbarWidth = this.getScrollBarWidth(); + } else { + this.scrollbarWidth = 0; + } } - }); -} -/** - * Create modal dialog for iOS devices. - * @ignore - */ + /** + * Hide soft keyboards on IOS systems. + * @ignore + */ -ModalWindow.prototype.createModalWindowIos = function() { - this.addClass('wrs_modal_ios'); - // Refresh the size when the orientation is changed - window.addEventListener('resize', function (e) { - if (_wrs_conf_modalWindow) { - _wrs_modalWindow.orientationChangeIosSoftkeyboard(); + }, { + key: "hideKeyboard", + value: function hideKeyboard() { + document.activeElement.blur(); } - }); -} + /** + * Focus to content object + * @ignore + */ + + }, { + key: "focus", + value: function focus() { + if (this.contentManager != null && typeof this.contentManager.onFocus !== 'undefined') { + this.contentManager.onFocus(); + } + } + + /** + * Returns true when the device is on portrait mode. + * @ignore + */ + + }, { + key: "portraitMode", + value: function portraitMode() { + return window.innerHeight > window.innerWidth; + } + + /** + * Change container sizes when the keyboard is opened on iOS. + * @ignore + */ + + }, { + key: "openedIosSoftkeyboard", + value: function openedIosSoftkeyboard() { + if (!this.iosSoftkeyboardOpened && this.iosDivHeight != null && this.iosDivHeight == "100" + this.iosMeasureUnit) { + if (this.portraitMode()) { + this.setContainerHeight("63" + this.iosMeasureUnit); + } else { + this.setContainerHeight("40" + this.iosMeasureUnit); + } + } + this.iosSoftkeyboardOpened = true; + } + + /** + * Change container sizes when the keyboard is closed on iOS. + * @ignore + */ + + }, { + key: "closedIosSoftkeyboard", + value: function closedIosSoftkeyboard() { + this.iosSoftkeyboardOpened = false; + this.setContainerHeight("100" + this.iosMeasureUnit); + } + + /** + * Change container sizes when orientation is changed on iOS. + * @ignore + */ + + }, { + key: "orientationChangeIosSoftkeyboard", + value: function orientationChangeIosSoftkeyboard() { + if (this.iosSoftkeyboardOpened) { + if (this.portraitMode()) { + this.setContainerHeight("63" + this.iosMeasureUnit); + } else { + this.setContainerHeight("40" + this.iosMeasureUnit); + } + } else { + this.setContainerHeight("100" + this.iosMeasureUnit); + } + } + + /** + * Change container sizes when orientation is changed on Android. + * @ignore + */ + + }, { + key: "orientationChangeAndroidSoftkeyboard", + value: function orientationChangeAndroidSoftkeyboard() { + this.setContainerHeight("100%"); + } + + /** + * Set iframe container height. + * @ignore + */ + + }, { + key: "setContainerHeight", + value: function setContainerHeight(height) { + this.iosDivHeight = height; + this.wrapper.style.height = height; + // this.editor.getElement().style.height = (this.container.offsetHeight -10) - this.controlsDiv.offsetHeight + 'px'; + } + + /** + * Check content of editor before close action + * @ignore + */ + + }, { + key: "showPopUpMessage", + value: function showPopUpMessage() { + if (this.properties.state == 'minimized') { + this.stack(); + } + this.popup.show(); + } + }, { + key: "setTitle", + value: function setTitle(title) { + this.title.innerHTML = title; + } + }]); + + return ModalWindow; +}(); /** - * Restore previous state, position and size of previous stacked modal window - * @ignore - */ + * This class shows a dialog message overlaying a dom element in order to + * accept / cancel discart changes. The dialog can be closed i.e the overlay dissapears + * o canceled. In this last case a callback function should be called. + * + * The constructor accepts the following attributes object: + * { + * overlayElement: '', Element to be overlayed. + * callbacks: { + * 'closeCallback' : function(), // Callback function to be called if the dialog is closed. + * 'cancelCallback' : function() // Callback function to be called if the dialog is cancelled + * strings: { + * 'submitString' : '', // Submit button string + * 'cancelString : ''. // Cancel button string + * 'message': '' // Message string + * } + * } + * @param {object} popupProperties + */ + + +var PopUpMessage = function () { + function PopUpMessage(popupProperties) { + _classCallCheck(this, PopUpMessage); + + this.overlay = popupProperties.overlayElement; + this.callbacks = popupProperties.callbacks; + this.overlayEnvolture = this.overlay.appendChild(document.createElement("div")); + this.overlayEnvolture.setAttribute("class", "wrs_popupmessage_overlay_envolture"); + + this.message = this.overlayEnvolture.appendChild(document.createElement("div")); + this.message.id = "wrs_popupmessage"; + this.message.setAttribute("class", "wrs_popupmessage_panel"); + this.message.innerHTML = popupProperties.strings.message; + + var overlay = this.overlayEnvolture.appendChild(document.createElement("div")); + overlay.setAttribute("class", "wrs_popupmessage_overlay"); + // We create a overlay that close popup message on click in there + overlay.addEventListener("click", this.cancelAction.bind(this)); + + this.buttonArea = this.message.appendChild(document.createElement('div')); + this.buttonArea.setAttribute("class", "wrs_popupmessage_button_area"); + this.buttonArea.id = 'wrs_popup_button_area'; + + // Buttons creation + var buttonSubmitArguments = { + class: "wrs_button_accept", + innerHTML: popupProperties.strings.submitString, + id: 'wrs_popup_accept_button' + }; + this.acceptButton = this.createButton(buttonSubmitArguments, this.closeAction.bind(this)); + this.buttonArea.appendChild(this.acceptButton); -ModalWindow.prototype.restoreStateModalWindow = function() { - if (this.properties.state == 'maximized') { - // Reseting states for prevent return to stack state. - this.maximizeModalWindow(); - } else if (this.properties.state == 'minimized') { - // Reseting states for prevent return to stack state. - this.properties.state = this.properties.previousState; - this.properties.previousState = ''; - this.minimizeModalWindow(); - } else { - this.stackModalWindow(); + var buttonCancelArguments = { + class: "wrs_button_cancel", + innerHTML: popupProperties.strings.cancelString, + id: 'wrs_popup_cancel_button' + }; + this.cancelButton = this.createButton(buttonCancelArguments, this.cancelAction.bind(this)); + this.buttonArea.appendChild(this.cancelButton); } -} -ModalWindow.prototype.stackModalWindow = function () { - this.properties.previousState = this.properties.state; - this.properties.state = 'stack'; - this.overlayDiv.style.background = "rgba(0,0,0,0)"; - this.removeClass('wrs_maximized'); - this.minimizeDiv.title = _wrs_stringManager.getString('minimize'); - this.removeClass('wrs_minimized'); - this.addClass('wrs_stack'); - - this.restoreModalProperties(); - // Sending sizes to iframe - if (typeof _wrs_popupWindow != 'undefined' && _wrs_popupWindow) { - this.containerDiv.style.position = "fixed"; - this.setResizeButtonsVisibility(); - this.applyIframeSize(); - } - // Need recalculate position of actual modal because window can was changed in fullscreenmode - this.recalculateScrollBar(); - this.recalculatePosition(); - this.recalculateScale(); - if (typeof _wrs_popupWindow != 'undefined' && _wrs_popupWindow) { - this.focus(); - } -} + /** + * This method create a button with arguments and return button dom object + * @param {object} parameters An object containg id, class and innerHTML button text. + * @param {object} callback Callback method to call on click event. + */ + + + _createClass(PopUpMessage, [{ + key: "createButton", + value: function createButton(parameters, callback) { + function popUpButton(parameters) { + this.element = document.createElement("button"); + this.element.setAttribute("id", parameters.id); + this.element.setAttribute("class", parameters.class); + this.element.innerHTML = parameters.innerHTML; + this.element.addEventListener("click", callback); + } + + popUpButton.prototype.getElement = function () { + return this.element; + }; -ModalWindow.prototype.minimizeModalWindow = function() { - // Saving width, height, top and bottom parameters to restore when open - this.saveModalProperties(); - if (this.properties.state == 'minimized' && this.properties.previousState == 'stack') { - this.stackModalWindow(); - } - else if (this.properties.state == 'minimized' && this.properties.previousState == 'maximized') { - this.maximizeModalWindow(); - } - else { - // Setting css to prevent important tag into css style - this.containerDiv.style.height = "30px"; - this.containerDiv.style.width = "250px"; - this.containerDiv.style.bottom = "0px"; - this.containerDiv.style.right = "10px"; - - this.removeListeners(); - this.properties.previousState = this.properties.state; - this.properties.state = "minimized"; - this.setResizeButtonsVisibility(); - this.overlayDiv.style.background = "rgba(0,0,0,0)"; - this.minimizeDiv.title = _wrs_stringManager.getString('maximize'); - - if (wrs_containsClass(this.overlayDiv, 'wrs_stack')) { - this.removeClass('wrs_stack'); + return new popUpButton(parameters).getElement(); } - else { - this.removeClass('wrs_maximized'); + + /** + * This method show the popupmessage containing a message, and two buttons + * to cancel the action or close the modal dialog. + */ + + }, { + key: "show", + value: function show() { + if (this.overlayEnvolture.style.display != 'block') { + // Clear focus with blur for prevent press anykey + document.activeElement.blur(); + + // For works with Safari + window.focus(); + this.overlayEnvolture.style.display = 'block'; + } else { + this.overlayEnvolture.style.display = 'none'; + _wrs_modalWindow.focus(); + } } - this.addClass('wrs_minimized'); - } -} -/** - * Maximize modal window. - * @ignore - */ -ModalWindow.prototype.maximizeModalWindow = function() { - // Saving width, height, top and bottom parameters to restore when open - this.saveModalProperties(); - if (this.properties.state != 'maximized') { - this.properties.previousState = this.properties.state; - this.properties.state = 'maximized'; - } - // Don't permit resize on maximize mode. - this.setResizeButtonsVisibility(); - - if (wrs_containsClass(this.overlayDiv, 'wrs_minimized')) { - this.minimizeDiv.title = _wrs_stringManager.getString('minimize'); - this.removeClass('wrs_minimized'); - } - else if (wrs_containsClass(this.overlayDiv, 'wrs_stack')) { - this.containerDiv.style.left = null; - this.containerDiv.style.top = null; - this.removeClass('wrs_stack'); - } - this.overlayDiv.style.background = "rgba(0,0,0,0.8)"; - this.overlayDiv.style.display = ''; - this.addClass('wrs_maximized'); - - // Set size to 80% screen with a max size. - this.setSize(parseInt(window.innerHeight * 0.8) , parseInt(window.innerWidth * 0.8)); - var sizeModificated = false; - if (this.containerDiv.clientHeight > 700) { - this.containerDiv.style.height = '700px'; - sizeModificated = true; - } - if (this.containerDiv.clientWidth > 1200) { - this.containerDiv.style.width = '1200px'; - sizeModificated = true; - } - - // Setting modal position in center on screen. - this.setPosition(window.innerHeight / 2 - this.containerDiv.offsetHeight / 2, window.innerWidth / 2 - this.containerDiv.offsetWidth / 2); - this.recalculateScale(); - this.recalculatePosition(); - this.applyIframeSize(); - this.focus(); -} + /** + * This method cancel the popupMessage: the dialog dissapears revealing the overlaid element. + * A callback method is called (if defined). For example a method to focus the overlaid element. + */ -/** - * Set modal size. - * @param {integer} height set a height of modal with an integer - * @param {integer} width set a width of modal with an integer - * @ignore - */ -ModalWindow.prototype.setSize = function(height, width) { - this.containerDiv.style.height = height + 'px'; - this.containerDiv.style.width = width + 'px'; - this.applyIframeSize(); -} + }, { + key: "cancelAction", + value: function cancelAction() { + this.overlayEnvolture.style.display = 'none'; + if (typeof this.callbacks.cancelCallback !== 'undefined') { + this.callbacks.cancelCallback(); + } + } -/** - * Set modal position. - * @param {integer} bottom set a bottom of div modal with an integer - * @param {integer} right set a right of div modal with an integer - * @ignore - */ -ModalWindow.prototype.setPosition = function(bottom, right) { - this.containerDiv.style.bottom = bottom + 'px'; - this.containerDiv.style.right = right + 'px'; -} + /** + * This method closes the popupMessage: the dialog dissapears and the close callback is called. + * For example to close the overlaid element. + */ -/** - * Save actual parameters of opened modal (like size and position...) to restore when modal will open. - * @ignore - */ -ModalWindow.prototype.saveModalProperties = function() { - // Saving values of modal only when modal is in stack state. - if (this.properties.state == 'stack') { - this.properties.position.bottom = parseInt(this.containerDiv.style.bottom); - this.properties.position.right = parseInt(this.containerDiv.style.right); - this.properties.size.width = parseInt(this.containerDiv.style.width); - this.properties.size.height = parseInt(this.containerDiv.style.height); - } -} + }, { + key: "closeAction", + value: function closeAction() { + this.cancelAction(); + if (typeof this.callbacks.closeCallback !== 'undefined') { + this.callbacks.closeCallback(); + } + } + }]); + + return PopUpMessage; +}(); /** + * This class implements ModalContent interface. Manage the following: + * - insertion in modal object (insert(modalObject) method) + * - actions to be done once the modal object has been submited (submitAction() method) + * - updates itself when modalObject is updated with a re-open action for example (update(modalObject) method) + * - comunicates to modalObject if some changes have be done (hasChanges() method) + * + * @param {object} editorAttributes editor attributes. See http://docs.wiris.com/en/mathtype/mathtype_web/sdk-api/parameters + * for further information. + * @ignore + */ -/** - * Restore previous parameters values of closed modal (like size and position) and apply this parameters in actual modal. - * @ignore - */ -ModalWindow.prototype.restoreModalProperties = function() { - if (this.properties.state == 'stack') { - // Restoring Bottom and Right values from last modal - this.setPosition(this.properties.position.bottom, this.properties.position.right); - // Restoring Height and Left values from last modal - this.setSize(this.properties.size.height, this.properties.size.width); - } -} -/** - * Set modal size. - * @ignore - */ -ModalWindow.prototype.applyIframeSize = function() { - this.iframeContainer.style.width = this.containerDiv.clientWidth + 'px'; - this.iframeContainer.style.height = this.containerDiv.clientHeight - 38 + 'px'; - this.iframe.style.width = parseInt(this.iframeContainer.style.width) - 12 + 'px'; - this.iframe.style.height = this.iframeContainer.style.height; - if ( typeof _wrs_popupWindow != 'undefined') { - _wrs_popupWindow.postMessage({'objectName' : 'editorResize', 'arguments': [_wrs_modalWindow.iframeContainer.offsetHeight - 10]}, this.iframeOrigin); - } -} +var contentManager = function () { + // Editor listener. + function contentManager(editorAttributes) { + _classCallCheck(this, contentManager); -/** - * Active and disable visibility of resize buttons in modal window depend on state. - * @ignore - */ -ModalWindow.prototype.setResizeButtonsVisibility = function() { - if (this.properties.state == 'stack') { - this.resizerTL.style.visibility = 'visible'; - this.resizerBR.style.visibility = 'visible'; + this.editorListener = new EditorListener(); + this.editor = null; + this.editorAttributes = editorAttributes; + this.lastImageWasNew = false; + // Device properties + var ua = navigator.userAgent.toLowerCase(); + this.deviceProperties = {}; + this.deviceProperties.isAndroid = ua.indexOf("android") > -1; + this.deviceProperties.isIOS = ua.indexOf("ipad") > -1 || ua.indexOf("iphone") > -1; + // Toolbar + this.toolbar = null; + this.loadEditor(); } - else { - this.resizerTL.style.visibility = 'hidden'; - this.resizerBR.style.visibility = 'hidden'; - } -} -/** - * Makes an object draggable adding mouse and touch events. - * @ignore - */ -ModalWindow.prototype.addListeners = function() { - // Mouse events. - wrs_addEvent(document.body, 'mousedown', this.startDrag.bind(this)); - wrs_addEvent(window, 'mouseup', this.stopDrag.bind(this)); - wrs_addEvent(document, 'mouseup', this.stopDrag.bind(this)); - wrs_addEvent(document, 'mousemove', this.drag.bind(this)); - wrs_addEvent(window, 'resize', this.onWindowResize.bind(this)); -} + /** + * Mandatory method: inserts editor into modal object content container. + * @param {object} modalObject + * @ignore + */ -/** - * Removes draggable events from an object. - * @ignore - */ -ModalWindow.prototype.removeListeners = function() { - // Mouse events. - wrs_removeEvent(document.body, 'mousedown', this.startDrag); - wrs_removeEvent(window, 'mouseup', this.stopDrag); - wrs_removeEvent(document, 'mouseup', this.stopDrag); - wrs_removeEvent(document.getElementsByClassName("wrs_modal_iframe")[0], 'mouseup', this.stopDrag); - wrs_removeEvent(document, 'mousemove', this.drag); - wrs_removeEvent(window, 'resize', this.onWindowResize); -} + _createClass(contentManager, [{ + key: "insert", + value: function insert(modalObject) { + // Before insert the editor we update the modal object title to avoid weird render display. + this.updateTitle(modalObject); + this.insertEditor(modalObject); + } + + /** + * Method to insert MathType into modal object. This method + * watis until editor JavaScript is loaded to insert the editor into + * contentContainer modal object element. + * @ignore + */ + + }, { + key: "insertEditor", + value: function insertEditor(modalObject) { + // To know if editor JavaScript is loaded we need to wait until com.wiris.jsEditor namespace is ready. + if ('com' in window && 'wiris' in window.com && 'jsEditor' in window.com.wiris) { + this.editor = com.wiris.jsEditor.JsEditor.newInstance(this.editorAttributes); + this.editor.insertInto(modalObject.contentContainer); + this.editor.focus(); + + // Editor listener: this object manages the changes logic of editor. + this.editor.getEditorModel().addEditorListener(this.editorListener); + + // iOS events. + if (modalObject.deviceProperties['isIOS']) { + setTimeout(function () { + _wrs_modalWindow.hideKeyboard(); + }, 400); + var formulaDisplayDiv = document.getElementsByClassName('wrs_formulaDisplay')[0]; + wrs_addEvent(formulaDisplayDiv, 'focus', modalObject.openedIosSoftkeyboard.bind(modalObject)); + wrs_addEvent(formulaDisplayDiv, 'blur', modalObject.closedIosSoftkeyboard.bind(modalObject)); + } -/** - * Returns mouse or touch coordinates (on touch events ev.ClientX doesn't exists) - * @param {event} ev mouse or touch event - * @return {object} with the X and Y coordinates. - * @ignore - */ -ModalWindow.prototype.eventClient = function(ev) { - if (typeof(ev.clientX) == 'undefined' && ev.changedTouches) { - var client = { - X : ev.changedTouches[0].clientX, - Y : ev.changedTouches[0].clientY - }; - return client; - } - else { - client = { - X : ev.clientX, - Y : ev.clientY - }; - return client; - } -} + this.onOpen(modalObject); + this.editor.onContentChanged; + } else { + setTimeout(contentManager.prototype.insertEditor.bind(this, modalObject), 100); + } + } + /** + * Loads MathType ediitor JavaScript. + * @ignore + */ + + }, { + key: "loadEditor", + value: function loadEditor() { + var queryParams = window.location.search.substring(1).split("&"); + var version = ""; + for (var i = 0; i < queryParams.length; i++) { + var pos = queryParams[i].indexOf("v="); + if (pos >= 0) { + version = queryParams[i].substring(2); + } + } -/** - * Set the overlay div display - * - * @param {event} ev touchstart or mousedown event. - * @ignore - */ -ModalWindow.prototype.setOverlayDiv = function(ev) { - this.overlayDiv.style.display = ''; -} + var script = document.createElement('script'); + script.type = 'text/javascript'; + var editorUrl = _wrs_conf_editorUrl; + // Change to https if necessary. + // We create an object url for parse url string and work more efficiently. + var urlObject = document.createElement('a'); + urlObject.href = editorUrl; + + if (window.location.href.indexOf("https://") == 0) { + // It check if browser is https and configuration is http. If this is so, we will replace protocol. + if (urlObject.protocol == 'http:') { + urlObject.protocol = 'https:'; + } + } -/** - * Start drag function: set the object _wrs_dragDataObject with the draggable object offsets coordinates. - * when drag starts (on touchstart or mousedown events). - * - * @param {event} ev touchstart or mousedown event. - * @ignore - */ -ModalWindow.prototype.startDrag = function(ev) { - if (this.properties.state == 'minimized') { - return; - } - if (ev.target.className == 'wrs_modal_title') { - if(typeof this.dragDataObject === 'undefined' || this.dragDataObject === null) { - ev = ev || event; - // Save first click mouse point on screen - this.dragDataObject = { - x: this.eventClient(ev).X, - y: this.eventClient(ev).Y - }; - // Reset last drag position when start drag - this.lastDrag = { - x: "0px", - y: "0px" - }; - // Init right and bottom values for window modal if it isn't exist. - if(this.containerDiv.style.right == ''){ - this.containerDiv.style.right = "0px"; + // Check protocol and remove port if it's standard. + if (urlObject.port == '80' || urlObject.port == '443') { + editorUrl = urlObject.protocol + '//' + urlObject.hostname + '/' + urlObject.pathname; + } else { + editorUrl = urlObject.protocol + '//' + urlObject.hostname + ':' + urlObject.port + '/' + urlObject.pathname; } - if(this.containerDiv.style.bottom == ''){ - this.containerDiv.style.bottom = "0px"; + + // Editor stats. + var statEditor = _wrs_conf_editor; + var statSaveMode = _wrs_conf_saveMode; + var statVersion = _wrs_conf_version; + + script.src = editorUrl + "?lang=" + _wrs_int_langCode + '&stats-editor=' + statEditor + '&stats-mode=' + statSaveMode + '&stats-version=' + statVersion; + document.getElementsByTagName('head')[0].appendChild(script); + } + + /** + * Set the editor initial content: an existing formula or a blank MathML + */ + + }, { + key: "setInitialContent", + value: function setInitialContent() { + if (!_wrs_isNewElement) { + var mathml = wrs_mathmlDecode(_wrs_temporalImage.getAttribute(_wrs_conf_imageMathmlAttribute)); + this.setMathML(mathml); } - // Disable mouse events on editor when we start to drag modal. - this.iframe.style['pointer-events'] = 'none'; - // Needed for IE11 for apply disabled mouse events on editor because iexplorer need a dinamic object to apply this property. - if (this.isIE11()) { - this.iframe.style['position'] = 'relative'; + } + + /** + * Set a MathML into editor. + * @param {string} mathml MathML string. + * @param {bool} focusDisabled if true editor don't get focus after the MathML is set. false by default. + * @ignore + */ + + }, { + key: "setMathML", + value: function setMathML(mathml, focusDisabled) { + // By default focus is enabled + if (typeof focusDisabled === 'undefined') { + focusDisabled = false; } - // Apply class for disable involuntary select text when drag. - wrs_addClass(document.body, 'wrs_noselect'); - // Obtain screen limits for prevent overflow. - this.limitWindow = this.getLimitWindow(); - // Prevent lost mouse events into other iframes - // Activate overlay div to prevent mouse events behind modal - if (_wrs_modalWindow.properties.state != "maximized") { - this.overlayDiv.style.display = ""; + // Using setMathML method is not a change produced by the user but for the API + // so we set to false the contentChange property of editorListener. + this.editor.setMathMLWithCallback(mathml, function () { + this.editorListener.setWaitingForChanges(true); + }.bind(this)); + // We need to wait a little until the callback finish. + setTimeout(function () { + this.editorListener.setIsContentChanged(false); + }.bind(this), 500); + + // In some scenarios - like closing modal object - editor mustn't be focused. + if (!focusDisabled) { + this.onFocus(); } } - } -} + /** + * Set focus on editor. + * @ignore + */ -/** - * Updates_wrs_dragDataObject with the draggable object coordinates when the draggable object is being moved. - * - * @param {event} ev touchmouve or mousemove events. - * @ignore - */ -ModalWindow.prototype.drag = function(ev) { - if (this.dragDataObject) { - ev.preventDefault(); - ev = ev || event; - // Calculate max and min between actual mouse position and limit of screeen. It restric the movement of modal into window. - var limitY = Math.min(this.eventClient(ev).Y + window.pageYOffset,this.limitWindow.minPointer.y + window.pageYOffset); - limitY = Math.max(this.limitWindow.maxPointer.y + window.pageYOffset,limitY); - var limitX = Math.min(this.eventClient(ev).X + window.pageXOffset,this.limitWindow.minPointer.x + window.pageXOffset); - limitX = Math.max(this.limitWindow.maxPointer.x + window.pageXOffset,limitX); - // Substract limit with first position to obtain relative pixels increment to the anchor point. - var dragX = limitX - this.dragDataObject.x + "px"; - var dragY = limitY - this.dragDataObject.y + "px"; - // Save last valid position of modal before window overflow. - this.lastDrag = { - x: dragX, - y:dragY - }; - // This move modal with hadware acceleration. - this.containerDiv.style.transform = "translate3d(" + dragX + "," + dragY + ",0)"; - this.containerDiv.style.position = 'absolute'; - } - if (this.resizeDataObject) { - var limitX = Math.min(this.eventClient(ev).X,window.innerWidth - this.scrollbarWidth - 7); - var limitY = Math.min(this.eventClient(ev).Y,window.innerHeight - 7); - if (limitX < 0) { - limitX = 0; + }, { + key: "onFocus", + value: function onFocus() { + // TODO: Check editor avaliable. + if (typeof this.editor !== 'undefined' && this.editor != null) { + this.editor.focus(); + } } - if (limitY < 0) { - limitY = 0; - } + /** + * Mandatory method: modal object calls this method to execute a callback action + * on submit. + * This method updates the edition area (inserting a new formula or update an older one), + * and focus the edition area too. + * @ignore + */ + + }, { + key: "submitAction", + value: function submitAction() { + var mathML = this.editor.getMathML(); + // Add class for custom editors. + if (wrs_int_getCustomEditorEnabled() != null) { + mathML = wrs_mathmlAddEditorAttribute(mathML); + } + var mathmlEntitiesEncoded = wrs_mathmlEntities(mathML); + wrs_int_updateFormula(mathmlEntitiesEncoded, null, _wrs_int_langCode); + wrs_int_disableCustomEditors(); + wrs_int_notifyWindowClosed(); + _wrs_editMode = window._wrs_conf_defaultEditMode ? _wrs_conf_defaultEditMode : 'images'; - var scaleMultiplier; - if (this.leftScale) { - scaleMultiplier = -1; + // Set disabled focus to prevent lost focus. + this.setEmptyMathML(); + wrs_int_disableCustomEditors(); + // Reconvering editing area focus. + setTimeout(function () { + if (typeof _wrs_currentEditor !== 'undefined' && _wrs_currentEditor) { + _wrs_currentEditor.focus(); + } + }, 100); } - else { - scaleMultiplier = 1; + + /** + * Set an empty MathML into the editor in order to clean the edit area. + * @ignore + */ + + }, { + key: "setEmptyMathML", + value: function setEmptyMathML() { + // As second argument we pass + if (this.deviceProperties.isAndroid || this.deviceProperties.isIOS) { + // We need to set a empty annotation in order to maintain editor in Hand mode. + this.setMathML('[]"', true); + } else { + this.setMathML('', true); + } } - this.containerDiv.style.width = this.initialWidth + scaleMultiplier * (limitX - this.resizeDataObject.x) + 'px'; - this.containerDiv.style.height = this.initialHeight + scaleMultiplier * (limitY - this.resizeDataObject.y) + 'px'; - if (!this.leftScale) { - if (this.resizeDataObject.x - limitX - this.initialWidth < -580) { - this.containerDiv.style.right = this.initialRight - (limitX - this.resizeDataObject.x) + 'px'; + + /** + * Mandatory method: modal object calls this method when is updated, for example re-editing a formula when the + * editor is open with another formula. This method updates the editor content (with an empty MathML or an exising formula), + * updates - if needed - the editor toolbar (math --> chem or chem --> math) and recover the focus. + * @param {object} modalObject + * @ignore + */ + + }, { + key: "onOpen", + value: function onOpen(modalObject) { + if (_wrs_isNewElement) { + this.setEmptyMathML(); + this.lastImageWasNew = true; + } else { + this.setMathML(wrs_mathmlDecode(_wrs_temporalImage.getAttribute(_wrs_conf_imageMathmlAttribute))); + this.lastImageWasNew = false; } - else { - this.containerDiv.style.right = this.initialRight + this.initialWidth - 580 + "px"; - this.containerDiv.style.width = "580px"; + this.updateToolbar(modalObject); + this.onFocus(); + } + + /** + * Sets the correct toolbar depending if exist other custom toolbars at the same time (e.g: Chemistry) + * @ignore + */ + + }, { + key: "updateToolbar", + value: function updateToolbar(modalObject) { + this.updateTitle(modalObject); + var customEditor; + if (customEditor = wrs_int_getCustomEditorEnabled()) { + var toolbar = customEditor.toolbar ? customEditor.toolbar : _wrs_int_wirisProperties['toolbar']; + if (this.toolbar == null || this.toolbar != toolbar) { + this.setToolbar(toolbar); + } + } else { + var toolbar = this.getToolbar(); + if (this.toolbar == null || this.toolbar != toolbar) { + this.setToolbar(toolbar); + wrs_int_disableCustomEditors(); + } } - if (this.resizeDataObject.y - limitY < this.initialHeight - 338) { - this.containerDiv.style.bottom = this.initialBottom - (limitY - this.resizeDataObject.y) + 'px'; + } + /** + * Updates the modalObject title: if a custom editor (with a custom toolbar) is enabled + * picks the custom editor title. Otherwise default title. + * @param {object} modalObject + * @ignore + */ + + }, { + key: "updateTitle", + value: function updateTitle(modalObject) { + var customEditor; + if (customEditor = wrs_int_getCustomEditorEnabled()) { + modalObject.setTitle(customEditor.title); + } else { + modalObject.setTitle('MathType'); } - else { - this.containerDiv.style.bottom = this.initialBottom + this.initialHeight - 338 + "px"; - this.containerDiv.style.height = "338px"; + } + /** + * Returns toolbar depending on the configuration local or serverside. + * @ignore + */ + + }, { + key: "getToolbar", + value: function getToolbar() { + var toolbar = typeof _wrs_conf_editorParameters == 'undefined' || typeof _wrs_conf_editorParameters['toolbar'] == 'undefined' ? 'general' : _wrs_conf_editorParameters['toolbar']; + if (toolbar == 'general') { + toolbar = typeof _wrs_int_wirisProperties == 'undefined' || typeof _wrs_int_wirisProperties['toolbar'] == 'undefined' ? 'general' : _wrs_int_wirisProperties['toolbar']; } + return toolbar; } - this.recalculateScale(); - this.recalculatePosition(); - } -} -/** - * Get limits of actual window to limit modal movement - * @return {Object} Object containing mouseX and mouseY are coordinates of actual mouse on screen. - * @ignore - */ -ModalWindow.prototype.getLimitWindow = function() { - // Obtain dimentions of window page. - var maxWidth = window.innerWidth; - var maxHeight = window.innerHeight; - - // Calculate relative position of mouse point into window. - var offSetToolbarY = (this.containerDiv.offsetHeight + parseInt(this.containerDiv.style.bottom)) - (maxHeight - (this.dragDataObject.y - window.pageXOffset)); - var offSetToolbarX = maxWidth - this.scrollbarWidth - (this.dragDataObject.x - window.pageXOffset) - parseInt(this.containerDiv.style.right); - - // Calculate limits with sizes of window, modal and mouse position. - var minPointerY = maxHeight - this.containerDiv.offsetHeight + offSetToolbarY; - var maxPointerY = this.titleDiv.offsetHeight - (this.titleDiv.offsetHeight - offSetToolbarY); - var minPointerX = maxWidth - offSetToolbarX - this.scrollbarWidth; - var maxPointerX = (this.containerDiv.offsetWidth - offSetToolbarX); - var minPointer = {x: minPointerX,y: minPointerY}; - var maxPointer = {x: maxPointerX,y: maxPointerY}; - return {minPointer : minPointer, maxPointer:maxPointer}; -} -/** - * Get Scrollbar width size of browser - * @ignore - */ -ModalWindow.prototype.getScrollBarWidth = function() { - // Create a paragraph with full width of page. - var inner = document.createElement('p'); - inner.style.width = "100%"; - inner.style.height = "200px"; - - // Create a hidden div to compare sizes. - var outer = document.createElement('div'); - outer.style.position = "absolute"; - outer.style.top = "0px"; - outer.style.left = "0px"; - outer.style.visibility = "hidden"; - outer.style.width = "200px"; - outer.style.height = "150px"; - outer.style.overflow = "hidden"; - outer.appendChild(inner); - - document.body.appendChild(outer); - var widthOuter = inner.offsetWidth; - - // Change type overflow of paragraph for measure scrollbar. - outer.style.overflow = 'scroll'; - var widthInner = inner.offsetWidth; - - // If measure is the same, we compare with internal div. - if (widthOuter == widthInner) { - widthInner = outer.clientWidth; - } - document.body.removeChild(outer); - - return (widthOuter - widthInner); -} - -/** - * Set the _wrs_dragDataObject to null when the drag finish (touchend or mouseup events). - * - * @param {event} ev touchend or mouseup event. - * @ignore - */ -ModalWindow.prototype.stopDrag = function(ev) { - // Due to we have multiple events that call this function, we need only to execute the next modifiers one time, - // when the user stops to drag and dragDataObject is not null (the object to drag is attached). - if (this.dragDataObject || this.resizeDataObject) { - // If modal doesn't change, it's not necessary to set position with interpolation - if(this.containerDiv.style.position != 'fixed'){ - this.containerDiv.style.position = 'fixed'; - // Fixed position makes the coords relative to the main window. So that, we need to transform - // the absolute coords to relative. - this.containerDiv.style.transform = ''; - if (this.dragDataObject) { - this.containerDiv.style.right = parseInt(this.containerDiv.style.right) - parseInt(this.lastDrag.x) + pageXOffset + "px"; - this.containerDiv.style.bottom = parseInt(this.containerDiv.style.bottom) - parseInt(this.lastDrag.y) + pageYOffset + "px"; - } - } - // We make focus on editor after drag modal windows to prevent lose focus. - this.focus(); - // Restore mouse events on iframe - this.iframe.style['pointer-events'] = 'auto'; - document.body.style['user-select'] = ''; - // Restore static state of iframe if we use iexplorer - if (this.isIE11()) { - this.iframe.style['position'] = null; - } - // Active text select event - wrs_removeClass(document.body, 'wrs_noselect'); - // Disable overlay for click behind modal - if (_wrs_modalWindow.properties.state != "maximized") { - this.overlayDiv.style.display = "none"; - } - } - this.dragDataObject = null; - this.resizeDataObject = null; - this.initialWidth = null; - this.leftScale = null; -} -/** - * Recalculating scale for modal when resize browser window - * - * @ignore - */ -ModalWindow.prototype.onWindowResize = function() { - this.recalculateScrollBar(); - this.recalculatePosition(); - this.recalculateScale(); -} + /** + * Set a toolbar into editor. + * @param {string} toolbar toolbar name. + * @ignore + */ -/** - * Recalculating position for modal when resize browser window - * - * @ignore - */ -ModalWindow.prototype.recalculatePosition = function() { - this.containerDiv.style.right = Math.min(parseInt(this.containerDiv.style.right), window.innerWidth - this.scrollbarWidth - this.containerDiv.offsetWidth) + "px"; - if(parseInt(this.containerDiv.style.right) < 0) { - this.containerDiv.style.right = "0px"; - } - this.containerDiv.style.bottom = Math.min(parseInt(this.containerDiv.style.bottom), window.innerHeight - this.containerDiv.offsetHeight) + "px"; - if(parseInt(this.containerDiv.style.bottom) < 0) { - this.containerDiv.style.bottom = "0px"; - } -} + }, { + key: "setToolbar", + value: function setToolbar(toolbar) { + this.toolbar = toolbar; + this.editor.setParams({ 'toolbar': this.toolbar }); + } -/** - * Recalculating scale for modal when resize browser window - * - * @ignore - */ -ModalWindow.prototype.recalculateScale = function() { - var sizeModificated = false; - if (parseInt(this.containerDiv.style.width) > 580) { - this.containerDiv.style.width = Math.min(parseInt(this.containerDiv.style.width), window.innerWidth - this.scrollbarWidth) + "px"; - sizeModificated = true; - } - else { - this.containerDiv.style.width = "580px"; - sizeModificated = true; - } - if (parseInt(this.containerDiv.style.height) > 338) { - this.containerDiv.style.height = Math.min(parseInt(this.containerDiv.style.height), window.innerHeight) + "px"; - sizeModificated = true; - } - else { - this.containerDiv.style.height = "338px"; - sizeModificated = true; - } - if (sizeModificated) { - this.applyIframeSize(); - } -} + /** + * Returns true if the content of the editor has been changed. The logic of the changes + * is delegated to editorListener object. + * @ignore + */ -/** - * Recalculating width of scrollBar browser - * - * @ignore - */ -ModalWindow.prototype.recalculateScrollBar = function() { - this.hasScrollBar = window.innerWidth > document.documentElement.clientWidth; - if(this.hasScrollBar){ - this.scrollbarWidth = this.getScrollBarWidth(); - } - else { - this.scrollbarWidth = 0; - } -} + }, { + key: "hasChanges", + value: function hasChanges() { + return !this.editor.isFormulaEmpty() && this.editorListener.getIsContentChanged(); + } + }]); + return contentManager; +}(); /** - * Hide soft keyboards on IOS systems. + * EditorListener class. This class implement EditorListener interface + * and contains the logic to determine if editor has been changed or not. * @ignore */ -ModalWindow.prototype.hideKeyboard = function() { - document.activeElement.blur(); -} -/** - * Returns the origin (i.e protocol + hostname + port) from an url string. - * This method is used to get the iframe window origin to allow postMessages. - * @param {string} url url string - * @return {string} origin string - * @ignore - */ -ModalWindow.prototype.getOriginFromUrl = function(url) { - var parser = document.createElement('a'); - parser.href = url; - return parser.protocol.indexOf('//') == -1 ? parser.protocol + '//' + parser.host : parser.protocol + parser.host; -} - -/** - * Enable safe cross-origin comunication between plugin and MathType services. We can't call directly - * MathType editor methods because the content iframe could be in a different domain. - * We use postMessage method to create a wrapper between modal window and editor. - * - */ +var EditorListener = function () { + function EditorListener() { + _classCallCheck(this, EditorListener); -/** - * Set a MathML into editor. - * @param {string} mathml MathML string. - * @ignore - */ -ModalWindow.prototype.setMathML = function(mathml, focusDisabled) { - _wrs_popupWindow.postMessage({'objectName' : 'editor', 'methodName' : 'setMathML', 'arguments': [mathml]}, this.iframeOrigin); - // Check if focus is not necessary when clean modal on close - if (!focusDisabled){ - this.focus(); + this.isContentChanged = false; + this.waitingForChanges = false; } -} -/** - * Set a MathML into editor and call function in back. - * @param {string} mathml MathML string. - * @ignore - */ -ModalWindow.prototype.setMathMLWithCallback = function(mathml) { - _wrs_popupWindow.postMessage({'objectName' : 'editorCallback', 'arguments': [mathml]}, this.iframeOrigin); - this.focus(); -} -/** - * Set a toolbar into editor. - * @param {string} toolbar toolbar name. - * @ignore - */ -ModalWindow.prototype.setToolbar = function(toolbar) { - this.toolbar = toolbar; - _wrs_popupWindow.postMessage({'objectName' : 'editor', 'methodName' : 'setParams', 'arguments': [{'toolbar' : toolbar}]}, this.iframeOrigin); -} + /** + * EditorListener method set if content is changed + * @ignore + */ -/** - * Set focus on editor. - * @ignore - */ -ModalWindow.prototype.focus = function() { - // Focus on iframe explicit - // We add this focus in iframe beacuse tiny3 have a problem with focus in chrome and it can't focus iframe automaticly - if (navigator.userAgent.search("Chrome/") >= 0 && navigator.userAgent.search('Edge') == -1) { - this.iframe.focus(); - } - _wrs_popupWindow.postMessage({'objectName' : 'editor', 'methodName' : 'focus', 'arguments': null}, this.iframeOrigin); -} + _createClass(EditorListener, [{ + key: "setIsContentChanged", + value: function setIsContentChanged(value) { + this.isContentChanged = value; + } + }, { + key: "getIsContentChanged", + + /** + * EditorListener method to get if content is changed + * @ignore + */ + value: function getIsContentChanged(value) { + return this.isContentChanged; + } + }, { + key: "setWaitingForChanges", + + /** + * EditorListener method to wait changes + * @ignore + */ + value: function setWaitingForChanges(value) { + this.waitingForChanges = value; + } + }, { + key: "caretPositionChanged", + + /** + * EditorListener method to overwrite + * @ignore + */ + value: function caretPositionChanged(editor) {} + }, { + key: "clipboardChanged", + + /** + * EditorListener method to overwrite + * @ignore + */ + value: function clipboardChanged(editor) {} + }, { + key: "contentChanged", + + /** + * EditorListener method to set if content is changed + * @ignore + */ + value: function contentChanged(editor) { + if (this.waitingForChanges === true && this.isContentChanged === false) { + this.isContentChanged = true; + } + } + /** + * EditorListener method to overwrite + * @ignore + */ -/** - * Fires editor event - * @param {string} eventName event name - * @ignore - */ -ModalWindow.prototype.fireEditorEvent = function(eventName) { - _wrs_popupWindow.postMessage({'objectName' : 'editorEvent', 'eventName' : eventName, 'arguments': null}, this.iframeOrigin); -} + }, { + key: "styleChanged", + value: function styleChanged(editor) {} + }, { + key: "transformationReceived", -/** - * Returns true when the device is on portrait mode. - * @ignore - */ -ModalWindow.prototype.portraitMode = function () { - return window.innerHeight > window.innerWidth; -} + /** + * EditorListener method to overwrite + * @ignore + */ + value: function transformationReceived(editor) {} + }]); + return EditorListener; +}(); // TODO: Methods to static? /** - * Change container sizes when the keyboard is opened on iOS. + * StringManager class to use strings in code correctly with control. * @ignore */ -ModalWindow.prototype.openedIosSoftkeyboard = function () { - if (!this.iosSoftkeyboardOpened && this.iosDivHeight != null &&this.iosDivHeight == "100" + this.iosMeasureUnit) { - if (this.portraitMode()) { - this.setIframeContainerHeight("63" + this.iosMeasureUnit); - } - else { - this.setIframeContainerHeight("40" + this.iosMeasureUnit); - } - } - this.iosSoftkeyboardOpened = true; -} -/** - * Change container sizes when the keyboard is closed on iOS. - * @ignore - */ -ModalWindow.prototype.closedIosSoftkeyboard = function () { - this.iosSoftkeyboardOpened = false; - this.setIframeContainerHeight("100" + this.iosMeasureUnit); -} -/** - * Change container sizes when orientation is changed on iOS. - * @ignore - */ -ModalWindow.prototype.orientationChangeIosSoftkeyboard = function () { - if (this.iosSoftkeyboardOpened) { - if (this.portraitMode()) { - this.setIframeContainerHeight("63" + this.iosMeasureUnit); - } - else { - this.setIframeContainerHeight("40" + this.iosMeasureUnit); - } - } - else { - this.setIframeContainerHeight("100" + this.iosMeasureUnit); +var StringManager = function () { + function StringManager() { + _classCallCheck(this, StringManager); + + // Strings are empty when it creates, it set when calls load method. + this.strings = null; + this.stringsLoaded = false; } -} -/** - * Change container sizes when orientation is changed on Android. - * @ignore - */ -ModalWindow.prototype.orientationChangeAndroidSoftkeyboard = function () { - this.setIframeContainerHeight("100%"); -} + /** + * This method return a string passing a key. + * @param {string} key of array strings that you want. + * @return string A text that you want or key if it doesn't exist. + * @ignore + */ -/** - * Set iframe container height. - * @ignore - */ -ModalWindow.prototype.setIframeContainerHeight = function (height) { - this.iosDivHeight = height; - _wrs_modalWindow.iframeContainer.style.height = height; - _wrs_popupWindow.postMessage({'objectName' : 'editorResize', 'arguments': [_wrs_modalWindow.iframeContainer.offsetHeight - 10]}, this.iframeOrigin); -} -// PopUpMessageClass definition -// This class generate a modal message to show information to user -function PopUpMessage(popupAttributes) -{ - this.overlayEnvolture = document.getElementsByClassName('wrs_modal_iframeContainer')[0].appendChild(document.createElement("DIV")); - this.overlayEnvolture.setAttribute("class", "wrs_popupmessage_overlay_envolture"); - - this.message = this.overlayEnvolture.appendChild(document.createElement("div")); - this.message.id = "wrs_popupmessage" - this.message.setAttribute("class", "wrs_popupmessage_panel"); - this.message.innerHTML = popupAttributes.message; - - var overlay = this.overlayEnvolture.appendChild(document.createElement("div")); - overlay.setAttribute("class", "wrs_popupmessage_overlay"); - // We create a overlay that close popup message on click in there - overlay.addEventListener("click", this.close.bind(this)); - - this.buttonArea = this.message.appendChild(document.createElement('div')); - this.buttonArea.setAttribute("class", "wrs_popupmessage_button_area"); - this.buttonArea.id = 'wrs_popup_button_area'; - - // Buttons creation - buttonSubmitArguments = { - class: "wrs_button_accept", - innerHTML: popupAttributes.submitString, - id: 'wrs_popup_accept_button' - }; - this.acceptButton = this.createButton(buttonSubmitArguments, this.closeModal.bind(this)); - this.buttonArea.appendChild(this.acceptButton); - buttonCancelArguments = { - class: "wrs_button_cancel", - innerHTML: popupAttributes.cancelString, - id: 'wrs_popup_cancel_button' - }; - this.cancelButton = this.createButton(buttonCancelArguments, this.close.bind(this)); - this.buttonArea.appendChild(this.cancelButton); - // By default, popupwindow give close modal message with close and cancel buttons - // You can set other message with other buttons - document.addEventListener('keydown',function(e) { - if (e.key !== undefined && e.repeat === false) { - if (e.key == "Escape" || e.key === 'Esc') { - _wrs_popupWindow.postMessage({'objectName' : 'checkCloseCondition'}, _wrs_modalWindow.iframeOrigin); + _createClass(StringManager, [{ + key: "getString", + value: function getString(key) { + // Wait 200ms and recall this method if strings aren't load. + if (!this.stringsLoaded) { + setTimeout(this.getString.bind(this, key), 100); + return; + } + if (key in this.strings) { + return this.strings[key]; + } + return key; + } + /** + * This method load all strings to the manager and unset it for prevent bad usage. + * @param {array} String array of language + * @ignore + */ + + }, { + key: "loadStrings", + value: function loadStrings(langStrings) { + if (!this.stringsLoaded) { + this.strings = langStrings; + // Activate variable to unlock getStrings + this.stringsLoaded = true; } } - }); -} -// This method create a button with arguments and return button dom object -PopUpMessage.prototype.createButton = function(parameters, callback) { - function popUpButton(parameters) { - this.element = document.createElement("button"); - this.element.setAttribute("id", parameters.id); - this.element.setAttribute("class", parameters.class); - this.element.innerHTML = parameters.innerHTML; - this.element.addEventListener("click", callback); - } + }]); - popUpButton.prototype.getElement = function() { - return this.element; - } + return StringManager; +}(); - return new popUpButton(parameters).getElement(); -} -// This method show popup message. -PopUpMessage.prototype.show = function(){ - if (this.overlayEnvolture.style.display != 'block') { - // Clear focus with blur for prevent press anykey - document.activeElement.blur(); - _wrs_popupWindow.postMessage({'objectName' : 'blur'}, _wrs_modalWindow.iframeOrigin); - // For works with Safari - window.focus(); - this.overlayEnvolture.style.display = 'block'; - } - else { - this.overlayEnvolture.style.display = 'none'; - _wrs_modalWindow.focus(); - } -} -// This method hide popup message -PopUpMessage.prototype.close = function(){ - this.overlayEnvolture.style.display = 'none'; - _wrs_modalWindow.focus(); -} -// This method close modal and close popupmessage -PopUpMessage.prototype.closeModal = function(){ - this.close(); - wrs_closeModalWindow(); -} var _wrs_conf_core_loaded = true; diff --git a/core/core.src.js b/core/core.src.js new file mode 100644 index 00000000..56e857a1 --- /dev/null +++ b/core/core.src.js @@ -0,0 +1,5915 @@ +var _wrs_popupWindow; +/** + * Global variables (strict mode) + */ +var _wrs_conf_createimagePath; +var _wrs_conf_showimagePath; +var _wrs_conf_editorPath; +var _wrs_conf_CASPath; +var _wrs_conf_createcasimagePath; +var _wrs_conf_getmathmlPath; +var _wrs_conf_servicePath; +var _wrs_conf_path; +var _wrs_conf_editor; + +var _wrs_temporalRange; + +function wrs_getCorePath() { + var scriptName = "core/core.js"; + var col = document.getElementsByTagName("script"); + for (var i = 0; i < col.length; i++) { + var d; + var src; + d = col[i]; + src = d.src; + var j = src.lastIndexOf(scriptName); + if (j >= 0) { + // That's my script! + return src.substr(0, j - 1); + } + } +} + +var _wrs_corePath = wrs_getCorePath(); + +/** + * Fires an element event. + * @param {object} element element where event should be fired. + * @param {string} event event to fire. + * @ignore + */ +function wrs_fireEvent(element, event) { + if (document.createEvent){ + var eventObject = document.createEvent('HTMLEvents'); + eventObject.initEvent(event, true, true); + return !element.dispatchEvent(eventObject); + } + + var eventObject = document.createEventObject(); + return element.fireEvent('on' + event, eventObject) +} + + +// Translated languages. +var _wrs_languages = 'ar,ca,cs,da,de,en,es,et,eu,fi,fr,gl,he,hr,hu,it,ja,ko,nl,no,pl,pt,pt_br,ru,sv,tr,zh,el'; + +// Load lang file at first to replace some variables +wrs_loadLangFile() + +// Vars. +var _wrs_stringManager; +var _wrs_currentPath = window.location.toString().substr(0, window.location.toString().lastIndexOf('/') + 1); +var _wrs_editMode = typeof _wrs_editMode != 'undefined' ? _wrs_editMode : undefined; +var _wrs_isNewElement = typeof _wrs_isNewElement !== 'undefined' ? _wrs_isNewElement : true; +var _wrs_temporalImage; +var _wrs_temporalFocusElement; +var _wrs_range; +// TODO: String Manager not loaded. +function loadStringLatex() { + if(_wrs_conf_core_loaded) { + var _wrs_latex_formula_name = _wrs_stringManager.getString('latex_name_label'); + } else { + setTimeout(loadStringLatex,100); + } +} +loadStringLatex(); + +var _wrs_latex_formula_number = 1; + +// Tags used for LaTeX formulas. +var _wrs_latexTags = { + 'open': '$$', + 'close': '$$' +}; +// LaTex client cache. +var _wrs_int_LatexCache = {}; +// Cache for all the mathmls withouts translation to LaTex. +var _wrs_int_nonLatexCache = {}; + +// Accessible client cache. +var _wrs_int_AccessibleCache = {}; + +var _wrs_xmlCharacters = { + 'tagOpener': '<', // Hex: \x3C. + 'tagCloser': '>', // Hex: \x3E. + 'doubleQuote': '"', // Hex: \x22. + 'ampersand': '&', // Hex: \x26. + 'quote': '\'' // Hex: \x27. +}; + +var _wrs_safeXmlCharacters = { + 'tagOpener': '«', // Hex: \xAB. + 'tagCloser': '»', // Hex: \xBB. + 'doubleQuote': '¨', // Hex: \xA8. + 'ampersand': '§', // Hex: \xA7. + 'quote': '`', // Hex: \x60. + 'realDoubleQuote': '¨' +}; + +var _wrs_safeXmlCharactersEntities = { + 'tagOpener': '«', + 'tagCloser': '»', + 'doubleQuote': '¨', + 'realDoubleQuote': '"' +} + +var _wrs_safeBadBlackboardCharacters = { + 'ltElement': '«mo»<«/mo»', + 'gtElement': '«mo»>«/mo»', + 'ampElement': '«mo»&«/mo»' +} + +var _wrs_safeGoodBlackboardCharacters = { + 'ltElement': '«mo»§lt;«/mo»', + 'gtElement': '«mo»§gt;«/mo»', + 'ampElement': '«mo»§amp;«/mo»' +} + +var _wrs_staticNodeLengths = { + 'IMG': 1, + 'BR': 1 +} + +// Backwards compatibily. + +if (!(window._wrs_conf_imageClassName)) { + var _wrs_conf_imageClassName = 'Wirisformula'; +} + +if (!(window._wrs_conf_CASClassName)) { + var _wrs_conf_CASClassName = 'Wiriscas'; +} + +// Mutation observers to avoid wiris image formulas class be removed. +if (typeof MutationObserver != 'undefined') { + var wrs_observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.oldValue == _wrs_conf_imageClassName && mutation.attributeName == 'class' && mutation.target.className.indexOf(_wrs_conf_imageClassName) == -1 ) { + mutation.target.className = _wrs_conf_imageClassName; + } + }); + }); + + var wrs_observer_config = { attributes: true, attributeOldValue:true }; +} + +// Plugin listeners for custom callbacks. This variable +// can be setted by the user using wrs_addPluginListener. +var wrs_pluginListeners = []; + +var _wrs_css_loaded = false; + +var _wrs_modalWindowProperties = typeof _wrs_modalWindowProperties != 'undefined' ? _wrs_modalWindowProperties : {}; +var _wrs_editor = typeof _wrs_editor != 'undefined' ? _wrs_editor : null; +var _wrs_modalWindow = typeof _wrs_modalWindow != 'undefined' ? _wrs_modalWindow : null; + +// If true all MathML should be parse despite of save mode. +var _wrs_parseXml = true; + +/** + * Adds element events. + * @param {object} target Target + * @param {function} doubleClickHandler Function to run when user double clicks the element + * @param {function} mousedownHandler Function to run when user mousedowns the element + * @param {function} mouseupHandler Function to run when user mouseups the element + * @ignore + */ +function wrs_addElementEvents(target, doubleClickHandler, mousedownHandler, mouseupHandler) { + if (doubleClickHandler) { + wrs_addEvent(target, 'dblclick', function (event) { + var realEvent = (event) ? event : window.event; + var element = realEvent.srcElement ? realEvent.srcElement : realEvent.target; + doubleClickHandler(target, element, realEvent); + }); + } + + if (mousedownHandler) { + wrs_addEvent(target, 'mousedown', function (event) { + var realEvent = (event) ? event : window.event; + var element = realEvent.srcElement ? realEvent.srcElement : realEvent.target; + _wrs_temporalFocusElement = element; + mousedownHandler(target, element, realEvent); + }); + } + + if (mouseupHandler) { + wrs_addEvent(target, 'mouseup', function (event) { + var realEvent = (event) ? event : window.event; + var element = realEvent.srcElement ? realEvent.srcElement : realEvent.target; + mouseupHandler(target, element, realEvent); + }); + } +} + +/** + * Cross-browser addEventListener/attachEvent function. + * @param {object} element Element target + * @param {event} event Event + * @param {function} func Function to run + * @ignore + */ +function wrs_addEvent(element, event, func) { + if (element.addEventListener) { + element.addEventListener(event, func, true); + } + else if (element.attachEvent) { + element.attachEvent('on' + event, func); + } +} + +/** + * Adds iframe events. + * @param {object} iframe Target + * @param {function} doubleClickHandler Function to run when user double clicks the iframe + * @param {function} mousedownHandler Function to run when user mousedowns the iframe + * @param {function} mouseupHandler Function to run when user mouseups the iframe + * @ignore + */ +function wrs_addIframeEvents(iframe, doubleClickHandler, mousedownHandler, mouseupHandler) { + wrs_initSetSize(); + wrs_addElementEvents(iframe.contentWindow.document, + function (target, element, event) { + doubleClickHandler(iframe, element, event); + }, + function (target, element, event) { + mousedownHandler(iframe, element, event); + }, + function (target, element, event) { + mouseupHandler(iframe, element, event); + } + ); +} + +/** + * Adds textarea events. + * @param {object} textarea Target + * @param {function} clickHandler Function to run when user clicks the textarea. + * @ignore + */ +function wrs_addTextareaEvents(textarea, clickHandler) { + if (clickHandler) { + wrs_addEvent(textarea, 'click', function (event) { + var realEvent = (event) ? event : window.event; + clickHandler(textarea, realEvent); + }); + } +} + +/** + * Converts applet code to img object. + * @param {object} creator Object with "createElement" method + * @param {string} appletCode Applet code + * @param {string} image Base 64 image stream + * @param {int} imageWidth Image width + * @param {int} imageHeight Image height + * @return object img object. + * @ignore + */ +function wrs_appletCodeToImgObject(creator, appletCode, image, imageWidth, imageHeight) { + var imageSrc = wrs_createImageCASSrc(image); + var imgObject = creator.createElement('img'); + + imgObject.src = imageSrc; + imgObject.align = 'middle'; + imgObject.width = imageWidth; + imgObject.height = imageHeight; + imgObject.setAttribute(_wrs_conf_CASMathmlAttribute, wrs_mathmlEncode(appletCode)); + imgObject.className = _wrs_conf_CASClassName; + + return imgObject; +} + +/** + * Checks if a determined array contains a determined element. + * @param {array} stack + * @param {object} element + * @return bool + * @ignore + */ +function wrs_arrayContains(stack, element) { + for (var i = stack.length - 1; i >= 0; --i) { + if (stack[i] === element) { + return i; + } + } + + return -1; +} + +/** + * Adds a specific className to given element + * @param {object} element + * @param {string} className + * @ignore + */ +function wrs_addClass(element, className) { + if (!wrs_containsClass(element, className)) { + element.className += " " + className; + } +} + +/** + * Checks if an element contains a class. + * @param {object} element + * @param {string} className + * @return bool + * @ignore + */ +function wrs_containsClass(element, className) { + if (element == null || !('className' in element)) { + return false; + } + + var currentClasses = element.className.split(' '); + + for (var i = currentClasses.length - 1; i >= 0; --i) { + if (currentClasses[i] == className) { + return true; + } + } + + return false; +} + +/** + * Remove a specific class + * @param {object} element + * @param {string} className + * @ignore + */ +function wrs_removeClass(element, className) { + var newClassName = ''; + var classes = element.className.split(" "); + + for (var i = 0; i < classes.length; i++) { + if(classes[i] != className) { + newClassName += classes[i] + " "; + } + } + element.className = newClassName.trim(); +} + +/** + * Converts old xmlinitialtext attribute (with «») to the correct one(with §lt;§gt;) + * @param {string} text String containtg safeXml characters + * @return {string} String with the safeXml charaters parsed. + * @ignore + */ +function wrs_convertOldXmlinitialtextAttribute(text){ + // Used to fix a bug with Cas imported from Moodle 1.9 to Moodle 2.x. + // This could be removed in future. + var val = 'value='; + + var xitpos = text.indexOf('xmlinitialtext'); + var valpos = text.indexOf(val, xitpos); + var quote = text.charAt(valpos + val.length); + var startquote = valpos + val.length + 1; + var endquote = text.indexOf(quote, startquote); + + var value = text.substring(startquote, endquote); + + var newvalue = value.split('«').join('§lt;'); + newvalue = newvalue.split('»').join('§gt;'); + newvalue = newvalue.split('&').join('§'); + newvalue = newvalue.split('¨').join('§quot;'); + + text = text.split(value).join(newvalue); + return text; +} + +/** + * Cross-browser solution for creating new elements. + * + * It fixes some browser bugs. + * + * @param {string} elementName The tag name of the wished element. + * @param {object} attributes An object where each key is a wished attribute name and each value is its value. + * @param {object} creator Optional param. If supplied, this function will use the "createElement" method from this param. Else, "document" will be used. + * @return {object} The DOM element with the specified attributes assignated. + * @ignore + */ +function wrs_createElement(elementName, attributes, creator) { + if (attributes === undefined) { + attributes = {}; + } + + if (creator === undefined) { + creator = document; + } + + var element; + + /* + * Internet Explorer fix: + * If you create a new object dynamically, you can't set a non-standard attribute. + * For example, you can't set the "src" attribute on an "applet" object. + * Other browsers will throw an exception and will run the standard code. + */ + + try { + var html = '<' + elementName; + + for (var attributeName in attributes) { + html += ' ' + attributeName + '="' + wrs_htmlentities(attributes[attributeName]) + '"'; + } + + html += '>'; + element = creator.createElement(html); + } + catch (e) { + element = creator.createElement(elementName); + + for (var attributeName in attributes) { + element.setAttribute(attributeName, attributes[attributeName]); + } + } + + return element; +} + +/** + * Cross-browser httpRequest creation. + * @return {object} httpRequest request object. + * @ignore + */ +function wrs_createHttpRequest() { + if (_wrs_currentPath.substr(0, 7) == 'file://') { + throw _wrs_stringManager.getString('exception_cross_site'); + } + + if (typeof XMLHttpRequest != 'undefined') { + return new XMLHttpRequest(); + } + + try { + return new ActiveXObject('Msxml2.XMLHTTP'); + } + catch (e) { + try { + return new ActiveXObject('Microsoft.XMLHTTP'); + } + catch (oc) { + } + } + + return false; +} + +/** + * Gets CAS image src with AJAX. + * @param {string} image Base 64 image stream + * @return {string} CAS image src. + * @ignore + */ +function wrs_createImageCASSrc(image, appletCode) { + var data = { + 'image': image, + 'mml': appletCode + }; + + return wrs_getContent(_wrs_conf_createcasimagePath, data); +} + +/** + * Gets formula image src with AJAX. + * @param {mathml} Mathml code. + * @param {object} data wiris properties object. + * @return string Image src. + * @ignore + */ +function wrs_createImageSrc(mathml, data) { + // Full base64 method (edit & save). + if (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default') { + data['base64'] = true; + } + + var result = wrs_getContent(_wrs_conf_createimagePath, data); + + if (result.indexOf('@BASE@') != -1) { + // Replacing '@BASE@' with the base URL of createimage. + var baseParts = _wrs_conf_createimagePath.split('/'); + baseParts.pop(); + result = result.split('@BASE@').join(baseParts.join('/')); + } + + return result; +} + +function wrs_createShowImageSrc(mathml, data, language) { + var dataMd5 = [] + var renderParams = 'mml,color,centerbaseline,zoom,dpi,fontSize,fontFamily,defaultStretchy,backgroundColor,format'; + var renderParamsArray = renderParams.split(','); + for (var key in renderParamsArray) { + var param = renderParamsArray[key]; + if (typeof data[param] != 'undefined') { + dataMd5[param] = data[param]; + } + } + // Data variables to get. + var dataObject = {}; + for (var key in data) { + // We don't need mathml in this request we try to get cached so we only need the formula md5 calculated before. + if (key != 'mml') { + dataObject[key] = data[key]; + } + } + dataObject.formula = com.wiris.js.JsPluginTools.md5encode(wrs_propertiesToString(dataMd5)); + dataObject.lang = (typeof language == 'undefined') ? 'en' : language; + dataObject.version = _wrs_conf_version; + + var result = wrs_getContent(_wrs_conf_showimagePath + '?' + wrs_httpBuildQuery(dataObject)); + return result; +} + +/** + * Creates new object using its html code. + * @param {string} objectCode html code + * @return {object} html object. + * @ignore + */ +function wrs_createObject(objectCode, creator) { + if (creator === undefined) { + creator = document; + } + + // Internet Explorer can't include "param" tag when is setting an innerHTML property. + objectCode = objectCode.split('').join('').split('').join(''); + + objectCode = objectCode.split('').join('
').split('').join('
'); + + var container = wrs_createElement('div', {}, creator); + container.innerHTML = objectCode; + + function recursiveParamsFix(object) { + if (object.getAttribute && object.getAttribute('wirisObject') == 'WirisParam') { + var attributesParsed = {}; + + for (var i = 0; i < object.attributes.length; ++i) { + if (object.attributes[i].nodeValue !== null) { + attributesParsed[object.attributes[i].nodeName] = object.attributes[i].nodeValue; + } + } + + var param = wrs_createElement('param', attributesParsed, creator); + + // IE fix. + if (param.NAME) { + param.name = param.NAME; + param.value = param.VALUE; + } + + param.removeAttribute('wirisObject'); + object.parentNode.replaceChild(param, object); + } + else if (object.getAttribute && object.getAttribute('wirisObject') == 'WirisApplet') { + var attributesParsed = {}; + + for (var i = 0; i < object.attributes.length; ++i) { + if (object.attributes[i].nodeValue !== null) { + attributesParsed[object.attributes[i].nodeName] = object.attributes[i].nodeValue; + } + } + + var applet = wrs_createElement('applet', attributesParsed, creator); + applet.removeAttribute('wirisObject'); + + for (var i = 0; i < object.childNodes.length; ++i) { + recursiveParamsFix(object.childNodes[i]); + + if (object.childNodes[i].nodeName.toLowerCase() == 'param') { + applet.appendChild(object.childNodes[i]); + --i; // When we insert the object child into the applet, object loses one child. + } + } + + object.parentNode.replaceChild(applet, object); + } + else { + for (var i = 0; i < object.childNodes.length; ++i) { + recursiveParamsFix(object.childNodes[i]); + } + } + } + + recursiveParamsFix(container); + return container.firstChild; +} + +/** + * Converts an object to its HTML code. + * @param {object} object DOM object.. + * @return {string} HTML code. + * @ignore + */ +function wrs_createObjectCode(object) { + + // In case that the image was not created, the object can be null or undefined. + if (typeof object == 'undefined' || object == null) { + return; + } + + if (object.nodeType == 1) { // ELEMENT_NODE. + var output = '<' + object.tagName; + + for (var i = 0; i < object.attributes.length; ++i) { + if (object.attributes[i].specified) { + output += ' ' + object.attributes[i].name + '="' + wrs_htmlentities(object.attributes[i].value) + '"'; + } + } + + if (object.childNodes.length > 0) { + output += '>'; + + for (var i = 0; i < object.childNodes.length; ++i) { + output += wrs_createObjectCode(object.childNodes[i]); + } + + output += ''; + } + else if (object.nodeName == 'DIV' || object.nodeName == 'SCRIPT') { + output += '>'; + } + else { + output += '/>'; + } + + return output; + } + + if (object.nodeType == 3) { // TEXT_NODE. + return wrs_htmlentities(object.nodeValue); + } + + return ''; +} + +/** + * Parses end HTML code. The end HTML code is HTML code with embedded images or LaTeX formulas created with MathType.
+ * By default this method converts the formula images and LaTeX strings in MathML.
+ * If image mode is enabled the images will not be converted into MathML. For further information see {@link http://www.wiris.com/plugins/docs/full-mathml-mode}. + * @param {string} code String to be parsed. + * @param {object} wirisProperties Extra attributes for the formula. + * @param {string} language Language for the formula. + * @return {string} + */ +function wrs_endParse(code, wirisProperties, language) { + code = wrs_endParseEditMode(code, wirisProperties, language); + return wrs_endParseSaveMode(code); +} + +function wrs_regexpIndexOf(input, regexp, start) { + var index = input.substring(start || 0).search(regexp); + return (index >= 0) ? (index + (start || 0)) : index; +} + +/** + * Parses end HTML code depending on the edit mode. + * @param {string} code HTML code to be parsed. + * @param {object} wirisProperties Extra formula attributes. + * @param {string} language Language for the formula. + * @return {string} + * @ignore + */ +function wrs_endParseEditMode(code, wirisProperties, language) { + // Converting LaTeX to images. + + if (window._wrs_conf_parseModes !== undefined && wrs_arrayContains(_wrs_conf_parseModes, 'latex') != -1) { + var output = ''; + var endPosition = 0; + var startPosition = code.indexOf('$$'); + while (startPosition != -1) { + output += code.substring(endPosition, startPosition); + endPosition = code.indexOf('$$', startPosition + 2); + + if (endPosition != -1) { + // Before, it was a condition here to execute the next codelines 'latex.indexOf('<') == -1'. + // We don't know why it was used, but seems to have a conflict with latex formulas that contains '<'. + var latex = code.substring(startPosition + 2, endPosition); + latex = wrs_htmlentitiesDecode(latex); + var mathml = wrs_getMathMLFromLatex(latex, true); + output += mathml; + endPosition += 2; + } + else { + output += '$$'; + endPosition = startPosition + 2; + } + + startPosition = code.indexOf('$$', endPosition); + } + + output += code.substring(endPosition, code.length); + code = output; + } + + if (window._wrs_conf_defaultEditMode && _wrs_conf_defaultEditMode == 'iframes') { + // Converting iframes to images. + var output = ''; + var pattern = ' class="' + _wrs_conf_imageClassName + '"'; + var formulaPosition = code.indexOf(pattern); + var endPosition = 0; + + while (formulaPosition != -1) { + // Looking for the actual startPosition. + startPosition = formulaPosition; + var i = formulaPosition; + var startTagFound = false; + + while (i >= 0 && !startTagFound) { // Going backwards until the start tag '<' is found. + var character = code.charAt(i); + + if (character == '"' || character == '\'') { + var characterNextPosition = code.lastIndexOf(character, i); + i = (characterNextPosition == -1) ? -1 : characterNextPosition; + } + else if (character == '<') { + startPosition = i; + startTagFound = true; + } + else if (character == '>') { + i = -1; // Break: we are inside a text node. + } + + --i; + } + + // Appending the previous code. + output += code.substring(endPosition, startPosition); + + // Looking for the endPosition. + + if (startTagFound) { + i = formulaPosition; + var counter = 1; + + while (i < code.length && counter > 0) { + var character = code.charAt(i); + + if (character == '"' || character == '\'') { + var characterNextPosition = code.indexOf(character, i); + i = (characterNextPosition == -1) ? code.length : characterNextPosition; + } + else if (character == '<') { + if (i + 1 < code.length && code.charAt(i + 1) == '/') { + --counter; + + if (counter == 0) { + endPosition = code.indexOf('>', i) + 1; + + if (endPosition == -1) { + // End tag stripped. + counter = -1; // to be != 0 and to break the loop. + } + } + } + else { + ++counter; + } + } + else if (character == '>' && code.charAt(i - 1) == '/') { + --counter; + + if (counter == 0) { + endPosition = i + 1; + } + } + + ++i; + } + + if (counter == 0) { + var formulaTagCode = code.substring(startPosition, endPosition); + var formulaTagObject = wrs_createObject(formulaTagCode); + var mathml = formulaTagObject.getAttribute(_wrs_conf_imageMathmlAttribute); + + if (mathml == null) { + mathml = formulaTagObject.getAttribute('alt'); + } + + var imgObject = wrs_mathmlToImgObject(document, mathml, wirisProperties, language); + output += wrs_createObjectCode(imgObject); + } + else { + // Start tag found but no end tag found. No process is done. A character is appended to avoid infinite loop in the next search. + output += code.charAt(formulaPosition); + endPosition = formulaPosition + 1; + } + } + else { + // No start tag is found. No process is done. A character is appended to avoid infinite loop in the next search. + output += code.charAt(formulaPosition); + endPosition = formulaPosition + 1; + } + + formulaPosition = code.indexOf(pattern, endPosition); + } + + output += code.substring(endPosition, code.length); + code = output; + } + + return code; +} + +/** + * Parses end HTML code depending on the save mode. + * @param {string} code HTML code + * @return {string} + * @ignore + */ +function wrs_endParseSaveMode(code) { + var output = ''; + var convertToXml = false; + var convertToSafeXml = false; + + if (window._wrs_conf_saveMode) { + if (_wrs_conf_saveMode == 'safeXml') { + convertToXml = true; + convertToSafeXml = true; + code = wrs_codeImgTransform(code, 'img2mathml'); + } + else if (_wrs_conf_saveMode == 'xml') { + convertToXml = true; + code = wrs_codeImgTransform(code, 'img2mathml'); + } + else if (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'image') { + code = wrs_codeImgTransform(code, 'img264'); + } + } + + return code; +} + +/** + * Gets the formula mathml or CAS appletCode using its image hash code. + * @param {string} variableName Variable to send on POST query to the server. + * @param {string} imageHashCode image hash code. + * @return {string} Corresponding mathml code. + * @ignore + */ +function wrs_getCode(variableName, imageHashCode) { + var data = {}; + data[variableName] = imageHashCode; + return wrs_getContent(_wrs_conf_getmathmlPath, data); +} + +/** + * Gets the content from an URL. + * @param {string} url target URL. + * @param {object} postVariables post variables. Null if a GET query should be done. + * @return {string} content of the target URL. + * @ignore + */ +function wrs_getContent(url, postVariables) { + try { + var httpRequest = wrs_createHttpRequest(); + + if (httpRequest) { + if (typeof postVariables === undefined || typeof postVariables == 'undefined') { + httpRequest.open('GET', url, false); + } + else if (url.substr(0, 1) == '/' || url.substr(0, 7) == 'http://' || url.substr(0, 8) == 'https://') { + httpRequest.open('POST', url, false); + } + else { + httpRequest.open('POST', _wrs_currentPath + url, false); + } + + if (postVariables !== undefined) { + httpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8'); + httpRequest.send(wrs_httpBuildQuery(postVariables)); + } + else { + httpRequest.send(null); + } + + return httpRequest.responseText; + } + + alert(_wrs_stringManager.getString('browser_no_compatible')); + } + catch (e) { + } + + return ''; +} + +/** + * Generates the innerHTML of an element. + * @param {object} element target element. + * @return {string} innertHTML of the target element. + * @ignore + */ +function wrs_getInnerHTML(element) { + var innerHTML = ''; + + for (var i = 0; i < element.childNodes.length; ++i) { + innerHTML += wrs_createObjectCode(element.childNodes[i]); + } + + return innerHTML; +} + +/** + * Converts MathML to LaTeX. + * @param {string} mathml MathML String + * @return {string} MathML corresponding LaTeX. + * @ignore + */ +function wrs_getLatexFromMathML(mathml) { + var data = { + 'service': 'mathml2latex', + 'mml': mathml + }; + + var jsonResponse = JSON.parse(wrs_getContent(_wrs_conf_servicePath, data)); + + var latex; + + if (jsonResponse.status == "ok") { + latex = jsonResponse.result.text; + } + + return latex; +} + +/** + * Extracts the latex of a determined position in a text. + * @param {string} textNode test to extract LaTeX + * @param {int} caretPosition starting position to find LaTeX. + * @param {object} latexTags optional parameter representing tags between latex is inserted. It has the 'open' attribute for the open tag and the 'close' attribute for the close tag. + * @return {object} An object with 3 keys: 'latex', 'start' and 'end'. Null if latex is not found. + * @ignore + */ +function wrs_getLatexFromTextNode(textNode, caretPosition, latexTags) { + // latexTags is an optional parameter. If is not set, use default latexTags. + if (typeof latexTags == 'undefined' || latexTags == null) { + latexTags = _wrs_latexTags; + } + // Looking for the first textNode. + var startNode = textNode; + + while (startNode.previousSibling && startNode.previousSibling.nodeType == 3) { // TEXT_NODE. + startNode = startNode.previousSibling; + } + + // Finding latex. + + /** + * It gets the next latex position and node from a specific node and position. + * @param {Object} currentNode node where searching latex. + * @param {number} currentPosition current position inside the currentNode. + * @param {Object} latexTagsToUse tags used at latex beggining and latex final. + * @param {boolean} searchEndTag If true, the first tag to search is an end tag. Otherwise, it searches the open tag first. + */ + function getNextLatexPosition(currentNode, currentPosition, latexTagsToUse, searchEndTag) { + var latexTags = latexTagsToUse; + if (searchEndTag) { + latexTags = { + 'open': latexTagsToUse.close, + 'close': latexTagsToUse.open + }; + } + + var position = currentNode.nodeValue.indexOf(latexTags.open, currentPosition); + + while (position == -1) { + currentNode = currentNode.nextSibling; + + if (!currentNode || currentNode.nodeType != 3) { // TEXT_NODE. + return null; // Not found. + } + + position = currentNode.nodeValue.indexOf(latexTags.close); + } + + return { + 'node': currentNode, + 'position': position + }; + } + + function isPrevious(node, position, endNode, endPosition) { + if (node == endNode) { + return (position <= endPosition); + } + + while (node && node != endNode) { + node = node.nextSibling; + } + + return (node == endNode); + } + + var start; + var end = { + 'node': startNode, + 'position': 0 + }; + var searchEndTag = false; + // Is supposed that open and close tags has the same length. + var tagLength = latexTags.open.length; + do { + var start = getNextLatexPosition(end.node, end.position, latexTags, searchEndTag); + + if (start == null || isPrevious(textNode, caretPosition, start.node, start.position)) { + return null; + } + + var end = getNextLatexPosition(start.node, start.position + tagLength, latexTags, !searchEndTag); + + if (end == null) { + return null; + } + + end.position += tagLength; + // For the next iteration, the start position to search corresponds to the opposite tag. + searchEndTag = !searchEndTag; + } while (isPrevious(end.node, end.position, textNode, caretPosition)); + + // Isolating latex. + var latex; + + if (start.node == end.node) { + latex = start.node.nodeValue.substring(start.position + tagLength, end.position - tagLength); + } + else { + latex = start.node.nodeValue.substring(start.position + tagLength, start.node.nodeValue.length); + var currentNode = start.node; + + do { + currentNode = currentNode.nextSibling; + + if (currentNode == end.node) { + latex += end.node.nodeValue.substring(0, end.position - tagLength); + } + else { + latex += currentNode.nodeValue; + } + } while (currentNode != end.node); + } + + return { + 'latex': latex, + 'startNode': start.node, + 'startPosition': start.position, + 'endNode': end.node, + 'endPosition': end.position + }; +} + +/** + * Converts LaTeX to MathML. + * @param {string} latex String + * @param {bool} includeLatexOnSemantics If true LaTeX would me included into MathML semantics. + * @return {string} converted mathML + * @ignore + */ +function wrs_getMathMLFromLatex(latex, includeLatexOnSemantics) { + if (_wrs_int_LatexCache.hasOwnProperty(latex)) { + return _wrs_int_LatexCache[latex]; + } + var data = { + 'service': 'latex2mathml', + 'latex': latex + }; + + if (includeLatexOnSemantics) { + data['saveLatex'] = ''; + } + + var jsonResponse = JSON.parse(wrs_getContent(_wrs_conf_servicePath, data)); + + var output; + if (jsonResponse.status == "ok") { + var output = jsonResponse.result.text; + output = output.split("\r").join('').split("\n").join(' '); + // Populate LatexCache. + wrs_populateLatexCache(latex, output); + } else { + output = "$$" + latex + "$$"; + } + + return output; +} + +/** + * Gets the node length in characters. + * @param {object} node HTML node. + * @return {int} node length + * @ignore + */ +function wrs_getNodeLength(node) { + if (node.nodeType == 3) { // TEXT_NODE. + return node.nodeValue.length; + } + + if (node.nodeType == 1) { // ELEMENT_NODE. + var length = _wrs_staticNodeLengths[node.nodeName.toUpperCase()]; + + if (length === undefined) { + length = 0; + } + + for (var i = 0; i < node.childNodes.length; ++i) { + length += wrs_getNodeLength(node.childNodes[i]); + } + + return length; + } + + return 0; +} + +/** + * Parses the query string and returns it as a Hash table. + * @param {object} windowObject a window object with a query string. + * @return {object} a hash table containing the query string. + * @ignore + */ +function wrs_getQueryParams(windowObject) { + var data = {}; + var start = windowObject.location.search.indexOf('?'); + start = (start == -1) ? 0 : start + 1; + var queryStringParts = windowObject.location.search.substr(start).split('&'); + + for (var i = 0; i < queryStringParts.length; ++i) { + var paramParts = queryStringParts[i].split('=', 2); + data[paramParts[0]] = wrs_urldecode(paramParts[1]); + } + + return data; +} + +/** + * Gets the selected node or text. + * If the caret is on a text node, concatenates it with all the previous and next text nodes. + * @param {object} target The editable element + * @param {boolean} isIframe Specifies if the target is an iframe or not + * @param {forceGetSelection} If true, ignores IE system to get the current selection and uses window.getSelection() + * @return {object} An object with the 'node' key setted if the item is an element or the keys 'node' and 'caretPosition' if the element is text + * @ignore + */ +function wrs_getSelectedItem(target, isIframe, forceGetSelection) { + var windowTarget; + + if (isIframe) { + windowTarget = target.contentWindow; + windowTarget.focus(); + } + else { + windowTarget = window; + target.focus(); + } + + if (document.selection && !forceGetSelection) { + var range = windowTarget.document.selection.createRange(); + + if (range.parentElement) { + if (range.htmlText.length > 0) { + if (range.text.length == 0) { + return wrs_getSelectedItem(target, isIframe, true); + } + + return null; + } + + windowTarget.document.execCommand('InsertImage', false, '#'); + var temporalObject = range.parentElement(); + + if (temporalObject.nodeName.toUpperCase() != 'IMG') { + // IE9 fix: parentElement() does not return the IMG node, returns the parent DIV node. In IE < 9, pasteHTML does not work well. + range.pasteHTML(''); + temporalObject = windowTarget.document.getElementById('wrs_openEditorWindow_temporalObject'); + } + + var node; + var caretPosition; + + if (temporalObject.nextSibling && temporalObject.nextSibling.nodeType == 3) { // TEXT_NODE. + node = temporalObject.nextSibling; + caretPosition = 0; + } + else if (temporalObject.previousSibling && temporalObject.previousSibling.nodeType == 3) { // TEXT_NODE. + node = temporalObject.previousSibling; + caretPosition = node.nodeValue.length; + } + else { + node = windowTarget.document.createTextNode(''); + temporalObject.parentNode.insertBefore(node, temporalObject); + caretPosition = 0; + } + + temporalObject.parentNode.removeChild(temporalObject); + + return { + 'node': node, + 'caretPosition': caretPosition + }; + } + + if (range.length > 1) { + return null; + } + + return { + 'node': range.item(0) + }; + } + + if (windowTarget.getSelection) { + var selection = windowTarget.getSelection(); + + try { + var range = selection.getRangeAt(0); + } + catch (e) { + var range = windowTarget.document.createRange(); + } + + var node = range.startContainer; + + if (node.nodeType == 3) { // TEXT_NODE. + if (range.startOffset != range.endOffset) { + return null; + } + + return { + 'node': node, + 'caretPosition': range.startOffset + }; + } + + if (node != range.endContainer) { + return null; + } + + if (node.nodeType == 1) { // ELEMENT_NODE. + var position = range.startOffset; + + if (node.childNodes[position]) { + return { + 'node': node.childNodes[position] + }; + } + } + } + + return null; +} + +/** + * Returns null if there isn't any item or if it is malformed. + * Otherwise returns a div DOM node containing the mathml image and the cursor position inside the textarea. + * @param {Object} textarea DOM Element. + * @ignore + */ +function wrs_getSelectedItemOnTextarea(textarea) { + var textNode = document.createTextNode(textarea.value); + var textNodeWithLatex = wrs_getLatexFromTextNode(textNode, textarea.selectionStart); + if (textNodeWithLatex == null) { + return null + }; + + // Try to get latex mathml from cache + var latex = textNodeWithLatex.latex; + var mathml = _wrs_int_LatexCache[latex]; + // If the formula was written and not generated by the editor, caches won't have the data. + if (typeof mathml == 'undefined') { + mathml = wrs_getMathMLFromLatex(latex); + } + + var mathmlWithoutSemantics = wrs_removeSemanticsMathml(mathml); + var img = wrs_parseMathmlToImg(mathmlWithoutSemantics, _wrs_xmlCharacters, _wrs_int_langCode); + var div = document.createElement('div'); + div.innerHTML = img; + + return { + 'node': div.firstChild, + 'startPosition': textNodeWithLatex.startPosition, + 'endPosition': textNodeWithLatex.endPosition + }; +} + +/** + * Converts the HTML of a image into the output code that WIRIS must return. + * By default returns the mathml stored on data-mahml attribute (if imgCode is a formula) + * or the Wiriscas attribute of a WIRIS applet. + * @param {string} imgCode the html code from a formula or a CAS image. + * @param {bool} convertToXml True if the image should be converted to xml. + * @param {bool} convertToSafeXml True if the image should be conerte to safeXmll + * @return {string} the Xml or safeXml of a WIRIS image. + * @ignore + */ +function wrs_getWIRISImageOutput(imgCode, convertToXml, convertToSafeXml) { + var imgObject = wrs_createObject(imgCode); + + if (imgObject) { + if (imgObject.className == _wrs_conf_imageClassName || imgObject.getAttribute(_wrs_conf_imageMathmlAttribute)) { + if (!convertToXml) { + return imgCode; + } + + var xmlCode = imgObject.getAttribute(_wrs_conf_imageMathmlAttribute); + + if (xmlCode == null) { + xmlCode = imgObject.getAttribute('alt'); + } + + if (!convertToSafeXml) { + xmlCode = wrs_mathmlDecode(xmlCode); + } + + return xmlCode; + } + else if (imgObject.className == _wrs_conf_CASClassName) { + var appletCode = imgObject.getAttribute(_wrs_conf_CASMathmlAttribute); + appletCode = wrs_mathmlDecode(appletCode); + var appletObject = wrs_createObject(appletCode); + appletObject.setAttribute('src', imgObject.src); + var object = appletObject; + var appletCodeToBeInserted = wrs_createObjectCode(appletObject); + + if (convertToSafeXml) { + appletCodeToBeInserted = wrs_mathmlEncode(appletCodeToBeInserted); + } + + return appletCodeToBeInserted; + } + } + + return imgCode; +} + +/** + * Parses a text and replaces all HTML special characters by their entities. + * @param {string} input Text to be paresed. + * @return {string} the input text with all their special characters replaced by their entities. + * @ignore + */ +function wrs_htmlentities(input) { + return input.split('&').join('&').split('<').join('<').split('>').join('>').split('"').join('"'); +} + +/** + * Parses a text and replaces all the HTML entities by their characters. + * @param {string} input Text to be parsed + * @return {string} The input text with all their entities replaced by characters. + * @ignore + */ +function wrs_htmlentitiesDecode(input) { + return input.split('"').join('"').split('>').join('>').split('<').join('<').split('&').join('&'); +} + +/** + * Converts a hash to a HTTP query. + * @param {hash} properties A key-value Hash + * @return {string} A HTTP query containing all the key value pairs with all the shpecial characters replaced by their entities. + * @ignore + */ +function wrs_httpBuildQuery(properties) { + var result = ''; + + for (var i in properties) { + if (properties[i] != null) { + result += wrs_urlencode(i) + '=' + wrs_urlencode(properties[i]) + '&'; + } + } + + // Deleting last '&' empty character. + if (result.substring(result.length - 1) == '&') { + result = result.substring(0, result.length - 1); + } + + return result; +} + +/** + * Parses initial HTML code. If the HTML contains data generated by WIRIS, this data would be converted as following: + *
+ * MathML code: Image containing the corresponding MathML formulas.
+ * MathML code with LaTeX annotation : LaTeX.
+ * 
+ * @param {string} code HTML code with data generated by MathType. + * @param {string} language Language for the formula. + * @return {string} HTML code with the WIRIS data converted into LaTeX and images. + */ +function wrs_initParse(code, language) { + /* Note: The code inside this function has been inverted. + If you invert again the code then you cannot use correctly LaTeX + in Moodle. + */ + wrs_initSetSize(); + code = wrs_initParseSaveMode(code, language); + return wrs_initParseEditMode(code); +} + +/** + * Parses initial HTML code into iframes. + * @param {object} windowTarget Target object window. + * @ignore + */ +function wrs_initParseImgToIframes(windowTarget) { + if (window._wrs_conf_defaultEditMode && _wrs_conf_defaultEditMode == 'iframes') { + var imgList = windowTarget.document.getElementsByTagName('img'); + var i = 0; + + while (i < imgList.length) { + if (imgList[i].className == _wrs_conf_imageClassName) { + var mathml = imgList[i].getAttribute(_wrs_conf_imageMathmlAttribute); + + if (mathml == null) { + mathml = imgList[i].getAttribute('alt'); + } + + var iframe = wrs_mathmlToIframeObject(windowTarget, wrs_mathmlDecode(mathml)); + imgList[i].parentNode.replaceChild(iframe, imgList[i]); + } + else { + ++i; + } + } + } +} + +/** + * Parses initial HTML code depending on the edit mode. + * @param {string} code HTML code. + * @return {string} parsed HTML code. + * @ignore + */ +function wrs_initParseEditMode(code) { + if (window._wrs_conf_parseModes !== undefined && wrs_arrayContains(_wrs_conf_parseModes, 'latex') != -1) { + var imgList = wrs_getElementsByNameFromString(code, 'img', true); + var token = 'encoding="LaTeX">'; + var carry = 0; // While replacing images with latex, the indexes of the found images changes respecting the original code, so this carry is needed. + + for (var i = 0; i < imgList.length; ++i) { + var imgCode = code.substring(imgList[i].start + carry, imgList[i].end + carry); + + if (imgCode.indexOf(' class="' + _wrs_conf_imageClassName + '"') != -1) { + var mathmlStartToken = ' ' + _wrs_conf_imageMathmlAttribute + '="'; + var mathmlStart = imgCode.indexOf(mathmlStartToken); + + if (mathmlStart == -1) { + mathmlStartToken = ' alt="'; + mathmlStart = imgCode.indexOf(mathmlStartToken); + } + + if (mathmlStart != -1) { + mathmlStart += mathmlStartToken.length; + var mathmlEnd = imgCode.indexOf('"', mathmlStart); + var mathml = wrs_mathmlDecode(imgCode.substring(mathmlStart, mathmlEnd)); + var latexStartPosition = mathml.indexOf(token); + + if (latexStartPosition != -1) { + latexStartPosition += token.length; + var latexEndPosition = mathml.indexOf('
', latexStartPosition); + var latex = mathml.substring(latexStartPosition, latexEndPosition); + + var replaceText = '$$' + wrs_htmlentitiesDecode(latex) + '$$'; + code = code.substring(0, imgList[i].start + carry) + replaceText + code.substring(imgList[i].end + carry); + carry += replaceText.length - (imgList[i].end - imgList[i].start); + } + } + } + } + } + + return code; +} + +/** + * Parses initial HTML code depending on the save mode. + * @param {string} code HTML code to be parsed + * @param {string} language Language for the formula. + * @return {string} HTML code parsed. + * @ignore + */ +function wrs_initParseSaveMode(code, language) { + if (window._wrs_conf_saveMode) { + + if (_wrs_parseXml) { + // Converting XML to tags. + code = wrs_parseMathmlToLatex(code, _wrs_safeXmlCharacters); + code = wrs_parseMathmlToLatex(code, _wrs_xmlCharacters); + // Safe XML and XML must be parsed regardeless of save mode. + // Order is important here, safeXml must be parsed first in order to avoid conflicts with data-mathml img attribute. + code = wrs_parseSafeAppletsToObjects(code); + code = wrs_parseMathmlToImg(code, _wrs_safeXmlCharacters, language); + code = wrs_parseMathmlToImg(code, _wrs_xmlCharacters, language); + } + + if (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'image') { + code = wrs_codeImgTransform(code, 'base642showimage'); + } + } + + var appletList = wrs_getElementsByNameFromString(code, 'applet', false); + var carry = 0; // While replacing applets with images, the indexes of the found applets changes respecting the original code, so this carry is needed. + + for (var i = 0; i < appletList.length; ++i) { + var appletCode = code.substring(appletList[i].start + carry, appletList[i].end + carry); + + // The second control in the if is used to find WIRIS applet which don't have Wiriscas class (as it was in old CAS applets). + if (appletCode.indexOf(' class="' + _wrs_conf_CASClassName + '"') != -1 || appletCode.toUpperCase().indexOf('WIRIS') != -1) { + if (appletCode.indexOf(' src="') != -1){ + var srcStart = appletCode.indexOf(' src="') + ' src="'.length; + var srcEnd = appletCode.indexOf('"', srcStart); + var src = appletCode.substring(srcStart, srcEnd); + } else{ + // This should happen only with old CAS imported from Moodle 1 to Moodle 2. + if (typeof(_wrs_conf_pluginBasePath) != 'undefined'){ + var src = _wrs_conf_pluginBasePath + '/integration/showcasimage.php?formula=noimage'; + } else { + var src = ''; + } + if (appletCode.indexOf(' class="' + _wrs_conf_CASClassName + '"') == -1){ + var closeSymbol = appletCode.indexOf('>'); + var appletTag = appletCode.substring(0, closeSymbol); + var newAppletTag = appletTag.split(' width=').join(' class="Wiriscas" width='); + appletCode = appletCode.split(appletTag).join(newAppletTag); + appletCode = appletCode.split('\'').join('"'); + } + } + + // Double click to edit has been removed here. + var imgCode = ''; + + code = code.substring(0, appletList[i].start + carry) + imgCode + code.substring(appletList[i].end + carry); + carry += imgCode.length - (appletList[i].end - appletList[i].start); + } + } + + return code; +} + +/** + * Looks for elements that match the given name in a HTML code string. + * Important: this function is very concrete for WIRIS code. It takes as preconditions lots of behaviors that are not the general case. + * + * @param {string} code HTML code + * @param {string} name Element names + * @param {boolean} autoClosed True if the elements are autoClosed. + * @return {array} An array containing all HTML elements of code matching the name argument. + * @ignore + */ +function wrs_getElementsByNameFromString(code, name, autoClosed) { + var elements = []; + var code = code.toLowerCase(); + name = name.toLowerCase(); + var start = code.indexOf('<' + name + ' '); + + while (start != -1) { // Look for nodes. + var endString; + + if (autoClosed) { + endString = '>'; + } + else { + endString = ''; + } + + var end = code.indexOf(endString, start); + + if (end != -1) { + end += endString.length; + + elements.push({ + 'start': start, + 'end': end + }); + } + else { + end = start + 1; + } + + start = code.indexOf('<' + name + ' ', end); + } + + return elements; +} + +/** + * Replaces a selection with an element. + * @param {object} element Element + * @param {object} focusElement Element to be focused + * @param {object} windowTarget Target + * @ignore + */ +function wrs_insertElementOnSelection(element, focusElement, windowTarget) { + try { + // Integration function + // If wrs_int_insertElementOnSelection function exists on + // integration script can call focus method from the editor instance. + // For example, on CKEditor calls CKEditorInstance.focus() method. + // With this method we can call proper focus methods which in some scenarios + // help's MathType to focus properly on the current editor window. + if (typeof wrs_int_insertElementOnSelection !== 'undefined') { + wrs_int_insertElementOnSelection(); + } + if(typeof focusElement.frameElement !== 'undefined'){ + function get_browser(){ + var ua = navigator.userAgent; + if(ua.search("Edge/") >= 0){ + return "EDGE"; + }else if(ua.search("Chrome/") >= 0){ + return "CHROME"; + }else if(ua.search("Trident/") >= 0){ + return "IE"; + }else if(ua.search("Firefox/") >= 0){ + return "FIREFOX"; + }else if(ua.search("Safari/") >= 0){ + return "SAFARI"; + } + } + var browserName = get_browser(); + // Iexplorer, Edge and Safari can't focus into iframe + if (browserName == 'SAFARI' || browserName == 'IE' || browserName == 'EDGE') { + focusElement.focus(); + }else{ + focusElement.frameElement.focus(); + } + }else{ + focusElement.focus(); + } + + if (_wrs_isNewElement) { + if (focusElement.type == "textarea") { + wrs_updateTextarea(focusElement, element.textContent); + } + else if (document.selection && document.getSelection == 0) { + var range = windowTarget.document.selection.createRange(); + windowTarget.document.execCommand('InsertImage', false, element.src); + + if (!('parentElement' in range)) { + windowTarget.document.execCommand('delete', false); + range = windowTarget.document.selection.createRange(); + windowTarget.document.execCommand('InsertImage', false, element.src); + } + + if ('parentElement' in range) { + var temporalObject = range.parentElement(); + + if (temporalObject.nodeName.toUpperCase() == 'IMG') { + temporalObject.parentNode.replaceChild(element, temporalObject); + } + else { + // IE9 fix: parentNode() does not return the IMG node, returns the parent DIV node. In IE < 9, pasteHTML does not work well. + range.pasteHTML(wrs_createObjectCode(element)); + } + } + } + else { + var selection = windowTarget.getSelection(); + // We have use wrs_range beacuse IExplorer delete selection when select another part of text. + if (_wrs_range) { + var range = _wrs_range; + _wrs_range = null; + } + else { + + try { + var range = selection.getRangeAt(0); + } + catch (e) { + var range = windowTarget.document.createRange(); + } + } + selection.removeAllRanges(); + + range.deleteContents(); + + var node = range.startContainer; + var position = range.startOffset; + + if (node.nodeType == 3) { // TEXT_NODE. + node = node.splitText(position); + node.parentNode.insertBefore(element, node); + node = node.parentNode; + } + else if (node.nodeType == 1) { // ELEMENT_NODE. + node.insertBefore(element, node.childNodes[position]); + } + // Fix to set the caret after the inserted image. + range.selectNode(element); + // Integration function. + // If wrs_int_setCaretPosition function exists on + // integration script can call caret method from the editor instance. + // With this method we can call proper specific editor methods which in some scenarios + // help's MathType to set caret position properly on the current editor window. + if (typeof wrs_int_selectRange != 'undefined') { + wrs_int_selectRange(range); + } + // Selection collapse must have to do it after the function 'wrs_int_selectRange' because + // can be that the range was changed and the selection needs to be updated. + position = range.endOffset; + selection.collapse(node, position); + } + } + else if (_wrs_temporalRange) { + if (document.selection && document.getSelection == 0) { + _wrs_isNewElement = true; + _wrs_temporalRange.select(); + wrs_insertElementOnSelection(element, focusElement, windowTarget); + } + else { + var parentNode = _wrs_temporalRange.startContainer; + _wrs_temporalRange.deleteContents(); + _wrs_temporalRange.insertNode(element); + } + } + else if (focusElement.type == "textarea") { + var item; + // Wrapper for some integrations that can have special behaviours to show latex. + if (typeof wrs_int_getSelectedItem != 'undefined') { + item = wrs_int_getSelectedItem(focusElement, false); + } + else { + item = wrs_getSelectedItemOnTextarea(focusElement); + } + wrs_updateExistingFormulaOnTextarea(focusElement, element.textContent, item.startPosition, item.endPosition); + } + else { + if (!element) { // Editor empty, formula has been erased on edit. + _wrs_temporalImage.parentNode.removeChild(_wrs_temporalImage); + } + _wrs_temporalImage.parentNode.replaceChild(element, _wrs_temporalImage); + function placeCaretAfterNode(node) { + if (typeof window.getSelection != "undefined") { + var range = windowTarget.document.createRange(); + range.setStartAfter(node); + range.collapse(true); + var selection = windowTarget.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + } + } + placeCaretAfterNode(element); + } + } + catch (e) { + } +} + +/** + * Checks if the mathml at position i is inside an HTML attribute or not. + * @param {string} content A string containing MathML code. + * @param {string} i Search index. + * @return {bool} True if is inside an HTML attribute. In other case, false. + * @ignore + */ +function wrs_isMathmlInAttribute(content, i) { + // Regex = '^[\'"][\\s]*=[\\s]*[\\w-]+([\\s]*("[^"]*"|\'[^\']*\')[\\s]*=[\\s]*[\\w-]+[\\s]*)*[\\s]+gmi<'; + var math_att = '[\'"][\\s]*=[\\s]*[\\w-]+'; // "=att OR '=att + var att_content = '"[^"]*"|\'[^\']*\''; // "blabla" OR 'blabla' + var att = '[\\s]*(' + att_content + ')[\\s]*=[\\s]*[\\w-]+[\\s]*'; // "blabla"=att OR 'blabla'=att + var atts = '(' + att + ')*'; // "blabla"=att1 "blabla"=att2 + var regex = '^' + math_att + atts + '[\\s]+gmi<'; // "=att "blabla"=att1 "blabla"=att2 gmi< . + var expression = new RegExp(regex); + + var actual_content = content.substring(0, i); + var reversed = actual_content.split('').reverse().join(''); + var exists = expression.test(reversed); + + return exists; +} + +/** + * WIRIS special encoding. + * We use these entities because IE doesn't support html entities on its attributes sometimes. Yes, sometimes. + * @param {string} input String to be decoded. + * @return {string} Decoded string. + * @ignore + */ +function wrs_mathmlDecode(input) { + // Decoding entities. + input = input.split(_wrs_safeXmlCharactersEntities.tagOpener).join(_wrs_safeXmlCharacters.tagOpener); + input = input.split(_wrs_safeXmlCharactersEntities.tagCloser).join(_wrs_safeXmlCharacters.tagCloser); + input = input.split(_wrs_safeXmlCharactersEntities.doubleQuote).join(_wrs_safeXmlCharacters.doubleQuote); + // Added to fix problem due to import from 1.9.x. + input = input.split(_wrs_safeXmlCharactersEntities.realDoubleQuote).join(_wrs_safeXmlCharacters.realDoubleQuote); + + // Blackboard. + if ('_wrs_blackboard' in window && window._wrs_blackboard){ + input = input.split(_wrs_safeBadBlackboardCharacters.ltElement).join(_wrs_safeGoodBlackboardCharacters.ltElement); + input = input.split(_wrs_safeBadBlackboardCharacters.gtElement).join(_wrs_safeGoodBlackboardCharacters.gtElement); + input = input.split(_wrs_safeBadBlackboardCharacters.ampElement).join(_wrs_safeGoodBlackboardCharacters.ampElement); + + /*var regex = /«mtext».*[<>&].*«\/mtext»/; + + var result = regex.exec(input); + while(result){ + var changedResult = result[0].split(_wrs_xmlCharacters.tagOpener).join('§lt;'); + changedResult = changedResult.split(_wrs_xmlCharacters.tagCloser).join('§gt;'); + changedResult = changedResult.split(_wrs_xmlCharacters.ampersand).join('§amp;'); + input = input.replace(result, changedResult); + result = regex.exec(input); + }*/ + } + + // Decoding characters. + input = input.split(_wrs_safeXmlCharacters.tagOpener).join(_wrs_xmlCharacters.tagOpener); + input = input.split(_wrs_safeXmlCharacters.tagCloser).join(_wrs_xmlCharacters.tagCloser); + input = input.split(_wrs_safeXmlCharacters.doubleQuote).join(_wrs_xmlCharacters.doubleQuote); + input = input.split(_wrs_safeXmlCharacters.ampersand).join(_wrs_xmlCharacters.ampersand); + input = input.split(_wrs_safeXmlCharacters.quote).join(_wrs_xmlCharacters.quote); + + // We are replacing $ by & when its part of an entity for retrocompatibility. Now, the standard is replace § by &. + var returnValue = ''; + var currentEntity = null; + + for (var i = 0; i < input.length; ++i) { + var character = input.charAt(i); + + if (currentEntity == null) { + if (character == '$') { + currentEntity = ''; + } + else { + returnValue += character; + } + } + else { + if (character == ';') { + returnValue += '&' + currentEntity + ';'; + currentEntity = null; + } + else if (character.match(/([a-zA-Z0-9#._-] | '-')/)) { // Character is part of an entity. + currentEntity += character; + } + else { + returnValue += '$' + currentEntity; // Is not an entity. + currentEntity = null; + --i; // Parse again the current character. + } + } + } + + return returnValue; +} + +/** + * WIRIS special encoding. + * We use these entities because IE doesn't support html entities on its attributes sometimes. Yes, sometimes. + * @param {string} input to be encoded + * @return {string} Encoded string. + * @ignore + */ +function wrs_mathmlEncode(input) { + input = input.split(_wrs_xmlCharacters.tagOpener).join(_wrs_safeXmlCharacters.tagOpener); + input = input.split(_wrs_xmlCharacters.tagCloser).join(_wrs_safeXmlCharacters.tagCloser); + input = input.split(_wrs_xmlCharacters.doubleQuote).join(_wrs_safeXmlCharacters.doubleQuote); + input = input.split(_wrs_xmlCharacters.ampersand).join(_wrs_safeXmlCharacters.ampersand); + input = input.split(_wrs_xmlCharacters.quote).join(_wrs_safeXmlCharacters.quote); + + return input; +} + +/** + * Converts special symbols (> 128) to entities and replaces all textual entities by its number entities. + * @param {string} mathml MathML string containing - or not - special symbols + * @return {string} MathML with all textual entities replaced. + * @ignore + */ +function wrs_mathmlEntities(mathml) { + var toReturn = ''; + + for (var i = 0; i < mathml.length; ++i) { + + var character = mathml.charAt(i); + + // Parsing > 128 characters. + if (mathml.codePointAt(i) > 128) { + toReturn += '&#' + mathml.codePointAt(i) + ';' + // For UTF-32 characters we need to move the index one position. + if (mathml.codePointAt(i) > 0xffff) { + i++; + } + } + else if (character == '&') { + var end = mathml.indexOf(';', i + 1); + + if (end >= 0) { + var container = document.createElement('span'); + container.innerHTML = mathml.substring(i, end + 1); + toReturn += '&#' + wrs_fixedCharCodeAt((container.innerText || container.textContent),0) + ';'; + i = end; + } + else { + toReturn += character; + } + } + else { + toReturn += character; + } + } + + return toReturn; +} + +/** + * Add wrs::type attribute to mathml if the mathml has been created with a custom editor + * for example, chemistry. + * @param {string} mathml a MathML string created with a custom editor, like chemistry. + * @return {string} The MathML string with his class containgin the editor toolbar string. + * @ignore + */ +function wrs_mathmlAddEditorAttribute(mathml) { + var toReturn = ''; + + var start = mathml.indexOf(''); + if (mathml.indexOf("class") == -1 ) { + // Adding custom editor type. + toReturn = mathml.substr(start, end) + ' class="wrs_' + wrs_int_getCustomEditorEnabled().toolbar + '">'; + toReturn += mathml.substr(end + 1, mathml.length); + return toReturn; + } + } + return mathml; + +} + +/** + * Fix charCodeAt() javascript function to handle non-Basic-Multilingual-Plane characters. + * @param {string} str String + * @param {int} idx An integer greater than or equal to 0 and less than the length of the string + * @return {int} An integer representing the UTF-16 code of the string at the given index. + * @ignore + */ + +function wrs_fixedCharCodeAt(str, idx) { + idx = idx || 0; + var code = str.charCodeAt(idx); + var hi, low; + + /* High surrogate (could change last hex to 0xDB7F to treat high + private surrogates as single characters) */ + + if (0xD800 <= code && code <= 0xDBFF) { + hi = code; + low = str.charCodeAt(idx + 1); + if (isNaN(low)) { + throw _wrs_stringManager.getString('exception_high_surrogate'); + } + return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; + } + + if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate. + /* We return false to allow loops to skip this iteration since should have + already handled high surrogate above in the previous iteration. */ + return false; + } + return code; +} + +/** + * Gets the accessible text of a given MathML calling mathml2accesible service. + * @param {string} mathml MathML to get the accesibility. + * @param {string} language Language of the accesibility. + * @return {string} Accessibility from mathml string on language string. + * @ignore + */ +function wrs_mathmlToAccessible(mathml, language, data) { + var accessibleText; + + if (_wrs_int_AccessibleCache.hasOwnProperty(mathml)) { + accessibleText = _wrs_int_AccessibleCache[mathml]; + } + else { + data['service'] = 'mathml2accessible'; + data['lang'] = _wrs_int_langCode; + var accesibleJsonResponse = JSON.parse(wrs_getContent(_wrs_conf_servicePath, data)); + if (accesibleJsonResponse.status != 'error') { + accessibleText = accesibleJsonResponse.result.text; + } + else { + accessibleText = _wrs_stringManager.getString('error_convert_accessibility'); + } + } + + return accessibleText; + +} + +/** + * Converts mathml to an iframe object. + * @param {object} windowTarget Window object. + * @param {string} mathml MathML to be converted. + * @return {object} iframe object containging parsed mathml. + * @ignore + */ +function wrs_mathmlToIframeObject(windowTarget, mathml) { + if (window.navigator.userAgent.toLowerCase().indexOf('webkit') != -1) { + // In WebKit, the formula is represented by a div instead of an iframe. + var container = windowTarget.document.createElement('span'); + container.className = _wrs_conf_imageClassName; + container.setAttribute(_wrs_conf_imageMathmlAttribute, mathml); + container.setAttribute('height', '1'); + container.setAttribute('width', '1'); + container.style.display = 'inline-block'; + container.style.cursor = 'pointer'; + container.style.webkitUserModify = 'read-only'; + container.style.webkitUserSelect = 'all'; + + var formulaContainer = windowTarget.document.createElement('span'); + formulaContainer.style.display = 'inline'; + container.appendChild(formulaContainer); + + function waitForViewer() { + if (windowTarget.com && windowTarget.com.wiris) { + if (!('_wrs_viewer' in windowTarget)) { + windowTarget._wrs_viewer = new windowTarget.com.wiris.jsEditor.JsViewerMain(_wrs_conf_pluginBasePath + '/integration/editor'); + windowTarget._wrs_viewer.insertCSS(null, windowTarget.document); + } + + windowTarget._wrs_viewer.paintFormulaOnContainer(mathml, formulaContainer, null); + + function prepareDiv() { + if (windowTarget._wrs_viewer.isReady()) { + container.style.height = formulaContainer.style.height; + container.style.width = formulaContainer.style.width; + container.style.verticalAlign = formulaContainer.style.verticalAlign; + } + else { + setTimeout(prepareDiv, 100); + } + }; + + prepareDiv(); + } + else { + setTimeout(waitForViewer, 100); + } + } + + if (!('_wrs_viewerAppended' in windowTarget)) { + var viewerScript = windowTarget.document.createElement('script'); + viewerScript.src = _wrs_conf_pluginBasePath + '/integration/editor/viewer.js'; + windowTarget.document.getElementsByTagName('head')[0].appendChild(viewerScript); + windowTarget._wrs_viewerAppended = true; + } + + waitForViewer(); + + return container; + } + + windowTarget.document.wrs_assignIframeEvents = function (myIframe) { + wrs_addEvent(myIframe.contentWindow.document, 'click', function () { + wrs_fireEvent(myIframe, 'dblclick'); + }); + }; + + var iframe = windowTarget.document.createElement('iframe'); + iframe.className = _wrs_conf_imageClassName; + iframe.setAttribute(_wrs_conf_imageMathmlAttribute, mathml); + iframe.style.display = 'inline'; + iframe.style.border = 'none'; + iframe.setAttribute('height', '1'); + iframe.setAttribute('width', '1'); + iframe.setAttribute('scrolling', 'no'); + iframe.setAttribute('frameBorder', '0'); + iframe.src = _wrs_conf_pluginBasePath + '/core/iframe.html#' + _wrs_conf_imageMathmlAttribute; + return iframe; +} + +/** + * Converts mathml to img object. + * @param {object} creator Object with the "createElement" method + * @param {string} mathml MathML code + * @param {object} wirisProperties object containing WIRIS custom properties + * @param {language} language Custom language for accesibility. + * @return {object} And image containing the formula image corresponding to mathml string. + * @ignore + */ +function wrs_mathmlToImgObject(creator, mathml, wirisProperties, language) { + var width; + var height; + var baseline; + var imgObject = creator.createElement('img'); + imgObject.align = 'middle'; + imgObject.style.maxWidth = 'none'; + var data = (wirisProperties) ? wirisProperties : {}; + + if (window._wrs_conf_useDigestInsteadOfMathml && _wrs_conf_useDigestInsteadOfMathml) { + data['returnDigest'] = 'true'; + } + + data['mml'] = mathml; + data['lang'] = language; + + if (_wrs_conf_setSize) { + // Request metrics of the generated image. + data['metrics'] = 'true'; + data['centerbaseline'] = 'false'; + } + + // Full base64 method (edit & save). + if (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default') { + data['base64'] = true; + } + + // Render js params: _wrs_int_wirisProperties contains some js render params. Since mathml can support render params, js params should be send only to editor, not to render. + + imgObject.className = _wrs_conf_imageClassName; + + // TODO Custom Editors: class="wrs_toolbar" should be given by the editor + // so the first condition shouldn't be longer necessary. + var customEditor; + if (customEditor = wrs_int_getCustomEditorEnabled()) { + imgObject.setAttribute('data-custom-editor', customEditor.toolbar); + } else if (mathml.indexOf('class="') != -1) { // We check here if the mathmnl has been created from a customEditor (such chemistry) + // to add data-custom-editor attribute to img object (if necessary). + var mathmlSubstring = mathml.substring(mathml.indexOf('class="') + 'class="'.length, mathml.length); + mathmlSubstring = mathmlSubstring.substring(0, mathmlSubstring.indexOf('"')); + mathmlSubstring = mathmlSubstring.substring(4,mathmlSubstring.length); + imgObject.setAttribute('data-custom-editor', mathmlSubstring); + } + + // Performance enabled. + if (_wrs_conf_wirisPluginPerformance && (_wrs_conf_saveMode == 'xml' || _wrs_conf_saveMode == 'safeXml')) { + + var result = JSON.parse(wrs_createShowImageSrc(mathml, data, language)); + if (result["status"] == 'warning') { + // POST call. + // if the mathml is malformed, this function will throw an exception. + try { + result = JSON.parse(wrs_getContent(_wrs_conf_showimagePath, data)); + } + catch (e) { + return; + } + } + result = result.result; + if (result['format'] == 'png') { + imgObject.src = 'data:image/png;base64,' + result['content']; + } else { + imgObject.src = 'data:image/svg+xml;charset=utf8,' + wrs_urlencode(result['content']); + } + imgObject.setAttribute(_wrs_conf_imageMathmlAttribute, wrs_mathmlEncode(mathml)); + if (_wrs_conf_setSize) { + wrs_setImgSize(imgObject, result['content'], true); + } + + if (window._wrs_conf_enableAccessibility && _wrs_conf_enableAccessibility) { + if (typeof result.alt == 'undefined') { + imgObject.alt = wrs_mathmlToAccessible(mathml, language, data); + wrs_populateAccessibleCache(mathml, imgObject.alt); + } + else { + imgObject.alt = result.alt; + } + } + } + else { + var result = wrs_createImageSrc(mathml, data); + if (window._wrs_conf_useDigestInsteadOfMathml && _wrs_conf_useDigestInsteadOfMathml) { + var parts = result.split(':', 2); + imgObject.setAttribute(_wrs_conf_imageMathmlAttribute, parts[0]); + imgObject.src = parts[1]; + } + else { + imgObject.setAttribute(_wrs_conf_imageMathmlAttribute, wrs_mathmlEncode(mathml)); + imgObject.src = result; + if (_wrs_conf_setSize) { + wrs_setImgSize(imgObject,result, (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default') ? true : false); + } + } + if (window._wrs_conf_enableAccessibility && _wrs_conf_enableAccessibility) { + imgObject.alt = wrs_mathmlToAccessible(mathml, language, data); + wrs_populateAccessibleCache(mathml, imgObject.alt); + } + } + /* if (_wrs_conf_setSize) { + var ar = wrs_urlToAssArray(result); + width = ar['cw']; + height = ar['ch']; + baseline = ar['cb']; + dpi = ar['dpi']; + if (dpi) { + width = width * 96/dpi; + height = height * 96/dpi; + baseline = baseline * 96/dpi; + } + // result = wrs_assArrayToUrl(ar); + }*/ + + if (typeof wrs_observer != 'undefined') { + wrs_observer.observe(imgObject, wrs_observer_config); + } + + // Role math https://www.w3.org/TR/wai-aria/roles#math. + imgObject.setAttribute('role', 'math'); + return imgObject; +} + +/** + * Opens a new CAS window. + * @param {object} target The editable element + * @param {boolean} isIframe Specifies if target is an iframe or not + * @param {string} language CAS language. + * @return {object} The opened window + * @ignore + */ +function wrs_openCASWindow(target, isIframe, language) { + if (isIframe === undefined) { + isIframe = true; + } + + _wrs_temporalRange = null; + + if (target) { + var selectedItem = wrs_getSelectedItem(target, isIframe); + + if (selectedItem != null && selectedItem.caretPosition === undefined && selectedItem.node.nodeName.toUpperCase() == 'IMG' && selectedItem.node.className == _wrs_conf_CASClassName) { + _wrs_temporalImage = selectedItem.node; + _wrs_isNewElement = false; + } + } + + var path = _wrs_conf_CASPath; + + if (language) { + path += '?lang=' + language; + } + + return window.open(path, 'WIRISCAS', _wrs_conf_CASAttributes); +} + +/** + * Opens a new editor window. + * @param {string} language Language code for the editor + * @param {object} target The editable element + * @param {boolean} isIframe Specifies if the target is an iframe or not + * @param {boolean} isModal Specifies if the target is a modal window or not + * @return {object} The opened window + * @ignore + */ +function wrs_openEditorWindow(language, target, isIframe) { + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + var isIOS = ((ua.indexOf("ipad") > -1) || (ua.indexOf("iphone") > -1)); + + if(isAndroid || isIOS) { + _wrs_conf_modalWindow = true; // Conf property must be overrided on tablet/phone devices. + } + + try { + if (isIframe) { + var selection = target.contentWindow.getSelection(); + _wrs_range = selection.getRangeAt(0); + } + else { + var selection = getSelection(); + _wrs_range = selection.getRangeAt(0); + } + } + catch (e) { + _wrs_range = null; + } + + if (isIframe === undefined) { + isIframe = true; + } + + // Avoid double slashes. + var path = _wrs_conf_path.lastIndexOf('/') == _wrs_conf_path.length - 1 ? _wrs_conf_path + "core/editor.html" : _wrs_conf_path + "/core/editor.html"; + + // Params for editor.html + if (language) { + path = wrs_addArgument(path, "lang", language); + } + + if (location.protocol == 'https:') { + path = wrs_addArgument(path, "secure", "true"); + } + + path = wrs_addArgument(path, "v", _wrs_plugin_version); + + var availableDirs = new Array('rtl', 'ltr'); + if (typeof _wrs_int_directionality != 'undefined' && wrs_arrayContains(availableDirs, _wrs_int_directionality) != -1){ + path = wrs_addArgument(path,"dir",_wrs_int_directionality); + } + + // Cross Domain Policy. + wrs_addArgument(path, 'host', 'localhost'); + + _wrs_editMode = (window._wrs_conf_defaultEditMode) ? _wrs_conf_defaultEditMode : 'images'; + _wrs_temporalRange = null; + + if (target) { + var selectedItem; + if (typeof wrs_int_getSelectedItem != 'undefined') { + selectedItem = wrs_int_getSelectedItem(target, isIframe); + } else { + selectedItem = wrs_getSelectedItem(target, isIframe); + } + + if (selectedItem != null) { + if (selectedItem.caretPosition === undefined) { + if (wrs_containsClass(selectedItem.node, _wrs_conf_imageClassName)) { + if (selectedItem.node.nodeName.toUpperCase() == 'IMG') { + _wrs_editMode = 'images'; + } + else if (selectedItem.node.nodeName.toUpperCase() == 'IFRAME') { + _wrs_editMode = 'iframes'; + } + + _wrs_temporalImage = selectedItem.node; + _wrs_isNewElement = false; + } + } + else { + var latexResult = wrs_getLatexFromTextNode(selectedItem.node, selectedItem.caretPosition); + + if (latexResult != null) { + _wrs_editMode = 'latex'; + + var mathml = wrs_getMathMLFromLatex(latexResult.latex); + _wrs_isNewElement = false; + + _wrs_temporalImage = document.createElement('img'); + _wrs_temporalImage.setAttribute(_wrs_conf_imageMathmlAttribute, wrs_mathmlEncode(mathml)); + var windowTarget = (isIframe) ? target.contentWindow : window; + + if (document.selection) { + var leftOffset = 0; + var previousNode = latexResult.startNode.previousSibling; + + while (previousNode) { + leftOffset += wrs_getNodeLength(previousNode); + previousNode = previousNode.previousSibling; + } + + _wrs_temporalRange = windowTarget.document.selection.createRange(); + _wrs_temporalRange.moveToElementText(latexResult.startNode.parentNode); + _wrs_temporalRange.move('character', leftOffset + latexResult.startPosition); + _wrs_temporalRange.moveEnd('character', latexResult.latex.length + 4); // Plus 4 for the '$$' characters. + } + else { + _wrs_temporalRange = windowTarget.document.createRange(); + _wrs_temporalRange.setStart(latexResult.startNode, latexResult.startPosition); + _wrs_temporalRange.setEnd(latexResult.endNode, latexResult.endPosition); + } + } + } + } + } + // Parse atributes of editor into object + var splitterEditorAtributes = _wrs_conf_editorAttributes.split(", "); + var resultEditorAtributes = {}; + for (var i = 0, len = splitterEditorAtributes.length; i < len; i++) { + var tempAtribute = splitterEditorAtributes[i].split('='); + var key = tempAtribute[0]; + var value = tempAtribute[1]; + resultEditorAtributes[key] = value; + } + resultEditorAtributes.language = _wrs_int_langCode; + + var title = wrs_int_getCustomEditorEnabled() != null ? wrs_int_getCustomEditorEnabled().title : _wrs_stringManager.getString('mathtype'); + if (_wrs_modalWindow == null) { + _wrs_modalWindow = new ModalWindow(_wrs_conf_editorAttributes); + _wrs_modalWindow.setContentManager(new contentManager(resultEditorAtributes)); + } + if (!_wrs_css_loaded) { + var fileref = document.createElement("link"); + fileref.setAttribute("rel", "stylesheet"); + fileref.setAttribute("type", "text/css"); + fileref.setAttribute("href", wrs_concatenateUrl(_wrs_conf_path, '/core/modal.css')); + document.getElementsByTagName("head")[0].appendChild(fileref); + _wrs_css_loaded = true; + } + // TODO: setMathML logic here. + _wrs_modalWindow.open(); +} + +function Core() { + this.init = true; +} + + +/** + * Converts all occurrences of mathml code to LATEX. The MathML code should containg to be converted. + * @param {string} content A string containing MathML valid code. + * @param {Object} characters An object containing special characters. + * @return {string} String with all MathML annotated occurrences replaced by the corresponding LaTeX code. + * @ignore + */ +function wrs_parseMathmlToLatex(content, characters){ + var output = ''; + var mathTagBegin = characters.tagOpener + 'math'; + var mathTagEnd = characters.tagOpener + '/math' + characters.tagCloser; + var openTarget = characters.tagOpener + 'annotation encoding=' + characters.doubleQuote + 'LaTeX' + characters.doubleQuote + characters.tagCloser; + var closeTarget = characters.tagOpener + '/annotation' + characters.tagCloser; + var start = content.indexOf(mathTagBegin); + var end = 0; + var mathml, startAnnotation, closeAnnotation; + + while (start != -1) { + output += content.substring(end, start); + end = content.indexOf(mathTagEnd, start); + + if (end == -1) { + end = content.length - 1; + } + else { + end += mathTagEnd.length; + } + + mathml = content.substring(start, end); + + startAnnotation = mathml.indexOf(openTarget); + if (startAnnotation != -1){ + startAnnotation += openTarget.length; + closeAnnotation = mathml.indexOf(closeTarget); + var latex = mathml.substring(startAnnotation, closeAnnotation); + if (characters == _wrs_safeXmlCharacters) { + latex = wrs_mathmlDecode(latex); + } + output += '$$' + latex + '$$'; + // Populate latex into cache. + wrs_populateLatexCache(latex, mathml); + }else{ + output += mathml; + } + + start = content.indexOf(mathTagBegin, end); + } + + output += content.substring(end, content.length); + return output; +} + +/** + * Converts all occurrences of mathml code to the corresponding image. + * @param {string} content An string with valid MathML code. The matml code doesn't contain semantics. + * @param {object} characters An object containing xmlCharacters or safeXmlCharacters relation. + * @param {string} language String containging a valid language code in order to generate formula accesibilty. + * @return {string} The input string with all the MathML ocurrences replaced by the corresponding image. + * @ignore + */ +function wrs_parseMathmlToImg(content, characters, language) { + var output = ''; + var mathTagBegin = characters.tagOpener + 'math'; + var mathTagEnd = characters.tagOpener + '/math' + characters.tagCloser; + var start = content.indexOf(mathTagBegin); + var end = 0; + + while (start != -1) { + output += content.substring(end, start); + // Avoid WIRIS images to be parsed. + var imageMathmlAtrribute = content.indexOf(_wrs_conf_imageMathmlAttribute); + end = content.indexOf(mathTagEnd, start); + + if (end == -1) { + end = content.length - 1; + } else if (imageMathmlAtrribute != -1) { + // First close tag of img attribute + // If a mathmlAttribute exists should be inside a img tag. + end += content.indexOf("/>", start); + } + else { + end += mathTagEnd.length; + } + + if (!wrs_isMathmlInAttribute(content, start) && imageMathmlAtrribute == -1){ + var mathml = content.substring(start, end); + mathml = (characters == _wrs_safeXmlCharacters) ? wrs_mathmlDecode(mathml) : wrs_mathmlEntities(mathml); + output += wrs_createObjectCode(wrs_mathmlToImgObject(document, mathml, null, language)); + } + else { + output += content.substring(start, end); + } + + start = content.indexOf(mathTagBegin, end); + } + + output += content.substring(end, content.length); + return output; +} + +/** + * Converts all occurrences of safe applet code to the corresponding code. + * @param {string} content String containging valid applet code ... + * @return {string} String with all the applet code conerted to safe tags. + * @ignore + */ +function wrs_parseSafeAppletsToObjects(content) { + var output = ''; + var appletTagBegin = _wrs_safeXmlCharacters.tagOpener + 'APPLET'; + var appletTagEnd = _wrs_safeXmlCharacters.tagOpener + '/APPLET' + _wrs_safeXmlCharacters.tagCloser; + var upperCaseContent = content.toUpperCase(); + var start = upperCaseContent.indexOf(appletTagBegin); + var end = 0; + var applet; + + while (start != -1) { + output += content.substring(end, start); + end = upperCaseContent.indexOf(appletTagEnd, start); + + if (end == -1) { + end = content.length - 1; + } + else { + end += appletTagEnd.length; + } + + applet = wrs_convertOldXmlinitialtextAttribute(content.substring(start, end)); + + output += wrs_mathmlDecode(applet); + start = upperCaseContent.indexOf(appletTagBegin, end); + } + + output += content.substring(end, content.length); + return output; +} + +/** + * Cross-browser removeEventListener/detachEvent function. + * @param {object} element Element target + * @param {event} event Event + * @param {function} func Function to run + * @ignore + */ +function wrs_removeEvent(element, event, func) { + if (element.removeEventListener) { + element.removeEventListener(event, func, true); + } + else if (element.detachEvent) { + element.detachEvent('on' + event, func); + } +} + +/** + * Splits an HTML content in three parts: the code before , the code between and and the code after . + * @param {string} code HTML code to be splited. + * @return {objet} An object with the structure {'prefix': xxx, 'code': yyy, 'sufix': zzz} + * @ignore + */ +function wrs_splitBody(code) { + var prefix = ''; + var sufix = ''; + var bodyPosition = code.indexOf('', bodyPosition); + + if (bodyPosition != -1) { + ++bodyPosition; + var endBodyPosition = code.indexOf('', bodyPosition); + + if (endBodyPosition == -1) { + endBodyPosition = code.length; + } + + prefix = code.substring(0, bodyPosition); + sufix = code.substring(endBodyPosition, code.length); + code = code.substring(bodyPosition, endBodyPosition); + } + } + + return { + 'prefix': prefix, + 'code': code, + 'sufix': sufix + }; +} + +/** + * Inserts or modifies CAS. + * @param {object} focusElement Element to be focused + * @param {object} windowTarget Window where the editable content is + * @param {string} appletCode Applet code + * @param {string} image Base 64 image stream + * @param {int} imageWidth Image width + * @param {int} imageHeight Image height + * @ignore + */ +function wrs_updateCAS(focusElement, windowTarget, appletCode, image, imageWidth, imageHeight) { + var imgObject = wrs_appletCodeToImgObject(windowTarget.document, appletCode, image, imageWidth, imageHeight); + wrs_insertElementOnSelection(imgObject, focusElement, windowTarget); +} + +var wrs_PluginEvent = function () { + this.cancelled = false; + this.defaultPrevented = false; +} + +wrs_PluginEvent.prototype.cancel = function () { + this.cancelled = true; +} + +wrs_PluginEvent.prototype.preventDefault = function () { + this.defaultPrevented = true; +} + +/** + * Fires MathType event listeners + * @param {String} eventName event name + * @param {Object} e event properties + * @return {bool} false if event has been prevented. + * @ignore + */ +function wrs_fireEventListeners(eventName, e) { + for (var i = 0; i < wrs_pluginListeners.length && !e.cancelled; ++i) { + if (wrs_pluginListeners[i][eventName]) { + // Calling listener. + wrs_pluginListeners[i][eventName](e); + } + } + + return e.defaultPrevented; +} + +/** + * Inserts or modifies formulas. + * @param {object} focusElement Element to be focused + * @param {object} windowTarget Window where the editable content is + * @param {string} mathml Mathml code + * @param {object} wirisProperties Extra attributes for the formula (like background color or font size). + * @param {string} editMode Current edit mode. + * @param {string} language Language for the formula. + * @ignore + */ +function wrs_updateFormula(focusElement, windowTarget, mathml, wirisProperties, editMode, language) { + // Before update listener. + + // Params on beforeUpdate listener + // - mathml + // - editMode (read only) + // - wirisProperties + // - language (read only). + + editMode = editMode !== null ? editMode : _wrs_editMode; + var e = new wrs_PluginEvent(); + + e.mathml = mathml; + + // Cloning wirisProperties object + // We don't want wirisProperties object modified. + e.wirisProperties = {}; + + for (var attr in wirisProperties) { + e.wirisProperties[attr] = wirisProperties[attr]; + } + + // Read only. + e.language = language; + e.editMode = editMode; + + if (wrs_fireEventListeners('onBeforeFormulaInsertion', e)) { + return; + } + + mathml = e.mathml; + wirisProperties = e.wirisProperties; + + // Setting empty params for after. + e = new wrs_PluginEvent(); + e.editMode = editMode; + e.windowTarget = windowTarget; + e.focusElement = focusElement; + + if (mathml.length == 0) { + wrs_insertElementOnSelection(null, focusElement, windowTarget); + } + else if (editMode == 'latex') { + e.latex = wrs_getLatexFromMathML(mathml); + // wrs_int_getNonLatexNode is an integration wrapper to have special behaviours for nonLatex. + // Not all the integrations have special behaviours for nonLatex. + if (typeof wrs_int_getNonLatexNode != 'undefined' && (typeof e.latex == 'undefined' || e.latex == null)) { + wrs_int_getNonLatexNode(e, windowTarget, mathml); + } + else { + e.node = windowTarget.document.createTextNode('$$' + e.latex + '$$'); + wrs_populateLatexCache(e.latex, mathml); + } + wrs_insertElementOnSelection(e.node, focusElement, windowTarget); + } + else if (editMode == 'iframes') { + var iframe = wrs_mathmlToIframeObject(windowTarget, mathml); + wrs_insertElementOnSelection(iframe, focusElement, windowTarget); + } + else { + e.node = wrs_mathmlToImgObject(windowTarget.document, mathml, wirisProperties, language); + wrs_insertElementOnSelection(e.node, focusElement, windowTarget); + } + + if (wrs_fireEventListeners('onAfterFormulaInsertion', e)) { + return; + } +} + +/** + * Inserts or modifies formulas or CAS on a textarea. + * @param {object} textarea Target + * @param {string} text Text to add in the textarea. For example, if you want to add the link to the image, you can call this function as wrs_updateTextarea(textarea, wrs_createImageSrc(mathml)); + * @ignore + */ +function wrs_updateTextarea(textarea, text) { + if (textarea && text) { + textarea.focus(); + + if (textarea.selectionStart != null) { + var selectionEnd = textarea.selectionEnd; + textarea.value = textarea.value.substring(0, textarea.selectionStart) + text + textarea.value.substring(textarea.selectionEnd, textarea.value.length); + textarea.selectionEnd = selectionEnd + text.length; + } + else { + var selection = document.selection.createRange(); + selection.text = text; + } + } +} + +/** + * Modifies existing formula on a textarea. + * @param {object} textarea Target + * @param {string} text Text to add in the textarea. For example, if you want to add the link to the image, you can call this function as wrs_updateTextarea(textarea, wrs_createImageSrc(mathml)); + * @param {number} start Beggining index from textarea where it needs to be replaced by text. + * @param {number} end Ending index from textarea where it needs to be replaced by text + * @ignore + */ +function wrs_updateExistingFormulaOnTextarea(textarea, text, start, end) { + textarea.focus(); + textarea.value = textarea.value.substring(0, start) + text + textarea.value.substring(end, textarea.value.length); + textarea.selectionEnd = start + text.length; +} + +/** + * URL decode function. + * @param {string} input String to be decoded + * @return {string} decode string. + * @ignore + */ +function wrs_urldecode(input) { + return decodeURIComponent(input); +} + +/** + * URL encode function. + * @param {string} clearString Input string to be encoded + * @return {string} encoded string. + * @ignore + */ +function wrs_urlencode(clearString) { + var output = ''; + // Method encodeURIComponent doesn't encode !'()*~ . + output = encodeURIComponent(clearString); + return output; +} + +function wrs_addArgument(path,key,value) { + var sep; + if (path.indexOf("?") > 0) { + sep = "&"; + } else { + sep = "?"; + } + return path + sep + key + "=" + value; +} + +function wrs_urlToAssArray(url) { + var i; + i = url.indexOf("?"); + if (i > 0) { + var query = url.substring(i + 1); + var ss = query.split("&"); + var h = new Object(); + for (i = 0; i < ss.length; i++) { + var s = ss[i]; + var kv = s.split("="); + if (kv.length > 1) { + h[kv[0]] = decodeURIComponent(kv[1].replace(/\+/g, ' ')); + } + } + return h; + } else { + return new Object(); + } +} + +function wrs_setImgSize(img, url, json) { + + if (json) { + // Cleaning data:image/png;base64. + if (_wrs_conf_imageFormat == 'svg') { + // SVG format. + // If SVG is encoded in base64 we need to convert the base64 bytes into a SVG string. + if (_wrs_conf_saveMode != 'base64') { + var ar = getMetricsFromSvgString(url); + } else { + var base64String = img.src.substr( img.src.indexOf('base64,') + 7, img.src.length); + var svgString = ''; + var bytes = wrs_b64ToByteArray(base64String, base64String.length); + for (var i = 0; i < bytes.length; i++) { + svgString += String.fromCharCode(bytes[i]); + } + var ar = getMetricsFromSvgString(svgString); + } + // PNG format: we store all metrics information in the first 88 bytes. + } else { + var base64String = img.src.substr( img.src.indexOf('base64,') + 7, img.src.length); + var bytes = wrs_b64ToByteArray(base64String, 88); + var ar = wrs_getMetricsFromBytes(bytes); + } + // Backwards compatibility: we store the metrics into createimage response. + } else { + var ar = wrs_urlToAssArray(url); + } + var width = ar['cw']; + if (!width) { + return; + } + var height = ar['ch']; + var baseline = ar['cb']; + var dpi = ar['dpi']; + if (dpi) { + width = width * 96 / dpi; + height = height * 96 / dpi; + baseline = baseline * 96 / dpi; + } + img.width = width; + img.height = height; + img.style.verticalAlign = "-" + (height - baseline) + "px"; +} + +function wrs_fixAfterResize(img) { + img.removeAttribute('style'); + img.removeAttribute('width'); + img.removeAttribute('height'); + // In order to avoid resize with max-width css property. + img.style.maxWidth = 'none'; + if (_wrs_conf_setSize) { + if (img.src.indexOf("data:image") != -1) { + if (_wrs_conf_imageFormat == 'svg') { + // ...data:image/svg+xml;charset=utf8, = 32. + var svg = wrs_urldecode(img.src.substring(32, img.src.length)) + wrs_setImgSize(img, svg, true); + } else { + // ...data:image/png;base64, == 22. + var base64 = img.src.substring(22,img.src.length); + wrs_setImgSize(img, base64, true); + } + } else { + wrs_setImgSize(img,img.src); + } + } +} + +function wrs_initSetSize() { + // Override _wrs_conf_setSize to align formulas when xml or safeXml mode are enabled. + _wrs_conf_setSize = _wrs_conf_setSize || _wrs_conf_saveMode == 'xml' || _wrs_conf_saveMode == 'safeXml' || (_wrs_conf_saveMode == 'base64' && _wrs_conf_editMode == 'default') + || (_wrs_conf_saveMode == 'image' && _wrs_conf_imageFormat == 'svg'); +} + +/** + * Loads a set of global variables containing server configuration. + * This method calls to configurationjs service, converting the response + * JSON into javascript variables + * @ignore + */ +function wrs_loadConfiguration() { + if (typeof _wrs_conf_path == 'undefined') { + _wrs_conf_path = _wrs_corePath; + } + + var httpRequest = typeof XMLHttpRequest != 'undefined' ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); + var configUrl = _wrs_int_conf_file.indexOf("/") == 0 || _wrs_int_conf_file.indexOf("http") == 0 ? _wrs_int_conf_file : wrs_concatenateUrl(_wrs_conf_path, _wrs_int_conf_file); + httpRequest.open('GET', configUrl, false); + httpRequest.send(null); + + var jsonConfiguration = JSON.parse(httpRequest.responseText); + + // JSON structure: {{jsVariableName, jsVariableValue}}. + + var variables = Object.keys(jsonConfiguration); + + for (var variable in variables) { + window[variables[variable]] = jsonConfiguration[variables[variable]]; + } + + // Services path (tech dependant). + wrs_loadServicePaths(configUrl); + + // End configuration. + var _wrs_conf_configuration_loaded = true; + if (typeof _wrs_conf_core_loaded != 'undefined') { + _wrs_conf_plugin_loaded = true; + } +} + +function wrs_loadServicePaths(url) { + // Services path (tech dependant). + _wrs_conf_createimagePath = url.replace('configurationjs', 'createimage'); + _wrs_conf_showimagePath = url.replace('configurationjs', 'showimage'); + _wrs_conf_editorPath = url.replace('configurationjs', 'editor'); + _wrs_conf_CASPath = url.replace('configurationjs', 'cas'); + _wrs_conf_createcasimagePath = url.replace('configurationjs', 'createcasimage'); + _wrs_conf_getmathmlPath = url.replace('configurationjs', 'getmathml'); + _wrs_conf_servicePath = url.replace('configurationjs', 'service'); +} + +var _wrs_conf_plugin_loaded = true; + +function wrs_loadLangFile() { + if (_wrs_conf_core_loaded) { + _wrs_stringManager = new StringManager(); + // When a language is not defined, put english (en) as default. + if (typeof _wrs_int_langCode == 'undefined' || _wrs_int_langCode == null) { + _wrs_int_langCode = 'en'; + } + + var langArray = _wrs_languages.split(','); + + if (langArray.indexOf(_wrs_int_langCode) == -1) { + _wrs_int_langCode = _wrs_int_langCode.substr(0,2); + } + + if (langArray.indexOf(_wrs_int_langCode) == -1) { + _wrs_int_langCode = 'en'; + } + + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = _wrs_corePath + "/lang/" + _wrs_int_langCode + "/strings.js"; + // When strings are loaded, it loads into stringManager + script.onload = function() { + + _wrs_stringManager.loadStrings(wrs_strings); + // Unseting global language strings array to prevent access. + wrs_strings = null; + }; + document.getElementsByTagName('head')[0].appendChild(script); + } else { + setTimeout(wrs_loadLangFile, 100); + } +} + +function wrs_concatenateUrl(path1, path2) { + var separator = ""; + if ((path1.indexOf("/") != path1.length) && (path2.indexOf("/") != 0)) { + separator = "/"; + } + return (path1 + separator + path2).replace(/([^:]\/)\/+/g, "$1"); +} + +// Loading javascript configuration. +if (typeof _wrs_conf_configuration_loaded == 'undefined') { + wrs_loadConfiguration(); +} else { + var configUrl = _wrs_int_conf_file.indexOf("/") == 0 || _wrs_int_conf_file.indexOf("http") == 0 ? _wrs_int_conf_file : wrs_concatenateUrl(_wrs_conf_path, _wrs_int_conf_file); + // If javascript configuration is loaded we need to load service paths manually. + wrs_loadServicePaths(configUrl); + _wrs_conf_plugin_loaded = true; +} + +/** + * Populates LaTeX cache into _wrs_int_LatexCache global variable. + * + * @param {string}latex LaTeX code (with $$ separators) + * @param {string} mathml matml LaTeX translation. + * @ignore + */ +function wrs_populateLatexCache(latex, mathml) { + if (mathml.indexOf('semantics') == -1 && mathml.indexOf('annotation') == -1 ) { + mathml = wrs_insertSemanticsMathml(mathml, latex); + } + if (!_wrs_int_LatexCache.hasOwnProperty(latex)) { + _wrs_int_LatexCache[latex] = mathml; + } +} + +/** + * Populates Non-LaTeX cache into _wrs_int_nonLatexCache global variable. + * Non-LaTeX is called to all the mathmls without LaTeX translation. + * + * @param {string}latex Non-LaTeX code. + * @param {string} mathml matml associated. + * @ignore + */ +function wrs_populateNonLatexCache(latex, mathml) { + if (!_wrs_int_LatexCache.hasOwnProperty(latex)) { + _wrs_int_nonLatexCache[latex] = mathml; + } +} + +/** + * Puts into _wrs_int_AccessibleCache global variable dictionary the pair mathml=>accessibleText. + * + * @param {string} mathml MatML text. + * @param {string} accessibleText Image accessible text + * @ignore + */ +function wrs_populateAccessibleCache(mathml, accessibleText) { + if (!_wrs_int_AccessibleCache.hasOwnProperty(mathml)) { + _wrs_int_AccessibleCache[mathml] = accessibleText; + } +} + +/** + * Add annotation tag to mathml without it (mathml comes from LaTeX string) + * @param {string} mathml MathML code generated by a LaTeX string. + * @param {string} latex Original LaTeX string + * @param {string} withoutLatexTranslate True if not exists latex translation from mathml. + * @return {string} new mathml containing LaTeX code on annotation tag. + * @ignore + */ +function wrs_insertSemanticsMathml(mathml, latex) { + + // If latex is empty, insert semantics doesn't provide information. We can avoid semantics insertion and return the mathml. + if (latex == "") { + return mathml; + } + + var firstEndTag = '>'; + var mathTagEnd = '<' + '/math' + '>'; + var openSemantics = '<' + 'semantics' + '>'; + var closeSemantics = '<' + '/semantics' + '>'; + var openTarget = ''; + var closeTarget = '<' + '/annotation' + '>'; + var mrowOpen = ''; + var mrowClose = ''; + + var indexMathBegin = mathml.indexOf(firstEndTag); + var indexMathEnd = mathml.indexOf(mathTagEnd); + var mathBeginExists = mathml.substring(mathml.indexOf('<'), mathml.indexOf('>')).indexOf('math'); + + if (indexMathBegin != -1 && indexMathEnd != -1 && mathBeginExists) { + var mathmlContent = mathml.substring(indexMathBegin + 1, indexMathEnd); + if (mathmlContent.indexOf(mrowOpen) != 0) { + var mathmlContentSemantics = openSemantics + mrowOpen + mathmlContent + mrowClose + openTarget + latex + closeTarget + closeSemantics; + } else { + var mathmlContentSemantics = openSemantics + mathmlContent + openTarget + latex + closeTarget + closeSemantics; + } + return mathml.replace(mathmlContent, mathmlContentSemantics); + } else { + return mathml; + } + +} + +/** + * Removes annotation tag to mathml. + * @param {*} mathml Valid MathML. + */ +function wrs_removeSemanticsMathml(mathml) { + var mathTagEnd = '<' + '/math' + '>'; + var openSemantics = '<' + 'semantics' + '>'; + var openAnnotation = ''; + + var mathmlWithoutSemantics = mathml; + var startSemantics = mathml.indexOf(openSemantics); + if (startSemantics != -1) { + var startAnnotation = mathml.indexOf(openAnnotation, startSemantics + openSemantics.length); + if (startAnnotation != -1) { + mathmlWithoutSemantics = mathml.substring(0, startSemantics) + mathml.substring(startSemantics + openSemantics.length, startAnnotation) + mathTagEnd; + } + } + + return mathmlWithoutSemantics; +} + +/** + * Transform html img tags inside a html code to mathml, base64 img tags (i.e with base64 on src) or showimage img tags (i.e with showimage.php on src) + * + * @param {String} code html code + * @param {String} mode base642showimage or img2mathml or img264 transform. + * @return {String} html code transformed. + * @ignore + */ +function wrs_codeImgTransform(code, mode) { + var output = ''; + + var endPosition = 0; + var pattern = /') { + endPosition = i + 1; + } + + ++i; + } + + if (endPosition < startPosition) { // The img tag is stripped. + output += code.substring(startPosition, code.length); + return output; + } + var imgCode = code.substring(startPosition, endPosition); + var imgObject = wrs_createObject(imgCode); + var xmlCode = imgObject.getAttribute(_wrs_conf_imageMathmlAttribute); + var convertToXml; + var convertToSafeXml; + + if (mode == 'base642showimage') { + if (xmlCode == null) { + xmlCode = imgObject.getAttribute('alt'); + } + xmlCode = wrs_mathmlDecode(xmlCode); + imgCode = wrs_mathmlToImgObject(document, xmlCode, null, null); + output += wrs_createObjectCode(imgCode); + } else if (mode == 'img2mathml') { + if (window._wrs_conf_saveMode) { + if (_wrs_conf_saveMode == 'safeXml') { + convertToXml = true; + convertToSafeXml = true; + } + else if (_wrs_conf_saveMode == 'xml') { + convertToXml = true; + convertToSafeXml = false; + } + } + output += wrs_getWIRISImageOutput(imgCode, convertToXml, convertToSafeXml); + } else if (mode == 'img264') { + + if (xmlCode == null) { + xmlCode = imgObject.getAttribute('alt'); + } + xmlCode = wrs_mathmlDecode(xmlCode); + + var properties = {}; + properties['base64'] = 'true'; + imgCode = wrs_mathmlToImgObject(document, xmlCode, properties, null) + // Metrics. + wrs_setImgSize(imgCode, imgCode.src, true); + + output += wrs_createObjectCode(imgCode); + } + } + output += code.substring(endPosition, code.length); + return output; +} + +/** + * Decode a base64 to its numeric value + * + * @param {String} el base64 character. + * @return {int} base64 char numeric value. + * @ignore + */ +function wrs_decode64(el) { + + var PLUS = '+'.charCodeAt(0); + var SLASH = '/'.charCodeAt(0); + var NUMBER = '0'.charCodeAt(0); + var LOWER = 'a'.charCodeAt(0); + var UPPER = 'A'.charCodeAt(0); + var PLUS_URL_SAFE = '-'.charCodeAt(0); + var SLASH_URL_SAFE = '_'.charCodeAt(0); + var code = el.charCodeAt(0); + + if (code === PLUS || code === PLUS_URL_SAFE) { + return 62; // Char '+'. + } + if (code === SLASH || code === SLASH_URL_SAFE){ + return 63 // Char '/'. + } + if (code < NUMBER){ + return -1 // No match. + } + if (code < NUMBER + 10){ + return code - NUMBER + 26 + 26 + } + if (code < UPPER + 26){ + return code - UPPER + } + if (code < LOWER + 26){ + return code - LOWER + 26 + } +} + +/** + * Converts a base64 string to a array of bytes. + * @param {String} b64String base64 string. + * @param {int} len dimension of byte array (by default whole string). + * @return {Array} Byte array. + * @ignore + */ +function wrs_b64ToByteArray(b64String, len) { + + var tmp; + + if (b64String.length % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4'); // Tipped base64. Length is fixed. + } + + var arr = new Array() + + if (!len) { // All b64String string. + var placeHolders = b64String.charAt(b64String.length - 2) === '=' ? 2 : b64String.charAt(b64String.length - 1) === '=' ? 1 : 0 + var l = placeHolders > 0 ? b64String.length - 4 : b64String.length; + } else { + var l = len; + } + + for (var i = 0; i < l; i += 4) { + // Ignoring code checker standards (bitewise operators). + // See https://tracker.moodle.org/browse/CONTRIB-5862 for further information. + // @codingStandardsIgnoreStart + tmp = (wrs_decode64(b64String.charAt(i)) << 18) | (wrs_decode64(b64String.charAt(i + 1)) << 12) | (wrs_decode64(b64String.charAt(i + 2)) << 6) | wrs_decode64(b64String.charAt(i + 3)); + + arr.push((tmp >> 16) & 0xFF); + arr.push((tmp >> 8) & 0xFF); + arr.push(tmp & 0xFF); + // @codingStandardsIgnoreEnd + } + + if (placeHolders) { + if (placeHolders === 2) { + // Ignoring code checker standards (bitewise operators). + // @codingStandardsIgnoreStart + tmp = (wrs_decode64(b64String.charAt(i)) << 2) | (wrs_decode64(b64String.charAt(i + 1)) >> 4); + arr.push(tmp & 0xFF) + } else if (placeHolders === 1) { + tmp = (wrs_decode64(b64String.charAt(i)) << 10) | (wrs_decode64(b64String.charAt(i + 1)) << 4) | (wrs_decode64(b64String.charAt(i + 2)) >> 2) + arr.push((tmp >> 8) & 0xFF); + arr.push(tmp & 0xFF); + // @codingStandardsIgnoreEnd + } + } + + return arr +} + +/** + * Returns the first 32-bit signed integer from a byte array. + * @param {Array} bytes array of bytes. + * @return {int} 32-bit signed integer. + * @ignore + */ +function wrs_readInt32(bytes) { + if (bytes.length < 4) { + return false; + } + var int32 = bytes.splice(0,4); + // @codingStandardsIgnoreStart + return (int32[0] << 24 | int32[1] << 16 | int32[2] << 8 | int32[3] << 0); + // @codingStandardsIgnoreEnd +} + +/** + * Read the first byte from a byte array. + * @param {array} bytes byte array. + * @return {int} first byte of the byte array. + * @ignore + */ +function wrs_readByte(bytes) { + // @codingStandardsIgnoreStart + return bytes.shift() << 0; + // @codingStandardsIgnoreEnd + +} + +/** + * Read an arbitrary number of bytes, from a fixed position on a byte array. + * @param {array} bytes byte array. + * @param {int} post start position. + * @param {int} len number of bytes to read. + * @return {array} byte array. + * @ignore + */ +function wrs_readBytes(bytes, pos, len) { + return bytes.splice(pos, len); +} + +/** + * Get metrics (width, height, baseline and dpi) from a png's byte array. + * @param {array} bytes png byte array. + * @return {array} An array containging the png's metrics. + * @ignore + */ +function wrs_getMetricsFromBytes(bytes) { + wrs_readBytes(bytes, 0, 8); + var alloc = 10; + var i = 0; + while (bytes.length >= 4) { + var len = wrs_readInt32(bytes); + var typ = wrs_readInt32(bytes); + if (typ == 0x49484452) { + var width = wrs_readInt32(bytes); + var height = wrs_readInt32(bytes); + // Read 5 bytes. + wrs_readInt32(bytes); + wrs_readByte(bytes); + } else if (typ == 0x62615345) { // Baseline: 'baSE'. + var baseline = wrs_readInt32(bytes); + } else if (typ == 0x70485973) { // Dpis: 'pHYs'. + var dpi = wrs_readInt32(bytes); + dpi = (Math.round(dpi / 39.37)); + wrs_readInt32(bytes); + wrs_readByte(bytes); + } + wrs_readInt32(bytes); + } + + if (typeof width != 'undefined') { + var arr = new Array(); + arr['cw'] = width; + arr['ch'] = height; + arr['dpi'] = dpi; + if (baseline) { + arr['cb'] = baseline; + } + + return arr; + } +} + +function getMetricsFromSvgString(svgString) { + var first = svgString.indexOf('height="'); + var last = svgString.indexOf('"',first + 8, svgString.length); + var height = svgString.substring(first + 8, last); + + first = svgString.indexOf('width="'); + last = svgString.indexOf('"',first + 7, svgString.length); + var width = svgString.substring(first + 7, last); + + first = svgString.indexOf('wrs:baseline="'); + last = svgString.indexOf('"',first + 14, svgString.length); + var baseline = svgString.substring(first + 14, last); + + if (typeof(width != 'undefined')) { + var arr = new Array(); + arr['cw'] = width; + arr['ch'] = height; + if (typeof baseline != 'undefined') { + arr['cb'] = baseline + } + + return arr; + } + +} + +/** + * Get custom active editor + * @ignore + */ +function wrs_int_getCustomEditorEnabled() { + var customEditorEnabled = null; + for (var key in _wrs_int_customEditors) { + if (_wrs_int_customEditors[key].enabled) { + customEditorEnabled = _wrs_int_customEditors[key]; + break; + } + } + + return customEditorEnabled; +} + + +/** + * Disable all custom editors + * @ignore + */ +function wrs_int_disableCustomEditors(){ + Object.keys(_wrs_int_customEditors).forEach(function(key) { + _wrs_int_customEditors[key].enabled = false; + }); +} + +/** + * Enable a custom editor + * @param {string} editor a custom editor to be enabled + * @ignore + */ +function wrs_int_enableCustomEditor(editor) { + // Only one custom editor enabled at the same time. + wrs_int_disableCustomEditors(); + if (_wrs_int_customEditors[editor]) { + _wrs_int_customEditors[editor].enabled = true; + } +} + +/** + * Convert a hash to string sorting keys to get a deterministic output + * @param {hash} h a key-value hash + * @return{string} A string with the form key1=value1...keyn=valuen + * @ignore + */ +function wrs_propertiesToString(h) { + // 1. Sort keys. We sort the keys because we want a deterministic output. + var keys = [] + for (var key in h) { + if (h.hasOwnProperty(key)) { + keys.push(key); + } + } + + var n = keys.length; + for (var i = 0; i < n; i++) { + for (var j = i + 1; j < n; j++) { + var s1 = keys[i]; + var s2 = keys[j]; + if (wrs_compareStrings(s1,s2) > 0) { + // Swap. + keys[i] = s2; + keys[j] = s1; + } + } + } + + // 2. Generate output. + var output = ''; + for (var i = 0; i < n; i++) { + var key = keys[i]; + output += key; + output += "="; + var value = h[key]; + value = value.replace("\\", "\\\\"); + value = value.replace("\n", "\\n"); + value = value.replace("\r", "\\r"); + value = value.replace("\t", "\\t"); + + output += value; + output += "\n"; + } + return output; +} + +/** + * Compare two strings using charCodeAt method + * @param {string} a first string to compare. + * @param {string} b second string to compare + * @return {int} the int difference between a and b + * @ignore + */ +function wrs_compareStrings(a, b){ + var i; + var an = a.length; + var bn = b.length; + var n = (an > bn) ? bn : an; + for(i = 0; i < n; i++){ + var c = wrs_fixedCharCodeAt(a,i) - wrs_fixedCharCodeAt(b,i); + if(c != 0) { + return c; + } + } + return a.length - b.length; +} + +// Polyfills. + +if (!Object.keys) { + Object.keys = (function () { + 'use strict'; + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function (obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = [], prop, i; + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + }()); +} + +/*! http://mths.be/codepointat v0.1.0 by @mathias */ +if (!String.prototype.codePointAt) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var codePointAt = function(position) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + var size = string.length; + // `ToInteger` + var index = position ? Number(position) : 0; + if (index != index) { // better `isNaN` + index = 0; + } + // Account for out-of-bounds indices: + if (index < 0 || index >= size) { + return undefined; + } + // Get the first code unit + var first = string.charCodeAt(index); + var second; + if ( // check if it’s the start of a surrogate pair + first >= 0xD800 && first <= 0xDBFF && // high surrogate + size > index + 1 // there is a next code unit + ) { + second = string.charCodeAt(index + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate + // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } + } + return first; + }; + if (Object.defineProperty) { + Object.defineProperty(String.prototype, 'codePointAt', { + 'value': codePointAt, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.codePointAt = codePointAt; + } + }()); +} + +/** + * Add a new callback to a MathType listener. + * @param {object} listener an Object containing listener name and a callback. + * @tutorial tutorial + */ +function wrs_addPluginListener(listener) { + wrs_pluginListeners.push(listener); +} + +/** + * Get the base URL (i.e the URL on core.js lives). + * @ignore + */ +function wrs_getServerPath() { + var url = _wrs_corePath; + var hostNameIndex = url.indexOf("/", url.indexOf("/") + 2); + return url.substr(0, hostNameIndex); +} + +/** + * This method updates all services paths if there are relative with the absolute + * URL path. + * @ignore + */ +function wrs_updateContextPath() { + if (typeof _wrs_conf_plugin_loaded == 'undefined') { + setTimeout(wrs_updateContextPath, 100); + } else { + if (_wrs_conf_showimagePath.indexOf("/") == 0) { + var serverPath = wrs_getServerPath() + _wrs_conf_showimagePath = serverPath + _wrs_conf_showimagePath; + _wrs_conf_editorPath = serverPath + _wrs_conf_editorPath; + _wrs_conf_CASPath = serverPath + _wrs_conf_CASPath; + _wrs_conf_createimagePath = serverPath + _wrs_conf_createimagePath; + _wrs_conf_createcasimagePath = serverPath + _wrs_conf_createcasimagePath; + _wrs_conf_getmathmlPath = serverPath + _wrs_conf_getmathmlPath; + _wrs_conf_servicePath = serverPath + _wrs_conf_servicePath; + } + } +} + +wrs_updateContextPath(); + +// Production steps of ECMA-262, Edition 5, 15.4.4.18 +// Reference: http://es5.github.io/#x15.4.4.18. +if (!Array.prototype.forEach) { + + Array.prototype.forEach = function(callback, thisArg) { + + var T, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Let O be the result of calling ToObject passing the |this| value as the argument. + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". + // 3. Let len be ToUint32(lenValue). + + // @codingStandardsIgnoreStart + var len = O.length >>> 0; + // @codingStandardsIgnoreEnd + + // 4. If IsCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 . + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + + // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Let k be 0. + k = 0; + + // 7. Repeat, while k < len. + while (k < len) { + + var kValue; + + // A. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // B. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk + // This step can be combined with c + // C. If kPresent is true. + if (k in O) { + + // I. Let kValue be the result of calling the Get internal method of O with argument Pk. + kValue = O[k]; + + // II. Call the Call internal method of callback with T as the this value and + // argument list containing kValue, k, and O. + callback.call(T, kValue, k, O); + } + // D. Increase k by 1. + k++; + } + // 8. return undefined. + }; +} +// @codingStandardsIgnoreStart +(function(){ +var HxOverrides = function() { } +HxOverrides.__name__ = true; +HxOverrides.dateStr = function(date) { + var m = date.getMonth() + 1; + var d = date.getDate(); + var h = date.getHours(); + var mi = date.getMinutes(); + var s = date.getSeconds(); + return date.getFullYear() + "-" + (m < 10?"0" + m:"" + m) + "-" + (d < 10?"0" + d:"" + d) + " " + (h < 10?"0" + h:"" + h) + ":" + (mi < 10?"0" + mi:"" + mi) + ":" + (s < 10?"0" + s:"" + s); +} +HxOverrides.strDate = function(s) { + switch(s.length) { + case 8: + var k = s.split(":"); + var d = new Date(); + d.setTime(0); + d.setUTCHours(k[0]); + d.setUTCMinutes(k[1]); + d.setUTCSeconds(k[2]); + return d; + case 10: + var k = s.split("-"); + return new Date(k[0],k[1] - 1,k[2],0,0,0); + case 19: + var k = s.split(" "); + var y = k[0].split("-"); + var t = k[1].split(":"); + return new Date(y[0],y[1] - 1,y[2],t[0],t[1],t[2]); + default: + throw "Invalid date format : " + s; + } +} +HxOverrides.cca = function(s,index) { + var x = s.charCodeAt(index); + if(x != x) return undefined; + return x; +} +HxOverrides.substr = function(s,pos,len) { + if(pos != null && pos != 0 && len != null && len < 0) return ""; + if(len == null) len = s.length; + if(pos < 0) { + pos = s.length + pos; + if(pos < 0) pos = 0; + } else if(len < 0) len = s.length + len - pos; + return s.substr(pos,len); +} +HxOverrides.remove = function(a,obj) { + var i = 0; + var l = a.length; + while(i < l) { + if(a[i] == obj) { + a.splice(i,1); + return true; + } + i++; + } + return false; +} +HxOverrides.iter = function(a) { + return { cur : 0, arr : a, hasNext : function() { + return this.cur < this.arr.length; + }, next : function() { + return this.arr[this.cur++]; + }}; +} +var IntIter = function(min,max) { + this.min = min; + this.max = max; +}; +IntIter.__name__ = true; +IntIter.prototype = { + next: function() { + return this.min++; + } + ,hasNext: function() { + return this.min < this.max; + } + ,__class__: IntIter +} +var Std = function() { } +Std.__name__ = true; +Std["is"] = function(v,t) { + return js.Boot.__instanceof(v,t); +} +Std.string = function(s) { + return js.Boot.__string_rec(s,""); +} +Std["int"] = function(x) { + return x | 0; +} +Std.parseInt = function(x) { + var v = parseInt(x,10); + if(v == 0 && (HxOverrides.cca(x,1) == 120 || HxOverrides.cca(x,1) == 88)) v = parseInt(x); + if(isNaN(v)) return null; + return v; +} +Std.parseFloat = function(x) { + return parseFloat(x); +} +Std.random = function(x) { + return Math.floor(Math.random() * x); +} +var com = com || {} +if(!com.wiris) com.wiris = {} +if(!com.wiris.js) com.wiris.js = {} +com.wiris.js.JsPluginTools = function() { + this.tryReady(); +}; +com.wiris.js.JsPluginTools.__name__ = true; +com.wiris.js.JsPluginTools.main = function() { + var ev; + ev = com.wiris.js.JsPluginTools.getInstance(); + haxe.Timer.delay($bind(ev,ev.tryReady),100); +} +com.wiris.js.JsPluginTools.getInstance = function() { + if(com.wiris.js.JsPluginTools.instance == null) com.wiris.js.JsPluginTools.instance = new com.wiris.js.JsPluginTools(); + return com.wiris.js.JsPluginTools.instance; +} +com.wiris.js.JsPluginTools.bypassEncapsulation = function() { + if(window.com == null) window.com = { }; + if(window.com.wiris == null) window.com.wiris = { }; + if(window.com.wiris.js == null) window.com.wiris.js = { }; + if(window.com.wiris.js.JsPluginTools == null) window.com.wiris.js.JsPluginTools = com.wiris.js.JsPluginTools.getInstance(); +} +com.wiris.js.JsPluginTools.prototype = { + md5encode: function(content) { + return haxe.Md5.encode(content); + } + ,doLoad: function() { + this.ready = true; + com.wiris.js.JsPluginTools.instance = this; + com.wiris.js.JsPluginTools.bypassEncapsulation(); + } + ,tryReady: function() { + this.ready = false; + if(js.Lib.document.readyState) { + this.doLoad(); + this.ready = true; + } + if(!this.ready) haxe.Timer.delay($bind(this,this.tryReady),100); + } + ,__class__: com.wiris.js.JsPluginTools +} +var haxe = haxe || {} +haxe.Log = function() { } +haxe.Log.__name__ = true; +haxe.Log.trace = function(v,infos) { + js.Boot.__trace(v,infos); +} +haxe.Log.clear = function() { + js.Boot.__clear_trace(); +} +haxe.Md5 = function() { +}; +haxe.Md5.__name__ = true; +haxe.Md5.encode = function(s) { + return new haxe.Md5().doEncode(s); +} +haxe.Md5.prototype = { + doEncode: function(str) { + var x = this.str2blks(str); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var step; + var i = 0; + while(i < x.length) { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + step = 0; + a = this.ff(a,b,c,d,x[i],7,-680876936); + d = this.ff(d,a,b,c,x[i + 1],12,-389564586); + c = this.ff(c,d,a,b,x[i + 2],17,606105819); + b = this.ff(b,c,d,a,x[i + 3],22,-1044525330); + a = this.ff(a,b,c,d,x[i + 4],7,-176418897); + d = this.ff(d,a,b,c,x[i + 5],12,1200080426); + c = this.ff(c,d,a,b,x[i + 6],17,-1473231341); + b = this.ff(b,c,d,a,x[i + 7],22,-45705983); + a = this.ff(a,b,c,d,x[i + 8],7,1770035416); + d = this.ff(d,a,b,c,x[i + 9],12,-1958414417); + c = this.ff(c,d,a,b,x[i + 10],17,-42063); + b = this.ff(b,c,d,a,x[i + 11],22,-1990404162); + a = this.ff(a,b,c,d,x[i + 12],7,1804603682); + d = this.ff(d,a,b,c,x[i + 13],12,-40341101); + c = this.ff(c,d,a,b,x[i + 14],17,-1502002290); + b = this.ff(b,c,d,a,x[i + 15],22,1236535329); + a = this.gg(a,b,c,d,x[i + 1],5,-165796510); + d = this.gg(d,a,b,c,x[i + 6],9,-1069501632); + c = this.gg(c,d,a,b,x[i + 11],14,643717713); + b = this.gg(b,c,d,a,x[i],20,-373897302); + a = this.gg(a,b,c,d,x[i + 5],5,-701558691); + d = this.gg(d,a,b,c,x[i + 10],9,38016083); + c = this.gg(c,d,a,b,x[i + 15],14,-660478335); + b = this.gg(b,c,d,a,x[i + 4],20,-405537848); + a = this.gg(a,b,c,d,x[i + 9],5,568446438); + d = this.gg(d,a,b,c,x[i + 14],9,-1019803690); + c = this.gg(c,d,a,b,x[i + 3],14,-187363961); + b = this.gg(b,c,d,a,x[i + 8],20,1163531501); + a = this.gg(a,b,c,d,x[i + 13],5,-1444681467); + d = this.gg(d,a,b,c,x[i + 2],9,-51403784); + c = this.gg(c,d,a,b,x[i + 7],14,1735328473); + b = this.gg(b,c,d,a,x[i + 12],20,-1926607734); + a = this.hh(a,b,c,d,x[i + 5],4,-378558); + d = this.hh(d,a,b,c,x[i + 8],11,-2022574463); + c = this.hh(c,d,a,b,x[i + 11],16,1839030562); + b = this.hh(b,c,d,a,x[i + 14],23,-35309556); + a = this.hh(a,b,c,d,x[i + 1],4,-1530992060); + d = this.hh(d,a,b,c,x[i + 4],11,1272893353); + c = this.hh(c,d,a,b,x[i + 7],16,-155497632); + b = this.hh(b,c,d,a,x[i + 10],23,-1094730640); + a = this.hh(a,b,c,d,x[i + 13],4,681279174); + d = this.hh(d,a,b,c,x[i],11,-358537222); + c = this.hh(c,d,a,b,x[i + 3],16,-722521979); + b = this.hh(b,c,d,a,x[i + 6],23,76029189); + a = this.hh(a,b,c,d,x[i + 9],4,-640364487); + d = this.hh(d,a,b,c,x[i + 12],11,-421815835); + c = this.hh(c,d,a,b,x[i + 15],16,530742520); + b = this.hh(b,c,d,a,x[i + 2],23,-995338651); + a = this.ii(a,b,c,d,x[i],6,-198630844); + d = this.ii(d,a,b,c,x[i + 7],10,1126891415); + c = this.ii(c,d,a,b,x[i + 14],15,-1416354905); + b = this.ii(b,c,d,a,x[i + 5],21,-57434055); + a = this.ii(a,b,c,d,x[i + 12],6,1700485571); + d = this.ii(d,a,b,c,x[i + 3],10,-1894986606); + c = this.ii(c,d,a,b,x[i + 10],15,-1051523); + b = this.ii(b,c,d,a,x[i + 1],21,-2054922799); + a = this.ii(a,b,c,d,x[i + 8],6,1873313359); + d = this.ii(d,a,b,c,x[i + 15],10,-30611744); + c = this.ii(c,d,a,b,x[i + 6],15,-1560198380); + b = this.ii(b,c,d,a,x[i + 13],21,1309151649); + a = this.ii(a,b,c,d,x[i + 4],6,-145523070); + d = this.ii(d,a,b,c,x[i + 11],10,-1120210379); + c = this.ii(c,d,a,b,x[i + 2],15,718787259); + b = this.ii(b,c,d,a,x[i + 9],21,-343485551); + a = this.addme(a,olda); + b = this.addme(b,oldb); + c = this.addme(c,oldc); + d = this.addme(d,oldd); + i += 16; + } + return this.rhex(a) + this.rhex(b) + this.rhex(c) + this.rhex(d); + } + ,ii: function(a,b,c,d,x,s,t) { + return this.cmn(this.bitXOR(c,this.bitOR(b,~d)),a,b,x,s,t); + } + ,hh: function(a,b,c,d,x,s,t) { + return this.cmn(this.bitXOR(this.bitXOR(b,c),d),a,b,x,s,t); + } + ,gg: function(a,b,c,d,x,s,t) { + return this.cmn(this.bitOR(this.bitAND(b,d),this.bitAND(c,~d)),a,b,x,s,t); + } + ,ff: function(a,b,c,d,x,s,t) { + return this.cmn(this.bitOR(this.bitAND(b,c),this.bitAND(~b,d)),a,b,x,s,t); + } + ,cmn: function(q,a,b,x,s,t) { + return this.addme(this.rol(this.addme(this.addme(a,q),this.addme(x,t)),s),b); + } + ,rol: function(num,cnt) { + return num << cnt | num >>> 32 - cnt; + } + ,str2blks: function(str) { + var nblk = (str.length + 8 >> 6) + 1; + var blks = new Array(); + var _g1 = 0, _g = nblk * 16; + while(_g1 < _g) { + var i = _g1++; + blks[i] = 0; + } + var i = 0; + while(i < str.length) { + blks[i >> 2] |= HxOverrides.cca(str,i) << (str.length * 8 + i) % 4 * 8; + i++; + } + blks[i >> 2] |= 128 << (str.length * 8 + i) % 4 * 8; + var l = str.length * 8; + var k = nblk * 16 - 2; + blks[k] = l & 255; + blks[k] |= (l >>> 8 & 255) << 8; + blks[k] |= (l >>> 16 & 255) << 16; + blks[k] |= (l >>> 24 & 255) << 24; + return blks; + } + ,rhex: function(num) { + var str = ""; + var hex_chr = "0123456789abcdef"; + var _g = 0; + while(_g < 4) { + var j = _g++; + str += hex_chr.charAt(num >> j * 8 + 4 & 15) + hex_chr.charAt(num >> j * 8 & 15); + } + return str; + } + ,addme: function(x,y) { + var lsw = (x & 65535) + (y & 65535); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return msw << 16 | lsw & 65535; + } + ,bitAND: function(a,b) { + var lsb = a & 1 & (b & 1); + var msb31 = a >>> 1 & b >>> 1; + return msb31 << 1 | lsb; + } + ,bitXOR: function(a,b) { + var lsb = a & 1 ^ b & 1; + var msb31 = a >>> 1 ^ b >>> 1; + return msb31 << 1 | lsb; + } + ,bitOR: function(a,b) { + var lsb = a & 1 | b & 1; + var msb31 = a >>> 1 | b >>> 1; + return msb31 << 1 | lsb; + } + ,__class__: haxe.Md5 +} +haxe.Timer = function(time_ms) { + var me = this; + this.id = window.setInterval(function() { + me.run(); + },time_ms); +}; +haxe.Timer.__name__ = true; +haxe.Timer.delay = function(f,time_ms) { + var t = new haxe.Timer(time_ms); + t.run = function() { + t.stop(); + f(); + }; + return t; +} +haxe.Timer.measure = function(f,pos) { + var t0 = haxe.Timer.stamp(); + var r = f(); + haxe.Log.trace(haxe.Timer.stamp() - t0 + "s",pos); + return r; +} +haxe.Timer.stamp = function() { + return new Date().getTime() / 1000; +} +haxe.Timer.prototype = { + run: function() { + } + ,stop: function() { + if(this.id == null) return; + window.clearInterval(this.id); + this.id = null; + } + ,__class__: haxe.Timer +} +var js = js || {} +js.Boot = function() { } +js.Boot.__name__ = true; +js.Boot.__unhtml = function(s) { + return s.split("&").join("&").split("<").join("<").split(">").join(">"); +} +js.Boot.__trace = function(v,i) { + var msg = i != null?i.fileName + ":" + i.lineNumber + ": ":""; + msg += js.Boot.__string_rec(v,""); + var d; + if(typeof(document) != "undefined" && (d = document.getElementById("haxe:trace")) != null) d.innerHTML += js.Boot.__unhtml(msg) + "
"; else if(typeof(console) != "undefined" && console.log != null) console.log(msg); +} +js.Boot.__clear_trace = function() { + var d = document.getElementById("haxe:trace"); + if(d != null) d.innerHTML = ""; +} +js.Boot.isClass = function(o) { + return o.__name__; +} +js.Boot.isEnum = function(e) { + return e.__ename__; +} +js.Boot.getClass = function(o) { + return o.__class__; +} +js.Boot.__string_rec = function(o,s) { + if(o == null) return "null"; + if(s.length >= 5) return "<...>"; + var t = typeof(o); + if(t == "function" && (o.__name__ || o.__ename__)) t = "object"; + switch(t) { + case "object": + if(o instanceof Array) { + if(o.__enum__) { + if(o.length == 2) return o[0]; + var str = o[0] + "("; + s += "\t"; + var _g1 = 2, _g = o.length; + while(_g1 < _g) { + var i = _g1++; + if(i != 2) str += "," + js.Boot.__string_rec(o[i],s); else str += js.Boot.__string_rec(o[i],s); + } + return str + ")"; + } + var l = o.length; + var i; + var str = "["; + s += "\t"; + var _g = 0; + while(_g < l) { + var i1 = _g++; + str += (i1 > 0?",":"") + js.Boot.__string_rec(o[i1],s); + } + str += "]"; + return str; + } + var tostr; + try { + tostr = o.toString; + } catch( e ) { + return "???"; + } + if(tostr != null && tostr != Object.toString) { + var s2 = o.toString(); + if(s2 != "[object Object]") return s2; + } + var k = null; + var str = "{\n"; + s += "\t"; + var hasp = o.hasOwnProperty != null; + for( var k in o ) { ; + if(hasp && !o.hasOwnProperty(k)) { + continue; + } + if(k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") { + continue; + } + if(str.length != 2) str += ", \n"; + str += s + k + " : " + js.Boot.__string_rec(o[k],s); + } + s = s.substring(1); + str += "\n" + s + "}"; + return str; + case "function": + return ""; + case "string": + return o; + default: + return String(o); + } +} +js.Boot.__interfLoop = function(cc,cl) { + if(cc == null) return false; + if(cc == cl) return true; + var intf = cc.__interfaces__; + if(intf != null) { + var _g1 = 0, _g = intf.length; + while(_g1 < _g) { + var i = _g1++; + var i1 = intf[i]; + if(i1 == cl || js.Boot.__interfLoop(i1,cl)) return true; + } + } + return js.Boot.__interfLoop(cc.__super__,cl); +} +js.Boot.__instanceof = function(o,cl) { + try { + if(o instanceof cl) { + if(cl == Array) return o.__enum__ == null; + return true; + } + if(js.Boot.__interfLoop(o.__class__,cl)) return true; + } catch( e ) { + if(cl == null) return false; + } + switch(cl) { + case Int: + return Math.ceil(o%2147483648.0) === o; + case Float: + return typeof(o) == "number"; + case Bool: + return o === true || o === false; + case String: + return typeof(o) == "string"; + case Dynamic: + return true; + default: + if(o == null) return false; + if(cl == Class && o.__name__ != null) return true; else null; + if(cl == Enum && o.__ename__ != null) return true; else null; + return o.__enum__ == cl; + } +} +js.Boot.__cast = function(o,t) { + if(js.Boot.__instanceof(o,t)) return o; else throw "Cannot cast " + Std.string(o) + " to " + Std.string(t); +} +js.Lib = function() { } +js.Lib.__name__ = true; +js.Lib.debug = function() { + debugger; +} +js.Lib.alert = function(v) { + alert(js.Boot.__string_rec(v,"")); +} +js.Lib.eval = function(code) { + return eval(code); +} +js.Lib.setErrorHandler = function(f) { + js.Lib.onerror = f; +} +var $_; +function $bind(o,m) { var f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; return f; }; +if(Array.prototype.indexOf) HxOverrides.remove = function(a,o) { + var i = a.indexOf(o); + if(i == -1) return false; + a.splice(i,1); + return true; +}; else null; +Math.__name__ = ["Math"]; +Math.NaN = Number.NaN; +Math.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; +Math.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; +Math.isFinite = function(i) { + return isFinite(i); +}; +Math.isNaN = function(i) { + return isNaN(i); +}; +String.prototype.__class__ = String; +String.__name__ = true; +Array.prototype.__class__ = Array; +Array.__name__ = true; +Date.prototype.__class__ = Date; +Date.__name__ = ["Date"]; +var Int = { __name__ : ["Int"]}; +var Dynamic = { __name__ : ["Dynamic"]}; +var Float = Number; +Float.__name__ = ["Float"]; +var Bool = Boolean; +Bool.__ename__ = ["Bool"]; +var Class = { __name__ : ["Class"]}; +var Enum = { }; +var Void = { __ename__ : ["Void"]}; +if(typeof document != "undefined") js.Lib.document = document; +if(typeof window != "undefined") { + js.Lib.window = window; + js.Lib.window.onerror = function(msg,url,line) { + var f = js.Lib.onerror; + if(f == null) return false; + return f(msg,[url + ":" + line]); + }; +} +com.wiris.js.JsPluginTools.main(); +delete Array.prototype.__class__; }()); +// @codingStandardsIgnoreEnd +/** + * Modal window constructor + * @param {Object} editorAttributes Editor attributes (width, height)... + * @ignore + */ +class ModalWindow { + + constructor(editorAttributes) { + // EditorAtributes + // TODO: Remove the argument: we need to find a way to detect mobile devices + // without known anything about editor. + this.editorAttributes = editorAttributes + + // Metrics + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + var isIOS = ((ua.indexOf("ipad") > -1) || (ua.indexOf("iphone") > -1)); + this.iosSoftkeyboardOpened = false; + this.iosMeasureUnit = ua.indexOf("crios") == -1 ? "%" : "vh"; + this.iosDivHeight = "100" + this.iosMeasureUnit; + + var deviceWidth = window.outerWidth; + var deviceHeight = window.outerHeight; + + var landscape = deviceWidth > deviceHeight; + var portrait = deviceWidth < deviceHeight; + + var editorWidth = editorAttributes.split(' ').join('').split(',')[0].split("=")[1]; + var editorHeight = editorAttributes.split(' ').join('').split(',')[1].split("=")[1]; + + // TODO: Detect isMobile without using editor metrics. + var isMobile = (landscape && editorHeight > deviceHeight) || (portrait && editorWidth > deviceWidth) ? true : false; + + // Device object properties. + + this.deviceProperties = { + orientation : landscape ? 'landscape' : 'portait', + isAndroid : isAndroid ? true : false, + isIOS : isIOS ? true : false, + isMobile : isMobile, + isDesktop : !isMobile && !isIOS && !isAndroid + }; + + this.properties = { + created : false, + state : '', + previousState : '', + position : {bottom: 0, right: 10}, + size : {height: 338, width: 580} + }; + + var attributes = {}; + attributes.class = 'wrs_modal_overlay'; + attributes.id = attributes.class + '_id'; + this.overlay = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_title_bar'; + attributes.id = attributes.class + '_id'; + this.titleBar = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_title'; + attributes.id = attributes.class + '_id'; + this.title = wrs_createElement('div', attributes); + this.title.innerHTML = ''; + + attributes = {}; + attributes.class = 'wrs_modal_close_button'; + attributes.id = attributes.class + '_id'; + attributes.title = _wrs_stringManager.getString('close'); + this.closeDiv = wrs_createElement('a', attributes);; + this.closeDiv.setAttribute('role','button'); + + attributes = {}; + attributes.class = 'wrs_modal_stack_button'; + attributes.id = attributes.class + '_id'; + attributes.title = "Exit full-screen"; + this.stackDiv = wrs_createElement('a', attributes); + this.stackDiv.setAttribute('role','button'); + + attributes = {}; + attributes.class = 'wrs_modal_maximize_button'; + attributes.id = attributes.class + '_id'; + attributes.title = _wrs_stringManager.getString('fullscreen'); + this.maximizeDiv = wrs_createElement('a', attributes); + this.maximizeDiv.setAttribute('role','button'); + + attributes = {}; + attributes.class = 'wrs_modal_minimize_button'; + attributes.id = attributes.class + '_id'; + attributes.title = _wrs_stringManager.getString('minimise'); + this.minimizeDiv = wrs_createElement('a', attributes); + this.minimizeDiv.setAttribute('role','button'); + + attributes = {}; + attributes.class = 'wrs_modal_dialogContainer'; + attributes.id = attributes.class + '_id'; + this.container = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_wrapper'; + attributes.id = attributes.class + '_id'; + this.wrapper = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_content_container'; + attributes.id = attributes.class + '_id'; + this.contentContainer = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_controls'; + attributes.id = attributes.class + '_id'; + this.controls = wrs_createElement('div', attributes); + + attributes = {}; + attributes.class = 'wrs_modal_buttons_container'; + attributes.id = attributes.class + '_id'; + this.buttonContainer = wrs_createElement('div', attributes); + + // Buttons: all button must be created using createSubmitButton method. + this.submitButton = this.createSubmitButton( + { + class: 'wrs_modal_button_accept', + innerHTML: _wrs_stringManager.getString('accept') + }, + this.submitAction.bind(this) + ); + + this.cancelButton = this.createSubmitButton( + { + class: 'wrs_modal_button_cancel', + innerHTML: _wrs_stringManager.getString('cancel') + }, + this.cancelAction.bind(this) + ); + + this.lastImageWasNew = true; + + this.contentManager = null; + + // Overlay popup. + var popupStrings = { + 'cancelString' : _wrs_stringManager.getString('cancel'), + 'submitString' : _wrs_stringManager.getString('close'), + 'message' : _wrs_stringManager.getString('close_modal_warning') + }; + + var callbacks = { + 'closeCallback' : function(){this.close()}.bind(this), + 'cancelCallback' : function(){this.focus()}.bind(this) + } + + var popupupProperties = { + 'overlayElement' : this.container, + 'callbacks' :callbacks, + 'strings': popupStrings + } + + this.popup = new PopUpMessage(popupupProperties); + } + /** + * This method sets the contentElement object. A contentElement object + * manages the logic of modal object content: submit, update, close and changes. + * @param {object} contentManager + * @ignore + */ + setContentManager(contentManager) { + this.contentManager = contentManager; + } + + /** + * Returns the modal contentElement object. + *@returns {object} + *@ignore + */ + getContentManager() { + return this.contentManager; + } + + /** + * This method is called when the modal object has been submited. Calls + * contentElement submitAction method - if exists - and closes the modal + * object. No logic about the content should be placed here, + * contentElement.submitAction is the responsible of the content logic. + * @ignore + */ + submitAction() { + if (typeof this.contentManager.submitAction !== 'undefined') { + this.contentManager.submitAction(); + } + this.close(); + } + + /** + * This method is called when the modal object has been cancelled. If + * contentElement has implemented hasChanges method, a confirm popup + * will be shown if hasChanges returns true. + * @ignore + */ + cancelAction() { + if (typeof this.contentManager.hasChanges === 'undefined') { + this.close(); + } else if (!this.contentManager.hasChanges()){ + this.close(); + } else { + this.showPopUpMessage(); + } + } + + /** + * Returns a submit button. The properties argument admits the following: + * properties { + * 'class' : '', // button class. + * 'innerHTML : '' // button innerHTML. + * } + * @param {Object} properties input button properties. + * @param {Function} callback callback function associated to click event. + * @ignore + */ + createSubmitButton(properties, callback) { + function SubmitButton(properties, callback) { + this.element = document.createElement('button'); + this.element.id = properties.class + '_id'; + this.element.className = properties.class; + this.element.innerHTML = properties.innerHTML; + wrs_addEvent(this.element, 'click', callback); + } + + SubmitButton.prototype.getElement = function() { + return this.element; + } + + return new SubmitButton(properties, callback).getElement(); + } + + /** + * Creates the modal window object inserting a contentElement object. + * @ignore + */ + create() { + + /*Modal Window Structure + _____________________________________________________________________________________ + |wrs_modal_dialog_Container | + | _________________________________________________________________________________ | + | |title_bar minimize_button stack_button close_button | | + | |_______________________________________________________________________________| | + | |wrapper | | + | | _____________________________________________________________________________ | | + | | |content | | | + | | | | | | + | | | | | | + | | |___________________________________________________________________________| | | + | | _____________________________________________________________________________ | | + | | |controls | | | + | | | ___________________________________ | | | + | | | |buttonContainer | | | | + | | | | _______________________________ | | | | + | | | | |button_accept | button_cancel| | | | | + | | | |_|_____________ |______________|_| | | | + | | |___________________________________________________________________________| | | + | |_______________________________________________________________________________| | + |___________________________________________________________________________________|*/ + + this.titleBar.appendChild(this.closeDiv); + this.titleBar.appendChild(this.stackDiv); + this.titleBar.appendChild(this.maximizeDiv); + this.titleBar.appendChild(this.minimizeDiv); + this.titleBar.appendChild(this.title); + + if (this.deviceProperties['isDesktop']) { + this.container.appendChild(this.titleBar); + } + + this.wrapper.appendChild(this.contentContainer); + this.wrapper.appendChild(this.controls); + + this.controls.appendChild(this.buttonContainer); + + this.buttonContainer.appendChild(this.submitButton); + this.buttonContainer.appendChild(this.cancelButton); + + this.container.appendChild(this.wrapper); + + // Check if browser has scrollBar before modal has modified. + this.recalculateScrollBar(); + + document.body.appendChild(this.container); + document.body.appendChild(this.overlay); + + if (this.deviceProperties['isDesktop']) { // Desktop. + this.createModalWindowDesktop(); + this.createResizeButtons(); + + this.addListeners(); + // Maximize window only when the configuration is set and the device is not iOS or Android. + if (_wrs_conf_modalWindowFullScreen) { + this.maximize(); + } + } + else if (this.deviceProperties['isAndroid']) { + this.createModalWindowAndroid(); + } + else if (this.deviceProperties['isIOS'] && !this.deviceProperties['isMobile']) { + this.createModalWindowIos(); + } + + if (this.contentManager != null) { + this.contentManager.insert(this); + } + + this.properties.open = true; + this.properties.created = true; + } + + /** + * Creates a button in the modal object to resize it. + * @ignore + */ + createResizeButtons() { + // This is a definition of Resize Button Bottom-Right + this.resizerBR = document.createElement('div'); + this.resizerBR.className = 'wrs_bottom_right_resizer'; + this.resizerBR.innerHTML = '◢'; + // This is a definition of Resize Button Top-Left + this.resizerTL = document.createElement('div'); + this.resizerTL.className = 'wrs_bottom_left_resizer'; + // Append resize buttons to modal + this.container.appendChild(this.resizerBR); + this.titleBar.appendChild(this.resizerTL); + // Add events to resize on click and drag + wrs_addEvent(this.resizerBR, 'mousedown', this.activateResizeStateBR.bind(this)); + wrs_addEvent(this.resizerTL, 'mousedown', this.activateResizeStateTL.bind(this)); + } + /** + * Method to initialize variables for Bottom-Right resize button + * @param {event} ev mouse + * @ignore + */ + activateResizeStateBR(ev) { + this.initializeResizeProperties(ev, false); + } + + /** + * Method to initialize variables for Top-Left resize button + * @param {event} ev mouse + * @ignore + */ + activateResizeStateTL(ev) { + this.initializeResizeProperties(ev, true); + } + + /** + * Common method to initialize variables at resize + * @param {event} ev mouse + * @ignore + */ + initializeResizeProperties(ev, leftOption) { + // Apply class for disable involuntary select text when drag. + wrs_addClass(document.body, 'wrs_noselect'); + wrs_addClass(this.overlay, 'wrs_overlay_active'); + this.resizeDataObject = { + x: this.eventClient(ev).X, + y: this.eventClient(ev).Y + }; + // Save Initial state of modal to compare on drag and obtain the difference. + this.initialWidth = parseInt(this.container.style.width); + this.initialHeight = parseInt(this.container.style.height); + if (!leftOption) { + this.initialRight = parseInt(this.container.style.right); + this.initialBottom = parseInt(this.container.style.bottom); + } else { + this.leftScale = true; + } + if (!this.initialRight){ + this.initialRight = 0; + } + if (!this.initialBottom){ + this.initialBottom = 0; + } + // Disable mouse events on editor when we start to drag modal. + document.body.style['user-select'] = 'none'; + } + + /** + * This method opens the modal window, restoring the previous state, position and metrics, + * if exists. + * By default the modal object opens in stack mode. + * @ignore + */ + open() { + //Removing close class. + this.removeClass('wrs_closed'); + // Hiding keyboard for mobile devices. + if (this.deviceProperties['isIOS'] || this.deviceProperties['isAndroid'] || this.deviceProperties['isMobile']) { + // Due to editor wait we need to wait until editor focus. + setTimeout(function() { this.hideKeyboard() }.bind(this), 400); + } + + // New modal window. He need to create the whole object. + if (!this.properties.created) { + this.create(); + } + else { + // Previous state closed. Open method can be called even the previous state is open, + // for exmample updating the content of the modal object. + if (!this.properties.open) { + this.properties.open = true; + + // Restoring the previous open state: if the modal object has been closed + // re-open it should preserve the state and the metrics. + if (!this.deviceProperties.isAndroid && !this.deviceProperties.isIOS) { + this.restoreState(); + } + } + + // Maximize window only when the configuration is set and the device is not iOs or Android. + if (this.deviceProperties['isDesktop'] && _wrs_conf_modalWindowFullScreen) { + this.maximize(); + } + + // In iOS we need to recalculate the size of the modal object because + // iOS keyboard is a float div which can overlay the modal object. + if (this.deviceProperties['isIOS']) { + this.iosSoftkeyboardOpened = false; + this.setContainerHeight("100" + this.iosMeasureUnit); + } + + // Updating content element. + if (typeof this.contentManager != 'undefined') { + this.contentManager.onOpen(this); + } + } + } + + /** + * Closes modal window and restores viewport header. + * @ignore + */ + close() { + this.removeClass('wrs_maximized'); + this.removeClass('wrs_minimized'); + this.removeClass('wrs_stack'); + this.addClass('wrs_closed'); + this.saveModalProperties(); + this.properties.open = false; + } + + /** + * Util function to kwnow if browser is internet explorer 11. + * @return {boolean} return true if browser is iexplorer v11 or false in others. + * @ignore + */ + isIE11() { + if (navigator.userAgent.search("Msie/") >= 0 || navigator.userAgent.search("Trident/") >= 0 || navigator.userAgent.search("Edge/") >= 0 ) { + return true; + } + return false; + } + + /** + * Adds a class to all modal DOM elements. + * @param {string} cls + * @ignore + */ + addClass(cls) { + wrs_addClass(this.overlay, cls); + wrs_addClass(this.titleBar, cls); + wrs_addClass(this.overlay, cls); + wrs_addClass(this.container, cls); + wrs_addClass(this.contentContainer, cls); + wrs_addClass(this.stackDiv, cls); + wrs_addClass(this.minimizeDiv, cls); + wrs_addClass(this.maximizeDiv, cls); + wrs_addClass(this.wrapper, cls); + } + + /** + * Remove a calss from all modal DOM elements. + * @param {string} cls + * @ignore + */ + removeClass(cls) { + wrs_removeClass(this.overlay, cls); + wrs_removeClass(this.titleBar, cls); + wrs_removeClass(this.overlay, cls); + wrs_removeClass(this.container, cls); + wrs_removeClass(this.contentContainer, cls); + wrs_removeClass(this.stackDiv, cls); + wrs_removeClass(this.minimizeDiv, cls); + wrs_removeClass(this.maximizeDiv, cls); + wrs_removeClass(this.wrapper, cls); + } + + /** + * Create modal dialog for desktop. + * @ignore + */ + createModalWindowDesktop() { + this.addClass('wrs_modal_desktop'); + this.stack(); + } + + /** + * Create modal dialog for non mobile android devices. + * @ignore + */ + createModalWindowAndroid() { + this.addClass('wrs_modal_android'); + window.addEventListener('resize', this.orientationChangeAndroidSoftkeyboard.bind(this)); + } + + /** + * Create modal dialog for iOS devices. + * @ignore + */ + createModalWindowIos() { + this.addClass('wrs_modal_ios'); + // Refresh the size when the orientation is changed + window.addEventListener('resize', this.orientationChangeIosSoftkeyboard.bind(this)); + } + + /** + * Restore previous state, position and size of previous stacked modal window + * @ignore + */ + restoreState() { + if (this.properties.state == 'maximized') { + // Reseting states for prevent return to stack state. + this.maximize(); + } else if (this.properties.state == 'minimized') { + // Reseting states for prevent return to stack state. + this.properties.state = this.properties.previousState; + this.properties.previousState = ''; + this.minimize(); + } else { + this.stack(); + } + } + + /** + * Stacks the modal object. + * @ignore + */ + stack() { + this.properties.previousState = this.properties.state; + this.properties.state = 'stack'; + this.removeClass('wrs_maximized'); + this.minimizeDiv.title = "Minimise"; + this.removeClass('wrs_minimized'); + this.addClass('wrs_stack'); + + this.restoreModalProperties(); + + if (typeof this.resizerBR !== 'undefined' && typeof this.resizerTL !== 'undefined') { + this.setResizeButtonsVisibility(); + } + + // Need recalculate position of actual modal because window can was changed in fullscreenmode + this.recalculateScrollBar(); + this.recalculatePosition(); + this.recalculateScale(); + this.focus(); + } + + /** + * Minimizes the modal object + * @ignore + */ + minimize() { + // Saving width, height, top and bottom parameters to restore when open + this.saveModalProperties(); + if (this.properties.state == 'minimized' && this.properties.previousState == 'stack') { + this.stack(); + } + else if (this.properties.state == 'minimized' && this.properties.previousState == 'maximized') { + this.maximize(); + } + else { + // Setting css to prevent important tag into css style + this.container.style.height = "30px"; + this.container.style.width = "250px"; + this.container.style.bottom = "0px"; + this.container.style.right = "10px"; + + this.removeListeners(); + this.properties.previousState = this.properties.state; + this.properties.state = "minimized"; + this.setResizeButtonsVisibility(); + this.minimizeDiv.title = "Maximise"; + + if (wrs_containsClass(this.overlay, 'wrs_stack')) { + this.removeClass('wrs_stack'); + } + else { + this.removeClass('wrs_maximized'); + } + this.addClass('wrs_minimized'); + } + } + + /** + * Maximizes the modal object. + * @ignore + */ + maximize() { + // Saving width, height, top and bottom parameters to restore when open + this.saveModalProperties(); + if (this.properties.state != 'maximized') { + this.properties.previousState = this.properties.state; + this.properties.state = 'maximized'; + } + // Don't permit resize on maximize mode. + this.setResizeButtonsVisibility(); + + if (wrs_containsClass(this.overlay, 'wrs_minimized')) { + this.minimizeDiv.title = "Minimise"; + this.removeClass('wrs_minimized'); + } + else if (wrs_containsClass(this.overlay, 'wrs_stack')) { + this.container.style.left = null; + this.container.style.top = null; + this.removeClass('wrs_stack'); + } + + this.addClass('wrs_maximized'); + + // Set size to 80% screen with a max size. + this.setSize(parseInt(window.innerHeight * 0.8) , parseInt(window.innerWidth * 0.8)); + var sizeModificated = false; + if (this.container.clientHeight > 700) { + this.container.style.height = '700px'; + sizeModificated = true; + } + if (this.container.clientWidth > 1200) { + this.container.style.width = '1200px'; + sizeModificated = true; + } + + // Setting modal position in center on screen. + this.setPosition(window.innerHeight / 2 - this.container.offsetHeight / 2, window.innerWidth / 2 - this.container.offsetWidth / 2); + this.recalculateScale(); + this.recalculatePosition(); + this.recalculateSize(); + this.focus(); + } + + /** + * Sets modal size. + * @param {integer} height set a height of modal with an integer + * @param {integer} width set a width of modal with an integer + * @ignore + */ + setSize(height, width) { + this.container.style.height = height + 'px'; + this.container.style.width = width + 'px'; + this.recalculateSize(); + } + + /** + * Sets modal position. + * @param {integer} bottom set a bottom of div modal with an integer + * @param {integer} right set a right of div modal with an integer + * @ignore + */ + setPosition(bottom, right) { + this.container.style.bottom = bottom + 'px'; + this.container.style.right = right + 'px'; + } + + /** + * Saves actual parameters of open modal object (like size and position...) to restore it + * once the modal object is re-opened. + * @ignore + */ + saveModalProperties() { + // Saving values of modal only when modal is in stack state. + if (this.properties.state == 'stack') { + this.properties.position.bottom = parseInt(this.container.style.bottom); + this.properties.position.right = parseInt(this.container.style.right); + this.properties.size.width = parseInt(this.container.style.width); + this.properties.size.height = parseInt(this.container.style.height); + } + } + + /** + * Restore previous parameters values of closed modal (like size and position) and apply this parameters in actual modal. + * @ignore + */ + restoreModalProperties() { + if (this.properties.state == 'stack') { + // Restoring Bottom and Right values from last modal + this.setPosition(this.properties.position.bottom, this.properties.position.right); + // Restoring Height and Left values from last modal + this.setSize(this.properties.size.height, this.properties.size.width); + } + } + + /** + * Set modal size. + * @ignore + */ + recalculateSize() { + this.wrapper.style.width = this.container.clientWidth - 12 + 'px'; + this.wrapper.style.height = this.container.clientHeight - 38 + 'px'; + this.contentContainer.style.height = parseInt(this.wrapper.offsetHeight - 50) + 'px'; + } + + /** + * Active and disable visibility of resize buttons in modal window depend on state. + * @ignore + */ + setResizeButtonsVisibility() { + if (this.properties.state == 'stack') { + this.resizerTL.style.visibility = 'visible'; + this.resizerBR.style.visibility = 'visible'; + } + else { + this.resizerTL.style.visibility = 'hidden'; + this.resizerBR.style.visibility = 'hidden'; + } + } + + /** + * Makes an object draggable adding mouse and touch events. + * @ignore + */ + addListeners() { + // Button events (maximize, minimize, stack and close). + this.maximizeDiv.addEventListener('click', this.maximize.bind(this), true); + this.stackDiv.addEventListener('click', this.stack.bind(this), true); + this.minimizeDiv.addEventListener('click', this.minimize.bind(this), true); + this.closeDiv.addEventListener('click', this.cancelAction.bind(this)); + + // Mouse events. + wrs_addEvent(window, 'mousedown', this.startDrag.bind(this)); + wrs_addEvent(window, 'mouseup', this.stopDrag.bind(this)); + wrs_addEvent(window, 'mousemove', this.drag.bind(this)); + wrs_addEvent(window, 'resize', this.onWindowResize.bind(this)); + // Key events. + wrs_addEvent(window, 'keydown', this.onKeyDown.bind(this)); + } + + /** + * Removes draggable events from an object. + * @ignore + */ + removeListeners() { + // Mouse events. + wrs_removeEvent(window, 'mousedown', this.startDrag); + wrs_removeEvent(window, 'mouseup', this.stopDrag); + wrs_removeEvent(window, 'mousemove', this.drag); + wrs_removeEvent(window, 'resize', this.onWindowResize); + // Key events + wrs_removeEvent(window, 'keydown', this.onKeyDown); + } + + + /** + * Returns mouse or touch coordinates (on touch events ev.ClientX doesn't exists) + * @param {event} ev mouse or touch event + * @return {object} with the X and Y coordinates. + * @ignore + */ + eventClient(ev) { + if (typeof(ev.clientX) == 'undefined' && ev.changedTouches) { + var client = { + X : ev.changedTouches[0].clientX, + Y : ev.changedTouches[0].clientY + }; + return client; + } + else { + client = { + X : ev.clientX, + Y : ev.clientY + }; + return client; + } + } + + /** + * Start drag function: set the object dragDataObject with the draggable object offsets coordinates. + * when drag starts (on touchstart or mousedown events). + * + * @param {event} ev touchstart or mousedown event. + * @ignore + */ + startDrag(ev) { + if (this.properties.state == 'minimized') { + return; + } + if (ev.target.className == 'wrs_modal_title') { + if(typeof this.dragDataObject === 'undefined' || this.dragDataObject === null) { + ev = ev || event; + // Save first click mouse point on screen + this.dragDataObject = { + x: this.eventClient(ev).X, + y: this.eventClient(ev).Y + }; + // Reset last drag position when start drag + this.lastDrag = { + x: "0px", + y: "0px" + }; + // Init right and bottom values for window modal if it isn't exist. + if(this.container.style.right == ''){ + this.container.style.right = "0px"; + } + if(this.container.style.bottom == ''){ + this.container.style.bottom = "0px"; + } + + // Needed for IE11 for apply disabled mouse events on editor because iexplorer need a dinamic object to apply this property. + if (this.isIE11()) { + // this.iframe.style['position'] = 'relative'; + } + // Apply class for disable involuntary select text when drag. + wrs_addClass(document.body, 'wrs_noselect'); + wrs_addClass(this.overlay, 'wrs_overlay_active'); + // Obtain screen limits for prevent overflow. + this.limitWindow = this.getLimitWindow(); + } + } + + } + + /** + * UpdatesdragDataObject with the draggable object coordinates when the draggable object is being moved. + * + * @param {event} ev touchmouve or mousemove events. + * @ignore + */ + drag(ev) { + if (this.dragDataObject) { + ev.preventDefault(); + ev = ev || event; + // Calculate max and min between actual mouse position and limit of screeen. It restric the movement of modal into window. + var limitY = Math.min(this.eventClient(ev).Y + window.pageYOffset,this.limitWindow.minPointer.y + window.pageYOffset); + limitY = Math.max(this.limitWindow.maxPointer.y + window.pageYOffset,limitY); + var limitX = Math.min(this.eventClient(ev).X + window.pageXOffset,this.limitWindow.minPointer.x + window.pageXOffset); + limitX = Math.max(this.limitWindow.maxPointer.x + window.pageXOffset,limitX); + // Substract limit with first position to obtain relative pixels increment to the anchor point. + var dragX = limitX - this.dragDataObject.x + "px"; + var dragY = limitY - this.dragDataObject.y + "px"; + // Save last valid position of modal before window overflow. + this.lastDrag = { + x: dragX, + y:dragY + }; + // This move modal with hadware acceleration. + this.container.style.transform = "translate3d(" + dragX + "," + dragY + ",0)"; + this.container.style.position = 'absolute'; + } + if (this.resizeDataObject) { + var limitX = Math.min(this.eventClient(ev).X,window.innerWidth - this.scrollbarWidth - 7); + var limitY = Math.min(this.eventClient(ev).Y,window.innerHeight - 7); + if (limitX < 0) { + limitX = 0; + } + + if (limitY < 0) { + limitY = 0; + } + + var scaleMultiplier; + if (this.leftScale) { + scaleMultiplier = -1; + } + else { + scaleMultiplier = 1; + } + this.container.style.width = this.initialWidth + scaleMultiplier * (limitX - this.resizeDataObject.x) + 'px'; + this.container.style.height = this.initialHeight + scaleMultiplier * (limitY - this.resizeDataObject.y) + 'px'; + if (!this.leftScale) { + if (this.resizeDataObject.x - limitX - this.initialWidth < -580) { + this.container.style.right = this.initialRight - (limitX - this.resizeDataObject.x) + 'px'; + } + else { + this.container.style.right = this.initialRight + this.initialWidth - 580 + "px"; + this.container.style.width = "580px"; + } + if (this.resizeDataObject.y - limitY < this.initialHeight - 338) { + this.container.style.bottom = this.initialBottom - (limitY - this.resizeDataObject.y) + 'px'; + } + else { + this.container.style.bottom = this.initialBottom + this.initialHeight - 338 + "px"; + this.container.style.height = "338px"; + } + } + this.recalculateScale(); + this.recalculatePosition(); + } + } + /** + * Get limits of actual window to limit modal movement + * @return {Object} Object containing mouseX and mouseY are coordinates of actual mouse on screen. + * @ignore + */ + getLimitWindow() { + // Obtain dimentions of window page. + var maxWidth = window.innerWidth; + var maxHeight = window.innerHeight; + + // Calculate relative position of mouse point into window. + var offSetToolbarY = (this.container.offsetHeight + parseInt(this.container.style.bottom)) - (maxHeight - (this.dragDataObject.y - window.pageXOffset)); + var offSetToolbarX = maxWidth - this.scrollbarWidth - (this.dragDataObject.x - window.pageXOffset) - parseInt(this.container.style.right); + + // Calculate limits with sizes of window, modal and mouse position. + var minPointerY = maxHeight - this.container.offsetHeight + offSetToolbarY; + var maxPointerY = this.title.offsetHeight - (this.title.offsetHeight - offSetToolbarY); + var minPointerX = maxWidth - offSetToolbarX - this.scrollbarWidth; + var maxPointerX = (this.container.offsetWidth - offSetToolbarX); + var minPointer = {x: minPointerX,y: minPointerY}; + var maxPointer = {x: maxPointerX,y: maxPointerY}; + return {minPointer : minPointer, maxPointer:maxPointer}; + } + /** + * Get Scrollbar width size of browser + * @ignore + */ + getScrollBarWidth() { + // Create a paragraph with full width of page. + var inner = document.createElement('p'); + inner.style.width = "100%"; + inner.style.height = "200px"; + + // Create a hidden div to compare sizes. + var outer = document.createElement('div'); + outer.style.position = "absolute"; + outer.style.top = "0px"; + outer.style.left = "0px"; + outer.style.visibility = "hidden"; + outer.style.width = "200px"; + outer.style.height = "150px"; + outer.style.overflow = "hidden"; + outer.appendChild(inner); + + document.body.appendChild(outer); + var widthOuter = inner.offsetWidth; + + // Change type overflow of paragraph for measure scrollbar. + outer.style.overflow = 'scroll'; + var widthInner = inner.offsetWidth; + + // If measure is the same, we compare with internal div. + if (widthOuter == widthInner) { + widthInner = outer.clientWidth; + } + document.body.removeChild(outer); + + return (widthOuter - widthInner); + } + + /** + * Set the dragDataObject to null when the drag finish (touchend or mouseup events). + * + * @param {event} ev touchend or mouseup event. + * @ignore + */ + stopDrag(ev) { + // Due to we have multiple events that call this function, we need only to execute the next modifiers one time, + // when the user stops to drag and dragDataObject is not null (the object to drag is attached). + if (this.dragDataObject || this.resizeDataObject) { + // If modal doesn't change, it's not necessary to set position with interpolation + if(this.container.style.position != 'fixed'){ + this.container.style.position = 'fixed'; + // Fixed position makes the coords relative to the main window. So that, we need to transform + // the absolute coords to relative. + this.container.style.transform = ''; + if (this.dragDataObject) { + this.container.style.right = parseInt(this.container.style.right) - parseInt(this.lastDrag.x) + pageXOffset + "px"; + this.container.style.bottom = parseInt(this.container.style.bottom) - parseInt(this.lastDrag.y) + pageYOffset + "px"; + } + } + // We make focus on editor after drag modal windows to prevent lose focus. + this.focus(); + // Restore mouse events on iframe + // this.iframe.style['pointer-events'] = 'auto'; + document.body.style['user-select'] = ''; + // Restore static state of iframe if we use iexplorer + if (this.isIE11()) { + // this.iframe.style['position'] = null; + } + // Active text select event + wrs_removeClass(document.body, 'wrs_noselect'); + wrs_removeClass(this.overlay, 'wrs_overlay_active'); + } + this.dragDataObject = null; + this.resizeDataObject = null; + this.initialWidth = null; + this.leftScale = null; + } + + /** + * Recalculating scale for modal when resize browser window * + * @ignore + */ + onWindowResize() { + this.recalculateScrollBar(); + this.recalculatePosition(); + this.recalculateScale(); + } + + /** + * Keydown events: + * Esc key close modal window. + * Tab key tab to submit button. + * @param {event} ev + */ + onKeyDown(ev) { + if (ev.key !== undefined && ev.repeat === false) { + // Code for detect Esc event + if (ev.key === "Escape" || ev.key === 'Esc') { + if (this.properties.open) { + this.cancelAction(); + } + } + // Code for detect Tab event + if (ev.key === "Tab") { + this.submitButton.focus(); + ev.preventDefault(); + } + } + } + /** + * Recalculating position for modal when resize browser window + * @ignore + */ + recalculatePosition() { + this.container.style.right = Math.min(parseInt(this.container.style.right), window.innerWidth - this.scrollbarWidth - this.container.offsetWidth) + "px"; + if(parseInt(this.container.style.right) < 0) { + this.container.style.right = "0px"; + } + this.container.style.bottom = Math.min(parseInt(this.container.style.bottom), window.innerHeight - this.container.offsetHeight) + "px"; + if(parseInt(this.container.style.bottom) < 0) { + this.container.style.bottom = "0px"; + } + } + + /** + * Recalculating scale for modal when resize browser window + * @ignore + */ + recalculateScale() { + var sizeModificated = false; + if (parseInt(this.container.style.width) > 580) { + this.container.style.width = Math.min(parseInt(this.container.style.width), window.innerWidth - this.scrollbarWidth) + "px"; + sizeModificated = true; + } + else { + this.container.style.width = "580px"; + sizeModificated = true; + } + if (parseInt(this.container.style.height) > 338) { + this.container.style.height = Math.min(parseInt(this.container.style.height), window.innerHeight) + "px"; + sizeModificated = true; + } + else { + this.container.style.height = "338px"; + sizeModificated = true; + } + if (sizeModificated) { + this.recalculateSize(); + } + } + + /** + * Recalculating width of scrollBar browser + * @ignore + */ + recalculateScrollBar() { + this.hasScrollBar = window.innerWidth > document.documentElement.clientWidth; + if(this.hasScrollBar){ + this.scrollbarWidth = this.getScrollBarWidth(); + } + else { + this.scrollbarWidth = 0; + } + } + + /** + * Hide soft keyboards on IOS systems. + * @ignore + */ + hideKeyboard() { + document.activeElement.blur(); + } + + /** + * Focus to content object + * @ignore + */ + focus() { + if (this.contentManager != null && typeof this.contentManager.onFocus !== 'undefined') { + this.contentManager.onFocus(); + } + } + + /** + * Returns true when the device is on portrait mode. + * @ignore + */ + portraitMode() { + return window.innerHeight > window.innerWidth; + } + + /** + * Change container sizes when the keyboard is opened on iOS. + * @ignore + */ + openedIosSoftkeyboard() { + if (!this.iosSoftkeyboardOpened && this.iosDivHeight != null && this.iosDivHeight == "100" + this.iosMeasureUnit) { + if (this.portraitMode()) { + this.setContainerHeight("63" + this.iosMeasureUnit); + } + else { + this.setContainerHeight("40" + this.iosMeasureUnit); + } + } + this.iosSoftkeyboardOpened = true; + } + + /** + * Change container sizes when the keyboard is closed on iOS. + * @ignore + */ + closedIosSoftkeyboard() { + this.iosSoftkeyboardOpened = false; + this.setContainerHeight("100" + this.iosMeasureUnit); + } + + /** + * Change container sizes when orientation is changed on iOS. + * @ignore + */ + orientationChangeIosSoftkeyboard() { + if (this.iosSoftkeyboardOpened) { + if (this.portraitMode()) { + this.setContainerHeight("63" + this.iosMeasureUnit); + } + else { + this.setContainerHeight("40" + this.iosMeasureUnit); + } + } + else { + this.setContainerHeight("100" + this.iosMeasureUnit); + } + } + + /** + * Change container sizes when orientation is changed on Android. + * @ignore + */ + orientationChangeAndroidSoftkeyboard() { + this.setContainerHeight("100%"); + } + + /** + * Set iframe container height. + * @ignore + */ + setContainerHeight(height) { + this.iosDivHeight = height; + this.wrapper.style.height = height; + // this.editor.getElement().style.height = (this.container.offsetHeight -10) - this.controlsDiv.offsetHeight + 'px'; + } + + /** + * Check content of editor before close action + * @ignore + */ + showPopUpMessage() { + if (this.properties.state == 'minimized') { + this.stack(); + } + this.popup.show(); + } + + setTitle(title) { + this.title.innerHTML = title; + } +} +/** + * This class shows a dialog message overlaying a dom element in order to + * accept / cancel discart changes. The dialog can be closed i.e the overlay dissapears + * o canceled. In this last case a callback function should be called. + * + * The constructor accepts the following attributes object: + * { + * overlayElement: '', Element to be overlayed. + * callbacks: { + * 'closeCallback' : function(), // Callback function to be called if the dialog is closed. + * 'cancelCallback' : function() // Callback function to be called if the dialog is cancelled + * strings: { + * 'submitString' : '', // Submit button string + * 'cancelString : ''. // Cancel button string + * 'message': '' // Message string + * } + * } + * @param {object} popupProperties + */ +class PopUpMessage { + + constructor(popupProperties) { + this.overlay = popupProperties.overlayElement; + this.callbacks = popupProperties.callbacks; + this.overlayEnvolture = this.overlay.appendChild(document.createElement("div")); + this.overlayEnvolture.setAttribute("class", "wrs_popupmessage_overlay_envolture"); + + this.message = this.overlayEnvolture.appendChild(document.createElement("div")); + this.message.id = "wrs_popupmessage" + this.message.setAttribute("class", "wrs_popupmessage_panel"); + this.message.innerHTML = popupProperties.strings.message; + + var overlay = this.overlayEnvolture.appendChild(document.createElement("div")); + overlay.setAttribute("class", "wrs_popupmessage_overlay"); + // We create a overlay that close popup message on click in there + overlay.addEventListener("click", this.cancelAction.bind(this)); + + this.buttonArea = this.message.appendChild(document.createElement('div')); + this.buttonArea.setAttribute("class", "wrs_popupmessage_button_area"); + this.buttonArea.id = 'wrs_popup_button_area'; + + // Buttons creation + var buttonSubmitArguments = { + class: "wrs_button_accept", + innerHTML: popupProperties.strings.submitString, + id: 'wrs_popup_accept_button' + }; + this.acceptButton = this.createButton(buttonSubmitArguments, this.closeAction.bind(this)); + this.buttonArea.appendChild(this.acceptButton); + + var buttonCancelArguments = { + class: "wrs_button_cancel", + innerHTML: popupProperties.strings.cancelString, + id: 'wrs_popup_cancel_button' + }; + this.cancelButton = this.createButton(buttonCancelArguments, this.cancelAction.bind(this)); + this.buttonArea.appendChild(this.cancelButton); + } + + /** + * This method create a button with arguments and return button dom object + * @param {object} parameters An object containg id, class and innerHTML button text. + * @param {object} callback Callback method to call on click event. + */ + createButton(parameters, callback) { + function popUpButton(parameters) { + this.element = document.createElement("button"); + this.element.setAttribute("id", parameters.id); + this.element.setAttribute("class", parameters.class); + this.element.innerHTML = parameters.innerHTML; + this.element.addEventListener("click", callback); + } + + popUpButton.prototype.getElement = function() { + return this.element; + } + + return new popUpButton(parameters).getElement(); + } + + /** + * This method show the popupmessage containing a message, and two buttons + * to cancel the action or close the modal dialog. + */ + show() { + if (this.overlayEnvolture.style.display != 'block') { + // Clear focus with blur for prevent press anykey + document.activeElement.blur(); + + // For works with Safari + window.focus(); + this.overlayEnvolture.style.display = 'block'; + } + else { + this.overlayEnvolture.style.display = 'none'; + _wrs_modalWindow.focus(); + } + } + + /** + * This method cancel the popupMessage: the dialog dissapears revealing the overlaid element. + * A callback method is called (if defined). For example a method to focus the overlaid element. + */ + cancelAction() { + this.overlayEnvolture.style.display = 'none'; + if (typeof this.callbacks.cancelCallback !== 'undefined') { + this.callbacks.cancelCallback(); + } + } + + /** + * This method closes the popupMessage: the dialog dissapears and the close callback is called. + * For example to close the overlaid element. + */ + closeAction() { + this.cancelAction(); + if (typeof this.callbacks.closeCallback !== 'undefined') { + this.callbacks.closeCallback(); + } + } +}/** + * This class implements ModalContent interface. Manage the following: + * - insertion in modal object (insert(modalObject) method) + * - actions to be done once the modal object has been submited (submitAction() method) + * - updates itself when modalObject is updated with a re-open action for example (update(modalObject) method) + * - comunicates to modalObject if some changes have be done (hasChanges() method) + * + * @param {object} editorAttributes editor attributes. See http://docs.wiris.com/en/mathtype/mathtype_web/sdk-api/parameters + * for further information. + * @ignore + */ +class contentManager { + // Editor listener. + constructor(editorAttributes) { + this.editorListener = new EditorListener(); + this.editor = null; + this.editorAttributes = editorAttributes; + this.lastImageWasNew = false; + // Device properties + var ua = navigator.userAgent.toLowerCase(); + this.deviceProperties = {}; + this.deviceProperties.isAndroid = ua.indexOf("android") > -1; + this.deviceProperties.isIOS = ((ua.indexOf("ipad") > -1) || (ua.indexOf("iphone") > -1)); + // Toolbar + this.toolbar = null; + this.loadEditor(); + } + + /** + * Mandatory method: inserts editor into modal object content container. + * @param {object} modalObject + * @ignore + */ + insert(modalObject) { + // Before insert the editor we update the modal object title to avoid weird render display. + this.updateTitle(modalObject); + this.insertEditor(modalObject); + } + + /** + * Method to insert MathType into modal object. This method + * watis until editor JavaScript is loaded to insert the editor into + * contentContainer modal object element. + * @ignore + */ + insertEditor(modalObject) { + // To know if editor JavaScript is loaded we need to wait until com.wiris.jsEditor namespace is ready. + if ('com' in window && 'wiris' in window.com && 'jsEditor' in window.com.wiris) { + this.editor = com.wiris.jsEditor.JsEditor.newInstance(this.editorAttributes); + this.editor.insertInto(modalObject.contentContainer); + this.editor.focus() + + // Editor listener: this object manages the changes logic of editor. + this.editor.getEditorModel().addEditorListener(this.editorListener); + + // iOS events. + if (modalObject.deviceProperties['isIOS']) { + setTimeout(function() { _wrs_modalWindow.hideKeyboard() }, 400); + var formulaDisplayDiv = document.getElementsByClassName('wrs_formulaDisplay')[0]; + wrs_addEvent(formulaDisplayDiv, 'focus', modalObject.openedIosSoftkeyboard.bind(modalObject)); + wrs_addEvent(formulaDisplayDiv, 'blur', modalObject.closedIosSoftkeyboard.bind(modalObject)); + } + + this.onOpen(modalObject); + this.editor.onContentChanged + } else { + setTimeout(contentManager.prototype.insertEditor.bind(this, modalObject), 100); + } + } + + /** + * Loads MathType ediitor JavaScript. + * @ignore + */ + loadEditor() { + var queryParams = window.location.search.substring(1).split("&"); + var version = ""; + for (var i = 0; i < queryParams.length; i++) { + var pos = queryParams[i].indexOf("v="); + if (pos >= 0) { + version = queryParams[i].substring(2); + } + } + + var script = document.createElement('script'); + script.type = 'text/javascript'; + var editorUrl = _wrs_conf_editorUrl; + // Change to https if necessary. + // We create an object url for parse url string and work more efficiently. + var urlObject = document.createElement('a'); + urlObject.href = editorUrl; + + if (window.location.href.indexOf("https://") == 0) { + // It check if browser is https and configuration is http. If this is so, we will replace protocol. + if (urlObject.protocol == 'http:') { + urlObject.protocol = 'https:'; + } + } + + // Check protocol and remove port if it's standard. + if (urlObject.port == '80' || urlObject.port == '443') { + editorUrl = urlObject.protocol + '//' + urlObject.hostname + '/' + urlObject.pathname; + } else { + editorUrl = urlObject.protocol + '//' + urlObject.hostname + ':' + urlObject.port + '/' + urlObject.pathname; + } + + // Editor stats. + var statEditor = _wrs_conf_editor; + var statSaveMode = _wrs_conf_saveMode; + var statVersion = _wrs_conf_version; + + script.src = editorUrl + "?lang=" + _wrs_int_langCode + '&stats-editor=' + statEditor + '&stats-mode=' + statSaveMode + '&stats-version=' + statVersion; + document.getElementsByTagName('head')[0].appendChild(script); + } + + /** + * Set the editor initial content: an existing formula or a blank MathML + */ + setInitialContent() { + if (!_wrs_isNewElement) { + var mathml = wrs_mathmlDecode(_wrs_temporalImage.getAttribute(_wrs_conf_imageMathmlAttribute)); + this.setMathML(mathml); + } + } + + /** + * Set a MathML into editor. + * @param {string} mathml MathML string. + * @param {bool} focusDisabled if true editor don't get focus after the MathML is set. false by default. + * @ignore + */ + setMathML(mathml, focusDisabled) { + // By default focus is enabled + if (typeof focusDisabled === 'undefined') { + focusDisabled = false; + } + // Using setMathML method is not a change produced by the user but for the API + // so we set to false the contentChange property of editorListener. + this.editor.setMathMLWithCallback(mathml, function() { + this.editorListener.setWaitingForChanges(true); + }.bind(this)); + // We need to wait a little until the callback finish. + setTimeout(function(){ + this.editorListener.setIsContentChanged(false);}.bind(this), 500); + + // In some scenarios - like closing modal object - editor mustn't be focused. + if (!focusDisabled){ + this.onFocus(); + } + } + + /** + * Set focus on editor. + * @ignore + */ + onFocus() { + // TODO: Check editor avaliable. + if (typeof this.editor !== 'undefined' && this.editor != null) { + this.editor.focus(); + } + } + + /** + * Mandatory method: modal object calls this method to execute a callback action + * on submit. + * This method updates the edition area (inserting a new formula or update an older one), + * and focus the edition area too. + * @ignore + */ + submitAction() { + var mathML = this.editor.getMathML(); + // Add class for custom editors. + if (wrs_int_getCustomEditorEnabled() != null) { + mathML = wrs_mathmlAddEditorAttribute(mathML); + } + var mathmlEntitiesEncoded = wrs_mathmlEntities(mathML); + wrs_int_updateFormula(mathmlEntitiesEncoded, null, _wrs_int_langCode); + wrs_int_disableCustomEditors(); + wrs_int_notifyWindowClosed(); + _wrs_editMode = (window._wrs_conf_defaultEditMode) ? _wrs_conf_defaultEditMode : 'images'; + + // Set disabled focus to prevent lost focus. + this.setEmptyMathML(); + wrs_int_disableCustomEditors(); + // Reconvering editing area focus. + setTimeout( + function() { + if (typeof _wrs_currentEditor !== 'undefined' && _wrs_currentEditor) { + _wrs_currentEditor.focus(); + } + }, 100); + } + + /** + * Set an empty MathML into the editor in order to clean the edit area. + * @ignore + */ + setEmptyMathML() { + // As second argument we pass + if (this.deviceProperties.isAndroid || this.deviceProperties.isIOS) { + // We need to set a empty annotation in order to maintain editor in Hand mode. + this.setMathML('[]"', true); + } else { + this.setMathML('', true); + } + } + + /** + * Mandatory method: modal object calls this method when is updated, for example re-editing a formula when the + * editor is open with another formula. This method updates the editor content (with an empty MathML or an exising formula), + * updates - if needed - the editor toolbar (math --> chem or chem --> math) and recover the focus. + * @param {object} modalObject + * @ignore + */ + onOpen(modalObject) { + if (_wrs_isNewElement) { + this.setEmptyMathML(); + this.lastImageWasNew = true; + } + else { + this.setMathML(wrs_mathmlDecode(_wrs_temporalImage.getAttribute(_wrs_conf_imageMathmlAttribute))); + this.lastImageWasNew = false; + } + this.updateToolbar(modalObject); + this.onFocus(); + } + + /** + * Sets the correct toolbar depending if exist other custom toolbars at the same time (e.g: Chemistry) + * @ignore + */ + updateToolbar(modalObject) { + this.updateTitle(modalObject); + var customEditor; + if (customEditor = wrs_int_getCustomEditorEnabled()) { + var toolbar = customEditor.toolbar ? customEditor.toolbar : _wrs_int_wirisProperties['toolbar']; + if (this.toolbar == null || this.toolbar != toolbar) { + this.setToolbar(toolbar); + } + } else { + var toolbar = this.getToolbar(); + if (this.toolbar == null || this.toolbar != toolbar) { + this.setToolbar(toolbar); + wrs_int_disableCustomEditors(); + } + } + } + /** + * Updates the modalObject title: if a custom editor (with a custom toolbar) is enabled + * picks the custom editor title. Otherwise default title. + * @param {object} modalObject + * @ignore + */ + updateTitle(modalObject) { + var customEditor; + if (customEditor = wrs_int_getCustomEditorEnabled()) { + modalObject.setTitle(customEditor.title); + } else { + modalObject.setTitle('MathType'); + } + } + /** + * Returns toolbar depending on the configuration local or serverside. + * @ignore + */ + getToolbar() { + var toolbar = (typeof _wrs_conf_editorParameters == 'undefined' || typeof _wrs_conf_editorParameters['toolbar'] == 'undefined') ? 'general' : _wrs_conf_editorParameters['toolbar']; + if(toolbar == 'general'){ + toolbar = (typeof _wrs_int_wirisProperties == 'undefined' || typeof _wrs_int_wirisProperties['toolbar'] == 'undefined') ? 'general' : _wrs_int_wirisProperties['toolbar']; + } + return toolbar; + } + + /** + * Set a toolbar into editor. + * @param {string} toolbar toolbar name. + * @ignore + */ + setToolbar(toolbar) { + this.toolbar = toolbar; + this.editor.setParams({'toolbar': this.toolbar}); + } + + /** + * Returns true if the content of the editor has been changed. The logic of the changes + * is delegated to editorListener object. + * @ignore + */ + hasChanges() { + return (!this.editor.isFormulaEmpty() && this.editorListener.getIsContentChanged()); + } +} +/** + * EditorListener class. This class implement EditorListener interface + * and contains the logic to determine if editor has been changed or not. + * @ignore + */ +class EditorListener { + constructor() { + this.isContentChanged = false; + this.waitingForChanges = false; + } + + /** + * EditorListener method set if content is changed + * @ignore + */ + setIsContentChanged (value) { + this.isContentChanged = value; + }; + /** + * EditorListener method to get if content is changed + * @ignore + */ + getIsContentChanged (value) { + return this.isContentChanged; + }; + /** + * EditorListener method to wait changes + * @ignore + */ + setWaitingForChanges (value) { + this.waitingForChanges = value; + }; + /** + * EditorListener method to overwrite + * @ignore + */ + caretPositionChanged (editor) {}; + /** + * EditorListener method to overwrite + * @ignore + */ + clipboardChanged (editor) {}; + /** + * EditorListener method to set if content is changed + * @ignore + */ + contentChanged (editor) { + if (this.waitingForChanges === true && this.isContentChanged === false) { + this.isContentChanged = true; + } + } + /** + * EditorListener method to overwrite + * @ignore + */ + styleChanged (editor) {}; + /** + * EditorListener method to overwrite + * @ignore + */ + transformationReceived (editor) {} +}// TODO: Methods to static? +/** + * StringManager class to use strings in code correctly with control. + * @ignore + */ +class StringManager { + constructor() { + // Strings are empty when it creates, it set when calls load method. + this.strings = null; + this.stringsLoaded = false; + } + + /** + * This method return a string passing a key. + * @param {string} key of array strings that you want. + * @return string A text that you want or key if it doesn't exist. + * @ignore + */ + getString(key) { + // Wait 200ms and recall this method if strings aren't load. + if (!this.stringsLoaded) { + setTimeout(this.getString.bind(this, key), 100); + return; + } + if (key in this.strings) { + return this.strings[key]; + } + return key; + } + /** + * This method load all strings to the manager and unset it for prevent bad usage. + * @param {array} String array of language + * @ignore + */ + loadStrings(langStrings) { + if (!this.stringsLoaded) { + this.strings = langStrings; + // Activate variable to unlock getStrings + this.stringsLoaded = true; + } + } +}var _wrs_conf_core_loaded = true; diff --git a/core/editor.css b/core/editor.css deleted file mode 100644 index 7620bf6b..00000000 --- a/core/editor.css +++ /dev/null @@ -1,69 +0,0 @@ -#controls { - margin: 3px 0; - overflow: hidden; -} -.wrs_links { - margin: 10px auto; - margin-bottom: 0; - font-family: arial, sans-serif; - padding: 6px; -} - -.wrs_links > a { - text-decoration: none; - color: #778e9a; -} - -.wrs_button_cancel { - min-width: 80px; - font-size: 14px; - border-radius: 3px; - border: 1px solid #778e9a; - padding: 6px 8px; - margin: 10px auto; - margin-left: 5px; - margin-bottom: 0; - cursor: pointer; - font-family: arial, sans-serif; -} - -.wrs_button_accept { - min-width: 80px; - font-size: 14px; - border-radius: 3px; - border: 1px solid #778e9a; - padding: 6px 8px; - margin: 10px auto; - margin-right: 5px; - margin-bottom: 0; - color: #fff; - background: #778e9a; - cursor: pointer; - font-family: arial, sans-serif; -} - -.wrs_editor_vertical_bar { - height: 20px; - float: right; - background: none; - width: 20px; - cursor: pointer; -} - -.wrs_buttonContainer { - display: inline; -} - -.wrs_buttonContainer.wrs_modalAndroid { - padding-left: 6px; -} - -.wrs_buttonContainer.wrs_modalDesktop { - padding-left: 0; -} - -@media only screen and (max-device-width: 480px) and (orientation: portrait) { - #container { - width: 140%; - } -} diff --git a/core/editor.html b/core/editor.html deleted file mode 100644 index c88235cf..00000000 --- a/core/editor.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - MathType - - - - - -
-
-
-
-
- -
-
- - diff --git a/core/editor.js b/core/editor.js deleted file mode 100644 index b4eb5ce6..00000000 --- a/core/editor.js +++ /dev/null @@ -1,496 +0,0 @@ -var _wrs_isNewElement; // Unfortunately we need this variabels as global variable for old I.E compatibility. - -(function(){ - var editor; - // Set wrs_int_opener variable and close method. - // For popup window opener is window.opener. For modal window window.parent. - var wrs_int_opener = window.opener ? window.opener : window.parent; - var _wrs_closeFunction = window.opener ? window.close : function() {getMethod(null, 'wrs_closeModalWindow', [], null)}; - var _wrs_showPopUpFunction = function() {getMethod(null, 'wrs_showPopUpMessage', [], null)}; - var _wrs_callbacks = []; - var _wrs_callId = 0; - var _wrs_int_loaded = false; - var _wrs_temporalImageAttribute = null; - // Variables needed for core.js (or not core.js depending). This variables are loaded first of all. - var _wrs_int_vars = ['_wrs_editMode', - '_wrs_isNewElement', - '_wrs_conf_editor', - '_wrs_conf_imageMathmlAttribute', - '_wrs_int_conf_file', - '_wrs_int_path' , - '_wrs_int_wirisProperties', - '_wrs_int_customEditors', - '_wrs_modalWindowProperties', - '_wrs_int_langCode', - '_wrs_stringManager' - ]; - - // Sometimes (yes, sometimes) internet explorer security policies hides popups - // popup window focus should be called from child window. - if (window.opener) { - window.focus(); - } - - // Loading core.js dependent variables (defined on _wrs_int_vars). - // Using getVars method for safely cross-origin communication. - getVars(_wrs_int_vars, function(object) { // Callback method to set variables. - for (var varName in object) { - window[varName] = object[varName]; // Variables set as global variables on window (core.js is on window can't change the scope). - } - if (!_wrs_isNewElement) { - // If is not a new image we need to load mathml image attribute using a postMessage method - // to avoid CORS policies. - getMethod('_wrs_temporalImage', 'getAttribute', [_wrs_conf_imageMathmlAttribute], function(imageAttribute){ - if (imageAttribute == null) { - getMethod('_wrs_temporalImage', 'getAttribute', ['alt'], function(imageAttributeAlt){ - _wrs_temporalImageAttribute = imageAttributeAlt; - _wrs_int_loaded = true; - }); - } else { - _wrs_temporalImageAttribute = imageAttribute; - _wrs_int_loaded = true; - } - }); - } - _wrs_int_loaded = true; - }); - - // Asyn methods - // waitForIntVariables loads core.js. Don't start until _wrs_int_vars has been loaded from _wrs_int_opener. - wrs_waitForIntVariables(); - // Method waitForCore() loads MathType. Don't start until core.js has been loaded. - wrs_waitForCore(); - - /** - * Pass a wrs_int_opener variable to a callback function using postMessage method in order to avoid - * javascript CORS issues when wrs_int_opener and html editor page are on differents domains (like tinyMCE external plugin). - * For old I.E versions (7,8 & 9) wrs_int_opener is directly called so CORS don't work for them. - * - * @param {String} varName wrs_int_opener variable name. - * @param {Function} callback callback function. - */ - function getVars(varNames, callback) { - _wrs_callbacks.push({ - 'isWirisMessage': true, - 'id': _wrs_callId, - 'callback': callback - }); - - try { - wrs_int_opener.postMessage({'isWirisMessage': true, 'id': _wrs_callId++, 'varNames' : varNames}, '*'); - } - catch (err) { // Method postMessage not defined (I.E 7 & 8 ) or not competible with window object (I.E 9). - _wrs_callbacks.splice(_wrs_callId, 1); - var varObject = {}; - for (var i = 0; i < varNames.length; i++) { - varObject[varNames[i]] = wrs_int_opener[varNames[i]]; - } - callback(varObject); - } - } - - /** - * Call a wrs_int_opener method and pass the result to a callback function using postMessage method in order to avoid - * javascript CORS issues when wrs_int_opener and html editor page are on differents domains (like tinyMCE external plugin). - * For old I.E versions (7,8 & 9) wrs_int_opener is directly called so CORS don't work for them. It is possible to call an object - * method using objectName variable. - * - * @param {String} objectName object name (null to call a wrs_int_opener method). - * @param {String} methodName method name. - * @param {Array} argumentss method arguments ([arg1,..,argn]) - * @param {Function} callback callback function. - */ - function getMethod(objectName, methodName, argumentss, callback) { - _wrs_callbacks.push({ - 'isWirisMessage': true, - 'id': _wrs_callId, - 'callback': callback - }); - try { - wrs_int_opener.postMessage({'isWirisMessage': true, 'id': _wrs_callId++, 'objectName': objectName, 'methodName' : methodName, 'arguments': argumentss}, '*'); - } - catch (err) { // Method postMessage not defined (I.E 7 & 8 ) or not competible with window object (I.E 9). - var object = (objectName == null) ? wrs_int_opener : wrs_int_opener[objectName]; - if (objectName == null) { - callback(object[methodName].apply(object, argumentss)); - } else { // Can't call apply method from some objects on old I.E. - var argumentsStrings = argumentss.join(','); - var result = object[methodName](argumentsStrings); - callback(result); - } - _wrs_callbacks.splice(_wrs_callId, 1); - } - } - - /** - * Load core.js library. - * - */ - function wrs_loadCore() { - // Get MathType version for caching. - var queryParams = window.location.search.substring(1).split("&"); - var version = ""; - for (var i = 0; i < queryParams.length; i++) { - var pos = queryParams[i].indexOf("v="); - if (pos >= 0) { - version = queryParams[i].substring(2); - } - } - // Append core.js. - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = "core.js?v=" + version; - document.getElementsByTagName('head')[0].appendChild(script); - } - - /** - * Load core.js when int variables has been loaded. - * - */ - function wrs_waitForIntVariables() { - if (_wrs_int_loaded) { - wrs_loadCore(); - } else { - setTimeout(wrs_waitForIntVariables, 100); - } - } - - /** - * Cross-browser addEventListener/attachEvent function. - * @param object element Element target - * @param event event Event - * @param function func Function to run - */ - function wrs_addEvent(element, event, func) { - if (element.addEventListener) { - element.addEventListener(event, func, true); - } - else if (element.attachEvent) { - element.attachEvent('on' + event, func); - } - } - - /** - * Load editor from _wrs_conf_editorUrl when core.js has been loaded. - * @return {[type]} [description] - */ - function wrs_waitForCore() { - if (typeof _wrs_conf_core_loaded != 'undefined' && typeof _wrs_conf_configuration_loaded != 'undefined' && _wrs_conf_configuration_loaded == true) { - // Insert editor. - var script = document.createElement('script'); - script.type = 'text/javascript'; - var editorUrl = _wrs_conf_editorUrl; - // Change to https if necessary. - // We create an object url for parse url string and work more efficiently. - var urlObject = document.createElement('a'); - urlObject.href = editorUrl; - - if (window.location.href.indexOf("https://") == 0) { - // It check if browser is https and configuration is http. If this is so, we will replace protocol. - if (urlObject.protocol == 'http:') { - urlObject.protocol = 'https:'; - } - } - // Check protocol and remove port if it's standard. - if (urlObject.port == '80' || urlObject.port == '443') { - editorUrl = urlObject.protocol + '//' + urlObject.hostname + '/' + urlObject.pathname; - } else { - editorUrl = urlObject.protocol + '//' + urlObject.hostname + ':' + urlObject.port + '/' + urlObject.pathname; - } - - // Editor stats. - statEditor = _wrs_conf_editor; - statSaveMode = _wrs_conf_saveMode; - statVersion = _wrs_conf_version; - - script.src = editorUrl + "?lang=" + _wrs_int_langCode + '&stats-editor=' + statEditor + '&stats-mode=' + statSaveMode + '&stats-version=' + statVersion; - document.getElementsByTagName('head')[0].appendChild(script); - } else { - setTimeout(wrs_waitForCore, 200); - } - } - - // Adding events: - // 1.- onMessage event: for enable cross-origin communication between editor window and _wrs_int_opener. - // 2.- onLoad event: inserts MathType into editor.html DOM. - // 3.- onUnload: communicates _wrs_int_opener that editor has been closed. - - wrs_addEvent(window, 'message', function (e) { // Safely enable cross-origin communication. - for (var i = 0; i < _wrs_callbacks.length; ++i) { - if (_wrs_callbacks[i].id == e.data.id && _wrs_callbacks[i].callback != null) { - _wrs_callbacks[i].callback(e.data.value); - _wrs_callbacks.splice(i, 1); - break; - } - } - }); - wrs_addEvent(window, 'load', function () { - function wrs_waitForEditor() { - if ((typeof _wrs_conf_core_loaded != 'undefined') && ('com' in window && 'wiris' in window.com && 'jsEditor' in window.com.wiris)) { - - var queryParams = wrs_getQueryParams(window); - var customEditor; - - wrs_attributes = _wrs_conf_editorParameters; - wrs_attributes.language = queryParams['lang']; - - if (typeof wrs_attributes['toolbar'] == 'undefined' && _wrs_conf_editorToolbar.length > 0) { - wrs_attributes['toolbar'] = _wrs_conf_editorToolbar; - } - - if (typeof(_wrs_int_wirisProperties) != 'undefined') { - for (var key in _wrs_int_wirisProperties) { - if (_wrs_int_wirisProperties.hasOwnProperty(key) && typeof(_wrs_int_wirisProperties[key]) != 'undefined') { - wrs_attributes[key] = _wrs_int_wirisProperties[key]; - } - } - } - - if (customEditor = wrs_int_getCustomEditorEnabled()) { - wrs_attributes['toolbar'] = customEditor.toolbar ? customEditor.toolbar : wrs_attributes['toolbar']; - } - - if (com.wiris.jsEditor.defaultBasePath) { - editor = com.wiris.jsEditor.JsEditor.newInstance(wrs_attributes); - } - else { - editor = new com.wiris.jsEditor.JsEditor('editor', null); - } - - // Set ModalWindow editor attribute. - - var ua = navigator.userAgent.toLowerCase(); - var isAndroid = ua.indexOf("android") > -1; - var isIOS = ((ua.indexOf("ipad") > -1) || (ua.indexOf("iphone") > -1)); - - var editorElement = editor.getElement(); - var editorContainer = document.getElementById('editorContainer'); - editor.insertInto(editorContainer); - - // Mathml content. - if (!_wrs_isNewElement) { - var mathml; - - if (typeof _wrs_conf_useDigestInsteadOfMathml != 'undefined' && _wrs_conf_useDigestInsteadOfMathml) { - mathml = wrs_getCode(_wrs_conf_digestPostVariable, _wrs_temporalImageAttribute); - } - else { - mathml = wrs_mathmlDecode(_wrs_temporalImageAttribute); - } - if (wrs_int_getCustomEditorEnabled() == null && mathml.indexOf('class="wrs_') != -1) { - var classIndexStart = mathml.indexOf('class="wrs_'); - classIndexEnd = mathml.indexOf(" ", classIndexStart); - mathml = mathml.substring(0, classIndexStart) + mathml.substring(classIndexEnd, mathml.length); - } - - editor.setMathMLWithCallback(mathml, function(){window.editorListener.setWaitingForChanges(true);}); - } - - // Submit button. - var controls = document.getElementById('controls'); - var submitButton = document.createElement('input'); - submitButton.type = 'button'; - submitButton.className = 'wrs_button_accept'; - submitButton.background = '#778e9a'; - submitButton.color = '#ffffff' - - submitButton.value = _wrs_stringManager.getString('accept'); - - wrs_addEvent(window, 'beforeunload', function() { - getMethod(null, 'wrs_int_disableCustomEditors', [], function(){ - }); - }); - - wrs_addEvent(submitButton, 'click', function () { - - // In order to avoid n-formulas on n-clicks - // submit button is disabled 1 second. - submitButton.disabled = true; - - setTimeout(function() { - submitButton.disabled = false; - }, 1000); - - // There are vars that are updated during the execution in core.js, we need to sync. - var varsToUpdate = ['_wrs_int_customEditors']; - getVars(varsToUpdate, function(object) { // Callback method to set variables. - for (var varName in object) { - window[varName] = object[varName]; // Variables set as global variables on window (core.js is on window can't change the scope). - } - - var mathml = ''; - - if (!editor.isFormulaEmpty()) { - mathml += editor.getMathML(); // If isn't empty, get mathml code to mathml variable. - if (wrs_int_getCustomEditorEnabled() != null) { - mathml = wrs_mathmlAddEditorAttribute(mathml); - } - else { - var startIndex = mathml.indexOf(' class="'); - if (startIndex != -1) { - var lastIndex = mathml.indexOf('"', startIndex + 8); - mathml = mathml.substring(0, startIndex) + mathml.substring(lastIndex + 1); - } - } - mathml = wrs_mathmlEntities(mathml); // Apply a parse. - } - - getMethod(null, 'wrs_int_updateFormula', [mathml, null, queryParams['lang']], function(){ - _wrs_closeFunction(); - }); - }); - - // Control buttons need to cause a editor blur in order to reset the state for the next time that it is opened. - editor.blur(); - }); - - var buttonContainer = document.getElementById('buttonContainer'); - buttonContainer.appendChild(submitButton); - - // Cancel button. - var cancelButton = document.createElement('input'); - cancelButton.type = 'button'; - cancelButton.className = 'wrs_button_cancel'; - cancelButton.value = _wrs_stringManager.getString('cancel'); - - wrs_addEvent(cancelButton, 'click', function () { - if (editor.isFormulaEmpty() || window.editorListener.getIsContentChanged() === false) { - _wrs_closeFunction(); - // Control buttons need to cause a editor blur in order to reset the state for the next time that it is opened. - editor.blur(); - }else{ - _wrs_showPopUpFunction(); - } - }); - - buttonContainer.appendChild(cancelButton); - - var latexButton = document.getElementById('a_latex'); - latexButton.innerHTML = _wrs_stringManager.getString('latex'); - - var manualButton = document.getElementById('a_manual'); - manualButton.innerHTML = _wrs_stringManager.getString('manual') - - // Class for modal dialog. - if (_wrs_conf_modalWindow) { - if (_wrs_modalWindowProperties.device == 'android') { - buttonContainer.className = buttonContainer.className + ' wrs_modalAndroid'; - } else { - buttonContainer.className = buttonContainer.className + ' wrs_modalDesktop'; - } - } - - var queryLang = ''; - if ('lang' in queryParams){ - queryLang = queryParams['lang'].substr(0, 2); - } - - if ((queryParams['dir'] == 'rtl') || ((queryLang == 'he' || queryLang == 'ar') && queryParams['dir'] != 'ltr')){ - var body = document.getElementsByTagName('BODY'); - body[0].setAttribute("dir","rtl"); - var links = document.getElementById('links'); - links.id = 'links_rtl'; - var controls = document.getElementById('buttonContainer'); - controls.id = 'controls_rtl'; - } - - window.editorListener = new EditorListener(); - editor.getEditorModel().addEditorListener(window.editorListener); - if(_wrs_isNewElement){ - window.editorListener.setWaitingForChanges(true); - } - - // At this point we listen to execute editor methods or fire editor events. - wrs_addEvent(window, 'message', function (e) { - if (e.data.objectName != 'undefined' && e.data.objectName == 'editor') { - editor[e.data.methodName].apply(editor, e.data.arguments); - }else if (e.data.objectName != 'undefined' && e.data.objectName == 'editorCallback') { - editor.setMathMLWithCallback(e.data.arguments[0], function(){window.editorListener.setIsContentChanged(false);window.editorListener.setWaitingForChanges(true);}); - }else if (e.data.objectName != 'undefined' && e.data.objectName == 'editorEvent') { - wrs_fireEvent(window.document, e.data.eventName); - }else if (e.data.objectName != 'undefined' && e.data.objectName == 'editorClose') { - window.editorListener.setWaitingForChanges(false); - window.editorListener.setIsContentChanged(false); - }else if (e.data.objectName != 'undefined' && e.data.objectName == 'editorResize') { - editor.getElement().style.height = (e.data.arguments[0] - controls.offsetHeight) + "px"; - }else if (e.data.objectName != 'undefined' && e.data.objectName == 'checkCloseCondition') { - if (editor.isFormulaEmpty() || window.editorListener.getIsContentChanged() === false) { - _wrs_closeFunction(); - }else{ - _wrs_showPopUpFunction(); - } - }else if (e.data.objectName != 'undefined' && e.data.objectName == 'blur') { - document.activeElement.blur(); - } - }); - - wrs_addEvent(window, 'mouseup', function(e) { - if (_wrs_conf_modalWindow) { - getMethod('_wrs_modalWindow', 'stopDrag', [], null); - } - }); - - wrs_addEvent(window, 'mousedown', function(e) { - if (_wrs_conf_modalWindow) { - getMethod('_wrs_modalWindow', 'setOverlayDiv', [], null); - } - }); - - // Dynamic resize when it is a phone with float keyboard - if (_wrs_conf_modalWindow && isIOS) { - var formulaDisplayDiv = document.getElementsByClassName('wrs_formulaDisplay')[0]; - wrs_addEvent(formulaDisplayDiv, 'focus', function (e) { - if (_wrs_conf_modalWindow) { - getMethod('_wrs_modalWindow', 'openedIosSoftkeyboard', [], null); - } - }); - - wrs_addEvent(formulaDisplayDiv, 'blur', function (e) { - if (_wrs_conf_modalWindow) { - getMethod('_wrs_modalWindow', 'closedIosSoftkeyboard', [], null); - } - }); - - // Set editor size - getMethod('_wrs_modalWindow', 'closedIosSoftkeyboard', [], null); - } - - // Event manager code - wrs_addEvent(window, 'keydown', function(e) { - if (_wrs_conf_modalWindow) { - if (e.key !== undefined && e.repeat === false) { - // Code for detect Esc event - if (e.key === "Escape" || e.key === 'Esc') { - if (editor.isFormulaEmpty() || window.editorListener.getIsContentChanged() === false) { - _wrs_closeFunction(); - }else{ - _wrs_showPopUpFunction(); - } - } - // Code for detect Tab event - if (e.key === "Tab") { - submitButton.focus(); - e.preventDefault(); - } - } - } - }); - - if (!isIOS) { - // Due to IOS use soft keyboard, we don't want to move the cursor to MathType. - editor.focus(); - // Set initial editor height. - editorElement.style.height = (document.getElementById('container').offsetHeight - controls.offsetHeight - 10) + 'px'; - } - } else { - setTimeout(wrs_waitForEditor, 100); - } - - } - wrs_waitForEditor(); - }); - wrs_addEvent(window, 'unload', function () { - getMethod(null, 'wrs_int_notifyWindowClosed', [], function(wrs_int_notifyWindowClosed){ - }); - }); -})(); diff --git a/core/iframe.html b/core/iframe.html deleted file mode 100644 index 0ee841ce..00000000 --- a/core/iframe.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - diff --git a/core/modal.css b/core/modal.css index b37f65bf..7817d5cd 100644 --- a/core/modal.css +++ b/core/modal.css @@ -11,10 +11,39 @@ pointer-events: auto; } +.wrs_modal_overlay.wrs_modal_ios { + visibility: hidden; + display: none; +} + +.wrs_modal_overlay.wrs_modal_android { + visibility: hidden; + display: none; +} + .wrs_modal_overlay.wrs_modal_ios.moodle { position: fixed; } +.wrs_modal_overlay.wrs_modal_desktop.wrs_stack { + background: rgba(0, 0, 0, 0); + display: none; +} + +.wrs_modal_overlay.wrs_modal_desktop.wrs_maximized { + background: rgba(0, 0, 0, 0.8); +} + +.wrs_modal_overlay.wrs_modal_desktop.wrs_minimized { + background: rgba(0, 0, 0, 0); + display: none; +} + +.wrs_modal_overlay.wrs_modal_desktop.wrs_closed { + background: rgba(0, 0, 0, 0); + display: none; +} + .wrs_modal_title { color: #fff; padding: 5px 0 5px 10px; @@ -115,7 +144,7 @@ background-image: url('icons/hover/fulls_icon_h.png'); } -.wrs_modal_iframe { +.wrs_modal_wrapper { display: block; margin: 6px; } @@ -126,7 +155,6 @@ } .wrs_modal_dialogContainer { - overflow: hidden; border: none; background: #fafafa; z-index: 999999; @@ -147,6 +175,12 @@ margin-right: 10px; } +.wrs_modal_dialogContainer.wrs_closed { + visibility: hidden; + display: none; + opacity: 0; +} + /* Class that exists but hasn't got css properties defined .wrs_modal_dialogContainer.wrs_modal_desktop.wrs_minimized.wrs_drag {} */ @@ -190,35 +224,63 @@ /* Class that exists but hasn't got css properties defined -.wrs_modal_iframeContainer.wrs_maximized {} */ +.wrs_content_container.wrs_maximized {} */ -.wrs_modal_iframeContainer.wrs_minimized { +.wrs_content_container.wrs_minimized { display: none; } -.wrs_modal_iframeContainer.wrs_modal_android { +/* .wrs_editor { + flex-grow: 1; +} */ + +.wrs_content_container.wrs_modal_android { width: 100%; - height: 100%; + flex-grow: 1; + display: flex; + flex-direction: column; +} + +.wrs_content_container.wrs_modal_android > div:first-child { + flex-grow: 1; +} + +.wrs_content_container.wrs_modal_ios > div:first-child { + flex-grow: 1; } -.wrs_modal_iframe.wrs_modal_android { +.wrs_content_container.wrs_modal_desktop > div:first-child { + flex-grow: 1; +} + +.wrs_modal_wrapper.wrs_modal_android { margin: auto; + display: flex; + flex-direction: column; + height: 100%; + width: 100%; } -.wrs_modal_iframeContainer.wrs_modal_ios { +.wrs_content_container.wrs_modal_desktop { width: 100%; - height: 100%; + flex-grow: 1; + display: flex; + flex-direction: column; } -.wrs_modal_iframe.wrs_modal_ios { +.wrs_content_container.wrs_modal_ios { width: 100%; - height: 100%; + flex-grow: 1; + display: flex; + flex-direction: column; } -.wrs_modal_iframe.wrs_modal_android { +.wrs_modal_wrapper.wrs_modal_ios { + margin: auto; + display: flex; + flex-direction: column; height: 100%; width: 100%; - margin: auto; } .wrs_virtual_keyboard { @@ -236,7 +298,7 @@ margin: auto; border-width: 0; } - .wrs_modal_iframe.wrs_modal_mobile { + .wrs_modal_wrapper.wrs_modal_mobile { width: 100vmin; height: 100vmin; margin: auto; @@ -250,7 +312,7 @@ margin: auto; border-width: 0; } - .wrs_modal_iframe.wrs_modal_mobile { + .wrs_modal_wrapper.wrs_modal_mobile { width: 100vmin; height: 100vmin; margin: auto; @@ -264,7 +326,7 @@ border-width: 0; } -.wrs_modal_iframe.wrs_modal_badStock { +.wrs_modal_wrapper.wrs_modal_badStock { width: 100%; height: 280px; margin: 0 auto; @@ -300,7 +362,34 @@ cursor: se-resize; } -.wrs_button_cancel { +.wrs_modal_controls { + height: 42px; + margin: 3px 0; + overflow: hidden; + line-height: normal; +} + +.wrs_modal_links { + margin: 10px auto; + margin-bottom: 0; + font-family: arial, sans-serif; + padding: 6px; + display: inline; + float: right; + text-align: right; +} + +.wrs_modal_links > a { + text-decoration: none; + color: #778e9a; + font-size: 16px; +} + +.wrs_modal_button_cancel, +.wrs_modal_button_cancel:hover, +.wrs_modal_button_cancel:visited, +.wrs_modal_button_cancel:active, +.wrs_modal_button_cancel:focus { min-width: 80px; font-size: 14px; border-radius: 3px; @@ -311,9 +400,14 @@ margin-bottom: 0; cursor: pointer; font-family: arial, sans-serif; + background-color: #DDDDDD; } -.wrs_button_accept { +.wrs_modal_button_accept, +.wrs_modal_button_accept:hover, +.wrs_modal_button_accept:visited, +.wrs_modal_button_accept:active, +.wrs_modal_button_accept:focus { min-width: 80px; font-size: 14px; border-radius: 3px; @@ -327,6 +421,48 @@ cursor: pointer; font-family: arial, sans-serif; } + +.wrs_editor_vertical_bar { + height: 20px; + float: right; + background: none; + width: 20px; + cursor: pointer; +} + +.wrs_modal_buttons_container { + display: inline; +} + +.wrs_modal_buttons_container.wrs_modalAndroid { + padding-left: 6px; +} + +.wrs_modal_buttons_container.wrs_modalDesktop { + padding-left: 0; +} + +.wrs_modal_buttons_container > button { + line-height: normal; + background-image: none; +} + +.wrs_modal_wrapper { + margin: 6px; + display: flex; + flex-direction: column; +} + +.wrs_modal_wrapper.wrs_modal_desktop.wrs_minimized { + display: none; +} + +@media only screen and (max-device-width: 480px) and (orientation: portrait) { + #wrs_modal_wrapper { + width: 140%; + } +} + .wrs_popupmessage_overlay_envolture { display: none; width: 100%; @@ -365,3 +501,49 @@ .wrs_popupmessage_button_area { margin: 10px 0 0 0; } + +.wrs_panelContainer * { + border: 0; +} + +.wrs_button_cancel { + min-width: 80px; + font-size: 14px; + border-radius: 3px; + border: 1px solid #778e9a; + padding: 6px 8px; + margin: 10px auto; + margin-left: 5px; + margin-bottom: 0; + cursor: pointer; + font-family: arial, sans-serif; +} + +.wrs_button_accept { + min-width: 80px; + font-size: 14px; + border-radius: 3px; + border: 1px solid #778e9a; + padding: 6px 8px; + margin: 10px auto; + margin-right: 5px; + margin-bottom: 0; + color: #fff; + background: #778e9a; + cursor: pointer; + font-family: arial, sans-serif; +} + +.wrs_editor button{ + box-shadow: none; +} + +.wrs_editor .wrs_header button{ + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.wrs_modal_overlay.wrs_modal_desktop.wrs_stack.wrs_overlay_active { + display: block; +} diff --git a/core/package.json b/core/package.json new file mode 100644 index 00000000..2662fcb3 --- /dev/null +++ b/core/package.json @@ -0,0 +1,14 @@ +{ + "babel": { + "presets": [ + "env" + ] + }, + "scripts": { + "build": "babel core.src.js --out-file core.js --watch" + }, + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-preset-env": "^1.7.0" + } +} diff --git a/lib.php b/lib.php index 0c83ed30..b60f5662 100644 --- a/lib.php +++ b/lib.php @@ -74,6 +74,9 @@ function atto_wiris_params_for_js() { } // Atto js plugin checks if the filter is - or not - active. - return array('lang' => current_language(), 'filter_enabled' => $filterwirisactive, 'version' => get_config('atto_wiris', 'version'), - 'editor_is_active' => $editorisactive, 'chemistry_is_active' => $chemistryisactive); + return array('lang' => current_language(), + 'filter_enabled' => $filterwirisactive, + 'version' => get_config('atto_wiris', 'version'), + 'editor_is_active' => $editorisactive, + 'chemistry_is_active' => $chemistryisactive); } diff --git a/tests/behat/edit_formula.feature b/tests/behat/edit_formula.feature index d3f68d67..7f4d58a0 100644 --- a/tests/behat/edit_formula.feature +++ b/tests/behat/edit_formula.feature @@ -13,11 +13,9 @@ Feature: MathType for Atto And I click on "On" "option" in the "MathType by WIRIS" "table_row" And I open my profile in edit mode And I click on "MathType" "button" - And I switch to "wrs_modal_iframe_id" iframe And I wait "5" seconds And I set mathtype formula to "1+2" - And I click on "//input[@class='wrs_button_accept']" "xpath_element" - And I switch to the main frame + And I click on "//button[@id='wrs_modal_button_accept_id']" "xpath_element" And I click on "Update profile" "button" And I follow "Profile" in the user menu # Checking formula image outside edit element. diff --git a/thirdpartylibs.xml b/thirdpartylibs.xml new file mode 100644 index 00000000..170ae021 --- /dev/null +++ b/thirdpartylibs.xml @@ -0,0 +1,10 @@ + + + + core + MathType JavaScript engine + 7.4.0.1390 + GPL + 3.0+ + + diff --git a/version.php b/version.php index 2c1df1cc..4909eae9 100644 --- a/version.php +++ b/version.php @@ -25,9 +25,9 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2018060400; -$plugin->release = '7.3.0.1389'; +$plugin->version = 2018071200; +$plugin->release = '7.4.0.1390'; $plugin->requires = 2014050800; $plugin->component = 'atto_wiris'; -$plugin->dependencies = array ('filter_wiris' => 2018060400); +$plugin->dependencies = array ('filter_wiris' => 2018071200); $plugin->maturity = MATURITY_STABLE; diff --git a/wirisplugin-generic.js b/wirisplugin-generic.js index cabe19f1..5ecebbb3 100644 --- a/wirisplugin-generic.js +++ b/wirisplugin-generic.js @@ -1,6 +1,6 @@ // Configuration. var _wrs_int_conf_file = "" + M.cfg.wwwroot + "/filter/wiris/integration/configurationjs.php"; -var _wrs_plugin_version = "7.3.0.1389"; +var _wrs_plugin_version = "7.4.0.1390"; var _wrs_int_conf_async = true; // Stats editor (needed by core/editor.js). diff --git a/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-debug.js b/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-debug.js index e2fae47c..b27017cf 100644 --- a/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-debug.js +++ b/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-debug.js @@ -128,13 +128,21 @@ Y.namespace('M.atto_wiris').Button = Y.Base.create('button', Y.M.editor_atto.Edi var host = this.get('host'); var wirisplugin = this; window._wrs_int_currentPlugin = this; - // check if elements of editor exists and return elements + // Check if elements of editor exists and return elements. var wrs_editors_elements_cond = typeof window._wrs_int_editors_elements == "undefined"; window._wrs_int_editors_elements = wrs_editors_elements_cond ? {} : window._wrs_int_editors_elements; // Update textarea value on change. host.on('change', function() { wirisplugin._unparseContent(); }); + // This code parse content when draft is called + // For more information view PLUGINS-1009 + host.on('atto:selectionchanged', function(e) { + // This condition is satisfied when event is throwed by draft + if (typeof e.event == 'undefined') { + wirisplugin._parseContent(); + } + }); // Override updateFromTextArea to update the content editable element. host._wirisUpdateFromTextArea = host.updateFromTextArea; host.updateFromTextArea = function() { @@ -166,7 +174,7 @@ Y.namespace('M.atto_wiris').Button = Y.Base.create('button', Y.M.editor_atto.Edi // We get the HTML content (with the imnages) instead of the raw html content // and convert images into data-mathml attribute. var html = host.editor.get('innerHTML'); - // Check if exist mathml tag for parse + // Check if exist mathml tag for parse. if(html.indexOf('math»') >= 0 || html.indexOf('math>') >= 0){ host.textarea.set('value', wrs_endParse(html, null, this._lang, true)); } diff --git a/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-min.js b/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-min.js index 247c77a7..a0ac9ba5 100644 --- a/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-min.js +++ b/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button-min.js @@ -1 +1 @@ -YUI.add("moodle-atto_wiris-button",function(e,t){e.namespace("M.atto_wiris").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_lang:"en",initializer:function(t){if(!t.filter_enabled)return;this._lang=t.lang,window._wrs_int_langCode=t.lang,window._wrs_plugin_version=t.version,window.wrs_int_notifyWindowClosed=function(){window._wrs_int_popup=null,window._wrs_temporalImage=null,window._wrs_isNewElement=!0},window.wrs_int_updateFormula=function(e,t){var n=window._wrs_int_currentPlugin.get("host").editor.getDOMNode();wrs_updateFormula(n,window,e,null,t,_wrs_int_currentPlugin._lang),window._wrs_int_currentPlugin.markUpdated(),window._wrs_int_currentPlugin._updateEditorImgHandlers()},window.wrs_int_updateCAS=function(e,t,n,r){var i=window._wrs_int_currentPlugin.get("host").editor.getDOMNode();wrs_updateCAS(i,window,e,t,n,r),window._wrs_int_currentPlugin.markUpdated(),window._wrs_int_currentPlugin._updateCasImgHandlers()},window._wrs_int_conf_file=M.cfg.wwwroot+"/filter/wiris/integration/configurationjs.php",window._wrs_int_conf_path=M.cfg.wwwroot+"/lib/editor/atto/plugins/wiris",window._wrs_int_conf_async=!0,window._wrs_int_popup=window._wrs_int_popup||null,window._wrs_int_coreLoading=window._wrs_int_coreLoading||!1,window._wrs_int_path=window._wrs_int_conf_file.split("/"),window._wrs_int_path.pop(),window._wrs_int_path=window._wrs_int_path.join("/");var n=window._wrs_int_path.indexOf("/")===0||window._wrs_int_path.indexOf("http");window._wrs_int_path=n?window._wrs_int_path:window._wrs_int_conf_path+"/"+window._wrs_int_path,window._wrs_isMoodle24=!0,window._wrs_int_customEditors={chemistry:{name:"ChemType",toolbar:"chemistry",icon:"chem.gif",enabled:!1,confVariable:"_wrs_conf_chemEnabled",title:"ChemType"}},window._wrs_int_coreLoading||(window._wrs_int_coreLoading=!0,e.Get.js(window._wrs_int_conf_path+"/core/core.js?v="+t.version,function(e){e}));var r=this.get("host"),i=this;window._wrs_int_currentPlugin=this;var s=typeof window._wrs_int_editors_elements=="undefined";window._wrs_int_editors_elements=s?{}:window._wrs_int_editors_elements,r.on("change",function(){i._unparseContent()}),r._wirisUpdateFromTextArea=r.updateFromTextArea,r.updateFromTextArea=function(){r._wirisUpdateFromTextArea(),i._parseContent()},this._parseContent(),this._addButtons(t);var o=r.textarea.ancestor("form");o&&o.on("submit",this._submitClean,this)},_submitClean:function(){var e=this.get("host"),t=e.editor.get("innerHTML");(t.indexOf("math\u00bb")>=0||t.indexOf("math>")>=0)&&e.textarea.set("value",wrs_endParse(t,null,this._lang,!0))},_addButtons:function(e){parseInt(e.editor_is_active)&&this.addButton({title:"wiris_editor_title",buttonName:"wiris_editor",icon:"formula",iconComponent:"atto_wiris",callback:this._editorButton}),parseInt(e.chemistry_is_active)&&this.addButton({title:"wiris_chem_editor_title",buttonName:"wiris_chem_editor",icon:"chem",iconComponent:"atto_wiris",callback:this._chemEditorButton});var t=this.get("host");t.plugins.collapse&&t.plugins.collapse._setVisibility(t.plugins.collapse.buttons.collapse)},_editorButton:function(){if(_wrs_int_popup)_wrs_int_popup.focus();else{var e=this.get("host");_wrs_int_currentPlugin=this,_wrs_int_popup=wrs_openEditorWindow(this._lang,e.editor.getDOMNode(),!1)}},_chemEditorButton:function(){if(_wrs_int_popup)_wrs_int_popup.focus();else{var e=this.get("host");_wrs_int_currentPlugin=this,wrs_int_enableCustomEditor("chemistry"),_wrs_int_popup=wrs_openEditorWindow(this._lang,e.editor.getDOMNode(),!1)}},_casButton:function(){if(_wrs_int_popup)_wrs_int_popup.focus();else{var e=this.get("host");_wrs_int_currentPlugin=this,_wrs_int_popup=wrs_openCASWindow(e.editor.getDOMNode(),!1,this._lang)}},_parseContent:function(){if(window._wrs_conf_plugin_loaded){var t=this.get("host"),n=t.editor.get("innerHTML");n=wrs_initParse(n,this._lang),t.editor.set("innerHTML",n),this.markUpdated(),this._updateCasImgHandlers(),this._updateEditorImgHandlers()}else e.later(50,this,this._parseContent)},_unparseContent:function(){if(window._wrs_conf_plugin_loaded){var t=this.get("host"),n=t.textarea.get("value");t.textarea.set("value",this._convertSafeMath(wrs_endParse(n,null,this._lang,!0)))}else e.later(50,this,this._unparseContent)},_handleElementDoubleclick:function(e,t,n){if(t.dataset.mathml){n.stopPropagation(),window._wrs_temporalImage=t,window._wrs_isNewElement=!1;var r=window._wrs_temporalImage.getAttribute("data-custom-editor");typeof r!="undefined"&&r&&window[_wrs_int_customEditors[r].confVariable]&&wrs_int_enableCustomEditor(r),window._wrs_int_editors_elements[e.id]._editorButton()}},_handleCasDoubleClick:function(e){window._wrs_temporalImage=e.currentTarget.getDOMNode(),window._wrs_isNewElement=!1,this._casButton(),e.stopPropagation()},_updateEditorImgHandlers:function(){wrs_addElementEvents(this.get("host").editor.getDOMNode(),this._handleElementDoubleclick),window._wrs_int_editors_elements[this.get("host").editor.getDOMNode().id]=this},_updateCasImgHandlers:function(){this.editor.all("img.Wiriscas").each(function(e){e.detachAll("dblclick"),e.on("dblclick",this._handleCasDoubleClick,this)},this)},_convertSafeMath:function(e){var t="",n="\u00abmath",r="\u00ab/math\u00bb",i=e.indexOf(n),s=0;while(i!=-1){t+=e.substring(s,i),imageMathmlAtrribute=e.indexOf(_wrs_conf_imageMathmlAttribute),s=e.indexOf(r,i),s==-1?s=e.length-1:imageMathmlAtrribute!=-1?s+=e.indexOf("/>",i):s+=r.length;if(!wrs_isMathmlInAttribute(e,i)&&imageMathmlAtrribute==-1){var o=e.substring(i,s);t+=wrs_mathmlDecode(o)}else t+=e.substring(i,s);i=e.indexOf(n,s)}return t+=e.substring(s,e.length),t}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","get"]}); +YUI.add("moodle-atto_wiris-button",function(e,t){e.namespace("M.atto_wiris").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_lang:"en",initializer:function(t){if(!t.filter_enabled)return;this._lang=t.lang,window._wrs_int_langCode=t.lang,window._wrs_plugin_version=t.version,window.wrs_int_notifyWindowClosed=function(){window._wrs_int_popup=null,window._wrs_temporalImage=null,window._wrs_isNewElement=!0},window.wrs_int_updateFormula=function(e,t){var n=window._wrs_int_currentPlugin.get("host").editor.getDOMNode();wrs_updateFormula(n,window,e,null,t,_wrs_int_currentPlugin._lang),window._wrs_int_currentPlugin.markUpdated(),window._wrs_int_currentPlugin._updateEditorImgHandlers()},window.wrs_int_updateCAS=function(e,t,n,r){var i=window._wrs_int_currentPlugin.get("host").editor.getDOMNode();wrs_updateCAS(i,window,e,t,n,r),window._wrs_int_currentPlugin.markUpdated(),window._wrs_int_currentPlugin._updateCasImgHandlers()},window._wrs_int_conf_file=M.cfg.wwwroot+"/filter/wiris/integration/configurationjs.php",window._wrs_int_conf_path=M.cfg.wwwroot+"/lib/editor/atto/plugins/wiris",window._wrs_int_conf_async=!0,window._wrs_int_popup=window._wrs_int_popup||null,window._wrs_int_coreLoading=window._wrs_int_coreLoading||!1,window._wrs_int_path=window._wrs_int_conf_file.split("/"),window._wrs_int_path.pop(),window._wrs_int_path=window._wrs_int_path.join("/");var n=window._wrs_int_path.indexOf("/")===0||window._wrs_int_path.indexOf("http");window._wrs_int_path=n?window._wrs_int_path:window._wrs_int_conf_path+"/"+window._wrs_int_path,window._wrs_isMoodle24=!0,window._wrs_int_customEditors={chemistry:{name:"ChemType",toolbar:"chemistry",icon:"chem.gif",enabled:!1,confVariable:"_wrs_conf_chemEnabled",title:"ChemType"}},window._wrs_int_coreLoading||(window._wrs_int_coreLoading=!0,e.Get.js(window._wrs_int_conf_path+"/core/core.js?v="+t.version,function(e){e}));var r=this.get("host"),i=this;window._wrs_int_currentPlugin=this;var s=typeof window._wrs_int_editors_elements=="undefined";window._wrs_int_editors_elements=s?{}:window._wrs_int_editors_elements,r.on("change",function(){i._unparseContent()}),r.on("atto:selectionchanged",function(e){typeof e.event=="undefined"&&i._parseContent()}),r._wirisUpdateFromTextArea=r.updateFromTextArea,r.updateFromTextArea=function(){r._wirisUpdateFromTextArea(),i._parseContent()},this._parseContent(),this._addButtons(t);var o=r.textarea.ancestor("form");o&&o.on("submit",this._submitClean,this)},_submitClean:function(){var e=this.get("host"),t=e.editor.get("innerHTML");(t.indexOf("math\u00bb")>=0||t.indexOf("math>")>=0)&&e.textarea.set("value",wrs_endParse(t,null,this._lang,!0))},_addButtons:function(e){parseInt(e.editor_is_active)&&this.addButton({title:"wiris_editor_title",buttonName:"wiris_editor",icon:"formula",iconComponent:"atto_wiris",callback:this._editorButton}),parseInt(e.chemistry_is_active)&&this.addButton({title:"wiris_chem_editor_title",buttonName:"wiris_chem_editor",icon:"chem",iconComponent:"atto_wiris",callback:this._chemEditorButton});var t=this.get("host");t.plugins.collapse&&t.plugins.collapse._setVisibility(t.plugins.collapse.buttons.collapse)},_editorButton:function(){if(_wrs_int_popup)_wrs_int_popup.focus();else{var e=this.get("host");_wrs_int_currentPlugin=this,_wrs_int_popup=wrs_openEditorWindow(this._lang,e.editor.getDOMNode(),!1)}},_chemEditorButton:function(){if(_wrs_int_popup)_wrs_int_popup.focus();else{var e=this.get("host");_wrs_int_currentPlugin=this,wrs_int_enableCustomEditor("chemistry"),_wrs_int_popup=wrs_openEditorWindow(this._lang,e.editor.getDOMNode(),!1)}},_casButton:function(){if(_wrs_int_popup)_wrs_int_popup.focus();else{var e=this.get("host");_wrs_int_currentPlugin=this,_wrs_int_popup=wrs_openCASWindow(e.editor.getDOMNode(),!1,this._lang)}},_parseContent:function(){if(window._wrs_conf_plugin_loaded){var t=this.get("host"),n=t.editor.get("innerHTML");n=wrs_initParse(n,this._lang),t.editor.set("innerHTML",n),this.markUpdated(),this._updateCasImgHandlers(),this._updateEditorImgHandlers()}else e.later(50,this,this._parseContent)},_unparseContent:function(){if(window._wrs_conf_plugin_loaded){var t=this.get("host"),n=t.textarea.get("value");t.textarea.set("value",this._convertSafeMath(wrs_endParse(n,null,this._lang,!0)))}else e.later(50,this,this._unparseContent)},_handleElementDoubleclick:function(e,t,n){if(t.dataset.mathml){n.stopPropagation(),window._wrs_temporalImage=t,window._wrs_isNewElement=!1;var r=window._wrs_temporalImage.getAttribute("data-custom-editor");typeof r!="undefined"&&r&&window[_wrs_int_customEditors[r].confVariable]&&wrs_int_enableCustomEditor(r),window._wrs_int_editors_elements[e.id]._editorButton()}},_handleCasDoubleClick:function(e){window._wrs_temporalImage=e.currentTarget.getDOMNode(),window._wrs_isNewElement=!1,this._casButton(),e.stopPropagation()},_updateEditorImgHandlers:function(){wrs_addElementEvents(this.get("host").editor.getDOMNode(),this._handleElementDoubleclick),window._wrs_int_editors_elements[this.get("host").editor.getDOMNode().id]=this},_updateCasImgHandlers:function(){this.editor.all("img.Wiriscas").each(function(e){e.detachAll("dblclick"),e.on("dblclick",this._handleCasDoubleClick,this)},this)},_convertSafeMath:function(e){var t="",n="\u00abmath",r="\u00ab/math\u00bb",i=e.indexOf(n),s=0;while(i!=-1){t+=e.substring(s,i),imageMathmlAtrribute=e.indexOf(_wrs_conf_imageMathmlAttribute),s=e.indexOf(r,i),s==-1?s=e.length-1:imageMathmlAtrribute!=-1?s+=e.indexOf("/>",i):s+=r.length;if(!wrs_isMathmlInAttribute(e,i)&&imageMathmlAtrribute==-1){var o=e.substring(i,s);t+=wrs_mathmlDecode(o)}else t+=e.substring(i,s);i=e.indexOf(n,s)}return t+=e.substring(s,e.length),t}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","get"]}); diff --git a/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button.js b/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button.js index a0da3df1..f03342aa 100644 --- a/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button.js +++ b/yui/build/moodle-atto_wiris-button/moodle-atto_wiris-button.js @@ -127,13 +127,21 @@ Y.namespace('M.atto_wiris').Button = Y.Base.create('button', Y.M.editor_atto.Edi var host = this.get('host'); var wirisplugin = this; window._wrs_int_currentPlugin = this; - // check if elements of editor exists and return elements + // Check if elements of editor exists and return elements. var wrs_editors_elements_cond = typeof window._wrs_int_editors_elements == "undefined"; window._wrs_int_editors_elements = wrs_editors_elements_cond ? {} : window._wrs_int_editors_elements; // Update textarea value on change. host.on('change', function() { wirisplugin._unparseContent(); }); + // This code parse content when draft is called + // For more information view PLUGINS-1009 + host.on('atto:selectionchanged', function(e) { + // This condition is satisfied when event is throwed by draft + if (typeof e.event == 'undefined') { + wirisplugin._parseContent(); + } + }); // Override updateFromTextArea to update the content editable element. host._wirisUpdateFromTextArea = host.updateFromTextArea; host.updateFromTextArea = function() { @@ -165,7 +173,7 @@ Y.namespace('M.atto_wiris').Button = Y.Base.create('button', Y.M.editor_atto.Edi // We get the HTML content (with the imnages) instead of the raw html content // and convert images into data-mathml attribute. var html = host.editor.get('innerHTML'); - // Check if exist mathml tag for parse + // Check if exist mathml tag for parse. if(html.indexOf('math»') >= 0 || html.indexOf('math>') >= 0){ host.textarea.set('value', wrs_endParse(html, null, this._lang, true)); } diff --git a/yui/src/button/js/button.js b/yui/src/button/js/button.js index 2ab07820..dc4ab9c7 100644 --- a/yui/src/button/js/button.js +++ b/yui/src/button/js/button.js @@ -126,13 +126,21 @@ Y.namespace('M.atto_wiris').Button = Y.Base.create('button', Y.M.editor_atto.Edi var host = this.get('host'); var wirisplugin = this; window._wrs_int_currentPlugin = this; - // check if elements of editor exists and return elements + // Check if elements of editor exists and return elements. var wrs_editors_elements_cond = typeof window._wrs_int_editors_elements == "undefined"; window._wrs_int_editors_elements = wrs_editors_elements_cond ? {} : window._wrs_int_editors_elements; // Update textarea value on change. host.on('change', function() { wirisplugin._unparseContent(); }); + // This code parse content when draft is called + // For more information view PLUGINS-1009 + host.on('atto:selectionchanged', function(e) { + // This condition is satisfied when event is throwed by draft + if (typeof e.event == 'undefined') { + wirisplugin._parseContent(); + } + }); // Override updateFromTextArea to update the content editable element. host._wirisUpdateFromTextArea = host.updateFromTextArea; host.updateFromTextArea = function() { @@ -164,7 +172,7 @@ Y.namespace('M.atto_wiris').Button = Y.Base.create('button', Y.M.editor_atto.Edi // We get the HTML content (with the imnages) instead of the raw html content // and convert images into data-mathml attribute. var html = host.editor.get('innerHTML'); - // Check if exist mathml tag for parse + // Check if exist mathml tag for parse. if(html.indexOf('math»') >= 0 || html.indexOf('math>') >= 0){ host.textarea.set('value', wrs_endParse(html, null, this._lang, true)); }