Custom replace method? - javascript

Is it possible to create your own custom, I believe the term is 'method'? For example, something like this:
var str = "test"
str.replaceSpecial();
where replaceSpecial() will automatically replace say, the letter e with something else.
The reason I'm interested in doing this is because what I want to do is grab strings and then run a large number of replace actions, so I'm hoping that when I call on replaceSpecial() it will run a function.
Thanks

You can add your methods to String.prototype which will become available to all strings. That is how trim() is implemented in most libraries for example.
String.prototype.replaceSpecial = function() {
return this.replace(/l/g, 'L');
};
"hello".replaceSpecial(); // heLLo
However, note that it is generally a bad practice to define very specific functionality on the native prototypes. The above is a good example of exactly the same problem. For such specific cases, consider using a custom function or wrapper to do the job.
function replaceSpecial(str) {
return str.replace(/l/g, 'L');
}
replaceSpecial("hello"); // heLLo
or under a custom namespace, for example.
var StringUtils = {
replaceSpecial: function(str) { .. },
..
};
StringUtils.replaceSpecial("hello"); // heLLo

Related

Pass arguments to string.prototype extension in javascript

I use string.prototype to linkify text passages on my website. On some sites I want to add different notes and thus want to pass an additional argument to linkify.
My initial idea was to do it as follows.
function linkifyText() {
var uglyLinksPattern = /\[(.*?)\]/gim; // match all letters between square brackets
if(!String.linkify) {
String.prototype.linkify = function(note) {
var textInput = this;
return textInput.replace(uglyLinksPattern, '<a target="_blank" rel="noopener nofollow" href="$&">$&</a>' + note);
}
}
return uglyLinksPattern
}
function linkifyDialogue (text) {
linkifyText();
var note = 'Ad';
var linkedText = String.linkify.call(text, note);
$('#textElem').html(linkedText);
}
I found some tutorials using call and apply. However, I wasn't able to transfer it to my case and hope to get an answer on how to pass an argument to the string.prototype property. So what is the trick?
The way you've tried to implement it is kinda weird (no offense intended). You could've made this much simpler. See:
//Do this just once
String.prototype.linkify=function(note) {
return this.replace(/\[(.*?)\]/gim,"<a target='_blank' rel='noopener nofollow' href='$1'>$1</a>"+note);
};
function linkifyDialogue(text) {
var note="Ad",
linkedText=text.linkify(note);
$('#textElem').html(linkedText);
}
All strings are objects already. If you add a method to the prototype, there's no need to use call() or apply() unless you actually need to (i.e. to call it with a different value for this, pass an array of values as different parameters, etc.).

How to use += operator

Since I started using JQuery ive always wondering how does this operator work in JQuery
Example:
for(var i = 0;i<=4;i++)
{
document.getElementById("mydiv").innerText += i;//<- works as expected
}
//results will be 0,1,2,3,4
but if i use JQuery instead i dont know how to do it
for(var i = 0;i<=4;i++)
{
$("mydiv").text(+i)//<- NO!
$("mydiv").text+(i)//<- NO!
$("mydiv").+text(i)//<- JAJA COME ON!
$("mydiv").text(i)+//<- I guess that was stupid
}
This isn't possible like this. Unlike innerText, text() is a method, not a property.
Try:
$("mydiv").text($("mydiv").text() + i);
Or if you'd rather not make 2 references to $("mydiv") you can do:
$("mydiv").text(function(i,v){
return v + i;
});
You can't use such shorcuts for jQuery methods, it only works for native assignment operators. For jQuery .text(), use a callback:
$("#mydiv").text(function(index, oldtext) {
return oldtext + i;
});
This callback thing works for all jQuery property "assignments", be it .html, .val, .prop, .attr, .css, or .text.
You just need to work with jQuery here. Use the text method to extract the value, and then call it again to set the new value:
for(var i = 0;i<=4;i++)
{
var mydiv = $("mydiv"),
t = mydiv.text();
mydiv.text(t + i);
}
Like other answers point out, jQuery is just a framework and is subject to same syntactic rules as any JavaScript code.
While I see the advantage of passing in a function to .text(), I don't think it's a general purpose approach to solve your real problem : how to concatenate text when you use a function instead of a variable.
I'd favor the usage of Array.join() for efficient concatenation:
var textParts = [ $("mydiv").text() ];
for(var i = 0;i<=4;i++)
{
textParts[textParts.length] = i;
}
$("mydiv").text(textParts.join(',')) // result: '...,1,2,3,4'
If you prefer the function approach over a loop, you could also use Array.map().
AFAIK DOM functions are rather slow so it's more effective to do the concatenation first and then set the div node value.
jQuery is not a programming language but a library built upon javascript, so, the rules are exactly the same as those you have to follow using javascript, and javascript has not been designed to understand these kinds of structures.
Edit
Of course I mean o.m(+1) does not increment o.p while o.p++ and o.p+=1 does :
var o = {};
o.p = 1;
o.m = function () { return this.p; };
o.m(+1); // o.p equals 1
o.p++; // o.p equals 2
o.p+=1; // o.p equals 3

