Related
I am new to Node Js. I can't find a solution for this problem: I have to use a for loop to change the XML and to create a new one. It already gives me one new XML file but I want ten new XML files in the end. I hope my explanation wasn't too bad.
for (t = 0; t < 10; t++) {
for (let i = 0; i < homeTeamStarting11.length; i++) {
homeTeamStarting11[i] = homeTeamStarting11[i];
let roleHome = homeTeamStarting11[i].role['$t']; //string
let roleAway = awayTeamStarting11[i].role['$t'];
let currentPosition = homeTeamStarting11[i].position;
let currentPositiontwo = awayTeamStarting11[i].position;
if (roleHome === 'GOALKEEPER' || roleAway === 'GOALKEEPER') {
} else if (roleHome === 'DEFENSE' || roleAway === 'DEFENSE') {
currentPosition['y']['$t'] = Math.floor(Math.random() * 80) + 3;
currentPositiontwo['y']['$t'] = Math.floor(Math.random() * 80) + 3;
currentPosition['x']['$t'] = Math.floor(Math.random() * 80) + 3;
currentPositiontwo['x']['$t'] = Math.floor(Math.random() * 80) + 3;
} else {
currentPosition['y']['$t'] = Math.random() * (90 - 1) + 1;
currentPositiontwo['y']['$t'] = Math.random() * (90 - 1) + 1;
currentPosition['x']['$t'] = Math.random() * (90 - 1) + 1;
currentPositiontwo['x']['$t'] = Math.random() * (90 - 1) + 1;
}
homeTeamStarting11[i].position = currentPosition;
//awayTeamStarting11[i].position = currentPositiontwo;
//const TeamTogether = homeTeamStarting11.concat(awayTeamStarting11);
json['lineup']['away']['startingEleven']['persons']['player'] = awayTeamStarting11;
json['lineup']['home']['startingEleven']['persons']['player'] = homeTeamStarting11;
}
const Name = JSON.stringify(json);
const xml = parser.toXml(Name);
let xjz = getFilename(2);
fs.writeFile('xmls/' + xjz + '.xml', xml, function(err, data) {
});
function getFilename(anyNumber) {
let filename = 'xmlfilename' + anyNumber;
return filename;
}
}
You call your getFilename() function with a constant 2. So each run through your for (t = 0; t < 10; t++) { ... } loop overwrites the same file.
Try using let xjz = getFilename(t); to get a different filename each time.
fabric.ThreePointArc = fabric.util.createClass(fabric.Circle, {
type: 'threePointArc',
points: [], //array of startPoint, intermediatePoint, endPoint
arcCenter: new fabric.Point(null, null),
arcBounds: null,
radius: null,
initialize: function (points, options) {
if (!points || points.length === 0) {
return;
}
this.points = points;
this.callSuper('initialize', options);
// supports only originX and originY as center
this.originX = this.originY = 'center';
},
_set: function(key, value) {
this.callSuper('_set', key, value);
if (key === 'points') {
this._calcArcCenter();
this._calcDimensions();
this.setCoords();
}
return this;
},
setRadius: function(value) {
this.radius = value;
return this;
},
_calcDimensions: function() {
this._calcArcAngles();
this.setRadius(cMath.getLength(this.arcCenter, this.points[0]));
this._calcArcBounds();
this.width = this.arcBounds.width;
this.height = this.arcBounds.height;
this.top = this.arcBounds.y + this.arcBounds.height / 2;
this.left = this.arcBounds.x + this.arcBounds.width / 2;
},
_calcArcCenter: function() {
var c1Mp = cMath.getMidPoint(this.points[0], this.points[1]), // chord 1 midpoint
c2Mp = cMath.getMidPoint(this.points[1], this.points[2]), // chord 2 midpoint
c1pm = -(1 / cMath.getSlope(this.points[0], this.points[1])), // chord 1 perpendicular bisector slope
c2pm = -(1 / cMath.getSlope(this.points[1], this.points[2])); // chord 2 perpendicular bisector slope
// chord perpendicular bisectors meet at the center
this.arcCenter.x = (c2Mp.y - (c2pm * c2Mp.x) + (c1pm * c1Mp.x) - c1Mp.y) / (c1pm - c2pm);
this.arcCenter.y = (c2pm * (this.arcCenter.x - c2Mp.x)) + c2Mp.y;
},
_calcArcBounds: function() {
var validPoints = this.buildValidPointsForArc(),
minX = fabric.util.array.min(validPoints, 'x'),
minY = fabric.util.array.min(validPoints, 'y'),
maxX = fabric.util.array.max(validPoints, 'x'),
maxY = fabric.util.array.max(validPoints, 'y'),
width = (maxX - minX) || 1,
height = (maxY - minY) || 1;
this.arcBounds = {
x: minX,
y: minY,
width: width,
height: height
}
},
buildValidPointsForArc: function() {
var direction = this.getRenderingDirection(),
possibleBoundingPoints = this.points.concat();
!this.arcAngles && this._calcArcAngles();
if (direction) {
for (var i = 1; i <= 4; i++) {
var randomAngle = i * (PI / 2);
if (this.arcAngles.startAngle < this.arcAngles.endAngle) {
!(this.arcAngles.startAngle <= randomAngle && randomAngle <= this.arcAngles.endAngle) &&
possibleBoundingPoints.push(this.generateArcPoint(randomAngle));
} else {
(this.arcAngles.endAngle <= randomAngle && randomAngle <= this.arcAngles.startAngle) &&
possibleBoundingPoints.push(this.generateArcPoint(randomAngle));
}
}
} else {
for (var i = 4; i >= 1; i--) {
var randomAngle = i * (PI / 2);
if (this.arcAngles.startAngle < this.arcAngles.endAngle) {
(this.arcAngles.startAngle <= randomAngle && randomAngle <= this.arcAngles.endAngle) &&
possibleBoundingPoints.push(this.generateArcPoint(randomAngle));
} else {
!(this.arcAngles.endAngle <= randomAngle && randomAngle <= this.arcAngles.startAngle) &&
possibleBoundingPoints.push(this.generateArcPoint(randomAngle));
}
}
}
return possibleBoundingPoints;
},
generateArcPoint: function(angle) {
return new fabric.Point(this.arcCenter.x + this.radius * Math.cos(angle), this.arcCenter.y + this.radius * Math.sin(angle));
},
_calcArcAngles: function() {
var angleKeyRepo = ["startAngle", "intermediateAngle", "endAngle"];
this.arcAngles = this.arcAngles || {};
this.points.forEach(function(point, index) {
var a = cMath.getAngle(this.arcCenter, point);
this.arcAngles[angleKeyRepo[index]] = a < 0 ? ((PI * 2) + a) :
a > 2 * PI ? ((PI * 2) - a) : a;
}, this);
},
getRenderingDirection: function() {
return (((this.points[1].x - this.points[0].x) * (this.points[2].y - this.points[0].y)) -
((this.points[1].y - this.points[0].y) * (this.points[2].x - this.points[0].x))) < 0;
},
_render: function(ctx, noTransform) {
if (!this.visible) {
return;
}
ctx.beginPath();
ctx.arc(
noTransform ? this.left : 0,
noTransform ? this.top : 0,
this.radius,
this.arcAngles.startAngle,
this.arcAngles.endAngle,
this.getRenderingDirection()
);
this._renderFill(ctx);
this._renderStroke(ctx);
},
toObject: function (propertiesToInclude) {
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
points: this.points
});
}
});
fabric.ThreePointArc.fromObject = function(object) {
return new fabric.ThreePointArc(object.points, object);
};
fabric.ThreePointArc.async = false;
fabric.util.cMath = {
getSlope : function (startPoint, endPoint) {
if (!startPoint || !endPoint) {
console.error('startPoint and endPoint are required to evaluate slope');
return;
}
// hack to get around the indefinte slope problem
if (endPoint.x == startPoint.x) startPoint.x = startPoint.x + 0.01;
if (endPoint.y == startPoint.y) endPoint.y = endPoint.y + 0.01;
return (endPoint.y - startPoint.y) / (endPoint.x - startPoint.x);
},
getMidPoint: function (startPoint, endPoint) {
if (!startPoint || !endPoint) {
console.error('startPoint and endPoint are required to evaluate slope');
return;
}
return { x: (startPoint.x + endPoint.x) / 2, y: (startPoint.y + endPoint.y) / 2 };
},
getAngle: function (startPoint, endPoint, isDegree) {
if (!startPoint || !endPoint) {
console.error('startPoint and endPoint are required to evaluate slope');
return;
}
var radians = Math.atan2((endPoint.y - startPoint.y), (endPoint.x - startPoint.x)),
degrees = fabric.util.radiansToDegrees(radians);
return isDegree ? degrees < 0 ? 360 + degrees : degrees : radians;
},
getLength: function (startPoint, endPoint) {
if (!startPoint || !endPoint) {
console.error('startPoint and endPoint are required to evaluate slope');
return;
}
return Math.sqrt(Math.pow(endPoint.y - startPoint.y, 2) + Math.pow(endPoint.x - startPoint.x, 2));
}
}
var canvas = new fabric.Canvas('c');
var startPoint = new fabric.Point(47.25423728813553, 56.91525423728814),
intermediatePoint = new fabric.Point( 76.33898305084739,19.8983050847458 ),
endPoint = new fabric.Point( 105.42372881355931,86 );
var arc = new fabric.ThreePointArc([startPoint, intermediatePoint, endPoint] , {
fill: "#FF0000",
stroke: "#000",
strokeWidth: 10
});
canvas.add(arc);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.10/fabric.js"></script>
<canvas id="c" width="800" height="800"></canvas>
I'm trying to create a three point arc on fabric js (ver: 1.4.10). What I want is exactly like this:- https://www.screencast.com/t/dMLvcOduEF
I have a points array from which I'm calcualting the startAngle, endAngle, arcCenter, radius, bounding boxc height and widht, top and left. When I draw it, the actual object is not drawing where my mouse points. It adds up the left and top value with the arcCenter.x and arcCenter.y respectively and draws there. Is there any workaround so that my arc can be drawn where my pointer is ?. So that I don't need to write any extra lines of code for moving, rotating and scaling.
What I'm getting is like this:- https://www.screencast.com/t/V9MUgB3pB
added the fiddle!!
Is there any workaround for this ??
I've been fiddling around with the jpeg-js module and Node JS Buffer, and attempting to create a small command line program that modifies the decoded JPEG buffer data and creates a pattern of X number of reversed scanlines and X number of normal scanlines before saving a new JPEG. In other words, I'm looking to flip portions of the image, but not the entire image itself (plenty of modules that do such a thing, of course, but not the specific use case I have).
To create the reversed/normal line patterns, I've been reading/writing line by line, and saving a slice of that line to a variable, then starting at the end of scanline and incrementally going down by slices of 4 bytes (the alloc for an RGBA value) until I'm at the beginning of the line. Code for the program:
'use strict';
const fs = require('fs');
const jpeg = require('jpeg-js');
const getPixels = require('get-pixels');
let a = fs.readFileSync('./IMG_0006_2.jpg');
let d = Buffer.allocUnsafe(a.width * a.height * 4);
let c = jpeg.decode(a);
let val = false; // track whether normal or reversed scanlines
let lineWidth = b.width * 4;
let lineCount = 0;
let track = 0;
let track2 = 0;
let track3 = 0;
let curr, currLine; // storage for writing/reading scnalines, respectively
let limit = {
one: Math.floor(Math.random() * 141),
two: Math.floor(Math.random() * 151),
three: Math.floor(Math.random() * 121)
};
if (limit.one < 30) {
limit.one = 30;
}
if (limit.two < 40) {
limit.two = 40;
}
if (limit.two < 20) {
limit.two = 20;
}
let calc = {};
calc.floor = 0;
calc.ceil = 0 + lineWidth;
d.forEach(function(item, i) {
if (i % lineWidth === 0) {
lineCount++;
/* // alternate scanline type, currently disabled to figure out how to succesfully reverse image
if (lineCount > 1 && lineCount % limit.one === 0) {
// val = !val;
}
*/
if (lineCount === 1) {
val = !val; // setting alt scanline check to true initially
} else if (calc.floor + lineWidth < b.data.length - 1) {
calc.floor += lineWidth;
calc.ceil += lineWidth;
}
currLine = c.data.slice(calc.floor, calc.ceil); // current line
track = val ? lineWidth : 0; // tracking variable for reading from scanline
track2 = val ? 4 : 0; // tracking variable for writing from scanline
}
//check if reversed and writing variable has written 4 bytes for RGBA
//if so, set writing source to 4 bytes at end of line and read from there incrementally
if (val && track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
} else {
curr = currLine; //set normal scanline
}
d[i] = curr[track2];
// check if there is no match between data source and decoded image
if (d[i] !== curr[track2]) {
if (track3 < 50) {
console.log(i);
}
track3++;
}
track2++; //update tracking variable
track = val ? track - 1 : track + 1; //update tracking variable
});
var rawImageData = {
data: d,
width: b.width,
height: b.height
};
console.log(b.data.length);
console.log('errors\t', track3);
var jpegImageData = jpeg.encode(rawImageData, 100);
fs.writeFile('foo2223.jpg', jpegImageData.data);
Alas, the reversed scanline code I've written does not properly. Unfortunately, I've only been able successfully reverse the red channel of my test image (see below left), with the blue and green channels just turning into vague blurs. The color scheme should look something like the right image.
What am I doing wrong here?
For reversed lines, you stored slices of 4 bytes(4 bytes = 1 pixel), then write the first value of the pixel(red) correctly.
But in the next iteration, you overwrite the slice curr with currLine, rest of channels gets wrong values.
if (val && track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
} else {
curr = currLine; //set normal scanline
}
Iteration 0: val == true, track2 == 4, set curr to next pixel, write red channel.
Iteration 1: val == true, track2 == 1, (val && track2 === 4) == false, set curr to currLine, write green channel.
You can move track2 === 4 branch to avoid this:
if (val) {
if (track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
}
} else {
curr = currLine; //set normal scanline
}
Fixed code should look like this:
function flipAlt(input, output) {
const fs = require('fs');
const jpeg = require('jpeg-js');
let a = fs.readFileSync(input);
let b = jpeg.decode(a);
let d = Buffer.allocUnsafe(b.width * b.height * 4);
let val = false; // track whether normal or reversed scanlines
let lineWidth = b.width * 4;
let lineCount = 0;
let track = 0;
let track2 = 0;
let track3 = 0;
let curr, currLine; // storage for writing/reading scnalines, respectively
let limit = {
one: Math.floor(Math.random() * 141),
two: Math.floor(Math.random() * 151),
three: Math.floor(Math.random() * 121)
};
if (limit.one < 30) {
limit.one = 30;
}
if (limit.two < 40) {
limit.two = 40;
}
if (limit.two < 20) {
limit.two = 20;
}
let calc = {};
calc.floor = 0;
calc.ceil = 0 + lineWidth;
d.forEach(function(item, i) {
if (i % lineWidth === 0) {
lineCount++;
if (lineCount > 1) {
val = !val;
}
if (lineCount === 1) {
val = !val; // setting alt scanline check to true initially
} else if (calc.floor + lineWidth < b.data.length - 1) {
calc.floor += lineWidth;
calc.ceil += lineWidth;
}
currLine = b.data.slice(calc.floor, calc.ceil); // current line
track = val ? lineWidth : 0; // tracking variable for reading from scanline
track2 = val ? 4 : 0; // tracking variable for writing from scanline
}
//check if reversed and writing variable has written 4 bytes for RGBA
//if so, set writing source to 4 bytes at end of line and read from there incrementally
if (val) {
if (track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
}
} else {
curr = currLine; //set normal scanline
}
d[i] = curr[track2];
// check if there is no match between data source and decoded image
if (d[i] !== curr[track2]) {
if (track3 < 50) {
console.log(i);
}
track3++;
}
track2++; //update tracking variable
track = val ? track - 1 : track + 1; //update tracking variable
});
var rawImageData = {
data: d,
width: b.width,
height: b.height
};
console.log(b.data.length);
console.log('errors\t', track3);
var jpegImageData = jpeg.encode(rawImageData, 100);
fs.writeFile(output, jpegImageData.data);
}
flipAlt('input.jpg', 'output.jpg');
Instead of tracking array indices, you can use utility library like lodash, it should make things easier:
function flipAlt(input, output) {
const fs = require('fs');
const jpeg = require('jpeg-js');
const _ = require('lodash');
const image = jpeg.decode(fs.readFileSync(input));
const lines = _.chunk(image.data, image.width*4);
const flipped = _.flatten(lines.map((line, index) => {
if (index % 2 != 0) {
return line;
}
const pixels = _.chunk(line, 4);
return _.flatten(pixels.reverse());
}));
const imageData = jpeg.encode({
width: image.width,
height: image.height,
data: new Buffer(flipped)
}, 100).data;
fs.writeFile(output, imageData);
}
flipAlt('input.jpg', 'output.jpg');
Is there a nodeJS-way to split an image into single chunks?
Probably with different dimensions?
Like the photos on image below
I've done something like this using jimp, however it can probably be much more concise...
var Jimp = require("jimp");
/**
* #param filename - input file
* #param numSlices - how many slices
* #param attenuation - how quickly slices get smaller
*/
function sliceImage(filename, numSlices, attenuation) {
Jimp.read(filename).then(image => {
let w = image.bitmap.width;
let h = image.bitmap.height;
let sliceWidth = w / numSlices;
let midlane = w / 2;
let slack = 0.001;
let slices = [];
function slicePair(left, right) {
if (left < (0 - slack) || (right - sliceWidth) > (w + slack)) {
return;
}
let leftSlice = image.clone();
let rightSlice = image.clone();
leftSlice.crop(left, 0, sliceWidth, h);
rightSlice.crop(right - sliceWidth, 0, sliceWidth, h);
slices.push(leftSlice);
slices.push(rightSlice);
slicePair(left - sliceWidth, right + sliceWidth);
}
function doSlice() {
if (numSlices % 2 == 0) {
slicePair(midlane - sliceWidth, midlane + sliceWidth);
} else {
let middle = image.clone();
middle.crop(midlane - (sliceWidth / 2), 0, sliceWidth, h);
slices.push(middle);
slicePair(midlane - (1.5 * sliceWidth), midlane + (1.5 * sliceWidth));
}
}
function overlay() {
let canvas = image.clone().brightness(1);
let odd = !(slices.length % 2 === 0);
let adjust = odd ? (sliceWidth / 2) : 0;
slices.reverse();
if (odd) {
let middle = slices.pop();
canvas.composite(middle, (midlane - (sliceWidth / 2)), 0);
}
for (let i = 0; i < (slices.length + 2); i++) {
let k = odd ? (i + 1) : i;
let left = slices.pop().crop(0, attenuation * k, sliceWidth, (h - (attenuation * k) * 2));
let right = slices.pop().crop(0, attenuation * k, sliceWidth, (h - (attenuation * k) * 2));
canvas.composite(left, (midlane - ((i + 1) * sliceWidth)) - adjust, (attenuation * k));
canvas.composite(right, (midlane + (i * sliceWidth)) + adjust, (attenuation * k));
}
canvas.write("result.jpg");
return canvas;
}
doSlice();
return overlay();
});
}
const Jimp = require('jimp') ;
const forloop=require('async-for-loop');
const width=1000, height=716,h=7,v=7;
async function crop(source,filename,offSetX, offSetY, width,height,cb) {
// Reading Image
const image = await Jimp.read
(source);
image.crop(offSetX, offSetY, width,height)
.write(filename);
}
//
forloop(h,(hi,nextH)=>{
forloop(v,async (vi,nextV)=>{
await crop('/Users/you/Projects/sliceit/imgs/image.jpg',`/Users/you/Projects/sliceit/out/slice_${hi}_${vi}.jpg`,
hi*width,vi*height,width,height,nextV
);
nextV();
},()=>{
nextH();
})
},()=>{
console.log("Image is processed successfully");
})
UPDATE - I added more information. The issue seems to be dependant on the order of the require statements. Unfortunately, fixing one breaks the other, please help!!!
I cannot figure out what is going on here. I have 5 files shown below, and am using index.js simply to demonstrate the issue. If HDate is required first then HDate.make fails within the HDateTime class. If HDateTime is required first, then HDateTime.make fails within the HDate class. Appears it is possibly a circular reference issue, but I have not been able to figure out how to correct the problem.
index.js - fails on DATETIME
var HDate = require('./HDate');
var HDateTime = require('./HDateTime');
var HTimeZone = require('./HTimeZone');
var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));
ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
MIDNIGHT: 2011-01-02T00:00:00Z UTC
C:\Users\Shawn\nodehaystack\HDateTime.js:95
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5,
^
TypeError: undefined is not a function
at Function.HDateTime.make (C:\Users\Shawn\nodehaystack\HDateTime.js:95:33)
at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:38)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
index.js - fails on MIDNIGHT
var HDateTime = require('./HDateTime');
var HDate = require('./HDate');
var HTimeZone = require('./HTimeZone');
var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));
ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
DATETIME: 2011-01-02T03:04:05Z UTC
C:\Users\Shawn\nodehaystack\HDate.js:46
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MI
^
TypeError: undefined is not a function
at HVal.HDate.midnight (C:\Users\Shawn\nodehaystack\HDate.js:46:60)
at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:51)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
HDate.js
var HVal = require('./HVal');
var HDateTime = require('./HDateTime');
var HTime = require('./HTime');
/** Private constructor */
function HDate(year, month, day) {
/** Four digit year such as 2011 */
this.year = year;
/** Month as 1-12 (Jan is 1, Dec is 12) */
this.month = month;
/** Day of month as 1-31 */
this.day = day;
};
HDate.prototype = Object.create(HVal.prototype);
/** int - Hash is based on year, month, day */
HDate.prototype.hashCode = function() { return (this.year << 16) ^ (this.month << 8) ^ this.day; };
/** String - Encode as "YYYY-MM-DD" */
HDate.prototype.toZinc = function() {
var s = this.year + "-";
if (this.month < 10) s += "0"; s += this.month + "-";
if (this.day < 10) s += "0"; s += this.day;
return s;
};
/** boolean - Equals is based on year, month, day */
HDate.prototype.equals = function(that) { return that instanceof HDate && this.year === that.year && this.month === that.month && this.day === that.day; };
/** int - Return sort order as negative, 0, or positive */
HDate.prototype.compareTo = function(that) {
if (this.year < that.year) return -1; else if (this.year > that.year) return 1;
if (this.month < that.month) return -1; else if (this.month > that.month) return 1;
if (this.day < that.day) return -1; else if (this.day > that.day) return 1;
return 0;
};
/** Convert this date into HDateTime for midnight in given timezone. */
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MIDNIGHT, tz); };
/** Return date in future given number of days */
HDate.prototype.plusDays = function(numDays) {
if (numDays == 0) return this;
if (numDays < 0) return this.minusDays(-numDays);
var year = this.year;
var month = this.month;
var day = this.day;
for (; numDays > 0; --numDays) {
day++;
if (day > HDate.daysInMonth(year, month)) {
day = 1;
month++;
if (month > 12) { month = 1; year++; }
}
}
return HDate.make(year, month, day);
};
/** Return date in past given number of days */
HDate.prototype.minusDays = function(numDays) {
if (numDays == 0) return this;
if (numDays < 0) return this.plusDays(-numDays);
var year = this.year;
var month = this.month;
var day = this.day;
for (; numDays > 0; --numDays) {
day--;
if (day <= 0) {
month--;
if (month < 1) { month = 12; year--; }
day = HDate.daysInMonth(year, month);
}
}
return HDate.make(year, month, day);
};
/** Return day of week: Sunday is 1, Saturday is 7 */
HDate.prototype.weekday = function() { return new Date(this.year, this.month-1, this.day).getDay() + 1; };
/** Export for use in testing */
module.exports = HDate;
/** Construct from basic fields, javascript Date instance, or String in format "YYYY-MM-DD" */
HDate.make = function(arg, month, day) {
if (arg instanceof Date) {
return new HDate(arg.getFullYear(), arg.getMonth() + 1, arg.getDate());
} else if (HVal.typeis(arg, 'string', String)) {
try {
var s = arg.split('-');
return new HDate(parseInt(s[0]), parseInt(s[1]), parseInt(s[2]));
} catch(err) {
throw err;
}
} else {
if (arg < 1900) throw new Error("Invalid year");
if (month < 1 || month > 12) throw new Error("Invalid month");
if (day < 1 || day > 31) throw new Error("Invalid day");
return new HDate(arg, month, day);
}
};
/** Get HDate for current time in default timezone */
HDate.today = function() { return HDateTime.now().date; };
/** Return if given year a leap year */
HDate.isLeapYear = function(year) {
if ((year & 3) != 0) return false;
return (year % 100 != 0) || (year % 400 == 0);
};
/** Return number of days in given year (xxxx) and month (1-12) */
var daysInMon = [ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
var daysInMonLeap = [ -1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
HDate.daysInMonth = function(year, mon) { return HDate.isLeapYear(year) ? daysInMonLeap[mon] : daysInMon[mon]; };
HDateTime.js
var moment = require('moment-timezone');
var HVal = require('./HVal');
var HDate = require('./HDate');
var HTime = require('./HTime');
var HTimeZone = require('./HTimeZone');
// require('./io/HZincReader');
/** Private constructor */
function HDateTime(date, time, tz, tzOffset) {
/** HDate - Date component of the timestamp */
this.date = date;
/** HTime - Time component of the timestamp */
this.time = time;
/** int - Offset in seconds from UTC including DST offset */
this.tz = tz;
/** HTimeZone - Timezone as Olson database city name */
this.tzOffset = (typeof(tzOffset) == 'undefined' ? 0 : tzOffset);
/** long - millis since Epoch */
this.millis;
}
HDateTime.prototype = Object.create(HVal.prototype);
/** long - Get this date time as Java milliseconds since epoch */
HDateTime.prototype.millis = function() {
if (this.millis <= 0) {
var d = new Date(this.date.year, this.date.month-1, this.date.day, this.time.hour, this.time.min, this.time.sec, this.time.ms);
//TODO: implement UTC timezone
this.millis = d.getTime();
}
return this.millis;
};
/** int - Hash code is based on date, time, tzOffset, and tz */
HDateTime.prototype.hashCode = function() { return this.date.hashCode() ^ this.time.hashCode() ^ tzOffset ^ this.tz.hashCode(); };
/** String - Encode as "YYYY-MM-DD'T'hh:mm:ss.FFFz zzzz" */
HDateTime.prototype.toZinc = function() {
var s = this.date.toZinc() + "T" + this.time.toZinc();
if (this.tzOffset == 0) s += "Z";
else {
var offset = this.tzOffset;
if (offset < 0) { s += "-"; offset = -offset; }
else { s += "+"; }
var zh = offset / 3600;
var zm = (offset % 3600) / 60;
if (zh < 10) s += "0"; s += zh + ":";
if (zm < 10) s += "0"; s += zm;
}
s += " " + this.tz;
return s;
};
/** boolean - Equals is based on date, time, tzOffset, and tz */
HDateTime.prototype.equals = function(that) {
return that instanceof HDateTime && this.date === that.date
&& this.time === that.time && this.tzOffset === that.tzOffset && this.tz === that.tz;
};
/** int - Comparison based on millis. */
HDateTime.prototype.compareTo = function(that) {
var thisMillis = this.millis();
var thatMillis = that.millis();
if (thisMillis < thatMillis) return -1;
else if (thisMillis > thatMillis) return 1;
return 0;
};
/** Export for use in testing */
module.exports = HDateTime;
function utcDate(year, month, day, hour, min, sec, ms) {
var d = new Date();
d.setUTCFullYear(year);
d.setUTCMonth(month-1);
d.setUTCDate(day);
d.setUTCHours(hour);
d.setUTCMinutes(min);
d.setUTCSeconds(sec);
d.setUTCMilliseconds(ms);
return d;
}
/** Construct from various values */
HDateTime.make = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
if (arg7 instanceof HTimeZone) {
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, arg6), arg7, arg8);
} else if (arg6 instanceof HTimeZone) {
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, 0), arg6, arg7);
} else if (arg3 instanceof HTimeZone) {
// use Date to decode millis to fields
var d = utcDate(arg1.year, arg1.month, arg1.day, arg2.hour, arg2.min, arg2.sec, arg2.ms);
var tzOffset = arg4;
if (typeof(tzOffset) == 'undefined') {
// convert to designated timezone
d = moment(d).tz(arg3.js.name);
tzOffset = d.utcOffset() * 60;
}
var ts = new HDateTime(arg1, arg2, arg3, tzOffset);
ts.millis = d.valueOf() + (tzOffset * -1000);
return ts;
} else if (this.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
// var val = new HZincReader(arg1).readScalar();
// if (typeof(val) == HDateTime) return val;
// throw new Error("Parse Error: " + arg1);
} else {
var tz = arg2;
if (typeof(tz) == 'undefined') tz = HTimeZone.DEFAULT;
var d = new Date(arg1);
// convert to designated timezone
d = moment(d).tz(tz.js.name);
tzOffset = d.utcOffset() * 60;
var ts = new HDateTime(HDate.make(d), HTime.make(d), tz, tz.getOffset());
ts.millis = arg1;
}
};
/** HDateTime - Get HDateTime for given timezone */
HDateTime.now = function(tz) { return make(new Date().getTime(), tz); };
HTime.js
var HVal = require('./HVal');
// require('./io/HZincReader');
/** Private constructor */
function HTime(hour, min, sec, ms) {
/** int - Hour of day as 0-23 */
this.hour = hour;
/** int - Minute of hour as 0-59 */
this.min = min;
/** int - Second of minute as 0-59 */
this.sec = (typeof(sec) == 'undefined' ? 0 : sec);
/** int - Fractional seconds in milliseconds 0-999 */
this.ms = (typeof(ms) == 'undefined' ? 0 : ms);
};
HTime.prototype = Object.create(HVal.prototype);
/** int - Hash code is based on hour, min, sec, ms */
HTime.prototype.hashCode = function() { return (this.hour << 24) ^ (this.min << 20) ^ (this.sec << 16) ^ this.ms; };
/** boolean - Equals is based on hour, min, sec, ms */
HTime.prototype.equals = function(that) {
return that instanceof HTime && this.hour === that.hour &&
this.min === that.min && this.sec === that.sec && this.ms === that.ms;
};
/** int - Return sort order as negative, 0, or positive */
HTime.prototype.compareTo = function(that) {
if (this.hour < that.hour) return -1; else if (this.hour > that.hour) return 1;
if (this.min < that.min) return -1; else if (this.min > that.min) return 1;
if (this.sec < that.sec) return -1; else if (this.sec > that.sec) return 1;
if (this.ms < that.ms) return -1; else if (this.ms > that.ms) return 1;
return 0;
};
/** String - Encode as "hh:mm:ss.FFF" */
HTime.prototype.toZinc = function() {
var s = "";
if (this.hour < 10) s += "0"; s += this.hour + ":";
if (this.min < 10) s += "0"; s += this.min + ":";
if (this.sec < 10) s += "0"; s += this.sec;
if (this.ms != 0) {
s += ".";
if (this.ms < 10) s += "0";
if (this.ms < 100) s += "0";
s += this.ms;
}
return s;
};
/** Export for use in testing */
module.exports = HTime;
/** Singleton for midnight 00:00 */
HTime.MIDNIGHT = new HTime(0, 0, 0, 0);
/** Construct with all fields, with Javascript Date object, or Parse from string fomat "hh:mm:ss.FF" */
HTime.make = function(arg1, min, sec, ms) {
if (HVal.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
// var val = new HZincReader(arg1).readScalar();
// if (val instanceof HTime) return val;
// throw new Error("Parse Error: " + arg1);
} else if (arg1 instanceof Date) {
return new HTime(arg1.getHours(), arg1.getMinutes(), arg1.getSeconds(), arg1.getMilliseconds());
} else {
return new HTime(arg1, min, sec, ms);
}
};
HTimeZone.js
var moment = require('moment-timezone');
var HVal = require('./HVal');
/** Package private constructor */
function HTimeZone(name, js) {
/** Haystack timezone name */
this.name = name;
/** Javascript (moment) representation of this timezone. */
this.js = js;
};
/** String - Return Haystack timezone name */
HTimeZone.prototype.toString = function() { return this.name; };
module.exports = HTimeZone;
HTimeZone.make = function(arg1, checked) {
if (typeof(checked) == 'undefined') return HTimeZone.make(arg1, true);
if (HVal.typeis(arg1, 'string', String)) {
/**
* Construct with Haystack timezone name, raise exception or
* return null on error based on check flag.
*/
// lookup in cache
var tz = cache[arg1];
if (typeof(tz) != 'undefined') return tz;
// map haystack id to Javascript full id
var jsId = toJS[arg1];
if (typeof(jsId) == 'undefined') {
if (checked) throw new Error("Unknown tz: " + arg1);
return undefined;
}
// resolve full id to HTimeZone and cache
var js = moment.tz.zone(jsId)
tz = new HTimeZone(arg1, js);
cache[arg1] = tz;
return tz;
} else {
/**
* Construct from Javascript timezone. Throw exception or return
* null based on checked flag.
*/
var jsId = arg1.name;
if (jsId.startsWith("GMT")) fixGMT(jsId);
var name = fromJS[jsId];
if (typeof(name) != 'undefined') return HTimeZone.make(name);
if (checked) throw new Error("Invalid Java timezone: " + arg1.name);
return;
}
};
function fixGMT(jsId) {
// Javscript (moment) IDs can be in the form "GMT[+,-]h" as well as
// "GMT", and "GMT0". In that case, convert the ID to "Etc/GMT[+,-]h".
// V8 uses the format "GMT[+,-]hh00 (used for default timezone), this also
// needs converted.
if (jsId.indexOf("+")<0 && jsId.indexOf("-")<0) return "Etc/" + jsId; // must be "GMT" or "GMT0" which are fine
// get the prefix
var pre = jsId.substring(0, 4);
var num = parseInt(jsId.substring(4, 6));
// ensure we have a valid value
if ((pre.substring(3)=="+" && num<13) || (pre.substring(3)=="-" & num<15)) return "Etc/" + pre + num;
// nothing we could do, return what was passed
return jsId;
}
var cache = {};
var toJS = {};
var fromJS = {};
{
try {
// only time zones which start with these
// regions are considered valid timezones
var regions = {};
regions["Africa"] = "ok";
regions["America"] = "ok";
regions["Antarctica"] = "ok";
regions["Asia"] = "ok";
regions["Atlantic"] = "ok";
regions["Australia"] = "ok";
regions["Etc"] = "ok";
regions["Europe"] = "ok";
regions["Indian"] = "ok";
regions["Pacific"] = "ok";
// iterate Javascript timezone IDs available
var ids = moment.tz.names();
for (var i=0; i<ids.length; ++i) {
var js = ids[i];
// skip ids not formatted as Region/City
var slash = js.indexOf('/');
if (slash < 0) continue;
var region = js.substring(0, slash);
if (typeof(regions[region]) == 'undefined') continue;
// get city name as haystack id
slash = js.lastIndexOf('/');
var haystack = js.substring(slash+1);
// store mapping b/w Javascript <-> Haystack
toJS[haystack] = js;
fromJS[js] = haystack;
}
} catch (err) {
console.log(err.stack);
}
var utc;
try {
utc = HTimeZone.make(moment.tz.zone("Etc/UTC"));
} catch (err) {
console.log(err.stack);
}
var def;
try {
// check if configured with system property
var defName = process.env["haystack.tz"];
if (defName != null) {
def = make(defName, false);
if (typeof(def) == 'undefined') console.log("WARN: invalid haystack.tz system property: " + defName);
}
// if we still don't have a default, try to use Javascript's
if (typeof(def) == 'undefined') {
var date = new Date().toString();
var gmtStart = date.indexOf("GMT");
var gmtEnd = date.indexOf(" ", gmtStart);
def = fromJS[fixGMT(date.substring(gmtStart, gmtEnd))];
}
} catch (err) {
console.log(err.stack);
def = utc;
}
/** UTC timezone */
HTimeZone.UTC = utc;
/** Default timezone for VM */
HTimeZone.DEFAULT = def;
}
The problem is how you export functions.
you say
module.exports = HDate which is fine
but then you do
module.exports.make = function(){} which is fine too.
But both together won't work reliable as you just experienced.
To fix that export the class either as something like module.exports.hdate OR add the make function to HDate:
Hdate.make = myFunction() {}
This goes obviously for all methods you export like this.
Let me know if that solves your problem :)
I have found that the best solution to this issue is to move all the require statements that could cause a cyclical dependency to ensure they are after the constructor for my class and after the module.exports statement. For example...
var HVal = require('./HVal');
/**
* HDate models a date (day in year) tag value.
* #see {#link http://project-haystack.org/doc/TagModel#tagKinds|Project Haystack}
*
* #constructor
* #private
* #extends {HVal}
* #param {int} year - Four digit year such as 2011
* #param {int} month - Month as 1-12 (Jan is 1, Dec is 12)
* #param {int} day - Day of month as 1-31
*/
function HDate(year, month, day) {
this.year = year;
this.month = month;
this.day = day;
}
HDate.prototype = Object.create(HVal.prototype);
module.exports = HDate;
var HDateTime = require('./HDateTime'),
HTime = require('./HTime');
Doing this method consistently in all of my classes prevents any cyclical issues.