<commit_before>import Ember from 'ember';
import {parseDateString, formatDate} from 'ghost/utils/date-formatting';
import SettingsMenuMixin from 'ghost/mixins/settings-menu-controller';
import SlugGenerator from 'ghost/models/slug-generator';
import boundOneWay from 'ghost/utils/bound-one-way';
import isNumber from 'ghost/utils/isNumber';

export default Ember.Controller.extend(SettingsMenuMixin, {
    debounceId: null,
    lastPromise: null,
    selectedAuthor: null,
    uploaderReference: null,

    application: Ember.inject.controller(),
    config: Ember.inject.service(),
    ghostPaths: Ember.inject.service('ghost-paths'),
    notifications: Ember.inject.service(),

    initializeSelectedAuthor: Ember.observer('model', function () {
        var self = this;

        return this.get('model.author').then(function (author) {
            self.set('selectedAuthor', author);
            return author;
        });
    }),

    authors: Ember.computed(function () {
        // Loaded asynchronously, so must use promise proxies.
        var deferred = {};

        deferred.promise = this.store.find('user', {limit: 'all'}).then(function (users) {
            return users.rejectBy('id', 'me').sortBy('name');
        }).then(function (users) {
            return users.filter(function (user) {
                return user.get('active');
            });
        });

        return Ember.ArrayProxy
            .extend(Ember.PromiseProxyMixin)
            .create(deferred);
    }),

    /*jshint unused:false */
    publishedAtValue: Ember.computed('model.published_at', {
        get: function () {
            var pubDate = this.get('model.published_at');

            if (pubDate) {
                return formatDate(pubDate);
            }

            return formatDate(moment());
        },
        set: function (key, value) {
            // We're using a fake setter to reset
            // the cache for this property
            return formatDate(moment());
        }
    }),
    /*jshint unused:true */

    slugValue: boundOneWay('model.slug'),

    // Lazy load the slug generator
    slugGenerator: Ember.computed(function () {
        return SlugGenerator.create({
            ghostPaths: this.get('ghostPaths'),
            slugType: 'post'
        });
    }),

    // Requests slug from title
    generateAndSetSlug: function (destination) {
        var self = this,
            title = this.get('model.titleScratch'),
            afterSave = this.get('lastPromise'),
            promise;

        // Only set an "untitled" slug once per post
        if (title === '(Untitled)' && this.get('model.slug')) {
            return;
        }

        promise = Ember.RSVP.resolve(afterSave).then(function () {
            return self.get('slugGenerator').generateSlug(title).then(function (slug) {
                self.set(destination, slug);
            }).catch(function () {
                // Nothing to do (would be nice to log this somewhere though),
                // but a rejected promise needs to be handled here so that a resolved
                // promise is returned.
            });
        });

        this.set('lastPromise', promise);
    },

    metaTitleScratch: boundOneWay('model.meta_title'),
    metaDescriptionScratch: boundOneWay('model.meta_description'),

    seoTitle: Ember.computed('model.titleScratch', 'metaTitleScratch', function () {
        var metaTitle = this.get('metaTitleScratch') || '';

        metaTitle = metaTitle.length > 0 ? metaTitle : this.get('model.titleScratch');

        if (metaTitle.length > 70) {
            metaTitle = metaTitle.substring(0, 70).trim();
            metaTitle = Ember.Handlebars.Utils.escapeExpression(metaTitle);
            metaTitle = Ember.String.htmlSafe(metaTitle + '…');
        }

        return metaTitle;
    }),

    seoDescription: Ember.computed('model.scratch', 'metaDescriptionScratch', function () {
        var metaDescription = this.get('metaDescriptionScratch') || '',
            el,
            html = '',
            placeholder;

        if (metaDescription.length > 0) {
            placeholder = metaDescription;
        } else {
            el = $('.rendered-markdown');

            // Get rendered markdown
            if (el !== undefined && el.length > 0) {
                html = el.clone();
                html.find('.js-drop-zone').remove();
                html = html[0].innerHTML;
            }

            // Strip HTML
            placeholder = $('<div />', {html: html}).text();
            // Replace new lines and trim
            // jscs: disable
            placeholder = placeholder.replace(/\n+/g, ' ').trim();
            // jscs: enable
        }

        if (placeholder.length > 156) {
            // Limit to 156 characters
            placeholder = placeholder.substring(0, 156).trim();
            placeholder = Ember.Handlebars.Utils.escapeExpression(placeholder);
            placeholder = Ember.String.htmlSafe(placeholder + '…');
        }

        return placeholder;
    }),

    seoURL: Ember.computed('model.slug', function () {
        var blogUrl = this.get('config').blogUrl,
            seoSlug = this.get('model.slug') ? this.get('model.slug') : '',
            seoURL = blogUrl + '/' + seoSlug;

        // only append a slash to the URL if the slug exists
        if (seoSlug) {
            seoURL += '/';
        }

        if (seoURL.length > 70) {
            seoURL = seoURL.substring(0, 70).trim();
            seoURL = Ember.String.htmlSafe(seoURL + '…');
        }

        return seoURL;
    }),

    // observe titleScratch, keeping the post's slug in sync
    // with it until saved for the first time.
    addTitleObserver: Ember.observer('model', function () {
        if (this.get('model.isNew') || this.get('model.title') === '(Untitled)') {
            this.addObserver('model.titleScratch', this, 'titleObserver');
        }
    }),

    titleObserver: function () {
        var debounceId,
            title = this.get('model.title');

        // generate a slug if a post is new and doesn't have a title yet or
        // if the title is still '(Untitled)' and the slug is unaltered.
        if ((this.get('model.isNew') && !title) || title === '(Untitled)') {
            debounceId = Ember.run.debounce(this, 'generateAndSetSlug', 'model.slug', 700);
        }

        this.set('debounceId', debounceId);
    },

    // live-query of all tags for tag input autocomplete
    availableTags: Ember.computed(function () {
        return this.get('store').filter('tag', {limit: 'all'}, function () {
            return true;
        });
    }),

    showErrors: function (errors) {
        errors = Ember.isArray(errors) ? errors : [errors];
        this.get('notifications').showErrors(errors);
    },

    actions: {
        togglePage: function () {
            var self = this;

            this.toggleProperty('model.page');
            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        toggleFeatured: function () {
            var self = this;

            this.toggleProperty('model.featured');

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save(this.get('saveOptions')).catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        /**
         * triggered by user manually changing slug
         */
        updateSlug: function (newSlug) {
            var slug = this.get('model.slug'),
                self = this;

            newSlug = newSlug || slug;

            newSlug = newSlug && newSlug.trim();

            // Ignore unchanged slugs or candidate slugs that are empty
            if (!newSlug || slug === newSlug) {
                // reset the input to its previous state
                this.set('slugValue', slug);

                return;
            }

            this.get('slugGenerator').generateSlug(newSlug).then(function (serverSlug) {
                // If after getting the sanitized and unique slug back from the API
                // we end up with a slug that matches the existing slug, abort the change
                if (serverSlug === slug) {
                    return;
                }

                // Because the server transforms the candidate slug by stripping
                // certain characters and appending a number onto the end of slugs
                // to enforce uniqueness, there are cases where we can get back a
                // candidate slug that is a duplicate of the original except for
                // the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2)

                // get the last token out of the slug candidate and see if it's a number
                var slugTokens = serverSlug.split('-'),
                    check = Number(slugTokens.pop());

                // if the candidate slug is the same as the existing slug except
                // for the incrementor then the existing slug should be used
                if (isNumber(check) && check > 0) {
                    if (slug === slugTokens.join('-') && serverSlug !== newSlug) {
                        self.set('slugValue', slug);

                        return;
                    }
                }

                self.set('model.slug', serverSlug);

                if (self.hasObserverFor('model.titleScratch')) {
                    self.removeObserver('model.titleScratch', self, 'titleObserver');
                }

                // If this is a new post.  Don't save the model.  Defer the save
                // to the user pressing the save button
                if (self.get('model.isNew')) {
                    return;
                }

                return self.get('model').save();
            }).catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        /**
         * Parse user's set published date.
         * Action sent by post settings menu view.
         * (#1351)
         */
        setPublishedAt: function (userInput) {
            var errMessage = '',
                newPublishedAt = parseDateString(userInput),
                publishedAt = this.get('model.published_at'),
                self = this;

            if (!userInput) {
                // Clear out the published_at field for a draft
                if (this.get('model.isDraft')) {
                    this.set('model.published_at', null);
                }

                return;
            }

            // Validate new Published date
            if (!newPublishedAt.isValid()) {
                errMessage = 'Published Date must be a valid date with format: ' +
                    'DD MMM YY @ HH:mm (e.g. 6 Dec 14 @ 15:00)';
            }
            if (newPublishedAt.diff(new Date(), 'h') > 0) {
                errMessage = 'Published Date cannot currently be in the future.';
            }

            // If errors, notify and exit.
            if (errMessage) {
                this.get('model.errors').add('post-setting-date', errMessage);

                return;
            }

            // Do nothing if the user didn't actually change the date
            if (publishedAt && publishedAt.isSame(newPublishedAt)) {
                return;
            }

            // Validation complete
            this.set('model.published_at', newPublishedAt);

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        setMetaTitle: function (metaTitle) {
            var property = 'meta_title',
                model = this.get('model'),
                currentTitle = model.get(property) || '';

            // Only update if the title has changed
            if (currentTitle === metaTitle) {
                return;
            }

            model.set(property, metaTitle);

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (model.get('isNew')) {
                return;
            }

            model.save();
        },

        setMetaDescription: function (metaDescription) {
            var property = 'meta_description',
                model = this.get('model'),
                currentDescription = model.get(property) || '';

            // Only update if the description has changed
            if (currentDescription === metaDescription) {
                return;
            }

            model.set(property, metaDescription);

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (model.get('isNew')) {
                return;
            }

            model.save();
        },

        setCoverImage: function (image) {
            var self = this;

            this.set('model.image', image);

            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        clearCoverImage: function () {
            var self = this;

            this.set('model.image', '');

            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        resetUploader: function () {
            var uploader = this.get('uploaderReference');

            if (uploader && uploader[0]) {
                uploader[0].uploaderUi.reset();
            }
        },

        resetPubDate: function () {
            this.set('publishedAtValue', '');
        },

        closeNavMenu: function () {
            this.get('application').send('closeNavMenu');
        },

        changeAuthor: function (newAuthor) {
            var author = this.get('model.author'),
                model = this.get('model'),
                self = this;

            // return if nothing changed
            if (newAuthor.get('id') === author.get('id')) {
                return;
            }

            model.set('author', newAuthor);

            // if this is a new post (never been saved before), don't try to save it
            if (this.get('model.isNew')) {
                return;
            }

            model.save().catch(function (errors) {
                self.showErrors(errors);
                self.set('selectedAuthor', author);
                model.rollback();
            });
        },

        addTag: function (tagName, index) {
            var self = this,
                currentTags = this.get('model.tags'),
                currentTagNames = currentTags.map(function (tag) { return tag.get('name').toLowerCase(); }),
                availableTagNames = null,
                tagToAdd = null;

            tagName = tagName.trim();

            // abort if tag is already selected
            if (currentTagNames.contains(tagName.toLowerCase())) {
                return;
            }

            this.get('availableTags').then(function (availableTags) {
                availableTagNames = availableTags.map(function (tag) { return tag.get('name').toLowerCase(); });

                // find existing tag or create new
                if (availableTagNames.contains(tagName.toLowerCase())) {
                    tagToAdd = availableTags.find(function (tag) {
                        return tag.get('name').toLowerCase() === tagName.toLowerCase();
                    });
                } else {
                    tagToAdd = self.get('store').createRecord('tag', {
                        name: tagName
                    });

                    // we need to set a UUID so that selectize has a unique value
                    // it will be ignored when sent to the server
                    tagToAdd.set('uuid', Ember.guidFor(tagToAdd));
                }

                // push tag onto post relationship
                if (tagToAdd) { self.get('model.tags').insertAt(index, tagToAdd); }
            });
        },

        removeTag: function (tag) {
            this.get('model.tags').removeObject(tag);

            if (tag.get('isNew')) {
                tag.destroyRecord();
            }
        }
    }
});
<commit_msg>Add config.blogUrl key to PSM's seoURL property Closes #5793<commit_after>import Ember from 'ember';
import {parseDateString, formatDate} from 'ghost/utils/date-formatting';
import SettingsMenuMixin from 'ghost/mixins/settings-menu-controller';
import SlugGenerator from 'ghost/models/slug-generator';
import boundOneWay from 'ghost/utils/bound-one-way';
import isNumber from 'ghost/utils/isNumber';

export default Ember.Controller.extend(SettingsMenuMixin, {
    debounceId: null,
    lastPromise: null,
    selectedAuthor: null,
    uploaderReference: null,

    application: Ember.inject.controller(),
    config: Ember.inject.service(),
    ghostPaths: Ember.inject.service('ghost-paths'),
    notifications: Ember.inject.service(),

    initializeSelectedAuthor: Ember.observer('model', function () {
        var self = this;

        return this.get('model.author').then(function (author) {
            self.set('selectedAuthor', author);
            return author;
        });
    }),

    authors: Ember.computed(function () {
        // Loaded asynchronously, so must use promise proxies.
        var deferred = {};

        deferred.promise = this.store.find('user', {limit: 'all'}).then(function (users) {
            return users.rejectBy('id', 'me').sortBy('name');
        }).then(function (users) {
            return users.filter(function (user) {
                return user.get('active');
            });
        });

        return Ember.ArrayProxy
            .extend(Ember.PromiseProxyMixin)
            .create(deferred);
    }),

    /*jshint unused:false */
    publishedAtValue: Ember.computed('model.published_at', {
        get: function () {
            var pubDate = this.get('model.published_at');

            if (pubDate) {
                return formatDate(pubDate);
            }

            return formatDate(moment());
        },
        set: function (key, value) {
            // We're using a fake setter to reset
            // the cache for this property
            return formatDate(moment());
        }
    }),
    /*jshint unused:true */

    slugValue: boundOneWay('model.slug'),

    // Lazy load the slug generator
    slugGenerator: Ember.computed(function () {
        return SlugGenerator.create({
            ghostPaths: this.get('ghostPaths'),
            slugType: 'post'
        });
    }),

    // Requests slug from title
    generateAndSetSlug: function (destination) {
        var self = this,
            title = this.get('model.titleScratch'),
            afterSave = this.get('lastPromise'),
            promise;

        // Only set an "untitled" slug once per post
        if (title === '(Untitled)' && this.get('model.slug')) {
            return;
        }

        promise = Ember.RSVP.resolve(afterSave).then(function () {
            return self.get('slugGenerator').generateSlug(title).then(function (slug) {
                self.set(destination, slug);
            }).catch(function () {
                // Nothing to do (would be nice to log this somewhere though),
                // but a rejected promise needs to be handled here so that a resolved
                // promise is returned.
            });
        });

        this.set('lastPromise', promise);
    },

    metaTitleScratch: boundOneWay('model.meta_title'),
    metaDescriptionScratch: boundOneWay('model.meta_description'),

    seoTitle: Ember.computed('model.titleScratch', 'metaTitleScratch', function () {
        var metaTitle = this.get('metaTitleScratch') || '';

        metaTitle = metaTitle.length > 0 ? metaTitle : this.get('model.titleScratch');

        if (metaTitle.length > 70) {
            metaTitle = metaTitle.substring(0, 70).trim();
            metaTitle = Ember.Handlebars.Utils.escapeExpression(metaTitle);
            metaTitle = Ember.String.htmlSafe(metaTitle + '…');
        }

        return metaTitle;
    }),

    seoDescription: Ember.computed('model.scratch', 'metaDescriptionScratch', function () {
        var metaDescription = this.get('metaDescriptionScratch') || '',
            el,
            html = '',
            placeholder;

        if (metaDescription.length > 0) {
            placeholder = metaDescription;
        } else {
            el = $('.rendered-markdown');

            // Get rendered markdown
            if (el !== undefined && el.length > 0) {
                html = el.clone();
                html.find('.js-drop-zone').remove();
                html = html[0].innerHTML;
            }

            // Strip HTML
            placeholder = $('<div />', {html: html}).text();
            // Replace new lines and trim
            // jscs: disable
            placeholder = placeholder.replace(/\n+/g, ' ').trim();
            // jscs: enable
        }

        if (placeholder.length > 156) {
            // Limit to 156 characters
            placeholder = placeholder.substring(0, 156).trim();
            placeholder = Ember.Handlebars.Utils.escapeExpression(placeholder);
            placeholder = Ember.String.htmlSafe(placeholder + '…');
        }

        return placeholder;
    }),

    seoURL: Ember.computed('model.slug', 'config.blogUrl', function () {
        var blogUrl = this.get('config.blogUrl'),
            seoSlug = this.get('model.slug') ? this.get('model.slug') : '',
            seoURL = blogUrl + '/' + seoSlug;

        // only append a slash to the URL if the slug exists
        if (seoSlug) {
            seoURL += '/';
        }

        if (seoURL.length > 70) {
            seoURL = seoURL.substring(0, 70).trim();
            seoURL = Ember.String.htmlSafe(seoURL + '…');
        }

        return seoURL;
    }),

    // observe titleScratch, keeping the post's slug in sync
    // with it until saved for the first time.
    addTitleObserver: Ember.observer('model', function () {
        if (this.get('model.isNew') || this.get('model.title') === '(Untitled)') {
            this.addObserver('model.titleScratch', this, 'titleObserver');
        }
    }),

    titleObserver: function () {
        var debounceId,
            title = this.get('model.title');

        // generate a slug if a post is new and doesn't have a title yet or
        // if the title is still '(Untitled)' and the slug is unaltered.
        if ((this.get('model.isNew') && !title) || title === '(Untitled)') {
            debounceId = Ember.run.debounce(this, 'generateAndSetSlug', 'model.slug', 700);
        }

        this.set('debounceId', debounceId);
    },

    // live-query of all tags for tag input autocomplete
    availableTags: Ember.computed(function () {
        return this.get('store').filter('tag', {limit: 'all'}, function () {
            return true;
        });
    }),

    showErrors: function (errors) {
        errors = Ember.isArray(errors) ? errors : [errors];
        this.get('notifications').showErrors(errors);
    },

    actions: {
        togglePage: function () {
            var self = this;

            this.toggleProperty('model.page');
            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        toggleFeatured: function () {
            var self = this;

            this.toggleProperty('model.featured');

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save(this.get('saveOptions')).catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        /**
         * triggered by user manually changing slug
         */
        updateSlug: function (newSlug) {
            var slug = this.get('model.slug'),
                self = this;

            newSlug = newSlug || slug;

            newSlug = newSlug && newSlug.trim();

            // Ignore unchanged slugs or candidate slugs that are empty
            if (!newSlug || slug === newSlug) {
                // reset the input to its previous state
                this.set('slugValue', slug);

                return;
            }

            this.get('slugGenerator').generateSlug(newSlug).then(function (serverSlug) {
                // If after getting the sanitized and unique slug back from the API
                // we end up with a slug that matches the existing slug, abort the change
                if (serverSlug === slug) {
                    return;
                }

                // Because the server transforms the candidate slug by stripping
                // certain characters and appending a number onto the end of slugs
                // to enforce uniqueness, there are cases where we can get back a
                // candidate slug that is a duplicate of the original except for
                // the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2)

                // get the last token out of the slug candidate and see if it's a number
                var slugTokens = serverSlug.split('-'),
                    check = Number(slugTokens.pop());

                // if the candidate slug is the same as the existing slug except
                // for the incrementor then the existing slug should be used
                if (isNumber(check) && check > 0) {
                    if (slug === slugTokens.join('-') && serverSlug !== newSlug) {
                        self.set('slugValue', slug);

                        return;
                    }
                }

                self.set('model.slug', serverSlug);

                if (self.hasObserverFor('model.titleScratch')) {
                    self.removeObserver('model.titleScratch', self, 'titleObserver');
                }

                // If this is a new post.  Don't save the model.  Defer the save
                // to the user pressing the save button
                if (self.get('model.isNew')) {
                    return;
                }

                return self.get('model').save();
            }).catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        /**
         * Parse user's set published date.
         * Action sent by post settings menu view.
         * (#1351)
         */
        setPublishedAt: function (userInput) {
            var errMessage = '',
                newPublishedAt = parseDateString(userInput),
                publishedAt = this.get('model.published_at'),
                self = this;

            if (!userInput) {
                // Clear out the published_at field for a draft
                if (this.get('model.isDraft')) {
                    this.set('model.published_at', null);
                }

                return;
            }

            // Validate new Published date
            if (!newPublishedAt.isValid()) {
                errMessage = 'Published Date must be a valid date with format: ' +
                    'DD MMM YY @ HH:mm (e.g. 6 Dec 14 @ 15:00)';
            }
            if (newPublishedAt.diff(new Date(), 'h') > 0) {
                errMessage = 'Published Date cannot currently be in the future.';
            }

            // If errors, notify and exit.
            if (errMessage) {
                this.get('model.errors').add('post-setting-date', errMessage);

                return;
            }

            // Do nothing if the user didn't actually change the date
            if (publishedAt && publishedAt.isSame(newPublishedAt)) {
                return;
            }

            // Validation complete
            this.set('model.published_at', newPublishedAt);

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        setMetaTitle: function (metaTitle) {
            var property = 'meta_title',
                model = this.get('model'),
                currentTitle = model.get(property) || '';

            // Only update if the title has changed
            if (currentTitle === metaTitle) {
                return;
            }

            model.set(property, metaTitle);

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (model.get('isNew')) {
                return;
            }

            model.save();
        },

        setMetaDescription: function (metaDescription) {
            var property = 'meta_description',
                model = this.get('model'),
                currentDescription = model.get(property) || '';

            // Only update if the description has changed
            if (currentDescription === metaDescription) {
                return;
            }

            model.set(property, metaDescription);

            // If this is a new post.  Don't save the model.  Defer the save
            // to the user pressing the save button
            if (model.get('isNew')) {
                return;
            }

            model.save();
        },

        setCoverImage: function (image) {
            var self = this;

            this.set('model.image', image);

            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        clearCoverImage: function () {
            var self = this;

            this.set('model.image', '');

            if (this.get('model.isNew')) {
                return;
            }

            this.get('model').save().catch(function (errors) {
                self.showErrors(errors);
                self.get('model').rollback();
            });
        },

        resetUploader: function () {
            var uploader = this.get('uploaderReference');

            if (uploader && uploader[0]) {
                uploader[0].uploaderUi.reset();
            }
        },

        resetPubDate: function () {
            this.set('publishedAtValue', '');
        },

        closeNavMenu: function () {
            this.get('application').send('closeNavMenu');
        },

        changeAuthor: function (newAuthor) {
            var author = this.get('model.author'),
                model = this.get('model'),
                self = this;

            // return if nothing changed
            if (newAuthor.get('id') === author.get('id')) {
                return;
            }

            model.set('author', newAuthor);

            // if this is a new post (never been saved before), don't try to save it
            if (this.get('model.isNew')) {
                return;
            }

            model.save().catch(function (errors) {
                self.showErrors(errors);
                self.set('selectedAuthor', author);
                model.rollback();
            });
        },

        addTag: function (tagName, index) {
            var self = this,
                currentTags = this.get('model.tags'),
                currentTagNames = currentTags.map(function (tag) { return tag.get('name').toLowerCase(); }),
                availableTagNames = null,
                tagToAdd = null;

            tagName = tagName.trim();

            // abort if tag is already selected
            if (currentTagNames.contains(tagName.toLowerCase())) {
                return;
            }

            this.get('availableTags').then(function (availableTags) {
                availableTagNames = availableTags.map(function (tag) { return tag.get('name').toLowerCase(); });

                // find existing tag or create new
                if (availableTagNames.contains(tagName.toLowerCase())) {
                    tagToAdd = availableTags.find(function (tag) {
                        return tag.get('name').toLowerCase() === tagName.toLowerCase();
                    });
                } else {
                    tagToAdd = self.get('store').createRecord('tag', {
                        name: tagName
                    });

                    // we need to set a UUID so that selectize has a unique value
                    // it will be ignored when sent to the server
                    tagToAdd.set('uuid', Ember.guidFor(tagToAdd));
                }

                // push tag onto post relationship
                if (tagToAdd) { self.get('model.tags').insertAt(index, tagToAdd); }
            });
        },

        removeTag: function (tag) {
            this.get('model.tags').removeObject(tag);

            if (tag.get('isNew')) {
                tag.destroyRecord();
            }
        }
    }
});
<|endoftext|>


<commit_before>var H5P = H5P || {};

H5P.Agamotto = function ($) {
  'use strict';
  /**
   * Constructor function.
   *
   * @param {object} options - Options from semantics.json.
   * @param {boolean} options.snap - If true, slider will snap to fixed positions.
   * @param {boolean} options.ticks - If true, slider container will display ticks.
   * @param {number} content - Id.
   */
  function Agamotto(options, id) {
    if (!options.items) {
      return;
    }

    this.options = options;
    this.options.items = sanitizeItems(this.options.items);

    this.maxItem = this.options.items.length - 1;
    this.selector = '.h5p-agamotto-wrapper';

    // Set hasDescription = true if at least one item has a description
    this.hasDescription = false;
    for (var i = 0; i < this.maxItem + 1; i++) {
      if (this.options.items[i].description !== '') {
        this.hasDescription = true;
        break;
      }
    }
    this.id = id;

    /**
     * Update images and descriptions.
     *
     * @param {Number} index - Index of top image.
     * @param {Number} opacity - Opacity of top image.
     */
    this.updateContent = function (index, opacity) {
      // Update images
      this.images.setImage(index, opacity);

      // Update descriptions
      if (this.hasDescription) {
        this.descriptions.setText(index, opacity);
      }
    };

    // Initialize event inheritance
    H5P.EventDispatcher.call(this);
  }

  // Extends the event dispatcher
  Agamotto.prototype = Object.create(H5P.EventDispatcher.prototype);
  Agamotto.prototype.constructor = Agamotto;

  /**
   * Attach function called by H5P framework to insert H5P content into page.
   *
   * @param {jQuery} container - Container to attach to.
   */
  Agamotto.prototype.attach = function ($container) {
    var that = this;

    // Setup HTML DOM
    $container.addClass('h5p-agamotto');
    if (!this.options.items || this.maxItem < 1) {
      $container.append('
I really need at least two images :-)
');
      this.trigger('resize');
      return;
    }

    /**
     * Load an Image.
     * TODO: Wouldn't this be better in images.js? Requires a promise here as well
     *
     * @param {string} path - Path to image.
     * @param {number} id - H5P ID.
     * @return {Promise} Promise for image being loaded.
     */
    function loadImage (path, id) {
      return new Promise(function(resolve, reject)  {
        var image = new Image();
        image.onload = function() {
          resolve(this);
        };
        image.onerror = function(error) {
          reject(error);
        };
        image.src = H5P.getPath(path, id);
      });
    }

    /*
     * Load images first before DOM is created; will help to prevent layout
     * problems in some cases.
     */
    var promises = [];
    for (var i = 0; i < that.options.items.length; i++) {
      promises.push(loadImage(that.options.items[i].image.path, that.id));
    }
    Promise.all(promises).then(function(results) {
      that.images = results;

      that.wrapper = document.createElement('div');
      that.wrapper.classList.add('h5p-agamotto-wrapper');
      that.wrapper.classList.add('h5p-agamotto-passepartout-horizontal');
      that.wrapper.classList.add('h5p-agamotto-passepartout-top');
      that.wrapper.classList.add('h5p-agamotto-passepartout-bottom');
      $container.append(that.wrapper);

      // Title
      if (that.options.title) {
        var title = document.createElement('div');
        title.classList.add('h5p-agamotto-title');
        title.innerHTML = '
' + that.options.title + '
';
        that.wrapper.appendChild(title);
      }

      // Images
      that.images = new H5P.Agamotto.Images(that.images);
      that.wrapper.appendChild(that.images.getDOM());
      that.images.resize();

      // Slider
      that.slider = new H5P.Agamotto.Slider({
        snap: that.options.snap,
        ticks: that.options.ticks,
        size: that.maxItem
      }, that.selector);
      that.wrapper.appendChild(that.slider.getDOM());
      that.slider.resize();

      // Descriptions
      if (that.hasDescription) {
        var descriptionTexts = [];
        for (i = 0; i <= that.maxItem; i++) {
          descriptionTexts[i] = that.options.items[i].description;
        }
        that.descriptions = new H5P.Agamotto.Descriptions(descriptionTexts, that.selector);
        that.wrapper.appendChild(that.descriptions.getDOM());
        that.descriptions.setHeight();
        // Passepartout at the bottom is not needed, because we have a description
        that.wrapper.classList.remove('h5p-agamotto-passepartout-bottom');
      }

      // Title
      if (that.options.title) {
        // Passepartout at the top is not needed, because we have a title
        that.wrapper.classList.remove('h5p-agamotto-passepartout-top');
      }
      else if (!that.hasDescription) {
        // No passepartout is needed at all, because we just have an image
        that.wrapper.classList.remove('h5p-agamotto-passepartout-horizontal');
        that.wrapper.classList.remove('h5p-agamotto-passepartout-top');
        that.wrapper.classList.remove('h5p-agamotto-passepartout-bottom');
      }

      that.slider.on('update', function(e) {
        /*
         * Map the slider value to the image indexes. Since we might not
         * want to initiate opacity shifts right away, we can add a margin to
         * the left and right of the slider where nothing happens
         */
        var margin = 5;
        var mappedValue = Agamotto.map(
          e.data.position,
          0 + margin,
          that.slider.getWidth() - margin,
          0,
          that.maxItem
        );
        // Account for margin change and mapping outside the image indexes
        var topIndex = Agamotto.constrain(Math.floor(mappedValue), 0, that.maxItem);

        /*
         * Using the cosine will allow an image to be displayed a little longer
         * before blending than a linear function
         */
        var linearOpacity = (1 - Agamotto.constrain(mappedValue - topIndex, 0, 1));
        var topOpacity = 0.5 * (1 - Math.cos(Math.PI * linearOpacity));

        that.updateContent(topIndex, topOpacity);
      });

      // Add Resize Handler
      window.addEventListener('resize', function (e) {
        /*
         * Decrease the size of the content if on a mobile device in landscape
         * orientation, because it might be hard to use it otherwise.
         * iOS devices don't switch screen.height and screen.width on rotation
         */
        if (isMobileDevice() && Math.abs(window.orientation) === 90) {
          if (/iPhone/.test(navigator.userAgent)) {
            that.wrapper.style.width = Math.round((screen.width / 2) * that.images.getRatio()) + 'px';
          }
          else {
            that.wrapper.style.width = Math.round((screen.height / 2) * that.images.getRatio()) + 'px';
          }
        }
        else {
          // Portrait orientation
          that.wrapper.style.width = 'auto';
        }

        that.images.resize();
        that.slider.resize();
        // The descriptions will get a scroll bar via CSS if neccesary, no resize needed

        // Resize iframe if image's height is too small or too high.
        var windowHeight = window.innerHeight;
        var wrapperHeight = that.wrapper.offsetHeight;
        var actionBar = document.querySelector('.h5p-actions');
        var actionBarHeight = actionBar ? actionBar.offsetHeight : -1;
        if (wrapperHeight + actionBarHeight + 1 !== windowHeight) {
          that.trigger('resize');
        }
      });

      // DOM completed.
      that.trigger('resize');
    });
  };

  /**
   * Remove missing items and limit amount.
   *
   * @param {Object} items - Items defined in semantics.org.
   * @return {Object} Sanitized items.
   */
  var sanitizeItems = function(items) {
    // Remove items with missing image
    for (var i = 0; i < items.length; i++) {
      if (items[i].image === undefined) {
        console.log('An image is missing. I will continue without it, but please check your settings.');
        items.splice(i, 1);
        i--;
      }
    }
    // Restrict to 50 images
    items = items.splice(0, 50);

    return items;
  };

  /**
   * Detect mobile devices (http://detectmobilebrowsers.com/)
   *
   * @returns {boolean} True if running on a mobile device.
   */
  var isMobileDevice = function() {
    var check = false;
    (function(a){
      if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;}) (navigator.userAgent||navigator.vendor||window.opera);
    return check;
  };

  /**
   * Map a value from one range to another.
   *
   * @param {number} value - Value to me remapped.
   * @param {number} lo1 - Lower boundary of first range.
   * @param {number} hi1 - Upper boundary of first range.
   * @param {number} lo2 - Lower boundary of second range.
   * @param {number} hi2 - Upper boundary of second range.
   * @return {number} - Remapped value.
   */
  Agamotto.map = function (value, lo1, hi1, lo2, hi2) {
    return lo2 + (hi2 - lo2) * (value - lo1) / (hi1 - lo1);
  };

  /**
   * Constrain a number value within a range.
   *
   * @param {number} value - Value to be constrained.
   * @param {number} lo - Lower boundary of the range.
   * @param {number} hi - Upper boundary of the range.
   * @returns {number} - Constrained value.
   */
  Agamotto.constrain = function (value, lo, hi) {
    return Math.min(hi, Math.max(lo, value));
  };

  return Agamotto;
}(H5P.jQuery);
<commit_msg>add xAPI event: experienced<commit_after>var H5P = H5P || {};

H5P.Agamotto = function ($) {
  'use strict';
  /**
   * Constructor function.
   *
   * @param {object} options - Options from semantics.json.
   * @param {boolean} options.snap - If true, slider will snap to fixed positions.
   * @param {boolean} options.ticks - If true, slider container will display ticks.
   * @param {number} content - Id.
   */
  function Agamotto(options, id) {
    if (!options.items) {
      return;
    }

    this.options = options;
    this.options.items = sanitizeItems(this.options.items);

    this.maxItem = this.options.items.length - 1;
    this.selector = '.h5p-agamotto-wrapper';

    // Set hasDescription = true if at least one item has a description
    this.hasDescription = false;
    for (var i = 0; i < this.maxItem + 1; i++) {
      if (this.options.items[i].description !== '') {
        this.hasDescription = true;
        break;
      }
    }
    this.id = id;

    /**
     * Update images and descriptions.
     *
     * @param {Number} index - Index of top image.
     * @param {Number} opacity - Opacity of top image.
     */
    this.updateContent = function (index, opacity) {
      // Update images
      this.images.setImage(index, opacity);

      // Update descriptions
      if (this.hasDescription) {
        this.descriptions.setText(index, opacity);
      }
    };

    // Initialize event inheritance
    H5P.EventDispatcher.call(this);
  }

  // Extends the event dispatcher
  Agamotto.prototype = Object.create(H5P.EventDispatcher.prototype);
  Agamotto.prototype.constructor = Agamotto;

  /**
   * Attach function called by H5P framework to insert H5P content into page.
   *
   * @param {jQuery} container - Container to attach to.
   */
  Agamotto.prototype.attach = function ($container) {
    var that = this;

    // Setup HTML DOM
    $container.addClass('h5p-agamotto');
    if (!this.options.items || this.maxItem < 1) {
      $container.append('
I really need at least two images :-)
');
      this.trigger('resize');
      return;
    }

    /**
     * Load an Image.
     * TODO: Wouldn't this be better in images.js? Requires a promise here as well
     *
     * @param {string} path - Path to image.
     * @param {number} id - H5P ID.
     * @return {Promise} Promise for image being loaded.
     */
    function loadImage (path, id) {
      return new Promise(function(resolve, reject)  {
        var image = new Image();
        image.onload = function() {
          resolve(this);
        };
        image.onerror = function(error) {
          reject(error);
        };
        image.src = H5P.getPath(path, id);
      });
    }

    /*
     * Load images first before DOM is created; will help to prevent layout
     * problems in some cases.
     */
    var promises = [];
    for (var i = 0; i < that.options.items.length; i++) {
      promises.push(loadImage(that.options.items[i].image.path, that.id));
    }
    Promise.all(promises).then(function(results) {
      that.images = results;

      that.wrapper = document.createElement('div');
      that.wrapper.classList.add('h5p-agamotto-wrapper');
      that.wrapper.classList.add('h5p-agamotto-passepartout-horizontal');
      that.wrapper.classList.add('h5p-agamotto-passepartout-top');
      that.wrapper.classList.add('h5p-agamotto-passepartout-bottom');
      $container.append(that.wrapper);

      // Title
      if (that.options.title) {
        var title = document.createElement('div');
        title.classList.add('h5p-agamotto-title');
        title.innerHTML = '
' + that.options.title + '
';
        that.wrapper.appendChild(title);
      }

      // Images
      that.images = new H5P.Agamotto.Images(that.images);
      that.wrapper.appendChild(that.images.getDOM());
      that.images.resize();

      // Slider
      that.slider = new H5P.Agamotto.Slider({
        snap: that.options.snap,
        ticks: that.options.ticks,
        size: that.maxItem
      }, that.selector);
      that.wrapper.appendChild(that.slider.getDOM());
      that.slider.resize();

      // Descriptions
      if (that.hasDescription) {
        var descriptionTexts = [];
        for (i = 0; i <= that.maxItem; i++) {
          descriptionTexts[i] = that.options.items[i].description;
        }
        that.descriptions = new H5P.Agamotto.Descriptions(descriptionTexts, that.selector);
        that.wrapper.appendChild(that.descriptions.getDOM());
        that.descriptions.setHeight();
        // Passepartout at the bottom is not needed, because we have a description
        that.wrapper.classList.remove('h5p-agamotto-passepartout-bottom');
      }

      // Title
      if (that.options.title) {
        // Passepartout at the top is not needed, because we have a title
        that.wrapper.classList.remove('h5p-agamotto-passepartout-top');
      }
      else if (!that.hasDescription) {
        // No passepartout is needed at all, because we just have an image
        that.wrapper.classList.remove('h5p-agamotto-passepartout-horizontal');
        that.wrapper.classList.remove('h5p-agamotto-passepartout-top');
        that.wrapper.classList.remove('h5p-agamotto-passepartout-bottom');
      }

      // xAPI triggered when viewing the content
      that.triggerXAPI('experienced');

      that.slider.on('update', function(e) {
        /*
         * Map the slider value to the image indexes. Since we might not
         * want to initiate opacity shifts right away, we can add a margin to
         * the left and right of the slider where nothing happens
         */
        var margin = 5;
        var mappedValue = Agamotto.map(
          e.data.position,
          0 + margin,
          that.slider.getWidth() - margin,
          0,
          that.maxItem
        );
        // Account for margin change and mapping outside the image indexes
        var topIndex = Agamotto.constrain(Math.floor(mappedValue), 0, that.maxItem);

        /*
         * Using the cosine will allow an image to be displayed a little longer
         * before blending than a linear function
         */
        var linearOpacity = (1 - Agamotto.constrain(mappedValue - topIndex, 0, 1));
        var topOpacity = 0.5 * (1 - Math.cos(Math.PI * linearOpacity));

        that.updateContent(topIndex, topOpacity);
      });

      // Add Resize Handler
      window.addEventListener('resize', function (e) {
        /*
         * Decrease the size of the content if on a mobile device in landscape
         * orientation, because it might be hard to use it otherwise.
         * iOS devices don't switch screen.height and screen.width on rotation
         */
        if (isMobileDevice() && Math.abs(window.orientation) === 90) {
          if (/iPhone/.test(navigator.userAgent)) {
            that.wrapper.style.width = Math.round((screen.width / 2) * that.images.getRatio()) + 'px';
          }
          else {
            that.wrapper.style.width = Math.round((screen.height / 2) * that.images.getRatio()) + 'px';
          }
        }
        else {
          // Portrait orientation
          that.wrapper.style.width = 'auto';
        }

        that.images.resize();
        that.slider.resize();
        // The descriptions will get a scroll bar via CSS if neccesary, no resize needed

        // Resize iframe if image's height is too small or too high.
        var windowHeight = window.innerHeight;
        var wrapperHeight = that.wrapper.offsetHeight;
        var actionBar = document.querySelector('.h5p-actions');
        var actionBarHeight = actionBar ? actionBar.offsetHeight : -1;
        if (wrapperHeight + actionBarHeight + 1 !== windowHeight) {
          that.trigger('resize');
        }
      });

      // DOM completed.
      that.trigger('resize');
    });
  };

  /**
   * Remove missing items and limit amount.
   *
   * @param {Object} items - Items defined in semantics.org.
   * @return {Object} Sanitized items.
   */
  var sanitizeItems = function(items) {
    // Remove items with missing image
    for (var i = 0; i < items.length; i++) {
      if (items[i].image === undefined) {
        console.log('An image is missing. I will continue without it, but please check your settings.');
        items.splice(i, 1);
        i--;
      }
    }
    // Restrict to 50 images
    items = items.splice(0, 50);

    return items;
  };

  /**
   * Detect mobile devices (http://detectmobilebrowsers.com/)
   *
   * @returns {boolean} True if running on a mobile device.
   */
  var isMobileDevice = function() {
    var check = false;
    (function(a){
      if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;}) (navigator.userAgent||navigator.vendor||window.opera);
    return check;
  };

  /**
   * Map a value from one range to another.
   *
   * @param {number} value - Value to me remapped.
   * @param {number} lo1 - Lower boundary of first range.
   * @param {number} hi1 - Upper boundary of first range.
   * @param {number} lo2 - Lower boundary of second range.
   * @param {number} hi2 - Upper boundary of second range.
   * @return {number} - Remapped value.
   */
  Agamotto.map = function (value, lo1, hi1, lo2, hi2) {
    return lo2 + (hi2 - lo2) * (value - lo1) / (hi1 - lo1);
  };

  /**
   * Constrain a number value within a range.
   *
   * @param {number} value - Value to be constrained.
   * @param {number} lo - Lower boundary of the range.
   * @param {number} hi - Upper boundary of the range.
   * @returns {number} - Constrained value.
   */
  Agamotto.constrain = function (value, lo, hi) {
    return Math.min(hi, Math.max(lo, value));
  };

  return Agamotto;
}(H5P.jQuery);
<|endoftext|>


<commit_before>let resultsTray = document.getElementById("resultsTray");

//Used to extact the current URL
function getUrlByParameter(name){
    var url=window.location.href;
    var indexOfEnd=url.length;
    var index=url.indexOf(name);
    var length=name.length;
    return url.slice(index+length+1,indexOfEnd);
}

function getBooked(url){
  // Gets the data for each book on the wikipedia url
  get_ia_books(url).then((data)=>{
    $(".loader").hide();
    if (data['status'] === 'error') {
      let p = document.createElement("p");
      p.appendChild((document.createTextNode(data.message)));
      resultsTray.setAttribute("style", "grid-template-columns:none;");
      resultsTray.appendChild(p);
    } else {
      for(let isbn of Object.keys(data)) {  // Iterate over each book to get data
        if (data[isbn]) {
          //if book has field: metadata, addBookFromArchive
          if(data[isbn].metadata){
            addBookFromArchive(data[isbn].metadata);
          }
          // //else if book has id, addBookFromArchive using getMetadataFromArchive
          // else if(getIdentifier(data[isbn])){
          //   getMetadataFromArchive(getIdentifier(data[isbn]));
          //   //TODO add this identifier to OL perhaps
          // }

          //else add book from OL
          else{
              addBookFromOpenLibrary(data[isbn]);
          }
        }
      }
    }
  });
}

function getMetadataFromArchive (id) {
  $.getJSON('https://archive.org/metadata/' + id, function (data) {
    addBookFromArchive(data.metadata);
  });
}

function getMetadataFromOpenLibrary (olid, isbn) {
  isbn = isbn.replace(/\-/g, '');
  $.getJSON('http://openlibrary.org/isbn/' + isbn + '.json', function (data) {
    addBookFromOpenLibrary(data);
  });
}

function addBookFromArchive(metadata){
  let book = document.createElement('div');
  let title = document.createElement('p');
  let strong = document.createElement('strong');
  let author = document.createElement('p');
  let details = document.createElement('div');
  let text_elements = document.createElement('div');
  let img = document.createElement("img");
  let button = document.createElement("a");

  details.setAttribute("class", "bottom_details");
  text_elements.setAttribute("class", "text_elements");
  button.setAttribute("class", "btn btn-success resize_fit_center");
  button.setAttribute("href", "#");
  button.addEventListener("click", function(){
    chrome.storage.sync.get(['show_context'],function(event1){
        if(event1.show_context==undefined){
            event1.show_context="tab";
        }
        if(event1.show_context=="tab"){
            chrome.tabs.create({url:"https://archive.org/details/" + metadata.identifier});
        }else{
          chrome.system.display.getInfo(function(displayInfo){
            let height = displayInfo[0].bounds.height;
            let width = displayInfo[0].bounds.width;
            chrome.windows.create({url:"https://archive.org/details/" + metadata.identifier, width:width/2, height:height, top:0, left:0, focused:true});
          });
        }
    });
  });
  img.setAttribute("src", "https://archive.org/services/img/" + metadata.identifier);
  img.setAttribute("class", "cover-img");
  button.appendChild(document.createTextNode("Read Now"));
  details.appendChild(img);
  details.appendChild(button);
  strong.appendChild(document.createTextNode(metadata.title));
  author.appendChild(document.createTextNode(metadata.creator));
  title.appendChild(strong);
  text_elements.appendChild(title);
  text_elements.appendChild(author);
  book.appendChild(text_elements);
  book.appendChild(details);
  if(resultsTray.childNodes.length >0){
    resultsTray.insertBefore(book, resultsTray.childNodes[0]);
  }else{
    resultsTray.appendChild(book);
  }
}

function addBookFromOpenLibrary(metadata){
  let book = document.createElement('div');
  let title = document.createElement('p');
  let strong = document.createElement('strong');
  let author = document.createElement('p');
  let details = document.createElement('div');
  let text_elements = document.createElement('div');
  let img = document.createElement("img");
  let button = document.createElement("a");

  text_elements.setAttribute("class", "text_elements");
  img.setAttribute("class", "cover-img");
  details.setAttribute("class", "bottom_details");
  button.setAttribute("class", "btn btn-warning resize_fit_center");
  button.setAttribute("href", "#");

  button.addEventListener("click", function(){
    chrome.storage.sync.get(['show_context'],function(event1){
        if(event1.show_context==undefined){
            event1.show_context="tab";
        }
        if(event1.show_context=="tab"){
            chrome.tabs.create({url:"https://archive.org/donate/"});
        }else{
          chrome.system.display.getInfo(function(displayInfo){
            let height = displayInfo[0].bounds.height;
            let width = displayInfo[0].bounds.width;
            chrome.windows.create({url:"https://archive.org/donate/", width:width/2, height:height, top:0, left:0, focused:true});
          });
        }
    });
  });

  if(metadata.covers){
    img.setAttribute("src", "http://covers.openlibrary.org/w/id/"+metadata.covers[0]+"-M.jpg");
  }else{ //TODO: find cover
    img = document.createElement("p");
    img.appendChild(document.createTextNode("No cover available"));
    img.setAttribute("class", "cover-img");
  }
  button.appendChild(document.createTextNode("Donate"));
  details.appendChild(img);
  details.appendChild(button);
  strong.appendChild(document.createTextNode(metadata.title));
  title.appendChild(strong);
  if(metadata.authors_metadata){
    author.appendChild(document.createTextNode(metadata.authors_metadata.name));
  }
  text_elements.appendChild(title);
  text_elements.appendChild(author);
  book.appendChild(text_elements);
  book.appendChild(details);
  resultsTray.appendChild(book);
}

function getAuthorFromOpenLibrary(key){
  let url = "http://openlibrary.org"+ key + ".json";
  return fetch(url)
    .then(res=>res.json())
    .then(json => json.name)
    .catch(err => console.log(err));
}

if(typeof module !=="undefined") {module.exports = {getUrlByParameter:getUrlByParameter};}
<commit_msg>Made some changes recommended by vangelis, such as deleting comments and replacing some (not all) dom manipulation with jquery syntax<commit_after>

//Used to extact the current URL
function getUrlByParameter(name){
    var url=window.location.href;
    var indexOfEnd=url.length;
    var index=url.indexOf(name);
    var length=name.length;
    return url.slice(index+length+1,indexOfEnd);
}

function getBooked(url){
  // Gets the data for each book on the wikipedia url
  get_ia_books(url).then((data)=>{
    $(".loader").hide();
    if (data['status'] === 'error') {
      $("#resultsTray").css("grid-template-columns", "none").append(
        $("

").text(data.message)
      )
    } else {
      for(let isbn of Object.keys(data)) {  // Iterate over each book to get data
        if (data[isbn]) {
          if(data[isbn].metadata){
            addBookFromArchive(data[isbn].metadata);
          }else{
            addBookFromOpenLibrary(data[isbn]);
          }
        }
      }
    }
  });
}

function getMetadataFromArchive (id) {
  $.getJSON('https://archive.org/metadata/' + id, function (data) {
    addBookFromArchive(data.metadata);
  });
}

function getMetadataFromOpenLibrary (olid, isbn) {
  isbn = isbn.replace(/\-/g, '');
  $.getJSON('http://openlibrary.org/isbn/' + isbn + '.json', function (data) {
    addBookFromOpenLibrary(data);
  });
}

function addBookFromArchive(metadata){
  let resultsTray = document.getElementById("resultsTray");
  let book = document.createElement('div');
  let title = document.createElement('p');
  let strong = document.createElement('strong');
  let author = document.createElement('p');
  let details = document.createElement('div');
  let text_elements = document.createElement('div');
  let img = document.createElement("img");
  let button = document.createElement("a");

  details.setAttribute("class", "bottom_details");
  text_elements.setAttribute("class", "text_elements");
  button.setAttribute("class", "btn btn-success resize_fit_center");
  button.setAttribute("href", "#");
  button.addEventListener("click", function(){
    chrome.storage.sync.get(['show_context'],function(event1){
        if(event1.show_context==undefined){
            event1.show_context="tab";
        }
        if(event1.show_context=="tab"){
            chrome.tabs.create({url:"https://archive.org/details/" + metadata.identifier});
        }else{
          chrome.system.display.getInfo(function(displayInfo){
            let height = displayInfo[0].bounds.height;
            let width = displayInfo[0].bounds.width;
            chrome.windows.create({url:"https://archive.org/details/" + metadata.identifier, width:width/2, height:height, top:0, left:0, focused:true});
          });
        }
    });
  });
  img.setAttribute("src", "https://archive.org/services/img/" + metadata.identifier);
  img.setAttribute("class", "cover-img");
  button.appendChild(document.createTextNode("Read Now"));
  details.appendChild(img);
  details.appendChild(button);
  strong.appendChild(document.createTextNode(metadata.title));
  author.appendChild(document.createTextNode(metadata.creator));
  title.appendChild(strong);
  text_elements.appendChild(title);
  text_elements.appendChild(author);
  book.appendChild(text_elements);
  book.appendChild(details);
  if(resultsTray.childNodes.length >0){
    resultsTray.insertBefore(book, resultsTray.childNodes[0]);
  }else{
    resultsTray.appendChild(book);
  }
}

function addBookFromOpenLibrary(metadata){
  let resultsTray = document.getElementById("resultsTray");
  let book = document.createElement('div');
  let title = document.createElement('p');
  let strong = document.createElement('strong');
  let author = document.createElement('p');
  let details = document.createElement('div');
  let text_elements = document.createElement('div');
  let img = document.createElement("img");
  let button = document.createElement("a");

  text_elements.setAttribute("class", "text_elements");
  img.setAttribute("class", "cover-img");
  details.setAttribute("class", "bottom_details");
  button.setAttribute("class", "btn btn-warning resize_fit_center");
  button.setAttribute("href", "#");

  button.addEventListener("click", function(){
    chrome.storage.sync.get(['show_context'],function(event1){
        if(event1.show_context==undefined){
            event1.show_context="tab";
        }
        if(event1.show_context=="tab"){
            chrome.tabs.create({url:"https://archive.org/donate/"});
        }else{
          chrome.system.display.getInfo(function(displayInfo){
            let height = displayInfo[0].bounds.height;
            let width = displayInfo[0].bounds.width;
            chrome.windows.create({url:"https://archive.org/donate/", width:width/2, height:height, top:0, left:0, focused:true});
          });
        }
    });
  });

  if(metadata.covers){
    img.setAttribute("src", "http://covers.openlibrary.org/w/id/"+metadata.covers[0]+"-M.jpg");
  }else{ //TODO: find cover
    img = document.createElement("p");
    img.appendChild(document.createTextNode("No cover available"));
    img.setAttribute("class", "cover-img");
  }
  button.appendChild(document.createTextNode("Donate"));
  details.appendChild(img);
  details.appendChild(button);
  strong.appendChild(document.createTextNode(metadata.title));
  title.appendChild(strong);
  if(metadata.authors_metadata){
    author.appendChild(document.createTextNode(metadata.authors_metadata.name));
  }
  text_elements.appendChild(title);
  text_elements.appendChild(author);
  book.appendChild(text_elements);
  book.appendChild(details);
  resultsTray.appendChild(book);
}

function getAuthorFromOpenLibrary(key){
  let url = "http://openlibrary.org"+ key + ".json";
  return fetch(url)
    .then(res=>res.json())
    .then(json => json.name)
    .catch(err => console.log(err));
}

if(typeof module !=="undefined") {module.exports = {getUrlByParameter:getUrlByParameter};}
<|endoftext|>


<commit_before>var w = 720;
var h = 200;

void setup() {
  size(w, h);
  background(100,100,255);
  //noLoop();
}

void draw() {
  /*
  var brk = {
    "canPass":false,
    "draw": draw_brick(x,y);
  };

  var rug = {
    "canPass":true,
    "draw": draw_rug(x,y);
  };

  var map = [[brk, brk, brk, brk, brk, brk, brk, brk, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, brk, brk, brk, brk, brk, brk, brk, brk]];

  for(y = 0, y < map.length; y++) {
    for(x = 0, x < map[y].length; x++) {
      map[y][x].draw(x,y);
    }
  }
  */

  for(var y = 1; y < 11; y++) {
     draw_water(1,y);
     draw_swamp(2,y);
     draw_grass(3,y);
     draw_path(4,y);
     draw_brick(5,y);
     draw_brick(34,y);
     draw_grass(35,y);
     draw_desert(36,y);
   }

  draw_brick(6,1);
  draw_brick(6,10);
    draw_brick(33,1);
  draw_brick(33,10);

    for(var y = 2; y < 10; y++) {
     draw_floor(6,y);
     draw_floor(33,y);
   }

  for(var x = 7; x < 33; x++) {
    draw_brick(x,1);
    draw_brick(x,10);
    draw_floor(x,9);
    draw_floor(x,2);
    for(var y = 3; y < 9; y++) {
      draw_rug(x,y);
    }
  }

draw_PlayerFacingDown();
}

void reload() {
  redraw();
  updateStats();
}

void draw_PlayerFacingDown() {
  fill(player.skin[0],player.skin[1],player.skin[2]);
  //ears
  ellipse(player.x-6-player.face*2,player.y-12,4,4);
  ellipse(player.x+6+player.face*2,player.y-12,4,4);
   // head
  ellipse(player.x,player.y-10,12+player.face*4,20);

  // arms
  rect(player.x-10,player.y+8,4,4);
  rect(player.x+6,player.y+8,4,4);

  // tshirt
  fill(player.tColor[0],player.tColor[1],player.tColor[2]);
  rect(player.x-6,player.y,12,14);
  rect(player.x-10,player.y,4,8);
  rect(player.x+6,player.y,4,8);

  /*
  // hat
  rect(player.x-player.face-6, player.y-25, player.face*2+12, 5);
  fill(0,0,100);
  rect(player.x-player.face-6-3, player.y-21, 3, 2);
  */

  // tshirt logo
  fill(255,255,255);
  text(player.tshirt,player.x-3,player.y+10);

  // eye whites
  fill(255,255,255);
  ellipse(player.x-3-player.face,player.y-11,4,5);
  ellipse(player.x+3+player.face,player.y-11,4,5);

  //pupils
  fill(0,0,0);
  ellipse(player.x-3-player.face,player.y-11,1,1);
  ellipse(player.x+3+player.face,player.y-11,1,1);

  // mouth
  rect(player.x-3,player.y-5,6,1);

  // hair
  for(x = player.x-player.face*2; x <= player.x+player.face*2; x+=2) {
    noFill();
    stroke(player.hairColor[0],player.hairColor[1],player.hairColor[2]);
    ellipse(x,player.y-20,2,3);
  }
  stroke(0,0,0);

  //pants
  fill(player.pColor[0],player.pColor[1],player.pColor[2]);
  noStroke();
  rect(player.x-6,player.y+16,4,3);
  rect(player.x+2, player.y+16,4,3);
  rect(player.x-6,player.y+14,12,2);
  stroke(0,0,0);

  //shoes
  fill(0,0,0);
  rect(player.x-5,player.y+18,5,2);
  rect(player.x+2,player.y+18,5,2);
}

//rug
void draw_rug(x,y) {
  noStroke();
  x = 20*x-20;
  y = 20*y-20;
  for(var xi = x; xi < x+20; xi+=4) {
      for(var yi = y; yi < y+20; yi+=4) {
          noStroke();
          fill(random(175,200), 0, 0);
          rect(xi,yi,4,4);
      }
  }
}

void draw_brick(x,y) {
  //stroke(255, 255, 255);
  x = 20*x-20;
  y = 20*y-20;
  for(var yi = y; yi < y+16; yi+=8) {
    //1st line
    stroke(30, 0, 0);
    fill(random(31,255),0,0);
    rect(x,yi,6,4);
    fill(random(31,225),0,0);
    rect(x+7,yi,7,4);
    stroke(30, 0, 0);
    fill(random(31,225),0,0);
    rect(x+14,yi,6,4);
    //2nd line
    noStroke();
    fill(random(31,255),0,0);
    rect(x,yi+4,4,4);
    fill(random(31,225),0,0);
    rect(x+16,yi+4,4,4);
    stroke(30, 0, 0);
    fill(random(31,225),0,0);
    rect(x+4,yi+4,7,4);
    fill(random(31,225),0,0);
    rect(x+10,yi+4,6,4);
  }
  //last line
  var yi = y+16;
  stroke(30, 0, 0);
  fill(random(31,255),0,0);
  rect(x,yi,6,4);
  fill(random(31,225),0,0);
  rect(x+7,yi,7,4);
  stroke(30, 0, 0);
  fill(random(31,225),0,0);
  rect(x+14,yi,6,4);
  rect(x+10,yi,6,4);
  noStroke();
  }

  void draw_grass(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(75,100),random(225,255),random(75,100));
            rect(xi,yi,4,4);
        }
    }
  }

void draw_floor(x,y) {
    //stroke(255, 255, 255);
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+16; xi+=8) {
            //1st line
            //stroke(51, 32, 2);
            fill(random(100,125),random(75,100),0);
            rect(xi,y,4,7);
            fill(random(100,125),random(75,100),0);
            rect(xi,y+7,4,7);
            //stroke(30, 0, 0);
            fill(random(100,125),random(75,100),0);
            rect(xi,y+14,4,6);
            //2nd line
            noStroke();
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y,4,4);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+16,4,4);
            //stroke(30, 0, 0);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+4,4,7);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+10,4,6);
    }
    //last line
    var xi = x+16;
    //stroke(51, 32, 2);
    fill(random(100,125),random(75,100),0);
    rect(xi,y,4,7);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+7,4,7);
    //stroke(30, 0, 0);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+14,4,6);
  }

  void draw_path(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(175,200), random(175,200), 0);
            rect(xi,yi,4,4);
        }
    }
  }

  void draw_desert(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            //224, 166, 137
            fill(random(230,255), random(200,229), random(175,199));
            rect(xi,yi,4,4);
        }
    }
}

