Convert markdown links from inline to reference - regex
I have a changelog file formatted using Github's markdown.
Initially I used inline links for every link I needed to add, that is:
This is some [example](http://www.stackoverflow.com) line of text.
Over time, as the file grew in size, it became a bit messy due mainly to this way of inserting links.
I'd like to convert all links from inline to reference (see description of each), that is convert the above line to this:
This is some [example][1] line of text.
[1]: http://www.stackoverflow.com
Since the file is rather large and contains many inline links, I was wondering if there is some automated way to do this. I use Sublime Text 3 to edit, but I couldn't find a suitable package for this task. Perhaps some clever regex?
That's a great requirement!
I've just created a new Node.js program (I know it's not a GUI but seems something more people would like the capability of) to do this on GitHub.
Here's also the code:
// node main.js test.md result.md
var fs = require('fs')
fs.readFile(process.argv[2], 'utf8', function (err, markdown) {
if (err) {
return console.log(err);
}
var counter = 1;
var matches = {};
var matcher = /\[.*?\]\((.*?)\)/g;
while (match = matcher.exec(markdown)) {
if (!matches[match[1]]) matches[match[1]] = counter++;
}
console.log(matches);
Object.keys(matches).forEach(function(url) {
var r = new RegExp("(\\[.*?\\])\\(" + url + "\\)", "g");
markdown = markdown.replace(r, "$1[" + matches[url] + "]");
markdown += "\n[" + matches[url] + "]: " + url;
});
fs.writeFile(process.argv[3], markdown, 'utf8', function (err) {
if (err) return console.log(err);
});
});
Save this as mdrelink.py in your Packages folder, and you can then run it with
view.run_command('mdrelink');
from within the command console.
I think I got the order thingy right – reversing is necessary because otherwise it would mess up the already cached indexes of next items. It should also automatically skip already used link numbers. My first Python and my first Sublime plugin, so please be gentle with me.
import sublime, sublime_plugin
class mdrelinkCommand(sublime_plugin.TextCommand):
def run(self, edit):
oldlinks = []
self.view.find_all("^\s*(\[\d+\]):", sublime.IGNORECASE, "\\1", oldlinks)
newlinkpos = self.view.find_all("\[.+?\](\(.+?\))")
orgtext = []
self.view.find_all("(\[.+?\])\(.+?\)", sublime.IGNORECASE, "\\1", orgtext)
orglink = []
self.view.find_all("\[.+?\]\((.+?)\)", sublime.IGNORECASE, "\\1", orglink)
orglink.reverse()
self.view.insert(edit, self.view.size(), '\n\n')
counter = 1
newnumbers = []
for r in newlinkpos:
while '['+str(counter)+']' in oldlinks:
counter += 1
oldlinks.append('['+str(counter)+']')
line = '[' + str(counter)+']: '+ orglink.pop() + '\n'
newnumbers.append(' ['+str(counter)+']')
self.view.insert(edit, self.view.size(), line)
for r in reversed(newlinkpos):
self.view.replace(edit, r, orgtext.pop()+newnumbers.pop())
Came across this question thanks to Google. Maybe this can help others:
My answer isn't Sublime specific, but if you're using JavaScript (Node) already, I'd use a Markdown parser and CST transformer like remark.
For example, to transform all the inline links in README.md to numerically-ascending reference-style links, you could run the following at your project's root:
npm install --save-dev remark-cli remark-renumber-references
npx remark --no-stdout --output --use renumber-references README.md
Or, if you want reference-style links derived from the source uri:
npm install --save-dev remark-cli remark-defsplit
npx remark --no-stdout --output --use defsplit README.md
Hopefully this info helps people like me not waste a whole day hacking together some horrendously unreliable regexp-based solution to this :)
extending #bjfletcher's answer, below is the code that considers any existing reference links.
Link to GitHub gist.
// node filename.js ReadMe.md RefLinks.md
import * as fs from 'fs'
fs.readFile(process.argv[2], 'utf8', function (err, mainMarkdown) {
if (err) {
return console.log(err);
}
let newMarkdown = existingRefLinks(mainMarkdown);
var counter = 1;
var matches = {};
var matcher = /\[.*?\]\((.*?)\)/g
let match;
while (match = matcher.exec(newMarkdown)) {
if (!matches[match[1]]) matches[match[1]] = counter++;
}
console.log(matches);
Object.keys(matches).forEach(function (url) {
var r = new RegExp("(\\[.*?\\])\\(" + url + "\\)", "g");
newMarkdown = newMarkdown.replace(r, "$1[" + matches[url] + "]");
newMarkdown += "\n[" + matches[url] + "]: " + url;
});
fs.writeFile(process.argv[3], newMarkdown, 'utf8', function (err) {
if (err) return console.log(err);
});
});
function existingRefLinks(markdown) {
let refLinks = {}, match;
const matcher = /\[(\d)]:\s(.*)/g; // /\[.*?\]\((.*?)\)/g
while (match = matcher.exec(markdown)) {
if (!refLinks[match[1]]) refLinks[match[1]] = match[2];
}
markdown = markdown.replaceAll(matcher, "")
Object.keys(refLinks).forEach(function (int) {
markdown = markdown.replace("][" + int + "]", "](" + refLinks[int] + ")");
});
return markdown
}
Note that I prefer using #Xunnamius's answer:
npm install --save-dev remark-reference-links remark-cli
npx remark README.md -o --use reference-links
Related
Typescript regex exclude whole string if followed by specific string
I'm been running into weird issues with regex and Typescript in which I'm trying to have my expression replace the value of test minus the first instance if followed by test. In other words, replace the first two lines that have test but for the third line below, replace only the second value of test. [test] [test].[db] [test].[test] Where it should look like: [newvalue] [newvalue].[db] [test].[newvalue] I've come up with lots of variations but this is the one that I thought was simple enough to solve it and regex101 can confirm this works: \[(\w+)\](?!\.\[test\]) But when using Typescript (custom task in VSTS build), it actually replaces the values like this: [newvalue] [newvalue].[db] [newvalue].[test] Update: It looks like a regex like (test)(?!.test) breaks when changing the use cases removing the square brackets, which makes me think this might be somewhere in the code. Could the problem be with the index that the value is replaced at? Some of the code in Typescript that is calling this: var filePattern = tl.getInput("filePattern", true); var tokenRegex = tl.getInput("tokenRegex", true); for (var i = 0; i < files.length; i++) { var file = files[i]; console.info(`Starting regex replacement in [${file}]`); var contents = fs.readFileSync(file).toString(); var reg = new RegExp(tokenRegex, "g"); // loop through each match var match: RegExpExecArray; // keep a separate var for the contents so that the regex index doesn't get messed up // by replacing items underneath it var newContents = contents; while((match = reg.exec(contents)) !== null) { var vName = match[1]; // find the variable value in the environment var vValue = tl.getVariable(vName); if (typeof vValue === 'undefined') { tl.warning(`Token [${vName}] does not have an environment value`); } else { newContents = newContents.replace(match[0], vValue); console.info(`Replaced token [${vName }]`); } } } Full code is for the task I'm using this with: https://github.com/colindembovsky/cols-agent-tasks/blob/master/Tasks/ReplaceTokens/replaceTokens.ts
For me this regex is working like you are expecting: \[(test)\](?!\.\[test\]) with a Typescript code like that myString.replace(/\[(test)\](?!\.\[test\])/g, "[newvalue]"); Instead, the regex you are using should replace also the [db] part. I've tried with this code: class Greeter { myString1: string; myString2: string; myString3: string; greeting: string; constructor(str1: string, str2: string, str3: string) { this.myString1 = str1.replace(/\[(test)\](?!\.\[test\])/g, "[newvalue]"); this.myString2 = str2.replace(/\[(test)\](?!\.\[test\])/g, "[newvalue]"); this.myString3 = str3.replace(/\[(test)\](?!\.\[test\])/g, "[newvalue]"); this.greeting = this.myString1 + "\n" + this.myString2 + "\n" + this.myString3; } greet() { return "Hello, these are your replacements:\n" + this.greeting; } } let greeter = new Greeter("[test]", "[test].[db]", "[test].[test]"); let button = document.createElement('button'); button.textContent = "Say Hello"; button.onclick = function() { alert(greeter.greet()); } document.body.appendChild(button); Online playground here.
Evaluate es6 template literals without eval() and new Function [duplicate]
Is it possible to create a template string as a usual string, let a = "b:${b}"; and then convert it into a template string, let b = 10; console.log(a.template()); // b:10 without eval, new Function and other means of dynamic code generation?
In my project I've created something like this with ES6: String.prototype.interpolate = function(params) { const names = Object.keys(params); const vals = Object.values(params); return new Function(...names, `return \`${this}\`;`)(...vals); } const template = 'Example text: ${text}'; const result = template.interpolate({ text: 'Foo Boo' }); console.log(result);
As your template string must get reference to the b variable dynamically (in runtime), so the answer is: NO, it's impossible to do it without dynamic code generation. But, with eval it's pretty simple: let tpl = eval('`'+a+'`');
No, there is not a way to do this without dynamic code generation. However, I have created a function which will turn a regular string into a function which can be provided with a map of values, using template strings internally. Generate Template String Gist /** * Produces a function which uses template strings to do simple interpolation from objects. * * Usage: * var makeMeKing = generateTemplateString('${name} is now the king of ${country}!'); * * console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'})); * // Logs 'Bryan is now the king of Scotland!' */ var generateTemplateString = (function(){ var cache = {}; function generateTemplate(template){ var fn = cache[template]; if (!fn){ // Replace ${expressions} (etc) with ${map.expressions}. var sanitized = template .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){ return `\$\{map.${match.trim()}\}`; }) // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string. .replace(/(\$\{(?!map\.)[^}]+\})/g, ''); fn = Function('map', `return \`${sanitized}\``); } return fn; } return generateTemplate; })(); Usage: var kingMaker = generateTemplateString('${name} is king!'); console.log(kingMaker({name: 'Bryan'})); // Logs 'Bryan is king!' to the console. Hope this helps somebody. If you find a problem with the code, please be so kind as to update the Gist.
What you're asking for here: //non working code quoted from the question let b=10; console.log(a.template());//b:10 is exactly equivalent (in terms of power and, er, safety) to eval: the ability to take a string containing code and execute that code; and also the ability for the executed code to see local variables in the caller's environment. There is no way in JS for a function to see local variables in its caller, unless that function is eval(). Even Function() can't do it. When you hear there's something called "template strings" coming to JavaScript, it's natural to assume it's a built-in template library, like Mustache. It isn't. It's mainly just string interpolation and multiline strings for JS. I think this is going to be a common misconception for a while, though. :(
There are many good solutions posted here, but none yet which utilizes the ES6 String.raw method. Here is my contriubution. It has an important limitation in that it will only accept properties from a passed in object, meaning no code execution in the template will work. function parseStringTemplate(str, obj) { let parts = str.split(/\$\{(?!\d)[\wæøåÆØÅ]*\}/); let args = str.match(/[^{\}]+(?=})/g) || []; let parameters = args.map(argument => obj[argument] || (obj[argument] === undefined ? "" : obj[argument])); return String.raw({ raw: parts }, ...parameters); } let template = "Hello, ${name}! Are you ${age} years old?"; let values = { name: "John Doe", age: 18 }; parseStringTemplate(template, values); // output: Hello, John Doe! Are you 18 years old? Split string into non-argument textual parts. See regex. parts: ["Hello, ", "! Are you ", " years old?"] Split string into property names. Empty array if match fails. args: ["name", "age"] Map parameters from obj by property name. Solution is limited by shallow one level mapping. Undefined values are substituted with an empty string, but other falsy values are accepted. parameters: ["John Doe", 18] Utilize String.raw(...) and return result.
TLDR: https://jsfiddle.net/bj89zntu/1/ Everyone seems to be worried about accessing variables. Why not just pass them? I'm sure it won't be too hard to get the variable context in the caller and pass it down. Use ninjagecko's answer to get the props from obj. function renderString(str,obj){ return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)}) } Here is the full code: function index(obj,is,value) { if (typeof is == 'string') is=is.split('.'); if (is.length==1 && value!==undefined) return obj[is[0]] = value; else if (is.length==0) return obj; else return index(obj[is[0]],is.slice(1), value); } function renderString(str,obj){ return str.replace(/\$\{.+?\}/g,(match)=>{return index(obj,match)}) } renderString('abc${a}asdas',{a:23,b:44}) //abc23asdas renderString('abc${a.c}asdas',{a:{c:22,d:55},b:44}) //abc22asdas
The issue here is to have a function that has access to the variables of its caller. This is why we see direct eval being used for template processing. A possible solution would be to generate a function taking formal parameters named by a dictionary's properties, and calling it with the corresponding values in the same order. An alternative way would be to have something simple as this: var name = "John Smith"; var message = "Hello, my name is ${name}"; console.log(new Function('return `' + message + '`;')()); And for anyone using Babel compiler we need to create closure which remembers the environment in which it was created: console.log(new Function('name', 'return `' + message + '`;')(name));
I liked s.meijer's answer and wrote my own version based on his: function parseTemplate(template, map, fallback) { return template.replace(/\$\{[^}]+\}/g, (match) => match .slice(2, -1) .trim() .split(".") .reduce( (searchObject, key) => searchObject[key] || fallback || match, map ) ); }
Similar to Daniel's answer (and s.meijer's gist) but more readable: const regex = /\${[^{]+}/g; export default function interpolate(template, variables, fallback) { return template.replace(regex, (match) => { const path = match.slice(2, -1).trim(); return getObjPath(path, variables, fallback); }); } //get the specified property or nested property of an object function getObjPath(path, obj, fallback = '') { return path.split('.').reduce((res, key) => res[key] || fallback, obj); } Note: This slightly improves s.meijer's original, since it won't match things like ${foo{bar} (the regex only allows non-curly brace characters inside ${ and }). UPDATE: I was asked for an example using this, so here you go: const replacements = { name: 'Bob', age: 37 } interpolate('My name is ${name}, and I am ${age}.', replacements)
#Mateusz Moska, solution works great, but when i used it in React Native(build mode), it throws an error: Invalid character '`', though it works when i run it in debug mode. So i wrote down my own solution using regex. String.prototype.interpolate = function(params) { let template = this for (let key in params) { template = template.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), params[key]) } return template } const template = 'Example text: ${text}', result = template.interpolate({ text: 'Foo Boo' }) console.log(result) Demo: https://es6console.com/j31pqx1p/ NOTE: Since I don't know the root cause of an issue, i raised a ticket in react-native repo, https://github.com/facebook/react-native/issues/14107, so that once they can able to fix/guide me about the same :)
You can use the string prototype, for example String.prototype.toTemplate=function(){ return eval('`'+this+'`'); } //... var a="b:${b}"; var b=10; console.log(a.toTemplate());//b:10 But the answer of the original question is no way.
I required this method with support for Internet Explorer. It turned out the back ticks aren't supported by even IE11. Also; using eval or it's equivalent Function doesn't feel right. For the one that notice; I also use backticks, but these ones are removed by compilers like babel. The methods suggested by other ones, depend on them on run-time. As said before; this is an issue in IE11 and lower. So this is what I came up with: function get(path, obj, fb = `$\{${path}}`) { return path.split('.').reduce((res, key) => res[key] || fb, obj); } function parseTpl(template, map, fallback) { return template.replace(/\$\{.+?}/g, (match) => { const path = match.substr(2, match.length - 3).trim(); return get(path, map, fallback); }); } Example output: const data = { person: { name: 'John', age: 18 } }; parseTpl('Hi ${person.name} (${person.age})', data); // output: Hi John (18) parseTpl('Hello ${person.name} from ${person.city}', data); // output: Hello John from ${person.city} parseTpl('Hello ${person.name} from ${person.city}', data, '-'); // output: Hello John from -
I currently can't comment on existing answers so I am unable to directly comment on Bryan Raynor's excellent response. Thus, this response is going to update his answer with a slight correction. In short, his function fails to actually cache the created function, so it will always recreate, regardless of whether it's seen the template before. Here is the corrected code: /** * Produces a function which uses template strings to do simple interpolation from objects. * * Usage: * var makeMeKing = generateTemplateString('${name} is now the king of ${country}!'); * * console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'})); * // Logs 'Bryan is now the king of Scotland!' */ var generateTemplateString = (function(){ var cache = {}; function generateTemplate(template){ var fn = cache[template]; if (!fn){ // Replace ${expressions} (etc) with ${map.expressions}. var sanitized = template .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){ return `\$\{map.${match.trim()}\}`; }) // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string. .replace(/(\$\{(?!map\.)[^}]+\})/g, ''); fn = cache[template] = Function('map', `return \`${sanitized}\``); } return fn; }; return generateTemplate; })();
Still dynamic but seems more controlled than just using a naked eval: const vm = require('vm') const moment = require('moment') let template = '### ${context.hours_worked[0].value} \n Hours worked \n #### ${Math.abs(context.hours_worked_avg_diff[0].value)}% ${fns.gt0(context.hours_worked_avg_diff[0].value, "more", "less")} than usual on ${fns.getDOW(new Date())}' let context = { hours_worked:[{value:10}], hours_worked_avg_diff:[{value:10}], } function getDOW(now) { return moment(now).locale('es').format('dddd') } function gt0(_in, tVal, fVal) { return _in >0 ? tVal: fVal } function templateIt(context, template) { const script = new vm.Script('`'+template+'`') return script.runInNewContext({context, fns:{getDOW, gt0 }}) } console.log(templateIt(context, template)) https://repl.it/IdVt/3
I made my own solution doing a type with a description as a function export class Foo { ... description?: Object; ... } let myFoo:Foo = { ... description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`. ... } and so doing: let myDescription = myFoo.description('Bar', 'bar');
I came up with this implementation and it works like a charm. function interpolateTemplate(template: string, args: any): string { return Object.entries(args).reduce( (result, [arg, val]) => result.replace(`$\{${arg}}`, `${val}`), template, ) } const template = 'This is an example: ${name}, ${age} ${email}' console.log(interpolateTemplate(template,{name:'Med', age:'20', email:'example#abc.com'})) You could raise an error if arg is not found in template
This solution works without ES6: function render(template, opts) { return new Function( 'return new Function (' + Object.keys(opts).reduce((args, arg) => args += '\'' + arg + '\',', '') + '\'return `' + template.replace(/(^|[^\\])'/g, '$1\\\'') + '`;\'' + ').apply(null, ' + JSON.stringify(Object.keys(opts).reduce((vals, key) => vals.push(opts[key]) && vals, [])) + ');' )(); } render("hello ${ name }", {name:'mo'}); // "hello mo" Note: the Function constructor is always created in the global scope, which could potentially cause global variables to be overwritten by the template, e.g. render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
You should try this tiny JS module, by Andrea Giammarchi, from github : https://github.com/WebReflection/backtick-template /*! (C) 2017 Andrea Giammarchi - MIT Style License */ function template(fn, $str, $object) {'use strict'; var stringify = JSON.stringify, hasTransformer = typeof fn === 'function', str = hasTransformer ? $str : fn, object = hasTransformer ? $object : $str, i = 0, length = str.length, strings = i < length ? [] : ['""'], values = hasTransformer ? [] : strings, open, close, counter ; while (i < length) { open = str.indexOf('${', i); if (-1 < open) { strings.push(stringify(str.slice(i, open))); open += 2; close = open; counter = 1; while (close < length) { switch (str.charAt(close++)) { case '}': counter -= 1; break; case '{': counter += 1; break; } if (counter < 1) { values.push('(' + str.slice(open, close - 1) + ')'); break; } } i = close; } else { strings.push(stringify(str.slice(i))); i = length; } } if (hasTransformer) { str = 'function' + (Math.random() * 1e5 | 0); if (strings.length === values.length) strings.push('""'); strings = [ str, 'with(this)return ' + str + '([' + strings + ']' + ( values.length ? (',' + values.join(',')) : '' ) + ')' ]; } else { strings = ['with(this)return ' + strings.join('+')]; } return Function.apply(null, strings).apply( object, hasTransformer ? [fn] : [] ); } template.asMethod = function (fn, object) {'use strict'; return typeof fn === 'function' ? template(fn, this, object) : template(this, fn); }; Demo (all the following tests return true): const info = 'template'; // just string `some ${info}` === template('some ${info}', {info}); // passing through a transformer transform `some ${info}` === template(transform, 'some ${info}', {info}); // using it as String method String.prototype.template = template.asMethod; `some ${info}` === 'some ${info}'.template({info}); transform `some ${info}` === 'some ${info}'.template(transform, {info});
Faz assim (This way): let a = 'b:${this.b}' let b = 10 function template(templateString, templateVars) { return new Function('return `' + templateString + '`').call(templateVars) } result.textContent = template(a, {b}) <b id=result></b>
Since we're reinventing the wheel on something that would be a lovely feature in javascript. I use eval(), which is not secure, but javascript is not secure. I readily admit that I'm not excellent with javascript, but I had a need, and I needed an answer so I made one. I chose to stylize my variables with an # rather than an $, particularly because I want to use the multiline feature of literals without evaluating til it's ready. So variable syntax is #{OptionalObject.OptionalObjectN.VARIABLE_NAME} I am no javascript expert, so I'd gladly take advice on improvement but... var prsLiteral, prsRegex = /\#\{(.*?)(?!\#\{)\}/g for(i = 0; i < myResultSet.length; i++) { prsLiteral = rt.replace(prsRegex,function (match,varname) { return eval(varname + "[" + i + "]"); // you could instead use return eval(varname) if you're not looping. }) console.log(prsLiteral); } A very simple implementation follows myResultSet = {totalrecords: 2, Name: ["Bob", "Stephanie"], Age: [37,22]}; rt = `My name is #{myResultSet.Name}, and I am #{myResultSet.Age}.` var prsLiteral, prsRegex = /\#\{(.*?)(?!\#\{)\}/g for(i = 0; i < myResultSet.totalrecords; i++) { prsLiteral = rt.replace(prsRegex,function (match,varname) { return eval(varname + "[" + i + "]"); // you could instead use return eval(varname) if you're not looping. }) console.log(prsLiteral); } In my actual implementation, I choose to use #{{variable}}. One more set of braces. Absurdly unlikely to encounter that unexpectedly. The regex for that would look like /\#\{\{(.*?)(?!\#\{\{)\}\}/g To make that easier to read \#\{\{ # opening sequence, #{{ literally. (.*?) # capturing the variable name # ^ captures only until it reaches the closing sequence (?! # negative lookahead, making sure the following # ^ pattern is not found ahead of the current character \#\{\{ # same as opening sequence, if you change that, change this ) \}\} # closing sequence. If you're not experienced with regex, a pretty safe rule is to escape every non-alphanumeric character, and don't ever needlessly escape letters as many escaped letters have special meaning to virtually all flavors of regex.
You can refer to this solution const interpolate = (str) => new Function(`return \`${new String(str)}\`;`)(); const foo = 'My'; const obj = { text: 'Hanibal Lector', firstNum: 1, secondNum: 2 } const str = "${foo} name is : ${obj.text}. sum = ${obj.firstNum} + ${obj.secondNum} = ${obj.firstNum + obj.secondNum}"; console.log(interpolate(str));
I realize I am late to the game, but you could: const a = (b) => `b:${b}`; let b = 10; console.log(a(b)); // b:10
toMatch not working with page object in Protractor
I'm fairly new to Jasmine, and I'm using Protractor with page object. Here's the page object as it appears, complete with function called by the spec: 'use strict'; module.exports = { structure: { cliButton: element(by.id('cliSubmit')), cliInput: element(by.id('commandLineInterface')), casDrawButton: element(by.id('casdrawTrigger')), benzeneIcon: element(by.id('cdBenzene')), casDrawCanvas: element(by.id('cdCanvasContainer')), saveUploadButton: element(by.id('cdOK')), lastCommandResponse: element(by.id('commandResponse3')), myFilesLink: element(by.id('my-stn-toggle')), structuresLink: element(by.id('structureOptionsToggle')), firstStructureName: element(by.id('structureNameLabel-0')), returnHome: element(by.className('return-link')), importStructure: element(by.id('importStructure')), importBrowse: element(by.id('structureImportBrowse')), importOK: element(by.id('structureImportModalOk')), editStructureName: element(by.id('editStructureNameSave-0')), structureDateTime: element(by.id('structureDate-0')), structureSnippet: element(by.id('structureImage-0')), firstEditButton: element(by.id('editStructure-0')), firstUploadButton: element(by.id('uploadStructure-0')), firstActionsButton: element(by.id('scriptActions-0')), firstDeleteButton: element(by.id('confirmDelete-0')), selectAll: element(by.id('selectAll')), deleteAll: element(by.id('deleteStructures')), selectCheckboxes: element(by.xpath("//*[contains(#id,'selectStructure')]")) }, casDrawSteps: function () { var structure = this.structure; var today = new Date(); var year = today.getFullYear(); structure.cliInput.sendKeys('fil reg'); structure.cliButton.click(); structure.casDrawButton.click(); browser.sleep(6000); structure.benzeneIcon.isEnabled(); structure.casDrawCanvas.isEnabled(); structure.benzeneIcon.click(); structure.casDrawCanvas.click(); structure.saveUploadButton.click(); structure.cliButton.isEnabled(); browser.sleep(6000); expect(structure.lastCommandResponse.getText()).toContain('STRUCTURE UPLOADED'); structure.myFilesLink.click(); structure.structuresLink.click(); expect(structure.firstStructureName.getText()).toMatch(year + '_' + /\d*/ + '_Structure'); structure.returnHome.click(); structure.cliButton.isEnabled() }, }; The problem is that the toMatch after the getText is not matching the regex, and according to all regex references I can find, it should. I have jasmine-matchers installed, so I'm uncertain why this is happening. Here's the stack I receive when I run the spec with this function: Failures: 1) Protractor STNext Smoke Test - Structure: CASDraw creates a structure in CasDraw and verifies the response in Messenger Message: Expected '2017_0013_Structure' to match '2017_/\d*/_Structure'. Stack: Error: Failed expectation at Object.casDrawSteps (/home/heb29/Desktop/casnc/repos/stn-kraken-qa/spec/pages/structure_page.js:50:56) at UserContext.<anonymous> (/home/heb29/Desktop/casnc/repos/stn-kraken-qa/spec/smoke_spec.js:93:23) at /usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:112:25 at new ManagedPromise (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1067:7) at ControlFlow.promise (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2396:12) at schedulerExecute (/usr/local/lib/node_modules/protractor/node_modules/jasminewd2/index.js:95:18) at TaskQueue.execute_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2970:14) at TaskQueue.executeNext_ (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2953:27) at asyncRun (/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2860:25) What am I missing?
You need to concatenate regular expressions and regular strings correctly: var text = structure.firstStructureName.getText(); expect(text).toMatch(year + /_\d*_Structure/.source);
How to transform link in markdown() to link with favicon using Google S2 Converter?
I'd like markdown links to have a favicon within the transformed link. https://www.google.com/s2/favicons?domain=http://cnn.com - will return the favicon from any domain. Marked (https://github.com/chjj/marked) - will turn all links in my code to a href's http://cnn.com becomes http://cnn.com So, How would I modify marked.js so that - http://cnn.com will become <img src="https://www.google.com/s2/favicons?domain=http://cnn.com">http://cnn.com I do see this line 452 marked.js autolink: /^<([^ >]+(#|:\/)[^ >]+)>/, Ref: https://github.com/chjj/marked/blob/master/lib/marked.js I'm using expressjs and NodeJS Thanks Rob
You can override a renderer method. Marked works in two steps: (1) it parses the Markdown into a bunch of tokens and (2) it renders those tokens to HTML. As you don't want to alter the Markdown parsing (it already properly identifies links), but you do want to alter the HTML output, you want to override the renderer for links. var renderer = new marked.Renderer(); get_favicon = function (text) { // return replacement text here... var out = '<img src="https://www.google.com/s2/favicons?domain=' out += text + '">' + text + '</a>' return out } renderer.link = function (href, title, text) { if (this.options.sanitize) { try { var prot = decodeURIComponent(unescape(href)) .replace(/[^\w:]/g, '') .toLowerCase(); } catch (e) { return ''; } if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { return ''; } } var out = '<a href="' + href + '"'; if (title) { out += ' title="' + title + '"'; } out += '>' + get_favicon(text) + '</a>'; return out; }; } // Pass the custom renderer to marked with the input. markdown(input, renderer=renderer) Note that I just took the default link method and altered it slightly to pass text through the get_favicon function. The get_favicon function accepts a text string and returns the replacement text (an image in this case). It could probably be improved as not all links will only have a domain as their text content. If the text contained more that the domain (path, fragment, query string, etc), then only use the domain for the favicon link. Or if the text did not contain a link at all (as the same renderer is used for all links, not just auto links) then the text should be returned unaltered. I'll leave those improvements as an exercise for the reader.
You don't have to mess with marked source code. This simple regexp should do the trick: const markedOutput = 'http://cnn.com'; const withFavIcons = markedOutput.replace(/(<a[^>]+>)(https?:\/\/[^<]+)(<\/a>)/gi, (m, open, url, close) => { const favicon = '<img src="https://www.google.com/s2/favicons?domain=' + url + '">'; const truncated = url.length > 50 ? url.slice(0, 47) + '...' : url; return open + favicon + truncated + close; });
Dynamically Create Meteor Handlebars Template Helpers?
I have a list of i18n translation strings within an meteor-i18n object that I'm iterating over. Instead of creating a Template Helper for each string manually though, which would soon become redundant and repetitive, I would like to create the Helpers dynamically, within a loop, like so: for (var namespace in Meteor.i18nMessages) { for (var msg in Meteor.i18nMessages[namespace]){ //Template[namespace][msg] = __(namespace + "." + msg); // <- works but is not reactive Template[namespace][msg] = function() { // <- Doesn't work: always returns last value from object return __(namespace + "." + msg); } } } However when I do, I lose reactivity. How would one go about solving this? I'm a fan of best-practices and elegant code. Thanks.
You need to precompile the handlebars templates with Meteor._def_template("templateName", function () { return "your html" }); This creates a template at Template.templateName which will be reactive with the helpers defined for it. EventedMind has some screencasts that show how this works in good detail: http://www.eventedmind.com/posts/handlebars-how-it-works http://www.eventedmind.com/posts/handlebars-precompiling
My text editor keeps calling me "evil" for using eval but this works: for (var namespace in Meteor.i18nMessages) { var obj = {}; for (var msg in Meteor.i18nMessages[namespace]) { var str = 'obj["' + msg + '"] = function() { return __("' + namespace + '.' + msg + '"); }'; console.log(str); eval(str); } Template[namespace].helpers(obj); }