<commit_before>'use strict';

define([
        'src/util/api',
        'src/util/ui',
        'superagent',
        'uri/URI',
        'lodash',
        'src/util/couchdbAttachments',
        'mime-types'
    ],
    function (API, ui, superagent, URI, _, CDB, mimeTypes) {

        const defaultOptions = {
            messages: {
                200: 'OK',
                201: 'Created',
                202: 'Accepted',
                204: 'No content',
                400: 'Bad request',
                401: 'Unauthorized',
                409: 'Conflict',
                403: 'Forbidden',
                408: 'Request timeout',
                500: 'Internal server error',
                502: 'Bad gateway'
            }
        };

        const getTypes = ['get', 'getAttachment', 'getView'];

        const messagesByType = {
            get: {
                401: 'Unauthorized to get entry'
            },
            create: {
                200: 'Entry created',
                201: 'Entry created',
                401: 'Unauthorized to create entry'
            },
            update: {
                200: 'Entry updated',
                401: 'Unauthorized to update entry'
            },
            delete: {
                200: 'Entry deleted',
                401: 'Unauthorized to delete entry'
            },
            addAttachment: {
                200: 'Added attachment',
                401: 'Unauthorized to add attachment'
            },
            deleteAttachment: {
                200: 'Attachment deleted',
                401: 'Unauthorized to delete attachment'
            },
            getAttachment: {
                401: 'Unauthorized to get attachment'
            },
            getView: {
                401: 'Unauthorized to get view'
            }
        };

        for (let key in defaultOptions.messages) {
            // For get requests default is not to show any messages
            if (key < '300') {
                for (let i = 0; i < getTypes.length; i++) {
                    messagesByType[getTypes[i]][key] = '';
                }
            }
        }

        const viewSearch = ['key', 'startkey', 'endkey'];
        const mandatoryOptions = ['url', 'database'];

        class Roc {
            constructor(opts) {
                for (var key in opts) {
                    if (opts.hasOwnProperty(key)) {
                        this[key] = opts[key];
                    }
                }

                for (let i = 0; i < mandatoryOptions.length; i++) {
                    if (!this[mandatoryOptions[i]]) {
                        throw new Error(`${mandatoryOptions[i]} is a mandatory option`);
                    }
                }
                this.messages = this.messages || {};
                this.variables = {};

                this.requestUrl = new URI(opts.url);
                this.databaseUrl = this.requestUrl.directory(`${this.requestUrl.directory()}/db/${this.database}`).normalize().href();
                this.entryUrl = `${this.databaseUrl}entry`;
                this.__ready = Promise.resolve();
            }

            view(viewName, options) {
                return this.__ready.then(() => {
                    options = createOptions(options, 'getView');
                    let requestUrl = new URI(`${this.databaseUrl}_view/${viewName}`);

                    for (let i = 0; i < viewSearch.length; i++) {
                        if (options[viewSearch[i]]) {
                            requestUrl.addSearch(viewSearch[i], JSON.stringify(options[viewSearch[i]]));
                        }
                    }

                    requestUrl = requestUrl.normalize().href();

                    return superagent.get(requestUrl)
                        .withCredentials()
                        .then(res => {
                            if (res && res.body && res.status == 200) {
                                if (options.varName) {
                                    this.variables[options.varName] = {
                                        type: 'view',
                                        requestUrl,
                                        data: res.body
                                    };
                                    API.createData(options.varName, res.body);
                                }
                            }
                            return res.body;
                        })
                        .then(handleSuccess(this, options))
                        .catch(handleError(this, options));
                });
            }

            document(uuid, options) {
                return this.get(uuid).then(doc => {
                    if (!doc) return;
                    if (options.varName) {
                        this.variables[options.varName] = {
                            type: 'document',
                            data: doc
                        };
                        API.createData(options.varName, doc);
                        return doc;
                    }
                });
            }

            get(entry, options) {
                return this.__ready.then(() => {
                    var uuid = getUuid(entry);
                    options = createOptions(options, 'get');
                    if (options.fromCache) {
                        return this._findByUuid(uuid);
                    } else {
                        return superagent.get(`${this.entryUrl}/${uuid}`)
                            .withCredentials()
                            .end()
                            .then(res => {
                                if (res.body && res.status == 200) {
                                    this._updateByUuid(uuid, res.body);
                                    return res.body;
                                }
                            }).catch(handleError(this, options));
                    }
                });
            }

            getById(id, options) {
                return this.__ready.then(() => {
                    options = createOptions(options, 'get');
                    var entry = this._findById(id);
                    if (!entry || options.fromCache) {
                        return entry;
                    }
                    return this.get(entry)
                        .catch(handleError(this, options));
                });
            }

            create(entry, options) {
                return this.__ready
                    .then(() => {
                        options = createOptions(options, 'create');
                        return superagent.post(this.entryUrl)
                            .withCredentials()
                            .send(entry)
                            .then(handleSuccess(this, options))
                            .then(res => {
                                if (res.body && (res.status == 200 || res.status == 201)) {
                                    return this.get(res.body.id);
                                }
                            })
                            .then(entry => {
                                if (!entry) return;
                                let keys = Object.keys(this.variables);
                                for (let i = 0; i < keys.length; i++) {
                                    this.variables[keys[i]].data.push(entry);
                                    this.variables[keys[i]].data.triggerChange();
                                }
                                return entry;
                            })
                            .catch(handleError(this, options));
                    });
            }

            update(entry, options) {
                return this.__ready.then(() => {
                    options = createOptions(options, 'update');
                    entry = DataObject.resurrect(entry);
                    return superagent.put(`${this.entryUrl}/${String(entry._id)}`)
                        .withCredentials()
                        .send(entry)
                        .then(handleSuccess(this, options))
                        .then(res => {
                            if (res.body && res.status == 200) {
                                entry._rev = res.body.rev;
                                this._updateByUuid(entry._id, entry);
                            }
                            return res.body;
                        })
                        .catch(handleError(this, options));
                });
            }

            deleteAttachment(entry, attachments, options) {
                return this.__ready.then(() => {
                    var uuid = getUuid(entry);
                    options = createOptions(options, 'deleteAttachment');
                    if (Array.isArray(attachments) && attachments.length === 0) return this.getAttachmentList(entry);
                    const cdb = this._getCdb(uuid);
                    return cdb.remove(attachments)
                        .then(attachments => {
                            return this.get(uuid).then(data => {
                                console.log('got doc', data);
                                this._updateByUuid(uuid, data);
                                return attachments;
                            });
                        })
                        .then(handleSuccess(this, options))
                        .catch(handleError(this, options));
                });
            }

            removeAttachment(entry, attachments, options) {
                return this.deleteAttachment(entry, attachments, options);
            }

            attach(type, entry, attachment, options) {
                return this.__ready.then(() => {
                    var fallbackContentType = 'application/octet-stream';
                    var attachOptions = createOptions(options, 'attach');
                    var prom = Promise.resolve();
                    if(!attachment.filename) {
                        fallbackContentType = 'plain/text';
                        attachment.contentType = undefined;
                        prom =  ui.enterValue().then(val => {
                            attachment.filename = val;
                        });
                    }

                    return prom.then(() => {
                        if(!attachment.filename) {
                            return;
                        }
                        attachment.filename = this.processor.getFilename(type, attachment.filename);

                        // Ideally jcamp extensions should be handled by mime-types
                        if(!attachment.contentType || attachment.contentType === 'application/octet-stream') {
                            attachment.contentType = mimeTypes.lookup(attachment.filename);
                        }
                        if(!attachment.contentType && /\.j?dx$/.test(attachment.filename)) {
                            attachment.contentType = 'chemical/jcamp-dx';
                        }
                        if(!attachment.contentType) {
                            attachment.contentType = fallbackContentType;
                        }
                        // Mute error so that it doesn't show up twice
                        return this.addAttachment(entry, attachment, createOptions(options, 'addAttachment', {muteError: true}))
                            .then(() => {
                                return this.get(entry, {fromCache: true})
                            })
                            .then(entry => {
                                if(!this.processor) {
                                    throw new Error('no processor');
                                }
                                this.processor.process(type, entry.$content, attachment);
                                return entry;
                            })
                            .then(entry => {
                                return this.update(entry);
                            }).then(() => {
                                return this.get(entry, {fromCache: true});
                            })
                            .then(handleSuccess(this, attachOptions))
                            .catch(handleError(this, attachOptions));
                    });
                });
            }

            getAttachment(entry, name, options) {
                return this.__ready.then(() => {
                    const uuid = getUuid(entry);
                    options = createOptions(options, 'getAttachment');
                    const cdb = this._getCdb(uuid);
                    return cdb.get(name)
                        .catch(handleError(this, options));
                });
            }

            getAttachmentList(entry) {
                return this.__ready.then(() => {
                    const uuid = getUuid(entry);
                    const cdb = this._getCdb(uuid);
                    return cdb.list();
                });
            }

            addAttachment(entry, attachments, options) {
                return this.__ready.then(() => {
                    attachments = DataObject.resurrect(attachments);
                    if (!Array.isArray(attachments)) {
                        attachments = [attachments];
                    }
                    var uuid = getUuid(entry);
                    options = createOptions(options, 'addAttachment');
                    const cdb = this._getCdb(uuid);
                    return cdb.inlineUploads(attachments)
                        .then(attachments => {
                            return this.get(uuid).then(data => {
                                console.log('got doc add att', data);
                                this._updateByUuid(uuid, data);
                                return attachments;
                            });
                        })
                        .then(handleSuccess(this, options))
                        .catch(handleError(this, options));
                })
            }

            addAttachmentById(id, attachment, options) {
                return this.__ready.then(() => {
                    var doc = this._findById(id);
                    if (!doc) return;
                    return this.addAttachment(doc._id, attachment, options);
                });
            }

            delete(entry, options) {
                return this.__ready.then(() => {
                    const uuid = getUuid(entry);
                    options = createOptions(options, 'delete');
                    return superagent.del(`${this.entryUrl}/${uuid}`)
                        .withCredentials()
                        .then(handleSuccess(this, options))
                        .then(res => {
                            if (res.body && res.status == 200) {
                                for (let key in this.variables) {
                                    const idx = this._findIndexByUuid(uuid, key);
                                    if (idx !== -1) {
                                        this.variables[key].data.splice(idx, 1);
                                        this.variables[key].data.triggerChange();
                                    }
                                }

                            }
                            return res.body;
                        })
                        .catch(handleError(this, options));
                });
            }

            remove(entry, options) {
                return this.delete(entry, options);
            }

            // Private
            _getCdb(uuid) {
                const docUrl = `${this.entryUrl}/${String(uuid)}`;
                return new CDB(docUrl);
            }

            _findByUuid(uuid, key) {
                if (key === undefined) {
                    var result;
                    // Return the first one found (they are all supposed to be the same...)
                    for (let key in this.variables) {
                        result = this._findByUuid(uuid, key);
                        if (result) return result;
                    }
                    return null;
                }

                if (!this.variables[key]) return null;
                if(this.variables[key].type === 'view') {
                    return this.variables[key].data.find(entry => String(entry._id) === String(uuid));
                } else if(this.variables[key].type === 'document') {
                    if(String(this.variables[key].data._id) === String(uuid)) {
                        return this.variables[key].data;
                    }
                }
            }

            _findById(id, key) {
                if (key === undefined) {
                    var result;
                    for (let key in this.variables) {
                        result = this._findById(id, key);
                        if (result) return result;
                    }
                    return null;
                }
                if (!this.variables[key]) return null;
                id = DataObject.resurrect(id);
                if (this.variables[key].type === 'document' && _.isEqual(DataObject.resurrect(this.variables[key].data.$id), id)) {
                    return this.variables[key].data;
                } else if (this.variables[key.type === 'view']) {
                    return this.variables[key].data.find(entry => _.isEqual(id, DataObject.resurrect(entry.$id)));
                }
                return null;
            }

            _findIndexByUuid(uuid, key) {
                if (!this.variables[key]) return -1;
                if (this.variables[key].type === 'document') {
                    return -1;
                }
                return this.variables[key].data.findIndex(entry => String(entry._id) === String(uuid));
            }

            _updateByUuid(uuid, data) {
                for (let key in this.variables) {
                    if (this.variables[key].type === 'view') {
                        const idx = this._findIndexByUuid(uuid, key);
                        if (idx !== -1) {
                            this.variables[key].data.setChildSync([idx], data);
                        }
                    } else if (this.variables[key].type === 'document') {
                        uuid = String(uuid);
                        const _id = this.variables[key].data._id;
                        if (uuid === _id) {
                            var newData = DataObject.resurrect(data);
                            this.variables[key].data = newData;
                            API.createData(key, newData);
                        }
                    }
                }
            }
        }

        function createOptions(options, type, custom) {
            var messages = Object.assign({}, defaultOptions.messages, messagesByType[type], options && options.messages);
            options = Object.assign({}, defaultOptions, options, custom);
            if (messages) options.messages = messages;
            options.type = type;
            return options;
        }

        function handleError(ctx, options) {
            return function (err) {
                if(!options.mute || !options.muteError) {
                    if (err.status || err.timeout) { // error comes from superagent
                        handleSuperagentError(err, ctx, options);
                    } else {
                        defaultErrorHandler(err);
                    }
                }
                // Propagate error
                throw err;
            };
        }

        function handleSuccess(ctx, options) {
            return function (data) {
                if(!options.mute && !options.muteSuccess) {
                    if (data.status) {
                        handleSuperagentSuccess(data, ctx, options);
                    } else if(options.type && options.type.match(/attachment/i)) {
                        handleSuperagentSuccess({status: 200}, ctx, options);
                    }
                }
                return data;
            };
        }

        function handleSuperagentSuccess(data, ctx, options) {
            const message = options.messages[data.status] || ctx.messages[data.status];
            if (message && !options.disableNotification) {
                ui.showNotification(message, 'success');
            }
        }

        function handleSuperagentError(err, ctx, options) {
            const message = options.messages[err.status] || ctx.messages[err.status];
            if (message && !options.disableNotification) {
                ui.showNotification(message, 'error');
            }
        }

        function getUuid(entry) {
            var uuid;
            var type = DataObject.getType(entry);
            if (type === 'string') {
                uuid = entry;
            } else if (type === 'object') {
                uuid = entry._id;
            } else {
                throw new Error('Bad arguments');
            }
            return String(uuid);
        }

        function defaultErrorHandler(err) {
            ui.showNotification(`Error: ${err.message}`, 'error');
        }

        return Roc;
    });