void draw_swamp(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(0,random(50,200),random(50,150));
            rect(xi,yi,4,4);
        }
    }
  }

void draw_water(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(0,50),random(0,50),random(225,255));
            rect(xi,yi,4,4);
        }
    }
}

<commit_msg>Update drawgame.js<commit_after>var w = 720;
var h = 200;

void setup() {
  size(w, h);
  background(100,100,255);
  //noLoop();
}

void draw() {
  /*
  var brk = {
    "canPass":false,
    "draw": draw_brick(x,y);
  };

  var rug = {
    "canPass":true,
    "draw": draw_rug(x,y);
  };

  var map = [[brk, brk, brk, brk, brk, brk, brk, brk, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, rug, rug, rug, rug, rug, rug, rug, brk],
             [brk, brk, brk, brk, brk, brk, brk, brk, brk]];

  for(y = 0, y < map.length; y++) {
    for(x = 0, x < map[y].length; x++) {
      map[y][x].draw(x,y);
    }
  }
  */

  for(var y = 1; y < 11; y++) {
     draw_bg('water',1,y);
     draw_bg('swamp',2,y);
     draw_bg('grass',3,y);
     draw_bg('path',4,y);
     draw_bg('brick',5,y);
     draw_bg('brick',34,y);
     draw_bg('path',35,y);
     draw_bg('sand',36,y);
   }

  draw_bg('brick',6,1);
  draw_bg('brick',6,10);
    draw_bg('brick',33,1);
  draw_bg('brick',33,10);

    for(var y = 2; y < 10; y++) {
     draw_bg('floor',6,y);
     draw_bg('floor',33,y);
   }

  for(var x = 7; x < 33; x++) {
    draw_bg('brick',x,1);
    draw_bg('brick',x,10);
    draw_bg('floor',x,9);
    draw_bg('floor',x,2);
    for(var y = 3; y < 9; y++) {
      draw_bg('rug',x,y);
    }
  }

draw_PlayerFacingDown();
}

