svg-edit - set background dynamically - svg-edit

I'm looking at using svg-edit to annotate images in a browser. The image is uploaded on the same page the svg-edit is embedded in and needs to be set dynamically on upload as the background for svg-edit. Can this be done?

You have to call the method of the object i.e svgCanvas.setBackground();
For this you can create an extension e.g : ext-backchange.js
this file look likes this:
svgEditor.addExtension("changeback", function() {
svgCanvas.setBackground('','abc.png');
return {};
});
Then include this extension in svg-edit.js as in the last line :
svgEditor.setConfig(
{
extensions: ['ext-backchange.js']
})

You can use setImageBackground(imageBackground) function
setImageBackground("image.png");
and add this function setImageBackground in svgcanvas.js This image appear on the canvas and can be rotated.
this.setImageBackground= function(val) {
var elem = addSvgElementFromJson({
"element": "image",
"attr": {
"x": ( svgcontent.getAttribute('x') - bgimg_with ) / 2,
"y": ( svgcontent.getAttribute('y') - bgimg_height ) / 2,
"width": bgimg_with,
"height": bgimg_height,
"id": 'ImgBckgd',
"opacity": 1,
"style": "pointer-events:inherit",
}
});
setHref(elem, last_good_img_url);
preventClickDefault(elem);
if(!elem) return;
var attrs = $(elem).attr(['width', 'height']);
var setsize = (!attrs.width || !attrs.height);
var cur_href = getHref(elem);
// Do nothing if no URL change or size change
if(cur_href !== val) {
setsize = true;
} else if(!setsize) return;
var batchCmd = new BatchCommand("Change Image URL");
setHref(elem, val);
batchCmd.addSubCommand(new ChangeElementCommand(elem, {
"#href": cur_href
}));
if(setsize) {
$(new Image()).load(function() {
var changes = $(elem).attr(['width', 'height']);
$(elem).attr({
width: this.width,
height: this.height
});
selectorManager.requestSelector(elem).resize();
batchCmd.addSubCommand(new ChangeElementCommand(elem, changes));
addCommandToHistory(batchCmd);
call("changed", [elem]);
}).attr('src',val);
} else {
addCommandToHistory(batchCmd);
}
};

Related

Multiple renderer in esri map

