<commit_before>(function(angular) {
    'use strict';

    PagesManagerFactory.$inject = ['posts', '$q', 'config'];
    function PagesManagerFactory(postsService, $q, config) {

        function PagesManager (max_results, sort) {
            var SORTS = {
                'editorial' : {order: {order: 'desc', missing:'_last', unmapped_type: 'long'}},
                'newest_first' : {_created: {order: 'desc', missing:'_last', unmapped_type: 'long'}},
                'oldest_first' : {_created: {order: 'asc', missing:'_last', unmapped_type: 'long'}}
            };
            var self = this;

            /**
             * Represent a page of posts
             * @param {array} [posts=[]] - a list of post to initialize the page
             */
            function Page(posts) {
                return {
                    posts: posts || [],
                    addPost: function(post) {this.posts.push(post);}
                };
            }

            /**
             * Retrieve the given page of post from the api
             * @param {integer} page - The page index to retrieve (start from 1)
             * @param {integer} [max_results=self.maxResults] - The maximum number of results to retrieve
             * @returns {promise}
             */
            function retrievePage(page, max_results) {
                // set request parameters
                var posts_criteria = {
                    source: {
                        query: {filtered: {filter: {and: [{term: {post_status: 'open'}}, {not: {term: {deleted: true}}}]}}},
                        sort: [SORTS[self.sort]]
                    },
                    page: page,
                    max_results: max_results || self.maxResults
                };
                return postsService.get(posts_criteria).$promise.then(function(data) {
                    // update posts meta data (used to know the total number of posts and pages)
                    self.meta = data._meta;
                    return data;
                });
            }

            /**
             * Change the order in the future posts request, remove exising post and load a new page
             * @param {string} sort_name - The name of the new order (see self.SORTS)
             * @returns {promise}
             */
            function changeOrder(sort_name) {
                self.sort = sort_name;
                self.pages = [];
                return fetchNewPage();
            }

            /**
             * Getter or Settre the order in the future posts request
             * Gets the order current used.
             */
            function order(sort_name) {
                if(sort_name) {
                    self.sort = sort_name;
                } else {
                    return self.sort;
                }
            }

            /**
             * Fetch a new page of posts and add it to the Pages Manager.
             * @returns {promise}
             */
            function fetchNewPage() {
                var promise = $q.when();
                // for the first time, retrieve the updates just to know the latest update date
                if (self.pages.length === 0) {
                    promise = self.retrieveUpdate().then(function(updates) {
                        updateLatestDates(updates._items);
                    });
                }
                return promise.then(function() {
                    return reloadPagesFrom(0, self.pages.length + 1);
                });
            }

            /**
             * Retrieve all the updates since the latest updated date
             * @param {boolean} [should_apply_updates=false] - If true, will apply the updates into the posts list
             * @returns {promise}
             */
            function retrieveUpdate(should_apply_updates) {
                should_apply_updates = should_apply_updates === true;
                var date = self.latestUpdatedDate ? self.latestUpdatedDate.utc().format() : undefined;
                var posts_criteria = {
                    page: 1,
                    source: {
                        sort: [{_updated: {order: 'desc'}}],
                        query: {filtered: {filter: {and: [
                            {range: {_updated: {gt: date}}}
                        ]}}}
                    }
                };
                return postsService.get(posts_criteria).$promise.then(function(updates) {
                    var meta = updates._meta;
                    // if
                    // - there is no other page
                    // - or if we don't give a latest update date (b/c we look after the meta or the latest date)
                    // = then we return the first page of result
                    if (meta.total <= meta.max_results * meta.page || !angular.isDefined(date)) {
                        return updates;
                    // Otherwise we ask page after page and concatenate them in the response
                    } else {
                        var promises = [];
                        for (var i = meta.page + 1; i <= Math.floor(meta.total / meta.max_results) + 1; i++) {
                            posts_criteria.page = i;
                            promises.push(postsService.get(posts_criteria).$promise);
                        }
                        return $q.all(promises).then(function(updates_pages) {
                            return angular.extend({} ,updates_pages[0], {
                                _items: [].concat.apply([], updates_pages.map(function(update) {return update._items;})),
                                _meta: angular.extend(meta, {max_results: meta.max_results * updates_pages.length})
                            });
                        });
                    }
                })
                // Apply the update if needed
                .then(function(updates) {
                    if (should_apply_updates) {
                        applyUpdates(updates._items);
                    }
                    return updates;
                });
            }

            /**
             * Apply the given updates to the posts list
             * @param {array} updates - List of updated posts
             */
            function applyUpdates(updates) {
                updates.forEach(function(post) {
                    var existing_post_indexes = getPostPageIndexes(post);
                    if (angular.isDefined(existing_post_indexes)) {
                        // post already in the list
                        if (post.deleted) {
                            // post deleted
                            removePost(post);
                        } else {
                            // post updated
                            if (post.post_status === 'draft') {
                               removePost(post);
                            } else {
                                // update
                                self.pages[existing_post_indexes[0]].posts[existing_post_indexes[1]] = post;
                                createPagesWithPosts();
                           }
                        }
                    } else {
                        // post doesn't exist in the list
                        if (!post.deleted && post.post_status === 'open') {
                            addPost(post);
                        }
                    }
                });
                // update date
                updateLatestDates(updates);
            }

            /**
             * Update the latest update date by using the given posts
             * @param {array} posts - List of posts
             */
            function updateLatestDates(posts) {
                var date;
                posts.forEach(function(post) {
                    date = moment(post._updated);
                    if (angular.isDefined(self.latestUpdatedDate)) {
                        if (self.latestUpdatedDate.diff(date) < 0) {
                            self.latestUpdatedDate = date;
                        }
                    } else {
                        self.latestUpdatedDate = date;
                    }
                });
            }

            /**
             * Recreate the pages from the given posts
             * @param {array} [posts=self.allPosts()] - List of posts
             */
            function createPagesWithPosts(posts) {
                posts = posts || self.allPosts();
                self.pages = [];
                // respect the order
                var sort_by = Object.keys(SORTS[self.sort])[0];
                var order_by = SORTS[self.sort][sort_by].order;
                posts = _.sortByOrder(posts, sort_by, order_by);
                var page;
                posts.forEach(function(post, index) {
                    if (index % self.maxResults === 0) {
                        page = new Page();
                    }
                    page.addPost(post);
                    if (page.posts.length === self.maxResults) {
                        addPage(page);
                        page = undefined;
                    }
                });
                if (angular.isDefined(page)) {
                    addPage(page);
                }
            }

            /**
             * Resynchronize the content of the given page and the following ones
             * @param {interger} page_index - index of the first page
             * @param {interger} [to_page=self.pages.length] - latest wanted page
             * @returns {promise}
             */
            function reloadPagesFrom(page_index, to_page) {
                to_page = to_page || self.pages.length;
                return retrievePage(1, to_page * self.maxResults).then(function(posts) {
                    createPagesWithPosts(posts._items);
                    return posts;

                });
            }

            /**
             * Returns the page index and the post index of the given post in the local pages
             * @param {Post} post_to_find - post to find in the pages
             * @returns {array|undefined} - [page_index, post_index]
             */
            function getPostPageIndexes(post_to_find){
                var page;
                for (var page_index = 0; page_index < self.pages.length; page_index++) {
                    page = self.pages[page_index];
                    for (var post_index = 0; post_index < page.posts.length; post_index++) {
                        if (page.posts[post_index]._id === post_to_find._id) {
                            return [page_index, post_index];
                        }
                    }
                }
            }

            /**
             * Add a post or a list of posts to the local pages
             * @param {Post|array<Post>} posts - posts to be added to the pages
             */
            function addPost(posts) {
                var all_posts = self.allPosts();
                if (!angular.isArray(posts)) {
                    posts = [posts];
                }
                // for every post, check if exist before or add it
                posts.forEach(function(post) {
                    if (!angular.isDefined(getPostPageIndexes(post))) {
                        all_posts.push(post);
                    }
                });
                // and recreate pages
                createPagesWithPosts(all_posts);
                // update date
                updateLatestDates(all_posts);
            }

            /**
             * Remove a post in the local pages
             * @param {Post} post_to_remove - posts to be removed from the pages
             */
            function removePost(post_to_remove) {
                var indexes = getPostPageIndexes(post_to_remove);
                if (angular.isDefined(post_to_remove)) {
                    var page_index = indexes[0];
                    var post_index = indexes[1];
                    self.pages[page_index].posts.splice(post_index, 1);
                    createPagesWithPosts(self.allPosts());
                }
            }

            /**
             * Add the given page to the Page Manager
             * @param {Page} page - a page instance
             */
            function addPage(page) {
                self.pages.push(page);
            }

            /**
             * Returns the number of posts in the local pages
             * @returns {integer}
             */
            function count() {
                return self.pages.reduce(function(previous_value, current_value) {
                    return previous_value + current_value.posts.length;
                }, 0);
            }

            angular.extend(self, {
                /**
                 * List of page instances
                 */
                pages: [],
                /**
                 * Represent the meta data of posts (total number for instance)
                 */
                meta: {},
                /**
                 * Set the initial order (see self.SORTS)
                 */
                sort: sort || 'editorial',
                /**
                 * Change the order in the future posts request, remove exising post and load a new page
                 */
                changeOrder: changeOrder,
                /**
                 * Setter or Getter the order in the future posts request.
                 */
                order: order,
                /**
                 * Number of results per page
                 */
                maxResults: max_results || config.settings.postsPerPage,
                /**
                 * Latest updated date. Used for retrieving updates since this date.
                 */
                latestUpdatedDate: undefined,
                /**
                 * Fetch a new page of posts
                 */
                fetchNewPage: fetchNewPage,
                /**
                 * Return the latest available updates
                 */
                retrieveUpdate: retrieveUpdate,
                /**
                 * Return all the posts from the local pages
                 */
                allPosts: function () {
                    var posts = self.pages.map(function(page) {
                        return page.posts.map(function(post) {
                            return post;
                        });
                    });
                    // flatten array
                    var merged = [];
                    return merged.concat.apply(merged, posts);
                }
            });
        }

        // return the Pages Manager constructor
        return PagesManager;
    }

    angular.module('liveblog-embed')
        .factory('PagesManager', PagesManagerFactory);

})(angular);
<commit_msg>fix: remove unpublished posts<commit_after>(function(angular) {
    'use strict';

    PagesManagerFactory.$inject = ['posts', '$q', 'config'];
    function PagesManagerFactory(postsService, $q, config) {

        function PagesManager (max_results, sort) {
            var SORTS = {
                'editorial' : {order: {order: 'desc', missing:'_last', unmapped_type: 'long'}},
                'newest_first' : {_created: {order: 'desc', missing:'_last', unmapped_type: 'long'}},
                'oldest_first' : {_created: {order: 'asc', missing:'_last', unmapped_type: 'long'}}
            };
            var self = this;

            /**
             * Represent a page of posts
             * @param {array} [posts=[]] - a list of post to initialize the page
             */
            function Page(posts) {
                return {
                    posts: posts || [],
                    addPost: function(post) {this.posts.push(post);}
                };
            }

            /**
             * Retrieve the given page of post from the api
             * @param {integer} page - The page index to retrieve (start from 1)
             * @param {integer} [max_results=self.maxResults] - The maximum number of results to retrieve
             * @returns {promise}
             */
            function retrievePage(page, max_results) {
                // set request parameters
                var posts_criteria = {
                    source: {
                        query: {filtered: {filter: {and: [{term: {post_status: 'open'}}, {not: {term: {deleted: true}}}]}}},
                        sort: [SORTS[self.sort]]
                    },
                    page: page,
                    max_results: max_results || self.maxResults
                };
                return postsService.get(posts_criteria).$promise.then(function(data) {
                    // update posts meta data (used to know the total number of posts and pages)
                    self.meta = data._meta;
                    return data;
                });
            }

            /**
             * Change the order in the future posts request, remove exising post and load a new page
             * @param {string} sort_name - The name of the new order (see self.SORTS)
             * @returns {promise}
             */
            function changeOrder(sort_name) {
                self.sort = sort_name;
                self.pages = [];
                return fetchNewPage();
            }

            /**
             * Getter or Settre the order in the future posts request
             * Gets the order current used.
             */
            function order(sort_name) {
                if(sort_name) {
                    self.sort = sort_name;
                } else {
                    return self.sort;
                }
            }

            /**
             * Fetch a new page of posts and add it to the Pages Manager.
             * @returns {promise}
             */
            function fetchNewPage() {
                var promise = $q.when();
                // for the first time, retrieve the updates just to know the latest update date
                if (self.pages.length === 0) {
                    promise = self.retrieveUpdate().then(function(updates) {
                        updateLatestDates(updates._items);
                    });
                }
                return promise.then(function() {
                    return reloadPagesFrom(0, self.pages.length + 1);
                });
            }

            /**
             * Retrieve all the updates since the latest updated date
             * @param {boolean} [should_apply_updates=false] - If true, will apply the updates into the posts list
             * @returns {promise}
             */
            function retrieveUpdate(should_apply_updates) {
                should_apply_updates = should_apply_updates === true;
                var date = self.latestUpdatedDate ? self.latestUpdatedDate.utc().format() : undefined;
                var posts_criteria = {
                    page: 1,
                    source: {
                        sort: [{_updated: {order: 'desc'}}],
                        query: {filtered: {filter: {and: [
                            {range: {_updated: {gt: date}}}
                        ]}}}
                    }
                };
                return postsService.get(posts_criteria).$promise.then(function(updates) {
                    var meta = updates._meta;
                    // if
                    // - there is no other page
                    // - or if we don't give a latest update date (b/c we look after the meta or the latest date)
                    // = then we return the first page of result
                    if (meta.total <= meta.max_results * meta.page || !angular.isDefined(date)) {
                        return updates;
                    // Otherwise we ask page after page and concatenate them in the response
                    } else {
                        var promises = [];
                        for (var i = meta.page + 1; i <= Math.floor(meta.total / meta.max_results) + 1; i++) {
                            posts_criteria.page = i;
                            promises.push(postsService.get(posts_criteria).$promise);
                        }
                        return $q.all(promises).then(function(updates_pages) {
                            return angular.extend({} ,updates_pages[0], {
                                _items: [].concat.apply([], updates_pages.map(function(update) {return update._items;})),
                                _meta: angular.extend(meta, {max_results: meta.max_results * updates_pages.length})
                            });
                        });
                    }
                })
                // Apply the update if needed
                .then(function(updates) {
                    if (should_apply_updates) {
                        applyUpdates(updates._items);
                    }
                    return updates;
                });
            }

            /**
             * Apply the given updates to the posts list
             * @param {array} updates - List of updated posts
             */
            function applyUpdates(updates) {
                updates.forEach(function(post) {
                    var existing_post_indexes = getPostPageIndexes(post);
                    if (angular.isDefined(existing_post_indexes)) {
                        // post already in the list
                        if (post.deleted) {
                            // post deleted
                            removePost(post);
                        } else {
                            // post updated
                            if (post.post_status !== 'open') {
                               removePost(post);
                            } else {
                                // update
                                self.pages[existing_post_indexes[0]].posts[existing_post_indexes[1]] = post;
                                createPagesWithPosts();
                           }
                        }
                    } else {
                        // post doesn't exist in the list
                        if (!post.deleted && post.post_status === 'open') {
                            addPost(post);
                        }
                    }
                });
                // update date
                updateLatestDates(updates);
            }

            /**
             * Update the latest update date by using the given posts
             * @param {array} posts - List of posts
             */
            function updateLatestDates(posts) {
                var date;
                posts.forEach(function(post) {
                    date = moment(post._updated);
                    if (angular.isDefined(self.latestUpdatedDate)) {
                        if (self.latestUpdatedDate.diff(date) < 0) {
                            self.latestUpdatedDate = date;
                        }
                    } else {
                        self.latestUpdatedDate = date;
                    }
                });
            }

            /**
             * Recreate the pages from the given posts
             * @param {array} [posts=self.allPosts()] - List of posts
             */
            function createPagesWithPosts(posts) {
                posts = posts || self.allPosts();
                self.pages = [];
                // respect the order
                var sort_by = Object.keys(SORTS[self.sort])[0];
                var order_by = SORTS[self.sort][sort_by].order;
                posts = _.sortByOrder(posts, sort_by, order_by);
                var page;
                posts.forEach(function(post, index) {
                    if (index % self.maxResults === 0) {
                        page = new Page();
                    }
                    page.addPost(post);
                    if (page.posts.length === self.maxResults) {
                        addPage(page);
                        page = undefined;
                    }
                });
                if (angular.isDefined(page)) {
                    addPage(page);
                }
            }

            /**
             * Resynchronize the content of the given page and the following ones
             * @param {interger} page_index - index of the first page
             * @param {interger} [to_page=self.pages.length] - latest wanted page
             * @returns {promise}
             */
            function reloadPagesFrom(page_index, to_page) {
                to_page = to_page || self.pages.length;
                return retrievePage(1, to_page * self.maxResults).then(function(posts) {
                    createPagesWithPosts(posts._items);
                    return posts;

                });
            }

            /**
             * Returns the page index and the post index of the given post in the local pages
             * @param {Post} post_to_find - post to find in the pages
             * @returns {array|undefined} - [page_index, post_index]
             */
            function getPostPageIndexes(post_to_find){
                var page;
                for (var page_index = 0; page_index < self.pages.length; page_index++) {
                    page = self.pages[page_index];
                    for (var post_index = 0; post_index < page.posts.length; post_index++) {
                        if (page.posts[post_index]._id === post_to_find._id) {
                            return [page_index, post_index];
                        }
                    }
                }
            }

            /**
             * Add a post or a list of posts to the local pages
             * @param {Post|array<Post>} posts - posts to be added to the pages
             */
            function addPost(posts) {
                var all_posts = self.allPosts();
                if (!angular.isArray(posts)) {
                    posts = [posts];
                }
                // for every post, check if exist before or add it
                posts.forEach(function(post) {
                    if (!angular.isDefined(getPostPageIndexes(post))) {
                        all_posts.push(post);
                    }
                });
                // and recreate pages
                createPagesWithPosts(all_posts);
                // update date
                updateLatestDates(all_posts);
            }

            /**
             * Remove a post in the local pages
             * @param {Post} post_to_remove - posts to be removed from the pages
             */
            function removePost(post_to_remove) {
                var indexes = getPostPageIndexes(post_to_remove);
                if (angular.isDefined(post_to_remove)) {
                    var page_index = indexes[0];
                    var post_index = indexes[1];
                    self.pages[page_index].posts.splice(post_index, 1);
                    createPagesWithPosts(self.allPosts());
                }
            }

            /**
             * Add the given page to the Page Manager
             * @param {Page} page - a page instance
             */
            function addPage(page) {
                self.pages.push(page);
            }

            /**
             * Returns the number of posts in the local pages
             * @returns {integer}
             */
            function count() {
                return self.pages.reduce(function(previous_value, current_value) {
                    return previous_value + current_value.posts.length;
                }, 0);
            }

            angular.extend(self, {
                /**
                 * List of page instances
                 */
                pages: [],
                /**
                 * Represent the meta data of posts (total number for instance)
                 */
                meta: {},
                /**
                 * Set the initial order (see self.SORTS)
                 */
                sort: sort || 'editorial',
                /**
                 * Change the order in the future posts request, remove exising post and load a new page
                 */
                changeOrder: changeOrder,
                /**
                 * Setter or Getter the order in the future posts request.
                 */
                order: order,
                /**
                 * Number of results per page
                 */
                maxResults: max_results || config.settings.postsPerPage,
                /**
                 * Latest updated date. Used for retrieving updates since this date.
                 */
                latestUpdatedDate: undefined,
                /**
                 * Fetch a new page of posts
                 */
                fetchNewPage: fetchNewPage,
                /**
                 * Return the latest available updates
                 */
                retrieveUpdate: retrieveUpdate,
                /**
                 * Return all the posts from the local pages
                 */
                allPosts: function () {
                    var posts = self.pages.map(function(page) {
                        return page.posts.map(function(post) {
                            return post;
                        });
                    });
                    // flatten array
                    var merged = [];
                    return merged.concat.apply(merged, posts);
                }
            });
        }

        // return the Pages Manager constructor
        return PagesManager;
    }

    angular.module('liveblog-embed')
        .factory('PagesManager', PagesManagerFactory);

})(angular);
<|endoftext|>