void reload() {
  redraw();
  updateStats();
}

void draw_PlayerFacingDown() {
  fill(player.skin[0],player.skin[1],player.skin[2]);
  //ears
  ellipse(player.x-6-player.face*2,player.y-12,4,4);
  ellipse(player.x+6+player.face*2,player.y-12,4,4);
   // head
  ellipse(player.x,player.y-10,12+player.face*4,20);

  // arms
  rect(player.x-10,player.y+8,4,4);
  rect(player.x+6,player.y+8,4,4);

  // tshirt
  fill(player.tColor[0],player.tColor[1],player.tColor[2]);
  rect(player.x-6,player.y,12,14);
  rect(player.x-10,player.y,4,8);
  rect(player.x+6,player.y,4,8);

  /*
  // hat
  rect(player.x-player.face-6, player.y-25, player.face*2+12, 5);
  fill(0,0,100);
  rect(player.x-player.face-6-3, player.y-21, 3, 2);
  */

  // tshirt logo
  fill(255,255,255);
  text(player.tshirt,player.x-3,player.y+10);

  // eye whites
  fill(255,255,255);
  ellipse(player.x-3-player.face,player.y-11,4,5);
  ellipse(player.x+3+player.face,player.y-11,4,5);

  //pupils
  fill(0,0,0);
  ellipse(player.x-3-player.face,player.y-11,1,1);
  ellipse(player.x+3+player.face,player.y-11,1,1);

  // mouth
  rect(player.x-3,player.y-5,6,1);

  // hair
  for(x = player.x-player.face*2; x <= player.x+player.face*2; x+=2) {
    noFill();
    stroke(player.hairColor[0],player.hairColor[1],player.hairColor[2]);
    ellipse(x,player.y-20,2,3);
  }
  stroke(0,0,0);

  //pants
  fill(player.pColor[0],player.pColor[1],player.pColor[2]);
  noStroke();
  rect(player.x-6,player.y+16,4,3);
  rect(player.x+2, player.y+16,4,3);
  rect(player.x-6,player.y+14,12,2);
  stroke(0,0,0);

  //shoes
  fill(0,0,0);
  rect(player.x-5,player.y+18,5,2);
  rect(player.x+2,player.y+18,5,2);
}

