<commit_before>
import webfinger from '../lib/webfinger/lib/webfinger.js';

const dispatchToActor = function(actor, activity) {
    HTTP.post(actor.inbox, {data: activity, npmRequestOptions: {
        httpSignature: {
            authorizationHeaderName: "Signature",
            keyId: activity.actor + "#main-key",
            key: Keys.findOne({id: activity.actor + "#main-key"}, {fields: {privateKeyPem: 1}}).privateKeyPem
        }
    }}, function(err, result) {
        if (err) console.log("Error: ", err);
        if (result) console.log("Result: ", result);
    });
};

dispatchActivity = function(activity) {

    activity = cleanActivityPub(activity);

    const targetArrays = ['to', 'cc', 'bto', 'bcc', 'audience'];

    for (let i = 0; i < targetArrays.length; i++) {
        const arrayName = targetArrays[i];
        const targetArray = activity[arrayName];
        for (let j = 0; j < targetArray.length; j++) {
            const targetID = targetArray[j];
            if (targetID === "https://www.w3.org/ns/activitystreams#Public")
                continue;
            let actor = Actors.findOne({id: targetID});
            if (actor)
                dispatchToActor(actor, activity);
            else {
                const list = FollowerLists.findOne({id: targetID});
                if (list)
                    for (let actorID in list.orderedItems) {
                        actor = Actors.findOne({id: actorID});
                        if (actor)
                            dispatchToActor(actor, activity);
                    }
            }
        }
    }
};

const checkFederatedActivityPermitted = function(activity) {

    //TODO: Check to see if foreign instances are blocked or banned.

    const actor = Actors.findOne({id: activity.actor});

    //I think theres some kind of race condition going on here, where it won't
    //have finished importing the actor by the time it gets to this code and so it throws an error?

    //I tried to ensure it would have to have finished by the time it gets here, but it seems like I didn't quite manage.
    if (!actor)
        throw new Meteor.Error('Actor Not Found', 'No actor with the given ID could be found in the database: ' + activity.actor);

    switch(activity.type) {

        //Users can follow without being verified. Thus, return here, instead of further down after the verification check.
        case 'Follow':
        case 'Undo':
            return;

        case 'Update':
        case 'Delete':

            checkUpdateOrDeleteActivityPermitted(activity);

        //No break here, as update and delete activities should be subject to the same restrictions as create and announce.
        case 'Create':

        if (Posts.findOne({id: activity.object.id}))
            throw new Meteor.Error('Post Already Exists', 'Cannot insert post, id already exists.');

        case 'Announce':
    }

    //TODO: Check to see if foreign actors are blocked or banned.
};

const processFederatedFollowActivity = function(activity) {
    const followee = Actors.findOne({id: activity.object});

    if (followee.local)
        FollowerLists.update({id: followee.followers}, {$inc: {totalItems: 1}, $push: {orderedItems: activity.actor}});
};

const processFederatedCreateActivity = function(activity) {
    const post = getObjectFromActivity(activity);

    if (Posts.findOne({id: post.id}))
        throw new Meteor.Error('Post Already Exists', 'Cannot insert post, id already exists: ' + JSON.stringify({prev: Posts.findOne({id: post.id}), post: post}));

    post.local = false;
    Posts.insert(post);

    return activity;
};

const processFederatedDeleteActivity = function(activity) {
    const postID = activity.object;

    deletePost(postID);

    return activity;
};

const processFederatedAcceptActivity = function(activity) {

    let follower;

    switch (typeof activity.object) {
        case 'string':
            follower = Actors.findOne({id: activity.object});
            break;
        case 'object':
            follower = Actors.findOne({id: activity.object.actor});
            break;
    }

    if (!follower)
        throw new Meteor.Error('Actor not found!', 'No actor with the given ID could be found: ' + JSON.stringify(activity.object));

    const followee = Actors.findOne({id: activity.actor});

    let pendingFollow = PendingFollows.findOne({follower: follower.id, followee: followee.id});

    if (!pendingFollow)
        throw new Meteor.Error('No Follow Pending!', 'No pending follow between those actors was found: ' + activity.object + ", " + activity.actor);

    FollowingLists.update({id: follower.following}, {$inc: {totalItems: 1}, $push: {orderedItems: activity.actor}});

    if (followee.local)
        FollowerLists.update({id: followee.followers}, {$inc: {totalItems: 1}, $push: {orderedItems: activity.object}});

    PendingFollows.remove(pendingFollow);

    return activity;
};

const processFederatedUndoActivity = function(activity) {

    let targetActivity = Activities.findOne({id: activity.object});

    if (targetActivity.type === "Follow"){

        const follower = Actors.findOne({id: targetActivity.actor});

        if (!follower)
            throw new Meteor.Error('Actor not found!', 'No actor with the given ID could be found: ' + targetActivity.object);

        const followee = Actors.findOne({id: targetActivity.actor});

        FollowingLists.update({id: follower.following}, {$inc: {totalItems: -1}, $pull: {orderedItems: targetActivity.actor}});

        if (followee.local)
            FollowerLists.update({id: followee.followers}, {$inc: {totalItems: -1}, $pull: {orderedItems: targetActivity.object}});

        PendingFollows.remove({follower: follower.id, followee: followee.id});

        return activity;
    }
};

const processFederatedActivity = function(activity) {

    if (activity.id && Activities.findOne({id: activity.id}))
        return;

    let processFederatedActivityCallback = function() {
        try {
            checkFederatedActivityPermitted(activity);
        } catch (error) {
            if (['Post Not Present', 'Post Already Exists'].includes(error.error)) {
                const _id = Activities.insert(activity);

                return Activities.findOne({_id: _id});
            } else throw error;
        }

        switch(activity.type) {
            case 'Create':
                try {
                    activity = processFederatedCreateActivity(activity);
                } catch (error) {
                    throw error;
                }
                break;
            case 'Delete':
            try {
                activity = processFederatedDeleteActivity(activity);
            } catch (error) {
                if (error.error === 'Post Not Present') {

                } else throw error;
            }
                break;
            case "Accept":
                activity = processFederatedAcceptActivity(activity);
                break;
        }

        Activities.insert(activity);
    }

    if (activity.actor && !Actors.findOne({id: activity.actor})) { //Is this posts actor already present? If not,
        importActivityJSONFromUrl(activity.actor, processFederatedActivityCallback); //add it, and finish the function as a callback.
    } else {
        processFederatedActivityCallback(); //If it is present, then continue as normal.
    }
};

const importActorFromActivityPubJSON = function(json) {
    if (!Actors.findOne({id: json.id})) { //Is actor already present? If not,
        json.local = false; //mark it as foreign,
        Actors.insert(json); //then add it.

        console.log("Added Actor: " + json.id);
    }
};

const importPostFromActivityPubJSON = function(json) {
    if (!Posts.findOne({id: json.id})) { //Is post already present? If not,
        json.local = false; //mark it as foreign,
        Posts.insert(json); //then add it.
    }
};

importActivityJSONFromUrl = function(url, callback) {
    console.log("Importing from: " + url);

    return getActivityFromUrl(url)
    .then((response) => {
        if (response) {
            return response.json();
        } else throw new Meteor.Error('No response from url');
    })
    .then((json) => {
        importFromActivityPubJSON(json);

        if (callback) callback(json);
        return json;
    }).catch((err) => { console.log(err); });
};

importFromActivityPubJSON = function(json) {
    if (!json) throw new Meteor.Error('Empty JSON');

    if (!json.type) throw new Meteor.Error('Untyped ActivityPub JSON');

    if (activityPubActorTypes.includes(json.type))
        importActorFromActivityPubJSON(json);

    else if (activityPubContentTypes.includes(json.type))
        importPostFromActivityPubJSON(json);
};

Meteor.methods({
    importActivityJSONFromUrl: importActivityJSONFromUrl,
    importFromActivityPubJSON: importFromActivityPubJSON,
    postActivity: function(object) {
        const user = Meteor.users.findOne({_id: this.userId});

        let result = processClientActivity(user, object);

        return result;
    }
});

const successfulJSON = function(data) {
    response = {
        statusCode: 200
    };

    if (data) {
        response.body = cleanActivityPub(data);
    }

    return response;
};

const failedJSON = function(message) {
    return {
        statusCode: 400,
        body: {status: "fail", message: message}
    };
};

Api = new Restivus({
    apiPath: '/',
    prettyJson: true
});

Api.addRoute('.well-known/host-meta', {}, {
    get: {
        action: function () {
            let response = successfulJSON();
            response.headers = {
                'Access-Control-Allow-Origin': '*',
                'Content-Type': 'application/xml; charset=UTF-8'
            };

            response.body = '<XRD>' +
                            '  <Link rel="lrdd"' +
                            '    type="application/xrd+xml"' +
                            '    template="' +
                            process.env.ROOT_URL + '/webfinger/xrd/{uri}" />' +
                            '</XRD>'

            return response;
        }
    }
});

Api.addRoute('.well-known/host-meta.json', {}, {
    get: {
        action: function () {
            let response = successfulJSON();
            response.headers = {
                'Access-Control-Allow-Origin': '*',
                'Content-Type': 'application/json; charset=UTF-8'
            };

            response.body = {
                links: [
                    {
                        "rel": "lrdd",
                        "type": "application/json",
                        "template": process.env.ROOT_URL + "/.well-known/webfinger?resource={uri}"
                    }
                ]
            }

            return response;
        }
    }
});

Api.addRoute('.well-known/webfinger', {}, {
    get: {
        action: function () {

            let resource = this.queryParams.resource;
            var re = /(acct:@?)(.*)(@.*)/;
            let handle = resource.replace(re, "$2");
            let actor = Actors.findOne({preferredUsername: handle, local: true});

            actor = cleanActivityPub(actor);

            let response = successfulJSON();

            response.headers = {
                'Content-Type': 'application/json; charset=UTF-8'
            };

            response.body = {
                subject: resource,
                aliases: [actor.url, actor.id],
                links: [
                    {
                        "rel":"http://webfinger.net/rel/profile-page",
                        "type":"text/html",
                        "href": actor.url
                    },
                    {
                        "rel":"self",
                        "type":"application/activity+json",
                        "href": actor.id
                    }
                ]
            }

            return response;
        }
    }
});

