InDesign Target XML Structure element by partial name - javascript

In my Structure pane there are some elements that their partial name is identical, eg. image01, image03, image03 etc.
I want to know if there is a way to access them via scripting using the itemByName() method, but by providing a partial name, like in CSS i can use
h1[rel*="external"]
Is there a similar way to do this in:
var items2 = items.xmlElements.itemByName("image");

You could try something like the code below. You can test against the markupTag.name properties with a regular expression. The regex is equivalent to something like /^image/ in your example (find image at the beginning of a string).
function itemsWithPartialName(item, partialName) {
var elems = item.xmlElements;
var result = [];
for (var i=0; i<elems.length; i++) {
var elem = elems[i];
var elemName = elem.markupTag.name;
var regex = new RegExp("^" + partialName);
if (regex.test(elemName)) {
result.push(elem);
}
}
return result;
}
itemsWithPartialName(/* some xml item */, 'image');

You can use an XPath:
var rootXE = app.activeDocument.xmlElements.item(0);
var tagXEs = rootXE.evaluateXPathExpression("//*[starts-with(local-name(),'image')]");

Related

Remove character from div id

I need to remove the tag within this div id. What am I doing wrong here?
function thanksForHelping(div){
var siblingOne = $(div).next();
var siblingTwo = $(div).next().next();
var NIDsiblingOne = siblingOne.substring(1);
var NIDsiblingTwo = siblingTwo.substring(1);
}
I want to see:
siblingOne == #yo
siblingTwo == #hi
NIDsiblingOne == yo
NIDsiblingTwo == hi
However I am receiving this error in my console:
TypeError: siblingOne.substring is not a function
.next() returns a jQuery object (docs), which is why you cannot call substring() on it. If you want the id, you need to use attr() or prop():
$(div).next().attr('id'); // or prop()
although it's a little unclear exactly what you're going for, but hopefully this should point you in the right direction.
It looks like you're saying you have some div like <div id="#myid"> This is incorrect. You shouldn't have the # in the id; that's just how it's referenced in CSS queries.
But, you could be using links to link to that div like this: <a href="#myid"> in which case you would want to strip the # to get the proper value if you were going to target with something like getElementById()
Even so, you're calling substring on a jQuery object, not on the id itself. Try something like:
function thanksForHelping(div){
var siblingOne = $(div).next(), siblingTwo = siblingOne.next();
var existingIdOne = siblingOne.attr("id");
var existingIdTwo = siblingTwo.attr("id");
var noHashIdOne = existingIdOne.replace(/^#/, ''); //Using regex here in case it doesn't actually have a leading #
var noHashIdTwo = existingIdOne.replace(/^#/, '');
}
If you were doing the link thing that I mentioned before, you'd have two ways to approach it: 1) Fetch the actual id like I show above, or use jQuery and just use the version with the hash. So, something like this:
function elementsForIntraPageLinks(){
var links = $("a.internal");
links.each(function(i, link) {
var href = link.href;
var targetElement = $(href);
console.log("targetElement:", targetElement);
});
}
or without jQuery but in a modern browser:
function elementsForIntraPageLinks(){
var links = document.querySelectorAll('a[href^="#"]');
links.forEach(function(link, i, links) {
console.log("targetElement:", document.querySelector(link.href));
});
}
or in a slightly older browser
function elementsForIntraPageLinks(){
var links = document.getElementsByTagName('a'), i, ii, link;
for(i = 0, ii = links.length; i < ii; i++){
link = links[i];
if (/^#/.test(link.href)) {
console.log("targetedElement", document.getElementById(link.href.replace(/^#/, ""));
}
});
}

Replacing all urls in a div

I am trying to write javascript code to find all the urls inside a div. Now this would be pretty easy if all the urls within the div were separated by spaces in which case I can just do a regex on what's inside the div to find them. However, the urls within this outer div may be in sub divs (or any other html tag) and I want to consider the subdivs as separators as well (and I don't want to get rid of these subdivs). To give an example, in the following I want to find www.foo.com and www.bar.com within the div with id "outer":
<div id="outer"><div>www.foo.com</div>www.bar.com</div>
What would be a good way of doing this?
You can apply a recursive call to all non-text child nodes.
function replaceWwwInNodes(node) {
//text node
if (node.nodeType === 3) {
node.textContent = node.textContent.replace(/* ??? */)
}
else {
Array.prototype.forEach.call(node.childNodes, function (elem) {
replaceWwwInNodes(elem);
});
}
}
replaceWwwInNodes(document.getElementById('outer'));
http://jsfiddle.net/UDX5V/
Try to use this sample http://jsfiddle.net/iklementiev/TaCx9/1/
var data = document.getElementById("outer").innerText;
var myRe = /www\.[0-9a-z-]+\.[a-z]{2,4}/igm;
var matches= data.match(myRe)
for (var i = 0; i < matches.length; i++) {
alert('match: ' + matches[i]);
}
this help to find all urls.
try this
var expression = /[-a-zA-Z0-9#:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9#:%_\+.~#?&//=]*)?/gi;
var regex = new RegExp(expression);
var regContent = $("#outer").html();
var newContent = regContent;
if(regContent.match(regex))
{
var textContent = regContent.match(regex);
for(var i=0;i<regContent.match(regex).length;i++)
{
newContent = newContent.replace(new RegExp(regContent.match(regex)[i], "g"), "test");
}
$("#outer").html(newContent);
}
this will get all url content and replace it as "test".

Getting Attributes from User submitted text! RegExp?

I am trying to pull the attributes out of piece of submitted text in Javascript and change it to an array.
So the user submits this:
<iframe src="http://www.stackoverflow.com/" width="123" height="123" frameborder="1"></iframe>
and I would get:
arr['src'] = http://www.stackoverflow.com/
arr['width'] = 123
arr['height'] = 123
arr['frameborder'] = 1
Just need a regexp I think but any help would be great!
I recommend to use a RegExp to parse user-inputed HTML, instead of creating a DOM object, because it's not desired to load external content (iframe, script, link, style, object, ...) when performing a "simple" task such as getting attribute values of a HTML string.
Using similar (although similarcontradiction?) methods as in my previous answer, I've created a function to match quoted attribute values. Both quoted, as non-quoted attributes are matched.
The code currently returns an object with attributes from the first tag, but it's easily extensible to retrieve all HTML elements (see bottom of answer).
Fiddle: http://jsfiddle.net/BP4nF/1/
// Example:
var htmlString = '<iframe src="http://www.stackoverflow.com/" width="123" height="123" frameborder="1" non-quoted=test></iframe>';
var arr = parseHTMLTag(htmlString);
//arr is the desired object. An easy method to verify:
alert(JSON.stringify(arr));
function parseHTMLTag(htmlString){
var tagPattern = /<[a-z]\S*(?:[^<>"']*(?:"[^"]*"|'[^']*'))*?[^<>]*(?:>|(?=<))/i;
var attPattern = /([-a-z0-9:._]+)\s*=(?:\s*(["'])((?:[^"']+|(?!\2).)*)\2|([^><\s]+))/ig;
// 1 = attribute, 2 = quote, 3 = value, 4=non-quoted value (either 3 or 4)
var tag = htmlString.match(tagPattern);
var attributes = {};
if(tag){ //If there's a tag match
tag = tag[0]; //Match the whole tag
var match;
while((match = attPattern.exec(tag)) !== null){
//match[1] = attribute, match[3] = value, match[4] = non-quoted value
attributes[match[1]] = match[3] || match[4];
}
}
return attributes;
}
The output of the example is equivalent to:
var arr = {
"src": "http://www.stackoverflow.com/",
"width": "123",
"height": "123",
"frameborder": "1",
"non-quoted": "test"
};
Extra: Modifying the function to get multiple matches (only showing code to update)
function parseHTMLTags(htmlString){
var tagPattern = /<([a-z]\S*)(?:[^<>"']*(?:"[^"]*"|'[^']*'))*?[^<>]*(?:>|(?=<))/ig;
// 1 = tag name
var attPattern = /([-a-z0-9:._]+)\s*=(?:\s*(["'])((?:[^"']+|(?!\2).)*)\2|([^><\s]+))/ig;
// 1 = attribute, 2 = quote, 3 = value, 4=non-quoted value (either 3 or 4)
var htmlObject = [];
var tag, match, attributes;
while(tag = tagPattern.exec(htmlString)){
attributes = {};
while(match = attPattern.exec(tag)){
attributes[match[1]] = match[3] || match[4];
}
htmlObject.push({
tagName: tag[1],
attributes: attributes
});
}
return htmlObject; //Array of all HTML elements
}
Assuming you're doing this client side, you're better off not using RegExp, but using the DOM:
var tmp = document.createElement("div");
tmp.innerHTML = userStr;
tmp = tmp.firstChild;
console.log(tmp.src);
console.log(tmp.width);
console.log(tmp.height);
console.log(tmp.frameBorder);
Just make sure you don't add the created element to the document without sanitizing it first. You might also need to loop over the created nodes until you get to an element node.
Assuming they will always enter an HTML element you could parse it and read the elements from the DOM, like so (untested):
var getAttributes = function(str) {
var a={}, div=document.createElement("div");
div.innerHTML = str;
var attrs=div.firstChild.attributes, len=attrs.length, i;
for (i=0; i<len; i++) {
a[attrs[i].nodeName] = attrs[i].nodeValue];
}
return a;
};
var x = getAttributes(inputStr);
x; // => {width:'123', height:123, src:'http://...', ...}
Instead of regexp, use pure JavaScript:
Grab iframe element:
var iframe = document.getElementsByTagName('iframe')[0];
and then access its properties using:
var arr = {
src : iframe.src,
width : iframe.width,
height : iframe.height,
frameborder : iframe.frameborder
};
I would personally do this with jQuery, if possible. With it, you can create a DOM element without actually injecting it into your page and creating a potential security hazard.
var userTxt = '<iframe src="http://www.stackoverflow.com/" width="123" height="123" frameborder="1"></iframe>';
var userInput = $(userTxt);
console.log(userInput.attr('src'));
console.log(userInput.attr('width'));
console.log(userInput.attr('height'));
console.log(userInput.attr('frameborder'));

how to get style="i want the css code" using regexp (javascript)

var text = '..<anything><anything style="color:red;">hello</anything><anything style="color:blue; font-size:1em;">hello</anything></anything>...';
or
var text = "..<anything><anything style='color:red;'>hello</anything><anything style='color:blue; font-size:1em;'>hello</anything></anything>...";
result:
array[0] = "color:red;";
array[1] = "color:blue; font-size:1em;";
Make a temporary element and use innerHTML, then getElementsByTagName and getAttribute('style') if it's a string like that.
If it's a reference to a DOM element skip the innerHTML part.
var d = document.createElement('div'),
text = '..<anything><anything style="color:red;">hello</anything><anything style="color:blue; font-size:1em;">hello</anything></anything>...',
styles = [];
d.innerHTML=text;
var els = d.getElementsByTagName('*');
for ( var i = els.length; i--; ) {
if ( els[i].getAttribute('style') ) {
styles.push( els[i].getAttribute('style') )
}
}
styles
The jQuery would be..
$(text).find('*').map(function() { return this.getAttribute('style') })
As has already been mentioned, it's not a great idea to use regular expressions for parsing HTML.
However, if you're determined to do it that way, this will do the job for you:
var matches = text.match(/\bstyle=(['"])(.*?)\1/gi);
var styles = [];
for(var i = 0; i < matches.length; i++) {
styles.push(matches[i].substring(7, matches[i].length - 1));
}
meder has already given the correct answer, but since a regex solution was desired I'll give one. All the usual warnings about parsing a nonregular language like HTML (or SGML, or XML) with regular expressions apply.
/<[a-z]+(?:\s+ [a-z]+\s*=\s*(?:[^\s"']*|"[^"]*"|'[^']*'))\s+style\s*=\s*"([^"]*)">/g

Using innerHTML.replace to replace text to create link

I'm using Sharepoint (WSS 3.0), which is unfortunately very limited in its ability to format survey questions (i.e., it strips any HTML you enter). I saw a solution elsewhere that suggested we add some JS to our master page file in order to allow line breaks. This works beautifully, but I'd like to see if we can allow links as well.
In our WSS surveys, I can now use {{br}} anywhere I want a line break (this works). I have tried extending the code to allow the use of link tags (e.g., {{link1}}url{{link2}}URL Title{{link3}}; but, this doesn't work, presumably because the updates aren't happening as a whole, and the browser then tries to render it piece by piece, confusing it. (FF and IE show different results, but both fail. If I mix up the order of the JS below -- i.e., do link3, 2 and then 1 -- the output changes as well, but still fails.) Is there a better way to do this?
<script language="JavaScript">
var className;
className = 'ms-formlabel';
var elements = new Array();
var elements = document.getElementsByTagName('td');
for (var e = 0; e < elements.length; e++)
{
if (elements[e].className == className){
elements[e].innerHTML = elements[e].innerHTML.replace(/{{br}}/g,'<br/>');
elements[e].innerHTML = elements[e].innerHTML.replace(/{{link1}}/g,'<a href="');
elements[e].innerHTML = elements[e].innerHTML.replace(/{{link2}}/g,'">');
elements[e].innerHTML = elements[e].innerHTML.replace(/{{link3}}/g,'</a>');}
}
</script>
Instead of modifying the innerHTML property in chunks (the browser tries to update the DOM each time you change innerHTML, which if you provide incomplete/broken markup, will obviously mess things up), do all your modifications against your own string variable, and then overwrite the entire innerHTML with your completed string:
<script language="JavaScript">
var className;
className = 'ms-formlabel';
var elements = new Array();
var elements = document.getElementsByTagName('td');
for (var e = 0; e < elements.length; e++)
{
if (elements[e].className == className) {
var newHTML = elements[e].innerHTML;
newHTML = newHTML.replace(/{{br}}/g,'<br/>');
newHTML = newHTML.replace(/{{link1}}/g,'<a href="');
newHTML = newHTML.replace(/{{link2}}/g,'">');
newHTML = newHTML.replace(/{{link3}}/g,'</a>');}
elements[e].innerHTML = newHTML;
}
</script>
The simple answer would be to build up the innerHTML and replace it all at once:
for (var e = 0; e < elements.length; e++)
{
if (elements[e].className == className) {
var newHTML = elements[e].innerHTML;
newHTML = newHTML.replace(/{{br}}/g,'<br/>');
newHTML = newHTML.replace(/{{link1}}/g,'<a href="');
newHTML = newHTML.replace(/{{link2}}/g,'">');
newHTML = newHTML.replace(/{{link3}}/g,'</a>');
elements[e].innerHTML = newHTML;
}
}
The more complex answer would be to use capturing groups in your regex and pass a function as the 2nd parameter to replace(), so as to use a single call to replace() for the HTML. For example,
elements[e].innerHTML = elements[e].innerHTML.replace(/({{link1}})|({{link2}})|({{link3}})/g,
function (match) {
var map = {
'{{link1}}' : '<a href="',
'{{link2}}' : '>',
'{{link3}}' : '</a>', }
return map[match];
});
The second solution is more complex and leads to some ugly regexes, but is more efficient than calling replace() over and over again.

Categories

Resources