KineticJs-how to update x and y position of the multiple images after resizing the stage layer - resize

As I am new to KineticJs so, I have tried implementing the functionality using Kinectic js for drawing the multiple image on different- different x and y. Now I wanted to resize the stage layer or canvas. I have done that by using the code given below
window.onresize = function (event) {
stage.setWidth(($('#tab' + tabId).innerWidth() / 100) * 80);
var _images = layer.getChildren();
for (var i = 0; i < _images.length; i++) {
if (typeof _images[i].getId() != 'undefined') {
//alert(stage.getScale().x);
_images[i].setX(_images[i].getX() * stage.getScale().x);
layer.draw();
}
}
}
but now the problem is the are being defined and now if browser resize than stage is resized but the images on the prev x and y are fixed . I would like to keep them fixed on the position on resizing of stage layer or canvas.Here are the link of the image before resize and after resizing.beforeresize and afterResize .
Here is my entire code given below:-
$("#tabs li").each(function () {
$(this).live("click", function () {
clearInterval(_timer);
var tabname = $(this).find("a").attr('name');
tabname = $.trim(tabname.replace("#tab", ""));
var tabId = $(this).find("a").attr('href');
tabId = $.trim(tabId.replace("#", ""));
$.ajax({
url: "/Home/GetTabsDetail",
dataType: 'json',
type: 'GET',
data: { tabId: tabId },
cache: false,
success: function (data) {
var bayStatus = [];
var i = 0;
var image_array = [];
var BayExist = false;
var BayCondition;
var imgSrc;
var CanvasBacgroundImage;
var _X;
var _bayNumber;
var _Y;
var _ZoneName;
$(data).each(function (i, val) {
i = i + 1;
if (!BayExist) {
bayStatus = val.BayStatus;
CanvasBacgroundImage = val.TabImageLocation;
BayExist = true;
}
$.each(val, function (k, v) {
if (k == "BayNumber") {
BayCondition = bayStatus[v];
_bayNumber = v;
if (BayCondition == "O")
imgSrc = "../../images/Parking/OccupiedCar.gif"
else if (BayCondition == "N")
imgSrc = "../../images/Parking/OpenCar.gif";
}
if (k == "BayX")
_X = v;
if (k == "BayY")
_Y = v;
if (k == "ZoneName")
_ZoneName = v;
});
image_array.push({ img: imgSrc, xAxis: _X, yAxis: _Y, toolTip: _bayNumber, ZoneName: _ZoneName });
});
var imageUrl = CanvasBacgroundImage;
if ($('#tab' + tabId).length) {
// $('#tab' + tabId).css('background-image', 'url("../../images/Parking/' + imageUrl + '")');
var stage = new Kinetic.Stage({
container: 'tab' + tabId,
width: ($('#tab' + tabId).innerWidth() / 100) * 80, // 80% width of the window.
height: 308
});
window.onresize = function (event) {
stage.setWidth(($('#tab' + tabId).innerWidth() / 100) * 80);
}
$('#tab' + tabId).find('.kineticjs-content').css({ 'background-image': 'url("../../images/Parking/' + imageUrl + '")', 'background-repeat': ' no-repeat', 'background-size': '100% 100%' });
var layer = new Kinetic.Layer();
var planetOverlay;
function writeMessage(message, _x, _y) {
text.setX(_x + 20);
text.setY(_y + 1);
text.setText(message);
layer.draw();
}
var text = new Kinetic.Text({
fontFamily: 'Arial',
fontSize: 14,
text: '',
fill: '#000',
width: 200,
height: 60,
align: 'center'
});
var opentooltip = new Opentip(
"div#tab" + tabId, //target element
"dummy", // will be replaced
"", // title
{
showOn: null // I'll manually manage the showOn effect
});
Opentip.styles.win = {
borderColor: "black",
shadow: false,
background: "#EAEAEA"
};
Opentip.defaultStyle = "win";
// _timer = setInterval(function () {
for (i = 0; i < image_array.length; i++) {
img = new Image();
img.src = image_array[i].img;
planetOverlay = new Kinetic.Image({
x: image_array[i].xAxis,
y: image_array[i].yAxis,
image: img,
height: 18,
width: 18,
id: image_array[i].toolTip,
name: image_array[i].ZoneName
});
planetOverlay.on('mouseover', function () {
opentooltip.setContent("<span style='color:#87898C;'><b>Bay:</b></span> <span style='color:#25A0D3;'>" + this.getId() + "</span><br> <span style='color:#87898C;'><b>Zone:</b></span><span style='color:#25A0D3;'>" + this.getName() + "</span>");
//writeMessage("Bay: " + this.getId() + " , Zone: " + this.getName(), this.getX(), this.getY());//other way of showing tooltip
opentooltip.show();
$("#opentip-1").offset({ left: this.getX(), top: this.getY() });
});
planetOverlay.on('mouseout', function () {
opentooltip.hide();
// writeMessage('');
});
planetOverlay.createImageHitRegion(function () {
layer.draw();
});
layer.add(planetOverlay);
layer.add(text);
stage.add(layer);
}
// clearInterval(_timer);
//$("#tab3 .kineticjs-content").find("canvas").css('background-image', 'url("' + imageUrl + '")');
// },
// 500)
}
}
,
error: function (result) {
alert('error');
}
});
});
});
I want to keep the icons on the position where they were before resizing. I have tried but could not get the right solution to get this done.
How can How can I update x,y position for the images . Any suggestions would be appreciated.
Thanks is advance.

In window.resize, you're changing the stage width by a scaling factor.
Save that scaling factor.
Then multiply the 'x' coordinate of your images by that scaling factor.
You can reset the 'x' position of your image like this:
yourImage.setX( yourImage.getX() * scalingFactor );
layer.draw();

In the above mentioned code for window.onresize. The code has been modified which as follow:-
window.onresize = function (event) {
_orignalWidth = stage.getWidth();
var _orignalHeight = stage.getHeight();
// alert(_orignalWidth);
// alert($('#tab' + tabId).outerHeight());
stage.setWidth(($('#tab' + tabId).innerWidth() / 100) * 80);
//stage.setHeight(($('#tab' + tabId).outerHeight() / 100) * 80);
_resizedWidth = stage.getWidth();
_resizedHeight = stage.getHeight();
// alert(_resizedWidth);
_scaleFactorX = _resizedWidth / _orignalWidth;
var _scaleFactorY = _resizedHeight / _orignalHeight;
//alert(_scaleFactor);
var _images = layer.getChildren();
for (var i = 0; i < _images.length; i++) {
if (typeof _images[i].getId() != 'undefined') {
//alert(stage.getScale().x);
_images[i].setX(_images[i].getX() * _scaleFactorX);
//_images[i].setY(_images[i].getY() * _scaleFactorY);
layer.draw();
}
}
}