Api.addRoute('post/:_id', {}, {
    get: {
        action: function () {
            let post = Posts.findOne({_id: this.urlParams._id});
            if (post) {
                let response = successfulJSON(post);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get post!");
        }
    }
});

Api.addRoute('activity/:_id', {}, {
    get: {
        action: function () {
            let activity = Activities.findOne({_id: this.urlParams._id});
            if (activity) {
                let response = successfulJSON(activity);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get post!");
        }
    }
});

Api.addRoute('actors/:handle', {}, {
    get: {
        action: function () {
            let actor = Actors.findOne({preferredUsername: this.urlParams.handle, local: true});
            if (actor) {
                let response = successfulJSON(actor);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get actor!");
        }
    }
});

Api.addRoute('actors/:handle/inbox', {}, {
    post: {
        action: function () {
            let object = this.request.body;
            processFederatedActivity(object);
            return successfulJSON();
        }
    }
});

Api.addRoute('actors/:handle/outbox', {}, {
    get: {
        action: function () {
            var outbox = Outboxes.findOne({id: process.env.ROOT_URL + "actors/" + this.urlParams.handle + "/outbox"});
            if (outbox) {
                let orderedItems = [];
                for (let i = 0; i < outbox.totalItems && i < 10; i++) {
                    orderedItems.push(Activities.findOne({id: outbox.orderedItems[i]}));
                }
                outbox.orderedItems = orderedItems;
                let response = successfulJSON(outbox);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get outbox!");
        }
    },
    post: {
        action: function () {
            let object = this.request.body;

            try {
                return successfulJSON(processClientActivity(object));
            } catch (exception) {
                return failedJSON(exception.reason);
            }
        }
    }
});

Api.addRoute('actors/:handle/following', {}, {
    get: {
        action: function () {
            let following = FollowingLists.findOne({id: process.env.ROOT_URL + "actors/" + this.urlParams.handle + "/following"});
            if (following) {
                let response = successfulJSON(following);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get following list!");
        }
    }
});

Api.addRoute('actors/:handle/followers', {}, {
    get: {
        action: function () {
            let actor = Actors.findOne({preferredUsername: this.urlParams.handle});
            if (actor) {
                let followers = FollowerLists.findOne({id: actor.followers});
                if (followers) {
                    return successfulJSON(followers);
                }
            }

            return failedJSON("Unable to get followers list!");
        }
    }
});
<commit_msg>removed unnecessary dependency<commit_after>
import webfinger from '../lib/webfinger/lib/webfinger.js';

const dispatchToActor = function(actor, activity) {
    HTTP.post(actor.inbox, {data: activity, npmRequestOptions: {
        httpSignature: {
            authorizationHeaderName: "Signature",
            keyId: activity.actor + "#main-key",
            key: Keys.findOne({id: activity.actor + "#main-key"}, {fields: {privateKeyPem: 1}}).privateKeyPem
        }
    }}, function(err, result) {
        if (err) console.log("Error: ", err);
        if (result) console.log("Result: ", result);
    });
};

dispatchActivity = function(activity) {

    activity = cleanActivityPub(activity);

    const targetArrays = ['to', 'cc', 'bto', 'bcc', 'audience'];

    for (let i = 0; i < targetArrays.length; i++) {
        const arrayName = targetArrays[i];
        const targetArray = activity[arrayName];
        for (let j = 0; j < targetArray.length; j++) {
            const targetID = targetArray[j];
            if (targetID === "https://www.w3.org/ns/activitystreams#Public")
                continue;
            let actor = Actors.findOne({id: targetID});
            if (actor)
                dispatchToActor(actor, activity);
            else {
                const list = FollowerLists.findOne({id: targetID});
                if (list)
                    for (let actorID in list.orderedItems) {
                        actor = Actors.findOne({id: actorID});
                        if (actor)
                            dispatchToActor(actor, activity);
                    }
            }
        }
    }
};

const checkFederatedActivityPermitted = function(activity) {

    //TODO: Check to see if foreign instances are blocked or banned.

    const actor = Actors.findOne({id: activity.actor});

    //I think theres some kind of race condition going on here, where it won't
    //have finished importing the actor by the time it gets to this code and so it throws an error?

    //I tried to ensure it would have to have finished by the time it gets here, but it seems like I didn't quite manage.
    if (!actor)
        throw new Meteor.Error('Actor Not Found', 'No actor with the given ID could be found in the database: ' + activity.actor);

    switch(activity.type) {

        //Users can follow without being verified. Thus, return here, instead of further down after the verification check.
        case 'Follow':
        case 'Undo':
            return;

        case 'Update':
        case 'Delete':

            checkUpdateOrDeleteActivityPermitted(activity);

        //No break here, as update and delete activities should be subject to the same restrictions as create and announce.
        case 'Create':

        if (Posts.findOne({id: activity.object.id}))
            throw new Meteor.Error('Post Already Exists', 'Cannot insert post, id already exists.');

        case 'Announce':
    }

    //TODO: Check to see if foreign actors are blocked or banned.
};

const processFederatedFollowActivity = function(activity) {
    const followee = Actors.findOne({id: activity.object});

    if (followee.local)
        FollowerLists.update({id: followee.followers}, {$inc: {totalItems: 1}, $push: {orderedItems: activity.actor}});
};

const processFederatedCreateActivity = function(activity) {
    const post = getObjectFromActivity(activity);

    if (Posts.findOne({id: post.id}))
        throw new Meteor.Error('Post Already Exists', 'Cannot insert post, id already exists: ' + JSON.stringify({prev: Posts.findOne({id: post.id}), post: post}));

    post.local = false;
    Posts.insert(post);

    return activity;
};

const processFederatedDeleteActivity = function(activity) {
    const postID = activity.object;

    deletePost(postID);

    return activity;
};

const processFederatedAcceptActivity = function(activity) {

    let follower;

    switch (typeof activity.object) {
        case 'string':
            follower = Actors.findOne({id: activity.object});
            break;
        case 'object':
            follower = Actors.findOne({id: activity.object.actor});
            break;
    }

    if (!follower)
        throw new Meteor.Error('Actor not found!', 'No actor with the given ID could be found: ' + JSON.stringify(activity.object));

    const followee = Actors.findOne({id: activity.actor});

    let pendingFollow = PendingFollows.findOne({follower: follower.id, followee: followee.id});

    if (!pendingFollow)
        throw new Meteor.Error('No Follow Pending!', 'No pending follow between those actors was found: ' + activity.object + ", " + activity.actor);

    FollowingLists.update({id: follower.following}, {$inc: {totalItems: 1}, $push: {orderedItems: activity.actor}});

    if (followee.local)
        FollowerLists.update({id: followee.followers}, {$inc: {totalItems: 1}, $push: {orderedItems: activity.object}});

    PendingFollows.remove(pendingFollow);

    return activity;
};

const processFederatedUndoActivity = function(activity) {

    let targetActivity = Activities.findOne({id: activity.object});

    if (targetActivity.type === "Follow"){

        const follower = Actors.findOne({id: targetActivity.actor});

        if (!follower)
            throw new Meteor.Error('Actor not found!', 'No actor with the given ID could be found: ' + targetActivity.object);

        const followee = Actors.findOne({id: targetActivity.actor});

        FollowingLists.update({id: follower.following}, {$inc: {totalItems: -1}, $pull: {orderedItems: targetActivity.actor}});

        if (followee.local)
            FollowerLists.update({id: followee.followers}, {$inc: {totalItems: -1}, $pull: {orderedItems: targetActivity.object}});

        PendingFollows.remove({follower: follower.id, followee: followee.id});

        return activity;
    }
};

const processFederatedActivity = function(activity) {

    if (activity.id && Activities.findOne({id: activity.id}))
        return;

    let processFederatedActivityCallback = function() {
        try {
            checkFederatedActivityPermitted(activity);
        } catch (error) {
            if (['Post Not Present', 'Post Already Exists'].includes(error.error)) {
                const _id = Activities.insert(activity);

                return Activities.findOne({_id: _id});
            } else throw error;
        }

        switch(activity.type) {
            case 'Create':
                try {
                    activity = processFederatedCreateActivity(activity);
                } catch (error) {
                    throw error;
                }
                break;
            case 'Delete':
            try {
                activity = processFederatedDeleteActivity(activity);
            } catch (error) {
                if (error.error === 'Post Not Present') {

                } else throw error;
            }
                break;
            case "Accept":
                activity = processFederatedAcceptActivity(activity);
                break;
        }

        Activities.insert(activity);
    }

    if (activity.actor && !Actors.findOne({id: activity.actor})) { //Is this posts actor already present? If not,
        importActivityJSONFromUrl(activity.actor, processFederatedActivityCallback); //add it, and finish the function as a callback.
    } else {
        processFederatedActivityCallback(); //If it is present, then continue as normal.
    }
};

const importActorFromActivityPubJSON = function(json) {
    if (!Actors.findOne({id: json.id})) { //Is actor already present? If not,
        json.local = false; //mark it as foreign,
        Actors.insert(json); //then add it.

        console.log("Added Actor: " + json.id);
    }
};

const importPostFromActivityPubJSON = function(json) {
    if (!Posts.findOne({id: json.id})) { //Is post already present? If not,
        json.local = false; //mark it as foreign,
        Posts.insert(json); //then add it.
    }
};

importActivityJSONFromUrl = function(url, callback) {
    console.log("Importing from: " + url);

    let json = JSON.parse(HTTP.get(url, {
        headers: {
            Accept: 'application/activity+json; profile="https://www.w3.org/ns/activitystreams#"'
        }
    }).content);

    importFromActivityPubJSON(json);

    if (callback) callback(json);

    return json;
};

importFromActivityPubJSON = function(json) {
    if (!json) throw new Meteor.Error('Empty JSON');

    if (!json.type) throw new Meteor.Error('Untyped ActivityPub JSON');

    if (activityPubActorTypes.includes(json.type))
        importActorFromActivityPubJSON(json);

    else if (activityPubContentTypes.includes(json.type))
        importPostFromActivityPubJSON(json);
};

Meteor.methods({
    importActivityJSONFromUrl: importActivityJSONFromUrl,
    importFromActivityPubJSON: importFromActivityPubJSON,
    postActivity: function(object) {
        const user = Meteor.users.findOne({_id: this.userId});

        let result = processClientActivity(user, object);

        return result;
    }
});

const successfulJSON = function(data) {
    response = {
        statusCode: 200
    };

    if (data) {
        response.body = cleanActivityPub(data);
    }

    return response;
};

const failedJSON = function(message) {
    return {
        statusCode: 400,
        body: {status: "fail", message: message}
    };
};

Api = new Restivus({
    apiPath: '/',
    prettyJson: true
});

Api.addRoute('.well-known/host-meta', {}, {
    get: {
        action: function () {
            let response = successfulJSON();
            response.headers = {
                'Access-Control-Allow-Origin': '*',
                'Content-Type': 'application/xml; charset=UTF-8'
            };

            response.body = '<XRD>' +
                            '  <Link rel="lrdd"' +
                            '    type="application/xrd+xml"' +
                            '    template="' +
                            process.env.ROOT_URL + '/webfinger/xrd/{uri}" />' +
                            '</XRD>'

            return response;
        }
    }
});

Api.addRoute('.well-known/host-meta.json', {}, {
    get: {
        action: function () {
            let response = successfulJSON();
            response.headers = {
                'Access-Control-Allow-Origin': '*',
                'Content-Type': 'application/json; charset=UTF-8'
            };

            response.body = {
                links: [
                    {
                        "rel": "lrdd",
                        "type": "application/json",
                        "template": process.env.ROOT_URL + "/.well-known/webfinger?resource={uri}"
                    }
                ]
            }

            return response;
        }
    }
});

Api.addRoute('.well-known/webfinger', {}, {
    get: {
        action: function () {

            let resource = this.queryParams.resource;
            var re = /(acct:@?)(.*)(@.*)/;
            let handle = resource.replace(re, "$2");
            let actor = Actors.findOne({preferredUsername: handle, local: true});

            actor = cleanActivityPub(actor);

            let response = successfulJSON();

            response.headers = {
                'Content-Type': 'application/json; charset=UTF-8'
            };

            response.body = {
                subject: resource,
                aliases: [actor.url, actor.id],
                links: [
                    {
                        "rel":"http://webfinger.net/rel/profile-page",
                        "type":"text/html",
                        "href": actor.url
                    },
                    {
                        "rel":"self",
                        "type":"application/activity+json",
                        "href": actor.id
                    }
                ]
            }

            return response;
        }
    }
});

Api.addRoute('post/:_id', {}, {
    get: {
        action: function () {
            let post = Posts.findOne({_id: this.urlParams._id});
            if (post) {
                let response = successfulJSON(post);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get post!");
        }
    }
});

Api.addRoute('activity/:_id', {}, {
    get: {
        action: function () {
            let activity = Activities.findOne({_id: this.urlParams._id});
            if (activity) {
                let response = successfulJSON(activity);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get post!");
        }
    }
});

Api.addRoute('actors/:handle', {}, {
    get: {
        action: function () {
            let actor = Actors.findOne({preferredUsername: this.urlParams.handle, local: true});
            if (actor) {
                let response = successfulJSON(actor);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get actor!");
        }
    }
});

Api.addRoute('actors/:handle/inbox', {}, {
    post: {
        action: function () {
            let object = this.request.body;
            processFederatedActivity(object);
            return successfulJSON();
        }
    }
});

Api.addRoute('actors/:handle/outbox', {}, {
    get: {
        action: function () {
            var outbox = Outboxes.findOne({id: process.env.ROOT_URL + "actors/" + this.urlParams.handle + "/outbox"});
            if (outbox) {
                let orderedItems = [];
                for (let i = 0; i < outbox.totalItems && i < 10; i++) {
                    orderedItems.push(Activities.findOne({id: outbox.orderedItems[i]}));
                }
                outbox.orderedItems = orderedItems;
                let response = successfulJSON(outbox);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get outbox!");
        }
    },
    post: {
        action: function () {
            let object = this.request.body;

            try {
                return successfulJSON(processClientActivity(object));
            } catch (exception) {
                return failedJSON(exception.reason);
            }
        }
    }
});

Api.addRoute('actors/:handle/following', {}, {
    get: {
        action: function () {
            let following = FollowingLists.findOne({id: process.env.ROOT_URL + "actors/" + this.urlParams.handle + "/following"});
            if (following) {
                let response = successfulJSON(following);
                response.headers = {
                    'Content-Type': 'activity/json; charset=UTF-8'
                };
                return response;
            } else return failedJSON("Unable to get following list!");
        }
    }
});

Api.addRoute('actors/:handle/followers', {}, {
    get: {
        action: function () {
            let actor = Actors.findOne({preferredUsername: this.urlParams.handle});
            if (actor) {
                let followers = FollowerLists.findOne({id: actor.followers});
                if (followers) {
                    return successfulJSON(followers);
                }
            }

            return failedJSON("Unable to get followers list!");
        }
    }
});
<|endoftext|>


<commit_before>'use strict'

export default class BrowserSurfaceView {
  /**
   *
   * @param {BrowserSurface} browserSurface
   * @returns {BrowserSurfaceView}
   */
  static create (browserSurface) {
    const canvas = this._createCanvas()
    const context2d = canvas.getContext('2d')
    this._setupMouseListeners(canvas, browserSurface)

    return new BrowserSurfaceView(canvas, context2d)
  }

  /**
   * @returns {HTMLCanvasElement}
   * @private
   */
  static _createCanvas () {
    const canvas = document.createElement('canvas')
    canvas.style.left = '0px'
    canvas.style.top = '0px'
    canvas.style.position = 'relative'
    document.body.appendChild(canvas)
    return canvas
  }

  static _setupMouseListeners (canvas, browserSurface) {
    const browserPointer = browserSurface.browserSeat.browserPointer
    canvas.addEventListener('mouseenter', (event) => {
      browserPointer.onMouseEnter(event, browserSurface)
    }, true)
    canvas.addEventListener('mouseleave', () => {
      browserPointer.onMouseLeave(browserSurface)
    }, true)
    // other mouse listeners are set in the browser pointer class
  }

  /**
   *
   * @param canvas
   * @param context2d
   */
  constructor (canvas, context2d) {
    this.canvas = canvas
    this.context2d = context2d
  }

  unfade () {
    this._unfade(0.08)
  }

  _unfade (opacity) {
    if (opacity < 1) {
      window.requestAnimationFrame(() => {
        this._unfade(opacity)
      })
    }
    this.canvas.style.opacity = opacity
    this.canvas.style.filter = 'alpha(opacity=' + opacity * 100 + ')'
    opacity *= 1.1
  }

  fade () {
    this._fade(1)
  }

  _fade (opacity) {
    if (opacity > 0.1) {
      window.requestAnimationFrame(() => {
        this._fade(opacity)
      })
    } else {
      document.body.removeChild(this.canvas)
    }
    this.canvas.style.opacity = opacity
    this.canvas.style.filter = 'alpha(opacity=' + opacity * 100 + ')'
    opacity *= 0.8
  }

  destroy () {
    this.fade()
  }
}
<commit_msg>give surface view canvas a reference to the view<commit_after>'use strict'
import Vect4 from './math/Vect4'
import Mat4 from './math/Mat4'

export default class BrowserSurfaceView {
  /**
   *
   * @param {BrowserSurface} browserSurface
   * @returns {BrowserSurfaceView}
   */
  static create (browserSurface) {
    const canvas = this._createCanvas()
    const context2d = canvas.getContext('2d')
    this._setupMouseListeners(canvas, browserSurface)

    const browserSurfaceView = new BrowserSurfaceView(canvas, context2d, browserSurface)
    canvas.view = browserSurfaceView
    return browserSurfaceView
  }

  /**
   * @returns {HTMLCanvasElement}
   * @private
   */
  static _createCanvas () {
    const canvas = document.createElement('canvas')
    canvas.style.left = '0px'
    canvas.style.top = '0px'
    canvas.style.position = 'relative'
    document.body.appendChild(canvas)
    return canvas
  }

  static _setupMouseListeners (canvas, browserSurface) {
    const browserPointer = browserSurface.browserSeat.browserPointer
    canvas.addEventListener('mouseenter', (event) => {
      browserPointer.onMouseEnter(event, browserSurface)
    }, true)
    canvas.addEventListener('mouseleave', () => {
      browserPointer.onMouseLeave(browserSurface)
    }, true)
    // other mouse listeners are set in the browser pointer class
  }

  /**
   * @private
   * @param canvas
   * @param context2d
   * @param browserSurface
   */
  constructor (canvas, context2d, browserSurface) {
    this.canvas = canvas
    this.context2d = context2d
    this.browserSurface = browserSurface
  }

  /**
   * @param {Point} canvasPoint
   * @return {Point}
   */
  toSurfaceSpace (canvasPoint) {
    const surfaceWidth = this.browserSurface.size.w
    const surfaceHeight = this.browserSurface.size.h
    const canvasWidth = this.canvas.width
    const canvasHeight = this.canvas.height

    const scalarVector = Vect4.create(surfaceWidth / canvasWidth, surfaceHeight / canvasHeight, 1, 1)

    return Mat4.scalarVector(scalarVector).timesPoint(canvasPoint)
  }

  unfade () {
    this._unfade(0.08)
  }

  _unfade (opacity) {
    if (opacity < 1) {
      window.requestAnimationFrame(() => {
        this._unfade(opacity)
      })
    }
    this.canvas.style.opacity = opacity
    this.canvas.style.filter = 'alpha(opacity=' + opacity * 100 + ')'
    opacity *= 1.1
  }

  fade () {
    this._fade(1)
  }

  _fade (opacity) {
    if (opacity > 0.1) {
      window.requestAnimationFrame(() => {
        this._fade(opacity)
      })
    } else {
      document.body.removeChild(this.canvas)
    }
    this.canvas.style.opacity = opacity
    this.canvas.style.filter = 'alpha(opacity=' + opacity * 100 + ')'
    opacity *= 0.8
  }

  destroy () {
    this.fade()
  }
}
<|endoftext|>


<commit_before>/**
 * @package     hubzero.cms.admin
 * @subpackage  com_dataviewer
 *
 * @author      Sudheera R. Fernando sudheera@xconsole.org
 * @copyright   Copyright 2005-2011,2013 Purdue University. All rights reserved.
 * @license     http://www.gnu.org/licenses/lgpl-3.0.html LGPLv3 or later; see LICENSE.txt
 */

db = {};
var db_back_link = '/administrator/index.php?option=com_dataviewer';

db.dbJQ = jQuery.noConflict();

(function($) {

	setInterval(function() {
		$.get('index.php');
	}, 1000 * 60 * 10);

}(db.dbJQ));

db.dbJQ(document).ready(function($) {

	/* Back Link */
	$('#toolbar-back a.toolbar').removeAttr('onclick').attr('href', db_back_link);

});
<commit_msg>[COM_DATAVIEWER] Cleanup: removing noConflict().<commit_after>/**
 * @package     hubzero.cms.admin
 * @subpackage  com_dataviewer
 *
 * @author      Sudheera R. Fernando sudheera@xconsole.org
 * @copyright   Copyright 2005-2011,2013 Purdue University. All rights reserved.
 * @license     http://www.gnu.org/licenses/lgpl-3.0.html LGPLv3 or later; see LICENSE.txt
 */

db = {};
var db_back_link = '/administrator/index.php?option=com_dataviewer';

db.dbJQ = jQuery;

(function($) {

	setInterval(function() {
		$.get('index.php');
	}, 1000 * 60 * 10);

}(db.dbJQ));

db.dbJQ(document).ready(function($) {

	/* Back Link */
	$('#toolbar-back a.toolbar').removeAttr('onclick').attr('href', db_back_link);

});
<|endoftext|>


<commit_before>import React, {Component} from 'react'

export class Image extends Component {
  render () {
    const {src} = this.props
    return (
      <img src={src.includes('http://') ? src: `http://${src}`} width='100%' />
    )
  }
}
<commit_msg>add min and max width<commit_after>import React, {Component} from 'react'

export class Image extends Component {
  render () {
    const {src} = this.props
    return (

    )
  }
}
<|endoftext|>


<commit_before>define({
	// This css path is used as a default by the Kalei project it self.
	//css_path: window.location.protocol + '//' + window.location.hostname + (window.location.port === '' ? '' : ':'+ window.location.port) + window.location.pathname +  'css/styles.css',
	css_path: window.location.protocol + '//' + window.location.hostname + (window.location.port === '' ? '' : ':'+ window.location.port) + window.location.pathname +  '../src/less/style.less',

	// You can configure any path by just deleting the one above and uncommenting the one below to point at your css directory
	// css_path: 'http://localhost/kaleistyleguide/css/styles.css',

	// You can manually list the css files to process by giving a css_paths array
	//css_paths: ['../css/styles.css', '../css/buttons.css'],

	// By default, Kalei parses raw CSS files using the jscssp library.
	// If you'd rather use a different processor or preprocessor, you can choose from the below:
	css_processor:
		//'jscssp',
		 'less',

	// You can optionally set configuration for marked.js
	marked_options: {
		tables: true
	},

	// Enter URLs of any external stylesheets you wish to include with this demo.
	// These will not be parsed as part of the documentation and are simply loaded into the page header.
	external_stylesheets: ['http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css']

	//disqus_shortname: 'kaleistyleguide'
});
<commit_msg>new css path name<commit_after>define({
	// This css path is used as a default by the Kalei project it self.
	//css_path: window.location.protocol + '//' + window.location.hostname + (window.location.port === '' ? '' : ':'+ window.location.port) + window.location.pathname +  'css/styles.css',
	css_path: window.location.protocol + '//' + window.location.hostname + (window.location.port === '' ? '' : ':'+ window.location.port) + window.location.pathname +  '../inc/less/style.less',

	// You can configure any path by just deleting the one above and uncommenting the one below to point at your css directory
	// css_path: 'http://localhost/kaleistyleguide/css/styles.css',

	// You can manually list the css files to process by giving a css_paths array
	//css_paths: ['../css/styles.css', '../css/buttons.css'],

	// By default, Kalei parses raw CSS files using the jscssp library.
	// If you'd rather use a different processor or preprocessor, you can choose from the below:
	css_processor:
		//'jscssp',
		 'less',

	// You can optionally set configuration for marked.js
	marked_options: {
		tables: true
	},

	// Enter URLs of any external stylesheets you wish to include with this demo.
	// These will not be parsed as part of the documentation and are simply loaded into the page header.
	external_stylesheets: ['http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css']

	//disqus_shortname: 'kaleistyleguide'
});
<|endoftext|>


<commit_before>  /**
 * @author Troy Ferrell & Yang Su
 */

<<<<<<< HEAD
function Tunnel() {
=======
function Tunnel(scene, callback) {
    this.scene = scene;
>>>>>>> cbd8dbf818591f50dd31e095974c35689223078c
    this.tunnelSegments = [];
    this.tunnelSections = [];
    // Index used to delete segments from the scene
    this.oldestLiveSection = 0;
    var that = this,
        texture_1 = THREE.ImageUtils.loadTexture('img/TunnelTexture.png', {}, function (data) {
        that.create(UTIL.getImageData(texture_1.image), callback);
    }),
        j,
        tunnelRing,
        startZ = -CONFIG.tunnelSegmentPerSection * CONFIG.tunnelSegmentDepth;
    //var texture_2 = THREE.ImageUtils.loadTexture('img/TrailTexture_2.png');
    //texture.wrapT = THREE.RepeatWrapping;

    this.tunnelMaterial = [
        new THREE.MeshLambertMaterial({
            map: texture_1,
            transparent : true
        }),
        //new THREE.MeshLambertMaterial(CONFIG.tunnelMaterial),
        new THREE.MeshLambertMaterial({
            color: 0x000000,
            opacity: 0.1,
            shading: THREE.FlatShading
        }),
        new THREE.MeshFaceMaterial()
    ];

    // this.generateTunnelSection(0);

    this.tunnelLights = [];
<<<<<<< HEAD
    var j, tunnelRing,
        startZ = -CONFIG.tunnelSegmentPerSection*CONFIG.tunnelSegmentDepth;
    for(j = 0; j < 3; j += 1) {
        tunnelRing = new LightRing(startZ - CONFIG.cameraFar*j);
=======
    for (j = 0; j < 3; j += 1) {
        tunnelRing = new LightRing(startZ - CONFIG.cameraFar * j, this.scene);
>>>>>>> cbd8dbf818591f50dd31e095974c35689223078c
        this.tunnelLights.push(tunnelRing);
    }
}

Tunnel.prototype.create = function(imgData, callback) {

    callback();
};
Tunnel.prototype.update = function (playerZ) {
    // Dynamic tunnel generation based on player position
    if (this.tunnelSegments.length * CONFIG.tunnelSegmentDepth <
            Math.abs(playerZ) + CONFIG.cameraFar) {
        this.generateTunnelSection(-this.tunnelSegments.length * CONFIG.tunnelSegmentDepth);
        if (this.tunnelSections.length - this.oldestLiveSection > CONFIG.tunnelLiveSections) {
            // Remove from scene
            window.scene.remove(this.tunnelSections[this.oldestLiveSection]);
            // Remove from tunnelSections
            delete this.tunnelSections[this.oldestLiveSection];
            // Move counter along
            this.oldestLiveSection += 1;
        }
    }

    // can't dynamically add lights to scene???
    // maybe instead of splicing array up everytime

    var firstLightRing = this.tunnelLights[0],
        lastLightRing = this.tunnelLights[this.tunnelLights.length - 1];
    if (Math.abs(firstLightRing.z) < Math.abs(playerZ) - CONFIG.cameraFar) {
        firstLightRing.repositionLightRing(lastLightRing.z - CONFIG.cameraFar);
        this.tunnelLights.splice(0, 1); // remove first element
        this.tunnelLights.push(firstLightRing); // add first element to element
    }

    _.each(this.tunnelLights, function (light) {
        light.update();
    });
};

Tunnel.prototype.generateTunnelSection = function (startZ) {
    var i = 0,
        newTunnelSeg = new TunnelSegment(startZ, this.tunnelMaterial),
        geometry = newTunnelSeg.geometry,
        newTunnelMesh;

    this.tunnelSegments.push(newTunnelSeg);

    for (i = 1; i < CONFIG.tunnelSegmentPerSection; i += 1) {
        newTunnelSeg = new TunnelSegment(startZ - i * CONFIG.tunnelSegmentDepth, this.tunnelMaterial);
        this.tunnelSegments.push(newTunnelSeg);
        // Merge with geometry
        THREE.GeometryUtils.merge(geometry, newTunnelSeg.geometry);
    }

    // Create a single mesh
    newTunnelMesh = new THREE.Mesh(geometry, this.tunnelMaterial[this.tunnelMaterial.length - 1]);
    this.tunnelSections.push(newTunnelMesh);
    window.scene.add(newTunnelMesh);
};

Tunnel.prototype.getFace = function (i, j) {
    if (i <= this.tunnelSegments.length && i >= 0) {
        return this.tunnelSegments[i].getFace(j);
    } else {
        console.log('Error: Tunnel getFace(' + i + ',' + j + ') index out of bounds');
        return null;
    }
};

<<<<<<< HEAD
function LightRing(startZ){
=======
function LightRing(startZ, scene) {
>>>>>>> cbd8dbf818591f50dd31e095974c35689223078c
    this.lights = [];
    this.rising = false;
    this.z = startZ;

    var deltaTheta = 2 * Math.PI / CONFIG.lightRingCount,
        radius = CONFIG.tunnelRadius,
        theta,
        newTunnelLight;

    for (theta = 0; theta < 2 * Math.PI; theta += deltaTheta) {
        newTunnelLight = new THREE.PointLight(CONFIG.lightColor, CONFIG.lightIntensity, CONFIG.lightRange);
        newTunnelLight.position.x = radius * Math.cos(theta);
        newTunnelLight.position.y = radius * Math.sin(theta);
        newTunnelLight.position.z = startZ;

        this.lights.push(newTunnelLight);
        window.scene.add(newTunnelLight);
    }
}

LightRing.prototype.update = function () {
    var step = CONFIG.lightIntensityStep;
    /*
     * I'm a fucking idiot. Just use sin funciton
     * EX: light.intensity = (MaxIntensity-MinIntensity)*sin(t) + MinIntensity;

    _.each(this.lights, function (light) {
        if (light.intensity >= step*10) this.rising = false;
        else if (light.intensity <= step*2) this.rising = true;

        if (this.rising) {
            light.intensity += step;
        }
        else{
            light.intensity -= step;
        }
    });*/
};

LightRing.prototype.repositionLightRing = function (newZ) {
    this.z = newZ;
    _.each(this.lights, function (light) {
        light.position.z = newZ;
    });
};

function TunnelSegment(startZ, materials) {
    this.geometry = new THREE.Geometry();
    this.geometry.dynamic = true;
    this.geometry.materials = materials;

    var deltaTheta = 2 * Math.PI / CONFIG.tunnelResolution,
        radius = CONFIG.tunnelRadius,
        faceCounter = 0,
        depth = CONFIG.tunnelSegmentDepth,
        theta,
        face,
        rcos,
        rsin,
        rcosd,
        rsind,
        faceuv,
        temp;

    // dynamically create quads for tunnel segment
    for (theta = 0; theta < 2 * Math.PI; theta += deltaTheta) {
        if (Math.floor(Math.random() * (materials.length - 1)) === 0) {
            rcos = radius * Math.cos(theta);
            rsin = radius * Math.sin(theta);
            rcosd = radius * Math.cos(theta + deltaTheta);
            rsind = radius * Math.sin(theta + deltaTheta);

            // Create vertices for current quad in cylinder segment
            this.geometry.vertices.push(UTIL.vtx3(rcos, rsin, startZ),
                                        UTIL.vtx3(rcos, rsin, startZ - depth),
                                        UTIL.vtx3(rcosd, rsind, startZ - depth),
                                        UTIL.vtx3(rcosd, rsind, startZ));

            // Define normals to point inward
            temp = faceCounter * 4;
            face = new THREE.Face4(temp + 3,
                                   temp + 2,
                                   temp + 1,
                                   temp);

            face.materialIndex = 0;
            this.geometry.faces.push(face);
            faceCounter += 1;

            // Configure UV Texturing coord data
            faceuv = [
                new THREE.UV(0, 1),
                new THREE.UV(1, 1),
                new THREE.UV(1, 0),
                new THREE.UV(0, 0)
            ];

            this.geometry.faceUvs[0].push(new THREE.UV(0, 1));
            this.geometry.faceVertexUvs[0].push(faceuv);
        }
    }

    this.geometry.computeFaceNormals();
    this.geometry.computeVertexNormals();
}

TunnelSegment.prototype.getFace = function (i) {
    if (i <= this.geometry.faces.length && i >= 0) {
        return this.geometry.faces[i];
    } else {
        console.log('Error: TunnelSegment getFace(' + i + ') index out of bounds');
        return null;
    }
};<commit_msg>Stuff<commit_after>  /**
 * @author Troy Ferrell & Yang Su
 */

function Tunnel(callback) {

    this.tunnelSegments = [];
    this.tunnelSections = [];
    // Index used to delete segments from the scene
    this.oldestLiveSection = 0;
    var that = this,
        texture_1 = THREE.ImageUtils.loadTexture('img/TunnelTexture.png', {}, function (data) {
        that.create(UTIL.getImageData(texture_1.image), callback);
    }),
        j,
        tunnelRing,
        startZ = -CONFIG.tunnelSegmentPerSection * CONFIG.tunnelSegmentDepth;
    //var texture_2 = THREE.ImageUtils.loadTexture('img/TrailTexture_2.png');
    //texture.wrapT = THREE.RepeatWrapping;

    this.tunnelMaterial = [
        new THREE.MeshLambertMaterial({
            map: texture_1,
            transparent : true
        }),
        //new THREE.MeshLambertMaterial(CONFIG.tunnelMaterial),
        new THREE.MeshLambertMaterial({
            color: 0x000000,
            opacity: 0.1,
            shading: THREE.FlatShading
        }),
        new THREE.MeshFaceMaterial()
    ];

    // this.generateTunnelSection(0);

    this.tunnelLights = [];
    var j, tunnelRing,
        startZ = -CONFIG.tunnelSegmentPerSection*CONFIG.tunnelSegmentDepth;
    for(j = 0; j < 3; j += 1) {
        tunnelRing = new LightRing(startZ - CONFIG.cameraFar*j);
        this.tunnelLights.push(tunnelRing);
    }
}

Tunnel.prototype.create = function(imgData, callback) {

    callback();
};
Tunnel.prototype.update = function (playerZ) {
    // Dynamic tunnel generation based on player position
    if (this.tunnelSegments.length * CONFIG.tunnelSegmentDepth <
            Math.abs(playerZ) + CONFIG.cameraFar) {
        this.generateTunnelSection(-this.tunnelSegments.length * CONFIG.tunnelSegmentDepth);
        if (this.tunnelSections.length - this.oldestLiveSection > CONFIG.tunnelLiveSections) {
            // Remove from scene
            window.scene.remove(this.tunnelSections[this.oldestLiveSection]);
            // Remove from tunnelSections
            delete this.tunnelSections[this.oldestLiveSection];
            // Move counter along
            this.oldestLiveSection += 1;
        }
    }

    // can't dynamically add lights to scene???
    // maybe instead of splicing array up everytime

    var firstLightRing = this.tunnelLights[0],
        lastLightRing = this.tunnelLights[this.tunnelLights.length - 1];
    if (Math.abs(firstLightRing.z) < Math.abs(playerZ) - CONFIG.cameraFar) {
        firstLightRing.repositionLightRing(lastLightRing.z - CONFIG.cameraFar);
        this.tunnelLights.splice(0, 1); // remove first element
        this.tunnelLights.push(firstLightRing); // add first element to element
    }

    _.each(this.tunnelLights, function (light) {
        light.update();
    });
};

Tunnel.prototype.generateTunnelSection = function (startZ) {
    var i = 0,
        newTunnelSeg = new TunnelSegment(startZ, this.tunnelMaterial),
        geometry = newTunnelSeg.geometry,
        newTunnelMesh;

    this.tunnelSegments.push(newTunnelSeg);

    for (i = 1; i < CONFIG.tunnelSegmentPerSection; i += 1) {
        newTunnelSeg = new TunnelSegment(startZ - i * CONFIG.tunnelSegmentDepth, this.tunnelMaterial);
        this.tunnelSegments.push(newTunnelSeg);
        // Merge with geometry
        THREE.GeometryUtils.merge(geometry, newTunnelSeg.geometry);
    }

    // Create a single mesh
    newTunnelMesh = new THREE.Mesh(geometry, this.tunnelMaterial[this.tunnelMaterial.length - 1]);
    this.tunnelSections.push(newTunnelMesh);
    window.scene.add(newTunnelMesh);
};

Tunnel.prototype.getFace = function (i, j) {
    if (i <= this.tunnelSegments.length && i >= 0) {
        return this.tunnelSegments[i].getFace(j);
    } else {
        console.log('Error: Tunnel getFace(' + i + ',' + j + ') index out of bounds');
        return null;
    }
};

function LightRing(startZ){
    this.lights = [];
    this.rising = false;
    this.z = startZ;

    var deltaTheta = 2 * Math.PI / CONFIG.lightRingCount,
        radius = CONFIG.tunnelRadius,
        theta,
        newTunnelLight;

    for (theta = 0; theta < 2 * Math.PI; theta += deltaTheta) {
        newTunnelLight = new THREE.PointLight(CONFIG.lightColor, CONFIG.lightIntensity, CONFIG.lightRange);
        newTunnelLight.position.x = radius * Math.cos(theta);
        newTunnelLight.position.y = radius * Math.sin(theta);
        newTunnelLight.position.z = startZ;

        this.lights.push(newTunnelLight);
        window.scene.add(newTunnelLight);
    }
}

LightRing.prototype.update = function () {
    var step = CONFIG.lightIntensityStep;
    /*
     * I'm a fucking idiot. Just use sin funciton
     * EX: light.intensity = (MaxIntensity-MinIntensity)*sin(t) + MinIntensity;

    _.each(this.lights, function (light) {
        if (light.intensity >= step*10) this.rising = false;
        else if (light.intensity <= step*2) this.rising = true;

        if (this.rising) {
            light.intensity += step;
        }
        else{
            light.intensity -= step;
        }
    });*/
};

LightRing.prototype.repositionLightRing = function (newZ) {
    this.z = newZ;
    _.each(this.lights, function (light) {
        light.position.z = newZ;
    });
};

function TunnelSegment(startZ, materials) {
    this.geometry = new THREE.Geometry();
    this.geometry.dynamic = true;
    this.geometry.materials = materials;

    var deltaTheta = 2 * Math.PI / CONFIG.tunnelResolution,
        radius = CONFIG.tunnelRadius,
        faceCounter = 0,
        depth = CONFIG.tunnelSegmentDepth,
        theta,
        face,
        rcos,
        rsin,
        rcosd,
        rsind,
        faceuv,
        temp;

    // dynamically create quads for tunnel segment
    for (theta = 0; theta < 2 * Math.PI; theta += deltaTheta) {
        if (Math.floor(Math.random() * (materials.length - 1)) === 0) {
            rcos = radius * Math.cos(theta);
            rsin = radius * Math.sin(theta);
            rcosd = radius * Math.cos(theta + deltaTheta);
            rsind = radius * Math.sin(theta + deltaTheta);

            // Create vertices for current quad in cylinder segment
            this.geometry.vertices.push(UTIL.vtx3(rcos, rsin, startZ),
                                        UTIL.vtx3(rcos, rsin, startZ - depth),
                                        UTIL.vtx3(rcosd, rsind, startZ - depth),
                                        UTIL.vtx3(rcosd, rsind, startZ));

            // Define normals to point inward
            temp = faceCounter * 4;
            face = new THREE.Face4(temp + 3,
                                   temp + 2,
                                   temp + 1,
                                   temp);

            face.materialIndex = 0;
            this.geometry.faces.push(face);
            faceCounter += 1;

            // Configure UV Texturing coord data
            faceuv = [
                new THREE.UV(0, 1),
                new THREE.UV(1, 1),
                new THREE.UV(1, 0),
                new THREE.UV(0, 0)
            ];

            this.geometry.faceUvs[0].push(new THREE.UV(0, 1));
            this.geometry.faceVertexUvs[0].push(faceuv);
        }
    }

    this.geometry.computeFaceNormals();
    this.geometry.computeVertexNormals();
}

TunnelSegment.prototype.getFace = function (i) {
    if (i <= this.geometry.faces.length && i >= 0) {
        return this.geometry.faces[i];
    } else {
        console.log('Error: TunnelSegment getFace(' + i + ') index out of bounds');
        return null;
    }
};<|endoftext|>


<commit_before>const { expect } = require('chai')
require('mocha')
const axios = require('axios')
const shell = require('shelljs')
const os = require('os')

// Cleared before each test block runs - use to configure
let customEnv = {}
let serverPid

const waitForServer = async done => {
  try {
    await axios.get('http://localhost:4000/developers')
    console.log('Server set up, running test...')
    done()
  } catch (err) {
    console.log('Server setting up, waiting...')
    setTimeout(() => waitForServer(done), 1000)
  }
}

const init = done => {
  console.log('Starting server...')
  const child = shell.exec('node server', {
    async: true,
    env: { ...shell.env, ...customEnv }
  })

  serverPid = child.pid

  waitForServer(done)
}

const cleanup = done => {
  customEnv = {}
  console.log(`Killing ${serverPid}`)
  const command = os.platform() === 'linux' ? 'pkill' : 'kill'
  shell.exec(`${command} -9 -P ${serverPid}`, { silent: true }, function(
    code,
    stdout,
    stderr
  ) {
    console.log('Killed server')
    shell.exec('lsof -t -i :4000')
    console.log({ code, stdout, stderr })
    done()
  })
}

describe('Node Server: Defaults', () => {
  beforeEach(init)

  afterEach(cleanup)

  it('should return a 200 when spun up.', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.status).to.equal(200)
    })
  })
})

describe('Disable X-XSS-Protection: Set', () => {
  before(function(done) {
    customEnv['DISABLE_X_XSS_PROTECTION'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not set an x-xss-protection header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-xss-protection']).to.equal(undefined)
    })
  })
})

describe('Disable X-Content-Type-Options: Set', () => {
  before(function(done) {
    customEnv['DISABLE_X_CONTENT_TYPE_OPTIONS'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not send a X-Content-Type-Options header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-content-type-options']).to.equal(undefined)
    })
  })
})

describe('Disable X-Frame-Options: Set', () => {
  before(function(done) {
    customEnv['DISABLE_X_FRAME_OPTIONS'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not send a X-Frame-Options header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-frame-options']).to.equal(undefined)
    })
  })
})

describe('Disable HSTS: Set', () => {
  before(function(done) {
    customEnv['DISABLE_HSTS'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not send a Strict-Transport-Security header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['strict-transport-security']).to.equal(undefined)
    })
  })
})

describe('Defaults', () => {
  before(function(done) {
    init(done)
  })

  afterEach(cleanup)

  it('should set security headers by default', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-xss-protection']).to.equal('1')
      expect(res.headers['x-content-type-options']).to.equal('nosniff')
      expect(res.headers['x-frame-options']).to.equal('deny')
      expect(res.headers['strict-transport-security']).to.equal(
        'max-age=86400; includeSubDomains'
      )
    })
  })
})

