Javascript Regex optimization - javascript

I have two JQuery/Javascript functions that highlights all string occurences in a web page, and then narrows these occurences one by one when clicking on the next/prev buttons.
Here is the current JS code for the next and previous buttons:
var outputHtmlContent;
var posScroll = 0;
// Trigger on search next occurence's button
$('#nextSearchOutputButton').on("click", function (e) {
e.preventDefault();
var searchString = "Command Name";
$("body").highlight(searchString);
$(".highlight").css({ backgroundColor: "#E0FFFF" });
var regex = /Command Name/g, result, indices = [];
var i = 0;
outputHtmlContent = $("#output").html();
if (posScroll == outputHtmlContent.match(/Command Name/g).length - 1) posScroll = 0;
else
posScroll++;
while ((result = regex.exec(outputHtmlContent))) {
if (posScroll == i) {
var index = result.index;
outputHtmlContent = outputHtmlContent.substring(0, index) + "<span class='highlight'>" + outputHtmlContent.substring(index, index + searchString.length) + "</span>" + outputHtmlContent.substring(index + searchString.length);
$("#output").html(outputHtmlContent);
document.getElementById("commandNumber" + posScroll).scrollIntoView();
return;
}
i++;
}
})
// Trigger on search previous occurence's button
$('#prevSearchOutputButton').on("click", function (e) {
e.preventDefault();
var searchString = "Command Name";
$("body").highlight(searchString);
$(".highlight").css({ backgroundColor: "#E0FFFF" });
var regex = /Command Name/g, result, indices = [];
var i = 0;
outputHtmlContent = $("#output").html();
if (posScroll == 0) posScroll = outputHtmlContent.match(/Command Name/g).length - 1;
else
posScroll--;
while ((result = regex.exec(outputHtmlContent)) && i <= posScroll) {
if (posScroll == i) {
var index = result.index;
outputHtmlContent = outputHtmlContent.substring(0, index) + "<span class='highlight'>" + outputHtmlContent.substring(index, index + searchString.length) + "</span>" + outputHtmlContent.substring(index + searchString.length);
$("#output").html(outputHtmlContent);
document.getElementById("commandNumber" + posScroll).scrollIntoView();
return;
}
i++;
}
})
The two functions work well, but I have a very critical issue concerning the time spent when executing the two functions, this time estimated to 10 seconds for IE when moving from one occurence to an other and is estimated to 4 seconds for FF and chrome.
I googled and found that the Regex performance issue is a classic topic. I found this nice atricle http://infrequently.org/2007/02/regexs-are-slow/.
I would rather remove completely this feature than offering it with such a low quality issue. Yet, I want to stick on it because it is very helpful for my user. Can anyone help improve the Regex performance for my functions ?

Related

Need some help on logic (Xpages : Javascript) To get update Candidate No by Add on 1 if found item

View itself is sorted by decending order!(First column is "New_Exam_No")
currentExamNo = doc.getItemValueString("New_Exam_No");
var Candvw:NotesView = database.getView("(Exam Application Subject Sort by ExamNo)");
var CandEntry:NotesViewEntry=Candvw.getEntryByKey(doc.getItemValueString("New_Exam_No"),true);
if (CandEntry!=null) //If have exam no, use this condition
{
var CandDoc = CandEntry.getDocument()
if (Canddoc.hasItem("Candidate_No")==true) // assign Candidate No yet?Yes!
{
var CandNo = Canddoc.getItemValueString("Candidate_No");
LCandNo = "LSBS"
MidCandNo = #Left(CandNo,7)
MCandNo = #TextToNumber(#Right(MidCandNo,2)) + 1;
CNo = #Right("00"+#Text(MCandNo),2);
RExamNo = currentExamNo;
CandidateNo = LCandNo + "/" + CNo + "/" +RExamNo ;
}
else
{
CandidateNo = "LSBS/01/" + currentExamNo;
}
}
else
{
CandidateNo = "LSBS/01/" + currentExamNo;
}
Why i always get the LSBS/01/examno as it doesn't add on 1.
getEntryByKey() takes a Vector rather than a String. Try:
var key:java.util.Vector = new java.util.Vector();
key.add(doc.getItemValueString("New_Exam_No"));
var CandEntry:NotesViewEntry=Candvw.getEntryByKey(key,true);
It's been a while since I used SSJS, but I think that should work.