<commit_msg>fix bad typo<commit_after>'use strict';

define([
        'src/util/api',
        'src/util/ui',
        'superagent',
        'uri/URI',
        'lodash',
        'src/util/couchdbAttachments',
        'mime-types'
    ],
    function (API, ui, superagent, URI, _, CDB, mimeTypes) {

        const defaultOptions = {
            messages: {
                200: 'OK',
                201: 'Created',
                202: 'Accepted',
                204: 'No content',
                400: 'Bad request',
                401: 'Unauthorized',
                409: 'Conflict',
                403: 'Forbidden',
                408: 'Request timeout',
                500: 'Internal server error',
                502: 'Bad gateway'
            }
        };

        const getTypes = ['get', 'getAttachment', 'getView'];

        const messagesByType = {
            get: {
                401: 'Unauthorized to get entry'
            },
            create: {
                200: 'Entry created',
                201: 'Entry created',
                401: 'Unauthorized to create entry'
            },
            update: {
                200: 'Entry updated',
                401: 'Unauthorized to update entry'
            },
            delete: {
                200: 'Entry deleted',
                401: 'Unauthorized to delete entry'
            },
            addAttachment: {
                200: 'Added attachment',
                401: 'Unauthorized to add attachment'
            },
            deleteAttachment: {
                200: 'Attachment deleted',
                401: 'Unauthorized to delete attachment'
            },
            getAttachment: {
                401: 'Unauthorized to get attachment'
            },
            getView: {
                401: 'Unauthorized to get view'
            }
        };

        for (let key in defaultOptions.messages) {
            // For get requests default is not to show any messages
            if (key < '300') {
                for (let i = 0; i < getTypes.length; i++) {
                    messagesByType[getTypes[i]][key] = '';
                }
            }
        }

        const viewSearch = ['key', 'startkey', 'endkey'];
        const mandatoryOptions = ['url', 'database'];

        class Roc {
            constructor(opts) {
                for (var key in opts) {
                    if (opts.hasOwnProperty(key)) {
                        this[key] = opts[key];
                    }
                }

                for (let i = 0; i < mandatoryOptions.length; i++) {
                    if (!this[mandatoryOptions[i]]) {
                        throw new Error(`${mandatoryOptions[i]} is a mandatory option`);
                    }
                }
                this.messages = this.messages || {};
                this.variables = {};

                this.requestUrl = new URI(opts.url);
                this.databaseUrl = this.requestUrl.directory(`${this.requestUrl.directory()}/db/${this.database}`).normalize().href();
                this.entryUrl = `${this.databaseUrl}entry`;
                this.__ready = Promise.resolve();
            }

            view(viewName, options) {
                return this.__ready.then(() => {
                    options = createOptions(options, 'getView');
                    let requestUrl = new URI(`${this.databaseUrl}_view/${viewName}`);

                    for (let i = 0; i < viewSearch.length; i++) {
                        if (options[viewSearch[i]]) {
                            requestUrl.addSearch(viewSearch[i], JSON.stringify(options[viewSearch[i]]));
                        }
                    }

                    requestUrl = requestUrl.normalize().href();

                    return superagent.get(requestUrl)
                        .withCredentials()
                        .then(res => {
                            if (res && res.body && res.status == 200) {
                                if (options.varName) {
                                    this.variables[options.varName] = {
                                        type: 'view',
                                        requestUrl,
                                        data: res.body
                                    };
                                    API.createData(options.varName, res.body);
                                }
                            }
                            return res.body;
                        })
                        .then(handleSuccess(this, options))
                        .catch(handleError(this, options));
                });
            }

            document(uuid, options) {
                return this.get(uuid).then(doc => {
                    if (!doc) return;
                    if (options.varName) {
                        this.variables[options.varName] = {
                            type: 'document',
                            data: doc
                        };
                        API.createData(options.varName, doc);
                        return doc;
                    }
                });
            }

            get(entry, options) {
                return this.__ready.then(() => {
                    var uuid = getUuid(entry);
                    options = createOptions(options, 'get');
                    if (options.fromCache) {
                        return this._findByUuid(uuid);
                    } else {
                        return superagent.get(`${this.entryUrl}/${uuid}`)
                            .withCredentials()
                            .end()
                            .then(res => {
                                if (res.body && res.status == 200) {
                                    this._updateByUuid(uuid, res.body);
                                    return res.body;
                                }
                            }).catch(handleError(this, options));
                    }
                });
            }

            getById(id, options) {
                return this.__ready.then(() => {
                    options = createOptions(options, 'get');
                    var entry = this._findById(id);
                    if (!entry || options.fromCache) {
                        return entry;
                    }
                    return this.get(entry)
                        .catch(handleError(this, options));
                });
            }

            create(entry, options) {
                return this.__ready
                    .then(() => {
                        options = createOptions(options, 'create');
                        return superagent.post(this.entryUrl)
                            .withCredentials()
                            .send(entry)
                            .then(handleSuccess(this, options))
                            .then(res => {
                                if (res.body && (res.status == 200 || res.status == 201)) {
                                    return this.get(res.body.id);
                                }
                            })
                            .then(entry => {
                                if (!entry) return;
                                let keys = Object.keys(this.variables);
                                for (let i = 0; i < keys.length; i++) {
                                    this.variables[keys[i]].data.push(entry);
                                    this.variables[keys[i]].data.triggerChange();
                                }
                                return entry;
                            })
                            .catch(handleError(this, options));
                    });
            }

            update(entry, options) {
                return this.__ready.then(() => {
                    options = createOptions(options, 'update');
                    entry = DataObject.resurrect(entry);
                    return superagent.put(`${this.entryUrl}/${String(entry._id)}`)
                        .withCredentials()
                        .send(entry)
                        .then(handleSuccess(this, options))
                        .then(res => {
                            if (res.body && res.status == 200) {
                                entry._rev = res.body.rev;
                                this._updateByUuid(entry._id, entry);
                            }
                            return res.body;
                        })
                        .catch(handleError(this, options));
                });
            }

            deleteAttachment(entry, attachments, options) {
                return this.__ready.then(() => {
                    var uuid = getUuid(entry);
                    options = createOptions(options, 'deleteAttachment');
                    if (Array.isArray(attachments) && attachments.length === 0) return this.getAttachmentList(entry);
                    const cdb = this._getCdb(uuid);
                    return cdb.remove(attachments)
                        .then(attachments => {
                            return this.get(uuid).then(data => {
                                console.log('got doc', data);
                                this._updateByUuid(uuid, data);
                                return attachments;
                            });
                        })
                        .then(handleSuccess(this, options))
                        .catch(handleError(this, options));
                });
            }

            removeAttachment(entry, attachments, options) {
                return this.deleteAttachment(entry, attachments, options);
            }

            attach(type, entry, attachment, options) {
                return this.__ready.then(() => {
                    var fallbackContentType = 'application/octet-stream';
                    var attachOptions = createOptions(options, 'attach');
                    var prom = Promise.resolve();
                    if(!attachment.filename) {
                        fallbackContentType = 'plain/text';
                        attachment.contentType = undefined;
                        prom =  ui.enterValue().then(val => {
                            attachment.filename = val;
                        });
                    }

                    return prom.then(() => {
                        if(!attachment.filename) {
                            return;
                        }
                        attachment.filename = this.processor.getFilename(type, attachment.filename);

                        // Ideally jcamp extensions should be handled by mime-types
                        if(!attachment.contentType || attachment.contentType === 'application/octet-stream') {
                            attachment.contentType = mimeTypes.lookup(attachment.filename);
                        }
                        if(!attachment.contentType && /\.j?dx$/.test(attachment.filename)) {
                            attachment.contentType = 'chemical/jcamp-dx';
                        }
                        if(!attachment.contentType) {
                            attachment.contentType = fallbackContentType;
                        }
                        // Mute error so that it doesn't show up twice
                        return this.addAttachment(entry, attachment, createOptions(options, 'addAttachment', {muteError: true}))
                            .then(() => {
                                return this.get(entry, {fromCache: true})
                            })
                            .then(entry => {
                                if(!this.processor) {
                                    throw new Error('no processor');
                                }
                                this.processor.process(type, entry.$content, attachment);
                                return entry;
                            })
                            .then(entry => {
                                return this.update(entry);
                            }).then(() => {
                                return this.get(entry, {fromCache: true});
                            })
                            .then(handleSuccess(this, attachOptions))
                            .catch(handleError(this, attachOptions));
                    });
                });
            }

            getAttachment(entry, name, options) {
                return this.__ready.then(() => {
                    const uuid = getUuid(entry);
                    options = createOptions(options, 'getAttachment');
                    const cdb = this._getCdb(uuid);
                    return cdb.get(name)
                        .catch(handleError(this, options));
                });
            }

            getAttachmentList(entry) {
                return this.__ready.then(() => {
                    const uuid = getUuid(entry);
                    const cdb = this._getCdb(uuid);
                    return cdb.list();
                });
            }

            addAttachment(entry, attachments, options) {
                return this.__ready.then(() => {
                    attachments = DataObject.resurrect(attachments);
                    if (!Array.isArray(attachments)) {
                        attachments = [attachments];
                    }
                    var uuid = getUuid(entry);
                    options = createOptions(options, 'addAttachment');
                    const cdb = this._getCdb(uuid);
                    return cdb.inlineUploads(attachments)
                        .then(attachments => {
                            return this.get(uuid).then(data => {
                                console.log('got doc add att', data);
                                this._updateByUuid(uuid, data);
                                return attachments;
                            });
                        })
                        .then(handleSuccess(this, options))
                        .catch(handleError(this, options));
                })
            }

            addAttachmentById(id, attachment, options) {
                return this.__ready.then(() => {
                    var doc = this._findById(id);
                    if (!doc) return;
                    return this.addAttachment(doc._id, attachment, options);
                });
            }

            delete(entry, options) {
                return this.__ready.then(() => {
                    const uuid = getUuid(entry);
                    options = createOptions(options, 'delete');
                    return superagent.del(`${this.entryUrl}/${uuid}`)
                        .withCredentials()
                        .then(handleSuccess(this, options))
                        .then(res => {
                            if (res.body && res.status == 200) {
                                for (let key in this.variables) {
                                    const idx = this._findIndexByUuid(uuid, key);
                                    if (idx !== -1) {
                                        this.variables[key].data.splice(idx, 1);
                                        this.variables[key].data.triggerChange();
                                    }
                                }

                            }
                            return res.body;
                        })
                        .catch(handleError(this, options));
                });
            }

            remove(entry, options) {
                return this.delete(entry, options);
            }

            // Private
            _getCdb(uuid) {
                const docUrl = `${this.entryUrl}/${String(uuid)}`;
                return new CDB(docUrl);
            }

            _findByUuid(uuid, key) {
                if (key === undefined) {
                    var result;
                    // Return the first one found (they are all supposed to be the same...)
                    for (let key in this.variables) {
                        result = this._findByUuid(uuid, key);
                        if (result) return result;
                    }
                    return null;
                }

                if (!this.variables[key]) return null;
                if(this.variables[key].type === 'view') {
                    return this.variables[key].data.find(entry => String(entry._id) === String(uuid));
                } else if(this.variables[key].type === 'document') {
                    if(String(this.variables[key].data._id) === String(uuid)) {
                        return this.variables[key].data;
                    }
                }
            }

            _findById(id, key) {
                if (key === undefined) {
                    var result;
                    for (let key in this.variables) {
                        result = this._findById(id, key);
                        if (result) return result;
                    }
                    return null;
                }
                if (!this.variables[key]) return null;
                id = DataObject.resurrect(id);
                if (this.variables[key].type === 'document' && _.isEqual(DataObject.resurrect(this.variables[key].data.$id), id)) {
                    return this.variables[key].data;
                } else if (this.variables[key].type === 'view') {
                    return this.variables[key].data.find(entry => _.isEqual(id, DataObject.resurrect(entry.$id)));
                }
                return null;
            }

            _findIndexByUuid(uuid, key) {
                if (!this.variables[key]) return -1;
                if (this.variables[key].type === 'document') {
                    return -1;
                }
                return this.variables[key].data.findIndex(entry => String(entry._id) === String(uuid));
            }

            _updateByUuid(uuid, data) {
                for (let key in this.variables) {
                    if (this.variables[key].type === 'view') {
                        const idx = this._findIndexByUuid(uuid, key);
                        if (idx !== -1) {
                            this.variables[key].data.setChildSync([idx], data);
                        }
                    } else if (this.variables[key].type === 'document') {
                        uuid = String(uuid);
                        const _id = this.variables[key].data._id;
                        if (uuid === _id) {
                            var newData = DataObject.resurrect(data);
                            this.variables[key].data = newData;
                            API.createData(key, newData);
                        }
                    }
                }
            }
        }

        function createOptions(options, type, custom) {
            var messages = Object.assign({}, defaultOptions.messages, messagesByType[type], options && options.messages);
            options = Object.assign({}, defaultOptions, options, custom);
            if (messages) options.messages = messages;
            options.type = type;
            return options;
        }

        function handleError(ctx, options) {
            return function (err) {
                if(!options.mute || !options.muteError) {
                    if (err.status || err.timeout) { // error comes from superagent
                        handleSuperagentError(err, ctx, options);
                    } else {
                        defaultErrorHandler(err);
                    }
                }
                // Propagate error
                throw err;
            };
        }

        function handleSuccess(ctx, options) {
            return function (data) {
                if(!options.mute && !options.muteSuccess) {
                    if (data.status) {
                        handleSuperagentSuccess(data, ctx, options);
                    } else if(options.type && options.type.match(/attachment/i)) {
                        handleSuperagentSuccess({status: 200}, ctx, options);
                    }
                }
                return data;
            };
        }

        function handleSuperagentSuccess(data, ctx, options) {
            const message = options.messages[data.status] || ctx.messages[data.status];
            if (message && !options.disableNotification) {
                ui.showNotification(message, 'success');
            }
        }

        function handleSuperagentError(err, ctx, options) {
            const message = options.messages[err.status] || ctx.messages[err.status];
            if (message && !options.disableNotification) {
                ui.showNotification(message, 'error');
            }
        }

        function getUuid(entry) {
            var uuid;
            var type = DataObject.getType(entry);
            if (type === 'string') {
                uuid = entry;
            } else if (type === 'object') {
                uuid = entry._id;
            } else {
                throw new Error('Bad arguments');
            }
            return String(uuid);
        }

        function defaultErrorHandler(err) {
            ui.showNotification(`Error: ${err.message}`, 'error');
        }

        return Roc;
    });