void draw_bg(bgo, x,y) {
  switch(bgo) {
    case 'brick':
      //stroke(255, 255, 255);
      x = 20*x-20;
      y = 20*y-20;
      for(var yi = y; yi < y+16; yi+=8) {
        //1st line
        stroke(30, 0, 0);
        fill(random(31,255),0,0);
        rect(x,yi,6,4);
        fill(random(31,225),0,0);
        rect(x+7,yi,7,4);
        stroke(30, 0, 0);
        fill(random(31,225),0,0);
        rect(x+14,yi,6,4);
        //2nd line
        noStroke();
        fill(random(31,255),0,0);
        rect(x,yi+4,4,4);
        fill(random(31,225),0,0);
        rect(x+16,yi+4,4,4);
        stroke(30, 0, 0);
        fill(random(31,225),0,0);
        rect(x+4,yi+4,7,4);
        fill(random(31,225),0,0);
        rect(x+10,yi+4,6,4);
      }
      //last line
      var yi = y+16;
      stroke(30, 0, 0);
      fill(random(31,255),0,0);
      rect(x,yi,6,4);
      fill(random(31,225),0,0);
      rect(x+7,yi,7,4);
      stroke(30, 0, 0);
      fill(random(31,225),0,0);
      rect(x+14,yi,6,4);
      rect(x+10,yi,6,4);
      noStroke();
      break;
    case 'rug':
        noStroke();
        x = 20*x-20;
        y = 20*y-20;
        for(var xi = x; xi < x+20; xi+=4) {
          for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(175,200), 0, 0);
            rect(xi,yi,4,4);
          }
        }
        break;
    }
  case 'grass':
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(75,100),random(225,255),random(75,100));
            rect(xi,yi,4,4);
        }
    }
  break;
  case 'floor':
     //stroke(255, 255, 255);
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+16; xi+=8) {
        //1st line
        //stroke(51, 32, 2);
        fill(random(100,125),random(75,100),0);
        rect(xi,y,4,7);
        fill(random(100,125),random(75,100),0);
        rect(xi,y+7,4,7);
        //stroke(30, 0, 0);
        fill(random(100,125),random(75,100),0);
        rect(xi,y+14,4,6);
        //2nd line
        noStroke();
        fill(random(100,125),random(75,100),0);
        rect(xi+4,y,4,4);
        fill(random(100,125),random(75,100),0);
        rect(xi+4,y+16,4,4);
        //stroke(30, 0, 0);
        fill(random(100,125),random(75,100),0);
        rect(xi+4,y+4,4,7);
        fill(random(100,125),random(75,100),0);
        rect(xi+4,y+10,4,6);
    }
    //last line
    var xi = x+16;
    //stroke(51, 32, 2);
    fill(random(100,125),random(75,100),0);
    rect(xi,y,4,7);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+7,4,7);
    //stroke(30, 0, 0);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+14,4,6);
    break;
  case 'path':
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(175,200), random(175,200), 0);
            rect(xi,yi,4,4);
        }
    }
    break;
  case 'sand':
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            //224, 166, 137
            fill(random(230,255), random(200,229), random(175,199));
            rect(xi,yi,4,4);
        }
    }
    break;
  case 'swamp':
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(0,random(50,200),random(50,150));
            rect(xi,yi,4,4);
        }
    }
    break;
  case 'water':
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(0,50),random(0,50),random(225,255));
            rect(xi,yi,4,4);
        }
    }
    break;
}