jQuery Refactoring - DRY

I have these two functions, inside of my main function. As you'll see the only difference between the two of them is in the middle with how they append/edit the html. I am thinking it would be nice to come up with two new functions, one that does the first half and the other that does the second half. I'm not sure if this is possible with jQuery, or even with JavaScript for that matter, as I don't know how to call these new functions inside of these functions, if that makes sense. Any help/guidance would be great!
Here is the first one
$('#save').click(function(){
var title = $('#title').val();
var tags = $('#tags').val();
var notes = $('#notes').val();
var myDate = new Date();
if (title.length < 1) {
$('.title-warn').show();
}
if (tags.length < 1) {
$('.tags-warn').show();
}
if (notes.length < 1) {
$('.notes-warn').show();
}
if (title.length >= 1 && tags.length >= 1 && notes.length >= 1) {
$allNotes.prepend('<li class="note"><div><h1>' + title + '</h1><div class="date"> <h2>'+ myDate.toDateString() +'</h2><span class="btn btn-edit">Edit</span></div><h3>' + tags + '</h3><p>' + notes + '</p></div></li>');
$allNotes.show();
$newNote.hide();
$('.title-warn').hide();
$('.tags-warn').hide();
$('.notes-warn').hide();
}
$('#title').val('');
$('#tags').val('');
$('#notes').val('');
$('#search').prop('disabled', false);
$('#search').attr("placeholder", "Search by title, tags, date, or even words/sentences in notes");
$('.btn-search').prop('disabled', false);
});
And now the second one
$('#edit').click(function(){
var title = $('#edit-title').val();
var tags = $('#edit-tags').val();
var notes = $('#edit-notes').val();
var myDate = new Date();
if (title.length < 1) {
$('.title-warn').show();
}
if (tags.length < 1) {
$('.tags-warn').show();
}
if (notes.length < 1) {
$('.notes-warn').show();
}
if (title.length >= 1 && tags.length >= 1 && notes.length >= 1) {
$('.edited-note').html('<div><h1>' + title + '</h1><div class="date"> <h2>'+ myDate.toDateString() +'</h2><span class="btn btn-edit">Edit</span></div><h3>' + tags + '</h3><p>' + notes + '</p></div>');
$('.allnotes').show();
$('.edit-note').hide();
$('.title-warn').hide();
$('.tags-warn').hide();
$('.notes-warn').hide();
}
$('#title').val('');
$('#tags').val('');
$('#notes').val('');
$('#search').prop('disabled', false);
$('#search').attr("placeholder", "Search by title, tags, date, or even words/sentences in notes");
$('.btn-search').prop('disabled', false);
$('.edited-note').removeClass('edited-note');
});
And of course if anyone has any suggestions on any other aspects of my code I am up for criticism!
You answered your own question! "As you'll see the only difference between the two of them is in the middle with how they append/edit the html." An initial pretty naive and mechanical attempt attempt at refactoring might look like something like this, just pulling out all the common code into shared functions:
function preHandler(title, tags, notes) {
if (title.length < 1) {
$('.title-warn').show();
}
if (tags.length < 1) {
$('.tags-warn').show();
}
if (notes.length < 1) {
$('.notes-warn').show();
}
return title.length >= 1 && tags.length >= 1 && notes.length >= 1;
}
function commonPost () {
$('#title').val('');
$('#tags').val('');
$('#notes').val('');
$('#search').prop('disabled', false);
$('#search').attr("placeholder", "Search by title, tags, date, or even words/sentences in notes");
$('.btn-search').prop('disabled', false);
}
$('#save').click(function(){
var title = $('#title').val();
var tags = $('#tags').val();
var notes = $('#notes').val();
var myDate = new Date();
if (preHandler(title, tags, notes)) {
$allNotes.prepend('<li class="note"><div><h1>' + title + '</h1><div class="date"> <h2>'+ myDate.toDateString() +'</h2><span class="btn btn-edit">Edit</span></div><h3>' + tags + '</h3><p>' + notes + '</p></div></li>');
$allNotes.show();
$newNote.hide();
$('.title-warn').hide();
$('.tags-warn').hide();
$('.notes-warn').hide();
}
commonPost();
});
$('#edit').click(function() {
var title = $('#edit-title').val();
var tags = $('#edit-tags').val();
var notes = $('#edit-notes').val();
var myDate = new Date();
if (preHandler(title, tags, notes)) {
$('.edited-note').html('<div><h1>' + title + '</h1><div class="date"> <h2>'+ myDate.toDateString() +'</h2><span class="btn btn-edit">Edit</span></div><h3>' + tags + '</h3><p>' + notes + '</p></div>');
$('.allnotes').show();
$('.edit-note').hide();
$('.title-warn').hide();
$('.tags-warn').hide();
$('.notes-warn').hide();
}
commonPost();
$('.edited-note').removeClass('edited-note');
});
It doesn't really win you that much when there are only two scenarios. But with more than two doing this sort of refactoring starts to reap rewards.
This first attempt could be refined (a lot). Perhaps nice folks will post. But this would be a good first attempt.
Generally I like creating some sort of a controller class which does all the UI work for each page in my application. This makes testing a whole lot easier because you can invoke your target methods in a very easy manner.
I also like prefer hiding my selectors behind easy to read variables, so if I were to read this file in a few weeks/months from now, I can quickly bring myself to speed by reading what the high-level logic is intending to do. (FYI in my answer below I haven't done this. I actually don't know what editTitle is supposed to represent. But here are few examples of how I would rename it as: TaskTitle, BlogTitle, etc.)
These are all guidelines really, but I wished I had always written my code like this. I would recommend you to take out some time and just read about javascript design patterns, programming idioms, etc.
var myApp = new app();
$(function () {
myApp.init();
});
var app = function () {
var editTitle = "#edit-title";
var editTitleWarning = ".title-warn";
var editTags = "#edit-tags";
var editTagsWarning = ".tags-warn";
var editNotes = "#edit-notes";
var editNotesWarning = ".notes-warn";
var saveButton = "#save";
var editButton = "#edit";
var updateUI = function (args) {
var isSave = args.data.isSave;
var title = $(editTitle).val();
var tags = $(editTags).val();
var notes = $(editNotes).val();
var myDate = new Date();
// (suggestion) add comment here
if (title.length < 1) {
$(editTitleWarning).show();
}
// (suggestion) add comment here
if (tags.length < 1) {
$(editTagsWarning).show();
}
// (suggestion) add comment here
if (notes.length < 1) {
$(editNotesWarning).show();
}
if (isSave) {
// add save append code here
} else {
// add edit html code here
}
// add remaining code here
};
this.init = function () {
$("body")
.on("click", saveButton, { isSave = true }, updateUI)
.on("click", editButton, { isSave = false }, updateUI);
};
}
Talking only about making the code DRY (versus about architecturing it properly in general), I'd probably rewrite the code as:
var handler = function(prefix, htmlHandler, removeEditedNoteCls) {
prefix = '#' + (prefix === false ? '' : (prefix + '-'));
var list = ['title', 'tags', 'notes'],
i = 0,
h = {
date: new Date
},
act = true;
for (; i < list.length; i++) {
h[list[i]] = $(prefix + list[i]).val();
if (h[list[i]].length < 1) {
$('.' + list[i] + '-warn').show();
act = false;
}
}
if (act) {
htmlHandler.call(this, h);
for (i = 0; i < list.length; i++) {
$('.' + list[i] + '-warn').hide();
}
}
for (i = 0; i < list.length; i++) {
$('#' + list[i]).val('');
}
$('#search').prop('disabled', false);
$('#search').attr("placeholder", "Search by title, tags, date, or even words/sentences in notes");
$('.btn-search').prop('disabled', false);
if (removeEditedNoteCls) {
$('.edited-note').removeClass('edited-note');
}
},
prepend = function(h) {
$allNotes.prepend('<li class="note"><div><h1>' + h.title + '</h1><div class="date"> <h2>' + h.date.toDateString() + '</h2><span class="btn btn-edit">Edit</span></div><h3>' + h.tags + '</h3><p>' + h.notes + '</p></div></li>');
$allNotes.show();
$newNote.hide();
},
replace = function(h) {
$('.edited-note').html('<div><h1>' + h.title + '</h1><div class="date"> <h2>' + h.date.toDateString() + '</h2><span class="btn btn-edit">Edit</span></div><h3>' + h.tags + '</h3><p>' + h.notes + '</p></div>');
$('.allnotes').show();
$('.edit-note').hide();
};
$('#save').click(function() {
handler('', prepend);
});
$('#edit').click(function() {
handler('edit', replace, true);
});
Basically, you:
Don't repeat var per earch variable declaration. Use commas to separate declarations under the same var. Although not a big deal with DRY but it makes code shorter and nicer.
Identify repeating/similar stuff and squash it into either:
Loops over arrays which contain the differences only. In your case it will be the array of IDs ['title', 'tags', 'notes']; OR
Methods accepting the differences as arguments. In your case both "edit" and "save" handlers are pretty similar, which should be the first signal to you to wrap them into a named method (handler in my example).

