"use strict";
/*global require*/
var URI = require("urijs");
var clone = require("terriajs-cesium/Source/Core/clone").default;
var defined = require("terriajs-cesium/Source/Core/defined").default;
var knockout = require("terriajs-cesium/Source/ThirdParty/knockout").default;
var loadJson = require("../Core/loadJson");
var TerriaError = require("../Core/TerriaError");
var CatalogGroup = require("./CatalogGroup");
var inherit = require("../Core/inherit");
var proxyCatalogItemUrl = require("./proxyCatalogItemUrl");
var replaceUnderscores = require("../Core/replaceUnderscores");
var ArcGisFeatureServerCatalogGroup = require("./ArcGisFeatureServerCatalogGroup");
var ArcGisMapServerCatalogGroup = require("./ArcGisMapServerCatalogGroup");
var i18next = require("i18next").default;
/**
* A {@link CatalogGroup} representing a collection of ArcGIS Services,
* eg. http://www.ga.gov.au/gis/rest/services/
*
* @alias ArcGisCatalogGroup
* @constructor
* @extends CatalogGroup
*
* @param {Terria} terria The Terria instance.
*/
var ArcGisCatalogGroup = function(terria) {
CatalogGroup.call(this, terria, "esri-group");
/**
* Gets or sets the URL of the services. This property is observable.
* @type {String}
*/
this.url = "";
/**
* Gets or sets a description of the custodian of the data sources in this group.
* This property is an HTML string that must be sanitized before display to the user.
* This property is observable.
* @type {String}
*/
this.dataCustodian = undefined;
/**
* Gets or sets a hash of names of blacklisted data layers. A layer that appears in this hash
* will not be shown to the user. In this hash, the keys should be the Title of the layers to blacklist,
* and the values should be "true". This property is observable.
* @type {Object}
*/
this.blacklist = undefined;
/**
* Gets or sets a hash of properties that will be set on each child item.
* For example, { 'treat404AsError': false }
*/
this.itemProperties = undefined;
knockout.track(this, ["url", "dataCustodian", "blacklist", "itemProperties"]);
};
inherit(CatalogGroup, ArcGisCatalogGroup);
Object.defineProperties(ArcGisCatalogGroup.prototype, {
/**
* Gets the type of data member represented by this instance.
* @memberOf ArcGisCatalogGroup.prototype
* @type {String}
*/
type: {
get: function() {
return "esri-group";
}
},
/**
* Gets a human-readable name for this type of data source, such as 'Web Map Service (WMS)'.
* @memberOf ArcGisCatalogGroup.prototype
* @type {String}
*/
typeName: {
get: function() {
return i18next.t("models.arcGisService.name");
}
},
/**
* Gets the set of functions used to serialize individual properties in {@link CatalogMember#serializeToJson}.
* When a property name on the model matches the name of a property in the serializers object literal,
* the value will be called as a function and passed a reference to the model, a reference to the destination
* JSON object literal, and the name of the property.
* @memberOf ArcGisCatalogGroup.prototype
* @type {Object}
*/
serializers: {
get: function() {
return ArcGisCatalogGroup.defaultSerializers;
}
}
});
/**
* Gets or sets the set of default serializer functions to use in {@link CatalogMember#serializeToJson}. Types derived from this type
* should expose this instance - cloned and modified if necesary - through their {@link CatalogMember#serializers} property.
* @type {Object}
*/
ArcGisCatalogGroup.defaultSerializers = clone(CatalogGroup.defaultSerializers);
ArcGisCatalogGroup.defaultSerializers.items =
CatalogGroup.enabledShareableItemsSerializer;
ArcGisCatalogGroup.defaultSerializers.isLoading = function(
wmsGroup,
json,
propertyName,
options
) {};
Object.freeze(ArcGisCatalogGroup.defaultSerializers);
ArcGisCatalogGroup.prototype._getValuesThatInfluenceLoad = function() {
return [this.url, this.blacklist];
};
ArcGisCatalogGroup.prototype._load = function() {
// Allow ArcGisCatalogGroup to function like an ArcGisMapServerCatalogGroup or an ArcGisFeatureServerCatalogGroup.
if (/\/MapServer\/?$/i.test(this.url)) {
return ArcGisMapServerCatalogGroup.loadMapServer(this);
} else if (/\/FeatureServer\/?$/i.test(this.url)) {
return ArcGisFeatureServerCatalogGroup.loadFeatureServer(this);
} else {
return loadRest(this);
}
};
function loadRest(catalogGroup) {
// Load as a generic REST endpoint.
var serviceUrl = cleanAndProxyUrl(catalogGroup, catalogGroup.url) + "?f=json";
var terria = catalogGroup.terria;
return loadJson(serviceUrl)
.then(function(serviceJson) {
// Is this really a MapServer REST response?
if (!serviceJson || (!serviceJson.folders && !serviceJson.services)) {
throw new TerriaError({
title: i18next.t("models.arcGisService.invalidServerTitle"),
message: i18next.t("models.arcGisService.invalidServerMessage", {
email:
'<a href="mailto:' +
terria.supportEmail +
'">' +
terria.supportEmail +
"</a>"
})
});
}
var basePath = getBasePath(catalogGroup);
var i;
var folders = serviceJson.folders;
if (defined(folders)) {
for (i = 0; i < folders.length; ++i) {
createGroup(catalogGroup, basePath, folders[i]);
}
}
var services = serviceJson.services;
if (defined(services)) {
for (i = 0; i < services.length; ++i) {
createMapOrFeatureServer(catalogGroup, basePath, services[i]);
}
}
})
.otherwise(function(e) {
throw new TerriaError({
sender: catalogGroup,
title: i18next.t("models.arcGisService.groupNotAvailableTitle"),
message: i18next.t("models.arcGisService.groupNotAvailableMessage", {
cors: '<a href="http://enable-cors.org/" target="_blank">CORS</a>',
appName: terria.appName,
email:
'<a href="mailto:' +
terria.supportEmail +
'">' +
terria.supportEmail +
"</a>"
})
});
});
}
function cleanAndProxyUrl(catalogGroup, url) {
// Strip off the search portion of the URL
var uri = new URI(url);
uri.search("");
var cleanedUrl = uri.toString();
return proxyCatalogItemUrl(catalogGroup, cleanedUrl, "1d");
}
function createGroup(catalogGroup, basePath, folderJson) {
var localName = removePathFromName(basePath, folderJson);
var newGroup = new ArcGisCatalogGroup(catalogGroup.terria);
newGroup.name = replaceUnderscores(localName);
newGroup.url = new URI(catalogGroup.url).segment(localName).toString();
newGroup.dataCustodian = catalogGroup.dataCustodian;
newGroup.blacklist = catalogGroup.blacklist;
if (defined(catalogGroup.itemProperties)) {
newGroup.updateFromJson(catalogGroup.itemProperties);
}
catalogGroup.add(newGroup);
return newGroup;
}
var validServerTypes = ["MapServer", "FeatureServer"];
function createMapOrFeatureServer(catalogGroup, basePath, serviceJson) {
var serverTypeIndex = validServerTypes.indexOf(serviceJson.type);
if (serverTypeIndex < 0) {
return;
}
var localName = removePathFromName(basePath, serviceJson.name);
var group;
if (serverTypeIndex === 0) {
group = new ArcGisMapServerCatalogGroup(catalogGroup.terria);
} else {
group = new ArcGisFeatureServerCatalogGroup(catalogGroup.terria);
}
group.name = replaceUnderscores(localName);
group.url = new URI(catalogGroup.url)
.segment(localName)
.segment(serviceJson.type)
.toString();
group.dataCustodian = catalogGroup.dataCustodian;
group.blacklist = catalogGroup.blacklist;
if (defined(catalogGroup.itemProperties)) {
for (var propertyName in catalogGroup.itemProperties) {
if (catalogGroup.itemProperties.hasOwnProperty(propertyName)) {
group[propertyName] = catalogGroup.itemProperties[propertyName];
}
}
}
catalogGroup.add(group);
return group;
}
function getBasePath(catalogGroup) {
var match = /rest\/services\/(.*)/i.exec(catalogGroup.url);
if (match && match.length > 1) {
return match[1];
} else {
return "";
}
}
function removePathFromName(basePath, name) {
if (!basePath && basePath.length === 0) {
return name;
}
var index = name.indexOf(basePath);
if (index === 0) {
return name.substring(basePath.length + 1);
} else {
return name;
}
}
module.exports = ArcGisCatalogGroup;