<|endoftext|>


<commit_before>'use strict';

function Block() {

}

var GameWidget = {
    controller: function(args) {
    },

    view: function(controller) {
        return m(".container", [
            m.component(EditorComponent),
            m.component(MapComponent),
        ]);
    },
};

var EditorComponent = {
    controller: function(args) {
        return {
            blocks: m.prop([]),
        };
    },

    view: function(controller) {
        return m("div#editor", {
            config: function(element, isInitialized) {
                if (!isInitialized) {
                    var drake = dragula({
                        copy: function(el, source) {
                            return source.classList.contains("block-container");
                        },
                        accepts: function(el, target, source, sibling) {
                            return target.classList.contains("block-acceptor");
                        },
                        isContainer: function(el) {
                            return el.classList.contains("block-container");
                        }
                    });
                    drake.containers.push(document.getElementById("workspace"));
                    drake.containers.push(document.getElementById("workbench"));
                    drake.on("drop", function(el, target, source, sibling) {
                        console.log(el, target, source, sibling);
                    });
                }
            },
        }, [
            m.component(WorkspaceComponent, {
                blocks: controller.blocks(),
            }),
            m("div#workbench", [
                m.component(ToolboxComponent),
                m.component(BlueprintComponent),
            ]),
        ]);
    },
};