<|endoftext|>


<commit_before>var w = 720;
var h = 200;

void setup() {
  size(w, h);
  background(100,100,255);
}

void draw() {

  /*
  function drawPath(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(175,200), random(175,200), 0);
            rect(xi,yi,4,4);
        }
    }
  }

  function drawDesert(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            //224, 166, 137
            fill(random(230,255), random(200,229), random(175,199));
            rect(xi,yi,4,4);
        }
    }
}

function drawSwamp(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(0,random(50,200),random(50,150));
            rect(xi,yi,4,4);
        }
    }
  }

  function drawfLoor(x,y) {
    //stroke(255, 255, 255);
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+16; xi+=8) {
            //1st line
            //stroke(51, 32, 2);
            fill(random(100,125),random(75,100),0);
            rect(xi,y,4,7);
            fill(random(100,125),random(75,100),0);
            rect(xi,y+7,4,7);
            //stroke(30, 0, 0);
            fill(random(100,125),random(75,100),0);
            rect(xi,y+14,4,6);
            //2nd line
            noStroke();
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y,4,4);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+16,4,4);
            //stroke(30, 0, 0);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+4,4,7);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+10,4,6);
    }
    //last line
    var xi = x+16;
    //stroke(51, 32, 2);
    fill(random(100,125),random(75,100),0);
    rect(xi,y,4,7);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+7,4,7);
    //stroke(30, 0, 0);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+14,4,6);
}
*/

  for(var y = 1; y < 11; y++) {
     draw_Brick(1,y);
     draw_Brick(36,y);
   }

  for(var x = 2; x < 36; x++) {
    draw_Brick(x,1);
    draw_Brick(x,10);
    for(var y = 2; y < 10; y++) {
      draw_rUg(x,y);
    }
  }

draw_PlayerFacingDown();
}

