148 lines
5.1 KiB
JavaScript
Raw Normal View History

import { loadScript } from 'netlify-cms-lib-util';
import { Iterable } from 'immutable';
/**
* Default Uploadcare widget configuration, can be overriden via config.yml.
*/
const defaultConfig = {
previewStep: true,
};
/**
* Determine whether an array of urls represents an unaltered set of Uploadcare
* group urls. If they've been changed or any are missing, a new group will need
* to be created to represent the current values.
*/
function isFileGroup(files) {
const basePatternString = `~${files.length}/nth/`;
const mapExpression = (val, idx) => new RegExp(`${basePatternString}${idx}/$`);
const expressions = Array.from({ length: files.length }, mapExpression);
return expressions.every(exp => files.some(url => exp.test(url)));
}
/**
* Returns a fileGroupInfo object wrapped in a promise-like object.
*/
function getFileGroup(files) {
/**
* Capture the group id from the first file in the files array.
*/
const groupId = new RegExp(`^.+/([^/]+~${files.length})/nth/`).exec(files[0])[1];
/**
* The `openDialog` method handles the jQuery promise object returned by
* `fileFrom`, but requires the promise returned by `loadFileGroup` to provide
* the result of it's `done` method.
*/
return new Promise(resolve =>
window.uploadcare.loadFileGroup(groupId).done(group => resolve(group)),
);
}
/**
* Convert a url or array/List of urls to Uploadcare file objects wrapped in
* promises, or Uploadcare groups when possible. Output is wrapped in a promise
* because the value we're returning may be a promise that we created.
*/
function getFiles(value, cdnBase) {
if (Array.isArray(value) || Iterable.isIterable(value)) {
const arr = Array.isArray(value) ? value : value.toJS();
return isFileGroup(arr) ? getFileGroup(arr) : arr.map(val => getFile(val, cdnBase));
}
return value && typeof value === 'string' ? getFile(value, cdnBase) : null;
}
/**
* Convert a single url to an Uploadcare file object wrapped in a promise-like
* object. Group urls that get passed here were not a part of a complete and
* untouched group, so they'll be uploaded as new images (only way to do it).
*/
function getFile(url, cdnBase) {
const groupPattern = /~\d+\/nth\/\d+\//;
const baseUrls = ['https://ucarecdn.com', cdnBase].filter(v => v);
const uploaded = baseUrls.some(baseUrl => url.startsWith(baseUrl) && !groupPattern.test(url));
return window.uploadcare.fileFrom(uploaded ? 'uploaded' : 'url', url);
}
/**
* Open the standalone dialog. A single instance is created and destroyed for
* each use.
*/
function openDialog(files, config, handleInsert) {
window.uploadcare.openDialog(files, config).done(({ promise }) =>
promise().then(({ cdnUrl, count }) => {
if (config.multiple) {
const urls = Array.from({ length: count }, (val, idx) => `${cdnUrl}nth/${idx}/`);
handleInsert(urls);
} else {
handleInsert(cdnUrl);
}
}),
);
}
/**
* Initialization function will only run once, returns an API object for Netlify
* CMS to call methods on.
*/
async function init({ options = { config: {} }, handleInsert }) {
const { publicKey, ...globalConfig } = options.config;
const baseConfig = { ...defaultConfig, ...globalConfig };
window.UPLOADCARE_LIVE = false;
window.UPLOADCARE_MANUAL_START = true;
window.UPLOADCARE_PUBLIC_KEY = publicKey;
/**
* Loading scripts via url because the uploadcare widget includes
* non-strict-mode code that's incompatible with our build system
*/
await loadScript('https://unpkg.com/uploadcare-widget@^3.6.0/uploadcare.full.js');
await loadScript(
'https://unpkg.com/uploadcare-widget-tab-effects@^1.2.1/dist/uploadcare.tab-effects.js',
);
/**
* Register the effects tab by default because the effects tab is awesome. Can
* be disabled via config.
*/
window.uploadcare.registerTab('preview', window.uploadcareTabEffects);
return {
/**
* On show, create a new widget, cache it in the widgets object, and open.
* No hide method is provided because the widget doesn't provide it.
*/
show: ({ value, config: instanceConfig = {}, imagesOnly }) => {
const config = { ...baseConfig, imagesOnly, ...instanceConfig };
const files = getFiles(value);
/**
* Resolve the promise only if it's ours. Only the jQuery promise objects
* from the Uploadcare library will have a `state` method.
*/
if (files && !files.state) {
files.then(result => openDialog(result, config, handleInsert));
} else {
openDialog(files, config, handleInsert);
}
},
/**
* Uploadcare doesn't provide a "media library" widget for viewing and
* selecting existing files, so we return `false` here so Netlify CMS only
* opens the Uploadcare widget when called from an editor control. This
* results in the "Media" button in the global nav being hidden.
*/
enableStandalone: () => false,
};
}
/**
* The object that will be registered only needs a (default) name and `init`
* method. The `init` method returns the API object.
*/
const uploadcareMediaLibrary = { name: 'uploadcare', init };
export default uploadcareMediaLibrary;