Related

TypeError: Cannot read property 'width' of undefined - Nuxt JS and canvas

I am trying to run a sketch on the canvas element in Nuxt JS but I am having some issues (I am completely new to Vue JS).
While I have no errors in VS Code, there is an error in the browser console:
client.js?06a0:84 TypeError: Cannot read property 'width' of undefined
at Blob.get (Blob_Point.js?a5e5:126)
at Blob.render (Blob_Point.js?a5e5:29)
below is the code for my Vue file:
<template>
<div>
<h1 class="pa-5 text-center">Blob</h1>
<canvas id="myCanvas" width="600" height="400"></canvas>
<!--<v-btn #click="drawRect">Clear</v-btn> -->
</div>
</template>
<script>
import Blob from "../assets/content/Blob_Point"
//import { Point } from "../assets/content/Blob_Point"
//import Point from "../assets/content/Blob_Point"
let oldMousePoint = { x: 0, y: 0 }
let blob = new Blob()
//let point = new Point()
let hover = false
let canvas
export default {
data() {
return {
canvas: null,
x: 0,
y: 0,
isDrawing: false,
rectWidth: 200,
//hover: false,
//oldMousePoint: { x: 0, y: 0 },
}
},
mounted() {
let canvas = document.getElementById("myCanvas")
this.canvas = canvas.getContext("2d")
},
created() {
new Blob("#C09EFF")
blob.canvas = canvas
blob.init()
blob.render()
},
methods: {
/* showCoordinates(e) {
this.x = e.offsetX
this.y = e.offsetY
},
drawLine(x1, y1, x2, y2) {
let ctx = this.vueCanvas
ctx.beginPath()
ctx.strokeStyle = "black"
ctx.lineWidth = 1
ctx.moveTo(x1, y1)
ctx.lineTo(x2, y2)
ctx.stroke()
ctx.closePath()
},
draw(e) {
if (this.isDrawing) {
this.drawLine(this.x, this.y, e.offsetX, e.offsetY)
this.x = e.offsetX
this.y = e.offsetY
}
},
beginDrawing(e) {
this.x = e.offsetX
this.y = e.offsetY
this.isDrawing = true
},
stopDrawing(e) {
if (this.isDrawing) {
this.drawLine(this.x, this.y, e.offsetX, e.offsetY)
this.x = 0
this.y = 0
this.isDrawing = false
//windowWidth = 0,
}
}, */
resize() {
document.getElementById("myCanvas").width = window.innerWidth
document.getElementById("myCanvas").height = window.innerHeight
},
/*drawRect() {
this.vueCanvas.clearRect(0, 0, 600, 800)
//this.vueCanvas.beginPath()
//this.vueCanvas.rect(20, 20, this.rectWidth, 100)
//this.vueCanvas.stroke()
},*/
mouseMove(e) {
let pos = blob.center
let diff = { x: e.clientX - pos.x, y: e.clientY - pos.y }
let dist = Math.sqrt(diff.x * diff.x + diff.y * diff.y)
let angle = null
blob.mousePos = {
x: pos.x - e.clientX,
y: pos.y - e.clientY,
}
if (dist < blob.radius && hover === false) {
let vector = {
x: e.clientX - pos.x,
y: e.clientY - pos.y,
}
angle = Math.atan2(vector.y, vector.x)
hover = true
// blob.color = '#77FF00';
} else if (dist > blob.radius && hover === true) {
let vector = {
x: e.clientX - pos.x,
y: e.clientY - pos.y,
}
angle = Math.atan2(vector.y, vector.x)
hover = false
blob.color = null
}
if (typeof angle == "number") {
let nearestPoint = null
let distanceFromPoint = 100
blob.points.forEach((point) => {
if (Math.abs(angle - point.azimuth) < distanceFromPoint) {
// console.log(point.azimuth, angle, distanceFromPoint);
nearestPoint = point
distanceFromPoint = Math.abs(angle - point.azimuth)
}
})
if (nearestPoint) {
let strength = {
x: oldMousePoint.x - e.clientX,
y: oldMousePoint.y - e.clientY,
}
strength =
Math.sqrt(strength.x * strength.x + strength.y * strength.y) * 1
if (strength > 100) strength = 100
nearestPoint.acceleration = (strength / 100) * (hover ? -1 : 1)
}
}
oldMousePoint.x = e.clientX
oldMousePoint.y = e.clientY
},
},
}
</script>
<style scoped>
#myCanvas {
border: 1px solid grey;
}
</style>
and below is the Blob_Point JS file that I am importing:
/* eslint-disable */
// Blob Class
export default class Blob {
// setup function
constructor(color) {
//the objects setup
// 'this' is a reference to the current class
this.points = []
this._color = color
}
init() {
for (let i = 0; i < this.numPoints; i++) {
let point = new Point(this.divisional * (i + 1), this)
//point.acceleration = -1 + Math.random() * 2;
this.push(point)
}
}
render() {
let canvas = this.canvas
let ctx = this.ctx
let position = this.position
let pointsArray = this.points
let radius = this.radius
let points = this.numPoints
let divisional = this.divisional
let center = this.center
ctx.clearRect(0, 0, canvas.width, canvas.height)
pointsArray[0].solveWith(pointsArray[points - 1], pointsArray[1])
let p0 = pointsArray[points - 1].position
let p1 = pointsArray[0].position
let _p2 = p1
// this is the draw
ctx.beginPath()
ctx.moveTo(center.x, center.y)
ctx.moveTo((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
for (let i = 1; i < points; i++) {
pointsArray[i].solveWith(
pointsArray[i - 1],
pointsArray[i + 1] || pointsArray[0]
)
let p2 = pointsArray[i].position
var xc = (p1.x + p2.x) / 2
var yc = (p1.y + p2.y) / 2
ctx.quadraticCurveTo(p1.x, p1.y, xc, yc)
// ctx.lineTo(p2.x, p2.y);
//ctx.fillStyle = this.color;
// ctx.fillRect(p1.x-2.5, p1.y-2.5, 5, 5);
p1 = p2
}
var xc = (p1.x + _p2.x) / 2
var yc = (p1.y + _p2.y) / 2
ctx.quadraticCurveTo(p1.x, p1.y, xc, yc)
ctx.lineTo(_p2.x, _p2.y)
ctx.closePath()
ctx.fillStyle = this.color
ctx.fill()
ctx.strokeStyle = this.color
// ctx.stroke();
/*
ctx.fillStyle = '#000000';
if(this.mousePos) {
let angle = Math.atan2(this.mousePos.y, this.mousePos.x) + Math.PI;
}*/
//requestAnimationFrame(this.render.bind(this))
}
push(item) {
if (item instanceof Point) {
this.points.push(item)
}
}
set color(value) {
this._color = value
}
get color() {
return this._color
}
set canvas(value) {
if (
value instanceof HTMLElement &&
value.tagName.toLowerCase() === "canvas"
) {
this._canvas = canvas
this.ctx = this._canvas.getContext("2d")
}
}
get canvas() {
return this._canvas
}
set numPoints(value) {
if (value > 10) {
this._points = value
}
}
get numPoints() {
return this._points || 32
}
set radius(value) {
if (value > 0) {
this._radius = value
}
}
get radius() {
return this._radius || 300
}
set position(value) {
if (typeof value == "object" && value.x && value.y) {
this._position = value
}
}
get position() {
return this._position || { x: 0.5, y: 0.5 }
}
get divisional() {
return (Math.PI * 2) / this.numPoints
}
get center() {
return {
x: this.canvas.width * this.position.x,
y: this.canvas.height * this.position.y,
}
}
set running(value) {
this._running = value === true
}
get running() {
return this.running !== false
}
}
// Point Class
export class Point {
constructor(azimuth, parent) {
this.parent = parent
this.azimuth = Math.PI - azimuth
this._components = {
x: Math.cos(this.azimuth),
y: Math.sin(this.azimuth),
}
this.acceleration = -0.3 + Math.random() * 0.6
}
solveWith(leftPoint, rightPoint) {
this.acceleration =
(-0.3 * this.radialEffect +
(leftPoint.radialEffect - this.radialEffect) +
(rightPoint.radialEffect - this.radialEffect)) *
this.elasticity -
this.speed * this.friction
}
set acceleration(value) {
if (typeof value == "number") {
this._acceleration = value
this.speed += this._acceleration * 2
}
}
get acceleration() {
return this._acceleration || 0
}
set speed(value) {
if (typeof value == "number") {
this._speed = value
this.radialEffect += this._speed * 3
}
}
get speed() {
return this._speed || 0
}
set radialEffect(value) {
if (typeof value == "number") {
this._radialEffect = value
}
}
get radialEffect() {
return this._radialEffect || 0
}
get position() {
return {
x:
this.parent.center.x +
this.components.x * (this.parent.radius + this.radialEffect),
y:
this.parent.center.y +
this.components.y * (this.parent.radius + this.radialEffect),
}
}
get components() {
return this._components
}
set elasticity(value) {
if (typeof value === "number") {
this._elasticity = value
}
}
get elasticity() {
return this._elasticity || 0.001
}
set friction(value) {
if (typeof value === "number") {
this._friction = value
}
}
get friction() {
return this._friction || 0.0085
}
}
Any ideas on the why lines 29 and 127 of the Blob_Point.js file are throwing errors?
I attached 2 screens of the developer tools in chrome to show the errors along with the JS its pointing to.
Thanks for any help!
The main issue I can identify is here. I have added comments to the code.
Blob_Point.js
render() {
let canvas = this.canvas // this references the Blob Class (not the Vue instance) and there is no initialised property such as canvas in the class
}
To fix this main issue, you can do something like
Blob_Point.js
export default class Blob {
constructor(color, canvas) {
//the objects setup
// 'this' is a reference to the current class
this.points = []
this._color = color;
this.canvas = canvas
}
}
And then in the .vue file
.Vue
mounted() {
let canvas = document.getElementById("myCanvas");
this.canvas = canvas.getContext("2d");
blob = new Blob("#C09EFF", this.canvas); // now canvas in the Blob Class constructor will refer to the vue instance canvas
blob.canvas = canvas;
blob.init();
blob.render();
},
I have identified another issue here.
set canvas(value) {
if (
value instanceof HTMLElement &&
value.tagName.toLowerCase() === "canvas"
) {
this._canvas = canvas // there is no initialised constructor property as _canvas
this.ctx = this._canvas.getContext("2d") // there is no initialised constructor property such as _canvas
}
}
get canvas() {
return this._canvas // there is no initialised constructor property as _canvas
}

zoomable bar chart using d3.js

Trying to apply the following: https://bl.ocks.org/mbostock/4015254, on to my dataset. I have changed the variables to fit my dataset.
My code is the following:
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 60},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseDate = d3.timeParse("%Y-%m-%d"),
formatDate = d3.timeFormat("%Y");
var x = d3.scaleTime()
.domain([new Date(2002, 0, 1), new Date(2003, 0, 1)])
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var area = d3.area()
.curve(d3.curveStepAfter)
.y0(y(0))
.y1(function(d) { return y(d.value); });
var areaPath = g.append("path")
.attr("clip-path", "url(#clip)")
.attr("fill", "steelblue");
var yGroup = g.append("g");
var xGroup = g.append("g")
.attr("transform", "translate(0," + height + ")");
var zoom = d3.zoom()
.scaleExtent([1 / 4, 8])
.translateExtent([[-width, -Infinity], [2 * width, Infinity]])
.on("zoom", zoomed);
var zoomRect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "none")
.attr("pointer-events", "all")
.call(zoom);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
d3.json("api.php", function(d) {
d.date = parseDate(d.date);
d.value = +d.close;
return d;
}, function(error, data) {
if (error) throw error;
var xExtent = d3.extent(data, function(d) { return d.date; });
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
});
function zoomed() {
var xz = d3.event.transform.rescaleX(x);
xGroup.call(xAxis.scale(xz));
areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
}
</script>
Yet I am getting an error on my console with the following error message:
d3.v4.min.js:2 Uncaught TypeError: Cannot read property 'length' of undefined
at SVGPathElement.t (d3.v4.min.js:2)
at SVGPathElement.<anonymous> (d3.v4.min.js:2)
at ut.each (d3.v4.min.js:2)
at ut.attr (d3.v4.min.js:2)
at SVGRectElement.zoomed (research.php:123)
at k.apply (d3.v4.min.js:2)
at it (d3.v4.min.js:2)
at a.emit (d3.v4.min.js:2)
at a.zoom (d3.v4.min.js:2)
at d3.v4.min.js:2
An excerpt of my dataset looks like this:
[{"id":"1","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2006-12-29","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"23700.000000000000000000","active":"1","exchange_id":"0"},{"id":"2","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-04","time":"15:00:00.000000","close":"2416.452637000000000000","volume":"16500.000000000000000000","active":"1","exchange_id":"0"},{"id":"3","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-05","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"45400.000000000000000000","active":"1","exchange_id":"0"},{"id":"4","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-09","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"28800.000000000000000000","active":"1","exchange_id":"0"},{"id":"5","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-10","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"27800.000000000000000000","active":"1","exchange_id":"0"},{"id":"6","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-11","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"25500.000000000000000000","active":"1","exchange_id":"0"},{"id":"7","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-12","time":"15:00:00.000000","close":"2378.546875000000000000","volume":"28100.000000000000000000","active":"1","exchange_id":"0"},{"id":"8","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-15","time":"15:00:00.000000","close":"2397.500244000000000000","volume":"23400.000000000000000000","active":"1","exchange_id":"0"}]
It consists of 2831 records. I don't understand why even my x and y axes aren't printing correctly. Thanks in advance.
The bl.ocks that you refer to uses a d3.csv which (if you look at the docs) has an accessor function that is passed each row of the data and then the whole date is accessed in the callback. Here's the accessor function:
function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
return d;
}
But in case of d3.json, there's no such accessor: d3.json(url[, callback]) which means you'll have to parse each row within the callback. Here's how:
d3.json("test.json", function(data) {
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
});
var xExtent = d3.extent(data, function(d) { return d.date; });
.....
Here's a fork of your code (I'm not sure why you have the file named as "api.php" when it is a JSON file. I've used a "test.json" file.
https://bl.ocks.org/shashank2104/21358032ac5507f7a6b7d620b1a4ef69/12b6089547e3b07edeca6214834d7e7a340be6a7
If you're unable to view that or if you're looking for a code snippet, here's one:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 60},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseDate = d3.timeParse("%Y-%m-%d"),
formatDate = d3.timeFormat("%Y");
var x = d3.scaleTime()
.domain([new Date(2006, 12, 1), new Date(2007, 1, 1)])
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var area = d3.area()
.curve(d3.curveStepAfter)
.y0(y(0))
.y1(function(d) { return y(d.value); });
var areaPath = g.append("path")
.attr("clip-path", "url(#clip)")
.attr("fill", "steelblue");
var yGroup = g.append("g");
var xGroup = g.append("g")
.attr("transform", "translate(0," + height + ")");
var zoom = d3.zoom()
.scaleExtent([1 / 4, 8])
.translateExtent([[-width, -Infinity], [2 * width, Infinity]])
.on("zoom", zoomed);
var zoomRect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "none")
.attr("pointer-events", "all")
.call(zoom);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var data = [{"id":"1","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2006-12-29","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"23700.000000000000000000","active":"1","exchange_id":"0"},{"id":"2","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-04","time":"15:00:00.000000","close":"2416.452637000000000000","volume":"16500.000000000000000000","active":"1","exchange_id":"0"},{"id":"3","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-05","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"45400.000000000000000000","active":"1","exchange_id":"0"},{"id":"4","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-09","time":"15:00:00.000000","close":"2388.023438000000000000","volume":"28800.000000000000000000","active":"1","exchange_id":"0"},{"id":"5","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-10","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"27800.000000000000000000","active":"1","exchange_id":"0"},{"id":"6","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-11","time":"15:00:00.000000","close":"2369.071045000000000000","volume":"25500.000000000000000000","active":"1","exchange_id":"0"},{"id":"7","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-12","time":"15:00:00.000000","close":"2378.546875000000000000","volume":"28100.000000000000000000","active":"1","exchange_id":"0"},{"id":"8","exchange_symbol":"TSE","currency":"JPY","stock_id":"1","stock_name":"KYOKUYO CO.,LTD.","stock_symbol":"1301.T","date":"2007-01-15","time":"15:00:00.000000","close":"2397.500244000000000000","volume":"23400.000000000000000000","active":"1","exchange_id":"0"}];
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
});
var xExtent = d3.extent(data, function(d) { return d.date; });
x.domain(xExtent);
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
function zoomed() {
var xz = d3.event.transform.rescaleX(x);
xGroup.call(xAxis.scale(xz));
areaPath.attr("d", area.x(function(d) { return xz(d.date); }));
}
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
Hope this makes sense. Also, I'm setting the x domain to the xExtent that is computed from the data. Play around with it to see the difference.
Somehow the load function is of a different type and uses the following format.
d3.json("api.php", function(data) {
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
});
var xExtent = d3.extent(data, function(d) { return d.date; });
zoom.translateExtent([[x(xExtent[0]), -Infinity], [x(xExtent[1]), Infinity]])
y.domain([0, d3.max(data, function(d) { return d.value; })]);
yGroup.call(yAxis).select(".domain").remove();
areaPath.datum(data);
zoomRect.call(zoom.transform, d3.zoomIdentity);
});

