blob: a4ffba99b7eabb705e71b7a0aa1d10cbc13b1797 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// creating the namespace
var org = org || {};
org.apache = org.apache || {};
org.apache.sling = org.apache.sling || {};
org.apache.sling.jcr = org.apache.sling.jcr || {};
org.apache.sling.jcr.js = org.apache.sling.jcr.js || {};
org.apache.sling.jcr.js.nodetypes = org.apache.sling.jcr.js.nodetypes || {};
/*
JSNodeTypes - The JavaScript Node Types library for Apache Sling
The documentation of the library can be found at:
http://www.jcrbrowser.org/sling/libs/jsnodetypes/content/documentation.html
*/
//defining the module
org.apache.sling.jcr.js.nodetypes.NodeTypeManager = (function() {
function NodeTypeManager(settingsParameter){
// copies the setting parameters to the object scope and configures the defaults
var noSettingsProvided = typeof settingsParameter === 'undefined' || settingsParameter == null;
var contextPath = (noSettingsProvided || typeof settingsParameter.contextPath === 'undefined') ? '' : settingsParameter.contextPath;
var defaultNTJsonURL = (noSettingsProvided || typeof settingsParameter.defaultNTJsonURL === 'undefined') ? contextPath+'/libs/jsnodetypes/js/defaultNT/defaultNT.json' : settingsParameter.defaultNTJsonURL;
this.defaultNTJson = getJson(defaultNTJsonURL);
this.nodeTypesJson = (noSettingsProvided || typeof settingsParameter.nodeTypesJson === 'undefined') ? getJson(contextPath+'/libs/jsnodetypes/content/nodetypes.json') : settingsParameter.nodeTypesJson;
initializeNodeTypes(this);
};
function getJson(url){
var result;
var xhr = null;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) { // Older IE.
xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0");
}
xhr.open("GET", url, false/*not async*/);
if (typeof xhr.overrideMimeType != "undefined"){
xhr.overrideMimeType("application/json");
}
xhr.onload = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
result = JSON.parse(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.send(null);
return result;
}
/* adding an indexOf function if it's not available */
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
}
/*
* Adds Object.keys if its not available.
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
*/
if (!Object.keys) {
Object.keys = (function() {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function(obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
}
/*
* This function walks recursively through all parent node types and calls the processing function with the current node type
*
* currentNodeType - the node type to retrieve the property defs from in this call
* processingFunction - the function to call on every node type
* processedNodeTypes - is used to avoid cycles by checking if a node type has been processed already
* iterationProperty - the property of the nodeType that should be used for iteration e.g. 'declaredSupertypes'
*/
function processNodeTypeGraph (currentNodeType, iterationProperty, processingFunction, processedNodeTypes){
if (currentNodeType == null || iterationProperty == null || iterationProperty==="" || processingFunction == null ) return;
var initialCall = typeof processedNodeTypes === 'undefined';
if (initialCall){
processedNodeTypes = [];
}
processingFunction(currentNodeType);
processedNodeTypes.push(currentNodeType.name);
for (var supertypeIndex in currentNodeType[iterationProperty]) {
newNodeTypeName = currentNodeType[iterationProperty][supertypeIndex];
newNodeType = this.getNodeType(newNodeTypeName);
/*
* skip the processing of node types that have already been processed
*/
var notProcessedYet = processedNodeTypes.indexOf(newNodeTypeName) < 0;
if (notProcessedYet){
processNodeTypeGraph.call(this, newNodeType, iterationProperty, processingFunction, processedNodeTypes);
}
}
};
/*
* Sets the value of all properties of defaultNT.json to the corresponding undefined properties of the specified node type.
* E.g. if nt.declaredChildNodeDefinitions[2].allowsSameNameSiblings is undefined it is set to testNodeType.declaredChildNodeDefinitions[0].allowsSameNameSiblings
*/
function setDefaults(nt){
if(typeof nt["declaredSupertypes"] === "undefined" && "nt:base" != nt.name){
nt["declaredSupertypes"] = this.defaultNTJson["declaredSupertypes"];
}
// node type defaults
for(var propName in this.defaultNTJson){
if (propName != "declaredPropertyDefinitions" && propName != "declaredChildNodeDefinitions"){
setDefaultNTProps.call(this, propName);
}
}
// property definition defaults
for(var propName in this.defaultNTJson.declaredPropertyDefinitions[0]){
/*
* Sets the default values from all this.defaultNTJson.declaredPropertyDefinitions[0] properties
* too all properties of all declaredPropertyDefinitions of 'nt'.
*/
for (var propDefIndex in nt.declaredPropertyDefinitions){
setDefaultPropDefProps.call(this, propDefIndex, propName);
}
}
// child node definition defaults
for(var propName in this.defaultNTJson.declaredChildNodeDefinitions[0]){
/*
* Sets the default values from all this.defaultNTJson.declaredChildNodeDefinitions[0] properties
* too all properties of all declaredChildNodeDefinitions of 'nt'.
*/
for (var childNodeDefIndex in nt.declaredChildNodeDefinitions){
setDefaultChildNodeDefProps.call(this, childNodeDefIndex, propName);
}
}
function setDefaultNTProps(propName){
if(typeof nt[propName] === "undefined") nt[propName] = this.defaultNTJson[propName];
}
function setDefaultPropDefProps(index, propName){
if(typeof nt.declaredPropertyDefinitions[index][propName] === "undefined") nt.declaredPropertyDefinitions[index][propName] = this.defaultNTJson.declaredPropertyDefinitions[0][propName];
}
function setDefaultChildNodeDefProps(index, propName){
if(typeof nt.declaredChildNodeDefinitions[index][propName] === "undefined") nt.declaredChildNodeDefinitions[index][propName] = this.defaultNTJson.declaredChildNodeDefinitions[0][propName];
}
};
NodeTypeManager.prototype.internalGetDefaultNodeType = function() {
return this.defaultNTJson;
};
NodeTypeManager.prototype.getNodeTypeNames = function(name) {
var ntNames = [];
for (var ntJson in this.nodeTypesJson) {
ntNames.push(ntJson);
}
return ntNames;
}
NodeTypeManager.prototype.getNodeType = function(name) {
return this.nodeTypesJson[name];
}
function initializeNodeTypes(that){
try {
for (var ntIndex in Object.keys(that.nodeTypesJson)){
var nodeTypeName = Object.keys(that.nodeTypesJson)[ntIndex];
if (typeof that.nodeTypesJson[nodeTypeName] != "undefined") {
that.nodeTypesJson[nodeTypeName].name = nodeTypeName;
/*
* Returns the child node definitions of the node type and those of all inherited node types.
* Definitions with the same child node name are returned if they differ in any other attribute.
*/
that.nodeTypesJson[nodeTypeName].getAllChildNodeDefinitions = function(){
var allCollectedChildNodeDefs = [];
var allCollectedChildNodeDefHashes = [];
processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
if (currentNodeType.declaredChildNodeDefinitions == null) return;
for (var childNodeDefIndex in currentNodeType.declaredChildNodeDefinitions) {
var childNodeDef = currentNodeType.declaredChildNodeDefinitions[childNodeDefIndex];
var childNodeDefName = childNodeDef.name;
var hashCode = childNodeDef.hashCode();
// in case the child has the same child node definition as its parent (supertype)
var processed = allCollectedChildNodeDefHashes.indexOf(hashCode) >= 0;
if (!processed){
allCollectedChildNodeDefHashes.push(hashCode);
allCollectedChildNodeDefs.push(childNodeDef);
}
}
});
return allCollectedChildNodeDefs;
};
/*
* Returns the property definitions of the node type and those of all inherited node types.
* Definitions with the same property name are returned if they differ in any other attribute.
*/
that.nodeTypesJson[nodeTypeName].getAllPropertyDefinitions = function(){
var allCollectedPropertyDefs = [];
var allCollectedPropertyDefHashes = [];
processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
if (currentNodeType.declaredPropertyDefinitions == null) return;
for (var propertyDefIndex in currentNodeType.declaredPropertyDefinitions) {
var propertyDef = currentNodeType.declaredPropertyDefinitions[propertyDefIndex];
var propertyDefName = propertyDef.name;
var hashCode = propertyDef.hashCode();
// in case the child has the same property definition as its parent (supertype)
var processed = allCollectedPropertyDefHashes.indexOf(hashCode) >= 0;
if (!processed){
allCollectedPropertyDefHashes.push(hashCode);
allCollectedPropertyDefs.push(propertyDef);
}
}
});
return allCollectedPropertyDefs;
};
/*
* Returns `true` if a node with the specified node name and node type can be added as a child node of the current node type.
*
* The first parameter is the string of the node name and
* the second parameter is a node type object (not a string).
*/
that.nodeTypesJson[nodeTypeName].canAddChildNode = function(nodeName, nodeTypeToAdd){
if (nodeName==null || nodeTypeToAdd==null) return false;
var allChildNodeDefNames = [];
processApplicableChildNodeTypes(that, this, function(cnDef, nodeTypeName){
if (cnDef.name === nodeName) {
allChildNodeDefNames.push(nodeName);
}
});
var canAddChildNode = false;
processApplicableChildNodeTypes(that, this, function(cnDef, nodeTypeName){
var noNonRisidualWithThatName = allChildNodeDefNames.indexOf(nodeName)<0;
var nodeNameMatches = (nodeName === cnDef.name) || ("*" === cnDef.name && noNonRisidualWithThatName);
var canAddToCurrentCnDef = !cnDef.protected && nodeNameMatches && nodeTypeToAdd.name === nodeTypeName;
canAddChildNode = canAddChildNode || canAddToCurrentCnDef;
});
return canAddChildNode;
}
/*
* Returns `true` if a property with the specified name and type can be to the current node type.
*
* The first parameter is the string of the property name and
* the second parameter is the property type (case insensitive).
*/
that.nodeTypesJson[nodeTypeName].canAddProperty = function(propertyName, propertyType){
if (propertyName == null || propertyType == null) return false;
var allPropertyDefNames = [];
processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
if (currentNodeType.declaredPropertyDefinitions == null) return;
for (var propDefIndex in currentNodeType.declaredPropertyDefinitions) {
var propDef = currentNodeType.declaredPropertyDefinitions[propDefIndex];
allPropertyDefNames.push(propDef.name);
}
});
var canAddProperty = false;
processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
if (currentNodeType.declaredPropertyDefinitions == null) return;
for (var propDefIndex=0; canAddProperty === false && propDefIndex < currentNodeType.declaredPropertyDefinitions.length; propDefIndex++) {
var propDef = currentNodeType.declaredPropertyDefinitions[propDefIndex];
var noNonRisidualWithThatName = allPropertyDefNames.indexOf(propertyName)<0;
var namesMatch = propDef.name === propertyName || ("*" === propDef.name && noNonRisidualWithThatName);
var typesMatch = propDef.requiredType.toLowerCase() === propertyType.toLowerCase() || "undefined" === propDef.requiredType;
var isNotProtected = !propDef.protected;
canAddProperty = namesMatch && typesMatch && isNotProtected;
}
});
return canAddProperty;
};
/*
* Returns all node types that can be used for child nodes of this node type and its super types.
* If a child node definition specifies multiple required primary types an applicable node type has
* to be a subtype of all of them.
* The parameter is a boolean that specifies if mixins should be included or not. If no parameter is passed 'true' is assumed and mixins are
* returned as well.
*/
that.nodeTypesJson[nodeTypeName].getApplicableCnTypesPerCnDef = function(includeMixins){
var allApplChildNodeTypes = {};
processApplicableChildNodeTypes(that, this, function(cnDef, nodeTypeName){
var nodeType = that.getNodeType(nodeTypeName);
if (typeof allApplChildNodeTypes[cnDef.name] === "undefined") {
allApplChildNodeTypes[cnDef.name] = {};
}
var includeAlsoMixins = typeof includeMixins === "undefined" || includeMixins;
if (nodeType.mixin === true && includeAlsoMixins || !nodeType.mixin){
allApplChildNodeTypes[cnDef.name][nodeTypeName] = that.getNodeType(nodeTypeName);
}
});
return allApplChildNodeTypes;
}
};
setDefaults.call(that, that.nodeTypesJson[nodeTypeName]);
initializeChildNodeDefs.call(that);
initializePropertyDefs.call(that);
}
} catch (e){
console.log("Error, the node types JSON is not an object");
};
function itemHashCode(item){
var hash = "";
hash += item.name;
hash += item.autoCreated;
hash += item.mandatory;
hash += item.protected;
hash += item.onParentVersion;
return hash;
}
function initializeChildNodeDefs(){
for (var childNodeDefIndex in that.nodeTypesJson[nodeTypeName].declaredChildNodeDefinitions) {
var childNodeDef = that.nodeTypesJson[nodeTypeName].declaredChildNodeDefinitions[childNodeDefIndex];
childNodeDef.hashCode = function (){
var hashCode = itemHashCode(this);
hashCode += this.allowsSameNameSiblings;
hashCode += this.defaultPrimaryType;
for (var reqPtIndex in this.requiredPrimaryTypes) {
hashCode += this.requiredPrimaryTypes[reqPtIndex];
}
return hashCode;
}
}
}
function initializePropertyDefs(){
for (var propertyIndex in that.nodeTypesJson[nodeTypeName].declaredPropertyDefinitions) {
var propertyDef = that.nodeTypesJson[nodeTypeName ].declaredPropertyDefinitions[propertyIndex];
propertyDef.hashCode = function (){
var hashCode = itemHashCode(this);
for (var defaultValueIndex in this.defaultValues) {
hashCode += this.defaultValues[defaultValueIndex];
}
for (var valueConstraintIndex in this.valueConstraints) {
hashCode += this.valueConstraints[valueConstraintIndex];
}
hashCode += this.requiredType;
hashCode += this.multiple;
return hashCode;
}
}
}
/*
* Navigates to every node types' supertype and sets the subtype property there.
*/
function addSubtypeRelation(nodeTypesJson){
for (var nodeTypeName in nodeTypesJson) {
nodeTypeJson = nodeTypesJson[nodeTypeName];
for (var supertypeIndex in nodeTypeJson.declaredSupertypes) {
var supertypeName = nodeTypeJson.declaredSupertypes[supertypeIndex];
var supertype = this.getNodeType(supertypeName);
if (typeof supertype.subtypes === "undefined") {
supertype.subtypes = [];
}
supertype.subtypes.push(nodeTypeName);
}
}
return nodeTypesJson;
}
addSubtypeRelation.call(that, that.nodeTypesJson);
}
function processApplicableChildNodeTypes(ntManager, nodeType, functionToCall){
var cnDefs = nodeType.getAllChildNodeDefinitions();
for (var cnDefIndex in cnDefs) {
var cnDef = cnDefs[cnDefIndex];
var nodeTypesPerChildNodeDef = {};
var reqChildNodeTypes = cnDef.requiredPrimaryTypes;
for (var reqChildNodeTypeIndex in reqChildNodeTypes) {
var childNodeTypeName = reqChildNodeTypes[reqChildNodeTypeIndex];
var childNodeType = ntManager.getNodeType(childNodeTypeName);
/*
* calls the function for every subtype of 'childNodeType' but skips
* node types that have already been processed (e.g. because of cycles)
*/
processNodeTypeGraph.call(ntManager, childNodeType, 'subtypes', function(currentNodeType){
if (currentNodeType != null) {
// if 'true' the type has not yet been found in _one_of_the_ required primary type's subtype tree
// of the child node definition
var cnDefWithTypeNotYetProcessed = typeof nodeTypesPerChildNodeDef[currentNodeType.name] === "undefined";
// This increments the occurency count of a node type in the subtype hierarchy of a required primary type.
nodeTypesPerChildNodeDef[currentNodeType.name] = cnDefWithTypeNotYetProcessed ? 1 : nodeTypesPerChildNodeDef[currentNodeType.name]+1;
}
});
}
for (var keyIndex in Object.keys(nodeTypesPerChildNodeDef)){
var nodeTypeName = Object.keys(nodeTypesPerChildNodeDef)[keyIndex];
/*
* If the type has been found in all iterations of the required primary types it means it is a subtype
* of all of them and can be used for the child node definition.
*/
var nodeTypeCountInThisChildNodeDef = nodeTypesPerChildNodeDef[nodeTypeName];
var subtypeOfAllReqPrimaryTypes = nodeTypeCountInThisChildNodeDef === cnDef.requiredPrimaryTypes.length;
if (subtypeOfAllReqPrimaryTypes){
functionToCall(cnDef, nodeTypeName);
}
}
}
}
return NodeTypeManager;
}());