describe('/ping', () => {
  before(function(done) {
    init(done)
  })

  after(cleanup)

  it('should return a 200', () => {
    return axios.get('http://localhost:4000/ping').then(function(res) {
      expect(res.status).to.equal(200)
    })
  })
})

describe('/metrics', () => {
  const TOKEN = 'abc123'
  let i = 0

  beforeEach(function(done) {
    customEnv.PROMETHEUS_TOKEN = i === 3 ? '' : TOKEN
    init(done)
    i++
  })

  afterEach(cleanup)

  it('should return a 404 if the bearer token doesnt match', () => {
    return axios
      .get('http://localhost:4000/metrics', {
        headers: { Authorization: 'Bearer: invalid' },
      })
      .catch(function(err) {
        expect(err.response.status).to.equal(404)
      })
  })

  it('should return a 404 if the bearer token doesnt exist', () => {
    return axios
      .get('http://localhost:4000/metrics', { headers: {} })
      .catch(function(err) {
        expect(err.response.status).to.equal(404)
      })
  })

  it('should return a 200 if PROMETHEUS_TOKEN is valid', () => {
    return axios
      .get('http://localhost:4000/metrics', {
        headers: { Authorization: `Bearer: ${TOKEN}` },
      })
      .then(function(res) {
        expect(res.status).to.equal(200)
        expect(res.data.includes('process_cpu_user_seconds_total')).to.be.true
      })
  })

  it('should return a 404 if PROMETHEUS_TOKEN and bearer token are both empty strings', () => {
    return axios
      .get('http://localhost:4000/metrics', {
        headers: { Authorization: `Bearer: ` },
      })
      .catch(function(err) {
        expect(err.response.status).to.equal(404)
      })
  })
})
<commit_msg>FRONTEND-1660 Add server test for CSP<commit_after>const { expect } = require('chai')
require('mocha')
const axios = require('axios')
const shell = require('shelljs')
const os = require('os')

