function decodeText(text) { var escapeXml = options.escapeXmlEntities; var r; text = text.replace(//gi, "\n"); if (escapeXml == 'always' || escapeXml == 'partial' || escapeXml == 'html') { text = text.replace(/</g, '<'); text = text.replace(/>/g, '>'); } if (escapeXml == 'html') { text = text.replace(/ /g, "\xA0"); text = text.replace(/&#(\d+);/g, function(str, p1) { return String.fromCharCode(parseInt(p1)); }); text = text.replace(/&#x([0-9a-f]+);/gi, function(str, p1) { return String.fromCharCode(parseInt(p1, 16)); }); text = text.replace(/ +/g, " "); // truncate multiple spaces to single space text = text.replace(/\xA0/g, " "); } if (escapeXml == 'always' || escapeXml == 'html') { text = text.replace(/'/g, "'"); text = text.replace(/"/g, '"'); text = text.replace(/&/g, '&'); } if ('true' == options.escapeDollar) { text = text.replace(/([^\\])\$\{/g, '$1$$$${'); // replace ${...} to $${...} text = text.replace(/^\$\{/g, '$$$${'); // replace ${...} to $${...} text = text.replace(/\\\$\{/g, '$${'); // replace \${...} to ${...} } text = text.replace(/^\s+/, ""); text = text.replace(/\s+$/, ""); return text; } function encodeText(text) { var escapeXml = options.escapeXmlEntities; if (escapeXml == 'always') { // & -> & // & -> &amp; // " -> &quot; //   ->   text = text.replace(/&(\w+);/g, '%%tmp_entity%%$1%%'); text = text.replace(/%%tmp_entity%%(amp|apos|quot|lt|gt)%%/g, '&$1;'); text = text.replace(/&/g, '&'); text = text.replace(/%%tmp_entity%%(\w+)%%/g, '&$1;'); text = text.replace(/\'/g, '''); text = text.replace(/\"/g, '"'); } else if (escapeXml == 'html') { // & -> & // ' -> ' // \xA0 ->   // & -> &amp; // " -> &quot; //   -> &nbsp; text = text.replace(/&(nbsp|amp|quot|apos|lt|gt|\d+|x\d+)(;|\W)/g, '&$1$2'); text = text.replace(/\xA0/g, ' '); text = text.replace(/ {2,}/g, function(str) { var result = ''; for (var i = 0; i < str.length; i++) { result += ' '; } return result; }); // convert multiple spaces to nbsp } if (escapeXml == 'always' || escapeXml == 'partial') { text = text.replace(//g, '>'); } if ('true' == options.escapeDollar) { text = text.replace(/([^\$])\$\{/g, '$1\\${'); // replace ${...} to \${...} text = text.replace(/^\$\{/g, '\\${'); // replace ${...} to \${...} text = text.replace(/\$\$\{/g, '${'); // replace $${...} to ${...} } text = text.replace(/\n/g, "
"); return text; } function convertText(command, converter) { var props = ['command', 'target', 'value']; for (var i = 0; i < props.length; i++) { var prop = props[i]; command[prop] = converter(command[prop]); } } /** * Parse source and update TestCase. Throw an exception if any error occurs. * * @param testCase TestCase to update * @param source The source to parse */ function parse(testCase, source) { var commandRegexp = new RegExp(options.commandLoadPattern, 'i'); var commentRegexp = new RegExp(options.commentLoadPattern, 'i'); var commandOrCommentRegexp = new RegExp("((" + options.commandLoadPattern + ")|(" + options.commentLoadPattern + "))", 'ig'); var doc = source; var commands = []; var commandFound = false; var lastIndex; while (true) { //log.debug("doc=" + doc + ", commandRegexp=" + commandRegexp); lastIndex = commandOrCommentRegexp.lastIndex; var docResult = commandOrCommentRegexp.exec(doc); if (docResult) { if (docResult[2]) { // command var command = new Command(); command.skip = docResult.index - lastIndex; command.index = lastIndex; var result = commandRegexp.exec(doc.substring(lastIndex)); eval(options.commandLoadScript, {command: command, result: result}); convertText(command, decodeText); commands.push(command); if (!commandFound) { // remove comments before the first command or comment for (var i = commands.length - 1; i >= 0; i--) { if (commands[i].skip > 0) { commands.splice(0, i); break; } } testCase.header = doc.substr(0, commands[0].index); commandFound = true; } } else { // comment var comment = new Comment(); comment.skip = docResult.index - lastIndex; comment.index = docResult.index; var result = commentRegexp.exec(doc.substring(lastIndex)); eval(options.commentLoadScript, {comment: comment, result: result}); commands.push(comment); } } else { break; } } if (commands.length > 0) { testCase.footer = doc.substring(lastIndex); log.debug("header=" + testCase.header); log.debug("footer=" + testCase.footer); //log.debug("commands.length=" + commands.length); testCase.commands = commands; } else { throw "no command found"; } } function getSourceForCommand(commandObj) { var command = null; var comment = null; var template = ''; if (commandObj.type == 'command') { command = commandObj; command = command.createCopy(); convertText(command, this.encodeText); template = options.commandTemplate; } else if (commandObj.type == 'comment') { comment = commandObj; template = options.commentTemplate; } var result; var text = template.replace(/\$\{([a-zA-Z0-9_\.]+)\}/g, function(str, p1, offset, s) { result = eval(p1, {command: command, comment: comment}); return result != null ? result : ''; }); return text; } /** * Format an array of commands to the snippet of source. * Used to copy the source into the clipboard. * * @param The array of commands to sort. */ function formatCommands(commands) { var commandsText = ''; for (i = 0; i < commands.length; i++) { var text = getSourceForCommand(commands[i]); commandsText = commandsText + text; } return commandsText; } /** * Format TestCase and return the source. * The 3rd and 4th parameters are used only in default HTML format. * * @param testCase TestCase to format * @param name The name of the test case, if any. It may be used to embed title into the source. * @param saveHeaderAndFooter true if the header and footer should be saved into the TestCase. * @param useDefaultHeaderAndFooter Parameter used for only default format. */ function format(testCase, name, saveHeaderAndFooter, useDefaultHeaderAndFooter) { var text; var commandsText = ""; var testText; var i; for (i = 0; i < testCase.commands.length; i++) { var text = getSourceForCommand(testCase.commands[i]); commandsText = commandsText + text; } var testText; if (testCase.header == null || testCase.footer == null || useDefaultHeaderAndFooter) { testText = options.testTemplate; testText = testText.replace(/\$\{name\}/g, name); var encoding = options["global.encoding"]; if (!encoding) encoding = "UTF-8"; testText = testText.replace(/\$\{encoding\}/g, encoding); var commandsIndex = testText.indexOf("${commands}"); if (commandsIndex >= 0) { var header = testText.substr(0, commandsIndex); var footer = testText.substr(commandsIndex + "${commands}".length); testText = header + commandsText + footer; if (saveHeaderAndFooter) { testCase.header = header; testCase.footer = footer; } } } else { testText = testCase.header + commandsText + testCase.footer; } return testText; } /* * Optional: The customizable option that can be used in format/parse functions. */ this.options = { commandLoadPattern: "]*>" + "\\s*()?" + "\\s*]*>\\s*([\\w]*?)\\s*" + "\\s*]*>([\\d\\D]*?)" + "\\s*(]*>([\\d\\D]*?)|)" + "\\s*\\s*", commandLoadScript: "command.command = result[2];\n" + "command.target = result[3];\n" + "command.value = result[5] || '';\n", commentLoadPattern: "\\s*", commentLoadScript: "comment.comment = result[1];\n", testTemplate: "\n" + "\n" + '\n' + "${name}\n" + "\n" + "\n" + '\n'+ '\n' + '\n' + "\n" + "${commands}\n" + "
${name}
\n" + "\n" + "\n", commandTemplate: "\n" + "\t${command.command}\n" + "\t${command.target}\n" + "\t${command.value}\n" + "\n", commentTemplate: "\n", escapeXmlEntities: "html", escapeDollar: "false" }; /* * Optional: XUL XML String for the UI of the options dialog */ this.configForm = //'' + //'' + //'' + 'Regular expression for each command entry' + '' + '' + 'Script to load command from the pattern' + '' + //'' + //'Regular expression for comments between commands' + //'' + //'' + //'Script to load comment from the pattern' + //'' + '' + //'' + //'' + //'' + 'Template for new test html file' + '' + '' + 'Template for command entries in the test html file' + '' + '' + 'Escape XML / HTML entities?' + '' + '' + '' + '' + '' + '' + '' + '' + ''; //'' + //'Template for comment entries in the test html file' + //'' + //'';