I have requirement which is using classbreakrenderer for showing clusters size as it breaks down, now i want to use one renderer which shows different different symbol for single cluster based on some attribute value.
I have tried to use UniqueValueRenderer but it not working.
Any suggestion
I have requirement which is using classbreakrenderer for showing clusters size as it breaks down, now i want to use one renderer which shows different different symbol for single cluster based on some attribute value.
I have tried to use UniqueValueRenderer but it not working.
I have requirement which is using classbreakrenderer for showing clusters size as it breaks down, now i want to use one renderer which shows different different symbol for single cluster based on some attribute value.
I have tried to use UniqueValueRenderer but it not working.
<script type="text/javascript">
window.dojoConfig = {
async: true,
packages: [
{
name: 'app',
location: window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + '/src'
}
]};
</script>
<!-- ArcGIS API for JavaScript library references -->
<script src="https://js.arcgis.com/3.15compact"></script>
<script>
require(["esri/map",
"esri/dijit/Geocoder",
"esri/dijit/HomeButton",
"esri/renderers/SimpleRenderer",
"esri/symbols/PictureMarkerSymbol",
"esri/layers/FeatureLayer",
"esri/InfoTemplate",
"esri/graphic",
"esri/graphicsUtils",
"app/clusterfeaturelayer",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/PictureMarkerSymbol",
"esri/renderers/ClassBreaksRenderer",
"esri/renderers/HeatmapRenderer",
"esri/renderers/UniqueValueRenderer",
"dojo/_base/Color",
"dojo/on",
"dojo/dom-style",
"dojo/_base/fx",
"dojo/fx/easing",
"dojo/dom",
"dojo/domReady!"],
function (Map, Geocoder, HomeButton, SimpleRenderer, PictureMarkerSymbol, FeatureLayer, UniqueValueRenderer, InfoTemplate, Graphic, graphicsUtils, ClusterFeatureLayer, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, PictureMarkerSymbol, ClassBreaksRenderer, HeatmapRenderer, Color, on, domStyle, fx, easing, dom) {
// Locals
var map,
popup,
clusterLayer,
geocoder,
infoTemplate,
defaultSym,
selectedSym,
activeClusterElement;
// Create map
map = new Map("mapDiv", {
basemap: "dark-gray",
center: [-120, 50],
zoom: 3
});
// Create widget
geocoder = new Geocoder({
value: "California, United States",
autoNavigate: true,
maxLocations: 25,
autoComplete: true,
arcgisGeocoder: {
outFields: "Place_addr, PlaceName, Score"
},
map: map
}, "search");
geocoder.startup();
var home = new HomeButton({
map: map
}, "homeButton");
home.startup();
// Add raw points
// var featureLayer = new FeatureLayer("http://services.arcgis.com/oKgs2tbjK6zwTdvi/arcgis/rest/services/Major_World_Cities/FeatureServer/0", {
// infoTemplate: infoTemplate,
// outFields: ["*"]
// });
// map.addLayer(featureLayer);
// Add heatmap
// var featureLayer2 = new FeatureLayer("http://services.arcgis.com/oKgs2tbjK6zwTdvi/arcgis/rest/services/Major_World_Cities/FeatureServer/0");
// var heatmapRenderer = new HeatmapRenderer();
// featureLayer2.setRenderer(heatmapRenderer);
// map.addLayer(featureLayer2);
// Add clusters
map.on("load", function () {
// Add layer
addClusterLayer();
addClusterLayerEvents();
});
// Set popup
popup = map.infoWindow;
popup.highlight = false;
popup.titleInBody = false;
popup.domNode.className += " light";
//popup.domNode.style.marginTop = "-17px"; // for pins only
// Popup content
//infoTemplate = new InfoTemplate("Seismic Activity > 4.0", "<p>Location: ${NAME}</p><p>Magnitude: ${OTHER_MAG1}</p><p>Date: ${YEAR}/${MONTH}/${DAY}</p>");
infoTemplate = new InfoTemplate("<b>${CITY_NAME}</b>", "<p>COUNTRY: ${CNTRY_NAME}</p><p>STATE/PROVINCE: ${ADMIN_NAME}</p><p>POPULATION: ${POP}</p>");
// Option 1: Esri marker for single locations and selections
// defaultSym = new createPictureSymbol("./images/blue-cluster-pin.png", 0, 8, 9, 16);
// selectedSym = new createPictureSymbol("./images/blue-cluster-pin.png", 0, 9, 11, 20);
// Option 2: Use circle markers for symbols - Red
defaultSym = new SimpleMarkerSymbol("circle", 16,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([102,0,0, 0.55]), 3),
new Color([255, 255, 255, 1]));
selectedSym = new SimpleMarkerSymbol("circle", 16,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([102,0,0, 0.85]), 3),
new Color([255, 255, 255, 1]));
// Create a feature layer to get feature service
function addClusterLayer() {
var renderer,
renderer1,
small,
medium,
large,
xlarge;
// Add cluster renderer
clusterLayer = new ClusterFeatureLayer({
"url": "http://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/World_Cities/FeatureServer/0",
//"url": "http://services.arcgis.com/BG6nSlhZSAWtExvp/arcgis/rest/services/GlobalSeismographyNetwork/FeatureServer/0",
// "url": "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0",
"distance": 95,
"id": "clusters",
"labelColor": "#fff",
"resolution": map.extent.getWidth() / map.width,
//"singleColor": "#888",
"singleSymbol": defaultSym,
"singleTemplate": infoTemplate,
"useDefaultSymbol": false,
"zoomOnClick": false,
"showSingles": true,
"objectIdField": "FID",
outFields: ["*"]
});
renderer = new ClassBreaksRenderer(defaultSym, "clusterCount");
// Red Clusters
small = new SimpleMarkerSymbol("circle", 25,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([212,116,60,0.5]), 15),
new Color([212,116,60,0.75]));
medium = new SimpleMarkerSymbol("circle", 50,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([178,70,37,0.5]), 15),
new Color([178,70,37,0.75]));
large = new SimpleMarkerSymbol("circle", 80,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([144,24,13,0.5]), 15),
new Color([144,24,13,0.75]));
xlarge = new SimpleMarkerSymbol("circle", 110,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([102,0,0,0.5]), 15),
new Color([102,0,0,0.75]));
// Break values - can adjust easily
renderer.addBreak(2, 50, small);
renderer.addBreak(50, 250, medium);
renderer.addBreak(250, 1000, large);
renderer.addBreak(1000, 50000, xlarge);
// Providing a ClassBreakRenderer is also optional
clusterLayer.setRenderer(renderer);
//added second renderer
var renderer1 = new UniqueValueRenderer(defaultSymbol, "CNTRY_NAME");
//add symbol for each possible value
renderer1.addValue("United States", new PictureMarkerSymbol({
"angle": 0,
"xoffset": 0,
"yoffset": 8,
"type": "esriPMS",
"url": "./images/blue-pin.png",
"contentType": "image/png",
"width": 24,
"height": 24
}));
renderer1.addValue("Argentina", new PictureMarkerSymbol({
"angle": 0,
"xoffset": 0,
"yoffset": 12,
"type": "esriPMS",
"url": "./images/blue-cluster-pin.png",
"contentType": "image/png",
"width": 24,
"height": 24
}));
//added to cluster
clusterLayer.setRenderer(renderer1);
map.addLayer(clusterLayer);
}
// Create png marker
// function createPictureSymbol(url, xOffset, yOffset, xWidth, yHeight) {
// return new PictureMarkerSymbol(
// {
// "angle": 0,
// "xoffset": xOffset,
// "yoffset": yOffset,
// "type": "esriPMS",
// "url": url,
// "contentType": "image/png",
// "width": xWidth,
// "height": yHeight
// }
// );
// }
// Create new graphic and add to map.graphics
function addSelectedFeature() {
var selIndex = map.infoWindow.selectedIndex,
selFeature;
if (selIndex !== -1) {
selFeature = map.infoWindow.features[selIndex];
// Remove old feature first
removeSelectedFeature();
// Add new graphic
map.infoWindow._lastSelected = new Graphic(selFeature.toJson());
map.infoWindow._lastSelected.setSymbol(selectedSym);
map.graphics.add(map.infoWindow._lastSelected);
}
}
// Remove graphic from map.graphics
function removeSelectedFeature() {
if (map.infoWindow._lastSelected) {
map.graphics.remove(map.infoWindow._lastSelected);
map.infoWindow._lastSelected = null;
}
}
// Highlight clusters
function setActiveClusterOpacity(elem, fillOpacity, strokeOpacity) {
var textElm;
if (elem) {
elem.setAttribute("fill-opacity", fillOpacity);
elem.setAttribute("stroke-opacity", strokeOpacity);
// Overide inherited properties for the text in the circle
textElm = elem.nextElementSibling;
if (textElm && textElm.nodeName === "text") {
textElm.setAttribute("fill-opacity", 1);
}
}
}
// Hide popup if selected feature is clustered
function onClustersShown(clusters) {
var i = 0,
extent;
if (map.infoWindow.isShowing && map.infoWindow._lastSelected) {
for (i; i < clusters.length; i++) {
if (clusters[i].attributes.clusterCount > 1) {
extent = clusterLayer._getClusterExtent(clusters[i]);
if (extent.contains(map.infoWindow._lastSelected.geometry)) {
map.infoWindow.hide();
break;
}
}
}
}
}
// Wire cluster layer events
function addClusterLayerEvents() {
// Mouse over events
clusterLayer.on("mouse-over", onMouseOverCluster);
clusterLayer.on("mouse-out", onMouseOutCluster);
// Clusters drawn
clusterLayer.on("clusters-shown", onClustersShown);
}
// Save the last selected graphic so we can highlight it
map.infoWindow.on("selection-change", function () {
addSelectedFeature();
animateInfoWindow();
});
// Clear selected graphic when infoWindow is hidden
map.infoWindow.on("hide", function () {
// re-activate cluster
setActiveClusterOpacity(activeClusterElement, 0.75, 0.5);
removeSelectedFeature();
});
// Popup enhancements
function onMouseOverCluster(e) {
if (e.graphic.attributes.clusterCount === 1) {
e.graphic._graphicsLayer.onClick(e);
} else {
if (e.target.nodeName === "circle") {
activeClusterElement = e.target;
setActiveClusterOpacity(activeClusterElement, 1, 1);
} else {
setActiveClusterOpacity(activeClusterElement, 1, 1);
}
}
}
function onMouseOutCluster(e) {
if (e.graphic.attributes.clusterCount > 1) {
if (e.target.nodeName === "circle" || e.target.nodeName === "text") {
setActiveClusterOpacity(activeClusterElement, 0.75, 0.5);
setActiveClusterOpacity(e.target, 0.75, 0.5);
}
}
}
function animateInfoWindow() {
domStyle.set(map.infoWindow.domNode, "opacity", 0);
fx.fadeIn({node: map.infoWindow.domNode,
duration: 150,
easing: easing.quadIn}).play();
}
// Click to close
map.on('click', function () {
if (map.infoWindow.isShowing) {
map.infoWindow.hide();
}
});
// ESC is pressed
map.on('key-down', function (e) {
if (e.keyCode === 27) {
map.infoWindow.hide();
}
});
// Dynamically reposition popups when map moves
map.on('extent-change', function () {
if (map.infoWindow.isShowing) {
map.infoWindow.reposition();
}
});
// Auto recenter map - optional
autoRecenter(map);
function autoRecenter(map) {
on(map, 'load', function (map) {
on(window, 'resize', map, map.resize);
});
on(map, 'resize', function(extent, width, height) {
map.__resizeCenter = map.extent.getCenter();
setTimeout(function() {
map.centerAt(map.__resizeCenter);
}, 100);
});
}
});
</script>
</head>
<body>
<div id="mapDiv"></div>
<div id="search"></div>
<div id="homeButton"></div>
</body>
</html>