// Cleared before each test block runs - use to configure
let customEnv = {}
let serverPid

const waitForServer = async done => {
  try {
    await axios.get('http://localhost:4000/developers')
    console.log('Server set up, running test...')
    done()
  } catch (err) {
    console.log('Server setting up, waiting...')
    setTimeout(() => waitForServer(done), 1000)
  }
}

const init = done => {
  console.log('Starting server...')
  const child = shell.exec('node server', {
    async: true,
    env: { ...shell.env, ...customEnv }
  })

  serverPid = child.pid

  waitForServer(done)
}

const cleanup = done => {
  customEnv = {}
  console.log(`Killing ${serverPid}`)
  const command = os.platform() === 'linux' ? 'pkill' : 'kill'
  shell.exec(`${command} -9 -P ${serverPid}`, { silent: true }, function(
    code,
    stdout,
    stderr
  ) {
    console.log('Killed server')
    shell.exec('lsof -t -i :4000')
    console.log({ code, stdout, stderr })
    done()
  })
}

describe('Node Server: Defaults', () => {
  beforeEach(init)

  afterEach(cleanup)

  it('should return a 200 when spun up.', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.status).to.equal(200)
    })
  })
})

describe('Disable X-XSS-Protection: Set', () => {
  before(function(done) {
    customEnv['DISABLE_X_XSS_PROTECTION'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not set an x-xss-protection header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-xss-protection']).to.equal(undefined)
    })
  })
})

