Please forgive me as I've never used Apache Velocity before but I have to figure this out!
I'm using an adobe recommendation template for products and I'm now adding a star rating system into it as noted in the image below:
However, as you can see the logic i have added (In JS) is only being executed the first time. and obviously not running through the loop, since it is in Velocity.
My question to the community is; how do i go about making this work?
I've tried for a day now to no avail, so ANY help would be greatly appreciated.
Please see the code below! (And thanks!)
[The #foreach loops is the velocity, and everything between the script tags is my appended code]
<div class="line">
<h2>More for You</h2>
#set($count=1)
#foreach($e in $entities)
#if($e.id != "" && $count < $entities.size() && $count <=18)
<li>
<script type="text/javascript">
var myString = $e.rating;
if (myString >= 5) { myString = 5;}
var myRegexp2 = /\d(?!.*\d)/;
var match2 = myRegexp2.exec(myString);
var starIMG = "<img src='http://kirklands.ugc.bazaarvoice.com/3768-en_us/" + match + "_" + match2 + "/5/rating.gif' alt='' />";
var myRegexp3 = /\d/;
var match = myRegexp3.exec(myString);
function myFunction() {
return(match);
}
</script>
<a class="productBlock" onclick="var s = s_gi(s_account);
s.linkTrackVars='events,eVar21';
s.linkTrackEvents='event16';
s.events='event16';
s.eVar21='';
s.tl(this,'o','Product Detail Cross-Sell');" href="$e.pageUrl?icid=hpFS ">
<img title="$e.name" alt="$e.name" src="$e.thumbnailUrl">
<h3>$e.name</h3>
<div id="starRating"><script>document.getElementById("starRating").innerHTML =
starIMG;</script></div>
<p>$$e.value</p>
</a>
</li>
#set($count = $count + 1)
#end
#end
</ul>
</div>
<div class="scroolBtn btnRight"><span class="btn" id="scrool-forward"></span></div>
</div>
jQuery(function () {
jQuery('#scrool').scrollbox({
direction: 'h',
switchItems: 3,
distance: 450,
autoPlay:false
});
jQuery('#scrool-backward').click(function () {
jQuery('#scrool').trigger('backward');
});
jQuery('#scrool-forward').click(function () {
jQuery('#scrool').trigger('forward');
});
});
I have not used Velocity template scripts for years but wild guess is an escaping problem. Everything you include in a velocity template is actually run by Velocity engine, you must escape $variable literals.
http://velocity.apache.org/engine/devel/user-guide.html#escapingvalidvtlreferences
edit No wait, you mean you wanted to run javascript code at server side, each foreach item step should evaluate javascript code?
Related
We use MathJax on our course website, which is implemented in Jekyll and hosted on GitHub pages. For small, straightforward equations, MathJax works great but I've never been able to get even slightly more complicated equations to work. I've spent many hours investigating and experimenting, so I thought I'd finally ask here.
For example, the following fails to render:
$$
S_{i} =
\begin{cases}
X_{1} & \text{if i = 1}\\
\alpha \cdot X_{i} + (1 - \alpha) \cdot S_{i-1} & \text{if i $>$ 1}
\end{cases}
$$
The rendered output in the web browser is literally blank.
When I look at the rendered html, I see:
<p>
<span class="MathJax_Preview" style="color: inherit; display: none;"></span>
<span id="MathJax-Element-5-Frame" class="mjx-chtml MathJax_CHTML" tabindex="0" data-mathml="
<math
xmlns="http://www.w3.org/1998/Math/MathML" />" role="presentation" style="font-size: 113%; position: relative;">
<span id="MJXc-Node-149" class="mjx-math" aria-hidden="true">
<span id="MJXc-Node-150" class="mjx-mrow"></span>
</span>
<span class="MJX_Assistive_MathML" role="presentation">
<math
xmlns="http://www.w3.org/1998/Math/MathML">
</math>
</span>
</span>
<script type="math/tex" id="MathJax-Element-5">%
<![CDATA[ S_{i} = \begin{cases} X_{1} & \text{if i = 1}\\ \alpha \cdot X_{i} + (1 - \alpha) \cdot S_{i-1} & \text{if i $>$ 1} \end{cases} %]]>
</script>
</p>
I'm not a LaTeX expert so I often write equations (or at least check them) in online editors like https://www.codecogs.com/latex/eqneditor.php or OverLeaf. They render fine there.
I asked this first (link) at the tex StackExchange but they said it was offtopic.
UPDATE: I ended up asking this on the MathJax user group (link) and received a working solution, which is described in detail on GitHub. Thanks all for your responses!
It looks like Jekyll inserts the math as MathJax <script> tags (<script type="math/tex">), and the contents of that tag are the TeX expression. Unfortunately, the plugin is inserting some additional text to mark the contents as CDATA. This is not needed in HTML, but may be for XHTML. Is your page XHTML (I don't know what Jekyll produces)?
In any case, the plugin probably originally inserted
<script type="math/tex">
% <![CDATA[
... your expression...
% ]]>
</script>
That would have worked (even if it is unnecessary), because the % in TeX marks a comment, so the <![CDATA[ will be ignored, and so will the closing ]]>.
Except that GitHub pages removes line breaks, and so you got
<script type="math/tex">% <![CDATA[... your expression...% ]]></script>
all on one line. That means the first % makes the entire rest of the expression into a comment, and the math is effectively commented out. That means you get no output, since the math expression is actually empty.
This has come up before, and I provided a work-around
for version 2:
<script type="text/x-mathjax-config">
MathJax.Hub.Register.StartupHook('TeX Jax Ready', function () {
MathJax.InputJax.TeX.prefilterHooks.Add(function (data) {
data.math = data.math.replace(/^% <!\[CDATA\[/, '').replace(/%\]\]>$/, '');
});
});
</script>
and a version 3 as well:
<script>
MathJax = {
startup: {
ready: function() {
var HTMLDomStrings = MathJax._.handlers.html.HTMLDomStrings.HTMLDomStrings;
var handleTag = HTMLDomStrings.prototype.handleTag;
HTMLDomStrings.prototype.handleTag = function (node, ignore) {
if (this.adaptor.kind(node) === '#comment') {
var text = this.adaptor.textContent(node);
if (text.match(/^\[CDATA\[(?:\n|.)*\]\]$/)) {
this.string += '<!'
this.extendString(node, text);
this.string += '>';
return this.adaptor.next(node);
}
}
return handleTag.call(this, node, ignore);
}
MathJax.startup.defaultReady();
MathJax.startup.document.inputJax[0].preFilters.add(function (data) {
data.math.math = data.math.math.replace(/^% <!\[CDATA\[/, '').replace(/%\]\]>$/, '');
});
}
}
};
</script>
In any case, it is the (probably unnecessary) CDATA "comments", together with the removal of line breaks, that is causing the problem.
I'm using uncode theme and I have a page heading that is showing 'Archive: Portfolio'
I want to remove the 'Archive:' bit from that heading.
In the source it looks like this:
<h1 class="header-title h1"><span>Archives: Projects</span></h1>
I have tried removing Archive from all the page titles with Yoast SEO plugin but it is still showing.
Is there a way to remove that word with javascript maybe does anyone know?
Thanks!
I'd be wary in removing it via javascript. It seems to me that adding a piece of text somewhere in the code's execution, and then removing it on the client-side smells like "contrived complexity".
Take a look at the wordpress template hierarchy, and manually search for the template file that's rendering the Archives: string of text.
I'd start with archive.php, and then fall my way up through other archive-*.php pages, then to taxonomy.php category.php, and so on.
If you're comfy in the command line, you might also consider grepping for the string: grep -r /path/to/wp/theme "Archive:" and sifting through the results to find the template file(s) with that on one of their lines.
But if you insist on removing the string via javascript, you might try dropping something like this at the bottom of the <body>, via a function in functions.php:
function remove_archive_text_via_js() {
if (is_archive()) { ?>
<script type="text/javascript">
var archiveHeaders = document.getElementsByClassName('header-title');
for (i = 0, headerCount = archiveHeaders.length; i < headerCount; i++) {
var replacedText = archiveHeaders[i].textContent.replace('Archives: ', '');
archiveHeaders[i].textContent = replacedText;
}
</script>
<?php }
}
add_action('wp_footer', 'remove_archive_text_via_js');
var elem = document.getElementsByClassName('header-title h1');
var innerSpan = elem[0].getElementsByTagName('span');
innerSpan[0].innerHTML = innerSpan[0].innerHTML.replace('Archives: ', 'jsfiddle');
jsfiddle: https://jsfiddle.net/orcadj3u/
$(function() {
$( "h1 span" ).each(function( index ) {
var newtext = $(this).text().replace("Archives: ", " ");
$(this).html(newtext);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h1 class="header-title h1"><span>Archives: Projects</span></h1><br>
<h1 class="header-title h1"><span>Archives: Solutions</span></h1><br>
<h1 class="header-title h1"><span>Archives: Yozgat</span></h1><br>
<h1 class="header-title h1"><span>Archives: Turkey</span></h1><br>
I have a variable account_number in which account number is stored. now i want to get the value of the element having id as account_number. How to do it in javascript ?
I tried doing document.getElementById(account_number).value, but it is null.
html looks like this :
<input class='transparent' disabled type='text' name='113114234567_name' id='113114234567_name' value = 'Neeloy' style='border:0px;height:25px;font-size:16px;line-height:25px;' />
and the js is :
function getElement()
{
var acc_list = document.forms.editBeneficiary.elements.bene_account_number_edit;
for(var i=0;i<acc_list.length;i++)
{
if(acc_list[i].checked == true)
{
var account_number = acc_list[i].value.toString();
var ben_name = account_number + "_name";
alert(document.getElementById("'" + ben_name.toString() + "'").value);
}
}
}
here bene_account_number_edit are the radio buttons.
Thanks
Are you storing just an integer as the element's id attribute? If so, browsers tend to behave in strange ways when looking for an element by an integer id. Try passing account_number.toString(), instead.
If that doesn't work, prepend something like "account_" to the beginning of your elements' id attributes and then call document.getElementById('account_' + account_number).value.
Why are you prefixing and post-fixing ' characters to the name string? ben_name is already a string because you've appended '_name' to the value.
I'd recommend doing a console.log of ben_name just to be sure you're getting the value you expect.
the way to use a variable for document.getElementById is the same as for any other function:
document.getElementById(ben_name);
I don't know why you think it would act any differently.
There is no use of converting ben_name to string because it is already the string.
Concatenation of two string will always give you string.
var account_number = acc_list[i].value.toString();
var ben_name = account_number + "_name";
try following code it will work fine
var ben_name=acc_list[i]+ "_name";
here also
alert(document.getElementById("'" + ben_name.toString() + "'").value);
try
alert(document.getElementById(ben_name).value);
I have tested similar type of code which worked correctly. If you are passing variable don't use quotes. What you are doing is passing ben_name.toString() as the value, it will definitely cause an error because it can not find any element with that id viz.(ben_name.toString()). In each function call, you are passing same value i.e. ben_name.toString() which is of course wrong.
I found this page in search for a fix for my issue...
Let's say you have a list of products:
<div class="rel-prod-item">
<img src="assets/product-photos/title-of-the-related-product_thumbnail.jpg" alt="Western Digital 1TB" />
<p class="rel-prod-title">Western Digital 1TB</p>
<p class="rel-prod-price" id="price_format_1">149.95</p>
add to cart
</div>
<div class="rel-prod-item">
<img src="assets/product-photos/title-of-the-related-product_thumbnail.jpg" alt="Western Digital 1TB" />
<p class="rel-prod-title">Western Digital 1TB</p>
<p class="rel-prod-price" id="price_format_2">139.95</p>
add to cart
</div>
<div class="rel-prod-item">
<img src="assets/product-photos/title-of-the-related-product_thumbnail.jpg" alt="Western Digital 1TB" />
<p class="rel-prod-title">Western Digital 1TB</p>
<p class="rel-prod-price" id="price_format_3">49.95</p>
add to cart
</div>
The designer made all the prices have the digits after the . be superscript. So your choice is to either have the cms spit out the price in 2 parts from the backend and put it back together with <sup> tags around it, or just leave it alone and change it via the DOM. That's what I opted for and here's what I came up with:
window.onload = function() {
var pricelist = document.getElementsByClassName("rel-prod-price");
var price_id = "";
for (var b = 1; b <= pricelist.length; b++) {
var price_id = "price_format_" + b;
var price_original = document.getElementById(price_id).innerHTML;
var price_parts = price_original.split(".");
var formatted_price = price_parts[0] + ".<b>" + price_parts[1] + "</b>";
document.getElementById(price_id).innerHTML = formatted_price;
}
}
And here's the CSS I used:
.rel-prod-item p.rel-prod-price b {
font-size: 50%;
position: relative;
top: -4px;
}
I hope this helps someone keep all their hair :-)
Here's a screenshot of the finished product
The only way I know how to print a huge string without using += is to use \ backslashes. ugly!
<div id="foo"></div>
<script type="text/javascript">
var longString = '<div id="lol">\
<div id="otherstuff">\
test content. maybe some code\
</div>\
</div>';
document.getElementById('foo').innerHTML = longString;
</script>
is there any way to do this where the longString is untainted? php has $foo = ''' long multiline string '''; I want this in javascript!
Anyone know of a better method for printing long, multi-line strings in javascript?
In general, the answer is: not in the language syntax. Though as Ken pointed out in his answer there are many work-arounds (my personal method is to load a file via AJAX). In your specific case though, I'd prefer creating a HTML constructor function so you can then define the HTML structure using javascript object literals. Something like:
var longString = makeHTML([{
div : {
id : "lol",
children : [{
div : {
id : "otherstuff",
children : [{
text : "test content. maybe some code"
}]
}]
}]
which I find to be much easier to handle. Plus, you this would allow you to use real function literals when you need it to avoid string quoting hell:
makeHTML([{
span : {
onclick : function (event) {/* do something */}
}
}]);
note: the implementation of makeHTML is left as exercise for the reader
Additional answer:
Found some old code after a quick scan through my hard disk. It's a bit different from what I suggested above so I thought I'd share it to illustrate one of the many ways you can write functions like this. Javascript is a very flexible language and there is not much that forces you to write code one way or another. Choose the API you feel most natural and comfortable and write code to implement it.
Here's the code:
function makeElement (tag, spec, children) {
var el = document.createElement(tag);
for (var n in spec) {
if (n == 'style') {
setStyle(el,spec[n]);
}
else {
el[n] = spec[n];
}
}
if (children && children.length) {
for (var i=0; i<children.length; i++) {
el.appendChild(children[i]);
}
}
return el;
}
/* implementation of setStyle is
* left as exercise for the reader
*/
Using it would be something like:
document.getElementById('foo').appendChild(
makeElement(div,{id:"lol"},[
makeElement(div,{id:"otherstuff"},[
makeText("test content. maybe some code")
])
])
);
/* implementation of makeText is
* left as exercise for the reader
*/
One technique if you have a big block is a <script> tag with an invalid type. It will be ignored by browsers.
<script type="text/x-my-stuff" id="longString">
<div id="lol">
<div id="otherstuff">
test content. maybe some code
</div>
</div>
</script>
<script type="text/javascript">
var longString = document.getElementById("longString").text;
document.getElementById('foo').innerHTML = longString;
</script>
A few somewhat unattractive options are discussed in the answers to this question.
You really could minimize this ugliness by creating your <div id="lol"> as HTML, and set its content with .innerHTML = "test content. maybe some code"
I don't like creating HTML in Javascript because of this exact issue, and instead use "template" elements which i simply clone then manipulate.
var lol = document.getElementById("template_lol").clone();
lol.firstChild.innerHTML = "code and stuff";
foo.appendChild(lol);
And this is the HTML:
<body>
<div>normal stuff</div>
<div style="display:none" id="templateBucket">
<div id="template_lol"><div class="otherstuff"></div></div>
</div>
</body>
This works too :
var longString =
'<div id="lol">' +
'<div id="otherstuff">' +
'test content. maybe some code' +
'</div>' +
'</div>';
Though I've never heard of this but, is it possible to retrieve a node from the DOM using JS, and then find out on what line of the file that node occurred on?
I'm open to anything, alternative browsers plugins/add-ons etc...it doesn't need to be cross-browser per say.
I would assume that this would be possible somehow considering that some JS debuggers are capable of finding the line number within a script tag, but I'm not entirely sure.
Ok, forgive me for how large this is. I thought this was a very interesting question but while playing with it, I quickly realized that innerHTML and its ilk are quite unreliable wrt maintaining whitespace, comments, etc. With that in mind, I fell back to actually pulling down a full copy of the source so that I could be absolutely sure I got the full source. I then used jquery and a few (relatively small) regexes to find the location of each node. It seems to work well although I'm sure I've missed some edge cases. And, yeah, yeah, regexes and two problems, blah blah blah.
Edit: As an exercise in building jquery plugins, I've modified my code to function reasonably well as a standalone plugin with an example similar to the html found below (which I will leave here for posterity). I've tried to make the code slightly more robust (such as now handling tags inside quoted strings, such as onclick), but the biggest remaining bug is that it can't account for any modifications to the page, such as appending elements. I would need probably need to use an iframe instead of an ajax call to handle that case.
<html>
<head id="node0">
<!-- first comment -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style id="node1">
/* div { border: 1px solid black; } */
pre { border: 1px solid black; }
</style>
<!-- second comment -->
<script>
$(function() {
// fetch and display source
var source;
$.ajax({
url: location.href,
type: 'get',
dataType: 'text',
success: function(data) {
source = data;
var lines = data.split(/\r?\n/);
var html = $.map(lines, function(line, i) {
return ['<span id="line_number_', i, '"><strong>', i, ':</strong> ', line.replace(/</g, '<').replace(/>/g, '>'), '</span>'].join('');
}).join('\n');
// now sanitize the raw html so you don't get false hits in code or comments
var inside = false;
var tag = '';
var closing = {
xmp: '<\\/\\s*xmp\\s*>',
script: '<\\/\\s*script\\s*>',
'!--': '-->'
};
var clean_source = $.map(lines, function(line) {
if (inside && line.match(closing[tag])) {
var re = new RegExp('.*(' + closing[tag] + ')', 'i');
line = line.replace(re, "$1");
inside = false;
} else if (inside) {
line = '';
}
if (line.match(/<(script|!--)/)) {
tag = RegExp.$1;
line = line.replace(/<(script|xmp|!--)[^>]*.*(<(\/(script|xmp)|--)?>)/i, "<$1>$2");
var re = new RegExp(closing[tag], 'i');
inside = ! (re).test(line);
}
return line;
});
// nodes we're looking for
var nodes = $.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(num) { return $('#node' + num) });
// now find each desired node in both the DOM and the source
var line_numbers = $.map(nodes, function(node) {
var tag = node.attr('tagName');
var tags = $(tag);
var index = tags.index(node) + 1;
var count = 0;
for (var i = 0; i < clean_source.length; i++) {
var re = new RegExp('<' + tag, 'gi');
var matches = clean_source[i].match(re);
if (matches && matches.length) {
count += matches.length;
if (count >= index) {
console.debug(node, tag, index, count, i);
return i;
}
}
}
return count;
});
// saved till end to avoid affecting source html
$('#source_pretty').html(html);
$('#source_raw').text(source);
$('#source_clean').text(clean_source.join('\n'));
$.each(line_numbers, function() { $('#line_number_' + this).css('background-color', 'orange'); });
},
});
var false_matches = [
"<div>",
"<div>",
"</div>",
"</div>"
].join('');
});
</script>
</head>
<!-- third comment -->
<body id="node2">
<div>
<pre id="source_pretty">
</pre>
<pre id="source_raw">
</pre>
<pre id="source_clean">
</pre>
</div>
<div id="node3">
<xmp>
<code>
// <xmp> is deprecated, you should put it in <code> instead
</code>
</xmp>
</div>
<!-- fourth comment -->
<div><div><div><div><div><div><span><div id="node4"><span><span><b><em>
<i><strong><pre></pre></strong></i><div><div id="node5"><div></div></div></div></em>
</b></span><span><span id="node6"></span></span></span></div></span></div></div></div></div></div></div>
<div>
<div>
<div id="node7">
<div>
<div>
<div id="node8">
<span>
<!-- fifth comment -->
<div>
<span>
<span>
<b>
<em id="node9">
<i>
<strong>
<pre>
</pre>
</strong>
</i>
<div>
<div>
<div>
</div>
</div>
</div>
</em>
</b>
</span>
<span>
<span id="node10">
</span>
</span>
</span>
</div>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Something like this?
var wholeDocument = document.getElementsByTagName('html')[0]
var findNode = document.getElementById('whatever')
var documentUpToFindNode = wholeDocument.substr(0, wholeDocument.indexOf(findNode.outerHTML))
var nlsUpToFindNode = documentUpToFindNode.match(/\n/g).length
This can be done. Start by getting the highest node in the document like this:
var htmlNode = document.getElementsByTagName('html')[0];
var node = htmlNode;
while (node.previousSibling !== null) {
node = node.previousSibling;
}
var firstNode = node;
(this code was tested and retrieved both the doctype node as well as comments above the html node)
Then you loop through all nodes (both siblings and children). In IE, you'll only see the elements and comments (not text nodes), so it'll be best to use FF or chrome or something (you said it wouldn't have to be cross browser).
When you get to each text node, parse it to look for carriage returns.
You could try: -
- start at the 'whatever' node,
- traverse to each previous node back to the doc begining while concatenating the html of each node,
- then count the new lines in your collected HTML.
Post the code once you nut it out coz thats a good question :)