Javascript clientHeight returns NaN in certain contexts

Interesting issue arising for me.
I am working on an infinite scroll thing in javascript that requires getting clientHeight of a div at some point:
here is the code:
document.addEventListener('DOMContentLoaded', init, false);
function init(){
var j = 0;
var inner1 = document.getElementById("innerDiv1");
inner1.addEventListener("scroll", function(event){ window.j = 2; checkForLoad();});
var inner2 = document.getElementById("innerDiv2");
inner2.addEventListener("scroll", function(event){ window.j = 1; checkForLoad();});
}
var i = 0;
var checkForLoad = function() {
var bottomDiv;
var bottomDivChild;
var offsetOfDiv;
var offsetOfPage;
if(window.j === 2){
bottomDiv = document.getElementById("innerDiv1");
bottomDivChild = bottomDiv.lastElementChild;
offsetOfDiv = bottomDivChild.offsetTop + bottomDivChild.clientHeight; //WORKS FINE
offsetOfPage = (document.getElementById("innerDiv1").pageYOffset) + (document.getElementById("innerDiv1").clientHeight); //THIS IS WHERE THE ISSUE IS
}
else if(window.j === 1){
bottomDiv = document.querySelector("innerDiv2 > div:last-child");
offsetOfDiv = bottomDiv.offsetTop + bottomDiv.clientHeight;
offsetOfPage = inner1.pageYOffset + inner1.innerHeight;
}
if(offsetOfPage > offsetOfDiv - 10) {
alert("time to add");
//eventually will be AJAX with XSL
if (i % 5 !== 0) {
i++;
alert("into the append part");
var newResults = document.createElement("div");
newDiv.innerHtml = "testing " + i;
if(window.j === 2){
document.getElementById("innerDiv1").appendChild(newResults);
}
else if(window.j === 1){
document.getElementById("innerDiv2").appendChild(newResults);
}
checkForLoad();
} else if (i%5 === 0) {
newDiv = document.createElement("div");
newDiv.innerHTML = "Show more Results! " + i;
document.getElementById("scrollingDiv").appendChild(newDiv);
newDiv.setAttribute("ID", "showResults");
newDiv.setAttribute("onClick", "showMore()");
}
}
};
I realize that there are some inconsistencies here, but they are in parts of the script i am not using. Here is the issue though, in the if(window.j === 2) statement, you can see that I use client height in a couple places. In the first one, it returns a number perfectly and life goes on as planned, but when I use it in the line where I get the "offSerOfPage", the sum becomes NaN. I am using firebug to debug it and when I add document.getElementById("innerDiv1").clientHeight to the watch list, it shows that it has a number attatched to it. Yet the sum is returned as NaN. I think my wording is a bit confusing here so if you need clarification, please ask! Thanks!