void draw_PlayerFacingDown() {
  fill(player.skin[0],player.skin[1],player.skin[2]);
  //ears
  ellipse(player.x-6-player.face*2,player.y-12,4,4);
  ellipse(player.x+6+player.face*2,player.y-12,4,4);
   // head
  ellipse(player.x,player.y-10,12+player.face*4,20);

  // arms
  rect(player.x-10,player.y+8,4,4);
  rect(player.x+6,player.y+8,4,4);

  // tshirt
  fill(player.tColor[0],player.tColor[1],player.tColor[2]);
  rect(player.x-6,player.y,12,14);
  rect(player.x-10,player.y,4,8);
  rect(player.x+6,player.y,4,8);

  /*
  // hat
  rect(player.x-player.face-6, player.y-25, player.face*2+12, 5);
  fill(0,0,100);
  rect(player.x-player.face-6-3, player.y-21, 3, 2);
  */

  // tshirt logo
  fill(255,255,255);
  text(player.tshirt,player.x-3,player.y+10);

  // eye whites
  fill(255,255,255);
  ellipse(player.x-3-player.face,player.y-11,4,5);
  ellipse(player.x+3+player.face,player.y-11,4,5);

  //pupils
  fill(0,0,0);
  ellipse(player.x-3-player.face,player.y-11,1,1);
  ellipse(player.x+3+player.face,player.y-11,1,1);

  // mouth
  rect(player.x-3,player.y-5,6,1);

  // hair
  for(x = player.x-player.face*2; x <= player.x+player.face*2; x+=2) {
    noFill();
    stroke(player.hairColor[0],player.hairColor[1],player.hairColor[2]);
    ellipse(x,player.y-20,2,3);
  }
  stroke(0,0,0);

  //pants
  fill(player.pColor[0],player.pColor[1],player.pColor[2]);
  noStroke();
  rect(player.x-6,player.y+16,4,3);
  rect(player.x+2, player.y+16,4,3);
  rect(player.x-6,player.y+14,12,2);
  stroke(0,0,0);

  //shoes
  fill(0,0,0);
  rect(player.x-5,player.y+18,5,2);
  rect(player.x+2,player.y+18,5,2);
}

