Skip to content
This repository was archived by the owner on Dec 16, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions lib/images.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ function applyImageMethods(constructor) {
// pass.icon(function(callback) { ... };
// console.log(pass.icon());
//
// The 2x suffix is used for high resolution version (file name uses @2x
// suffix).
// The 2x and 3x suffix is used for high resolution version (file name uses
// @2x or @3x suffix).
//
// pass.icon2x("icon@2x.png");
// console.log(pass.icon2x());
//
// pass.icon3x("icon@3x.png");
// console.log(pass.icon3x());
IMAGES.forEach(function(key) {
prototype[key] = function(value) {
if (arguments.length === 0) {
Expand All @@ -35,12 +38,22 @@ function applyImageMethods(constructor) {
}
};

var retina = key + "2x";
prototype[retina] = function(value) {
var retina2x = key + "2x";
prototype[retina2x] = function(value) {
if (arguments.length === 0) {
return this.images[retina2x];
} else {
this.images[retina2x] = value;
return this;
}
};

var retina3x = key + "3x";
prototype[retina3x] = function(value) {
if (arguments.length === 0) {
return this.images[retina];
return this.images[retina3x];
} else {
this.images[retina] = value;
this.images[retina3x] = value;
return this;
}
};
Expand All @@ -56,8 +69,11 @@ function applyImageMethods(constructor) {
var files = File.readdirSync(path);
files.forEach(function(filename) {
var basename = Path.basename(filename, ".png");
if (/@2x$/.test(basename) && ~IMAGES.indexOf(basename.slice(0, -3))) {
// High resolution
if (/@3x$/.test(basename) && ~IMAGES.indexOf(basename.slice(0, -3))) {
// High resolution 3x
self.images[basename.replace(/@3x$/, "3x")] = Path.resolve(path, filename);
} else if (/@2x$/.test(basename) && ~IMAGES.indexOf(basename.slice(0, -3))) {
// High resolution 2x
self.images[basename.replace(/@2x$/, "2x")] = Path.resolve(path, filename);
} else if (~IMAGES.indexOf(basename)) {
// Normal resolution
Expand Down
110 changes: 80 additions & 30 deletions lib/pass.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ var HTTP = require("http");
var HTTPS = require("https");
var Path = require("path");
var Zip = require("./zip");
var async = require("async");
var zlib = require("zlib");


// Top-level pass fields.
var TOP_LEVEL = [ "authenticationToken", "backgroundColor", "barcode", "description",
"foregroundColor", "labelColor", "locations", "logoText",
"organizationName", "relevantDate", "serialNumber",
"organizationName", "relevantDate", "serialNumber",
"suppressStripShine", "webServiceURL"];
// These top level fields are required for a valid pass.
var REQUIRED_TOP_LEVEL = [ "description", "organizationName", "passTypeIdentifier",
Expand All @@ -33,9 +34,11 @@ var REQUIRED_IMAGES = [ "icon", "logo" ];

// Create a new pass.
//
// template - The template
// fields - Pass fields (description, serialNumber, logoText)
function Pass(template, fields, images) {
// tempplate - The template
// fields - Pass fields (description, serialNumber, logoText)
function Pass(template, fields, images, mockSignature, mockModifiedDate) {
this.mockSignature = mockSignature
this.mockModifiedDate = mockModifiedDate
this.template = template;
this.fields = cloneObject(fields);
// Structure is basically reference to all the fields under a given style
Expand Down Expand Up @@ -139,7 +142,7 @@ Fields.prototype.add = function(key, label, value, options) {
// key - Field key
// label - Field label (optional)
// value - Field value
// Other field options (e.g. dateStyle)
// Other field options (e.g. dateStyle)
Fields.prototype.get = function(key) {
var fields = this.pass.structure[this.key];
if (fields) {
Expand Down Expand Up @@ -210,6 +213,8 @@ Pass.prototype.pipe = function(output) {
var self = this;
var zip = new Zip(output);
var lastError;
var doneImages = false;
var doneLocals = false;

zip.on("error", function(error) {
lastError = error;
Expand All @@ -228,30 +233,38 @@ Pass.prototype.pipe = function(output) {
// Construct manifest here
var manifest = {};
// Add file to zip and it's SHA to manifest
function addFile(filename) {
var file = zip.addFile(filename);
function addFile(filename, modified) {
var file = zip.addFile(filename, modified);
var sha = new SHAWriteStream(manifest, filename, file);
return sha;
}

// Create pass.json
var passJson = new Buffer(JSON.stringify(this.getPassJSON()), "utf-8");
addFile("pass.json").end(passJson, "utf8");

var expecting = 0;
for (var key in this.images) {
var filename = key.replace(/2x$/, "@2x") + ".png";
addImage(addFile(filename), this.images[key], function(error) {
--expecting;
if (error)
lastError = error;
if (expecting === 0)
doneWithImages();
});
++expecting;
}
addFile("pass.json", self.mockModifiedDate).end(passJson, "utf8");

var pass = this
async.eachSeries(Object.keys(pass.images), function (key, cb) {
var filename = key.replace(/3x$/, "@3x");
filename = filename.replace(/2x$/, "@2x");
filename += ".png";
writeFile(addFile(filename, self.mockModifiedDate), pass.images[key], cb);
}, function(err) {
if (err) {
lastError = err;
doneWithWriting();
} else {
if(!pass.localizations) return doneWithWriting();
async.eachSeries(Object.keys(pass.localizations), function (key, cb) {
writeFile(addFile(key, self.mockModifiedDate), pass.localizations[key], cb);
}, function(err) {
if (err) lastError = err;
doneWithWriting();
})
}
});

function doneWithImages() {
function doneWithWriting() {
if (lastError) {
zip.close();
self.emit("error", lastError);
Expand All @@ -268,7 +281,7 @@ Pass.prototype.pipe = function(output) {
zip.on("error", function(error) {
self.emit("error", error);
});
});
}, self.mockSignature, self.mockModifiedDate);
});
}
}
Expand All @@ -291,7 +304,7 @@ Pass.prototype.render = function(response, callback) {
};


function addImage(file, source, callback) {
function writeFile(file, source, callback) {
if (typeof(source) == "string" || source instanceof String) {
if (/^https?:/i.test(source)) {
// URL
Expand Down Expand Up @@ -331,17 +344,56 @@ function addImage(file, source, callback) {
}
}

function addLocalFile(file, source, callback) {
if (typeof(source) == "string" || source instanceof String) {
if (/^https?:/i.test(source)) {
// URL
var protocol = /^https:/i.test(source) ? HTTPS : HTTP;
protocol.get(source, function(response) {
if (response.statusCode == 200) {
response.on("end", callback);
response.pipe(file);
response.resume();
} else
callback(new Error("Server returned " + response.statusCode + " for " + source));
}).on("error", callback);
} else {
// Assume filename
var stream = File.createReadStream(source);
stream.pipe(file);
file.on("close", callback);
}
} else if (source instanceof Buffer) {
file.on("close", callback);
file.write(source);
file.end();
} else if (typeof(source) == "function") {
try {
source(file);
callback();
} catch (error) {
callback(error);
}
} else {
// image is not a supported type
callback(new Error("Cannot load image " + file.filename + ", must be String (filename), Buffer or function"));
}
}

// Add manifest.json and signature files.
Pass.prototype.signZip = function(zip, manifest, callback) {
Pass.prototype.signZip = function(zip, manifest, callback, mockSignature, mockModifiedDate) {
var json = JSON.stringify(manifest);
// Add manifest.json
zip.addFile("manifest.json").end(json, "utf-8");
zip.addFile("manifest.json", mockModifiedDate).end(json, "utf-8");
// Create signature
signManifest(this.template, json, function(error, signature) {
if (!error) {
// Write signature file
zip.addFile("signature").end(signature);
if (mockSignature) {
zip.addFile("signature", mockModifiedDate).end(mockSignature);
} else {
zip.addFile("signature", mockModifiedDate).end(signature);
}
}
callback(error);
});
Expand All @@ -360,9 +412,7 @@ function signManifest(template, manifest, callback) {
"-passin", "pass:" + template.password
];
var sign = execFile("openssl", args, { stdio: "pipe" }, function(error, stdout, stderr) {
var trimmedStderr = stderr.trim();
// Windows outputs some unhelpful error messages, but still produces a valid signature
if (error || (trimmedStderr && trimmedStderr.indexOf('- done') < 0)) {
if (error) {
callback(new Error(stderr));
} else {
var signature = stdout.split(/\n\n/)[3];
Expand Down
7 changes: 4 additions & 3 deletions lib/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var TEMPLATE = [ "passTypeIdentifier", "teamIdentifier",
// Create a new template.
//
// style - Pass style (coupon, eventTicket, etc)
// fields - Pass fields (passTypeIdentifier, teamIdentifier, etc)
// fields - Pass fields (passTypeIdentifier, teamIdentifier, etc)
function Template(style, fields) {
if (!~STYLES.indexOf(style))
throw new Error("Unsupported pass style " + style);
Expand Down Expand Up @@ -44,14 +44,15 @@ Template.prototype.keys = function(path, password) {


// Create a new pass from a template.
Template.prototype.createPass = function(fields) {
Template.prototype.createPass = function(fields, mocks) {
// Combine template and pass fields
var combined = {};
for (var k1 in this.fields)
combined[k1] = this.fields[k1];
for (var k2 in fields)
combined[k2] = fields[k2];
return new Pass(this, combined, this.images);

return new Pass(this, combined, this.images, mocks && mocks.signature || undefined, mocks && mocks.modifiedDate || undefined);
};


Expand Down