var WorkspaceComponent = {
    controller: function(args) {

    },

    view: function(controller, args) {
        return m("div#workspace.block-acceptor", "Blocks go here");
    },
};

var BlueprintComponent = {
    controller: function(args) {

    },

    view: function(controller) {
        return m(".blueprint.workbench-area", [
            m("header", [
                m("span.class", "Robot"),
                " can",
            ]),
            m(".methods.block-container", [
                m(".method.workbench-item", "moveForward"),
                m(".method.workbench-item", "turnLeft"),
                m(".method.workbench-item", "selfDestruct"),
                m(".method.workbench-item", "reverse"),
            ]),
        ]);
    }
};

var ToolboxComponent = {
    controller: function(args) {

    },

    view: function(controller) {
        return m(".toolbox.workbench-area", [
            m("header", "Toolbox"),
            m(".control-flow-structures.block-container", [
                m(".control-flow-structure.workbench-item", "if..else.."),
                m(".control-flow-structure.workbench-item", "forever"),
                m(".boolean.workbench-item", "true"),
                m(".boolean.workbench-item", "false"),
                m(".primitive.workbench-item", "text"),
                m(".primitive.workbench-item", "number"),
                m(".control-flow-structure.workbench-item", [
                    "tell ",
                    m("span.object", "object"),
                    " to ",
                    m("span.method", "do something"),
                ]),
            ]),
        ]);
    }
};

var MapComponent = {
    controller: function(args) {
        var controller = {
            phaser: null,
            create: function() {
                var game = controller.phaser;

                var text = "test";
                var style = {
                    font: "24px Arial",
                    fill: "#FFFFFF",
                    align: "center",
                };

                game.add.text(game.world.centerX, 0, text, style);
            },
        };

        return controller;
    },

    view: function(controller) {
        return m("div#map", [
            m("div#worldMap", {
                config: function(element, isInitialized) {
                    if (!isInitialized) {
                        // TODO: figure out how to get this to scale properly
                        controller.phaser = new Phaser.Game(384, 612, Phaser.CANVAS, element, {
                            create: controller.create,
                        });
                    }
                },
            }),
            m("nav#gameControls", "Controls"),
        ]);
    },
};

m.mount(document.body, GameWidget);
<commit_msg>Handle drop events (poorly) on the model level<commit_after>'use strict';