<commit_before>import React from 'react';
import renderer from 'react-test-renderer';
import moment from 'moment';
import Providers from '@ui/TestProviders';

import DateInput from './';

describe('The DateInput component', () => {
  it('should render', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          value={moment.utc('1/1/2015').toDate()}
          minimumDate={moment
            .utc('1/1/2015')
            .startOf('day')
            .add(1, 'days')
            .toDate()}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
  it('should render with a displayValue', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          value={moment.utc('1/1/2015').toDate()}
          displayValue={moment.utc('1/1/2015').format('YYYY/MM/DD')}
          minimumDate={moment
            .utc('1/1/2015')
            .startOf('day')
            .add(1, 'days')
            .toDate()}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
  it('should render with a placeholder', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          placeholder={'mm/dd/yyyy'}
          value={moment.utc('1/1/2015').toDate()}
          minimumDate={moment
            .utc('1/1/2015')
            .startOf('day')
            .add(1, 'days')
            .toDate()}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
  it('should render with a label', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          value={moment.utc('1/1/2015').toDate()}
          displayValue={moment.utc('1/1/2015').format('YYYY/MM/DD')}
          minimumDate={moment
            .utc('1/1/2015')
            .startOf('day')
            .add(1, 'days')
            .toDate()}
          label={'Date Label'}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
});
<commit_msg>Updated tests<commit_after>import React from 'react';
import renderer from 'react-test-renderer';
import moment from 'moment';
import Providers from '@ui/TestProviders';

import DateInput from './';

describe('The DateInput component', () => {
  it('should render', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          value={moment.utc('1/1/2015').toDate()}
          minimumDate={moment
            .utc('1/2/2015')
            .startOf('day')
            .toDate()}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
  it('should render with a displayValue', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          value={moment.utc('1/1/2015').toDate()}
          displayValue={moment.utc('1/1/2015').format('YYYY/MM/DD')}
          minimumDate={moment
            .utc('1/2/2015')
            .startOf('day')
            .toDate()}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
  it('should render with a placeholder', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          placeholder={'mm/dd/yyyy'}
          value={moment.utc('1/1/2015').toDate()}
          minimumDate={moment
            .utc('1/2/2015')
            .startOf('day')
            .toDate()}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
  it('should render with a label', () => {
    const tree = renderer.create(
      <Providers>
        <DateInput
          value={moment.utc('1/1/2015').toDate()}
          displayValue={moment.utc('1/1/2015').format('YYYY/MM/DD')}
          minimumDate={moment
            .utc('1/2/2015')
            .startOf('day')
            .toDate()}
          label={'Date Label'}
        />
      </Providers>,
    );
    expect(tree).toMatchSnapshot();
  });
});
<|endoftext|>


<commit_before>sortColors.js
<commit_msg>add desc<commit_after>// Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent,
// with the colors in the order red, white and blue.

// Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.

// Note:
// You are not suppose to use the library's sort function for this problem.
<|endoftext|>


<commit_before>module.exports = function(config) {
	var async = require('async')
	  , crypto = require('crypto')
    , async = require('async')
    , request = require('request')
    , mongoose = require('mongoose')
    , moment = require('moment')
    , User = require('../models/user')
    , Device = require('../models/device')
    , Layer = require('../models/layer')
    , Feature = require('../models/feature')
    , Location = require('../models/location')
    , api = require('../api')
	  , fs = require('fs-extra');

	var timeout = 200000;

	var username = 'df00d591feb2c5478db826dfb5199dbb';
  var p***REMOVED***word = 'df00d591feb2c5478db826dfb5199dbb';
  var uid = '0e748bc5d2bacdf167731d93e30c21fc';

  var cryptoAlgorithm = 'aes256';
  var decipher = crypto.createDecipher(cryptoAlgorithm, config.key);
  username = decipher.update(username, 'hex', 'utf8') + decipher.final('utf8');
  decipher = crypto.createDecipher(cryptoAlgorithm, config.key);
  p***REMOVED***word = decipher.update(p***REMOVED***word, 'hex', 'utf8') + decipher.final('utf8');
  decipher = crypto.createDecipher(cryptoAlgorithm, config.key);
  uid = decipher.update(uid, 'hex', 'utf8') + decipher.final('utf8');

	var baseUrl = config.baseUrl;
	var token;

	var lastAttachmentTime = {};
	var lastFeatureTime = {};

	var featureLayers;

	var headers = {};

	var getToken = function(done) {
    console.log('getting token');

    var options = {
      //url: baseUrl + '/api/login',
      url: 'https://magedg.geointapps.org/api/login',
      json: {
        username: username,
        uid: uid,
        p***REMOVED***word: p***REMOVED***word
      }
    };

    request.post(options, function(err, res, body) {
      if (err) return done(err);

      if (res.statusCode != 200) return done(new Error('Error hitting login api, respose code: ' + res.statusCode));
      token = body.token;
      headers['Authorization'] = 'Bearer ' + body.token;
      done();
    });
  }

	var getLastAttachmentSyncTime = function(done) {
		fs.readJson("rage/.attachment_sync.json", function(err, readInLastAttachmentTime) {
	    lastAttachmentTime = readInLastAttachmentTime || {};
	    console.log('last', lastAttachmentTime);
	    done();
	  });

	}

	var getAllFeatureLayers = function(callback) {
		Layer.getLayers(function (err, layers) {
			// filter out non feature layers
			featureLayers = [];
			featureLayers = layers.filter(function(layer) {
				return layer.type == "Feature";
			});
      console.info('Syncing features for ' + featureLayers.length + ' layers');
      callback();
    });
	}

	var getFeaturesSince = function(startTime, layer, callback) {
		var options = {	};

		if (startTime) {
			var startTimePlusOne = moment(startTime).add('ms', 1);

			options.filter = {
				startDate: startTimePlusOne
			};
		}
		console.info('Getting features for layer ', layer);
		new api.Feature(layer).getAll(options, callback);
	}

	var getAttachmentsFromFeature = function(feature) {
		return feature.attachments || [];
	}

	var sortFeaturesByTimestamp = function(features) {
		return features.sort(function(a, b) {
			if (moment(a.properties.timestamp).isBefore(b.properties.timestamp))
		     return 1;
		  if (moment(a.properties.timestamp).isAfter(b.properties.timestamp))
		     return -1;
		  // a must be equal to b
		  return 0;
		});
	}

	var writeLastSyncTime = function(done) {
		fs.writeJson("rage/.attachment_sync.json", lastFeatureTime, done);
	}

	var appendErrorFile = function(string) {
		fs.writeJson("rage/.attachment_sync_errors.json", string, done);
	}

	// if this feature time is newer than we currently have
	// update the last feature time
	var updateLastFeatureTime = function(feature, layer) {
		if (feature.properties.timestamp) {
      var featureTime = moment(feature.properties.timestamp);
      if (!lastFeatureTime[layer.collectionName] || featureTime.isAfter(lastFeatureTime[layer.collectionName])) {
        lastFeatureTime[layer.collectionName] = featureTime;
      }
    }
	}

	var sequentiallySyncAttachments = function(features, layer, done) {

		console.info('syncing layer ' + layer.name);
		console.info('Sequentially syncing for ' + features.length + ' features');

		async.eachSeries(features, function(feature, done) {

			// get the attachments
			var attachments = getAttachmentsFromFeature(feature);

			var localDone = done;

			// sync each attachment
			pullAttachmentsCallbackWhenComplete(feature, layer, attachments, function(err) {
				if (!err) {
					console.info('successfully synced attachments for feature ' + feature.id + ' time is: ' + feature.properties.timestamp);
					updateLastFeatureTime(feature, layer);
				}
				localDone(err);
			});

		}, function(err) {
			done();
		});
	}

	var pullAttachmentsCallbackWhenComplete = function(feature, layer, attachments, callback) {
		console.info("syncing " + attachments.length + " attachments");
		async.eachSeries(attachments, function(attachment, done) {

			var url = baseUrl + '/FeatureServer/'+
				layer.id + '/features/' +
				feature.id + '/attachments/' +
				attachment._id + '?access_token=' + token;
			var r = request(url);
			r.on('response', function (resp) {
				console.info('attachment response status code is ' + resp.statusCode);
				if (resp.statusCode == 200) {
				  r.pipe(fs.createWriteStream(config.attachmentBase + '/' + attachment.relativePath));
				  console.info('write the file for url ' + url + ' to ' + config.attachmentBase + '/' + attachment.relativePath);
				  done();
				} else if (resp.statusCode == 404) {
					// uhhh no data, hmmm
					console.info('no data for ' + url);
					done();
				} else {
					console.info('failed to sync with error code ' + resp.statusCode + ' url is ' + url);
					done(new Error('something bad happend'));
				}
			});
			r.on('error', function(resp) {
				done(new Error('more bad stuff happened'));
			});
		},
		function(err) {
			console.info(' error ', err);
			callback(err);
		});
	}

	var syncAttachments = function(done) {

		// global featureLayers is all the layers
		// loop the layers, get all the features for each layer then pull the
		// attachments for each feature

		async.eachSeries(featureLayers,
      function(layer, done) {
      	console.log('syncing attachments since ' + lastAttachmentTime[layer.collectionName]);
				// use that time to get the features since then
				var featuresToSync;
				getFeaturesSince(lastAttachmentTime[layer.collectionName], layer, function(features) {
					featuresToSync = features;
					console.log("features to sync is " + featuresToSync.length);
					// sort the features by timestamp
					featuresToSync = sortFeaturesByTimestamp(featuresToSync);

					sequentiallySyncAttachments(featuresToSync, layer, done);
				});
			},
			function(err) {
				done();
			}
		);
	}

	var doIt = function() {

		var token;

		  console.log('pulling data');

		  async.series({
		  	lastTime: getLastAttachmentSyncTime,
		    token: getToken,
		    layers: getAllFeatureLayers,
		    attachments: syncAttachments
		  },
		  function(err, results) {
		  	console.info('done syncing, setting up timeout');
			  writeLastSyncTime();
		    // all done each ran in order, results is now equal to: {token: 1, features: 2, etc...}
		    setTimeout(doIt, timeout);
		  });
	}

	doIt();
}<commit_msg>someone did something dumb<commit_after>module.exports = function(config) {
	var async = require('async')
	  , crypto = require('crypto')
    , async = require('async')
    , request = require('request')
    , mongoose = require('mongoose')
    , moment = require('moment')
    , User = require('../models/user')
    , Device = require('../models/device')
    , Layer = require('../models/layer')
    , Feature = require('../models/feature')
    , Location = require('../models/location')
    , api = require('../api')
	  , fs = require('fs-extra');

	var timeout = 200000;

	var username = 'df00d591feb2c5478db826dfb5199dbb';
  var p***REMOVED***word = 'df00d591feb2c5478db826dfb5199dbb';
  var uid = '0e748bc5d2bacdf167731d93e30c21fc';

  var cryptoAlgorithm = 'aes256';
  var decipher = crypto.createDecipher(cryptoAlgorithm, config.key);
  username = decipher.update(username, 'hex', 'utf8') + decipher.final('utf8');
  decipher = crypto.createDecipher(cryptoAlgorithm, config.key);
  p***REMOVED***word = decipher.update(p***REMOVED***word, 'hex', 'utf8') + decipher.final('utf8');
  decipher = crypto.createDecipher(cryptoAlgorithm, config.key);
  uid = decipher.update(uid, 'hex', 'utf8') + decipher.final('utf8');

	var baseUrl = config.baseUrl;
	var token;

	var lastAttachmentTime = {};
	var lastFeatureTime = {};

	var featureLayers;

	var headers = {};

	var getToken = function(done) {
    console.log('getting token');

    var options = {
      url: baseUrl + '/api/login',
      json: {
        username: username,
        uid: uid,
        p***REMOVED***word: p***REMOVED***word
      }
    };

    request.post(options, function(err, res, body) {
      if (err) return done(err);

      if (res.statusCode != 200) return done(new Error('Error hitting login api, respose code: ' + res.statusCode));
      token = body.token;
      headers['Authorization'] = 'Bearer ' + body.token;
      done();
    });
  }

	var getLastAttachmentSyncTime = function(done) {
		fs.readJson("rage/.attachment_sync.json", function(err, readInLastAttachmentTime) {
	    lastAttachmentTime = readInLastAttachmentTime || {};
	    console.log('last', lastAttachmentTime);
	    done();
	  });

	}

	var getAllFeatureLayers = function(callback) {
		Layer.getLayers(function (err, layers) {
			// filter out non feature layers
			featureLayers = [];
			featureLayers = layers.filter(function(layer) {
				return layer.type == "Feature";
			});
      console.info('Syncing features for ' + featureLayers.length + ' layers');
      callback();
    });
	}

	var getFeaturesSince = function(startTime, layer, callback) {
		var options = {	};

		if (startTime) {
			var startTimePlusOne = moment(startTime).add('ms', 1);

			options.filter = {
				startDate: startTimePlusOne
			};
		}
		console.info('Getting features for layer ', layer);
		new api.Feature(layer).getAll(options, callback);
	}

	var getAttachmentsFromFeature = function(feature) {
		return feature.attachments || [];
	}

	var sortFeaturesByTimestamp = function(features) {
		return features.sort(function(a, b) {
			if (moment(a.properties.timestamp).isBefore(b.properties.timestamp))
		     return 1;
		  if (moment(a.properties.timestamp).isAfter(b.properties.timestamp))
		     return -1;
		  // a must be equal to b
		  return 0;
		});
	}

	var writeLastSyncTime = function(done) {
		fs.writeJson("rage/.attachment_sync.json", lastFeatureTime, done);
	}

	var appendErrorFile = function(string) {
		fs.writeJson("rage/.attachment_sync_errors.json", string, done);
	}

	// if this feature time is newer than we currently have
	// update the last feature time
	var updateLastFeatureTime = function(feature, layer) {
		if (feature.properties.timestamp) {
      var featureTime = moment(feature.properties.timestamp);
      if (!lastFeatureTime[layer.collectionName] || featureTime.isAfter(lastFeatureTime[layer.collectionName])) {
        lastFeatureTime[layer.collectionName] = featureTime;
      }
    }
	}

	var sequentiallySyncAttachments = function(features, layer, done) {

		console.info('syncing layer ' + layer.name);
		console.info('Sequentially syncing for ' + features.length + ' features');

		async.eachSeries(features, function(feature, done) {

			// get the attachments
			var attachments = getAttachmentsFromFeature(feature);

			var localDone = done;

			// sync each attachment
			pullAttachmentsCallbackWhenComplete(feature, layer, attachments, function(err) {
				if (!err) {
					console.info('successfully synced attachments for feature ' + feature.id + ' time is: ' + feature.properties.timestamp);
					updateLastFeatureTime(feature, layer);
				}
				localDone(err);
			});

		}, function(err) {
			done();
		});
	}

	var pullAttachmentsCallbackWhenComplete = function(feature, layer, attachments, callback) {
		console.info("syncing " + attachments.length + " attachments");
		async.eachSeries(attachments, function(attachment, done) {

			var url = baseUrl + '/FeatureServer/'+
				layer.id + '/features/' +
				feature.id + '/attachments/' +
				attachment._id + '?access_token=' + token;
			var r = request(url);
			r.on('response', function (resp) {
				console.info('attachment response status code is ' + resp.statusCode);
				if (resp.statusCode == 200) {
				  r.pipe(fs.createWriteStream(config.attachmentBase + '/' + attachment.relativePath));
				  console.info('write the file for url ' + url + ' to ' + config.attachmentBase + '/' + attachment.relativePath);
				  done();
				} else if (resp.statusCode == 404) {
					// uhhh no data, hmmm
					console.info('no data for ' + url);
					done();
				} else {
					console.info('failed to sync with error code ' + resp.statusCode + ' url is ' + url);
					done(new Error('something bad happend'));
				}
			});
			r.on('error', function(resp) {
				done(new Error('more bad stuff happened'));
			});
		},
		function(err) {
			console.info(' error ', err);
			callback(err);
		});
	}

	var syncAttachments = function(done) {

		// global featureLayers is all the layers
		// loop the layers, get all the features for each layer then pull the
		// attachments for each feature

		async.eachSeries(featureLayers,
      function(layer, done) {
      	console.log('syncing attachments since ' + lastAttachmentTime[layer.collectionName]);
				// use that time to get the features since then
				var featuresToSync;
				getFeaturesSince(lastAttachmentTime[layer.collectionName], layer, function(features) {
					featuresToSync = features;
					console.log("features to sync is " + featuresToSync.length);
					// sort the features by timestamp
					featuresToSync = sortFeaturesByTimestamp(featuresToSync);

					sequentiallySyncAttachments(featuresToSync, layer, done);
				});
			},
			function(err) {
				done();
			}
		);
	}

	var doIt = function() {

		var token;

		  console.log('pulling data');

		  async.series({
		  	lastTime: getLastAttachmentSyncTime,
		    token: getToken,
		    layers: getAllFeatureLayers,
		    attachments: syncAttachments
		  },
		  function(err, results) {
		  	console.info('done syncing, setting up timeout');
			  writeLastSyncTime();
		    // all done each ran in order, results is now equal to: {token: 1, features: 2, etc...}
		    setTimeout(doIt, timeout);
		  });
	}

	doIt();
}<|endoftext|>


<commit_before>module.exports = {
  name: 'Crosspost Message',
  section: 'Messaging',

  subtitle (data) {
    const message = ['Command Message', 'Temp Variable', 'Server Variable', 'Global Variable']
    return `${message[parseInt(data.message)]}`
  },

  variableStorage (data, varType) {
    if (parseInt(data.storage) !== varType) return
    return [data.varName2, 'Message']
  },

  fields: ['message', 'varName', 'storage', 'varName2'],

  html (isEvent, data) {
    return `

  Source Message:

  <select id="message" class="round" onchange="glob.messageChange(this, 'varNameContainer')">
   ${data.messages[isEvent ? 1 : 0]}
  </select>

  Variable Name:

  <input id="varName" class="round" type="text" list="variableList">

  Store In:

  <select id="storage" class="round" onchange="glob.variableChange(this, 'varNameContainer2')">
   ${data.variables[0]}
  </select>

  Variable Name:

  <input id="varName2" class="round" type="text">

`
  },

  init () {
    const { glob, document } = this

    glob.sendTargetChange(document.getElementById('message'), 'varNameContainer')
    glob.variableChange(document.getElementById('storage'), 'varNameContainer2')
  },

  action (cache) {
    const data = cache.actions[cache.index]
    const varName = this.evalMessage(data.varName, cache)
    const message = this.getMessage(parseInt(data.message), varName, cache)

    if (!message) return
    if (!message.crosspost) throw new Error('You need at least Discord.js version 12.4.0 to use this mod.')

    message.crosspost().then((msg) => {
      const varName2 = this.evalMessage(data.varName2, cache)
      const storage = parseInt(data.storage)
      this.storeValue(msg, storage, varName2, cache)
      this.callNextAction(cache)
    }).catch(console.error)
  },

  mod () {}
}
<commit_msg>Update crosspost_message_MOD.js<commit_after>module.exports = {
  name: 'Crosspost Message',
  section: 'Messaging',

  subtitle (data) {
    const message = ['Command Message', 'Temp Variable', 'Server Variable', 'Global Variable']
    return `${message[parseInt(data.message)]}`
  },

  variableStorage (data, varType) {
    if (parseInt(data.storage) !== varType) return
    return [data.varName2, 'Message']
  },

  fields: ['message', 'varName', 'storage', 'varName2'],

  html (isEvent, data) {
    return `

  Source Message:

  <select id="message" class="round" onchange="glob.messageChange(this, 'varNameContainer')">
   ${data.messages[isEvent ? 1 : 0]}
  </select>

  Variable Name:

  <input id="varName" class="round" type="text" list="variableList">

  Store In:

  <select id="storage" class="round" onchange="glob.variableChange(this, 'varNameContainer2')">
   ${data.variables[0]}
  </select>

  Variable Name:

  <input id="varName2" class="round" type="text">

`
  },

  init () {
    const { glob, document } = this

    glob.sendTargetChange(document.getElementById('message'), 'varNameContainer')
    glob.variableChange(document.getElementById('storage'), 'varNameContainer2')
  },

  action (cache) {
    const data = cache.actions[cache.index]
    const varName = this.evalMessage(data.varName, cache)
    const message = this.getMessage(parseInt(data.message), varName, cache)

    if (!message) throw new Error('No message found to crosspost.')
    if (!message.crosspost) throw new Error('You need at least Discord.js version 12.4.0 to use this mod.')

    message.crosspost().then((msg) => {
      const varName2 = this.evalMessage(data.varName2, cache)
      const storage = parseInt(data.storage)
      this.storeValue(msg, storage, varName2, cache)
      this.callNextAction(cache)
    }).catch(console.error)
  },

  mod () {}
}
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------- \\
// File: Tap.js                                                               \\
// Module: Touch                                                              \\
// Requires: Gesture.js                                                       \\
// Author: Neil Jenkins                                                       \\
// License: © 2010-2014 FastMail Pty Ltd. All rights reserved.                \\
// -------------------------------------------------------------------------- \\

/*global document */

"use strict";

( function ( NS ) {

/*  We can't just call preventDefault on touch(start|move), as this would
    prevent scrolling and also prevent links we want to act as normal from
    working. So we use this hack instead to capture the subsequent click and
    remove it from the app's existence.
*/
var MouseEventRemover = NS.Class({
    init: function ( target, defaultPrevented ) {
        this.target = target;
        this.stop = defaultPrevented;
        this.time = Date.now();
        NS.ViewEventsController.addEventTarget( this, 40 );
    },
    fire: function ( type, event ) {
        var isClick = ( type === 'click' ) && !event.originalType,
            isMouse = isClick || /^mouse/.test( type );
        if ( type === 'touchstart' || Date.now() - this.time > 1000 ) {
            NS.ViewEventsController.removeEventTarget( this );
            return false;
        }
        if ( isMouse && ( this.stop || event.target !== this.target ) ) {
            event.preventDefault();
        }
        return isMouse;
    }
});

var TapEvent = function ( type, target, preventDefault ) {
    this.isEvent = true;
    this.type = type;
    this.originalType = 'tap';
    this.target = target;
    this.preventDefault = preventDefault;
};

var TrackedTouch = function ( x, y, time, target ) {
    this.x = x;
    this.y = y;
    this.time = time;
    var activeEls = this.activeEls = [];
    do {
        if ( /^(?:A|BUTTON|INPUT|LABEL)$/.test( target.nodeName ) ) {
            activeEls.push( target );
            NS.Element.addClass( target, 'tap-active' );
        }
    } while ( target = target.parentNode );
};

TrackedTouch.prototype.done  = function () {
    var activeEls = this.activeEls,
        i, l;
    for ( i = 0, l = activeEls.length; i < l; i += 1 ) {
        NS.Element.removeClass( activeEls[i], 'tap-active' );
    }
};

/*  A tap is defined as a touch which:

    * Lasts less than 200ms.
    * Moves less than 5px from the initial touch point.

    There may be other touches occurring at the same time (e.g. you could be
    holding one button and tap another; the tap gesture will still be
    recognised).
*/
NS.Tap = new NS.Gesture({

    _tracking: {},

    cancel: function () {
        var tracking = this._tracking,
            id;
        for ( id in tracking ) {
            tracking[ id ].done();
        }
        this._tracking = {};
    },

    start: function ( event ) {
        var touches = event.changedTouches,
            tracking = this._tracking,
            now = Date.now(),
            i, l, touch, id;
        for ( i = 0, l = touches.length; i < l; i += 1 ) {
            touch = touches[i];
            id = touch.identifier;
            if ( !tracking[ id ] ) {
                tracking[ id ] = new TrackedTouch(
                    touch.screenX, touch.screenY, now, touch.target );
            }
        }
    },

    move: function ( event ) {
        var touches = event.changedTouches,
            tracking = this._tracking,
            i, l, touch, id, trackedTouch, deltaX, deltaY;
        for ( i = 0, l = touches.length; i < l; i += 1 ) {
            touch = touches[i];
            id = touch.identifier;
            trackedTouch = tracking[ id ];
            if ( trackedTouch ) {
                deltaX = touch.screenX - trackedTouch.x;
                deltaY = touch.screenY - trackedTouch.y;
                if ( deltaX * deltaX + deltaY * deltaY > 25 ) {
                    trackedTouch.done();
                    delete tracking[ id ];
                }
            }
        }
    },

    end: function ( event ) {
        var touches = event.changedTouches,
            tracking = this._tracking,
            now = Date.now(),
            i, l, touch, id, trackedTouch, defaultPrevented, target, nodeName,
            ViewEventsController = NS.ViewEventsController,
            preventDefault = function () {
                defaultPrevented = true;
            };
        for ( i = 0, l = touches.length; i < l; i += 1 ) {
            touch = touches[i];
            id = touch.identifier;
            trackedTouch = tracking[ id ];
            if ( trackedTouch ) {
                if ( now - trackedTouch.time < 200 ) {
                    defaultPrevented = false;
                    target = touch.target;
                    ViewEventsController.handleEvent(
                        new TapEvent( 'tap', target, preventDefault )
                    );
                    ViewEventsController.handleEvent(
                        new TapEvent( 'click', target, preventDefault )
                    );
                    // The tap could trigger a UI change. When the click event
                    // is fired 300ms later, if there is now an input under the
                    // area the touch took place, in iOS the keyboard will
                    // appear, even though the preventDefault on the click event
                    // stops it actually being focussed. Calling preventDefault
                    // on the touchend event stops this happening, however we
                    // must not do this if the user actually taps an input!
                    nodeName = target.nodeName;
                    if ( nodeName !== 'INPUT' && nodeName !== 'TEXTAREA' &&
                            nodeName !== 'SELECT' ) {
                        event.preventDefault();
                    }
                    new MouseEventRemover( target, defaultPrevented );
                }
                trackedTouch.done();
                delete tracking[ id ];
            }
        }
    }
});

}( this.O ) );
<commit_msg>Add defaultPrevented property to Tap events.<commit_after>// -------------------------------------------------------------------------- \\
// File: Tap.js                                                               \\
// Module: Touch                                                              \\
// Requires: Gesture.js                                                       \\
// Author: Neil Jenkins                                                       \\
// License: © 2010-2014 FastMail Pty Ltd. All rights reserved.                \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

/*  We can't just call preventDefault on touch(start|move), as this would
    prevent scrolling and also prevent links we want to act as normal from
    working. So we use this hack instead to capture the subsequent click and
    remove it from the app's existence.
*/
var MouseEventRemover = NS.Class({
    init: function ( target, defaultPrevented ) {
        this.target = target;
        this.stop = defaultPrevented;
        this.time = Date.now();
        NS.ViewEventsController.addEventTarget( this, 40 );
    },
    fire: function ( type, event ) {
        var isClick = ( type === 'click' ) && !event.originalType,
            isMouse = isClick || /^mouse/.test( type );
        if ( type === 'touchstart' || Date.now() - this.time > 1000 ) {
            NS.ViewEventsController.removeEventTarget( this );
            return false;
        }
        if ( isMouse && ( this.stop || event.target !== this.target ) ) {
            event.preventDefault();
        }
        return isMouse;
    }
});

var TapEvent = NS.Class({
    init: function ( type, target ) {
        this.isEvent = true;
        this.type = type;
        this.originalType = 'tap';
        this.target = target;
        this.defaultPrevented = false;
    },
    preventDefault: function () {
        this.defaultPrevented = true;
    }
});

var TrackedTouch = function ( x, y, time, target ) {
    this.x = x;
    this.y = y;
    this.time = time;
    var activeEls = this.activeEls = [];
    do {
        if ( /^(?:A|BUTTON|INPUT|LABEL)$/.test( target.nodeName ) ) {
            activeEls.push( target );
            NS.Element.addClass( target, 'tap-active' );
        }
    } while ( target = target.parentNode );
};

TrackedTouch.prototype.done  = function () {
    var activeEls = this.activeEls,
        i, l;
    for ( i = 0, l = activeEls.length; i < l; i += 1 ) {
        NS.Element.removeClass( activeEls[i], 'tap-active' );
    }
};

/*  A tap is defined as a touch which:

    * Lasts less than 200ms.
    * Moves less than 5px from the initial touch point.

    There may be other touches occurring at the same time (e.g. you could be
    holding one button and tap another; the tap gesture will still be
    recognised).
*/
NS.Tap = new NS.Gesture({

    _tracking: {},

    cancel: function () {
        var tracking = this._tracking,
            id;
        for ( id in tracking ) {
            tracking[ id ].done();
        }
        this._tracking = {};
    },

    start: function ( event ) {
        var touches = event.changedTouches,
            tracking = this._tracking,
            now = Date.now(),
            i, l, touch, id;
        for ( i = 0, l = touches.length; i < l; i += 1 ) {
            touch = touches[i];
            id = touch.identifier;
            if ( !tracking[ id ] ) {
                tracking[ id ] = new TrackedTouch(
                    touch.screenX, touch.screenY, now, touch.target );
            }
        }
    },

    move: function ( event ) {
        var touches = event.changedTouches,
            tracking = this._tracking,
            i, l, touch, id, trackedTouch, deltaX, deltaY;
        for ( i = 0, l = touches.length; i < l; i += 1 ) {
            touch = touches[i];
            id = touch.identifier;
            trackedTouch = tracking[ id ];
            if ( trackedTouch ) {
                deltaX = touch.screenX - trackedTouch.x;
                deltaY = touch.screenY - trackedTouch.y;
                if ( deltaX * deltaX + deltaY * deltaY > 25 ) {
                    trackedTouch.done();
                    delete tracking[ id ];
                }
            }
        }
    },

    end: function ( event ) {
        var touches = event.changedTouches,
            tracking = this._tracking,
            now = Date.now(),
            i, l, touch, id, trackedTouch, target, tapEvent, clickEvent,
            nodeName,
            ViewEventsController = NS.ViewEventsController;
        for ( i = 0, l = touches.length; i < l; i += 1 ) {
            touch = touches[i];
            id = touch.identifier;
            trackedTouch = tracking[ id ];
            if ( trackedTouch ) {
                if ( now - trackedTouch.time < 200 ) {
                    target = touch.target;
                    tapEvent = new TapEvent( 'tap', target );
                    ViewEventsController.handleEvent( tapEvent );
                    clickEvent = new TapEvent( 'click', target );
                    clickEvent.defaultPrevented = tapEvent.defaultPrevented;
                    ViewEventsController.handleEvent( clickEvent );
                    // The tap could trigger a UI change. When the click event
                    // is fired 300ms later, if there is now an input under the
                    // area the touch took place, in iOS the keyboard will
                    // appear, even though the preventDefault on the click event
                    // stops it actually being focussed. Calling preventDefault
                    // on the touchend event stops this happening, however we
                    // must not do this if the user actually taps an input!
                    nodeName = target.nodeName;
                    if ( nodeName !== 'INPUT' && nodeName !== 'TEXTAREA' &&
                            nodeName !== 'SELECT' ) {
                        event.preventDefault();
                    }
                    new MouseEventRemover( target, clickEvent.defaultPrevented );
                }
                trackedTouch.done();
                delete tracking[ id ];
            }
        }
    }
});

}( this.O ) );
<|endoftext|>


<commit_before>var statusElement = document.getElementById('status');
var startButton = document.getElementById('start');
var stopButton = document.getElementById('stop');
var takePhotoButton = document.getElementById('takePhoto');
var loadPhoto = document.getElementById('loadPhoto');
var saveButton = document.getElementById('save');
var segmentationButton = document.getElementById('segmentation');

var previewCanvas = document.getElementById('preview');
var imageCanvas = document.getElementById('image');
var overlayCanvas = document.getElementById('overlay');

var ep, photoCapture;
var previewContext, previewData, imageContext, imageData, overlayContext;
var currentPhoto, savePhoto;

var width = 640, height = 480;
var canvasWidth = 400, canvasHeight = 300;
var topX, topY, bottomX, bottomY;
var lastX, lastY;
var mouseDown = 0;
var hasImage = false;
var stopDrawLine = false;
var imgMarkups = [];

function doBlendColorPop(colorImage, maskImage) {
  var GREY = 0x7f;
  var alpha;
  for (var x = 0; x < colorImage.width; x++) {
    for (var y = 0; y < colorImage.height; y++) {
      var index = y * colorImage.width * 4 + x * 4;
      var maskIndex = y * maskImage.width + x;
      var mask = maskImage.data[maskIndex];
      if (mask > 0) alpha = 1.0;
      else alpha = 0.0;

      colorImage.data[index] = parseInt(alpha * colorImage.data[index] +
          (1 - alpha) * ((colorImage.data[index] >> 4) + GREY));
      colorImage.data[index + 1] = parseInt(alpha * colorImage.data[index + 1] +
          (1 - alpha) * ((colorImage.data[index + 1] >> 4) + GREY));
      colorImage.data[index + 2] = parseInt(alpha * colorImage.data[index + 2] +
          (1 - alpha) * ((colorImage.data[index + 2] >> 4) + GREY));
    }
  }
}

function main() {
  ep = realsense.DepthEnabledPhotography.EnhancedPhoto;
  photoCapture = realsense.DepthEnabledPhotography.PhotoCapture;

  previewContext = previewCanvas.getContext('2d');
  imageContext = imageCanvas.getContext('2d');
  overlayContext = overlay.getContext('2d');
  previewData = previewContext.createImageData(width, height);

  var gettingImage = false;

  overlayCanvas.addEventListener('mousedown', function(e) {
    if (!hasImage)
      return;
    mouseDown++;
    if (mouseDown == 1) {
      topX = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
      topY = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
    } else if (mouseDown == 2) {
      bottomX = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
      bottomY = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
      ep.objectSegment(currentPhoto, { x: topX, y: topY }, { x: bottomX, y: bottomY }).then(
          function(maskImage) {
            var len = maskImage.width * maskImage.height;
            for (var i = 0; i < len; i++) {
              imgMarkups[i] = 0;
            }
            currentPhoto.queryContainerImage().then(
                function(colorImage) {
                  doBlendColorPop(colorImage, maskImage);

                  imageContext.clearRect(0, 0, width, height);
                  imageData = imageContext.createImageData(
                      colorImage.width, colorImage.height);
                  imageData.data.set(colorImage.data);
                  imageContext.putImageData(imageData, 0, 0);

                  currentPhoto.clone().then(
                      function(photo) {
                        savePhoto = photo;
                        savePhoto.setReferenceImage(colorImage).then(
                            function() {
                              statusElement.innerHTML = 'Finish processing segmentation!' +
                                  'Click and drag the left(foregroud) and right(background)' +
                                  'mouse buttons to refine the mask';
                            },
                            function(e) { statusElement.innerHTML = e; });
                      },
                      function(e) { statusElement.innerHTML = e; });
                },
                function(e) { statusElement.innerHTML = e });
          },
          function(e) { statusElement.innerHTML = e });
    } else {
      stopDrawLine = false;
      // Image markup
      var x = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
      var y = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
      // makeUpImage({x: x, y: y}, true);
      overlayContext.beginPath();
      if (e.button == 0) {
        overlayContext.strokeStyle = 'blue';
        imgMarkups[y * width + x] = 1;
      } else {
        overlayContext.strokeStyle = 'green';
        imgMarkups[y * width + x] = 2;
      }
      overlayContext.lineWidth = 0.5;
      overlayContext.moveTo(x, y);
      lastX = x;
      lastY = y;
    }
  }, false);

  overlayCanvas.addEventListener('mousemove', function(e) {
    bottomX = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
    bottomY = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
    if (mouseDown == 1) {
      overlayContext.clearRect(0, 0, width, height);
      var offsetX = bottomX - topX;
      var offsetY = bottomY - topY;
      overlayContext.strokeStyle = 'red';
      overlayContext.lineWidth = 2;
      overlayContext.strokeRect(topX, topY, offsetX, offsetY);
    } else if (mouseDown > 2) {
      var value = e.button == 0 ? 1 : 2;
      if (!stopDrawLine) {
        overlayContext.lineTo(bottomX, bottomY);
        overlayContext.stroke();
        // for precessing
        if (lastX == bottomX) {
          for (var y = lastY; y != bottomY; y += (lastY < bottomY) ? 1 : -1)
          {
            imgMarkups[y * width + lastX] = value;
          }
        } else {
          var slope = (bottomY - lastY) / (bottomX - lastX);

          for (var x = lastX; x != bottomX; x += (lastX < bottomX) ? 1 : -1)
          {
            var y = parseInt((slope * (x - lastX) + lastY) + 0.5);

            imgMarkups[y * width + x] = value;
          }
        }

        lastX = bottomX;
        lastY = bottomY;
      } else {
        overlayContext.closePath();
      }

    }
  }, false);

  overlayCanvas.addEventListener('mouseup', function(e) {
    if (mouseDown > 2) {
      stopDrawLine = true;
      var markupImage = {
        format: 'Y8',
        width: width,
        height: height,
        data: imgMarkups
      };
      ep.refineMask(markupImage).then(
          function(maskImage) {
            currentPhoto.queryContainerImage().then(
                function(colorImage) {
                  doBlendColorPop(colorImage, maskImage);

                  imageContext.clearRect(0, 0, width, height);
                  imageData = imageContext.createImageData(
                      colorImage.width, colorImage.height);
                  imageData.data.set(colorImage.data);
                  imageContext.putImageData(imageData, 0, 0);

                  currentPhoto.clone().then(
                      function(photo) {
                        savePhoto = photo;
                        savePhoto.setReferenceImage(colorImage).then(
                            function() {
                              statusElement.innerHTML = 'Click and drag the left(foregroud) and' +
                              'right(background) mouse buttons to refine the mask';
                            },
                            function(e) { statusElement.innerHTML = e; });
                      },
                      function(e) { statusElement.innerHTML = e; });
                },
                function() { });
          },
          function(e) { statusElement.innerHTML += e; });
    }
  }, false);

  photoCapture.onpreview = function(e) {
    if (gettingImage)
      return;
    gettingImage = true;
    ep.getPreviewImage().then(
        function(image) {
          previewData.data.set(image.data);
          previewContext.putImageData(previewData, 0, 0);
          gettingImage = false;
        }, function() { });
  };

  photoCapture.onerror = function(e) {
    statusElement.innerHTML = 'Status Info : onerror: ' + e.status;
  };

  startButton.onclick = function(e) {
    statusElement.innerHTML = 'Status Info : Start: ';
    gettingImage = false;
    photoCapture.startPreview().then(function(e) { statusElement.innerHTML += e; },
                                     function(e) { statusElement.innerHTML += e; });
  };

  takePhotoButton.onclick = function(e) {
    photoCapture.takePhoto().then(
        function(photo) {
          currentPhoto = photo;
          savePhoto = photo;
          currentPhoto.queryContainerImage().then(
              function(image) {
                imageData =
                    imageContext.createImageData(image.width, image.height);
                statusElement.innerHTML = 'Select the bounding box around the object.';
                overlayContext.clearRect(0, 0, width, height);
                imageData.data.set(image.data);
                imageContext.putImageData(imageData, 0, 0);
                hasImage = true;
              },
              function(e) { statusElement.innerHTML += e; });
        },
        function(e) { statusElement.innerHTML += e; });
  };

  saveButton.onclick = function(e) {
    if (!savePhoto) {
      statusElement.innerHTML = 'There is no photo to save';
      return;
    }
    savePhoto.saveXDM().then(
        function(blob) {
          var reader = new FileReader();
          reader.onload = function(evt) {
            var image = document.getElementById('imageDisplayArea');
            image.src = evt.target.result;
            // Currently, crosswalk has bugs to download (see XWALK-5220). So the following code
            // doesn't work. Once download is enabled. The processed image will be
            // downloaded into 'Downloads' folder.
            var a = document.createElement('a');
            a.href = evt.target.result;
            a.download = true;
            a.click();
            statusElement.innerHTML = 'Save successfully';
          };
          reader.readAsDataURL(blob);
        },
        function(e) { statusElement.innerHTML = e; });
  };

  loadPhoto.addEventListener('change', function(e) {
    var file = loadPhoto.files[0];
    var dp = new realsense.DepthEnabledPhotography.DepthPhoto();
    dp.loadXDM(file).then(
        function (sucess) {
          currentPhoto = dp;
          savePhoto = dp;
          currentPhoto.queryContainerImage().then(
              function(image) {
                imageContext.clearRect(0, 0, width, height);
                imageData = imageContext.createImageData(image.width, image.height);
                statusElement.innerHTML = 'Select the bounding box around the object.';
                overlayContext.clearRect(0, 0, width, height);
                imageData.data.set(image.data);
                imageContext.putImageData(imageData, 0, 0);
                hasImage = true;
              },
              function(e) { statusElement.innerHTML = e; });
        },
        function(e) { statusElement.innerHTML = e; });
  });

  stopButton.onclick = function(e) {
    statusElement.innerHTML = 'Status Info : Stop: ';
    photoCapture.stopPreview().then(function(e) { statusElement.innerHTML += e; },
                                    function(e) { statusElement.innerHTML += e; });
  };

  segmentationButton.onclick = function(e) {
    overlayContext.clearRect(0, 0, width, height);
    mouseDown = 0;
    currentPhoto.queryContainerImage().then(
        function(image) {
          imageContext.clearRect(0, 0, width, height);
          imageData =
              imageContext.createImageData(image.width, image.height);
          statusElement.innerHTML = 'Select the bounding box around the object.';
          overlayContext.clearRect(0, 0, width, height);
          imageData.data.set(image.data);
          imageContext.putImageData(imageData, 0, 0);
          hasImage = true;
        },
        function(e) { statusElement.innerHTML += e; });
  };
}
<commit_msg>[EP] Fixed wrong interface for getPreviewImage<commit_after>var statusElement = document.getElementById('status');
var startButton = document.getElementById('start');
var stopButton = document.getElementById('stop');
var takePhotoButton = document.getElementById('takePhoto');
var loadPhoto = document.getElementById('loadPhoto');
var saveButton = document.getElementById('save');
var segmentationButton = document.getElementById('segmentation');

var previewCanvas = document.getElementById('preview');
var imageCanvas = document.getElementById('image');
var overlayCanvas = document.getElementById('overlay');

var ep, photoCapture;
var previewContext, previewData, imageContext, imageData, overlayContext;
var currentPhoto, savePhoto;

var width = 640, height = 480;
var canvasWidth = 400, canvasHeight = 300;
var topX, topY, bottomX, bottomY;
var lastX, lastY;
var mouseDown = 0;
var hasImage = false;
var stopDrawLine = false;
var imgMarkups = [];

function doBlendColorPop(colorImage, maskImage) {
  var GREY = 0x7f;
  var alpha;
  for (var x = 0; x < colorImage.width; x++) {
    for (var y = 0; y < colorImage.height; y++) {
      var index = y * colorImage.width * 4 + x * 4;
      var maskIndex = y * maskImage.width + x;
      var mask = maskImage.data[maskIndex];
      if (mask > 0) alpha = 1.0;
      else alpha = 0.0;

      colorImage.data[index] = parseInt(alpha * colorImage.data[index] +
          (1 - alpha) * ((colorImage.data[index] >> 4) + GREY));
      colorImage.data[index + 1] = parseInt(alpha * colorImage.data[index + 1] +
          (1 - alpha) * ((colorImage.data[index + 1] >> 4) + GREY));
      colorImage.data[index + 2] = parseInt(alpha * colorImage.data[index + 2] +
          (1 - alpha) * ((colorImage.data[index + 2] >> 4) + GREY));
    }
  }
}

function main() {
  ep = realsense.DepthEnabledPhotography.EnhancedPhoto;
  photoCapture = realsense.DepthEnabledPhotography.PhotoCapture;

  previewContext = previewCanvas.getContext('2d');
  imageContext = imageCanvas.getContext('2d');
  overlayContext = overlay.getContext('2d');
  previewData = previewContext.createImageData(width, height);

  var gettingImage = false;

  overlayCanvas.addEventListener('mousedown', function(e) {
    if (!hasImage)
      return;
    mouseDown++;
    if (mouseDown == 1) {
      topX = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
      topY = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
    } else if (mouseDown == 2) {
      bottomX = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
      bottomY = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
      ep.objectSegment(currentPhoto, { x: topX, y: topY }, { x: bottomX, y: bottomY }).then(
          function(maskImage) {
            var len = maskImage.width * maskImage.height;
            for (var i = 0; i < len; i++) {
              imgMarkups[i] = 0;
            }
            currentPhoto.queryContainerImage().then(
                function(colorImage) {
                  doBlendColorPop(colorImage, maskImage);

                  imageContext.clearRect(0, 0, width, height);
                  imageData = imageContext.createImageData(
                      colorImage.width, colorImage.height);
                  imageData.data.set(colorImage.data);
                  imageContext.putImageData(imageData, 0, 0);

                  currentPhoto.clone().then(
                      function(photo) {
                        savePhoto = photo;
                        savePhoto.setReferenceImage(colorImage).then(
                            function() {
                              statusElement.innerHTML = 'Finish processing segmentation!' +
                                  'Click and drag the left(foregroud) and right(background)' +
                                  'mouse buttons to refine the mask';
                            },
                            function(e) { statusElement.innerHTML = e; });
                      },
                      function(e) { statusElement.innerHTML = e; });
                },
                function(e) { statusElement.innerHTML = e });
          },
          function(e) { statusElement.innerHTML = e });
    } else {
      stopDrawLine = false;
      // Image markup
      var x = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
      var y = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
      // makeUpImage({x: x, y: y}, true);
      overlayContext.beginPath();
      if (e.button == 0) {
        overlayContext.strokeStyle = 'blue';
        imgMarkups[y * width + x] = 1;
      } else {
        overlayContext.strokeStyle = 'green';
        imgMarkups[y * width + x] = 2;
      }
      overlayContext.lineWidth = 0.5;
      overlayContext.moveTo(x, y);
      lastX = x;
      lastY = y;
    }
  }, false);

  overlayCanvas.addEventListener('mousemove', function(e) {
    bottomX = parseInt((e.clientX - overlayCanvas.offsetLeft) * width / canvasWidth);
    bottomY = parseInt((e.clientY - overlayCanvas.offsetTop) * height / canvasHeight);
    if (mouseDown == 1) {
      overlayContext.clearRect(0, 0, width, height);
      var offsetX = bottomX - topX;
      var offsetY = bottomY - topY;
      overlayContext.strokeStyle = 'red';
      overlayContext.lineWidth = 2;
      overlayContext.strokeRect(topX, topY, offsetX, offsetY);
    } else if (mouseDown > 2) {
      var value = e.button == 0 ? 1 : 2;
      if (!stopDrawLine) {
        overlayContext.lineTo(bottomX, bottomY);
        overlayContext.stroke();
        // for precessing
        if (lastX == bottomX) {
          for (var y = lastY; y != bottomY; y += (lastY < bottomY) ? 1 : -1)
          {
            imgMarkups[y * width + lastX] = value;
          }
        } else {
          var slope = (bottomY - lastY) / (bottomX - lastX);

          for (var x = lastX; x != bottomX; x += (lastX < bottomX) ? 1 : -1)
          {
            var y = parseInt((slope * (x - lastX) + lastY) + 0.5);

            imgMarkups[y * width + x] = value;
          }
        }

        lastX = bottomX;
        lastY = bottomY;
      } else {
        overlayContext.closePath();
      }

    }
  }, false);

  overlayCanvas.addEventListener('mouseup', function(e) {
    if (mouseDown > 2) {
      stopDrawLine = true;
      var markupImage = {
        format: 'Y8',
        width: width,
        height: height,
        data: imgMarkups
      };
      ep.refineMask(markupImage).then(
          function(maskImage) {
            currentPhoto.queryContainerImage().then(
                function(colorImage) {
                  doBlendColorPop(colorImage, maskImage);

                  imageContext.clearRect(0, 0, width, height);
                  imageData = imageContext.createImageData(
                      colorImage.width, colorImage.height);
                  imageData.data.set(colorImage.data);
                  imageContext.putImageData(imageData, 0, 0);

                  currentPhoto.clone().then(
                      function(photo) {
                        savePhoto = photo;
                        savePhoto.setReferenceImage(colorImage).then(
                            function() {
                              statusElement.innerHTML = 'Click and drag the left(foregroud) and' +
                              'right(background) mouse buttons to refine the mask';
                            },
                            function(e) { statusElement.innerHTML = e; });
                      },
                      function(e) { statusElement.innerHTML = e; });
                },
                function() { });
          },
          function(e) { statusElement.innerHTML += e; });
    }
  }, false);

  photoCapture.onpreview = function(e) {
    if (gettingImage)
      return;
    gettingImage = true;
    photoCapture.getPreviewImage().then(
        function(image) {
          previewData.data.set(image.data);
          previewContext.putImageData(previewData, 0, 0);
          gettingImage = false;
        }, function() { });
  };

  photoCapture.onerror = function(e) {
    statusElement.innerHTML = 'Status Info : onerror: ' + e.status;
  };

  startButton.onclick = function(e) {
    statusElement.innerHTML = 'Status Info : Start: ';
    gettingImage = false;
    photoCapture.startPreview().then(function(e) { statusElement.innerHTML += e; },
                                     function(e) { statusElement.innerHTML += e; });
  };

  takePhotoButton.onclick = function(e) {
    photoCapture.takePhoto().then(
        function(photo) {
          currentPhoto = photo;
          savePhoto = photo;
          currentPhoto.queryContainerImage().then(
              function(image) {
                imageData =
                    imageContext.createImageData(image.width, image.height);
                statusElement.innerHTML = 'Select the bounding box around the object.';
                overlayContext.clearRect(0, 0, width, height);
                imageData.data.set(image.data);
                imageContext.putImageData(imageData, 0, 0);
                hasImage = true;
              },
              function(e) { statusElement.innerHTML += e; });
        },
        function(e) { statusElement.innerHTML += e; });
  };

  saveButton.onclick = function(e) {
    if (!savePhoto) {
      statusElement.innerHTML = 'There is no photo to save';
      return;
    }
    savePhoto.saveXDM().then(
        function(blob) {
          var reader = new FileReader();
          reader.onload = function(evt) {
            var image = document.getElementById('imageDisplayArea');
            image.src = evt.target.result;
            // Currently, crosswalk has bugs to download (see XWALK-5220). So the following code
            // doesn't work. Once download is enabled. The processed image will be
            // downloaded into 'Downloads' folder.
            var a = document.createElement('a');
            a.href = evt.target.result;
            a.download = true;
            a.click();
            statusElement.innerHTML = 'Save successfully';
          };
          reader.readAsDataURL(blob);
        },
        function(e) { statusElement.innerHTML = e; });
  };

  loadPhoto.addEventListener('change', function(e) {
    var file = loadPhoto.files[0];
    var dp = new realsense.DepthEnabledPhotography.DepthPhoto();
    dp.loadXDM(file).then(
        function (sucess) {
          currentPhoto = dp;
          savePhoto = dp;
          currentPhoto.queryContainerImage().then(
              function(image) {
                imageContext.clearRect(0, 0, width, height);
                imageData = imageContext.createImageData(image.width, image.height);
                statusElement.innerHTML = 'Select the bounding box around the object.';
                overlayContext.clearRect(0, 0, width, height);
                imageData.data.set(image.data);
                imageContext.putImageData(imageData, 0, 0);
                hasImage = true;
              },
              function(e) { statusElement.innerHTML = e; });
        },
        function(e) { statusElement.innerHTML = e; });
  });

  stopButton.onclick = function(e) {
    statusElement.innerHTML = 'Status Info : Stop: ';
    photoCapture.stopPreview().then(function(e) { statusElement.innerHTML += e; },
                                    function(e) { statusElement.innerHTML += e; });
  };

  segmentationButton.onclick = function(e) {
    overlayContext.clearRect(0, 0, width, height);
    mouseDown = 0;
    currentPhoto.queryContainerImage().then(
        function(image) {
          imageContext.clearRect(0, 0, width, height);
          imageData =
              imageContext.createImageData(image.width, image.height);
          statusElement.innerHTML = 'Select the bounding box around the object.';
          overlayContext.clearRect(0, 0, width, height);
          imageData.data.set(image.data);
          imageContext.putImageData(imageData, 0, 0);
          hasImage = true;
        },
        function(e) { statusElement.innerHTML += e; });
  };
}
<|endoftext|>