I'm trying to use a script to replace a particular string with a different string. I think the code is right, but I keep getting the error "Object does not allow properties to be added or changed."
Does anyone know what could be going wrong?
function searchAndReplace() {
var teams = SitesApp.getPageByUrl("https://sites.google.com/a/directory/teams");
var list = teams.getChildren();
list.forEach(function(element){
page = element.getChildren();
});
page.forEach(function(element) {
var html = element.getHtmlContent();
html.replace(/foo/, 'bar');
element.setHtmlContent = html;
});
};
Try This:
Javascript reference:
The replace() method returns a new string with some or all matches of a pattern replaced by a replacement.
I think the issue here is that forEach cannot change the array that it is called upon. From developer.mozilla.org "forEach() does not mutate the array on which it is called (although callback, if invoked, may do so)."
Try doing it with a regular loop.
I am trying to convert XML to SVG using Java, but it looks like the shapes information is getting lost in the process.
Given a simple draw.io graph:
After running XmlToSvg.java I get:
I saved it as an uncompressed XML. I'm using the mxgraph-all.jar from the mxGraph Repo
Do you know if there are hidden settings to enable to preserve shapes and colors?
Short version
It looks like despite the claims on the GitHub page, no implementation except for JavaScript one is really fully featured and production ready. Particularly Java implementation (as well .Net and PHP server-side ones) doesn't support "Cube" shape out of the box.
More details
Colors
You didn't provide your example XML but when I generate similar graph I get something like
<?xml version="1.0" encoding="UTF-8"?>
<mxGraphModel dx="1426" dy="816" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850"
pageHeight="1100" background="#ffffff" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="445" y="60" width="230" height="150" as="geometry"/>
</mxCell>
<mxCell id="3" value="" style="ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
<mxGeometry x="500" y="320" width="120" height="120" as="geometry"/>
</mxCell>
<mxCell id="4" value="" style="endArrow=classic;html=1;" parent="1" source="3" target="2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="430" y="510" as="sourcePoint"/>
<mxPoint x="480" y="460" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="5" value="" style="shape=cube;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="80" y="320" width="170" height="110" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
Important thing here is that this XML does not contain any information about colors. Thus whole idea about "preserving colors" is wrong. In Java implementation you can configure "default colors" using an instance of mxStylesheet class and use it to init mxGraph object. For example to change colors to black and white you may do something like this:
mxStylesheet stylesheet = new mxStylesheet();
// configure "figures" aka "vertex"
{
Map<String, Object> style = stylesheet.getDefaultVertexStyle();
style.put(mxConstants.STYLE_FILLCOLOR, "#FFFFFF");
style.put(mxConstants.STYLE_STROKECOLOR, "#000000");
style.put(mxConstants.STYLE_FONTCOLOR, "#000000");
}
// configure "lines" aka "edges"
{
Map<String, Object> style = stylesheet.getDefaultEdgeStyle();
style.put(mxConstants.STYLE_STROKECOLOR, "#000000");
style.put(mxConstants.STYLE_FONTCOLOR, "#000000");
}
mxGraph graph = new mxGraph(stylesheet);
You may look at mxStylesheet.createDefaultVertexStyle and mxStylesheet.createDefaultEdgeStyle for some details.
Shapes
The "ellipse" shape is not handled correctly because there is no code to parse "ellipse;whiteSpace=wrap;html=1;" and understand that the shape should be "ellipse" (comapre this to the "double ellipse" style "ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;" that contains explicit shape value). In JS implementation the first part of the style seems to select a handler function that will handle the rest of the string and do actual work. There seems to be no such feature in Java implmenetation at all. You can work this around by using "named styles" feature and define default shape for corresponding "handler" in the same mxStylesheet object like this:
// I just copied the whole list of mxConstants.SHAPE_ here
// you probably should filter it by removing non-primitive shapes
// such as mxConstants.SHAPE_DOUBLE_ELLIPSE
String[] shapes = new String[] {
mxConstants.SHAPE_RECTANGLE,
mxConstants.SHAPE_ELLIPSE,
mxConstants.SHAPE_DOUBLE_RECTANGLE,
mxConstants.SHAPE_DOUBLE_ELLIPSE,
mxConstants.SHAPE_RHOMBUS,
mxConstants.SHAPE_LINE,
mxConstants.SHAPE_IMAGE,
mxConstants.SHAPE_ARROW,
mxConstants.SHAPE_CURVE,
mxConstants.SHAPE_LABEL,
mxConstants.SHAPE_CYLINDER,
mxConstants.SHAPE_SWIMLANE,
mxConstants.SHAPE_CONNECTOR,
mxConstants.SHAPE_ACTOR,
mxConstants.SHAPE_CLOUD,
mxConstants.SHAPE_TRIANGLE,
mxConstants.SHAPE_HEXAGON,
};
Map<String, Map<String, Object>> styles = stylesheet.getStyles();
for (String sh : shapes)
{
Map<String, Object> style = new HashMap<>();
style.put(mxConstants.STYLE_SHAPE, sh);
styles.put(sh, style);
}
Still you may notice that the list of the mxConstants.SHAPE_ doesn't contain "cube". In JS implementation "cube" is a compound shape that is handled by a specialized handler in examples/grapheditor/www/js/Shape.js which is not a part of the core library! It means that if you want to support such advanced shapes in your Java code, you'll have to roll out the code to handle it yourself.
P.S. With all those changes (hacks) the image I get using Java code from the XML in the first snippet is:
There is an XML-file, containing parameters of the most generic shapes. You should load it into stylesheet to make images look exactly as they were drawn in editor. Default stylesheet is default.xml.
So first of all make your code to get 2 things: stylesheet and diagram content.
String diagramText = getAsString(diagramPath);
String stylesheetText = getAsString(stylesheetPath);
Next, the simplest way to create SVG image is to utilize classes from mxgraph-core.jar. It looks like this
mxStylesheet stylesheet = new mxStylesheet(); // mxgraph-core.jar
InputSource is = new InputSource(new StringReader(stylesheetText));
Document document = documentBuilder.parse(is);
mxCodec codec = new mxCodec(document);
codec.decode(document.getDocumentElement(), stylesheet);
mxIGraphModel model = new mxGraphModel();
mxGraph graph = new mxGraph(model, context.stylesheet);
is = new InputSource(new StringReader(diagramText));
document = documentBuilder.parse(new InputSource(is));
codec = new mxCodec(document);
codec.decode(document.getDocumentElement(), model);
final Document svgDocument = documentBuilder.newDocument();
mxCellRenderer.drawCells(
graph,
null,
1d,
null,
new mxCellRenderer.CanvasFactory() {
#Override
public mxICanvas createCanvas(int width, int height) {
Element root = output.createElement("svg");
String w = Integer.toString(width);
String h = Integer.toString(height);
root.setAttribute("width", w);
root.setAttribute("height", h);
root.setAttribute("viewBox", "0 0 " + w + " " + h);
root.setAttribute("version", "1.1");
root.setAttribute("xmlns", "http://www.w3.org/2000/svg");
root.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
output.appendChild(root);
mxSvgCanvas canvas = new mxSvgCanvas(svgDocument);
canvas.setEmbedded(true);
return canvas;
}
});
return svgDocument; // this is the result
However, as SergGr pointed, Java implementation of mxgraph library doesn't contain some useful shapes. Their drawing rules are described by JavaScript functions in Shape.js.
I tried to execute that JavaScript in ScriptEngine shipped in Java standard library. Unfortunately this idea didn't work, because the JavaScript code somewhere deep inside interacts with browser.
But if we run the code in a browser, it works well. I did it successfully with HtmlUnit.
Write a JavaScript function to call from Java:
function convertToSVG(diagramText, stylesheetText) {
var stylesheet = new mxStylesheet();
var doc = mxUtils.parseXml(stylesheetText);
var stylesheetRoot = doc.documentElement;
var stylesheetCodec = new mxCodec(doc);
var dom = document.implementation;
stylesheetCodec.decode(stylesheetRoot, stylesheet);
doc = dom.createDocument(null, "div", null);
var model = new mxGraphModel();
var graph = new mxGraph(doc.documentElement, model, "exact", stylesheet);
doc = new DOMParser().parseFromString(diagram, "text/xml");
var codec = new mxCodec(doc);
codec.decode(doc.documentElement, model);
doc = dom.createDocument("http://www.w3.org/2000/svg", "svg", null);
var svgRoot = doc.documentElement;
var bounds = graph.getGraphBounds();
svgRoot.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svgRoot.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
svgRoot.setAttribute("width", bounds.width);
svgRoot.setAttribute("height", bounds.height);
svgRoot.setAttribute("viewBox", "0 0 " + bounds.width + " " + bounds.height);
svgRoot.setAttribute("version", "1.1");
var svgCanvas = new mxSvgCanvas2D(svgRoot);
svgCanvas.translate(-bounds.x, -bounds.y);
var exporter = new mxImageExport();
var state = graph.getView().getState(model.root);
exporter.drawState(state, svgCanvas);
var result = new XMLSerializer().serializeToString(doc);
return result;
}
Load this text into String and run the following code
String jsFunction = getAsString("convertToSVG.js");
Path file = Files.createTempFile("44179673-", ".html"); // do not forget to delete it
String hmltText = "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head><title>Empty file</title></head><body/></html>";
Files.write(file, Arrays.asList(htmlText));
WebClient webClient = new WebClient(); // net.sourceforge.htmlunit:htmlunit
HtmlPage page = webClient.getPage(file.toUri().toString());
String initScript = ""
+ "var mxLoadResources = false;"
+ "var mxLoadStylesheets = false;"
+ "var urlParams = new Object();";
page.executeJavaScript(initScript);
page.executeJavaScript(getAsString("mxClient.min.js"));
page.executeJavaScript(getAsString("Graph.js")); // Shape.js depends on it
page.executeJavaScript(getAsString("Shapes.js"));
ScriptResult scriptResult = page.executeJavaScript(jsFunction);
Object convertFunc = scriptResult.getJavaScriptResult();
Object args[] = new Object[]{ diagramText, stylesheetText };
scriptResult = page.executeJavaScriptFunction(convertFunc, null, args, null);
String svg = scriptResult.getJavaScriptResult().toString();
The code above seems to work well for me.
I'm trying to set up a function in Polymer 1.0 which will allow a JSON response to tell my application which {{BindingVariable}} in which to insert the response. Unfortunately, the syntax for referencing these binding variables seems to be similar to this:this.BindingVariable, which doesn't allow for dynamic variable names.
What I really need is a way to reference these dynamically like how we can reference anything else in the DOM/PolyDOM. For example: document.querySelector('#'+elementID).
Is there any way to reference binding annotations dynamically? I've searched through the entire Polymer DOM and can't find them listed anywhere even though I know they're in the page.
example
app._onResponseRetrieved = function(e) {
for (var key in e.detail.response) {
// none of these work, but they demonstrate what I'm trying to accomplish
// this.key = e.detail.response[key];
// this.querySelector(key) = e.detail.response[key];
// window[key] = e.detail.response[key];
// document[key] = e.detail.response[key];
// Polymer.dom(key) = e.detail.response[key];
}
JSON Sent to _onResponseRetrieved
{"contactFormOutput":"Success!"}
Binding Annotation in index.html
<div>{{contactFormOutput}}</div>
this[key] = e.detail.response[key];
Javascript allows [] on any object for dynamic property referencing
I have problems binding this JSON to my list view.
http://pubapi.cryptsy.com/api.php?method=marketdatav2
No data is displayed.
Data.js
(function () {
"use strict";
var _list;
WinJS.xhr({ url: 'http://pubapi.cryptsy.com/api.php?method=marketdatav2' }).then(
function (response) {
var json = JSON.parse(response.responseText);
_list = new WinJS.Binding.List(json.return.markets);
},
function (error) {
//handle error
}
);
var publicMembers =
{
itemList: _list
};
WinJS.Namespace.define("DataExample", publicMembers);
})();
HTML:
<section aria-label="Main content" role="main">
<div id="listItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="listItem">
<div class="listItemTemplate-Detail">
<h4 data-win-bind="innerText: label"></h4>
</div>
</div>
</div>
<div id="listView" data-win-control="WinJS.UI.ListView" data-win-options="{itemDataSource : DataExample.itemList, itemTemplate: select('#listItemTemplate'), layout: {type: WinJS.UI.GridLayout}}"></div>
</section>
I feel that the API is not that well formed.
Isnt this part a bit odd?
"markets":{"ADT/XPM":{...}...}
There are three things going on in your code here.
First, a ListView must be bound to a WinJS.Binding.List's dataSource property, not the List directly. So in your HTML you can use itemDataSource: DataExample.itemList.dataSource, or you can make your DataExample.itemList dereference the dataSource at that level.
Second, you're also running into the issue that the declarative binding of itemDataSource in data-win-options is happening well before DataExample.itemList is even populated. At the point that the ListView gets instantiated, _list and therefore itemList will be undefined. This causes a problem with trying to dereference .dataSource.
The way around this is to make sure that DataExample.itemList is initialized with at least an empty instance of WinJS.Binding.List on startup. So putting this and the first bit together, we have this:
var _list = new WinJS.Binding.List();
var publicMembers =
{
itemList: _list.dataSource
};
With this, you can later replace _list with a different List instance, and the ListView will refresh itself.
This brings us to the third issue, populating the List with your HTTP response data. The WinJS.Binding.List takes an array in its constructor, not an object. You're passing the parsed JSON object straight from the HTTP request, which won't work.
Now if you have a WinJS.Binding.List instance already in _list as before, then you can just walk the object and add items directly to the List as follows:
var jm = json.return.markets;
for (var i in jm) {
_list.push(jm[i]);
}
Alternately, you could populate a separate array and then create a new List from that. In this case, however, you'll need to assign that new List.dataSource to the ListView in code:
var jm = json.return.markets;
var markets = [];
for (var i in jm) {
markets.push(jm[i]);
}
_list = new WinJS.Binding.List(markets);
var listview = document.getElementById("listView").winControl;
listview.itemDataSource = _list.dataSource;
Both ways will work (I tested them). Although the first solution is simpler and shorter, you'll need to make sure to clear out the List if you make another HTTP request and repopulate from that. With the second solution you just create a new List with each request and hand that to the ListView, which might work better depending on your particular needs.
Note also that in the second solution you can remove the itemDataSource option from the HTML altogether, and also eliminate the DataExample namespace and its variables because you'll assign the data source in code each time. Then you can also keep _list entirely local to the HTTP request.
Hope that helps. If you want to know more about ListView intricacies, see Chapter 7 of my free ebook from MSPress, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
e4x / as3: How to access a node with a dash in its name.
I've set the namespace for my XML to use SMIL and I'm able to pull the src attribute of an element this way:
my.node.#src which gets "this is some URL"
However, I have another attr called 'system-bitrate'. Because of the minus sign, I can't do #system-bitrate So I attempted what I normally do which is my.node.attribute('system-bitrate') which isn't working.
Oddly enough, not even my.node.attribute('src') works. I suspect this is due to the namespace? So how to I get attributes out using ny.node.attribute ?
The only thing that works is my.node.attributes()[1]. I know that's not the "right way" so I'm hoping someone can enlighten me!
FYI I'm working with SMIL files
** edit **
Here's the namespace required for the XML I'm using:
default xml namespace = new Namespace('http://www.w3.org/2001/SMIL20/Language');
And an example of the XML I'm working with:
<smil xmlns="http://www.w3.org/2001/SMIL20/Language">
<head>
<meta name="title" content="Live"/>
</head>
<body>
<switch>
<video src="myStreamName" system-bitrate="200000"/>
</switch>
</body>
</smil>
** code sample for DennisJaaman **
default xml namespace = new Namespace('http://www.w3.org/2001/SMIL20/Language');
var xml:XML = XML(event.target.data);
for each(var o:XML in xml.body['switch'].video) {
if(!hs) hs = o;
else {
trace(o.attributes()[1]); // works
trace(o.#url); // doesn't work either (makes me wonder about NS issues
trace(o['#system-bitrate']); // doesn't work
trace(o.attribute('#system-bitrate') // doesn't work
// etc etc, I just left a few in here
}
}
Try to use square brackets like in the sample below:
default xml namespace = new Namespace("http://www.w3.org/2001/SMIL20/Language");
var xmlSmpl:XML=<smil xmlns="http://www.w3.org/2001/SMIL20/Language">
<head>
<meta name="title" content="Live"/>
</head>
<body>
<switch>
<video src="myStreamName" system-bitrate="200000"/>
</switch>
</body>
</smil>;
trace (xmlSmpl.body['switch']['video']['#system-bitrate']);
Behold! The power of QName!
my.node.attribute(
new QName( 'http://www.w3.org/2001/SMIL20/Language', 'system-bitrate' )
)
The thing about attribute (and descendant, and child...) is that its parameter is type * (anonymously). This is because it really isn't a String, it is coerced to a QName (without a URI) in the background. This means you were searching under the default URI for something under the URI above.
Let me know how that code above works out.
Check out this post:
e4x / as3: How to access a node with a dash in its name
******EDIT****:
And use the following notation to get XML Attributes that contain a - (dash)
trace("Video system-bitrate: " + my.node.video["#system-bitrate"]);
These do not work:
trace("Video system-bitrate: " + my.node.video.#["system-bitrate"]);
trace("Video system-bitrate: " + my.node.video.attribute("#system-bitrate"));
For more info check the LiveDocs
Cheers