//rug
void draw_rUg(x,y) {
  noStroke();
  x = 20*x-20;
  y = 20*y-20;
  for(var xi = x; xi < x+20; xi+=4) {
      for(var yi = y; yi < y+20; yi+=4) {
          noStroke();
          fill(random(175,200), 0, 0);
          rect(xi,yi,4,4);
      }
  }
}

void draw_Brick(x,y) {
  //stroke(255, 255, 255);
  x = 20*x-20;
  y = 20*y-20;
  for(var yi = y; yi < y+16; yi+=8) {
    //1st line
    stroke(30, 0, 0);
    fill(random(31,255),0,0);
    rect(x,yi,6,4);
    fill(random(31,225),0,0);
    rect(x+7,yi,7,4);
    stroke(30, 0, 0);
    fill(random(31,225),0,0);
    rect(x+14,yi,6,4);
    //2nd line
    noStroke();
    fill(random(31,255),0,0);
    rect(x,yi+4,4,4);
    fill(random(31,225),0,0);
    rect(x+16,yi+4,4,4);
    stroke(30, 0, 0);
    fill(random(31,225),0,0);
    rect(x+4,yi+4,7,4);
    fill(random(31,225),0,0);
    rect(x+10,yi+4,6,4);
  }
  //last line
  var yi = y+16;
  stroke(30, 0, 0);
  fill(random(31,255),0,0);
  rect(x,yi,6,4);
  fill(random(31,225),0,0);
  rect(x+7,yi,7,4);
  stroke(30, 0, 0);
  fill(random(31,225),0,0);
  rect(x+14,yi,6,4);
  rect(x+10,yi,6,4);
  noStroke();
  }

  /*
  void draw_Grass(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(75,100),random(225,255),random(75,100));
            rect(xi,yi,4,4);
        }
    }
  }
  */