Update the data in amcharts in angular5

I am using xy chart in amcharts as per my requirement.
In that amcharts, whenever on click of a bubble, I need to highlight the bubbles. So I have added listeners to my options as follows:
"listeners": [
{
"event": "clickGraphItem",
"method": function(event) {
var bubbleId = event.item.dataContext.id;
var toolTip = "";
dataProvider.forEach((data) => {
if (bubbleId != "" && bubbleId == data.id) {
if (data.bubbleSize == 10) {
data.bubbleSize = 20;
data.shape = "diamond";
} else {
data.bubbleSize = 10;
data.shape = "round";
}
} else {
data.bubbleSize = 10;
data.shape = "round";
}
});
this.AmCharts.updateChart(this.chart, () => {
// Change whatever properties you want
this.chart.dataProvider = dataProvider;
});
}
}],
But I am getting an error as "Cannot read property 'Amcharts' of undefined". I am not able to resolve the issue. Can anyone help me on this?
Referal Code for Amcharts angular as follows:
https://github.com/amcharts/amcharts3-angular2

DataTables image (or at least image title) export to PDF

Using DataTables and Buttons (NOT TableTools, which is retired) extension. Some cells have progressbars and small icons. Is there a way to export these images (or at least their titles) to PDF? Found some possible hacks on this page, but all of them were for retired TableTools.
Checked https://datatables.net/reference/button/pdf and https://datatables.net/reference/api/buttons.exportData%28%29 but couldn't find any method to achieve this goal. Tested by adding this code:
stripHtml: false
but whole HTML code (like img src=...) was included in PDF file instead of images.
If exporting images isn't possible, is there a way to export at least alt or title attribute of each image? That would be enough.
I assume you are using pdfHtml5. dataTables is using pdfmake in order to export pdf files. When pdfmake is used from within a browser it needs images to be defined as base64 encoded dataurls.
Example : You have rendered a <img src="myglyph.png"> in the first column of some of the rows - those glyphs should be included in the PDF. First create an Image reference to the glyph :
var myGlyph = new Image();
myGlyph.src = 'myglyph.png';
In your customize function you must now
1) build a dictionary with all images that should be included in the PDF
2) replace text nodes with image nodes to reference images
buttons : [
{
extend : 'pdfHtml5',
customize: function(doc) {
//ensure doc.images exists
doc.images = doc.images || {};
//build dictionary
doc.images['myGlyph'] = getBase64Image(myGlyph);
//..add more images[xyz]=anotherDataUrl here
//when the content is <img src="myglyph.png">
//remove the text node and insert an image node
for (var i=1;i<doc.content[1].table.body.length;i++) {
if (doc.content[1].table.body[i][0].text == '<img src="myglyph.png">') {
delete doc.content[1].table.body[i][0].text;
doc.content[1].table.body[i][0].image = 'myGlyph';
}
}
},
exportOptions : {
stripHtml: false
}
}
Here is a an example of a getBase64Image function
function getBase64Image(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
return canvas.toDataURL("image/png");
}
If you just want to show the title of images in the PDF - or in any other way want to manipulate the text content of the PDF - then it is a little bit easier. The content of each column in each row can be formatted through the exportOptions.format.body callback :
buttons : [
{
extend : 'pdfHtml5',
exportOptions : {
stripHtml: false
format: {
body: function(data, col, row) {
var isImg = ~data.toLowerCase().indexOf('img') ? $(data).is('img') : false;
if (isImg) {
return $(data).attr('title');
}
return data;
}
}
}
]
The reason format.body cannot be used along with images is that is only let us pass data back to the text node part of the PDF document.
See also
http://pdfmake.org/#/gettingstarted (look for Images section)
https://github.com/bpampuch/pdfmake/blob/master/examples/images.js
Since no suggestions received, I had to make a hack in order to get PDF file formatted the way I want.
In case someone has the same issue, you can use hidden span to display image alt/title near image itself. I'm sure it's not the best practice, but it will do the trick. So the code will look like:
<img src='image.png' alt='some_title'/><span class='hidden'>some_title</span>
This way datatables will show only the image, while PDF file will contain text you need.
This is my CODE!
HTML
<div class="dt-btn"></div>
<table>
<thead><tr><th>No</th><th>IMAGE</th><th>NOTE</th></tr></thead>
<tbody>
<tr>
<td>{{$NO}}</td>
<td>{{$imgSRC}}</td>
<td>{{$NAME}}<br />
{{$GRADE}}<br />
{{$PROFILE}}<br />
{{$CODE}}<br />
</td>
</tr>
</tbody>
</table>
JAVASCRIPT
$.extend( true, $.fn.dataTable.defaults, {
buttons: [{
text: '<i class="bx bx-download font-medium-1"></i><span class="align-middle ml-25">Download PDF</span>',
className: 'btn btn-light-secondary mb-1 mx-1 dnPDF',
extend: 'pdfHtml5',
pageSize: 'A4',
styles: {
fullWidth: { fontSize: 11, bold: true, alignment: 'left', margin: [0,0,0,0] }
},
action: function ( e, dt, node, config ) {
var that = this;
setTimeout( function () {
$.fn.dataTable.ext.buttons.pdfHtml5.action.call(that, e, dt, node, config);
$( ".donePDF" ).remove();
$( ".dnPDF" ).prop("disabled", false);
}, 50);
},
customize: function(doc) {
doc.defaultStyle.fontSize = 11;
doc.defaultStyle.alignment = 'left';
doc.content[1].table.dontBreakRows = true;
if (doc) {
for (var i = 1; i < doc.content[1].table.body.length; i++) {
// 1st Column - display IMAGE
var imgtext = doc.content[1].table.body[i][0].text;
delete doc.content[1].table.body[i][0].text;
jQuery.ajax({
type: "GET",
dataType: "json",
url: "{{route('base64')}}",
data: { src: imgtext },
async: false,
success: function(resp) {
//console.log(resp.data);
doc.content[1].table.body[i][0] = {
margin: [0, 0, 0, 3],
alignment: 'center',
image: resp.data,
width: 80,
height: 136
};
}
});
// 2nd Column - display NOTE(4 line)
var bodyhtml = doc.content[1].table.body[i][1].text;
var bodytext = bodyhtml.split("\n");
var bodystyle = []
for (var j = 0; j < bodytext.length; j++) {
switch(j) {
case 0:
// NAME
var _text = { margin:[0, 0, 0, 3], color:"#000000", fillColor:'#ffffff', bold:true, fontSize:13, alignment:'center', text:bodytext[j] };
break;
case 1:
// GRADE
var _text = { margin:[0, 0, 0, 3], color:"blue", fillColor:'#ffffff', bold:false, fontSize:11, alignment:'left', text:bodytext[j] };
break;
case 3:
// CODE
var _text = { margin:[0, 0, 0, 3], color:"#000000", fillColor:'#ffffff', bold:true, fontSize:11, alignment:'left', text:bodytext[j] };
break;
default:
// OTHERS
var _text = { margin:[0, 0, 0, 3], color:"#000000", fillColor:'#ffffff', bold:false, fontSize:11, alignment:'left', text:bodytext[j] };
break;
}
bodystyle[j] = _text;
};
doc.content[1].table.body[i][1] = bodystyle;
}
}
},
exportOptions: {
columns: [ 1, 2 ],
stripNewlines: false,
stripHtml: true,
modifier: {
page: 'all' // 'all', 'current'
},
}
}],
columns: [
{ className: 'iNo', orderable: true, visible: true},
{ className: 'iIMG', orderable: false, visible: false },
{ className: 'iPDF', orderable: false, visible: false, responsivePriority: 10001 } ]
});
var table = $('#table').DataTable();
table.buttons().container().appendTo('.dt-btn');
$('.dnPDF').on('click', function(){
$(this).append('<span class="spinner-border spinner-border-sm donePDF" role="status" aria-hidden="true"></span>').closest('button').attr('disabled','disabled');
});
$.fn.dataTable.Buttons.defaults.dom.container.className = '';
$.fn.dataTable.Buttons.defaults.dom.button.className = 'btn';
PHP
public function base64(Request $request)
{
$request->validate([
'src' => 'required|string'
]);
$fTYPE = pathinfo($request->src, PATHINFO_EXTENSION);
$fDATA = #file_get_contents($request->src);
$imgDATA = base64_encode($fDATA);
$imgSRC = 'data:image/' . $fTYPE . ';base64,'.$imgDATA;
$error = ($imgDATA!='') ? 0 : 1;
$msg = ($error) ? 'Error' : 'Success';
return response()->json([ 'msg' => $msg, 'error'=> $error, 'data' => $imgSRC]);
}
[Sample][1]: https://i.stack.imgur.com/35Wlm.jpg
In addition to davidkonrad's answer. I created dynamically all base64 images and used them in Datatables pdfmake like this:
for (var i = 1; i < doc.content[2].table.body.length; i++) {
if (doc.content[2].table.body[i][1].text.indexOf('<img src=') !== -1) {
html = doc.content[2].table.body[i][1].text;
var regex = /<img.*?src=['"](.*?)['"]/;
var src = regex.exec(html)[1];
var tempImage = new Image();
tempImage.src = src;
doc.images[src] = getBase64Image(tempImage)
delete doc.content[2].table.body[i][1].text;
doc.content[2].table.body[i][1].image = src;
doc.content[2].table.body[i][1].fit = [50, 50];
}
//here i am removing the html links so that i can use stripHtml: true,
if (doc.content[2].table.body[i][2].text.indexOf('<a href="details.php?') !== -1) {
html = $.parseHTML(doc.content[2].table.body[i][2].text);
delete doc.content[2].table.body[i][1].text;
doc.content[2].table.body[i][2].text = html[0].innerHTML;
}
}

Dojo domConstruct.create won't create element on postCreate

I'm new to Dojo and I'm making my first widget to be used in EPiServer CMS.
The widget is supposed to dynamically add contacts when clicking on a button. This means that the only control my templateString has is a button. When clicking on it I call a function that creates all of the other controls that will contain the contact infomation, such as name, email etc. This is working fine, I use domConstruct.create without a problem.
Thing is, when I want to create the existing contacts based on the value that comes from the server I can't manage to use domConstruct.create, it just returns "undefined". I'm making the call in the postCreate event which happens after the DOM is ready. (I've tested this as well). Does anyone have a clue what could be wrong? Like I said creating the controls when clicking on the Add button works like a charm.
The function that throws the error is:
postCreate: function () {
// call base implementation
this.inherited(arguments);
//this._loadContacts(this.value);
var node = domConstruct.create("fieldset", { id: "test" }, "cccp"); //this simple create instruction returns undefined here.
//Bind button
this.connect(this.btnAdd, "onclick", dojo.partial(this._createContact, new Object()));
},
Exactly on the line that does the domConstruct.create. Below is the entire code for the widget:
define([
"dojo/_base/array",
"dojo/_base/connect",
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/dom-construct",
"dojo/on",
"dijit/_CssStateMixin",
"dijit/_Widget",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"epi/epi",
"epi/shell/widget/_ValueRequiredMixin"
],
function (
array,
connect,
declare,
lang,
domConstruct,
on,
_CssStateMixin,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
epi,
_ValueRequiredMixin
) {
var amountContacts = 0;
var contactContainerPrefixName = "contactInfo";
return declare("meridian.editors.StringList", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin, _ValueRequiredMixin], {
templateString: "<div class=\"dijitInline\">\
<form id=\"cccp\" action=\"\">\
</form>\
<button id=\"btnAdd\" data-dojo-attach-point=\"btnAdd\" type=\"button\" class=\"\">Add Contact</button><br/> \
<span>${helptext}</span>\
</div>",
baseClass: "epiStringList",
helptext: "Place items on separate lines",
intermediateChanges: false,
value: null,
multiple: true,
onChange: function (value) { },
_onChange: function (value) {
this.onChange(value);
},
postCreate: function () {
// call base implementation
this.inherited(arguments);
//this._loadContacts(this.value);
var node = domConstruct.create("fieldset", { id: "test" }, "cccp"); //this simple create instruction returns undefined here.
//Bind button
this.connect(this.btnAdd, "onclick", dojo.partial(this._createContact, new Object()));
},
isValid: function () {
// summary:
// Check if widget's value is valid.
// tags:
// protected, override
return !this.required || lang.isArray(this.value) && this.value.length > 0 && this.value.join() != "";
},
_loadContacts:function(data)
{
for (var i = 0; i < data.length; i++) {
this._createContact(data[i]);
}
},
_createContact:function(data)
{
//increase the number of contacts
amountContacts++;
var contactInfoContainer = contactContainerPrefixName+amountContacts;
//Create container for contact data
var contactInfo = domConstruct.create("fieldset", { id: contactInfoContainer }, "cccp");
//Create container for PROPERTIES
var contactInfoProperties = domConstruct.create("div", {class: "properties"}, contactInfoContainer);
//Name
this._createTextbox("name", data.fullName, "Name", contactInfoProperties, false, false);
/*//Email
this._createTextbox("email", "Email", contactInfoProperties, false);
//Phone 1
this._createTextbox("phoneOne", "Phone 1", contactInfoProperties, true);
//Phone 2
this._createTextbox("phoneTwo", "Phone 2", contactInfoProperties, true);
//Fax
this._createTextbox("fax", "Fax", contactInfoProperties, false);
//Organization
this._createTextbox("organization", "Organization", contactInfoProperties, false);
//Department
this._createTextbox("department", "Department/Unit", contactInfoProperties, false);
//Role
this._createTextbox("role", "Role/Job title", contactInfoProperties, false);*/
//Website
this._createTextbox("website", data.website, "Website", contactInfoProperties, false, false);
//Address
//this._createTextbox("address", "Address", contactInfoProperties, false);
//TLP
this._createTLP_DDL("tlp", "TLP", contactInfoProperties);
//Create container for CATEGORIES
var contactInfoCategories = domConstruct.create("div", { class: "categories" }, contactInfoContainer);
//Categories heading
var p=domConstruct.create("p", { class: "heading" }, contactInfoCategories);
p.innerHTML = "Categories";
//Alert, Warning & Incident Response
this._createCategory("alert", contactInfoCategories, "Alert, Warning & Incident Response");
//Threat
this._createCategory("threat", contactInfoCategories, "Threat");
//Vulnerability
this._createCategory("vulnerability", contactInfoCategories, "Vulnerability");
//Industry
this._createCategory("industry", contactInfoCategories, "Industry");
//Policy
this._createCategory("policy", contactInfoCategories, "Policy");
//R & D
this._createCategory("rd", contactInfoCategories, "R & D");
//Sharing
this._createCategory("sharing", contactInfoCategories, "Sharing");
//Crime
this._createCategory("crime", contactInfoCategories, "Crime");
//SCADA/Process Control
this._createCategory("scada", contactInfoCategories, "SCADA/Process Control");
//Assurance
this._createCategory("assurance", contactInfoCategories, "Assurance");
//Standards
this._createCategory("standards", contactInfoCategories, "Standards");
//Resilience
this._createCategory("resilience", contactInfoCategories, "Resilience");
//Exercises
this._createCategory("exercises", contactInfoCategories, "Exercises");
//Defence
this._createCategory("defence", contactInfoCategories, "Defence");
//Media handling
this._createCategory("media", contactInfoCategories, "Media handling");
//General PoC
this._createCategory("poc", contactInfoCategories, "General PoC");
//Add remove button
var btnRemove = domConstruct.create("input", { type: "button", id: "btnRemove" + amountContacts, value: "Remove"/*,onclick:"_deleteContact(this)"*/ }, contactInfo);
this.connect(btnRemove, "onclick", dojo.partial(this._deleteContact, btnRemove.id));
},
_createTextbox:function(id, textBoxValue, labelText, parent, checkbox, checkboxChecked)
{
var currentId = id+amountContacts;
var textboxContainer = domConstruct.create("div", {class:"form-item"}, parent);
this._createLabel(currentId, textboxContainer, labelText);
//Create textbox and attach update handler
var textbox = domConstruct.create("input", { type: "text", id: currentId, name: currentId }, textboxContainer);
if (textBoxValue)
textbox.value = textBoxValue;
this.connect(textbox, "onchange", this._updateValue);
if (checkbox) {
this._createCheckbox("inline", textboxContainer, currentId + "_24", "24/7?");
}
},
_createTLP_DDL:function(id, labelText, parent)
{
var currentId = id+amountContacts;
var ddlContainer = domConstruct.create("div", { class: "form-item" }, parent);
this._createLabel(currentId, ddlContainer, labelText);
var ddl = domConstruct.create("select", { name: currentId, id: currentId }, ddlContainer);
//Add options
var option1 = domConstruct.create("option", { val: "-1" }, ddl);
option1.innerHTML = "None";
var option1 = domConstruct.create("option", { val: "1" }, ddl);
option1.innerHTML = "Yes";
var option1 = domConstruct.create("option", { val: "0" }, ddl);
option1.innerHTML = "No";
},
_createCategory:function(id, parent, checkboxText)
{
var currentId = id + amountContacts;
var categoryContainer = domConstruct.create("div", { class: "form-item inline" }, parent);
this._createCheckbox("", categoryContainer, currentId, checkboxText);
},
_createLabel:function(forId, container, labelText)
{
var label = domConstruct.create("label", { for: forId }, container);
label.innerHTML = labelText;
},
_createCheckbox:function(labelClass, container, checkboxId, checkboxText){
var labelCheckbox = domConstruct.create("label", { class: labelClass }, container);
//Create checkbox and attach update handler
var checkbox = domConstruct.create("input", { type: "checkbox", id: checkboxId, name: checkboxId }, labelCheckbox);
this.connect(checkbox, "onchange", this._updateValue);
var span = domConstruct.create("span", {}, labelCheckbox);
span.innerHTML = checkboxText;
},
_deleteContact:function(targetBtnId)
{
var userInput = window.confirm("Are you sure you want to delete this contact?");
if (userInput) {
dojo.destroy(contactContainerPrefixName+targetBtnId[targetBtnId.length-1]);
}
},
_updateValue: function () {
//TODO: Delete test data
var contacts = [];
//The amount of contacts can have changed after deletions, so the global variables might not hold the real value
var actualAmountContacts = 0;
for (var i = 1; i <= amountContacts; i++) {
if (this._isValidContact(i)) {
var currentContact = new Object();
currentContact.fullName = this._getValueById("name" + i);
currentContact.website = this._getValueById("website" + i);
contacts[actualAmountContacts] = currentContact;
actualAmountContacts++;
}
}
this._set("value", contacts);
this.onChange(contacts);
},
_getValueById: function (id) {
var node = dojo.byId(id);
var textValue = (node != null) ? node.value : "";
return textValue;
},
_isValidContact:function(itemNumber){
//If there's no name or telephone then it's not a valid contact
return (this._getValueById("name" + itemNumber) != "" || this._getValueById("phoneOne" + itemNumber) != "" || this._getValueById("phoneTwo" + itemNumber) != "");
},
});
});
UPDATE:
After a lot of test I've realized it's not the domConstruct.create statement which causes the error, the problem is that the form element that has "id:cccp" and to which I want to attach the created elements is null at this point. It is defined in the templateString as you can see in the source code but it's still null in the postCreate method. So the question is now, what event gets triggered when the HTML in the templateString is loaded?
In the end I solved the problem, so I write the workaround here and I hope it helps somebody!
I changed the templateString and added the attribute data-dojo-attach-point=\"form\" to the form that was not being found. Then in the postCreate method I accessed the form using this (this.form ), which worked without a problem. The final implementation for the function turned out as follows:
postCreate: function () {
// call base implementation
this.inherited(arguments);
//this._loadContacts(this.value);
var node = domConstruct.create("fieldset", { id: "test" }, this.form); //this simple create instruction returns undefined here.
//Bind button
this.connect(this.btnAdd, "onclick", dojo.partial(this._createContact, new Object()));
},
I still don't understand why dojo.byId("cccp") would return null, but I'm happy I found a workaround that solved the problem. Cheers!

Fit content width-wise in pdf

When rendering to pdf, I need the html page to be 100% of the print width. Otherwise content gets cut off. Is there an easy way to do this?
I came up with a work-around, which gets the html width after rendering and then sets the zoom factor to force the correct width.
var page = require('webpage').create(),
system = require('system'),
dpi = 89.9, // strange, but that's what I get on Mac
pageWidth = 210; // in mm
var getWidth = function() {
return page.evaluate(function() {
// get width manually
// ...
return width;
});
};
page.paperSize = {format: 'A4', orientation: 'portrait'};
page.open(system.args[1], function (status) {
window.setTimeout(function () {
page.zoomFactor = (pageWidth / 25.4) * dpi / getWidth();
page.render(system.args[2]);
phantom.exit();
}, 200);
});
Looking for a straight forward solution, because getting the width requires me to do some tricks due to the framework I use.
Managed to solve this by adding the following CSS rule which will zoom out the page and make it fit:
html {
zoom: 0.60;
}
Edit rasterize.js as follows.
Make a backup of rasterize.js
Remove this block (approx. line 18-31)
if (system.args.length > 4) {
page.zoomFactor = system.args[4];
}
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit();
} else {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
});
Replace it with this code block
if (system.args.length > 4) {
page.zoomFactor = parseFloat(system.args[4].split("=")[1]);
}
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit(1);
} else {
window.setTimeout(function () {
console.log("zoomFactor: " + page.zoomFactor);
page.evaluate(function(zoom) {
return document.querySelector('body').style.zoom = zoom;
}, page.zoomFactor); // <-- your zoom here
page.render(output);
phantom.exit();
}, 200);
}
});
Then you can export html to pdf on-the-fly like this:
~/bin/phantomjs ~/bin/phantomjs/examples/rasterize.js \
'http://example.com/index.html' \
mysite.pdf paperformat=A4 zoom=0.4