PDF export from icCube reporting with custom widget

PDF export for custom widget doesn't work from icCube reporting. Server engine version is 5.0.3 and reporting 5.0.3 (6:2163).
We didn't see any error message within the logs, the process remains stuck into the Monitoring -> Active Requests tab.
The report included is based on icCube demo Sales schema.
Widget code:
FishboneChart.js:
test = {
module : {}
}
test.module.FishboneChart = (function (_super) {
__extends(FishboneChart, _super);
function FishboneChart(container, options) {
_super.call(this, container, options);
this.options = options;
this.container = container;
var chartContainer = $("<div class='ic3w-fishbone-chart'></div>");
var contentContainer = $("<div class='ic3w-fishbone-chart-content'></div>");
var ct = $(this.container);
ct.append(chartContainer);
chartContainer.append(contentContainer);
};
$.extend(FishboneChart.prototype, {
buildInHtml : function (gviTable) {
var chartData = buildChartDataJson(gviTable);
renderFishboneChart(chartData);
}
});
return FishboneChart;
})(viz.GviChartBase);
test.module.FishboneChartAdapterFactory = (function (_super) {
__extends(FishboneChartAdapterFactory, _super);
function FishboneChartAdapterFactory() {
_super.call(this, "test.module.FishboneChartAdapter");
}
FishboneChartAdapterFactory.prototype.getId = function () {
return 'testFishboneChart';
};
FishboneChartAdapterFactory.prototype.getMenuGroupName = function () {
return "testMenu"
};
FishboneChartAdapterFactory.prototype.getTag = function () {
return "testFishboneChart"
};
return FishboneChartAdapterFactory;
})(ic3.WidgetAdapterFactory);
test.module.FishboneChartAdapter = (function (_super) {
__extends(FishboneChartAdapter, _super);
function FishboneChartAdapter(context) {
_super.call(this, context);
this.classID = "test.module.FishboneChartAdapter";
}
FishboneChartAdapter.prototype.reportTypeName = function () {
return 'Test fishbone-chart';
};
FishboneChartAdapter.prototype.getWidgetGroupName = function() {
return 'chart';
};
FishboneChartAdapter.prototype.getNameForCssClass = function() {
return 'test-chart';
};
FishboneChartAdapter.prototype.gutsMeta = function () {
return [
{
name: 'title',
type: 'text'
}
]
};
FishboneChartAdapter.prototype.createWidget = function (options, container) {
return new test.module.FishboneChart(container, options);
};
return FishboneChartAdapter;
})(ic3.ChartAdapter);
var initializeFishbone = function(d3){
"use strict";
d3.fishbone = function(){
var _margin = 50,
_marginRoot = 20,
_marginTail = 100,
_marginTop = 30,
_nodes,
_links,
_node,
_link,
_root,
_arrowId = function(d){ return "arrow"; },
_children = function(d){ return d.children; },
_label = function(d){ return d.name; },
_perNodeTick = function(d){},
_linkScale = d3.scale.log()
.domain([1, 5])
.range([20, 10]),
_chartSize = getChartSize(),
_force = d3.layout.force()
.gravity(0)
.size([
_chartSize.width,
_chartSize.height
])
.linkDistance(_linkDistance)
.chargeDistance([10])
.on("tick", _tick);
var fb1 = function($){
_links = [];
_nodes = [];
_build_nodes($.datum());
_force
.nodes(_nodes)
.links(_links);
_link = $.selectAll(".link")
.data(_links);
_link.enter().append("line");
_link
.attr({
"class": function(d){ return "link link-" + d.depth; },
"marker-end": function(d){
return d.arrow ? "url(#" + _arrowId(d) + ")" : null;
}
});
_link.exit().remove();
_node = $.selectAll(".node").data(_nodes);
_node.enter().append("g")
.attr({
"class": function(d){ return "node" + (d.root ? " root" : ""); }
})
.append("text");
_node.select("text")
.attr({
"class": function(d){ return "label-" + d.depth; },
"text-anchor": function(d){
return !d.depth ? "start" : d.horizontal ? "end" : "middle";
},
dy: function(d){
return d.horizontal ? ".35em" : d.region === 1 ? "1em" : "-.2em";
}
})
.text(_label);
_node.exit().remove();
_node
.call(_force.drag)
.on("mousedown", function(){ d3.event.stopPropagation(); });
_root = $.select(".root").node();
};
function _arrow($){
var defs = $.selectAll("defs").data([1]);
defs.enter().append("defs");
defs.selectAll("marker#" + _arrowId())
.data([1])
.enter().append("marker")
.attr({
id: _arrowId(),
viewBox: "0 -5 10 10",
refX: 10,
refY: 0,
markerWidth: 10,
markerHeight: 10,
orient: "auto"
})
.append("path")
.attr({d: "M0,-5L10,0L0,5"});
}
function _build_nodes(node){
_nodes.push(node);
var cx = 0;
var between = [node, node.connector],
nodeLinks = [{
source: node,
target: node.connector,
arrow: true,
depth: node.depth || 0
}],
prev,
childLinkCount;
if(!node.parent){
_nodes.push(prev = {tail: true});
between = [prev, node];
nodeLinks[0].source = prev;
nodeLinks[0].target = node;
node.horizontal = true;
node.vertical = false;
node.depth = 0;
node.root = true;
node.totalLinks = []
}else{
node.connector.maxChildIdx = 0;
node.connector.totalLinks = [];
}
node.linkCount = 1;
(_children(node) || []).forEach(function(child, idx){
child.parent = node;
child.depth = (node.depth || 0) + 1;
child.childIdx = idx;
child.region = node.region ? node.region : (idx & 1 ? 1 : -1);
child.horizontal = !node.horizontal;
child.vertical = !node.vertical;
if(node.root && prev && !prev.tail){
_nodes.push(child.connector = {
between: between,
childIdx: prev.childIdx
})
prev = null;
}else{
_nodes.push(prev = child.connector = {between: between, childIdx: cx++});
}
nodeLinks.push({
source: child,
target: child.connector,
depth: child.depth
});
childLinkCount = _build_nodes(child);
node.linkCount += childLinkCount;
between[1].totalLinks.push(childLinkCount);
});
between[1].maxChildIdx = cx;
Array.prototype.unshift.apply(_links, nodeLinks);
return node.linkCount;
}
function _linePosition($){
$.attr({
x1: function(d){ return d.source.x; },
y1: function(d){ return d.source.y; },
x2: function(d){ return d.target.x; },
y2: function(d){ return d.target.y; }
})
}
function _nodePosition($){
$.attr("transform", function(d){
return "translate(" + d.x + "," + d.y + ")";
})
}
function _linkDistance(d){
return (d.target.maxChildIdx + 1) * _linkScale(d.depth === 0 ? 1 : d.depth);
}
function _tick(e){
var k = 6 * e.alpha,
size = _force.size(),
width = size[0],
height = size[1],
a,
b;
_nodes.forEach(function(d){
if(d.root){ d.x = width - (_marginRoot + _root.getBBox().width); }
if(d.tail){ d.x = _marginTail; d.y = height / 2; }
if(d.depth === 1){
d.y = d.region === -1 ? _marginTop : (height - _marginTop);
d.x -= 10 * k;
}
if(d.vertical){ d.y += k * d.region; }
if(d.depth){ d.x -= k; }
if(d.between){
a = d.between[0];
b = d.between[1];
d.x = b.x - (1 + d.childIdx) * (b.x - a.x) / (b.maxChildIdx + 1);
d.y = b.y - (1 + d.childIdx) * (b.y - a.y) / (b.maxChildIdx + 1);
}
_perNodeTick(d);
});
_node.call(_nodePosition);
_link.call(_linePosition);
}
fb1.links = function(){ return _links; };
fb1.nodes = function(){ return _nodes; };
fb1.force = function(){ return _force; };
fb1.defaultArrow = _arrow;
fb1.margin = function(_){
if(!arguments.length){ return _margin; }
_margin = _;
return my;
};
fb1.children = function(_){
if(!arguments.length){ return _children; }
_children = _;
return my;
};
fb1.label = function(_){
if(!arguments.length){ return _label; }
_label = _;
return my;
};
fb1.perNodeTick = function(_){
if(!arguments.length){ return _perNodeTick; }
_perNodeTick = _;
return my;
};
return fb1;
};
};
var getChartSize = function() {
var chart = $('.ic3w-fishbone-chart');
return {
width : chart.width() || 0,
height : chart.height() || 0
};
};
var renderFishboneChart = function(chartData) {
if (!d3.fishbone) {
initializeFishbone(d3);
}
d3.selectAll('.ic3w-fishbone-chart-content svg').remove();
var fishbone = d3.fishbone();
d3.select('.ic3w-fishbone-chart-content')
.append("svg")
.attr(getChartSize())
.datum(chartData)
.call(fishbone.defaultArrow)
.call(fishbone);
fishbone.force().start();
};
var buildChartDataJson = function(gviTable) {
var rowCount = gviTable.getRowCount();
var colCount = gviTable.getColumnCount();
var currentLevel, levelDepthStr;
var currentBranch = [];
for (var i = 0; i < colCount; i++) {
levelDepthStr = gviTable.getPropertyForColumnHeader(i, 0, "a_ld");
currentLevel = parseInt(levelDepthStr);
if (!isNaN(currentLevel)) {
var currentEl = {
name: gviTable.getColumnLabel(i),
children: []
};
if (currentLevel <= currentBranch.length - 1) {
removeElements(currentBranch, currentLevel);
}
currentBranch[currentLevel] = currentEl;
if (currentLevel != 0) {
var parent = currentBranch[currentLevel - 1];
parent.children.push(currentEl);
}
}
}
return currentBranch.length > 0 ? currentBranch[0] : {};
};
var removeElements = function(array, targetLength) {
if (array) {
while (array.length > targetLength) {
array.pop();
}
}
};
FishBonePlugin.js:
ic3globals.plugins.push(
{
name: "Fishbone Chart",
loadCSS: function (options) {
var root = options.rootLocal + "plugins/";
ic3css(root + 'Fishbone.css');
},
loadJS: function (options) {
var root = options.rootLocal + "plugins/";
var deps = ['FishboneChart'];
$script(root + 'FishboneChart.js', deps[0]);
$script.ready(deps, function () { /* asynchronous callback */
options.callback && options.callback();
});
},
registerWidgetFactories: function (manager) {
manager.addWidgetFactory(new test.module.FishboneChartAdapterFactory());
},
registerBabylonTags: function (babylon) {
}
});
Fishbone.css:
.ic3w-fishbone-chart {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.ic3w-fishbone-chart-content *{
font-family: "Gill Sans", "Gill Sans MT";
}
.ic3w-fishbone-chart-content .label-0 {
font-size: 2em;
}
.ic3w-fishbone-chart-content .label-1 {
font-size: 1.5em;
fill: #111;
}
.ic3w-fishbone-chart-content .label-2 {
font-size: 1em;
fill: #444;
}
.ic3w-fishbone-chart-content .label-3 {
font-size: .9em;
fill: #888;
}
.ic3w-fishbone-chart-content .label-4 {
font-size: .8em;
fill: #aaa;
}
.ic3w-fishbone-chart-content .link-0 {
stroke: #000;
stroke-width: 2px;
}
.ic3w-fishbone-chart-content .link-1 {
stroke: #333;
stroke-width: 1px;
}
.ic3w-fishbone-chart-content .link-2, .ic3w-fishbone-chart-content .link-3, .ic3w-fishbone-chart-content .link-4 {
stroke: #666;
stroke-width: .5px;
}
Report code:
{"classID":"ic3.ReportGuts","guts_":{"ic3Version":13,"schemaName":"Sales","cubeName":"Sales","layout":{"classID":"ic3.FixedLayout","guts_":{"ic3Version":13,"grid":10,"boxes":[{"classID":"ic3.FixedLayoutBox","guts_":{"ic3Version":13,"header":"widget-0 ( widgetGroup.testMenu/testFishboneChart )","behaviour":"Fixed Box","noPrint":false,"position":{"top":0,"left":170,"width":1380,"height":820},"widgetAdapterUid":"w1","zIndex":2001,"ic3_uid":"ic3-164"}}]}},"widgetMgr":{"classID":"ic3.WidgetAdapterContainerMgr","guts_":{"ic3Version":13,"items":[{"classID":"test.module.FishboneChartAdapter","guts_":{"ic3Version":13,"navigationGuts":{"classID":"ic3.NavigationStrategy","guts_":{"ic3Version":13,"menuVisibility":{"back":true,"axisXChange":"All","axisYChange":"All","filter":"All","reset":true,"widget":true,"others":"All"},"maxAxisMemberCount":25}},"ic3_name":"widget-0","ic3_uid":"w1","ic3_eventMapper":{"classID":"ic3.EventWidgetMapper","guts_":{"__ic3_widgetEventsDescription":{}}},"ic3_mdxBuilderUid":"m1","__ic3_widgetTypeName":"widgetGroup.testMenu/testFishboneChart"}}]}},"constantMgr":{"classID":"ic3.ConstantsMgr","guts_":{"ic3Version":13}},"cssMgr":{"classID":"ic3.CssMgr","guts_":{}},"javascriptMgr":{"classID":"ic3.ReportJavascriptMgr","guts_":{"ic3Version":13,"js":"/** \n * A function called each time an event is generated. \n * \n * #param context the same object is passed between consumeEvent calls. \n * Can be used to store information. \n * { \n * $report : jQuery context of the report container \n * fireEvent : a function( name, value ) triggering an event \n * } \n * \n * #param event the event information \n * \n { \n * name : as specified in the 'Events' tab \n * value : (optional) actual event value \n * type : (optional) e.g., ic3selection \n * } \n * \n * Check the 'Report Event Names' menu for the list of available events. \n */ \n \nfunction consumeEvent( context, event ) { \n if (event.name == 'ic3-report-init') { \n \n ic3globals.plugins.push(\n {\n name: \"Fishbone Chart\",\n\n loadCSS: function (options) {\n },\n\n loadJS: function (options) {\n },\n\n registerWidgetFactories: function (manager) {\n console.log('korte1');\n manager.addWidgetFactory(new test.module.FishboneChartAdapterFactory());\n },\n\n registerBabylonTags: function (babylon) {\n }\n });\n } \n}"}},"calcMeasureMgr":{"classID":"ic3.CalcMeasureMgr","guts_":{"measures":[]}},"mdxQueriesMgr":{"classID":"ic3.MdxQueriesContainerMgr","guts_":{"mdxQueries":{"classID":"ic3.BaseContainerMgr","guts_":{"ic3Version":13,"items":[{"classID":"ic3.QueryBuilderWidget","guts_":{"mdxWizard":{"classID":"ic3.QueryBuilderWizardForm","guts_":{"rows":[],"cols":[{"classID":"ic3.QueryBuilderHierarchyForm","guts_":{"hierarchy":{"name":"Geography","uniqueName":"[Customers].[Geography]"},"type":"membersOf","membersOf":"all members"}}],"filters":[],"nonEmptyOnRows":false,"nonEmptyOnColumns":false}},"mdxFlat":{"classID":"ic3.QueryBuilderFlatMdxForm","guts_":{"useMdxStatement":false}},"ic3_name":"mdx Query-0","ic3_uid":"m1"}}]}},"mdxFilter":{"classID":"ic3.BaseContainerMgr","guts_":{"ic3Version":13,"items":[]}},"actionBuilders":{"classID":"ic3.BaseContainerMgr","guts_":{"ic3Version":13,"items":[]}}}}}}
Thanks,
Balint Ruzsa
Widget adapter code needs two extra functions(workaround for internal bug):
FishboneChartAdapter.prototype.hasNavigation = function()
{
return false;
};
FishboneChartAdapter.prototype.getNavigationMenuItems = function()
{
return [];
};
isRendered method should be modified, because layout for chart computed dynamicaly. I suggest to use simple setTimeout to with heuristic timeout, but you may implement custom check.
FishboneChart:
test.module.FishboneChart = (function (_super) {
__extends(FishboneChart, _super);
function FishboneChart(container, options) {
_super.call(this, container, options);
this.options = options;
this.container = container;
this.rendered = false;
var chartContainer = $("<div class='ic3w-fishbone-chart'></div>");
var contentContainer = $("<div class='ic3w-fishbone-chart-content'></div>");
var ct = $(this.container);
ct.append(chartContainer);
chartContainer.append(contentContainer);
};
$.extend(FishboneChart.prototype, {
buildInHtml: function (gviTable) {
var chartData = buildChartDataJson(gviTable);
renderFishboneChart(chartData);
var self = this;
// wait until layout is stabilized
setTimeout(function(){self.rendered = true;}, 10000);
},
isRendered: function () {
return this.rendered;
}
});
return FishboneChart;
})(viz.GviChartBase);

Backbone - Test method in view that uses ReadFile

I have written a backbone view which takes a file object or blob as an option in instantiation and then checks that file for EXIF data, corrects orientation and resizes the image if necessary depending on the options passed in.
Within the view there is a function mainFn which takes the file object and calls all other subsequent functions.
My issue is how to I test mainFn that uses ReadFile and an image constructor?
For my test set-up I am using mocah, chai, sinon and phantomjs.
In my sample code I have removed all other functions as to not add unnecessary clutter. If you wish to see the whole view visit its github repository.
var imageUpLoad = Backbone.View.extend({
template: _.template(document.getElementById("file-uploader-template").innerHTML),
// global variables passed in through options - required
_file: null, // our target file
cb: null,
maxFileSize: null, // megabytes
maxHeight: null, // pixels - resize target
maxWidth: null, // pixels - resize target
minWidth: null, // pixels
maxAllowedHeight: null, //pixels
maxAllowedWidth: null, // pixels
// globals determined through function
sourceWidth: null,
sourceHeight: null,
initialize: function (options) {
this._file = options.file;
this.cb = options.cb;
this.maxHeight = options.maxHeight;
this.maxWidth = options.maxWidth;
this.maxFileSize = options.maxFileSize;
this.minWidth = options.minWidth;
this.maxAllowedHeight = options.maxAllowedHeight;
this.maxAllowedWidth = options.maxAllowedWidth;
},
render: function () {
this.setElement(this.template());
this.mainFn(this._file);
return this;
},
// returns the width and height of the source file and calls the transform function
mainFn: function (file) {
var fr = new FileReader();
var that = this;
fr.onloadend = function () {
var _img = new Image();
// image width and height can only be determined once the image has loaded
_img.onload = function () {
that.sourceWidth = _img.width;
that.sourceHeight = _img.height;
that.transformImg(file);
};
_img.src = fr.result;
};
fr.readAsDataURL(file);
}
});
My test set-up
describe("image-upload view", function () {
before(function () {
// create test fixture
this.$fixture = $('<div id="image-view-fixture"></div><div>');
});
beforeEach(function () {
// fake image
this.b64DataJPG = '/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAA' +
'ABABIBAwABAAAABgASAAAAAAD/2wBDAAEBAQEBAQEBAQEBAQEB' +
'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' +
'EBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEB' +
'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' +
'EBAQEBAQH/wAARCAABAAIDASIAAhEBAxEB/8QAHwAAAQUBAQEB' +
'AQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBA' +
'QAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk' +
'M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1' +
'hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj' +
'pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+' +
'Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAA' +
'AAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAx' +
'EEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl' +
'8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2' +
'hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq' +
'srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8v' +
'P09fb3+Pn6/9oADAMBAAIRAxEAPwD+/iiiigD/2Q==';
var b64toBlob = function (b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var input = b64Data.replace(/\s/g, '');
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
try{
var blob = new Blob( byteArrays, {type : contentType});
}
catch(e){
// TypeError old chrome and FF
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if(e.name == 'TypeError' && window.BlobBuilder){
var bb = new BlobBuilder();
bb.append(byteArrays);
blob = bb.getBlob(contentType);
}
else if(e.name == "InvalidStateError"){
// InvalidStateError (tested on FF13 WinXP)
blob = new Blob(byteArrays, {type : contentType});
}
else{
// We're screwed, blob constructor unsupported entirely
}
}
return blob;
};
this.blobJPG = b64toBlob(this.b64DataJPG, "image/jpg");
/* **************** */
this.$fixture.empty().appendTo($("#fixtures"));
this.view = new imageUpLoad({
file: this.blobJPG,
cb: function (url) {console.log(url);},
maxFileSize: 500000,
minWidth: 200,
maxHeight: 900,
maxWidth: 1000,
maxAllowedHeight: 4300,
maxAllowedWidth: 1000
});
this.renderSpy = sinon.spy(this.view, "render");
this.readFileDataStub = sinon.stub(this.view, 'readFileData');
this.resizeImageStub = sinon.stub(this.view, 'resizeImage');
this.returnDataUrlStub = sinon.stub(this.view, 'returnDataUrl');
this.mainFnSpy = sinon.spy(this.view, 'mainFn');
this.transformImgStub = sinon.stub(this.view, 'transformImg');
this.sizeConfigStub = sinon.stub(this.view, 'sizeConfig');
this.resizeConfStub = sinon.stub(this.view, 'resizeConf');
this.callbackSpy = sinon.spy();
});
afterEach(function () {
this.renderSpy.restore();
this.readFileDataStub.restore();
this.resizeImageStub.restore();
this.returnDataUrlStub.restore();
this.mainFnSpy.restore();
this.sizeConfigStub.restore();
this.resizeConfStub.restore();
this.transformImgStub.restore();
});
after(function () {
$("#fixtures").empty();
});
it("can render", function () {
var _view = this.view.render();
expect(this.renderSpy).to.have.been.called;
expect(this.view).to.equal(_view);
});
});
You could either mock the FileReader / Image on the window, e.g.
// beforeEach
var _FileReader = window.FileReader;
window.FileReader = sinon.stub().return('whatever');
// afterEach
window.FileReader = _FileReader;
Or reference the constructor on the instance, e.g.
// view.js
var View = Backbone.View.extend({
FileReader: window.FileReader,
mainFn: function() {
var fileReader = new this.FileReader();
}
});
// view.spec.js
sinon.stub(this.view, 'FileReader').return('whatever');
Personally I'd prefer the latter as there's no risk of breaking the global reference if, for example, you forget to reassign the original value.

how to to process result of google distance matrix api further?

i am new to programming.. i have this code which gives distance between two points but need to further multiply it by an integer say 10.. the project i am working on is abt calculating distance between two points and multiplying it with fare/Km like Rs.10/km (Indian Rupees) for the same. So if the distance is 30 km the fare would be 30*10 = Rs.300
Thanks in advance
following is the code
<script>
var map;
var geocoder;
var bounds = new google.maps.LatLngBounds();
var markersArray = [];
var origin1 = '';
var destinationA = '';
var destinationIcon = 'https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=D|FF0000|000000';
var originIcon = 'https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=O|FFFF00|000000';
function initialize() {
var opts = {
center: new google.maps.LatLng(55.53, 9.4),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map'), opts);
var fromText = document.getElementById('FAdd');
var options = {
componentRestrictions: {country: 'in'}
};var fromAuto = new google.maps.places.Autocomplete(fromText, options);
fromAuto.bindTo('bound', map);
var toText = document.getElementById('TAdd');
var toAuto = new google.maps.places.Autocomplete(toText, options);
toAuto.bindTo('bound', map);
geocoder = new google.maps.Geocoder();
}
function calculateDistances() {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [document.getElementById("FAdd").value],
destinations: [document.getElementById("TAdd").value],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, callback);
}
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
alert('Error was: ' + status);
} else {
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var outputDiv = document.getElementById('outputDiv');
outputDiv.innerHTML = '';
deleteOverlays();
for (var i = 0; i < origins.length; i++) {
var results = response.rows[i].elements;
addMarker(origins[i], false);
for (var j = 0; j < results.length; j++) {
addMarker(destinations[j], true);
outputDiv.innerHTML += results[j].distance.text + '<br>';
}
}
}
}
function addMarker(location, isDestination) {
var icon;
if (isDestination) {
icon = destinationIcon;
} else {
icon = originIcon;
}
geocoder.geocode({'address': location}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
bounds.extend(results[0].geometry.location);
map.fitBounds(bounds);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location,
icon: icon
});
markersArray.push(marker);
} else {
alert('Geocode was not successful for the following reason: '
+ status);
}
});
}
function deleteOverlays() {
if (markersArray) {
for (i in markersArray) {
markersArray[i].setMap(null);
}
markersArray.length = 0;
}
}
</script>
I use an Ajax call to PHP, and haven't yet used getDistanceMatrix(), but this should be an easy fix.
First, if you know you will always only have one origin and one destination, you don't need the "for" loop in your callback function. Second, you're taking the distance text rather than the distance value.
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
[...]
} else {
deleteOverlays();
var outputDiv = document.getElementById('outputDiv'),
origin = response.originAddresses[0],
destination = response.destinationAddresses[0],
result = response.rows[0].elements[0],
distance = result.distance.value,
text = result.distance.text,
price = 10 * distance;
outputDiv.innerHTML = '<p>' + text + ': Rs.' + price + '</p>';
addMarker(origin, false);
addMarker(destination, false);
}
}
I haven't tested this, so it probably needs to be tweaked. ( See https://developers.google.com/maps/documentation/distancematrix/#DistanceMatrixResponses )