function Block(data) {
    this.kind = m.prop(data.kind);
    this.subkind = m.prop(data.subkind || null);
    this.value = m.prop(data.value);
}

var GameWidget = {
    controller: function(args) {
    },

    view: function(controller) {
        return m(".container", [
            m.component(EditorComponent),
            m.component(MapComponent),
        ]);
    },
};

var EditorComponent = {
    controller: function(args) {
        return {
            blocks: m.prop([]),
        };
    },

    view: function(controller) {
        return m("div#editor", {
            config: function(element, isInitialized) {
                if (!isInitialized) {
                    var drake = dragula({
                        copy: function(el, source) {
                            return source.classList.contains("block-container");
                        },
                        accepts: function(el, target, source, sibling) {
                            return target.classList.contains("block-acceptor");
                        },
                        isContainer: function(el) {
                            return el.classList.contains("block-container");
                        }
                    });
                    drake.containers.push(document.getElementById("workspace"));
                    drake.containers.push(document.getElementById("workbench"));
                    drake.on("drop", function(el, target, source, sibling) {
                        // TODO: only do this if source is not target;
                        // else, need to reorder block list
                        m.startComputation();
                        // TODO: separate the creation of the Block
                        // and deciding where to attach it (to the
                        // main block list or to a sublist)
                        if (el.classList.contains("control-flow-structure")) {
                            controller.blocks().push(new Block({
                                kind: "control-flow-structure",
                                subkind: el.dataset.subkind,
                                value: null,
                            }));
                        }
                        else if (el.classList.contains("primitive")) {
                             controller.blocks().push(new Block({
                                 kind: "primitive",
                                 subkind: el.innerText,
                                 value: null,
                            }));
                        }
                        // These are not legal
                        // else if (el.classList.contains("method")) {
                        //     controller.blocks().push(new Block({
                        //         kind: "method",
                        //         // TODO: subkind based on source
                        //         value: el.innerText,
                        //     }));
                        // }
                        // else if (el.classList.contains("boolean")) {
                        //     controller.blocks().push(new Block({
                        //          kind: "boolean",
                        //          value: el.innerText == "true" ? true : false,
                        //     }));
                        // }

                        drake.remove();
                        console.log(el, target, source, sibling);
                        m.endComputation();
                    });
                }
            },
        }, [
            m.component(WorkspaceComponent, {
                blocks: controller.blocks(),
            }),
            m("div#workbench", [
                m.component(ToolboxComponent),
                m.component(BlueprintComponent),
            ]),
        ]);
    },
};

var WorkspaceComponent = {
    controller: function(args) {

    },

    view: function(controller, args) {
        return m("div#workspace.block-acceptor", args.blocks.map(function(block, index) {
            switch (block.kind()) {
            case "primitive":
                if (block.subkind() === "number") {
                    return m("div.primitive", [
                        "Number: ",
                        m("input[type='number']")
                    ]);
                }
                else if (block.subkind() === "text") {
                    return m("div.primitive", [
                        "Text: ",
                        m("input[type='text']")
                    ]);
                }
                break;
            case "control-flow-structure":
                if (block.subkind() === "tell") {
                    return m("div.control-flow-structure", [
                        "tell ",
                        m("div.block-hole.value"),
                        " to ",
                        m("div.block-hole.method"),
                    ]);
                }
                break;
            default:
                return m("div", {
                    class: block.kind(),
                }, block.value());
            }
        }));
    },
};

var BlueprintComponent = {
    controller: function(args) {

    },

    view: function(controller) {
        return m(".blueprint.workbench-area", [
            m("header", [
                m("span.class", "Robot"),
                " can",
            ]),
            m(".methods.block-container", [
                m(".method.workbench-item", "moveForward"),
                m(".method.workbench-item", "turnLeft"),
                m(".method.workbench-item", "selfDestruct"),
                m(".method.workbench-item", "reverse"),
            ]),
        ]);
    }
};

var ToolboxComponent = {
    controller: function(args) {

    },

    view: function(controller) {
        return m(".toolbox.workbench-area", [
            m("header", "Toolbox"),
            m(".control-flow-structures.block-container", [
                m(".control-flow-structure.workbench-item", {
                    "data-subkind": "if/else",
                }, "if..else.."),
                m(".control-flow-structure.workbench-item", {
                    "data-subkind": "forever",
                }, "forever"),
                m(".boolean.workbench-item", "true"),
                m(".boolean.workbench-item", "false"),
                m(".primitive.workbench-item", "text"),
                m(".primitive.workbench-item", "number"),
                m(".control-flow-structure.workbench-item", {
                    "data-subkind": "tell",
                }, [
                    "tell ",
                    m("span.object", "object"),
                    " to ",
                    m("span.method", "do something"),
                ]),
            ]),
        ]);
    }
};

var MapComponent = {
    controller: function(args) {
        var controller = {
            phaser: null,
            create: function() {
                var game = controller.phaser;

                var text = "test";
                var style = {
                    font: "24px Arial",
                    fill: "#FFFFFF",
                    align: "center",
                };

                game.add.text(game.world.centerX, 0, text, style);
            },
        };

        return controller;
    },

    view: function(controller) {
        return m("div#map", [
            m("div#worldMap", {
                config: function(element, isInitialized) {
                    if (!isInitialized) {
                        // TODO: figure out how to get this to scale properly
                        controller.phaser = new Phaser.Game(384, 612, Phaser.CANVAS, element, {
                            create: controller.create,
                        });
                    }
                },
            }),
            m("nav#gameControls", "Controls"),
        ]);
    },
};

m.mount(document.body, GameWidget);
<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed 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.
*/

'use strict';

// MODULES //

var tape = require( 'tape' );
var arrayShape = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof arrayShape, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function throws an error if not provided an array', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		void 0,
		{},
		function noop() {}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			arrayShape( value );
		};
	}
});

