I'm trying to parse an xml Object in extendscript and especially deal with the Attributes. I know i can access xml attributes by
xmlObj.#attributename
and
xmlObj.attributes()
returns a list of all attributes, but I also need the attribute names not just the values. Is there anyway to get something like and associative array/object with names and values?
(I use extendscript for illustrator CS6)
thank you,
arno
The code below should get you going. Take also a look into the XMLElement Object.
var main = function() {
// create some xml and write it to file
var root = new XML("<root/>");
var child = new XML("<child/>");
child.#string = "Hello Attribute"; // jshint ignore:line
child.#num = 23; // jshint ignore:line
root.appendChild(child);
var file = new File("~/Desktop/test.xml");
var xml = root.toXMLString();
file.open("W");
file.write(xml);
file.close();
// get the current doc
var doc = app.activeDocument;
// import the xml
doc.importXML(file);
// get the elements
var xmlroot = doc.xmlElements[0];
var xmlchild = xmlroot.xmlElements[0];
// loop all attributes of element "child"
// and write them into the console
for (var i = 0; i < xmlchild.xmlAttributes.length; i++) {
var attr = xmlchild.xmlAttributes[i];
$.writeln(attr.name);
}
};
main();
i've found a way solve it with regular Expressions
function getAttributes(xml_node_str) {
// select the start tag <elem >
var reg_exp = /<[^>]*>/;
var start_tag_str = reg_exp.exec(xml_node_str);
// extract the attributes
reg_exp = /[^"\s]*="[^"]*"/g;
var result;
var attributes = [];
while ((result = reg_exp.exec(start_tag_str)) !== null) {
// the attribute (name="value")
var attr = result[0];
// array containing name and "value"
var attr_arr = attr.split('=');
// delete the "'s
attr_arr[1] = attr_arr[1].substr(1, attr_arr[1].length - 2);
attributes.push(attr_arr);
}
return attributes;
}
I still parse the xml with Extendscripts/Illustrators xml-class and then extract the attributes manually
var xml = <root><obj a1="01" a2="02" ></obj></root > ;
var attributes = getAttributes(xml.obj.toXMLString());
for (var i = 0; i < attributes.length; i++) {
alert(attributes[i][0] + ' -> ' + attributes[i][1]);
}
Related
I have worked with code that pulls table information off a site and then places into Google Sheets. While this had worked great for months, it has come to my attention that is has randomly stopped working.
I am getting the message "TypeError: Cannot read property "length" from undefined." From code:
for (var c=0; c<current_adds_array.length; c++) {
I have done extensive searching but cannot come to conclusion as to what is wrong.
Full code seen here:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Get Data')
.addItem('Add new dispatch items','addNewThings')
.addToUi();
}
function addNewThings() {
// get page
var html = UrlFetchApp.fetch("#").getContentText();
// bypass google's new XmlService because html isn't well-formed
var doc = Xml.parse(html, true);
var bodyHtml = doc.html.body.toXmlString();
// but still use XmlService so we can use getDescendants() and getChild(), etc.
// see: https://developers.google.com/apps-script/reference/xml-service/
doc = XmlService.parse(bodyHtml);
var html = doc.getRootElement();
// a way to dig around
// Logger.log(doc.getRootElement().getChild('form').getChildren('table'));
// find and dig into table using getElementById and getElementsByTagName (by class fails)
var tablecontents = getElementById(html, 'formId:tableExUpdateId');
// we could dig deeper by tag name (next two lines)
// var tbodycontents = getElementsByTagName(tablecontents, 'tbody');
// var trcontents = getElementsByTagName(tbodycontents, 'tr');
// or just get it directly, since we know it's immediate children
var trcontents = tablecontents.getChild('tbody').getChildren('tr');
// create a nice little array to pass
var current_adds_array = Array();
// now let's iterate through them
for (var i=0; i<trcontents.length; i++) {
//Logger.log(trcontents[i].getDescendants());
// and grab all the spans
var trcontentsspan = getElementsByTagName(trcontents[i], 'span');
// if there's as many as expected, let's get values
if (trcontentsspan.length > 5) {
var call_num = trcontentsspan[0].getValue();
var call_time = trcontentsspan[1].getValue();
var rptd_location = trcontentsspan[2].getValue();
var rptd_district = trcontentsspan[3].getValue();
var call_nature = trcontentsspan[4].getValue();
var call_status = trcontentsspan[5].getValue();
//saveRow(call_num, call_time, rptd_location, rptd_district, call_nature, call_status);
current_adds_array.push(Array(call_num, call_time, rptd_location, rptd_district, call_nature, call_status));
}
}
saveRow(current_adds_array);
}
//doGet();
function saveRow(current_adds_array) {
// load in sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// find the current last row to make data range
var current_last_row = sheet.getLastRow();
var current_last_row_begin = current_last_row - 50;
if (current_last_row_begin < 1) current_last_row_begin = 1;
if (current_last_row < 1) current_last_row = 1;
//Logger.log("A"+current_last_row_begin+":F"+current_last_row);
var last_x_rows = sheet.getRange("A"+current_last_row_begin+":F"+current_last_row).getValues();
var call_num, call_time, rptd_location, rptd_district, call_nature, call_status;
// iterate through the current adds array
for (var c=0; c<current_adds_array.length; c++) {
call_num = current_adds_array[c][0];
call_time = current_adds_array[c][1];
rptd_location = current_adds_array[c][2];
rptd_district = current_adds_array[c][3];
call_nature = current_adds_array[c][4];
call_status = current_adds_array[c][5];
// find out if the ID is already there
var is_in_spreadsheet = false;
for (var i=0; i<last_x_rows.length; i++) {
//Logger.log(call_num+" == "+last_15_rows[i][0]);
if (call_num == last_x_rows[i][0] && call_time != last_x_rows[i][1]) is_in_spreadsheet = true;
}
Logger.log(is_in_spreadsheet);
//Logger.log(last_15_rows.length);
if (!is_in_spreadsheet) {
Logger.log("Adding "+call_num);
sheet.appendRow([call_num,call_time,rptd_location,rptd_district,call_nature,call_status]);
}
}
}
function getElementById(element, idToFind) {
var descendants = element.getDescendants();
for(i in descendants) {
var elt = descendants[i].asElement();
if( elt !=null) {
var id = elt.getAttribute('id');
if( id !=null && id.getValue()== idToFind) return elt;
}
}
}
function clearRange() {
//replace 'Sheet1' with your actual sheet name
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
sheet.getRange('A2:F').clearContent();}
function getElementsByTagName(element, tagName) {
var data = [];
var descendants = element.getDescendants();
for(i in descendants) {
var elt = descendants[i].asElement();
if( elt !=null && elt.getName()== tagName) data.push(elt);
}
return data;
}
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("C:C");
range.setValues(range.getValues().map(function(row) {
return [row[0].replace(/MKE$/, " Milwaukee, Wisconsin")];
}));
Please be careful when instantiating a new array. You are currently using var current_adds_array = Array(). You're not only missing the new keyword, but also, this constructor is intended to instantiate an Array with an Array-like object.
Try changing this to var current_adds_array = []
I am unable to determine if a DataMergeField is contained within a TextFrame.
var document = app.open('template.indd');
var dataMerge = document.dataMergeProperties;
var field;
for (field in document.dataMergeTextPlaceholders) {
var story = field.parentStory;
var content = story.contents;
var textFrame = story.textFrames.item(0);
//textFrame is null
}
//....
I am attempting to provide wrap, fit, fill options for the textual contents of any DataMergeFields, adjusting them based on the TextFrame dimensions. Without knowing any of the DataMergeFields or TextFrames properties used in the document.
Something like this should work in JS:
var document = app.open('template.indd');
var hs = document.dataMergeTextPlaceholders;
var n = hs.length, h, tf;
while (n--) {
h = hs[n];
if ( h.storyOffset.parentTextFrames.length ) {
tf = h.storyOffset.parentTextFrames[0];
//do something with tf
}
}
I was able to get this to work with the following. I am guessing that using document as opposed to app.documents.item(0) was the issue.
app.open('template.indd');
var phs = app.documents.item(0).dataMergeTextPlaceholders;
var i, textFrame, ph, story;
for (i = 0; i < phs.length; i++) {
ph = phs.item(i);
if (ph instanceof DataMergeTextPlaceholder) {
story = ph.parentStory;
if (story.textFrames.length > 0) {
textFrame = story.textFrames.item(0);
//...
}
}
}
I have excel as an activeX object in javascript. I seem to be missing something with reards to how to interact with the object model from there. My watch window shows the value of the "Value" property of the range I am trying to pull data from as "undefined" when I try to assign "range.Value" to an array.
Unfortunately I am unable to update the outdated browsers on my machine at work so I cannot upload pictures.
My script:
function open_files(A, B, C)
{
var excel = new ActiveXObject("Excel.Application");
excel.Visible=true;
excel.DisplayAlerts = false;
var wbA = excel.Workbooks.Open(document.getElementById(A).value);
var wbB = excel.Workbooks.Open(document.getElementById(B).value);
var wbC = excel.Workbooks.Open(document.getElementById(C).value);
excel.EnableEvents = false;
excel.ScreenUpdating = false;
excel.Calculation = -4135 //xlCalculationManual enumeration;
var wb_collection = [wbA, wbB, wbC];
excel.Application.Run("'" + wbA.name + "'" + '!update_links');
var CLIN_list = [wbA.Sheets("Control Form").Range("B62:B141").value(1)]
for (i = 0; i = CLIN_list.length; i++)
{
if (CLIN_list(i) > 0)
{
var CLIN_list_count = i
}
}
var decrement_range_start = wbA.Sheets("Fee & Decrement Table").Range("AJ14")
//for (i = 0; i < 80; i++){
//Sheets("Fee & Decrement Table").Cells(decrement_range_start.column+i
// Model Setup for VBA
wbA.Sheets("CONTROL FORM").Activate
wbA.Sheets("CONTROL FORM").OLEObjects("TextBox21").Object.Text = wbB.fullname
wbA.Sheets("CONTROL FORM").OLEObjects("TextBox22").Object.Text = wbC.fullname
excel.Application.Run("'" + wbA.name + "'" + '!Run_JPO');
I found an answer on another forum. A Range cannot be assigned directly to a js array, it has to be converted. The line below works to fill my CLIN_list variable.
var CLIN_list = new VBArray(wbA.Sheets("Control Form").Range("B62:B141").value).toArray();
I'm currently writing a Sketch Plugin and I'm trying to store data inside an global array. In the copy.sketchscript the data is generated, and in paste.sketchscript I'm trying to retrieve the array data. However, when I log the variable, it returns empty. What can I do to update the array data properly so that every function can access it?
Here's my code.
library/common.js
var verticalRulers = new Array; // global?
function copyVertical(context) {
var doc = context.document
var target = [[doc currentPage] currentArtboard] || [doc currentPage]
var countVertical = [[target verticalRulerData] numberOfGuides]
for(var i=0; i < countVertical; i++) {
var thisRuler = [[target verticalRulerData] guideAtIndex:i]
verticalRulers.push(thisRuler);
}
}
function pasteVertical(context) {
var doc = context.document
var target = [[doc currentPage] currentArtboard] || [doc currentPage]
for(i = 0; i < verticalRulers.length; i++) {
var thisRuler = verticalRulers[i];
[[target verticalRulerData] addGuideWithValue: thisRuler]
}
}
copy.sketchscript
#import 'library/common.js'
function onRun(context) {
verticalRulers = copyVertical(context);
log(verticalRulers) // return right data from variable;
}
paste.sketchscript
#import 'library/common.js'
function onRun(context) {
pasteVertical(context);
log(verticalRulers); // returns an empty array
}
I have an array as a attribute on a link.
Here is the array
images="["one.jpg","two.jpg"]"
How would I parse through this array and have it read back to me one.jpg,two.jpg?
This is what I am doing now and it is giving me an error back. I don't believe json parsing is whats needed here.
var imgs = $("#"+number).attr("images");
var imgList = jQuery.parseJSON(imgs);
EDIT: ACTUAL CODE
var number = $(this).attr("data-id");
var url = $("#"+number).attr("url");
$(".portfolio-url").html("<h3 class='pacifico'>url</h3><p><a href='http://"+url+"' target='_blank'>"+url+"</a></p>");
var cli = $("#"+number).attr("client");
$(".portfolio-client").html("<h3 class='pacifico'>client</h3><p>"+cli+"</p>");
var pgs = $("#"+number).attr("pages");
pgs = pgs.replace(/\[/g,"");
pgs = pgs.replace(/\]/g,"");
pgs = pgs.replace(/\"/g,"");
var pages = new Array();
pages = pgs.split(",");
var img = $("#"+number).attr("images");
img = img.replace(/\{/g,"");
img = img.replace(/\}/g,"");
img = img.replace(/\"/g,"");
var images = new Array();
images = img.split(",");
var portSkills = "<h3 class='pacifico'>skills</h2>";
portSkills += "<p>";
for (i=0;i<pages.length;i++) {
if (pages[i] != "Clients") {
var finalPage = "";
for (j=0;j<pages[i].length;j++)
{
var ch = pages[i].charAt(j);
if (ch == ch.toUpperCase()) {
finalPage += " ";
}
finalPage += pages[i].charAt(j);
}
portSkills += finalPage+"<br />";
}
}
portSkills += "</p>";
$(".portfolio-skills").html(portSkills);
var imgs = $("#"+number).attr("images");
var imgList = jQuery.parseJSON(imgs);
Basically, its looping through parameters
I'd encourage you to modify your attribute-value format to something along these lines:
<div id="one" data-images="file1.jpg,file2.jpg">Foo, Bar</div>
Note here I'm using a valid data- attribute, and the value of this attribute is just a list of comma-separated filenames. No need to place [ or ] in this value in order to get an array.
Now, to get your array:
var images = $("#one").data("images").split(",");
Which results in the following array:
["file1.jpg", "file2.jpg"]
Don't put that kind of string in the attribute, you could just put a comma separated string instead. (And you could use data attribute.)
For example:
<a id="foo" data-images="one.jpg,two.jpg">foo</a>
then you could get it by:
var imgList = $('#foo').data('images').split(',');
for (var i = 0; i < images.length; i++) {
var image = images[i];
}
For starters:
images = ["one.jpg", "two.jpg"]; is an array, yours is invalid.
to have it read back to you
for(image in images)
console.log(images[image]);
or the jQuery way
$.each(images, function(index){
console.log(images[index]);
});
if its a String that you need to split
then but that is of course if the string looks like this
var img = '["one.jpg", "two.jpg"]';
var images = img.replace(/\[|\]|"| /g,'').split(',');
this will give you an array parsed from a string that looks like an array.
Give the join() method a try:
images.join();
=> "one.jpg,two.jpg"
images.join(", ");
=> "one.jpg, two.jpg"
Edit: To declare your Array:
var images = ["img1", "img2", "img3"];
or
var images = new Array("img1", "img2", "img3");
Then you can use the join() method, and if that still doesn't work, try the following:
// should be true, if not then you don't have an Array
var isArray = (images instanceof Array);