allume
Version:
A cross-platform package bootloader for javascript.
946 lines (866 loc) • 74.5 kB
JavaScript
/////////////////////////////////////////////////////////////////////////////////////
//
// module 'cc.pkx.0.2.1/'
//
/////////////////////////////////////////////////////////////////////////////////////
(function(using, require) {
define.parameters = {};
define.parameters.wrapped = true;
define.parameters.system = "pkx";
define.parameters.id = "cc.pkx.0.2.1/";
define.parameters.pkx = {
"name": "cc.pkx",
"version": "0.2.1",
"title": "PKX Module Library",
"description": "Library for loading PKX modules, and working with PKX packages.",
"pkx": {
"main": "pkx.js",
"dependencies": [
"cc.host.0.2",
"cc.io.0.2",
"cc.io.format.tar.0.2",
"cc.io.format.gzip.0.2",
"cc.log.0.2",
"cc.string.0.2",
"cc.object.0.2",
"cc.boolean.0.2",
"cc.array.0.2",
"cc.type.0.2",
"cc.version.0.2",
"cc.event.0.2",
"cc.validate.0.2"
]
}
};
define.parameters.dependencies = [ "pkx", "module", "configuration", "requirer" ];
define.parameters.dependencies[0] = define.parameters.pkx;
define.parameters.dependencies.push(define.cache.get("cc.host.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.io.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.io.format.tar.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.io.format.gzip.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.log.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.string.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.object.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.boolean.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.array.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.type.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.version.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.event.0.2/"));
define.parameters.dependencies.push(define.cache.get("cc.validate.0.2/"));
using = define.getUsing(define.parameters.id);
require = define.getRequire(define.parameters.id, require);
/////////////////////////////////////////////////////////////////////////////////////////////
//
// cc.pkx
//
// Library for loading PKX modules, and working with PKX packages.
//
// License
// Apache License Version 2.0
//
// Copyright Nick Verlinden (info@createconform.com)
//
/////////////////////////////////////////////////////////////////////////////////////////////
(function() {
var PKX_SYSTEM = "pkx";
var DEPENDENCY_PKX = PKX_SYSTEM;
var DEPENDENCY_CONFIG = "configuration";
var DEPENDENCY_REQUIRER = "requirer";
function PKX(pkx, module, configuration) {
var self = this;
this.repositoryURL = "";
this.repositoryResolveLocal = false; // if set to true, dependencies will be resolved relative to root of package (embedded).
if (!configuration && typeof define != "undefined" && define.parameters.configuration) {
configuration = define.parameters.configuration;
}
if (configuration && configuration.repository) {
this.repositoryURL = configuration.repository;
if (this.repositoryURL.substr(this.repositoryURL.length - 1) != "/") {
this.repositoryURL += "/";
}
}
var init = false;
var processors = {};
var processing = {};
var repositories = [];
var volumes = [];
var requested = {};
var host;
var event;
var io;
var log;
var string;
var object;
var boolean;
var array;
var tar;
var type;
var validate;
var gzip;
var version;
this.PKX_SYSTEM = PKX_SYSTEM;
this.PKX_FILE_EXTENSION = "." + PKX_SYSTEM;
this.PKX_DESCRIPTOR_FILENAME = "package.json";
this.DEPENDENCY_PKX = DEPENDENCY_PKX;
this.DEPENDENCY_CONFIG = DEPENDENCY_CONFIG;
this.PROTOCOL_PKX = PKX_SYSTEM;
this.OPERATION_FETCH_PKX = "pkx-operation-fetch-" + PKX_SYSTEM;
this.ERROR_INVALID_PKX_VOLUME = "pkx-error-invalid-volume";
this.ERROR_INVALID_PKX_DESCRIPTOR = "pkx-error-invalid-descriptor";
this.ERROR_INVALID_PKX_SELECTOR = "pkx-error-invalid-selector";
this.ERROR_INVALID_REPOSITORY = "pkx-error-invalid-repository";
this.ERROR_INVALID_REQUEST_PROCESSOR = "pkx-error-invalid-request-processor";
this.ERROR_TARGET_MISMATCH = "pkx-error-target-mismatch";
this.ERROR_NO_ENTRY_POINT = "pkx-error-no-entry-point";
this.ERROR_DEPENDENCY = "pkx-error-dependency";
var INDENT_OFFSET = 4;
function findVolume(selector) {
var id = selector.package + "/" + (selector.resource? selector.resource : "");
for (var v in volumes) {
if (version.compare(id, v, selector.upgradable) || id == v) {
return volumes[v];
}
}
}
this.load = function(request, handler) {
var loader = null;
var selector;
try {
selector = new self.PKXSelector(request);
}
catch(e) {
if (e instanceof Error && e.name == self.ERROR_INVALID_PKX_SELECTOR) {
throw new RangeError(e.message);
}
else {
throw e;
}
}
function fetch(callback, fail) {
// check target
try {
validate(selector.target, object)
.when(object.compare, host);
}
catch(e) {
if (selector.optional) {
// gracefully stop
callback(null, true);
return;
}
else {
throw new Error(self.ERROR_TARGET_MISMATCH, "Target does not match, and the request is not optional.", selector.target);
}
}
// change relative paths for embedded resources
if (selector.package.substr(0, 2) == "./" && handler && handler.context) {
selector.package = "pkx:///" + handler.context.id.substr(0, handler.context.id.indexOf("/")) + (selector.package.length > 2 ? "/" + selector.package.substr(2) : "");
}
var pkxVolume;
function fetchModule(options) {
// check define cache for existing module
if (typeof define != "undefined" && define.using && !selector.raw) {
var cached = define.cache.get(selector.id, selector.upgradable);
if (cached) {
complete(cached);
return;
}
}
// if scheme is pkx, then it's most likely a dependency
if (selector.uri.scheme == "pkx") {
for (var v in volumes) {
if (selector.uri.path && volumes[v].pkx.id == selector.uri.path.substr(1)) {
pkxVolume = volumes[v];
getPackageDependencies(pkxVolume);
return;
}
}
}
// get existing volume for package
var packageId = selector.package + "/" + (selector.resource? selector.resource : "");
pkxVolume = findVolume(selector);
// create new volume
if (!pkxVolume) {
pkxVolume = new self.PKXVolume(selector.uri, options, selector.id);
pkxVolume.events.addEventListener(io.EVENT_VOLUME_INITIALIZATION_PROGRESS, progress);
// add the volume already (during initialisation)
var volAlreadyExists = volumes[packageId]? true : false;
if (!volAlreadyExists) {
volumes[packageId] = pkxVolume;
}
pkxVolume.then(function (volume) {
// delete temp vol
if (volume.pkx.id != packageId && !volAlreadyExists) {
delete volumes[packageId];
}
// register volume
io.volumes.register(volume);
getPackageDependencies(volume);
}, error);
}
else {
pkxVolume.ready().then(getPackageDependencies).catch(error);
}
function getPackageDependencies(volume) {
var requests = [];
var pkxDeps = volume.pkx.pkx.dependencies;
if (selector.raw && selector.ignoreDependencies) {
getResourceFromVolume();
return;
}
else {
for (var d in pkxDeps) {
switch (type.getType(pkxDeps[d])) {
case type.TYPE_OBJECT:
if (pkxDeps[d].system &&
pkxDeps[d].system != PKX_SYSTEM) {
requests[d] = pkxDeps[d];
}
// fallthrough intended
case type.TYPE_STRING:
requests[d] = new self.PKXSelector(pkxDeps[d]);
if (selector.raw) {
requests[d].wrap = selector.wrap;
requests[d].raw = true;
}
break;
default:
// unknown system
requests[d] = pkxDeps[d];
}
// modify relative uri for embedded packages
var embedded;
if (requests[d].package.substr(0, 2) == "./") {
requests[d].package = "pkx:///" + volume.pkx.id + (requests[d].package.length > 2 ? "/" + requests[d].package.substr(2) : "");
embedded = true;
}
else if (volume.localId.lastIndexOf("/") == volume.localId.length - 1 && self.repositoryResolveLocal) {
requests[d].package = "pkx:///" + volume.pkx.id + "/" + requests[d].package;
embedded = true;
}
if (requests[d].wrap) {
requests[d].ignoreCache = true;
}
// skip circular, embedded dependencies
var selUri = selector.uri.toString();
selUri = selUri.substr(0, selUri.indexOf("/",7)) || selector.uri.toString();
if (embedded && selector.uri.scheme == "pkx" && selUri == "pkx:///" + volume.pkx.id) {
requests[d] = null;
}
// modify package url if parent is not an archive (for debugging)
// fix this code below if you want non-archive packages to load it's dependencies directly from the code in the subfolders instead of fetching from repo.
// The reason the code below is commented, is because if you use embedded packages the code below breaks it.
// Specifically in node.js.
/*if (!selector.isArchive) {
var name = "";
var nameParts = requests[d].package.substr(requests[d].package.lastIndexOf("/") + 1).split(".");
for (var i = 0; i < nameParts.length; i++) {
if (isNaN(nameParts[i])) {
name += (name != "" ? "." : "") + nameParts[i];
}
}
requests[d].package = requests[d].package.substr(0, requests[d].package.lastIndexOf("/") + 1) + name + "/";
}*/
}
}
// filter out removed dependencies
var filtered = [];
for (var r in requests) {
if (requests[r]) {
filtered.push(requests[r]);
}
}
requests = filtered;
if (typeof using === "undefined") {
error(new Error("It seems that using.js is missing. Please make sure it is loaded."));
return;
}
using.apply(this, requests).then(getResourceFromVolume, function(loader) {
var halt;
var mods = [];
for (var r in loader.requests) {
if (loader.requests[r].err.length > 0 && !loader.requests[r].request.optional) {
halt = true;
}
else {
mods.push(loader.requests[r].module);
}
}
if (!halt) {
getResourceFromVolume.apply(this, mods);
return;
}
error(new Error(self.ERROR_DEPENDENCY, "", loader));
}, true);
}
function getResourceFromVolume() {
// find out which resource to load
var resource = null;
if (selector.resource) {
resource = selector.resource;
}
else {
// get first matching main (by target)
if (type.isString(pkxVolume.pkx.pkx.main)) {
resource = pkxVolume.pkx.pkx.main;
}
else if (pkxVolume.pkx.pkx.main) {
for (var m in pkxVolume.pkx.pkx.main) {
if(pkxVolume.pkx.pkx.main[m].target) {
try {
validate(pkxVolume.pkx.pkx.main[m].target, object)
.when(object.compare, host);
resource = pkxVolume.pkx.pkx.main[m].resource;
}
catch(e) {
// ignore for now
}
}
}
}
}
if (!resource) {
error(new Error(self.ERROR_NO_ENTRY_POINT, "Package '" + pkxVolume.pkx.id + "' does not have an entry point defined for the current target."));
return;
}
if (resource.substr(0,1) != "/") {
resource = "/" + resource;
}
var dependencies = [];
for (var a=0;a<arguments.length;a++) {
// dependency module
dependencies[a] = arguments[a];
}
if (!requested[pkxVolume.pkx.id + resource] && !selector.raw) {
requested[pkxVolume.pkx.id + resource] = true;
}
else if (!selector.raw) {
define.cache.waitFor(pkxVolume.pkx.id + (selector.resource || pkxVolume.pkx.id.substr(pkxVolume.pkx.id.length - 1) == "/"? selector.resource : "/"), complete);
return;
}
pkxVolume.open(resource).then(function readDataFromResourceStream(stream) {
if (selector.raw && !selector.wrap) {
complete(stream, dependencies);
return;
}
stream.events.addEventListener(io.EVENT_STREAM_READ_PROGRESS, progress);
stream.readAsString().then(function processCode(data) {
// remove progress listener
stream.events.removeEventListener(progress);
var name = stream.getName();
var ext = name.substr(stream.getName().lastIndexOf(".") + 1);
var raw = ext != "js" && ext != "json" && (ext != "css" || (ext == "css" && !host.isRuntimeBrowserFamily() && host.runtime != host.RUNTIME_NWJS));
// wrap code for define
if (ext == "js") {
data = self.load.wrap(data, pkxVolume.pkx, selector.resource, pkxVolume.pkx.pkx.dependencies, selector.configuration, host.runtime != host.RUNTIME_NODEJS, pkxVolume.pkx.id + resource, selector.raw && selector.wrap);
}
if (selector.raw || raw) {
complete(new io.BufferedStream(data.toUint8Array()), dependencies);
return;
}
// add dependencies
define.parameters = {};
define.parameters.system = PKX_SYSTEM;
define.parameters.id = pkxVolume.pkx.id + (selector.resource || pkxVolume.pkx.id.substr(pkxVolume.pkx.id.length - 1) == "/"? selector.resource : "/");
define.parameters.pkx = pkxVolume.pkx;
define.parameters.dependencies = [ DEPENDENCY_PKX, "module", DEPENDENCY_CONFIG ];
define.parameters.dependencies[0] = pkxVolume.pkx;
for(var d in dependencies) {
define.parameters.dependencies.push(dependencies[d]);
}
if (ext == "js") {
// load code
if (host.isRuntimeNodeFamily() && !host.isRuntimeBrowserFamily()) {
try {
require("vm").runInThisContext(data, {filename: (selector.uri.scheme == "file"? process.cwd() : "") + resource, lineOffset: -1});
}
catch (e) {
error(e);
return;
}
complete(null, null, true);
}
else if (host.isRuntimeBrowserFamily()) {
var script = document.createElement("script");
script.language = "javascript";
script.type = "text/javascript";
script.text = data;
try {
document.body.appendChild(script);
}
catch (e) {
error(e);
return;
}
complete(null, null, true);
}
else {
error(new Error("Loading code in runtime '" + host.runtime + "' is not supported."));
}
}
if (ext == "json") {
var json;
try {
json = JSON.parse(data);
}
catch (e) {
error(e);
return;
}
complete(json, dependencies);
}
if (ext == "css") {
if (typeof document !== "undefined") {
data += "\r\n/*# sourceURL=http://" + (pkxVolume.pkx.id + resource) + "*/";
var style = document.createElement("style");
style.rel = "stylesheet";
style.type = "text/css";
if (style.styleSheet) {
style.styleSheet.cssText = data;
}
else {
style.appendChild(document.createTextNode(data));
}
try {
document.head.appendChild(style);
}
catch (e) {
error(e);
return;
}
}
complete(data, dependencies);
}
}, error);
}, error);
}
var waiters = [];
function complete(fact, dependencies, checkWait) {
if (loader) {
// check for delayed loading
if (checkWait && define.parameters.wait) {
waiters = define.parameters.wait;
for (var w in waiters) {
waiters[w].then(complete, error);
}
return;
}
else {
// wait for all waiters to complete
var allDone = true;
for (var w in waiters) {
if (!waiters[w].done) {
allDone = false;
break;
}
}
if (!allDone) {
return;
}
}
if (!fact) {
fact = define.cache.get(pkxVolume.pkx.id + (request.resource? request.resource : "/"));
}
else if (!(fact instanceof define.Module)) {
var mod = new define.Module();
mod.id = pkxVolume.pkx.id + (selector.resource || pkxVolume.pkx.id.substr(pkxVolume.pkx.id.length - 1) == "/"? selector.resource : "/");
mod.factory = fact;
mod.dependencies = dependencies || [];
mod.parameters = {
"id" : pkxVolume.pkx.id + (request.resource? request.resource : "/"),
"system" : PKX_SYSTEM,
"pkx" : pkxVolume.pkx
};
fact = mod;
}
callback(fact);
return;
}
callback(fact);
}
var lastPVol = null;
var lastPStr = null;
var lastPercentage = null;
function progress(sender, p) {
if (loader) {
if (sender == pkxVolume) {
lastPVol = p.percentage;
}
if (sender == resStr) {
lastPStr = p.percentage;
}
var currentPercentage = ((lastPVol? lastPVol : 0) + (lastPStr? lastPStr : 0)) / 2;
if (lastPercentage != currentPercentage) {
lastPercentage = currentPercentage;
loader.progress = new type.Progress({
"percentage" : currentPercentage,
"operation" : {
"type" : self.OPERATION_FETCH_PKX
},
"emitter" : sender
});
}
}
}
}
function error(e) {
if (loader) {
loader.err.push(e);
if (!loader.module && pkxVolume) {
loader.module = new define.Module();
loader.module.id = (pkxVolume.pkx? pkxVolume.pkx.id : (request.package? request.package : request)) + (request.resource || (pkxVolume.pkx && pkxVolume.pkx.id.substr(pkxVolume.pkx.id.length - 1) == "/")? request.resource : "/");
if (pkxVolume.pkx) {
loader.module.parameters = {
"id": pkxVolume.pkx.id + (request.resource ? request.resource : "/"),
"system": PKX_SYSTEM,
"pkx": pkxVolume.pkx
}
}
}
callback();
return;
}
fail(e);
}
// find a processor
var selProcID = (typeof selector === "string")? selector : selector.package;
for (var p in processors) {
var promise = processors[p](selector);
if (!promise) {
continue;
}
if (processing[selProcID]) {
// subscribe to existing processor
processing[selProcID].addEventListener("ready", function(sender, a) {
// the selector uri could be updated, so propagate the update
selector.uri = processing[selProcID].selector.uri;
fetchModule(a); }
);
processing[selProcID].addEventListener("error", function(sender, e) {
error(e);
} );
return;
}
// create new event emitter
processing[selProcID] = new event.Emitter(promise);
processing[selProcID].selector = selector;
function createReadyCallback(id) {
return function(a) { processing[id].fire("ready", a); delete processing[id]; fetchModule(a); }
}
function createErrorCallback(id) {
return function(e) { processing[id].fire("error", e); error(e); }
}
promise.then(createReadyCallback(selProcID), createErrorCallback(selProcID));
return;
}
// no processor was found
try {
selector = new self.PKXSelector(request, true);
}
catch(e) {
error(e);
return;
}
fetchModule();
}
if (typeof define != "undefined" && define.using) {
loader = new define.Loader(request, fetch);
return loader;
}
else {
return new Promise(function(resolve, refuse) {
fetch(resolve, refuse);
});
}
};
this.load.factory = function(module, factory, request, requirer) {
// decorate dependencies
var dependencies = module.dependencies.slice(0);
var args = [];
// add package object to front if there was no string left in dependencies to process
var addPkx = true;
for (var d in dependencies) {
if (!isNaN(d)) {
if (Object.prototype.toString.call(dependencies[d]) !== "[object String]") {
if (dependencies[d] === module.parameters.pkx) {
addPkx = false;
break;
}
}
}
}
if (addPkx) {
args.push(module.parameters.pkx);
}
// Replace the pkx descriptor or pkx placeholder with an instance of PKX, unless type, string and validate
// are not loaded yet. This means that when they are first instantiated by this module, they will not have
// the functions available to a PKX instance. This is not a disaster since those base libraries do not use
// them. Any instances created after the initial load will have them available, except if they're singleton.
for (var d in dependencies) {
if ((dependencies[d] === module.parameters.pkx ||
!isNaN(d) && dependencies[d] == DEPENDENCY_PKX) &&
type && string && object && array && boolean && validate) {
dependencies[d] = new self.PKX(dependencies[d]);
}
if (!isNaN(d) && dependencies[d] == DEPENDENCY_CONFIG) {
dependencies[d] = request && request.configuration? request.configuration : (module.parameters.configuration? module.parameters.configuration : {});
}
if (!isNaN(d) && dependencies[d] == DEPENDENCY_REQUIRER) {
dependencies[d] = requirer;
}
}
// add other dependencies (only numbered index)
for (var a=0;a<dependencies.length;a++) {
args.push(dependencies[a]);
}
// execute module factory
return factory.apply(factory, args);
};
this.load.wrap = function(code, pkx, resourceId, dependencies, configuration, addSourceMap, sourceMapName, isEmbedded) {
if (!resourceId) {
resourceId = "/";
}
var depStr = "";
if (isEmbedded) {
}
// stringify the package definition (pretty print)
var descriptor = JSON.stringify(pkx, null, Array(INDENT_OFFSET + 1).join(" "));
// generate module definition code
var moduleCode = "(function(using, require) {\n";
if (isEmbedded) {
// add configuration
configuration = configuration? "\ndefine.parameters.configuration = " + JSON.stringify(configuration, null, Array(INDENT_OFFSET + 1).join(" ")) + ";" : "";
// add package dependency
depStr += "\ndefine.parameters.dependencies = [ \"" + DEPENDENCY_PKX + "\", \"module\", \"" + DEPENDENCY_CONFIG + "\", \"" + DEPENDENCY_REQUIRER + "\" ];";
depStr += "\ndefine.parameters.dependencies[0] = define.parameters.pkx;";
if (dependencies) {
for (var t = 0; t < dependencies.length; t++) {
var dep = dependencies[t];
if (dep) {
var resName = dep + "/";
if (Object.prototype.toString.call(dep) === "[object Object]") {
resName = dep.package + (dep.resource ? dep.resource : "/");
}
depStr += "\ndefine.parameters.dependencies.push(define.cache.get(\"" + resName + "\"));";
}
}
}
moduleCode += "define.parameters = {};\n" + (isEmbedded? "define.parameters.wrapped = true;\n" : "") + "define.parameters.system = \"" + PKX_SYSTEM + "\";\ndefine.parameters.id = \"" + pkx.id + resourceId + "\";\ndefine.parameters.pkx = " + descriptor + ";" + depStr + configuration + "\n";
}
moduleCode += "using = define.getUsing(define.parameters.id);\n";
moduleCode += "require = define.getRequire(define.parameters.id, require);\n";
moduleCode += code + "\n})(typeof using != \"undefined\"? using : null, typeof require != \"undefined\"? require : null);" + (addSourceMap? "\n//# sourceURL=http://" + (sourceMapName? sourceMapName : (pkx.id + resourceId)) : "");
// add code indent to make it pretty
var moduleLines = moduleCode.split(/\r*\n/);
var indentWhiteSpace = Array(INDENT_OFFSET + 1).join(" ");
// add header to make it pretty
if (isEmbedded) {
moduleCode = "/////////////////////////////////////////////////////////////////////////////////////\n";
moduleCode += "//\n";
moduleCode += "// module '" + pkx.id + resourceId + "'\n";
moduleCode += "//\n";
moduleCode += "/////////////////////////////////////////////////////////////////////////////////////\n";
moduleCode += moduleLines[0] + "\n";
for (var l=1;l<moduleLines.length - (addSourceMap? 2 : 1);l++) {
moduleCode += indentWhiteSpace + moduleLines[l] + "\n";
}
if (addSourceMap) {
moduleCode += moduleLines[moduleLines.length - 2] + "\n";
}
moduleCode += moduleLines[moduleLines.length - 1] + "\n";
}
return moduleCode;
};
this.load.addRepository = function(name, url) {
if (url.substr(url.length - 1) != "/") {
url += "/";
}
if (!name || name == "") {
if (self.repositoryURL) {
throw new Error(self.ERROR_INVALID_REPOSITORY, "Main repository is already registered to '" + repositories[name] + "'.");
}
self.repositoryURL = url;
return;
}
if (repositories[name]) {
throw new Error(self.ERROR_INVALID_REPOSITORY, "Repository '" + name + "' is already registered to '" + repositories[name] + "'.");
}
repositories[name] = url;
};
this.load.addRequestProcessor = function(name, fn) {
if (processors[name]) {
throw new Error(self.ERROR_INVALID_REQUEST_PROCESSOR, "A processor with name '" + name + "' is already registered.");
}
processors[name] = fn;
};
this.PKXSelector = function(selector, validateId) {
var own = this;
var errName = self.ERROR_INVALID_PKX_SELECTOR;
var addDefaultResource = false;
var valid = true; // indicates invalid naming pattern
var majorVersion = null;
var minorVersion = null;
var patchVersion = null;
var repository = null;
switch(type.getType(selector)) {
case type.TYPE_STRING:
selector = { "package" : selector };
addDefaultResource = true;
break;
case type.TYPE_OBJECT:
break;
default:
throw new Error(errName, "Mandatory parameter 'selector' should be of type 'String' or 'Object'.");
}
// name
try {
validate(selector.package, string, errName, "package")
.when(string.isURL);
}
catch(e) {
validate(selector.package, string, errName, "package")
.allow(string.LOWERCASE_LETTER, string.DIGIT, string.DASH, string.DOT, string.SLASH)
.notNull();
}
// verify naming pattern
var idxSlash = selector.package.lastIndexOf("/", selector.package.length - 2);
// when a slash is detected trim off pkx extension if present
var parts = (idxSlash >= 0? selector.package.substring(idxSlash + 1, selector.package.lastIndexOf(self.PKX_FILE_EXTENSION) == selector.package.length - self.PKX_FILE_EXTENSION.length? selector.package.length - self.PKX_FILE_EXTENSION.length: null) : selector.package).split(".");
if (parts.length > 0 && parts[parts.length - 1].substr(parts[parts.length - 1].length - 1) == "/") {
parts[parts.length - 1] = parts[parts.length - 1].substr(0,parts[parts.length - 1].length - 1);
}
// if package does not contain any slashes, assume it is an id
if (selector.package.indexOf("/") == -1) {
if (!isNaN(parts[parts.length - 1]) && !isNaN(parts[parts.length - 3])) {
patchVersion = isNaN(parts[parts.length - 1]);
}
minorVersion = parts[parts.length - (isNaN(parts[parts.length - 3]) ? 1 : 2)];
if (isNaN(minorVersion)) {
minorVersion = null;
valid = false;
}
majorVersion = parts[parts.length - (isNaN(parts[parts.length - 3]) ? 2 : 3)];
if (isNaN(majorVersion)) {
majorVersion = null;
valid = false;
}
}
if (validateId && !valid) {
throw new Error(errName, "Mandatory property 'package' does not match the " + PKX_SYSTEM + " naming pattern.");
}
validate(selector.resource, string, errName)
.when(string.isPath);
validate(selector.target, object, errName);
validate(selector.raw, boolean, errName);
validate(selector.wrap, boolean, errName);
validate(selector.system, string, errName);
validate(selector.optional, boolean, errName);
// normalise path (add leading slash)
if (selector.resource && selector.resource.length > 0 && selector.resource.substr(0,1) != "/") {
selector.resource = "/" + selector.resource;
}
var uri;
var vers = {};
Object.defineProperty(vers, "major", {
get: function() {
return majorVersion;
}
});
Object.defineProperty(vers, "minor", {
get: function() {
return minorVersion;
}
});
Object.defineProperty(vers, "patch", {
get: function() {
return patchVersion;
}
});
Object.defineProperty(this, "version", {
get: function() {
return vers;
}
});
Object.defineProperty(this, "id", {
get: function() {
return own.package + (addDefaultResource? "/" : (own.resource || ""));
}
});
Object.defineProperty(this, "name", {
get: function() {
// get full name (strip version numbers from id string)
var name = "";
var nameParts = own.package.split(".");
for (var i = 0; i < nameParts.length; i++) {
if (isNaN(nameParts[i])) {
name += (name != "" ? "." : "") + nameParts[i];
}
}
return name;
}
});
Object.defineProperty(this, "repository", {
get: function() {
return repository;
}
});
Object.defineProperty(this, "uri", {
get: function() {
return uri;
},
set: function(u) {
if (typeof u === "string") {
uri = io.URI.parse(replaceVariables(u));
}
else if ((u instanceof io.URI)) {
uri = u;
}
else {
throw new Error("The URI should be of type 'String' or 'URI'.");
}
}
});
Object.defineProperty(this, "isArchive", {
get: function() {
return own.package.lastIndexOf("/") != own.package.length - 1;
}
});
this.package = selector.package;
this.resource = selector.resource;
this.target = selector.target;
this.raw = selector.raw || false;
this.wrap = selector.wrap || false;
this.system = selector.system;
this.optional = selector.optional || false;
this.upgradable = selector.upgradable || (using? using.UPGRADABLE_NONE : null);
this.ignoreDependencies = selector.ignoreDependencies || false;
this.configuration = selector.configuration || null;
this.parseURI = function(u, namespaceSeperator) {
return io.URI.parse(replaceVariables(u, namespaceSeperator));
};
function replaceVariables(u, namespaceSeperator) {
// add .pkx extension
var uriPKXName = (namespaceSeperator? own.package.replace(/\./g,namespaceSeperator) : own.package) + (own.package.lastIndexOf(self.PKX_FILE_EXTENSION) != own.package.length - self.PKX_FILE_EXTENSION.length && own.isArchive ? self.PKX_FILE_EXTENSION : "");
// replace variables
return u.replace(/\$NAME_NO_NS/g, (namespaceSeperator? own.name.replace(/\./g,namespaceSeperator) : own.name).substr(own.repository && own.repository.namespace && own.repository.namespace.length > 0? own.repository.namespace.length + 1 : 0))
.replace(/\$NAME/g, (namespaceSeperator? own.name.replace(/\./g,namespaceSeperator) : own.name))
.replace(/\$PATCH/g, patchVersion)
.replace(/\$MINOR/g, minorVersion)
.replace(/\$MAJOR/g, majorVersion)
.replace(/\$PACKAGE/g, uriPKXName)
.replace(/\$ID/g, (namespaceSeperator? own.package.replace(/\./g,namespaceSeperator) : own.package));
}
// find repository
var protocolDetected = selector.package.indexOf("://") != -1;
if (protocolDetected || ((host.runtime == host.RUNTIME_NODEJS || host.runtime == host.RUNTIME_NWJS) && selector.package.indexOf(".") == 0)) {
repository = {"namespace": "", "url": ""};
}
else {
var reposSorted = version.sort(repositories, "desc");
for (var r in reposSorted) {
if (selector.package.indexOf(reposSorted[r] + ".") == 0) {
repository = {"namespace": reposSorted[r], "url": repositories[reposSorted[r]]};
}
}
if (!repository) {
repository = {"namespace": "", "url": self.repositoryU