%PDF- %PDF-
| Direktori : /proc/thread-self/root/data/old/home/stash/stash/atlassian-stash/static/util/ |
| Current File : //proc/thread-self/root/data/old/home/stash/stash/atlassian-stash/static/util/deprecation.js |
define('util/deprecation', [
'underscore',
'util/events',
'util/text'
], function(
_,
events,
textUtil
) {
'use strict';
var has = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;
/**
* Return a function that logs a deprecation warning to the console the first time it is called.
*
* @param {string|Function} displayNameOrShowMessageFn the name of the thing being deprecated. Alternatively, a function to be returned
* @param {string?} alternativeName the name of the alternative thing to use
* @param {string?} sinceVersion the version this has been deprecated since
* @param {string?} removeInVersion the version this will be removed in
* @return {Function} that logs the warning
*/
function getShowDeprecationMessage(displayNameOrShowMessageFn, alternativeName, sinceVersion, removeInVersion) {
if (typeof displayNameOrShowMessageFn === 'function') {
return displayNameOrShowMessageFn;
}
var called = false;
return function deprecated() {
if (!called) {
called = true;
var message = textUtil.toSentenceCase(displayNameOrShowMessageFn) + " has been deprecated" + (sinceVersion ? " since " + sinceVersion : '') +
" and will be removed in " + (removeInVersion || "a future release") + ".";
if (alternativeName) {
message += " Use " + alternativeName + " instead.";
}
var err = new Error();
var stack = err.stack || err.stacktrace;
var stackMessage = (stack && stack.replace(/^Error\n/, "")) || 'No stack trace of the deprecated usage is available in your current browser.';
console.log(message + "\n" + stackMessage);
}
};
}
/**
* Returns a wrapped version of the function that logs a deprecation warning when the function is used.
* @param {Function} fn the fn to wrap
* @param {string|Function} displayNameOrShowMessageFn the name of the fn to be displayed in the message. Alternatively, a function to log deprecation warnings
* @param {string} alternativeName the name of an alternative function to use
* @param {string} sinceVersion the version this has been deprecated since
* @param {string} removeInVersion the version this will be removed in
* @return {Function} wrapping the original function
*/
function deprecateFunctionExpression(fn, displayNameOrShowMessageFn, alternativeName, sinceVersion, removeInVersion) {
if (fn.__deprecated) {
return fn; // don't double deprecate
}
var showDeprecationMessage = getShowDeprecationMessage(displayNameOrShowMessageFn || fn.name || 'this function', alternativeName, sinceVersion, removeInVersion);
var deprecatedFn = function deprecated() {
showDeprecationMessage();
return fn.apply(this, arguments);
};
Object.defineProperty(deprecatedFn, '__deprecated', {
enumerable : false,
configurable : false,
writable : false,
value : true
});
return deprecatedFn;
}
/**
* Returns a wrapped version of the constructor that logs a deprecation warning when the constructor is instantiated.
* @param {Function} constructorFn the constructor function to wrap
* @param {string|Function} displayNameOrShowMessageFn the name of the fn to be displayed in the message. Alternatively, a function to log deprecation warnings
* @param {string} alternativeName the name of an alternative function to use
* @param {string} sinceVersion the version this has been deprecated since
* @param {string} removeInVersion the version this will be removed in
* @return {Function} wrapping the original function
*/
function deprecateConstructor(constructorFn, displayNameOrShowMessageFn, alternativeName, sinceVersion, removeInVersion) {
if (constructorFn.__deprecated) { // don't double deprecate
return constructorFn;
}
var deprecatedConstructor = deprecateFunctionExpression(constructorFn, displayNameOrShowMessageFn, alternativeName, sinceVersion, removeInVersion);
deprecatedConstructor.prototype = constructorFn.prototype;
_.extend(deprecatedConstructor, constructorFn); //copy static methods across;
return deprecatedConstructor;
}
/**
* Wraps a "value" object property in a deprecation warning in browsers supporting Object.defineProperty
* @param {Object} obj the object containing the property
* @param {string} prop the name of the property to deprecate
* @param {string|Function} displayNameOrShowMessageFn the display name of the property to deprecate (optional, will fall back to the property name). Alternatively, a function to log deprecation warnings
* @param {string} alternativeName the name of an alternative to use
* @param {string} sinceVersion the version this has been deprecated since
* @param {string} removeInVersion the version this will be removed in
*/
function deprecateValueProperty(obj, prop, displayNameOrShowMessageFn, alternativeName, sinceVersion, removeInVersion) {
var oldVal = obj[prop];
var showDeprecationMessage = getShowDeprecationMessage(displayNameOrShowMessageFn || prop, alternativeName, sinceVersion, removeInVersion);
Object.defineProperty(obj, prop, {
get : function () {
showDeprecationMessage();
return oldVal;
},
set : function(val) {
oldVal = val;
showDeprecationMessage();
return val;
}
});
}
/**
* Wraps an object property in a deprecation warning, if possible. functions will always log warnings, but other
* types of properties will only log in browsers supporting Object.defineProperty
* @param {Object} obj the object containing the property
* @param {string} prop the name of the property to deprecate
* @param {string|Function} displayNameOrShowMessageFn the display name of the property to deprecate (optional, will fall back to the property name). Alternatively, a function to log deprecation warnings
* @param {string} alternativeName the name of an alternative to use
* @param {string} sinceVersion the version this has been deprecated since
* @param {string} removeInVersion the version this will be removed in
*/
function deprecateObjectProperty(obj, prop, displayNameOrShowMessageFn, alternativeName, sinceVersion, removeInVersion) {
if (typeof obj[prop] === 'function') {
// assume all functions are constructors.
obj[prop] = deprecateConstructor(obj[prop], displayNameOrShowMessageFn || prop, alternativeName, sinceVersion, removeInVersion);
} else {
deprecateValueProperty(obj, prop, displayNameOrShowMessageFn, alternativeName, sinceVersion, removeInVersion);
}
}
function deprecateAllProperties(obj, objDisplayPrefix, alternativeNamePrefix, sinceVersion, removeInVersion) {
for(var attr in obj) {
if (has.call(obj, attr)) {
deprecateObjectProperty(obj, attr, objDisplayPrefix + attr, alternativeNamePrefix && (alternativeNamePrefix + attr),
sinceVersion, removeInVersion);
}
}
}
// These properties will not be touched since Backbone uses them on the model itself.
// But the deprecation should "just work" because they are synonymous with the attributes.
var whitelistedProperty = "id";
// These properties cannot be deprecated well since Backbone uses them on the model itself.
// We throw early when they are deprecated.
var blacklistedProperties = /^(attributes|url|isNew|hasChanged|changed(Attributes)|previous(Attributes)|clone)$/;
/**
* This function will deprecate a json property on an object that has been converted to a Brace.Model
* @param {Brace.Model} BraceModel the Brace.Model class that contains the attribute.
* @param {string} className the name of the Brace.Model class
* @param {string} attr the name of the attribute to deprecate
* @param {string} sinceVersion the version this has been deprecated since
* @param {string} removeInVersion the version this will be removed in
*/
function deprecateJsonModelProp(BraceModel, className, attr, sinceVersion, removeInVersion) {
if (whitelistedProperty === attr) {
return;
}
if (blacklistedProperties.test(attr)) {
throw new Error("The property " + attr + " cannot be deprecated when converting to a Brace model.");
}
var showDeprecationMessage = getShowDeprecationMessage(
className + '::' + attr,
className + "::get|set('" + attr + "')",
sinceVersion, removeInVersion);
Object.defineProperty(BraceModel.prototype, attr, {
get : function () {
showDeprecationMessage();
return this.get(attr);
},
set : function(val) {
showDeprecationMessage();
this.set(attr, val);
}
});
}
/**
* This function will deprecate a JSON model in favor of a Brace.Model
* @param {Brace.Model} BraceModel the Brace.Model class that has replaced a JSON model
* @param {string} className the name of the Brace.Model class
* @param {string} sinceVersion the version in which the JSON became a Brace model
* @param {string} removeInVersion the version in which the JSON attributes will be removed.
*/
function deprecateJsonModel(BraceModel, className, sinceVersion, removeInVersion) {
var namedAttrs = BraceModel.prototype.namedAttributes;
var attr;
if (toString.call(namedAttrs) === '[object Array]') {
var i = namedAttrs.length;
while(i--) {
deprecateJsonModelProp(BraceModel, className, namedAttrs[i], sinceVersion, removeInVersion);
}
} else {
for(attr in namedAttrs) {
if (has.call(namedAttrs, attr)) {
deprecateJsonModelProp(BraceModel, className, attr, sinceVersion, removeInVersion);
}
}
}
}
/**
* Deprecate an attribute of a brace model by deprecating the getFoo and setFoo convenience methods.
* @param {function} BraceModel the Brace.Model with the attribute to deprecate
* @param {string} attributeName the name of the attribute to deprecate
* @param {string} alternativeName the name or instructions for the alternative
* @param {string} sinceVersion the version that the attribute was deprecated
* @param {string} removeInVersion the version that the attribute will be removed
*/
function deprecateBraceAttribute(BraceModel, attributeName, alternativeName, sinceVersion, removeInVersion) {
if (has.call(BraceModel.prototype.namedAttributes, attributeName)) {
var SentenceCaseAttribute = textUtil.toSentenceCase(attributeName);
BraceModel.prototype['get' + SentenceCaseAttribute] = deprecateFunctionExpression(BraceModel.prototype['get' + SentenceCaseAttribute],
attributeName, alternativeName, sinceVersion, removeInVersion);
BraceModel.prototype['set' + SentenceCaseAttribute] = deprecateFunctionExpression(BraceModel.prototype['set' + SentenceCaseAttribute],
attributeName, alternativeName, sinceVersion, removeInVersion);
}
}
/**
* Deprecates the Brace methods on a model that should now be referenced as read-only JSON.
*
* NOTE: This should rarely be used. It is currently used for a bug that leaked into our API.
*
* @param {Brace.Model} braceModel the model to deprecate and replace with raw JSON
* @param {string} sinceVersion the version that the model was deprecated
* @param {string} removeInVersion the version that the model will be removed
*/
function deprecateJsonAsBraceModel(braceModel, sinceVersion, removeInVersion) {
var proto = braceModel.constructor.prototype;
braceModel = braceModel.clone();
var json = braceModel.toJSON();
var attr;
// a backdoor reference we can use without triggering any deprecation warnings.
Object.defineProperty(braceModel, '__json', {
enumerable : false,
configurable : false,
writable : false,
value : json
});
// Use a single deprecation message for all properties
// This avoids calls that cause 4-6 warnings to pop up at once E.g.,
// if you call .getMyProp(), which internally calls .get('myProp'), which accesses .attributes, etc...
var deprecationMessageFn = getShowDeprecationMessage(
"use of this object's Backbone properties",
"raw JSON properties on this object", sinceVersion, removeInVersion);
for(attr in braceModel) {
// No hasOwnProperty check - we want to handle everything accessible from this model
// that won't already be handled as raw json later
if (!has.call(json, attr)) {
deprecateObjectProperty(braceModel, attr, deprecationMessageFn);
}
}
// add the raw json properties onto the object
for (attr in json) {
if (has.call(json, attr) && !has.call(braceModel, attr) && !has.call(proto, attr)) {
braceModel[attr] = json[attr];
}
}
return braceModel;
}
var eventDeprecations = {};
/**
* @param {string} eventName
* @param {*} context
* @param {*} args excess arguments are passed to the handlers as
* @param {string?} alternativeName the name of the alternative thing to use
* @param {string?} sinceVersion the version this has been deprecated since
* @param {string?} removeInVersion the version this will be removed in
*/
function triggerDeprecated(eventName, context/*, ...args, alternativeName, sinceVersion, removeInVersion*/) {
if (events.listeners(eventName).length) {
if (arguments.length < 5) {
throw new Error("eventName, context, alternativeName, sinceVersion, and removeInVersion must all be provided (but can be null).");
}
var triggerArgs = Array.prototype.slice.call(arguments, 0, arguments.length - 3);
var alternativeName = arguments[arguments.length - 3];
var sinceVersion = arguments[arguments.length - 2];
var removeInVersion = arguments[arguments.length - 1];
var showMessage = eventDeprecations[eventName] ||
(eventDeprecations[eventName] =
getShowDeprecationMessage("Event '" + eventName + "'", "'" + alternativeName + "'", sinceVersion, removeInVersion));
showMessage();
events.trigger.apply(events, triggerArgs);
}
}
return {
fn : deprecateFunctionExpression,
construct : deprecateConstructor,
prop : deprecateObjectProperty,
obj : deprecateAllProperties,
braceAsJson : deprecateJsonModel,
braceAttribute: deprecateBraceAttribute,
jsonAsBrace : deprecateJsonAsBraceModel,
triggerDeprecated : triggerDeprecated,
getMessageLogger : getShowDeprecationMessage
};
});