describe('Disable X-Content-Type-Options: Set', () => {
  before(function(done) {
    customEnv['DISABLE_X_CONTENT_TYPE_OPTIONS'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not send a X-Content-Type-Options header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-content-type-options']).to.equal(undefined)
    })
  })
})

describe('Disable X-Frame-Options: Set', () => {
  before(function(done) {
    customEnv['DISABLE_X_FRAME_OPTIONS'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not send a X-Frame-Options header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-frame-options']).to.equal(undefined)
    })
  })
})

describe('Disable HSTS: Set', () => {
  before(function(done) {
    customEnv['DISABLE_HSTS'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should not send a Strict-Transport-Security header if disabled', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['strict-transport-security']).to.equal(undefined)
    })
  })
})

describe('Enforce CSP: Set', () => {
  before(function(done) {
    customEnv['ENFORCE_CSP'] = 'true'
    init(done)
  })

  afterEach(cleanup)

  it('should set CSP header', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['content-security-policy']).to.not.be.undefined
    })
  })
})

describe('Defaults', () => {
  before(function(done) {
    init(done)
  })

  afterEach(cleanup)

  it('should set security headers by default', () => {
    return axios.get('http://localhost:4000/developers').then(function(res) {
      expect(res.headers['x-xss-protection']).to.equal('1')
      expect(res.headers['x-content-type-options']).to.equal('nosniff')
      expect(res.headers['x-frame-options']).to.equal('deny')
      expect(res.headers['strict-transport-security']).to.equal(
        'max-age=86400; includeSubDomains'
      )
      expect(res.headers['content-security-policy']).to.be.undefined
    })
  })
})