Jquery- Dynamic added fields - prevent the last Item to be removed

I have a form field that is being duplicated / removed dynamically
See the fiddle here : http://jsfiddle.net/obmerk99/y2d4c/6/
In this feature, I also need to increment the NAME of the field ( and ID etc.. ) like so :
o99_brsa_settings[brsa_dash_wdgt_content][0]
o99_brsa_settings[brsa_dash_wdgt_content][1]
o99_brsa_settings[brsa_dash_wdgt_content][2] ...
It is working, but the problem is that when I add / remove fields , when it gets to the last ( actually first one ) , it will give me "undefined" and will not add anymore fields .
To see the problem you will need to "play" a bit with add/remove .
I believe the main problem is how to keep all of those on the same array level if we have [0] and [0][2]
I am far from a JS guru, and this code was somehow assembled from various sources. But I am kind of stuck right now, So any help will be appreciated .
Try this way:
$(function () {
$(".addScnt").on("click", function () {
var i = checkNumberOfElements();
var scntDiv = $("div[id^='widget_dup']:last");
var prevDiv = scntDiv.clone();
var newname = $(prevDiv).find("textarea").attr("name").substring(0, $(prevDiv).find("textarea").attr('name').indexOf(']'));
prevDiv.find('textarea').attr('name', newname + "][" + i + "]");
prevDiv.find('textarea').attr('id', newname + "][" + i + "]");
prevDiv.find('label').attr('for', newname + "][" + i + "]");
prevDiv.attr('id', $(prevDiv).attr('id') + "_" + i);
$(scntDiv).after(prevDiv);
});
$(document).on("click", ".remScnt", function (event) {
var i = checkNumberOfElements();
if (i <= 1) {
return false;
} else {
var target = $(event.currentTarget);
target.parent("div").remove();
}
});
});
function checkNumberOfElements() {
// Number of textareas
var i = $("textarea[name^='o99_brsa_settings[brsa_dash_wdgt_content]']").length;
// Number of divs
// var i = $("div[id^='widget_dup']").length;
if (typeof i === undefined) {
return 0;
} else {
return i;
}
}
Demo: http://jsfiddle.net/y2d4c/7/

