apostrophe
Version:
The Apostrophe Content Management System.
363 lines (330 loc) • 11.8 kB
JavaScript
apos.define('apostrophe-browser-utils', {
construct: function(self, options) {
self.options = options;
// If the current page in the browser is https:, make this
// URL https: too.
self.sslIfNeeded = function(url) {
var ssl = (document.location.protocol === 'https:');
if (ssl) {
if (url.match(/^http:/)) {
url = url.replace(/^http:/, 'https:');
}
}
return url;
};
// KEEP IN SYNC WITH SERVER SIDE VERSION IN apostrophe.js
//
// Convert a name to camel case.
//
// Useful in converting CSV with friendly headings into sensible property names.
//
// Only digits and ASCII letters remain.
//
// Anything that isn't a digit or an ASCII letter prompts the next character
// to be uppercase. Existing uppercase letters also trigger uppercase, unless
// they are the first character; this preserves existing camelCase names.
self.camelName = function(s) {
var i;
var n = '';
var nextUp = false;
for (i = 0; (i < s.length); i++) {
var c = s.charAt(i);
// If the next character is already uppercase, preserve that, unless
// it is the first character
if ((i > 0) && c.match(/[A-Z]/)) {
nextUp = true;
}
if (c.match(/[A-Za-z0-9]/)) {
if (nextUp) {
n += c.toUpperCase();
nextUp = false;
} else {
n += c.toLowerCase();
}
} else {
nextUp = true;
}
}
return n;
};
// Convert other name formats such as underscore and camelCase to a hyphenated css-style
// name.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.cssName = function(camel) {
// Keep in sync with client side version
var i;
var css = '';
var dash = false;
var start = true;
for (i = 0; (i < camel.length); i++) {
var c = camel.charAt(i);
var lower = ((c >= 'a') && (c <= 'z'));
var upper = ((c >= 'A') && (c <= 'Z'));
var digit = ((c >= '0') && (c <= '9'));
if (!(lower || upper || digit)) {
if (!start) {
dash = true;
}
continue;
}
start = false;
if (upper) {
if (i > 0) {
dash = true;
}
c = c.toLowerCase();
}
if (dash) {
css += '-';
// Preserve double dash if they were literal dashes.
// This is common in modern CSS patterns
if ((i >= 2) && (camel.substr(i - 2, 2) === '--')) {
css += '-';
}
dash = false;
}
css += c;
}
return css;
};
// Create an event name from one or more strings. The original strings can be
// CSS names or camelized names, it makes no difference. The end result
// is always in a consistent format.
//
// Examples:
//
// apos.eventName('aposChange', 'blog') ---> aposChangeBlog
// apos.eventName('aposChangeEvents') ---> aposChangeEvents
// apos.eventName('apos-jump-gleefully') ---> aposJumpGleefully
//
// It doesn't matter how many arguments you pass. Each new argument
// is treated as a word boundary.
//
// This method is often useful both when triggering and when listening.
// No need to remember the correct way to construct an event name.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.eventName = function() {
var args = Array.prototype.slice.call(arguments, 0);
return apos.camelName(args.join('-'));
};
// Widget ids should be valid names for javascript variables, just in case
// we find that useful, so avoid hyphens. Prefixed with a `w` so it can
// always be distinguished from a cuid that came from the server; if we
// start using cuid in the browser we must keep the `w` prefix
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.generateId = function() {
return 'w' + Math.floor(Math.random() * 1000000000) + Math.floor(Math.random() * 1000000000);
};
self.entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": ''',
"/": '/'
};
// Escape HTML as plaintext.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.escapeHtml = function(string) {
return String(string).replace(/[&<>"'/]/g, function (s) {
return self.entityMap[s];
});
};
// String.replace does NOT do this
// Regexps can but they can't be trusted with unicode ):
// Keep in sync with server side version
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.globalReplace = function(haystack, needle, replacement) {
var result = '';
while (true) {
if (!haystack.length) {
return result;
}
var index = haystack.indexOf(needle);
if (index === -1) {
result += haystack;
return result;
}
result += haystack.substr(0, index);
result += replacement;
haystack = haystack.substr(index + needle.length);
}
};
// pad an integer with leading zeroes, creating a string.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.padInteger = function(i, places) {
var s = i + '';
while (s.length < places) {
s = '0' + s;
}
return s;
};
// Capitalize the first letter of a string.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.capitalizeFirst = function(s) {
return s.charAt(0).toUpperCase() + s.substr(1);
};
// Turn the provided string into a string suitable for use as a slug.
// ONE punctuation character normally forbidden in slugs may
// optionally be permitted by specifying it via options.allow.
// The separator may be changed via options.separator.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.slugify = function(s, options) {
return sluggo(s, options);
};
// Clone the given object recursively, discarding all
// properties whose names begin with `_` except
// for `_id`. Returns the clone.
//
// This removes the output of joins and
// other dynamic loaders, so that dynamically available
// content is not stored redundantly in MongoDB.
//
// If the object is an array, the clone is also an array.
//
// Date objects are cloned as such. All other non-JSON
// objects are cloned as plain JSON objects.
//
// If `keepScalars` is true, properties beginning with `_`
// are kept as long as they are not objects. This is useful
// when using `clonePermanent` to limit JSON inserted into
// browser attributes, rather than filtering for the database.
// Preserving simple string properties like `._url` is usually
// a good thing in the former case.
//
// Arrays are cloned as such only if they are true arrays
// (Array.isArray returns true). Otherwise all objects with
// a length property would be treated as arrays, which is
// an unrealistic restriction on apostrophe doc schemas.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.clonePermanent = function(o, keepScalars) {
var c;
var isArray = Array.isArray(o);
if (isArray) {
c = [];
} else {
c = {};
}
function iterator(val, key) {
// careful, don't crash on numeric keys
if (typeof key === 'string') {
if ((key.charAt(0) === '_') && (key !== '_id')) {
if ((!keepScalars) || (typeof val === 'object')) {
return;
}
}
}
if ((val === null) || (val === undefined)) {
// typeof(null) is object, sigh
c[key] = null;
} else if (typeof (val) !== 'object') {
c[key] = val;
} else if (val instanceof Date) {
c[key] = new Date(val);
} else {
c[key] = self.clonePermanent(val, keepScalars);
}
}
if (isArray) {
_.each(o, iterator);
} else {
_.forOwn(o, iterator);
}
return c;
};
// Log a message. The default
// implementation wraps `console.log` and passes on
// all arguments, so substitution strings may be used.
//
// Overrides should be written with support for
// substitution strings in mind. See the
// `console.log` documentation.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.log = function(msg) {
if ((typeof window.console) === 'object') {
window.console.log.apply(window.console, arguments);
}
};
// Log an informational message. The default
// implementation invokes
// `console.info` if available, otherwise
// an alias for `apos.utils.log`. Note that
// substitution strings may be used.
//
// Overrides should be written with support for
// substitution strings in mind. See the
// `console.log` documentation.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.info = function(msg) {
if ((typeof window.console) === 'object') {
if (window.console.info) {
window.console.info.apply(window.console, arguments);
return;
}
}
apos.utils.log.apply(apos, arguments);
};
// Log an error message. The default implementation
// wraps `console.error` and passes on all arguments,
// so substitution strings may be used.
//
// Overrides should be written with support for
// substitution strings in mind. See the
// `console.log` documentation.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.error = function(msg) {
if ((typeof window.console) === 'object') {
window.console.error.apply(window.console, arguments);
}
};
// Log a warning. The default implementation wraps
// `console.warn` and passes on all arguments,
// so substitution strings may be used.
// If `console.warn` does not exist, falls back
// to `apos.utils.error`.
//
// Overrides should be written with support for
// substitution strings in mind. See the
// `console.log` documentation.
//
// The intention is that `apos.utils.warn` should be
// called for situations less dire than
// `apos.utils.error`.
//
// NOTE: this method is not available if `lean: true` is active
// for `apostrophe-assets` and the user is not logged in.
self.warn = function(msg) {
if ((typeof window.console) === 'object') {
if (window.console.warn) {
window.console.warn.apply(window.console, arguments);
return;
}
}
apos.utils.error.apply(apos, arguments);
};
}
});