<commit_msg>Update drawgame.js<commit_after>var w = 720;
var h = 200;

var brk = {
  "canPass":false,
  "draw": draw_Brick();
};

var rug = {
  "canPass":true,
  "draw": draw_rUg();
};

var map = [[brk, brk, brk, brk, brk, brk, brk, brk, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, rug, rug, rug, rug, rug, rug, rug, brk],
           [brk, brk, brk, brk, brk, brk, brk, brk, brk]];

void setup() {
  size(w, h);
  background(100,100,255);
}

void draw() {

  /*
  function drawPath(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(175,200), random(175,200), 0);
            rect(xi,yi,4,4);
        }
    }
  }

  function drawDesert(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            //224, 166, 137
            fill(random(230,255), random(200,229), random(175,199));
            rect(xi,yi,4,4);
        }
    }
}

function drawSwamp(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(0,random(50,200),random(50,150));
            rect(xi,yi,4,4);
        }
    }
  }

  function drawfLoor(x,y) {
    //stroke(255, 255, 255);
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+16; xi+=8) {
            //1st line
            //stroke(51, 32, 2);
            fill(random(100,125),random(75,100),0);
            rect(xi,y,4,7);
            fill(random(100,125),random(75,100),0);
            rect(xi,y+7,4,7);
            //stroke(30, 0, 0);
            fill(random(100,125),random(75,100),0);
            rect(xi,y+14,4,6);
            //2nd line
            noStroke();
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y,4,4);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+16,4,4);
            //stroke(30, 0, 0);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+4,4,7);
            fill(random(100,125),random(75,100),0);
            rect(xi+4,y+10,4,6);
    }
    //last line
    var xi = x+16;
    //stroke(51, 32, 2);
    fill(random(100,125),random(75,100),0);
    rect(xi,y,4,7);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+7,4,7);
    //stroke(30, 0, 0);
    fill(random(100,125),random(75,100),0);
    rect(xi,y+14,4,6);
}
*/

  for(var y = 1; y < 11; y++) {
     draw_Brick(1,y);
     draw_Brick(36,y);
   }

  for(var x = 2; x < 36; x++) {
    draw_Brick(x,1);
    draw_Brick(x,10);
    for(var y = 2; y < 10; y++) {
      draw_rUg(x,y);
    }
  }

draw_PlayerFacingDown();
}

void draw_PlayerFacingDown() {
  fill(player.skin[0],player.skin[1],player.skin[2]);
  //ears
  ellipse(player.x-6-player.face*2,player.y-12,4,4);
  ellipse(player.x+6+player.face*2,player.y-12,4,4);
   // head
  ellipse(player.x,player.y-10,12+player.face*4,20);

  // arms
  rect(player.x-10,player.y+8,4,4);
  rect(player.x+6,player.y+8,4,4);

  // tshirt
  fill(player.tColor[0],player.tColor[1],player.tColor[2]);
  rect(player.x-6,player.y,12,14);
  rect(player.x-10,player.y,4,8);
  rect(player.x+6,player.y,4,8);

  /*
  // hat
  rect(player.x-player.face-6, player.y-25, player.face*2+12, 5);
  fill(0,0,100);
  rect(player.x-player.face-6-3, player.y-21, 3, 2);
  */

  // tshirt logo
  fill(255,255,255);
  text(player.tshirt,player.x-3,player.y+10);

  // eye whites
  fill(255,255,255);
  ellipse(player.x-3-player.face,player.y-11,4,5);
  ellipse(player.x+3+player.face,player.y-11,4,5);

  //pupils
  fill(0,0,0);
  ellipse(player.x-3-player.face,player.y-11,1,1);
  ellipse(player.x+3+player.face,player.y-11,1,1);

  // mouth
  rect(player.x-3,player.y-5,6,1);

  // hair
  for(x = player.x-player.face*2; x <= player.x+player.face*2; x+=2) {
    noFill();
    stroke(player.hairColor[0],player.hairColor[1],player.hairColor[2]);
    ellipse(x,player.y-20,2,3);
  }
  stroke(0,0,0);

  //pants
  fill(player.pColor[0],player.pColor[1],player.pColor[2]);
  noStroke();
  rect(player.x-6,player.y+16,4,3);
  rect(player.x+2, player.y+16,4,3);
  rect(player.x-6,player.y+14,12,2);
  stroke(0,0,0);

  //shoes
  fill(0,0,0);
  rect(player.x-5,player.y+18,5,2);
  rect(player.x+2,player.y+18,5,2);
}

//rug
void draw_rUg(x,y) {
  noStroke();
  x = 20*x-20;
  y = 20*y-20;
  for(var xi = x; xi < x+20; xi+=4) {
      for(var yi = y; yi < y+20; yi+=4) {
          noStroke();
          fill(random(175,200), 0, 0);
          rect(xi,yi,4,4);
      }
  }
}

void draw_Brick(x,y) {
  //stroke(255, 255, 255);
  x = 20*x-20;
  y = 20*y-20;
  for(var yi = y; yi < y+16; yi+=8) {
    //1st line
    stroke(30, 0, 0);
    fill(random(31,255),0,0);
    rect(x,yi,6,4);
    fill(random(31,225),0,0);
    rect(x+7,yi,7,4);
    stroke(30, 0, 0);
    fill(random(31,225),0,0);
    rect(x+14,yi,6,4);
    //2nd line
    noStroke();
    fill(random(31,255),0,0);
    rect(x,yi+4,4,4);
    fill(random(31,225),0,0);
    rect(x+16,yi+4,4,4);
    stroke(30, 0, 0);
    fill(random(31,225),0,0);
    rect(x+4,yi+4,7,4);
    fill(random(31,225),0,0);
    rect(x+10,yi+4,6,4);
  }
  //last line
  var yi = y+16;
  stroke(30, 0, 0);
  fill(random(31,255),0,0);
  rect(x,yi,6,4);
  fill(random(31,225),0,0);
  rect(x+7,yi,7,4);
  stroke(30, 0, 0);
  fill(random(31,225),0,0);
  rect(x+14,yi,6,4);
  rect(x+10,yi,6,4);
  noStroke();
  }

  /*
  void draw_Grass(x,y) {
    noStroke();
    x = 20*x-20;
    y = 20*y-20;
    for(var xi = x; xi < x+20; xi+=4) {
        for(var yi = y; yi < y+20; yi+=4) {
            noStroke();
            fill(random(75,100),random(225,255),random(75,100));
            rect(xi,yi,4,4);
        }
    }
  }
  */
<|endoftext|>