Long string pagination done right

I want to split a long text into smaller chunks, that will act as pages.
var longText = document.getElementById('content').innerHTML;
for (i=0; i<10; i++) {
var page = longText.substring(i*100,(i+1)*100);
document.write(page + "<br /><hr />");
}
See it here on jsfiddle.
This code splits the text, but in a stupid way, cutting also words in half.
It would be far better, for example, creating substrings ending at the last space in a certain number of characters (count 100 characters, then go back to the last space).
How would you achieve it?
Second shot
Third shot
I would use:
function paginate(longText, pageSize) {
var parts = longText.split(/[ \n\t]/g);
if (parts.length == 0)
return [];
var pages = [parts.unshift()];
for (var i = 0; i < parts.length; i += 1) {
var part = parts[i];
if (part.length + pages[pages.length - 1].length < pageSize) {
pages[pages.length - 1] += " " + part;
} else {
pages.push(part);
}
}
return parts;
}
For those looking for a working answer:
<div id="long-text">Lorem ipsum [...]</div>
<script>
var splitter = function(id) {
var longText = document.getElementById(id).innerHTML;
var pageLenght = 200;
var charsDone = 0;
var htmlBefore = "<p>";
var htmlAfter = "</p>";
while (charsDone <= longText.length && (pageLenght+charsDone)<longText.length) {
var pageBox = longText.substr(lastSpace,pageLenght);
var lastSpace = charsDone + pageBox.lastIndexOf(" ");
var page = longText.substring(charsDone,lastSpace);
document.write(htmlBefore + page + htmlAfter);
charsDone = lastSpace;
}
document.write(longText.substr(lastSpace,pageLenght));
}
splitter("#long-text");
You can easily use arrays instead of writing to document.
You will also want to set your html to your needs, do it in htmlBefore and htmlAfter.
See it in action here.

Categories

Resources