(function(window)
{
var Gitana = window.Gitana;
Gitana.AbstractNode = Gitana.AbstractRepositoryObject.extend(
/** @lends Gitana.AbstractNode.prototype */
{
/**
* @constructs
* @augments Gitana.AbstractRepositoryObject
*
* @class Abstract base class for Gitana Node implementations.
*
* @param {Gitana.Branch} branch
* @param {Object} [object]
*/
constructor: function(branch, object)
{
// helper methods for system-managed state
this.__qname = (function() {
var _qname = null;
return function(qname) {
if (!Gitana.isUndefined(qname)) { _qname = qname; }
return _qname;
};
})();
this.__type = (function() {
var _type = null;
return function(type) {
if (!Gitana.isUndefined(type)) { _type = type; }
return _type;
};
})();
this.__features = (function() {
var _features = {};
return function(features) {
if (!Gitana.isUndefined(features)) { _features = features; }
return _features;
};
})();
this.__stats = (function() {
var _stats = {};
return function(stats) {
if (!Gitana.isUndefined(stats)) { _stats = stats; }
return _stats;
};
})();
this.__is_association = (function() {
var _is_association = false;
return function(is_association) {
if (!Gitana.isUndefined(is_association)) { _is_association = is_association; }
return _is_association;
};
})();
// now call base
// important this happens AFTER helpers above so that handleSystemProperties works
this.base(branch.getRepository(), object);
//////////////////////////////////////////////////////////////////////////////////////////////
//
// PRIVILEGED METHODS
//
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the Gitana Branch object.
*
* @inner
*
* @returns {Gitana.Branch} The Gitana Branch object
*/
this.getBranch = function() { return branch; };
/**
* Gets the Gitana Branch id.
*
* @inner
*
* @returns {String} The Gitana Branch id
*/
this.getBranchId = function() { return branch.getId(); };
},
/**
* @override
*/
getUri: function()
{
return "/repositories/" + this.getRepositoryId() + "/branches/" + this.getBranchId() + "/nodes/" + this.getId();
},
/**
* @override
*/
ref: function()
{
return "node://" + this.getPlatformId() + "/" + this.getRepositoryId() + "/" + this.getBranchId() + "/" + this.getId();
},
/**
* @override
*/
clone: function()
{
return this.getFactory().node(this.getBranch(), this);
},
/**
* @override
*/
handleSystemProperties: function(response)
{
this.base(response);
// strip out "_qname"
if (this["_qname"])
{
var _qname = this["_qname"];
delete this["_qname"];
this.__qname(_qname);
}
// strip out "_type"
if (this["_type"])
{
var _type = this["_type"];
delete this["_type"];
this.__type(_type);
}
// strip out "_features"
if (this["_features"])
{
var _features = this["_features"];
delete this["_features"];
this.__features(_features);
}
// strip out "_statistics"
if (this["_statistics"] && typeof(this["_statistics"]) == "object")
{
var stats = this["_statistics"];
delete this["_statistics"];
this.__stats(stats);
}
// strip out "_is_association"
if (!Gitana.isUndefined(this["_is_association"]))
{
var _is_association = this["_is_association"];
delete this["_is_association"];
this.__is_association(_is_association);
}
},
/**
* Override to include:
*
* __qname
* __type
* __features
* __stats
* __is_association
*
* @param otherObject
*/
chainCopyState: function(otherObject)
{
this.base(otherObject);
if (otherObject.__qname) {
this.__qname(otherObject.__qname());
}
if (otherObject.__type) {
this.__type(otherObject.__type());
}
if (otherObject.__features) {
this.__features(otherObject.__features());
}
if (otherObject.__stats) {
this.__stats(otherObject.__stats());
}
if (otherObject.__is_association) {
this.__is_association(otherObject.__is_association());
}
},
/**
* Hands back the stats.
*/
stats: function()
{
return this.__stats();
},
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// FEATURES
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Hands back a list of the feature ids that this node has.
*
* @public
*
* @param [Function] callback optional callback
*
* @returns {Array} An array of strings that are the ids of the features.
*/
getFeatureIds: function(callback)
{
var self = this;
var f = function()
{
var featureIds = [];
for (var featureId in this.__features()) {
featureIds[featureIds.length] = featureId;
}
return featureIds;
};
if (callback)
{
return this.then(function() {
callback.call(this, f.call(self));
});
}
return f.call(self);
},
/**
* Gets the configuration for a given feature.
*
* @public
*
* @param {String} featureId the id of the feature
* @param [Function] callback optional callback
*
* @returns {Object} the JSON object configuration for the feature
*/
getFeature: function(featureId, callback)
{
var self = this;
if (callback)
{
return this.then(function() {
callback.call(this, self.__features()[featureId]);
});
}
return self.__features()[featureId];
},
/**
* Removes a feature from this node.
*
* @public
*
* @param {String} featureId the id of the feature
*/
removeFeature: function(featureId)
{
var self = this;
var uriFunction = function()
{
return self.getUri() + "/features/" + featureId;
};
return this.chainDelete(this, uriFunction).reload().then(function() {
self.loadFrom(this);
});
},
/**
* Adds a feature to this node.
*
* @public
* @param {String} featureId the id of the feature
* @param [Object] featureConfig the JSON object configuration for the feature
*/
addFeature: function(featureId, featureConfig)
{
var self = this;
var uriFunction = function()
{
return self.getUri() + "/features/" + featureId;
};
if (!featureConfig) {
featureConfig = {};
}
return this.chainPostEmpty(null, uriFunction, {}, featureConfig).reload().then(function() {
self.loadFrom(this);
});
},
/**
* Indicates whether this node has the given feature.
*
* @public
*
* @param {String} featureId the id of the feature
* @param [Function] callback optional callback to receive result (for chaining)
*
* @returns {Boolean} whether this node has this feature
*/
hasFeature: function(featureId, callback)
{
if (callback)
{
return this.then(function() {
var hasFeature = !Gitana.isEmpty(this.__features()[featureId]);
callback.call(this, hasFeature);
});
}
return !Gitana.isEmpty(this.__features()[featureId]);
},
/**
* Indicates whether the current object is an association.
*
* @public
*
* @returns {Boolean} whether this node is an association
*/
isAssociation: function()
{
return this.__is_association();
},
/**
* Indicates whether this node has the "f:container" feature
*
* @public
*
* @returns {Boolean} whether this node has the "f:container" feature
*/
isContainer: function()
{
return this.hasFeature("f:container");
},
/**
* Touches the node. This allows the node to reindex and regenerate any renditions it may
* have associated with it.
*
* @public
*
* @chained node (this)
*/
touch: function()
{
var self = this;
var uriFunction = function()
{
return self.getUri() + "/touch";
};
return this.chainPost(null, uriFunction);
},
//////////////////////////////////////////////////////////////////////////////////////////
//
// TYPE
//
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the type QName for this node.
*
* @public
*
* @returns {String} the type qname of this node.
*/
getTypeQName: function()
{
return this.__type();
},
/**
* Changes the type QName for this node.
*
* @public
* @param {String} typeQName the qname of the type to change to
*/
changeTypeQName: function(typeQName)
{
var self = this;
var uriFunction = function()
{
return self.getUri() + "/change_type?type=" + typeQName;
};
return this.chainPostEmpty(null, uriFunction).reload().then(function() {
self.loadFrom(this);
});
},
//////////////////////////////////////////////////////////////////////////////////////////
//
// QNAME
//
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the QName for this node.
*
* @public
*
* @returns {String} the qname of this node.
*/
getQName: function()
{
return this.__qname();
},
/**
* Sets the QName of this node.
*
* @public
* @param {String} typeQName the qname of the type to change to
*/
changeQName: function(qname)
{
var self = this;
var uriFunction = function()
{
return self.getUri() + "/change_qname?qname=" + qname;
};
return this.chainPostEmpty(null, uriFunction).reload().then(function() {
self.loadFrom(this);
});
},
//////////////////////////////////////////////////////////////////////////////////////////
//
// ATTACHMENTS
//
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Lists the attachments of this node.
*
* If local is set to true, the attachments are drawn from precached values on the node.
*
* @chained attachment map
*
* @param local
*
* @public
*/
listAttachments: Gitana.Methods.listAttachments(Gitana.NodeAttachmentMap),
/**
* Picks off a single attachment
*
* @chained attachment
*
* @param attachmentId (null for default)
*/
attachment: function(attachmentId)
{
return this.listAttachments().select(attachmentId);
},
/**
* Non-chained method for getting a download URI for this node's attachment.
*/
attachmentDownloadUri: function(attachmentId)
{
return this.getDriver().baseURL + this.getUri() + "/attachments/" + attachmentId;
},
/**
* Creates an attachment.
*
* When using this method from within the JS driver, it really only works for text-based content such
* as JSON or text.
*
* @chained attachment
*
* @param attachmentId (use null or false for default attachment)
* @param contentType
* @param data
* @param filename
*/
attach: function(attachmentId, contentType, data, filename)
{
var paramsFunction = function(params) {
if (filename) { params["filename"] = filename; }
};
var delegate = Gitana.Methods.attach.call(this, Gitana.NodeAttachment, paramsFunction);
return delegate.call(this, attachmentId, contentType, data);
},
/**
* Generates a URI to a preview resource.
*/
getPreviewUri: Gitana.Methods.getPreviewUri(),
/**
* Deletes an attachment.
*
* @param attachmentId
*/
unattach: Gitana.Methods.unattach()
});
})(window);