describe('/ping', () => {
  before(function(done) {
    init(done)
  })

  after(cleanup)

  it('should return a 200', () => {
    return axios.get('http://localhost:4000/ping').then(function(res) {
      expect(res.status).to.equal(200)
    })
  })
})

describe('/metrics', () => {
  const TOKEN = 'abc123'
  let i = 0

  beforeEach(function(done) {
    customEnv.PROMETHEUS_TOKEN = i === 3 ? '' : TOKEN
    init(done)
    i++
  })

  afterEach(cleanup)

  it('should return a 404 if the bearer token doesnt match', () => {
    return axios
      .get('http://localhost:4000/metrics', {
        headers: { Authorization: 'Bearer: invalid' },
      })
      .catch(function(err) {
        expect(err.response.status).to.equal(404)
      })
  })

  it('should return a 404 if the bearer token doesnt exist', () => {
    return axios
      .get('http://localhost:4000/metrics', { headers: {} })
      .catch(function(err) {
        expect(err.response.status).to.equal(404)
      })
  })

  it('should return a 200 if PROMETHEUS_TOKEN is valid', () => {
    return axios
      .get('http://localhost:4000/metrics', {
        headers: { Authorization: `Bearer: ${TOKEN}` },
      })
      .then(function(res) {
        expect(res.status).to.equal(200)
        expect(res.data.includes('process_cpu_user_seconds_total')).to.be.true
      })
  })

  it('should return a 404 if PROMETHEUS_TOKEN and bearer token are both empty strings', () => {
    return axios
      .get('http://localhost:4000/metrics', {
        headers: { Authorization: `Bearer: ` },
      })
      .catch(function(err) {
        expect(err.response.status).to.equal(404)
      })
  })
})
<|endoftext|>


<commit_before>

function OSMProvider () {

}

OSMProvider.prototype.getUrl = function ( x, y, z ) {

	return 'https://b.tile.openstreetmap.org/' + z + '/' + x + '/' + y + '.png';

}

OSMProvider.prototype.getAttribution = function () {

	var a = document.createElement( 'a' );

	a.textContent = '© OpenStreetMap contributors';
	a.href = 'http://www.openstreetmap.org/copyright';

	return a;

}<commit_msg>lint fixes<commit_after>

function OSMProvider () {

}

OSMProvider.prototype.getUrl = function ( x, y, z ) {

	return 'https://b.tile.openstreetmap.org/' + z + '/' + x + '/' + y + '.png';

};

OSMProvider.prototype.getAttribution = function () {

	var a = document.createElement( 'a' );

	a.textContent = '© OpenStreetMap contributors';
	a.href = 'http://www.openstreetmap.org/copyright';

	return a;

};<|endoftext|>


<commit_before>var _ = require('underscore');
var CDB = require('cartodb.js');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var template = require('./input-color-categories.tpl');
var StackLayoutView = require('../../../../../../components/stack-layout/stack-layout-view');
var InputColorPickerView = require('../input-color-picker/input-color-picker-view');
var CategoriesListView = require('./input-color-categories-list-view');
var rampList = require('cartocolor');

var MAX_VALUES = 10;
// max values + "others" color
var DEFAULT_COLORS = _.clone(rampList.Prism[MAX_VALUES + 1]);

var queryTemplate = _.template('SELECT <%= column %>, count(<%= column %>) FROM (<%= sql %>) _table_sql GROUP BY <%= column %> ORDER BY count DESC LIMIT <%= max_values %>');

function _quote (c) {
  if (c && c !== true) {
    return '"' + c.toString().replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
  }
  return c;
}