How to realize parsing of own html tags in text

I have task to realize own tags that making text bold, underline or strikethrough with any nesting.
Like a
*bold text* _underlinetext_ -strikethrough-
Also I need to make own hyperlink like a
[link | http://stackoverflow.com]
The first thought that came - it apply regexp. The code:
View.prototype.parseText = function(text) {
text = text.replace(/\*([^\*]+)\*/g, '<b>$1</b>');
text = text.replace(/\_([^\_]+)\_/g, '<u>$1</u>');
text = text.replace(/\-([^\-]+)\-/g, '<s>$1</s>');
text = text.replace(/\[([^\|].+)\|(.+)\]/g, '$1');
return text;};
It's working but I need extensibility. Regex is not a good idea, since it's hardcoded. How to realize that task with finite state machine (or any jQuery plugin))? I would be grateful for any help.
I can suggest you the following implementation http://jsfiddle.net/NwRCm/5/
It uses the State design pattern (little modified because of JavaScript and the purpose). Under the surface all states are implemented with regular expressions but that's the most efficient way, in my opinion.
/* View definition */
function View(container) {
this.container = container;
this._parsers = [];
this._currentState = 0;
};
View.prototype.parse = function(text) {
var self = this;
this._parsers.forEach(function (e) {
self._parse(e);
});
return this.container.innerHTML;
};
View.prototype._parse = function (parser) {
var text = parser.parse(this.container.innerHTML);
this.container.innerHTML = text;
return text;
};
View.prototype.nextState = function () {
if (this._currentState < this._parsers.length) {
return this._parse(this._parsers[this._currentState++]);
}
return null;
};
View.prototype.addParser = function (parser) {
if (parser instanceof Parser) {
return this._parsers.push(parser);
} else {
throw 'The parser you\'re trying to add is not an instance of Parser';
}
};
/* end of the View definition */
/* Simulation of interface */
function Parser() {};
Parser.prototype.parse = function () {
throw 'Not implemented!';
};
/* Implementation of bold parser */
function BoldParser() {};
BoldParser.prototype = new Parser();
BoldParser.prototype.parse = function (text) {
text = text.replace(/\*([^\*]+)\*/g, '<b>$1</b>');
return text;
};
/* Implementation of underline parser */
function UnderlineParser() {};
UnderlineParser.prototype = new Parser();
UnderlineParser.prototype.parse = function (text) {
text = text.replace(/\_([^\_]+)\_/g, '<u>$1</u>');
return text;
};
/* Link parser */
function LinkParser() {};
LinkParser.prototype = new Parser();
LinkParser.prototype.parse = function (text) {
text = text.replace(/\[([^\|].+)\|(.+)\]/g, '$1');
return text;
};
var v = new View(document.getElementById('container'));
v.addParser(new UnderlineParser());
v.addParser(new BoldParser());
v.addParser(new LinkParser());
v.nextState();
v.nextState();
v.nextState();
​Let me look a little deeper in the implementation.
First we have a base "class" (constructor function) View. Each view has it's base container and a list of parsers, it also remember which parser should be applied next.
After that we have the "abstract class" (constructor function with method in the prototype which throws an exception) named Parser it defines a method parse which must be implemented by each parser.
After that we just define different concrete parsers and add them to the view. We can pass the states one by one (View's nextState) or pass all states in a single method call (View's parse). We can dynamically add new parsers.
A thing which can be approved is including flyweight factory for managing the parsers.
Approach with the "abstract" constructor function is also very useful when implementing different patterns like Template method for example.
Edit may be there's a bit overhead because of the definition of all these constructor functions and objects. Everything can be done with callbacks i.e. each state to be a different function. I used this approach because I was looking for the easiest for understanding, clear from language specific features answer. I hope that I achieved it.
No matter what you do, to extend your tagging system, you will need to:
1. define the tag, and
2. replace it with equivalent HTML.
Even if you write your own parser in js, at the end of the day, you will still have to do the 2 above steps, so it is no more extensible than what you have now.
Regex is the tool for the job unless you have other requirements (i.e. as replace only within such an such element, but do something else in another element, which requires parsing).
You can wrap your regex calls in a function and simply add regex replaces to that function when you need to extend the feature. If needed in several pages, add it in an external js file.
function formatUserContent(text)
{
text = text.replace(/\*([^\*]+)\*/g, '<b>$1</b>');
text = text.replace(/\_([^\_]+)\_/g, '<u>$1</u>');
text = text.replace(/\-([^\-]+)\-/g, '<s>$1</s>');
text = text.replace(/\[([^\|].+)\|(.+)\]/g, '$1');
return text;
}
Once that's done, extending the feature is as simple as adding
text = text.replace(/\+([^\-]+)\+/g, '<em>$1</em>');
in the body of the function. I doubt that rolling out your own finite state machine will be any easier to extend, quite the opposite.
Spending hours on a finite state machine in the hope that it might save a few minutes at some unknown time in the future is just not a good investment... unless of course you want an excuse to write a finite state machine, in which case, go ahead.
As a side note, I would recommend making your regex a little more fool proof.
text = text.replace(/\[([^\|].+)\|\s*(http://.+)\]/g, '$1');
(Unless you have UI elements that will do the job for the user)
Perhaps you want to use an existing library, for instance the Markdown library at http://www.showdown.im/
If you prefer to write your own, then I'd recommend looking at the source code to see how it's parsed (and maybe the source code for Markdown processors in other languages). Some recommendations for you:
Use jQuery for manipulating the markup
Don't use regular expressions for parsing a language. You'll run into problems when markup elements
are mixed together.

jquery selector for multiple classes

I have elements in my DOM with class="LiveVal:variablepart" and i would like to write a JQuery selector that works even if the elements have other classes on tom of the above. Eg. class="header LiveVal:varablepart" or class="LiveVal:varablepart header".
It works fro me if LiveVal is the first class with:
$('[class^=LiveVal:]').each(function ( intIndex ) { somefunction });
but obviously not if another class is before LiveVal.
In the function I need to extract the variable part. I planned to do like this:
theclass = $( this ).attr('class');
varpart = theclass.replace('\bLiveVal:(.+?)[\s]', '$1');
..but alas, it doesn't match. I've tested the regex on http://gskinner.com/RegExr/ where it seems to work, but it doesn't in javascript !?
Any help would be greatly appreciated.
This will check if a class name contains 'LiveVal:'
$('[class*=LiveVal:]').each(function ( intIndex ) { somefunction });
EDIT
did not realise you had that requirement (although a good one). You can do this instead: $('[class^="LiveVal:"], [class*=" LiveVal:"]')
Here is a fiddle: http://jsfiddle.net/wY8Mh/
It might be somewhat faster to do this with an explicit filter:
$("*").filter(function() { return /\bLiveVal:/.test(this.className); }).something();
It depends on whether the native "querySelectorAll" does the work, and does it quickly. This also would avoid the "FooLiveVal" problem.
It's worth noting that in an HTML5 world, it might be better to use a "data-LiveVal" attribute to store that "variable part" information on your elements. Then you could just say:
$('[data-LiveVal]').something();
In the HTML, it'd look like this:
<div class='whatever' data-LiveVal='variable part'>
Since version 1.5, jQuery will fetch stuff in a "data-foo" attribute when you pass the tail of the attribute (the part after "data-") to the ".data()" method:
var variablePart = $(this).data('LiveVal');
The ".data()" method will not, however, update the "data-foo" property when you store a new "variable part".
edit — if you want the value that's stuffed into the class after your property name prefix ("LivaVal:"), you can extract it like this:
var rLiveVal = /\bLiveVal:(\S*)\b/;
$('*').filter(function() { return rLiveVal.test(this.className); }).each(function() {
var variablePart = rLiveVal.exec(this.className)[1];
//
// ... do something ...
//
});
(or some variation on that theme).

Help me implementing addClassName in my JavaScript project the right way

I find myself often needing addClassName and removeClassName functions for Elements. However, I dare not to extend the native Element due to potential collisions. So, now I am wondering what to do.
I took a look at ExtJS, and it achieves this like:
var element = document.getElementById('...');
var elem = new Ext.Element(element);
elem.addClass('something');
I like the idea of not extending the native Element here, but I do not really like the idea of wrapping the element so hard over a custom object, because now I can't do several things like setAttribute easily, for example:
var element = document.getElementById('...');
var elem = new Ext.Element(element);
elem.addClass('something');
elem.setAttribute('src', '...'); // Fails
However, I could do:
var element = document.getElementById('...');
element.setAttribute('src', '...');
var elem = new Ext.Element(element);
elem.addClass('something');
but to me this does not look really nice. It looks a bit complex instead.
Are there any other alternative ways I could use besides wrapping the element around my own object that offers me these cool methods?
Just check if there's already an implementation defined. I'm pretty sure addClassName is not going to have a different signature than you have at the moment.
if (!node.prototype.addClassName) {
node.prototype.addClassName = function addClassName(name) {
// your code
}
}

Categories

Resources