tape( 'the function returns array dimensions (0d)', function test( t ) {
	var arr;

	arr = [];
	t.deepEqual( arrayShape( arr ), [ 0 ], 'returns expected value' );

	arr = [ [] ];
	t.deepEqual( arrayShape( arr ), [ 1, 0 ], 'returns expected value' );

	arr = [ [ [] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 0 ], 'returns expected value' );

	arr = [ [], [] ];
	t.deepEqual( arrayShape( arr ), [ 2, 0 ], 'returns expected value' );

	arr = [ [ [] ], [ [] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 0 ], 'returns expected value' );

	arr = [ [], [], [] ];
	t.deepEqual( arrayShape( arr ), [ 3, 0 ], 'returns expected value' );

	arr = [ [ [ [] ] ], [ [ [] ] ], [ [ [] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 0 ], 'returns expected value' );

	arr = [ [ [ [ [] ] ] ], [ [ [ [] ] ] ], [ [ [ [] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 0 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (1d)', function test( t ) {
	var arr;

	arr = [ 1 ];
	t.deepEqual( arrayShape( arr ), [ 1 ], 'returns expected value' );

	arr = [ 1, 2 ];
	t.deepEqual( arrayShape( arr ), [ 2 ], 'returns expected value' );

	arr = [ 1, 2, 3 ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ], 3 ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ], null ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], null ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (2d)', function test( t ) {
	var arr;

	arr = [ [ 1 ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ], [ 3 ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], [ 3 ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], [ null ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1 ], 'returns expected value' );

	arr = [
		[ 1, 2, 3 ],
		[ 4, 5, 6 ],
		[ 7, 8, 9 ]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (3d)', function test( t ) {
	var arr;

	arr = [ [ [ 1 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], [ [ 3 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ], [ [ 3 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ], [ [ null ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1 ], 'returns expected value' );

	arr = [
		[ [ 1, 1, 1 ], [ 2, 2, 2 ], [ 3, 3, 3 ] ],
		[ [ 4, 4, 4 ], [ 5, 5, 5 ], [ 6, 6, 6 ] ],
		[ [ 7, 7, 7 ], [ 8, 8, 8 ], [ 9, 9, 9 ] ]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3, 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (4d)', function test( t ) {
	var arr;

	arr = [ [ [ [ 1 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ], [ [ [ 3 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ], [ [ [ 3 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ], [ [ [ null ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1 ], 'returns expected value' );

	arr = [
		[
			[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
			[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
			[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
		],
		[
			[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
			[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
			[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
		],
		[
			[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
			[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
			[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
		]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3, 3, 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (5d)', function test( t ) {
	var arr;

	arr = [ [ [ [ [ 1 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ], [ [ [ [ 3 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ [ 1 ] ] ] ] ], [ [ [ [ [ 2 ] ] ] ] ], [ [ [ [ 3 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ [ 1 ] ] ] ] ], [ [ [ [ [ 2 ] ] ] ] ], [ [ [ [ null ] ] ] ] ]; // eslint-disable-line max-len
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [
		[
			[
				[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
				[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
				[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
			],
			[
				[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
				[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
				[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
			],
			[
				[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
				[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
				[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
			]
		],
		[
			[
				[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
				[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
				[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
			],
			[
				[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
				[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
				[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
			],
			[
				[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
				[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
				[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
			]
		],
		[
			[
				[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
				[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
				[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
			],
			[
				[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
				[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
				[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
			],
			[
				[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
				[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
				[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
			]
		]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3, 3, 3, 3 ], 'returns expected value' );

	t.end();
});
<commit_msg>Add test case<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed 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.
*/

'use strict';

// MODULES //

var tape = require( 'tape' );
var arrayShape = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof arrayShape, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function throws an error if not provided an array', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		void 0,
		{},
		function noop() {}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			arrayShape( value );
		};
	}
});

tape( 'the function returns array dimensions (0d)', function test( t ) {
	var arr;

	arr = [];
	t.deepEqual( arrayShape( arr ), [ 0 ], 'returns expected value' );

	arr = [ [] ];
	t.deepEqual( arrayShape( arr ), [ 1, 0 ], 'returns expected value' );

	arr = [ [ [] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 0 ], 'returns expected value' );

	arr = [ [], [] ];
	t.deepEqual( arrayShape( arr ), [ 2, 0 ], 'returns expected value' );

	arr = [ [ [] ], [ [] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 0 ], 'returns expected value' );

	arr = [ [], [], [] ];
	t.deepEqual( arrayShape( arr ), [ 3, 0 ], 'returns expected value' );

	arr = [ [ [ [] ] ], [ [ [] ] ], [ [ [] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 0 ], 'returns expected value' );

	arr = [ [ [ [ [] ] ] ], [ [ [ [] ] ] ], [ [ [ [] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 0 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (1d)', function test( t ) {
	var arr;

	arr = [ 1 ];
	t.deepEqual( arrayShape( arr ), [ 1 ], 'returns expected value' );

	arr = [ 'beep' ];
	t.deepEqual( arrayShape( arr ), [ 1 ], 'returns expected value' );

	arr = [ 1, 2 ];
	t.deepEqual( arrayShape( arr ), [ 2 ], 'returns expected value' );

	arr = [ 1, 2, 3 ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ], 3 ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ], null ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], null ];
	t.deepEqual( arrayShape( arr ), [ 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (2d)', function test( t ) {
	var arr;

	arr = [ [ 1 ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1 ], 'returns expected value' );

	arr = [ [ 1 ], [ 2 ], [ 3 ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], [ 3 ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], [ null ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1 ], 'returns expected value' );

	arr = [
		[ 1, 2, 3 ],
		[ 4, 5, 6 ],
		[ 7, 8, 9 ]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (3d)', function test( t ) {
	var arr;

	arr = [ [ [ 1 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 1 ], 'returns expected value' );

	arr = [ [ [ 1 ] ], [ [ 2 ] ], [ [ 3 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ], [ [ 3 ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ], [ [ null ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1 ], 'returns expected value' );

	arr = [
		[ [ 1, 1, 1 ], [ 2, 2, 2 ], [ 3, 3, 3 ] ],
		[ [ 4, 4, 4 ], [ 5, 5, 5 ], [ 6, 6, 6 ] ],
		[ [ 7, 7, 7 ], [ 8, 8, 8 ], [ 9, 9, 9 ] ]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3, 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (4d)', function test( t ) {
	var arr;

	arr = [ [ [ [ 1 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ 1 ] ] ], [ [ [ 2 ] ] ], [ [ [ 3 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ], [ [ [ 3 ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ], [ [ [ null ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1 ], 'returns expected value' );

	arr = [
		[
			[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
			[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
			[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
		],
		[
			[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
			[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
			[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
		],
		[
			[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
			[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
			[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
		]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3, 3, 3 ], 'returns expected value' );

	t.end();
});

tape( 'the function returns array dimensions (5d)', function test( t ) {
	var arr;

	arr = [ [ [ [ [ 1 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 1, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 2, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ 1 ] ] ] ], [ [ [ [ 2 ] ] ] ], [ [ [ [ 3 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ [ 1 ] ] ] ] ], [ [ [ [ [ 2 ] ] ] ] ], [ [ [ [ 3 ] ] ] ] ];
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [ [ [ [ [ [ 1 ] ] ] ] ], [ [ [ [ [ 2 ] ] ] ] ], [ [ [ [ null ] ] ] ] ]; // eslint-disable-line max-len
	t.deepEqual( arrayShape( arr ), [ 3, 1, 1, 1, 1 ], 'returns expected value' );

	arr = [
		[
			[
				[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
				[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
				[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
			],
			[
				[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
				[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
				[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
			],
			[
				[ [ 1, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 1 ] ],
				[ [ 2, 2, 2 ], [ 2, 2, 2 ], [ 2, 2, 2 ] ],
				[ [ 3, 3, 3 ], [ 3, 3, 3 ], [ 3, 3, 3 ] ]
			]
		],
		[
			[
				[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
				[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
				[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
			],
			[
				[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
				[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
				[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
			],
			[
				[ [ 4, 4, 4 ], [ 4, 4, 4 ], [ 4, 4, 4 ] ],
				[ [ 5, 5, 5 ], [ 5, 5, 5 ], [ 5, 5, 5 ] ],
				[ [ 6, 6, 6 ], [ 6, 6, 6 ], [ 6, 6, 6 ] ]
			]
		],
		[
			[
				[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
				[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
				[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
			],
			[
				[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
				[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
				[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
			],
			[
				[ [ 7, 7, 7 ], [ 7, 7, 7 ], [ 7, 7, 7 ] ],
				[ [ 8, 8, 8 ], [ 8, 8, 8 ], [ 8, 8, 8 ] ],
				[ [ 9, 9, 9 ], [ 9, 9, 9 ], [ 9, 9, 9 ] ]
			]
		]
	];
	t.deepEqual( arrayShape( arr ), [ 3, 3, 3, 3, 3 ], 'returns expected value' );

	t.end();
});
<|endoftext|>


<commit_before>"use strict";

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }

function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

var _require = require('url'),
    format = _require.format,
    parse = _require.parse,
    URLSearchParams = _require.URLSearchParams;

var visit = require('unist-util-visit');

var fetch = require('node-fetch');

module.exports = function plugin(opts) {
  if (_typeof(opts) !== 'object' || !Object.keys(opts).length) {
    throw new Error('remark-iframes needs to be passed a configuration object as option');
  }

  function detectProvider(url) {
    var hostname = parse(url).hostname;
    return opts[hostname];
  }

  function blockTokenizer(eat, value, silent) {
    if (!value.startsWith('!(http')) return;
    var eatenValue = '';
    var url = '';
    var specialChars = ['!', '(', ')'];

    for (var i = 0; i < value.length && value[i - 1] !== ')'; i++) {
      eatenValue += value[i];

      if (!specialChars.includes(value[i])) {
        url += value[i];
      }
    }
    /* istanbul ignore if - never used (yet) */

    if (silent) return true;
    var provider = detectProvider(url);

    if (!provider || provider.disabled === true || provider.match && provider.match instanceof RegExp && !provider.match.test(url)) {
      return eat(eatenValue)({
        type: 'paragraph',
        children: [{
          type: 'text',
          value: eatenValue
        }]
      });
    }

    var finalUrl, thumbnail;
    var data = {
      hName: provider.tag || 'iframe',
      hProperties: {
        src: 'tmp',
        width: provider.width,
        height: provider.height,
        allowfullscreen: true,
        frameborder: '0'
      }
    };

    if (provider.oembed) {
      Object.assign(data, {
        oembed: {
          provider: provider,
          url: "".concat(provider.oembed, "?format=json&url=").concat(encodeURIComponent(url)),
          fallback: {
            type: 'link',
            url: url,
            children: [{
              type: 'text',
              value: url
            }]
          }
        }
      });
    } else {
      finalUrl = computeFinalUrl(provider, url);
      thumbnail = computeThumbnail(provider, finalUrl);
      Object.assign(data, {
        hProperties: {
          src: finalUrl,
          width: provider.width,
          height: provider.height,
          allowfullscreen: true,
          frameborder: '0'
        },
        thumbnail: thumbnail
      });
    }

    eat(eatenValue)({
      type: 'iframe',
      src: url,
      data: data
    });
  }

  var Parser = this.Parser; // Inject blockTokenizer

  var blockTokenizers = Parser.prototype.blockTokenizers;
  var blockMethods = Parser.prototype.blockMethods;
  blockTokenizers.iframes = blockTokenizer;
  blockMethods.splice(blockMethods.indexOf('blockquote') + 1, 0, 'iframes');
  var Compiler = this.Compiler;

  if (Compiler) {
    var visitors = Compiler.prototype.visitors;
    if (!visitors) return;

    visitors.iframe = function (node) {
      return "!(".concat(node.src, ")");
    };
  }

  return (
    /*#__PURE__*/
    function () {
      var _transform = _asyncToGenerator(
      /*#__PURE__*/
      regeneratorRuntime.mark(function _callee3(tree, vfile, next) {
        var toVisit, nextVisitOrBail;
        return regeneratorRuntime.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                nextVisitOrBail = function _ref4() {
                  if (toVisit === 0) next();
                };

                toVisit = 0;
                visit(tree, 'iframe',
                /*#__PURE__*/
                function () {
                  var _ref = _asyncToGenerator(
                  /*#__PURE__*/
                  regeneratorRuntime.mark(function _callee(node) {
                    return regeneratorRuntime.wrap(function _callee$(_context) {
                      while (1) {
                        switch (_context.prev = _context.next) {
                          case 0:
                            toVisit++;

                          case 1:
                          case "end":
                            return _context.stop();
                        }
                      }
                    }, _callee);
                  }));

                  return function (_x4) {
                    return _ref.apply(this, arguments);
                  };
                }());
                nextVisitOrBail();
                visit(tree, 'iframe',
                /*#__PURE__*/
                function () {
                  var _ref2 = _asyncToGenerator(
                  /*#__PURE__*/
                  regeneratorRuntime.mark(function _callee2(node) {
                    var data, oembed, provider, fallback, _ref3, url, thumbnail, height, width, message;

                    return regeneratorRuntime.wrap(function _callee2$(_context2) {
                      while (1) {
                        switch (_context2.prev = _context2.next) {
                          case 0:
                            if (node.data.oembed) {
                              _context2.next = 4;
                              break;
                            }

                            toVisit--;
                            nextVisitOrBail();
                            return _context2.abrupt("return");

                          case 4:
                            data = node.data;
                            oembed = data.oembed;
                            provider = data.oembed.provider;
                            fallback = data.oembed.fallback;
                            _context2.prev = 8;
                            _context2.next = 11;
                            return fetchEmbed(oembed.url);

                          case 11:
                            _ref3 = _context2.sent;
                            url = _ref3.url;
                            thumbnail = _ref3.thumbnail;
                            height = _ref3.height;
                            width = _ref3.width;
                            node.thumbnail = thumbnail;
                            Object.assign(data.hProperties, {
                              src: url,
                              width: provider.width || width,
                              height: provider.height || height,
                              allowfullscreen: true,
                              frameborder: '0'
                            });
                            _context2.next = 27;
                            break;

                          case 20:
                            _context2.prev = 20;
                            _context2.t0 = _context2["catch"](8);
                            message = _context2.t0.message;

                            if (_context2.t0.name === 'AbortError') {
                              message = "oEmbed URL timeout: ".concat(oembed.url);
                            }

                            vfile.message(message, node.position, oembed.url);
                            node.data = {};
                            Object.assign(node, fallback);

                          case 27:
                            delete data.oembed;
                            toVisit--;
                            nextVisitOrBail();

                          case 30:
                          case "end":
                            return _context2.stop();
                        }
                      }
                    }, _callee2, null, [[8, 20]]);
                  }));

                  return function (_x5) {
                    return _ref2.apply(this, arguments);
                  };
                }());

              case 5:
              case "end":
                return _context3.stop();
            }
          }
        }, _callee3);
      }));

      function transform(_x, _x2, _x3) {
        return _transform.apply(this, arguments);
      }

      return transform;
    }()
  );
};

function computeFinalUrl(provider, url) {
  var finalUrl = url;
  var parsed = parse(finalUrl);

  if (provider.droppedQueryParameters && parsed.search) {
    var search = new URLSearchParams(parsed.search);
    provider.droppedQueryParameters.forEach(function (ignored) {
      return search["delete"](ignored);
    });
    parsed.search = search.toString();
    finalUrl = format(parsed);
  }

  if (provider.replace && provider.replace.length) {
    provider.replace.forEach(function (rule) {
      var _rule = _slicedToArray(rule, 2),
          from = _rule[0],
          to = _rule[1];

      if (from && to) finalUrl = finalUrl.replace(from, to);
      parsed = parse(finalUrl);
    });
    finalUrl = format(parsed);
  }

  if (provider.removeFileName) {
    parsed.pathname = parsed.pathname.substring(0, parsed.pathname.lastIndexOf('/'));
    finalUrl = format(parsed);
  }

  if (provider.removeAfter && finalUrl.includes(provider.removeAfter)) {
    finalUrl = finalUrl.substring(0, finalUrl.indexOf(provider.removeAfter));
  }

  if (provider.append) {
    finalUrl += provider.append;
  }

  return finalUrl;
}

function computeThumbnail(provider, url) {
  var thumbnailURL = '';
  var thumbnailConfig = provider.thumbnail;

  if (thumbnailConfig && thumbnailConfig.format) {
    thumbnailURL = thumbnailConfig.format;
    Object.keys(thumbnailConfig).filter(function (key) {
      return key !== 'format';
    }).forEach(function (key) {
      var search = new RegExp("{".concat(key, "}"), 'g');
      var replace = new RegExp(thumbnailConfig[key]).exec(url);
      if (replace) thumbnailURL = thumbnailURL.replace(search, replace[1]);
    });
  }

  return thumbnailURL;
}

function fetchEmbed(_x6) {
  return _fetchEmbed.apply(this, arguments);
}

function _fetchEmbed() {
  _fetchEmbed = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee4(url) {
    return regeneratorRuntime.wrap(function _callee4$(_context4) {
      while (1) {
        switch (_context4.prev = _context4.next) {
          case 0:
            return _context4.abrupt("return", fetch(url, {
              timeout: 1500
            }).then(function (res) {
              return res.json();
            }).then(function (oembedRes) {
              var oembedUrl = oembedRes.html.match(/src="([A-Za-z0-9_/?&%=:.]+)"/)[1];
              var oembedThumbnail = oembedRes.thumbnail_url;
              return {
                url: oembedUrl,
                thumbnail: oembedThumbnail,
                width: oembedRes.width,
                height: oembedRes.height
              };
            }));

          case 1:
          case "end":
            return _context4.stop();
        }
      }
    }, _callee4);
  }));
  return _fetchEmbed.apply(this, arguments);
}<commit_msg>remark-iframes: fix dist<commit_after>"use strict";

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }

function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

var _require = require('url'),
    format = _require.format,
    parse = _require.parse,
    URLSearchParams = _require.URLSearchParams;

var visit = require('unist-util-visit');

var fetch = require('node-fetch');

module.exports = function plugin(opts) {
  if (_typeof(opts) !== 'object' || !Object.keys(opts).length) {
    throw new Error('remark-iframes needs to be passed a configuration object as option');
  }

  function detectProvider(url) {
    var hostname = parse(url).hostname;
    return opts[hostname];
  }

  function blockTokenizer(eat, value, silent) {
    if (!value.startsWith('!(http')) return;
    var eatenValue = '';
    var url = '';
    var specialChars = ['!', '(', ')'];

    for (var i = 0; i < value.length && value[i - 1] !== ')'; i++) {
      eatenValue += value[i];

      if (!specialChars.includes(value[i])) {
        url += value[i];
      }
    }
    /* istanbul ignore if - never used (yet) */

    if (silent) return true;
    var provider = detectProvider(url);

    if (!provider || provider.disabled === true || provider.match && provider.match instanceof RegExp && !provider.match.test(url)) {
      return eat(eatenValue)({
        type: 'paragraph',
        children: [{
          type: 'text',
          value: eatenValue
        }]
      });
    }

    var finalUrl, thumbnail;
    var data = {
      hName: provider.tag || 'iframe',
      hProperties: {
        src: 'tmp',
        width: provider.width,
        height: provider.height,
        allowfullscreen: true,
        frameborder: '0'
      }
    };

    if (provider.oembed) {
      Object.assign(data, {
        oembed: {
          provider: provider,
          url: "".concat(provider.oembed, "?format=json&url=").concat(encodeURIComponent(url)),
          fallback: {
            type: 'link',
            url: url,
            children: [{
              type: 'text',
              value: url
            }]
          }
        }
      });
    } else {
      finalUrl = computeFinalUrl(provider, url);
      thumbnail = computeThumbnail(provider, finalUrl);
      Object.assign(data, {
        hProperties: {
          src: finalUrl,
          width: provider.width,
          height: provider.height,
          allowfullscreen: true,
          frameborder: '0'
        },
        thumbnail: thumbnail
      });
    }

    eat(eatenValue)({
      type: 'iframe',
      src: url,
      data: data
    });
  }

  var Parser = this.Parser; // Inject blockTokenizer

  var blockTokenizers = Parser.prototype.blockTokenizers;
  var blockMethods = Parser.prototype.blockMethods;
  blockTokenizers.iframes = blockTokenizer;
  blockMethods.splice(blockMethods.indexOf('blockquote') + 1, 0, 'iframes');
  var Compiler = this.Compiler;

  if (Compiler) {
    var visitors = Compiler.prototype.visitors;
    if (!visitors) return;

    visitors.iframe = function (node) {
      return "!(".concat(node.src, ")");
    };
  }

  return (
    /*#__PURE__*/
    function () {
      var _transform = _asyncToGenerator(
      /*#__PURE__*/
      regeneratorRuntime.mark(function _callee3(tree, vfile, next) {
        var toVisit, nextVisitOrBail;
        return regeneratorRuntime.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                nextVisitOrBail = function _ref4() {
                  if (toVisit === 0) next();
                };

                toVisit = 0;
                visit(tree, 'iframe',
                /*#__PURE__*/
                function () {
                  var _ref = _asyncToGenerator(
                  /*#__PURE__*/
                  regeneratorRuntime.mark(function _callee(node) {
                    return regeneratorRuntime.wrap(function _callee$(_context) {
                      while (1) {
                        switch (_context.prev = _context.next) {
                          case 0:
                            toVisit++;

                          case 1:
                          case "end":
                            return _context.stop();
                        }
                      }
                    }, _callee);
                  }));

                  return function (_x4) {
                    return _ref.apply(this, arguments);
                  };
                }());
                nextVisitOrBail();
                visit(tree, 'iframe',
                /*#__PURE__*/
                function () {
                  var _ref2 = _asyncToGenerator(
                  /*#__PURE__*/
                  regeneratorRuntime.mark(function _callee2(node) {
                    var data, oembed, provider, fallback, _ref3, url, thumbnail, height, width, message;

                    return regeneratorRuntime.wrap(function _callee2$(_context2) {
                      while (1) {
                        switch (_context2.prev = _context2.next) {
                          case 0:
                            if (node.data.oembed) {
                              _context2.next = 4;
                              break;
                            }

                            toVisit--;
                            nextVisitOrBail();
                            return _context2.abrupt("return");

                          case 4:
                            data = node.data;
                            oembed = data.oembed;
                            provider = data.oembed.provider;
                            fallback = data.oembed.fallback;
                            _context2.prev = 8;
                            _context2.next = 11;
                            return fetchEmbed(oembed.url);

                          case 11:
                            _ref3 = _context2.sent;
                            url = _ref3.url;
                            thumbnail = _ref3.thumbnail;
                            height = _ref3.height;
                            width = _ref3.width;
                            node.thumbnail = thumbnail;
                            Object.assign(data.hProperties, {
                              src: url,
                              width: provider.width || width,
                              height: provider.height || height,
                              allowfullscreen: true,
                              frameborder: '0'
                            });
                            _context2.next = 27;
                            break;

                          case 20:
                            _context2.prev = 20;
                            _context2.t0 = _context2["catch"](8);
                            message = _context2.t0.message;

                            if (_context2.t0.name === 'AbortError') {
                              message = "oEmbed URL timeout: ".concat(oembed.url);
                            }

                            vfile.message(message, node.position, oembed.url);
                            node.data = {};
                            Object.assign(node, fallback);

                          case 27:
                            delete data.oembed;
                            toVisit--;
                            nextVisitOrBail();

                          case 30:
                          case "end":
                            return _context2.stop();
                        }
                      }
                    }, _callee2, null, [[8, 20]]);
                  }));

                  return function (_x5) {
                    return _ref2.apply(this, arguments);
                  };
                }());

              case 5:
              case "end":
                return _context3.stop();
            }
          }
        }, _callee3);
      }));

      function transform(_x, _x2, _x3) {
        return _transform.apply(this, arguments);
      }

      return transform;
    }()
  );
};

function computeFinalUrl(provider, url) {
  var finalUrl = url;
  var parsed = parse(finalUrl);

  if (provider.droppedQueryParameters && parsed.search) {
    var search = new URLSearchParams(parsed.search);
    provider.droppedQueryParameters.forEach(function (ignored) {
      return search["delete"](ignored);
    });
    parsed.search = search.toString();
    finalUrl = format(parsed);
  }

  if (provider.replace && provider.replace.length) {
    provider.replace.forEach(function (rule) {
      var _rule = _slicedToArray(rule, 2),
          from = _rule[0],
          to = _rule[1];

      if (from && to) finalUrl = finalUrl.replace(from, to);
      parsed = parse(finalUrl);
    });
    finalUrl = format(parsed);
  }

  if (provider.removeFileName) {
    parsed.pathname = parsed.pathname.substring(0, parsed.pathname.lastIndexOf('/'));
    finalUrl = format(parsed);
  }

  if (provider.removeAfter && finalUrl.includes(provider.removeAfter)) {
    finalUrl = finalUrl.substring(0, finalUrl.indexOf(provider.removeAfter));
  }

  if (provider.append) {
    finalUrl += provider.append;
  }

  return finalUrl;
}

function computeThumbnail(provider, url) {
  var thumbnailURL = '';
  var thumbnailConfig = provider.thumbnail;

  if (thumbnailConfig && thumbnailConfig.format) {
    thumbnailURL = thumbnailConfig.format;
    Object.keys(thumbnailConfig).filter(function (key) {
      return key !== 'format';
    }).forEach(function (key) {
      var search = new RegExp("{".concat(key, "}"), 'g');
      var replace = new RegExp(thumbnailConfig[key]).exec(url);
      if (replace) thumbnailURL = thumbnailURL.replace(search, replace[1]);
    });
  }

  return thumbnailURL;
}

function fetchEmbed(_x6) {
  return _fetchEmbed.apply(this, arguments);
}

function _fetchEmbed() {
  _fetchEmbed = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee4(url) {
    return regeneratorRuntime.wrap(function _callee4$(_context4) {
      while (1) {
        switch (_context4.prev = _context4.next) {
          case 0:
            return _context4.abrupt("return", fetch(url, {
              timeout: 1500
            }).then(function (res) {
              return res.json();
            }).then(function (oembedRes) {
              var oembedUrl = oembedRes.html.match(/src="(.+?)"/)[1];
              var oembedThumbnail = oembedRes.thumbnail_url;
              return {
                url: oembedUrl,
                thumbnail: oembedThumbnail,
                width: oembedRes.width,
                height: oembedRes.height
              };
            }));

          case 1:
          case "end":
            return _context4.stop();
        }
      }
    }, _callee4);
  }));
  return _fetchEmbed.apply(this, arguments);
}<|endoftext|>