module.exports = CoreView.extend({
  events: {
    'click .js-back': '_onClickBack',
    'click .js-quantification': '_onClickQuantification'
  },

  initialize: function (opts) {
    if (!opts.columns) throw new Error('columns param is required');
    if (!opts.configModel) throw new Error('configModel param is required');
    if (!opts.query) throw new Error('query param is required');

    this._columns = opts.columns;
    this._configModel = opts.configModel;
    this._query = opts.query;

    this._initBinds();
    this._initViews();

    if (!this.model.get('domain') || !this.model.get('range')) {
      this._fetchColumns();
    }
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();

    var column = this._getColumn();
    var columnType = column && column.type;

    this.$el.append(template({
      columnType: columnType,
      attribute: this.model.get('attribute'),
      quantification: this.model.get('quantification') || 'category'
    }));

    if (this._stackLayoutView) {
      this.$('.js-content').append(this._stackLayoutView.render().$el);
    }

    return this;
  },

  _initBinds: function () {
    this.model.bind('change:attribute', this._fetchColumns, this);
  },

  _initViews: function () {
    var self = this;

    var stackViewCollection = new Backbone.Collection([{
      createStackView: function (stackLayoutModel, opts) {
        return self._createRangeListView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createColorPickerView(stackLayoutModel, opts).bind(self);
      }
    }]);

    this._stackLayoutView = new StackLayoutView({
      collection: stackViewCollection
    });
  },

  _getColumn: function () {
    return _.find(this._columns, function (column) {
      return column.label === this.model.get('attribute');
    }, this);
  },

  _fetchColumns: function () {
    if (!this.model.get('attribute')) {
      return;
    }

    if (this._query) {
      this._startLoader();

      var sql = new CDB.SQL({
        user: this._configModel.get('user_name'),
        sql_api_template: this._configModel.get('sql_api_template'),
        api_key: this._configModel.get('api_key')
      });

      sql.execute(
        queryTemplate({
          sql: this._query,
          column: this.model.get('attribute'),
          max_values: MAX_VALUES + 1
        }),
        null,
        {
          extra_params: ['page', 'rows_per_page'],
          page: 0,
          rows_per_page: 40,
          success: this._onQueryDone.bind(this),
          error: function () {
            // TODO: what happens if fails?
          }
        }
      );
    } else {
      this._onQueryDone();
    }
  },

  _onQueryDone: function (data) {
    data = data || {};
    this._stopLoader();
    this._updateRangeAndDomain(data.rows);
    this._initViews();
    this.render();
  },

  _updateRangeAndDomain: function (rows) {
    rows = rows || [];
    var categoryNames = _.pluck(rows, this.model.get('attribute'));

    var domain = _.map(categoryNames, function (name, i) {
      return name;
    }).slice(0, MAX_VALUES).sort();

    if (this.model.get('attribute_type') !== 'number') {
      domain = domain.filter(function (item, pos, self) {
        return self.indexOf(item) === pos;
      }).map(_quote);
    }

    var range = _.map(categoryNames, function (name, i) {
      return DEFAULT_COLORS[i];
    });

    this.model.set({
      range: range,
      domain: domain
    });
  },

  _startLoader: function () {
    this.$('.js-loader').removeClass('is-hidden');
    this.$('.js-content').addClass('is-hidden');
  },

  _stopLoader: function () {
    this.$('.js-loader').addClass('is-hidden');
    this.$('.js-content').removeClass('is-hidden');
  },

  _updateRange: function (categories) {
    var range = _.clone(this.model.get('range'));
    range[this._index] = categories[this._index].color;
    this.model.set('range', range);
  },

  _createColorPickerView: function (stackLayoutModel, opts) {
    var range = _.map(this.model.get('range'), function (color, i) {
      return { val: color, color: color, title: this.model.get('domain')[i] };
    }, this);

    var view = new InputColorPickerView({
      index: this._index,
      ramp: range
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('change', this._updateRange, this);
    view.bind('changeIndex', function (index) {
      this._index = index;
    }, this);

    return view;
  },

  _createRangeListView: function (stackLayoutModel, opts) {
    var view = new CategoriesListView({
      maxValues: MAX_VALUES,
      model: this.model
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('selectItem', function (index) {
      this._index = index;
      stackLayoutModel.nextStep();
    }, this);

    return view;
  },

  _onClickQuantification: function (e) {
    this.killEvent(e);
    this.trigger('selectQuantification', this);
  },

  _onClickBack: function (e) {
    this.killEvent(e);
    this.trigger('back', this);
  },

  remove: function () {
    CoreView.prototype.remove.apply(this);
  }
}, {
  MAX_VALUES: MAX_VALUES
});
<commit_msg>Remove unnecessary sort for domain categories<commit_after>var _ = require('underscore');
var CDB = require('cartodb.js');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var template = require('./input-color-categories.tpl');
var StackLayoutView = require('../../../../../../components/stack-layout/stack-layout-view');
var InputColorPickerView = require('../input-color-picker/input-color-picker-view');
var CategoriesListView = require('./input-color-categories-list-view');
var rampList = require('cartocolor');

var MAX_VALUES = 10;
// max values + "others" color
var DEFAULT_COLORS = _.clone(rampList.Prism[MAX_VALUES + 1]);

var queryTemplate = _.template('SELECT <%= column %>, count(<%= column %>) FROM (<%= sql %>) _table_sql GROUP BY <%= column %> ORDER BY count DESC LIMIT <%= max_values %>');

function _quote (c) {
  if (c && c !== true) {
    return '"' + c.toString().replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
  }
  return c;
}

module.exports = CoreView.extend({
  events: {
    'click .js-back': '_onClickBack',
    'click .js-quantification': '_onClickQuantification'
  },

  initialize: function (opts) {
    if (!opts.columns) throw new Error('columns param is required');
    if (!opts.configModel) throw new Error('configModel param is required');
    if (!opts.query) throw new Error('query param is required');

    this._columns = opts.columns;
    this._configModel = opts.configModel;
    this._query = opts.query;

    this._initBinds();
    this._initViews();

    if (!this.model.get('domain') || !this.model.get('range')) {
      this._fetchColumns();
    }
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();

    var column = this._getColumn();
    var columnType = column && column.type;

    this.$el.append(template({
      columnType: columnType,
      attribute: this.model.get('attribute'),
      quantification: this.model.get('quantification') || 'category'
    }));

    if (this._stackLayoutView) {
      this.$('.js-content').append(this._stackLayoutView.render().$el);
    }

    return this;
  },

  _initBinds: function () {
    this.model.bind('change:attribute', this._fetchColumns, this);
  },

  _initViews: function () {
    var self = this;

    var stackViewCollection = new Backbone.Collection([{
      createStackView: function (stackLayoutModel, opts) {
        return self._createRangeListView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createColorPickerView(stackLayoutModel, opts).bind(self);
      }
    }]);

    this._stackLayoutView = new StackLayoutView({
      collection: stackViewCollection
    });
  },

  _getColumn: function () {
    return _.find(this._columns, function (column) {
      return column.label === this.model.get('attribute');
    }, this);
  },

  _fetchColumns: function () {
    if (!this.model.get('attribute')) {
      return;
    }

    if (this._query) {
      this._startLoader();

      var sql = new CDB.SQL({
        user: this._configModel.get('user_name'),
        sql_api_template: this._configModel.get('sql_api_template'),
        api_key: this._configModel.get('api_key')
      });

      sql.execute(
        queryTemplate({
          sql: this._query,
          column: this.model.get('attribute'),
          max_values: MAX_VALUES + 1
        }),
        null,
        {
          success: this._onQueryDone.bind(this),
          error: function () {
            // TODO: what happens if fails?
          }
        }
      );
    } else {
      this._onQueryDone();
    }
  },

  _onQueryDone: function (data) {
    data = data || {};
    this._stopLoader();
    this._updateRangeAndDomain(data.rows);
    this._initViews();
    this.render();
  },

  _updateRangeAndDomain: function (rows) {
    rows = rows || [];
    var categoryNames = _.pluck(rows, this.model.get('attribute'));

    var domain = _.map(categoryNames, function (name, i) {
      return name;
    }).slice(0, MAX_VALUES);

    if (this.model.get('attribute_type') !== 'number') {
      domain = domain.filter(function (item, pos, self) {
        return self.indexOf(item) === pos;
      }).map(_quote);
    }

    var range = _.map(categoryNames, function (name, i) {
      return DEFAULT_COLORS[i];
    });

    this.model.set({
      range: range,
      domain: domain
    });
  },

  _startLoader: function () {
    this.$('.js-loader').removeClass('is-hidden');
    this.$('.js-content').addClass('is-hidden');
  },

  _stopLoader: function () {
    this.$('.js-loader').addClass('is-hidden');
    this.$('.js-content').removeClass('is-hidden');
  },

  _updateRange: function (categories) {
    var range = _.clone(this.model.get('range'));
    range[this._index] = categories[this._index].color;
    this.model.set('range', range);
  },

  _createColorPickerView: function (stackLayoutModel, opts) {
    var range = _.map(this.model.get('range'), function (color, i) {
      return { val: color, color: color, title: this.model.get('domain')[i] };
    }, this);

    var view = new InputColorPickerView({
      index: this._index,
      ramp: range
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('change', this._updateRange, this);
    view.bind('changeIndex', function (index) {
      this._index = index;
    }, this);

    return view;
  },

  _createRangeListView: function (stackLayoutModel, opts) {
    var view = new CategoriesListView({
      maxValues: MAX_VALUES,
      model: this.model
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('selectItem', function (index) {
      this._index = index;
      stackLayoutModel.nextStep();
    }, this);

    return view;
  },

  _onClickQuantification: function (e) {
    this.killEvent(e);
    this.trigger('selectQuantification', this);
  },

  _onClickBack: function (e) {
    this.killEvent(e);
    this.trigger('back', this);
  },

  remove: function () {
    CoreView.prototype.remove.apply(this);
  }
}, {
  MAX_VALUES: MAX_VALUES
});
<|endoftext|>


<commit_before>import {select as d3Select} from "d3-selection";
import {transition as d3Transition} from "d3-transition";
const d3 = {
  select: d3Select,
  transition: d3Transition
};
import {accessor, constant} from "d3plus-common";

/**
    @class image
    @desc Creates SVG images based on an array of data.
    @example
a sample row of data

var data = {"url": "file.png", "width": "100", "height": "50"};
@example
passed to the generator

new Image().data([data]).render();
@example
creates the following

<image class="d3plus-shape-image" opacity="1" href="file.png" width="100" height="50" x="0" y="0"></image>
@example
this is shorthand for the following

image().data([data])();
@example
which also allows a post-draw callback function

image().data([data])(function() { alert("draw complete!"); })
*/
export default class Icon {

  constructor() {
    this._duration = 600;
    this._height = accessor("height");
    this._id = accessor("url");
    this._select;
    this._url = accessor("url");
    this._width = accessor("width");
    this._x = accessor("x", 0);
    this._y = accessor("y", 0);
  }

  /**
      @memberof Image
      @desc Renders the current Image to the page. If a *callback* is specified, it will be called once the images are done drawing.
      @param {Function} [*callback* = undefined]
  */
  render(callback) {

    if (this._select === void 0) this.select(d3.select("body").append("svg").style("width", `${window.innerWidth}px`).style("height", `${window.innerHeight}px`).style("display", "block").node());

    const images = this._select.selectAll(".d3plus-shape-image").data(this._data, this._id);

    const enter = images.enter().append("image")
      .attr("class", "d3plus-shape-image")
      .attr("opacity", 0);

    const t = d3.transition().duration(this._duration),
          that = this,
          update = enter.merge(images);

    update
        .attr("xlink:href", this._url)
      .transition(t)
        .attr("opacity", 1)
        .attr("width", (d, i) => this._width(d, i))
        .attr("height", (d, i) => this._height(d, i))
        .attr("x", (d, i) => this._x(d, i))
        .attr("y", (d, i) => this._y(d, i))
        .each(function(d, i) {
          const image = d3.select(this), link = that._url(d, i);
          const fullAddress = link.indexOf("http://") === 0 || link.indexOf("https://") === 0;
          if (!fullAddress || link.indexOf(window.location.hostname) === 0) {
            const img = new Image();
            img.src = link;
            img.crossOrigin = "Anonymous";
            img.onload = function() {
              const canvas = document.createElement("canvas");
              canvas.width = this.width;
              canvas.height = this.height;
              const context = canvas.getContext("2d");
              context.drawImage(this, 0, 0);
              image.attr("xlink:href", canvas.toDataURL("image/png"));
            };
          }
        });

    images.exit().transition(t)
      .attr("width", (d, i) => this._width(d, i))
      .attr("height", (d, i) => this._height(d, i))
      .attr("x", (d, i) => this._x(d, i))
      .attr("y", (d, i) => this._y(d, i))
      .attr("opacity", 0).remove();

    if (callback) setTimeout(callback, this._duration + 100);

    return this;

  }

  /**
      @memberof Image
      @desc If *data* is specified, sets the data array to the specified array and returns the current class instance. If *data* is not specified, returns the current data array. An <image> tag will be drawn for each object in the array.
      @param {Array} [*data* = []]
  */
  data(_) {
    return arguments.length ? (this._data = _, this) : this._data;
  }

  /**
      @memberof Image
      @desc If *ms* is specified, sets the animation duration to the specified number and returns the current class instance. If *ms* is not specified, returns the current animation duration.
      @param {Number} [*ms* = 600]
  */
  duration(_) {
    return arguments.length ? (this._duration = _, this) : this._duration;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the height accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current height accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.height;
}
  */
  height(_) {
    return arguments.length ? (this._height = typeof _ === "function" ? _ : constant(_), this) : this._height;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the id accessor to the specified function and returns the current class instance. If *value* is not specified, returns the current id accessor. This is useful if you want to duplicate the same image.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  id(_) {
    return arguments.length ? (this._id = _, this) : this._id;
  }

  /**
      @memberof Image
      @desc If *selector* is specified, sets the SVG container element to the specified d3 selector or DOM element and returns the current class instance. If *selector* is not specified, returns the current SVG container element.
      @param {String|HTMLElement} [*selector* = d3.select("body").append("svg")]
  */
  select(_) {
    return arguments.length ? (this._select = d3.select(_), this) : this._select;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the URL accessor to the specified function and returns the current class instance. If *value* is not specified, returns the current URL accessor.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  url(_) {
    return arguments.length ? (this._url = _, this) : this._url;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the width accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current width accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.width;
}
  */
  width(_) {
    return arguments.length ? (this._width = typeof _ === "function" ? _ : constant(_), this) : this._width;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the x accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current x accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.x || 0;
}
  */
  x(_) {
    return arguments.length ? (this._x = typeof _ === "function" ? _ : constant(_), this) : this._x;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the y accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current y accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.y || 0;
}
  */
  y(_) {
    return arguments.length ? (this._y = typeof _ === "function" ? _ : constant(_), this) : this._y;
  }

}
<commit_msg>fixes Image doc name<commit_after>import {select as d3Select} from "d3-selection";
import {transition as d3Transition} from "d3-transition";
const d3 = {
  select: d3Select,
  transition: d3Transition
};
import {accessor, constant} from "d3plus-common";

/**
    @class Image
    @desc Creates SVG images based on an array of data.
    @example
a sample row of data

var data = {"url": "file.png", "width": "100", "height": "50"};
@example
passed to the generator

new Image().data([data]).render();
@example
creates the following

<image class="d3plus-shape-image" opacity="1" href="file.png" width="100" height="50" x="0" y="0"></image>
@example
this is shorthand for the following

image().data([data])();
@example
which also allows a post-draw callback function

image().data([data])(function() { alert("draw complete!"); })
*/
export default class Image {

  constructor() {
    this._duration = 600;
    this._height = accessor("height");
    this._id = accessor("url");
    this._select;
    this._url = accessor("url");
    this._width = accessor("width");
    this._x = accessor("x", 0);
    this._y = accessor("y", 0);
  }

  /**
      @memberof Image
      @desc Renders the current Image to the page. If a *callback* is specified, it will be called once the images are done drawing.
      @param {Function} [*callback* = undefined]
  */
  render(callback) {

    if (this._select === void 0) this.select(d3.select("body").append("svg").style("width", `${window.innerWidth}px`).style("height", `${window.innerHeight}px`).style("display", "block").node());

    const images = this._select.selectAll(".d3plus-shape-image").data(this._data, this._id);

    const enter = images.enter().append("image")
      .attr("class", "d3plus-shape-image")
      .attr("opacity", 0);

    const t = d3.transition().duration(this._duration),
          that = this,
          update = enter.merge(images);

    update
        .attr("xlink:href", this._url)
      .transition(t)
        .attr("opacity", 1)
        .attr("width", (d, i) => this._width(d, i))
        .attr("height", (d, i) => this._height(d, i))
        .attr("x", (d, i) => this._x(d, i))
        .attr("y", (d, i) => this._y(d, i))
        .each(function(d, i) {
          const image = d3.select(this), link = that._url(d, i);
          const fullAddress = link.indexOf("http://") === 0 || link.indexOf("https://") === 0;
          if (!fullAddress || link.indexOf(window.location.hostname) === 0) {
            const img = new Image();
            img.src = link;
            img.crossOrigin = "Anonymous";
            img.onload = function() {
              const canvas = document.createElement("canvas");
              canvas.width = this.width;
              canvas.height = this.height;
              const context = canvas.getContext("2d");
              context.drawImage(this, 0, 0);
              image.attr("xlink:href", canvas.toDataURL("image/png"));
            };
          }
        });

    images.exit().transition(t)
      .attr("width", (d, i) => this._width(d, i))
      .attr("height", (d, i) => this._height(d, i))
      .attr("x", (d, i) => this._x(d, i))
      .attr("y", (d, i) => this._y(d, i))
      .attr("opacity", 0).remove();

    if (callback) setTimeout(callback, this._duration + 100);

    return this;

  }

  /**
      @memberof Image
      @desc If *data* is specified, sets the data array to the specified array and returns the current class instance. If *data* is not specified, returns the current data array. An <image> tag will be drawn for each object in the array.
      @param {Array} [*data* = []]
  */
  data(_) {
    return arguments.length ? (this._data = _, this) : this._data;
  }

  /**
      @memberof Image
      @desc If *ms* is specified, sets the animation duration to the specified number and returns the current class instance. If *ms* is not specified, returns the current animation duration.
      @param {Number} [*ms* = 600]
  */
  duration(_) {
    return arguments.length ? (this._duration = _, this) : this._duration;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the height accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current height accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.height;
}
  */
  height(_) {
    return arguments.length ? (this._height = typeof _ === "function" ? _ : constant(_), this) : this._height;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the id accessor to the specified function and returns the current class instance. If *value* is not specified, returns the current id accessor. This is useful if you want to duplicate the same image.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  id(_) {
    return arguments.length ? (this._id = _, this) : this._id;
  }

  /**
      @memberof Image
      @desc If *selector* is specified, sets the SVG container element to the specified d3 selector or DOM element and returns the current class instance. If *selector* is not specified, returns the current SVG container element.
      @param {String|HTMLElement} [*selector* = d3.select("body").append("svg")]
  */
  select(_) {
    return arguments.length ? (this._select = d3.select(_), this) : this._select;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the URL accessor to the specified function and returns the current class instance. If *value* is not specified, returns the current URL accessor.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  url(_) {
    return arguments.length ? (this._url = _, this) : this._url;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the width accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current width accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.width;
}
  */
  width(_) {
    return arguments.length ? (this._width = typeof _ === "function" ? _ : constant(_), this) : this._width;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the x accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current x accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.x || 0;
}
  */
  x(_) {
    return arguments.length ? (this._x = typeof _ === "function" ? _ : constant(_), this) : this._x;
  }

  /**
      @memberof Image
      @desc If *value* is specified, sets the y accessor to the specified function or number and returns the current class instance. If *value* is not specified, returns the current y accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.y || 0;
}
  */
  y(_) {
    return arguments.length ? (this._y = typeof _ === "function" ? _ : constant(_), this) : this._y;
  }

}
<|endoftext|>


<commit_before>import {
  default as React,
  Component,
} from "react";

import {
  ToastContainer,
  ToastMessage,
} from "react-toastr";

import "./App.css";

const ToastMessageFactory = React.createFactory(ToastMessage.animation);

export default class App extends Component {

  addAlert = this.addAlert.bind(this);
  clearAlert = this.clearAlert.bind(this);

  addAlert() {
    this.refs.container.success(`hi! Now is ${new Date()}`, `///title\\\\\\`, {
      closeButton: true,
    });
  }

  clearAlert() {
    this.refs.container.clear();
  }

  render() {
    return (

        <ToastContainer
          toastMessageFactory={ToastMessageFactory}
          ref="container"
          className="toast-top-right"
        />

          React-Toastr
          React.js toastr component

          <button className="primary" onClick={this.addAlert}>
            Hello
          </button>
          <button className="primary" onClick={this.clearAlert}>
            CLEAR
          </button>

    );
  }
}
<commit_msg>docs(gh-pages): http to https<commit_after>import {
  default as React,
  Component,
} from "react";

import {
  ToastContainer,
  ToastMessage,
} from "react-toastr";

import "./App.css";

const ToastMessageFactory = React.createFactory(ToastMessage.animation);

export default class App extends Component {

  addAlert = this.addAlert.bind(this);
  clearAlert = this.clearAlert.bind(this);

  addAlert() {
    this.refs.container.success(`hi! Now is ${new Date()}`, `///title\\\\\\`, {
      closeButton: true,
    });
  }

  clearAlert() {
    this.refs.container.clear();
  }

  render() {
    return (

        <ToastContainer
          toastMessageFactory={ToastMessageFactory}
          ref="container"
          className="toast-top-right"
        />

          React-Toastr
          React.js toastr component

          <button className="primary" onClick={this.addAlert}>
            Hello
          </button>
          <button className="primary" onClick={this.clearAlert}>
            CLEAR
          </button>

    );
  }
}
<|endoftext|>