<commit_before>/*
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var _ = require('underscore');
var assert = require('assert');
var AuthzAPI = require('oae-authz');
var AuthzUtil = require('oae-authz/lib/util');

describe('Authz-Permissions', function() {

    /**
     * Make a single membership or role change object to apply to a group membership or resource role.
     *
     * @param  {String} principalId   The principalId whose role to change
     * @param  {String} role          The role to change to
     * @return {Object}               The change JSON Object to apply
     */
    var makeChange = function(principalId, role) {
        var change = {};
        change[principalId] = role;
        return change;
    };

    describe('#hasRole()', function() {

        it('verify invalid principal id error', function(callback) {
            AuthzAPI.hasRole('not a id', 'c:cam:Foo.docx', 'member', function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify non-principal member id error', function(callback) {
            AuthzAPI.hasRole('c:cam:mrvisser', 'c:cam:Foo.docx', 'member', function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify null role error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'c:cam:Foo.docx', null, function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify undefined role error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'c:cam:Foo.docx', undefined, function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify false role error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'c:cam:Foo.docx', false, function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify invalid resource id error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'not a id', 'member', function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify empty data', function(callback) {
            var userId = AuthzUtil.toId('u', 'hr-empty', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-empty', 'SomeContent');
            AuthzAPI.hasRole(userId, resourceId, 'member', function(err, hasRole) {
                assert.ok(!err);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'hr-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-direct', 'SomeContent');
            AuthzAPI.updateRoles(resourceId, makeChange(userId, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                    assert.ok(!err);
                    assert.ok(hasRole);
                    callback();
                });
            });
        });

        it('verify no direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'hr-no-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-no-direct', 'SomeContent');
            AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                assert.ok(!err);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'hr-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'hr-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-single', 'SomeContent');

            AuthzAPI.updateRoles(groupId, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(resourceId, makeChange(groupId, 'viewer'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                        assert.ok(!err);
                        assert.ok(hasRole);
                        callback();
                    });
                });
            });
        });

        it('verify no direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'hr-no-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'hr-no-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-no-single', 'SomeContent');

            AuthzAPI.updateRoles(groupId, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                    assert.ok(!err);
                    assert.ok(!hasRole);
                    callback();
                });
            });
        });

        it('verify direct multi group permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-multi', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-multi', 'oae-backend-team');
            var userId = AuthzUtil.toId('u', 'hr-multi', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-multi', 'SomeContent');

            AuthzAPI.updateRoles(groupId1, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(groupId2, makeChange(userId, 'member'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                        assert.ok(!err);
                        AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
                            assert.ok(!err);
                            AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                                assert.ok(!err);
                                assert.ok(hasRole);
                                AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                    assert.ok(!err);
                                    assert.ok(hasRole);
                                    callback();
                                });
                            });
                        });
                    });
                });
            });
        });

        it('verify no direct multi group permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-no-multi', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-no-multi', 'oae-backend-team');
            var userId = AuthzUtil.toId('u', 'hr-no-multi', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-no-multi', 'SomeContent');

            AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
               assert.ok(!err);
                    AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                        assert.ok(!err);
                        assert.ok(!hasRole);
                        AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                            assert.ok(!err);
                            assert.ok(!hasRole);
                            callback();
                        });
                    });
                });
            });
        });

        it('verify multi group hierarchy permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-hier', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-hier', 'oae-backend-team');
            var userId = AuthzUtil.toId('u', 'hr-hier', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-hier', 'SomeContent');

            AuthzAPI.updateRoles(groupId1, makeChange(groupId2, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(groupId2, makeChange(userId, 'member'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                        assert.ok(!err);
                        AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                            assert.ok(!err);
                            assert.ok(hasRole);
                            AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                assert.ok(!err);
                                assert.ok(!hasRole);
                                AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
                                    assert.ok(!err);
                                    AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                                        assert.ok(!err);
                                        assert.ok(hasRole);
                                        AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                            assert.ok(!err);
                                            assert.ok(hasRole);
                                            callback();
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
            });
        });

        it('verify circular group hierarchy permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-circ', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-circ', 'oae-backend-team');
            var groupId3 = AuthzUtil.toId('g', 'hr-circ', 'oae-ui-team');
            var userId = AuthzUtil.toId('u', 'hr-circ', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-circ', 'SomeContent');

            AuthzAPI.updateRoles(groupId1, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(groupId2, makeChange(groupId1, 'member'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.updateRoles(groupId3, makeChange(groupId2, 'member'), function(err) {
                        assert.ok(!err);
                        AuthzAPI.updateRoles(groupId1, makeChange(groupId3, 'member'), function(err) {
                            assert.ok(!err);
                            AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                                assert.ok(!err);
                                AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
                                    assert.ok(!err);
                                    AuthzAPI.updateRoles(resourceId, makeChange(groupId3, 'editor'), function(err) {
                                        assert.ok(!err);
                                        AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                                            assert.ok(!err);
                                            assert.ok(hasRole);
                                            AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                                assert.ok(!err);
                                                assert.ok(hasRole);
                                                AuthzAPI.hasRole(userId, resourceId, 'editor', function(err, hasRole) {
                                                    assert.ok(!err);
                                                    assert.ok(hasRole);
                                                    callback();
                                                });
                                            });
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
            });
        });
    });

    describe('#hasAnyRole()', function() {

        it('verify invalid principal id error', function(callback) {
            AuthzAPI.hasAnyRole('not a id', 'c:cam:Foo.docx', function(err, hasAnyRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify non-principal id error', function(callback) {
            AuthzAPI.hasAnyRole('c:cam:mrvisser', 'c:cam:Foo.docx', function(err, hasAnyRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify invalid resource id error', function(callback) {
            AuthzAPI.hasAnyRole('u:cam:mrvisser', 'not a id', function(err, hasAnyRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify empty data', function(callback) {
            var userId = AuthzUtil.toId('u', 'har-empty', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-empty', 'SomeContent');
            AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                assert.ok(!err);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'har-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-direct', 'SomeContent');
            AuthzAPI.updateRoles(resourceId, makeChange(userId, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                    assert.ok(!err);
                    assert.ok(hasAnyRole);
                    callback();
                });
            });
        });

        it('verify no direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'har-no-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-no-direct', 'SomeContent');
            AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                assert.ok(!err);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'har-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'har-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-single', 'SomeContent');

            AuthzAPI.updateRoles(groupId, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(resourceId, makeChange(groupId, 'viewer'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                        assert.ok(!err);
                        assert.ok(hasAnyRole);
                        callback();
                    });
                });
            });
        });

        it('verify no direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'har-no-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'har-no-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-no-single', 'SomeContent');

            AuthzAPI.updateRoles(resourceId, makeChange(groupId, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                    assert.ok(!err);
                    assert.ok(!hasAnyRole);
                    callback();
                });
            });
        });
    });

});
<commit_msg>Fixing a space.<commit_after>/*
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var _ = require('underscore');
var assert = require('assert');
var AuthzAPI = require('oae-authz');
var AuthzUtil = require('oae-authz/lib/util');

describe('Authz-Permissions', function() {

    /**
     * Make a single membership or role change object to apply to a group membership or resource role.
     *
     * @param  {String} principalId   The principalId whose role to change
     * @param  {String} role          The role to change to
     * @return {Object}               The change JSON Object to apply
     */
    var makeChange = function(principalId, role) {
        var change = {};
        change[principalId] = role;
        return change;
    };

    describe('#hasRole()', function() {

        it('verify invalid principal id error', function(callback) {
            AuthzAPI.hasRole('not a id', 'c:cam:Foo.docx', 'member', function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify non-principal member id error', function(callback) {
            AuthzAPI.hasRole('c:cam:mrvisser', 'c:cam:Foo.docx', 'member', function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify null role error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'c:cam:Foo.docx', null, function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify undefined role error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'c:cam:Foo.docx', undefined, function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify false role error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'c:cam:Foo.docx', false, function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify invalid resource id error', function(callback) {
            AuthzAPI.hasRole('u:cam:mrvisser', 'not a id', 'member', function(err, hasRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify empty data', function(callback) {
            var userId = AuthzUtil.toId('u', 'hr-empty', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-empty', 'SomeContent');
            AuthzAPI.hasRole(userId, resourceId, 'member', function(err, hasRole) {
                assert.ok(!err);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'hr-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-direct', 'SomeContent');
            AuthzAPI.updateRoles(resourceId, makeChange(userId, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                    assert.ok(!err);
                    assert.ok(hasRole);
                    callback();
                });
            });
        });

        it('verify no direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'hr-no-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-no-direct', 'SomeContent');
            AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                assert.ok(!err);
                assert.ok(!hasRole);
                callback();
            });
        });

        it('verify direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'hr-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'hr-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-single', 'SomeContent');

            AuthzAPI.updateRoles(groupId, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(resourceId, makeChange(groupId, 'viewer'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                        assert.ok(!err);
                        assert.ok(hasRole);
                        callback();
                    });
                });
            });
        });

        it('verify no direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'hr-no-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'hr-no-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-no-single', 'SomeContent');

            AuthzAPI.updateRoles(groupId, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                    assert.ok(!err);
                    assert.ok(!hasRole);
                    callback();
                });
            });
        });

        it('verify direct multi group permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-multi', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-multi', 'oae-backend-team');
            var userId = AuthzUtil.toId('u', 'hr-multi', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-multi', 'SomeContent');

            AuthzAPI.updateRoles(groupId1, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(groupId2, makeChange(userId, 'member'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                        assert.ok(!err);
                        AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
                            assert.ok(!err);
                            AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                                assert.ok(!err);
                                assert.ok(hasRole);
                                AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                    assert.ok(!err);
                                    assert.ok(hasRole);
                                    callback();
                                });
                            });
                        });
                    });
                });
            });
        });

        it('verify no direct multi group permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-no-multi', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-no-multi', 'oae-backend-team');
            var userId = AuthzUtil.toId('u', 'hr-no-multi', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-no-multi', 'SomeContent');

            AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
                assert.ok(!err);
                    AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                        assert.ok(!err);
                        assert.ok(!hasRole);
                        AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                            assert.ok(!err);
                            assert.ok(!hasRole);
                            callback();
                        });
                    });
                });
            });
        });

        it('verify multi group hierarchy permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-hier', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-hier', 'oae-backend-team');
            var userId = AuthzUtil.toId('u', 'hr-hier', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-hier', 'SomeContent');

            AuthzAPI.updateRoles(groupId1, makeChange(groupId2, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(groupId2, makeChange(userId, 'member'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                        assert.ok(!err);
                        AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                            assert.ok(!err);
                            assert.ok(hasRole);
                            AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                assert.ok(!err);
                                assert.ok(!hasRole);
                                AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
                                    assert.ok(!err);
                                    AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                                        assert.ok(!err);
                                        assert.ok(hasRole);
                                        AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                            assert.ok(!err);
                                            assert.ok(hasRole);
                                            callback();
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
            });
        });

        it('verify circular group hierarchy permission', function(callback) {
            var groupId1 = AuthzUtil.toId('g', 'hr-circ', 'oae-team');
            var groupId2 = AuthzUtil.toId('g', 'hr-circ', 'oae-backend-team');
            var groupId3 = AuthzUtil.toId('g', 'hr-circ', 'oae-ui-team');
            var userId = AuthzUtil.toId('u', 'hr-circ', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'hr-circ', 'SomeContent');

            AuthzAPI.updateRoles(groupId1, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(groupId2, makeChange(groupId1, 'member'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.updateRoles(groupId3, makeChange(groupId2, 'member'), function(err) {
                        assert.ok(!err);
                        AuthzAPI.updateRoles(groupId1, makeChange(groupId3, 'member'), function(err) {
                            assert.ok(!err);
                            AuthzAPI.updateRoles(resourceId, makeChange(groupId1, 'viewer'), function(err) {
                                assert.ok(!err);
                                AuthzAPI.updateRoles(resourceId, makeChange(groupId2, 'manager'), function(err) {
                                    assert.ok(!err);
                                    AuthzAPI.updateRoles(resourceId, makeChange(groupId3, 'editor'), function(err) {
                                        assert.ok(!err);
                                        AuthzAPI.hasRole(userId, resourceId, 'viewer', function(err, hasRole) {
                                            assert.ok(!err);
                                            assert.ok(hasRole);
                                            AuthzAPI.hasRole(userId, resourceId, 'manager', function(err, hasRole) {
                                                assert.ok(!err);
                                                assert.ok(hasRole);
                                                AuthzAPI.hasRole(userId, resourceId, 'editor', function(err, hasRole) {
                                                    assert.ok(!err);
                                                    assert.ok(hasRole);
                                                    callback();
                                                });
                                            });
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
            });
        });
    });

    describe('#hasAnyRole()', function() {

        it('verify invalid principal id error', function(callback) {
            AuthzAPI.hasAnyRole('not a id', 'c:cam:Foo.docx', function(err, hasAnyRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify non-principal id error', function(callback) {
            AuthzAPI.hasAnyRole('c:cam:mrvisser', 'c:cam:Foo.docx', function(err, hasAnyRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify invalid resource id error', function(callback) {
            AuthzAPI.hasAnyRole('u:cam:mrvisser', 'not a id', function(err, hasAnyRole) {
                assert.ok(err);
                assert.equal(err.code, 400);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify empty data', function(callback) {
            var userId = AuthzUtil.toId('u', 'har-empty', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-empty', 'SomeContent');
            AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                assert.ok(!err);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'har-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-direct', 'SomeContent');
            AuthzAPI.updateRoles(resourceId, makeChange(userId, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                    assert.ok(!err);
                    assert.ok(hasAnyRole);
                    callback();
                });
            });
        });

        it('verify no direct permission', function(callback) {
            var userId = AuthzUtil.toId('u', 'har-no-direct', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-no-direct', 'SomeContent');
            AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                assert.ok(!err);
                assert.ok(!hasAnyRole);
                callback();
            });
        });

        it('verify direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'har-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'har-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-single', 'SomeContent');

            AuthzAPI.updateRoles(groupId, makeChange(userId, 'member'), function(err) {
                assert.ok(!err);
                AuthzAPI.updateRoles(resourceId, makeChange(groupId, 'viewer'), function(err) {
                    assert.ok(!err);
                    AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                        assert.ok(!err);
                        assert.ok(hasAnyRole);
                        callback();
                    });
                });
            });
        });

        it('verify no direct single group permission', function(callback) {
            var groupId = AuthzUtil.toId('g', 'har-no-single', 'oae-team');
            var userId = AuthzUtil.toId('u', 'har-no-single', 'mrvisser');
            var resourceId = AuthzUtil.toId('c', 'har-no-single', 'SomeContent');

            AuthzAPI.updateRoles(resourceId, makeChange(groupId, 'viewer'), function(err) {
                assert.ok(!err);
                AuthzAPI.hasAnyRole(userId, resourceId, function(err, hasAnyRole) {
                    assert.ok(!err);
                    assert.ok(!hasAnyRole);
                    callback();
                });
            });
        });
    });

});
<|endoftext|>


<commit_before>$(function() {
    function FlashArduinoViewModel(parameters) {
        var self = this;

        self.settings = parameters[0];
        self.loginState = parameters[1];
        self.connection = parameters[2];

        self.hex_path = ko.observable();

        self._displayNotification = function(response, titleSuccess, textSuccess) {
            if (response.result) {
                new PNotify({
                    title: "Flashing firmware succeeded",
                    text: textSuccess,
                    type: "success",
                    hide: false
                })
            } else {
                new PNotify({
                    title: gettext("Something went wrong"),
                    text: gettext("Flashing the firmware failed, please see the log for details"),
                    type: "error",
                    hide: false
                });
            }
        };

     }
    // This is how our plugin registers itself with the application, by adding some configuration
    // information to the global variable ADDITIONAL_VIEWMODELS
    ADDITIONAL_VIEWMODELS.push([
        // This is the constructor to call for instantiating the plugin
        FlashArduinoViewModel,

        // This is a list of dependencies to inject into the plugin, the order which you request
        // here is the order in which the dependencies will be injected into your view model upon
        // instantiation via the parameters argument
        ["settingsViewModel", "loginStateViewModel", "connectionViewModel"],

        // Finally, this is the list of all elements we want this view model to be bound to.
        [document.getElementById("#settings_plugin_flasharduino")]
    ]);
});
<commit_msg>Changed dialog binding<commit_after>$(function() {
    function FlashArduinoViewModel(parameters) {
        var self = this;

        self.settings = parameters[0];
        self.loginState = parameters[1];
        self.connection = parameters[2];

        self.hex_path = ko.observable();

        self._displayNotification = function(response, titleSuccess, textSuccess) {
            if (response.result) {
                new PNotify({
                    title: "Flashing firmware succeeded",
                    text: textSuccess,
                    type: "success",
                    hide: false
                })
            } else {
                new PNotify({
                    title: gettext("Something went wrong"),
                    text: gettext("Flashing the firmware failed, please see the log for details"),
                    type: "error",
                    hide: false
                });
            }
        };

     }
    // This is how our plugin registers itself with the application, by adding some configuration
    // information to the global variable ADDITIONAL_VIEWMODELS
    ADDITIONAL_VIEWMODELS.push([
        // This is the constructor to call for instantiating the plugin
        FlashArduinoViewModel,

        // This is a list of dependencies to inject into the plugin, the order which you request
        // here is the order in which the dependencies will be injected into your view model upon
        // instantiation via the parameters argument
        ["settingsViewModel", "loginStateViewModel", "connectionViewModel"],

        // Finally, this is the list of all elements we want this view model to be bound to.
        "#settings_plugin_flasharduino"
    ]);
});
<|endoftext|>


<commit_before>var auction_doc_id = 'test';
beforeEach(module('auction'));

describe('Unit: Testing Filters - ', function() {
	it('formatnumber should format positive numbers', inject(function($filter) {
    expect($filter('formatnumber')(100000)).toEqual('100 000');
    expect($filter('formatnumber')(10000)).toEqual('10 000');
    expect($filter('formatnumber')(1000)).toEqual('1 000');
    expect($filter('formatnumber')(100)).toEqual('100');
    expect($filter('formatnumber')(10)).toEqual('10');
    expect($filter('formatnumber')(1)).toEqual('1');
  }));

	it('formatnumber should format nagative numbers', inject(function($filter) {
    expect($filter('formatnumber')(-100000)).toEqual('-100 000');
    expect($filter('formatnumber')(-10000)).toEqual('-10 000');
    expect($filter('formatnumber')(-1000)).toEqual('-1 000');
    expect($filter('formatnumber')(-100)).toEqual('-100');
    expect($filter('formatnumber')(-10)).toEqual('-10');
    expect($filter('formatnumber')(-1)).toEqual('-1');
  }));

	it('formatnumber should works with incorect values', inject(function($filter) {
    expect($filter('formatnumber')('string')).toEqual('');
    expect($filter('formatnumber')('')).toEqual($filter('number')('')); // '0'
    expect($filter('formatnumber')({})).toEqual('');
    expect($filter('formatnumber')(true)).toEqual('1');
    expect($filter('formatnumber')(false)).toEqual('0');
  }));
});<commit_msg>JS prettify<commit_after>var auction_doc_id = 'test';
beforeEach(module('auction'));

describe('Unit: Testing Filter "formatnumber" - ', function() {
  it('formatnumber should format positive numbers', inject(function($filter) {
    expect($filter('formatnumber')(100000)).toEqual('100 000');
    expect($filter('formatnumber')(10000)).toEqual('10 000');
    expect($filter('formatnumber')(1000)).toEqual('1 000');
    expect($filter('formatnumber')(100)).toEqual('100');
    expect($filter('formatnumber')(10)).toEqual('10');
    expect($filter('formatnumber')(1)).toEqual('1');
  }));

  it('formatnumber should format nagative numbers', inject(function($filter) {
    expect($filter('formatnumber')(-100000)).toEqual('-100 000');
    expect($filter('formatnumber')(-10000)).toEqual('-10 000');
    expect($filter('formatnumber')(-1000)).toEqual('-1 000');
    expect($filter('formatnumber')(-100)).toEqual('-100');
    expect($filter('formatnumber')(-10)).toEqual('-10');
    expect($filter('formatnumber')(-1)).toEqual('-1');
  }));

  it('formatnumber should works with incorect values', inject(function($filter) {
    expect($filter('formatnumber')('string')).toEqual('');
    expect($filter('formatnumber')('')).toEqual($filter('number')('')); // '0'
    expect($filter('formatnumber')({})).toEqual('');
    expect($filter('formatnumber')(true)).toEqual('1');
    expect($filter('formatnumber')(false)).toEqual('0');
  }));
});<|endoftext|>


<commit_before>import React, { Component } from 'react';
import styled from 'react-emotion';
import { Link } from 'react-router-dom';

import { ShieldIcon, InfoIcon, TrashcanIcon, ArrowRightIcon } from '@voussoir/icons';
import { colors } from '@voussoir/ui/src/theme';
import { Button } from '@voussoir/ui/src/primitives/buttons';
import { CheckboxPrimitive } from '@voussoir/ui/src/primitives/forms';
import { A11yText } from '@voussoir/ui/src/primitives/typography';
import DeleteItemModal from './DeleteItemModal';

// This import is loaded by the @voussoir/field-views-loader loader.
// It imports all the views required for a keystone app by looking at the adminMetaData
import FieldTypes from '../FIELD_TYPES';

// Styled Components
const Table = styled('table')({
  borderCollapse: 'collapse',
  borderSpacing: 0,
  tableLayout: 'fixed',
  width: '100%',
});
const HeaderCell = styled('td')({
  borderBottom: `2px solid ${colors.N10}`,
  color: '#999',
  display: 'table-cell',
  fontWeight: 'normal',
  paddingBottom: '8px',
  textAlign: 'left',
  verticalAlign: 'bottom',
});
const BodyCell = styled('td')(({ isSelected }) => ({
  backgroundColor: isSelected ? colors.B.L90 : null,
  boxShadow: isSelected
    ? `0 1px 0 ${colors.B.L75}, 0 -1px 0 ${colors.B.L75}`
    : `0 -1px 0 ${colors.N10}`,
  padding: '8px 0',
  position: 'relative',
  fontSize: 15,
}));
const ItemLink = styled(Link)`
  color: ${colors.text};

  /* Increase hittable area on item link */
  &::before {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
  }
`;

const NoResults = ({ children, ...props }) => (

    <InfoIcon css={{ height: 48, width: 48, marginBottom: '0.5em' }} />
    {children}

);

const SortDirectionArrow = styled.span(({ size = '0.2em', rotate = '0deg' }) => ({
  borderLeft: `${size} solid transparent`,
  borderRight: `${size} solid transparent`,
  borderTop: `${size} solid`,
  display: 'inline-block',
  height: 0,
  marginLeft: '0.33em',
  marginTop: '-0.125em',
  verticalAlign: 'middle',
  width: 0,
  transform: `rotate(${rotate})`,
}));

// Functional Components

type SortLinkProps = {
  handleSortChange: Function,
  active: boolean,
  sortAscending: boolean,
};

class SortLink extends React.Component<SortLinkProps> {
  onClick = () => {
    const { field, sortAscending, sortable } = this.props;
    if (sortable) {
      // Set direction to the opposite of the current sortAscending value
      this.props.handleSortChange({ field, direction: sortAscending ? 'DESC' : 'ASC' });
    }
  };

  render() {
    const styles = {
      borderBottom: `2px solid ${colors.N10}`,
      display: 'table-cell',
      fontWeight: 'normal',
      paddingBottom: '8px',
      textAlign: 'left',
      verticalAlign: 'bottom',
      color: this.props.active ? '#000' : '#999',
    };

    // TODO: Do we want to make `sortable` a field config option?
    return (

        {this.props.field.label}
        {this.props.sortable && (
          <SortDirectionArrow
            rotate={this.props.active && !this.props.sortAscending ? '180deg' : '0deg'}
          />
        )}

    );
  }
}

class ListDisplayRow extends Component {
  static defaultProps = {
    itemErrors: {},
  };

  state = {
    showDeleteModal: false,
  };
  componentDidMount() {
    this.mounted = true;
  }
  componentWillUnmount() {
    this.mounted = false;
  }
  showDeleteModal = () => {
    this.setState({ showDeleteModal: true });
  };
  closeDeleteModal = () => {
    this.setState({ showDeleteModal: false });
  };
  onDelete = result => {
    if (this.props.onDelete) this.props.onDelete(result);
    if (!this.mounted) return;
    this.setState({ showDeleteModal: false });
  };
  renderDeleteModal() {
    const { showDeleteModal } = this.state;
    const { item, list } = this.props;

    return (
      <DeleteItemModal
        isOpen={showDeleteModal}
        item={item}
        list={list}
        onClose={this.closeDeleteModal}
        onDelete={this.onDelete}
      />
    );
  }
  render() {
    const { list, link, item, itemErrors, fields } = this.props;

    return (

        <BodyCell>
          <Button
            appearance="warning"
            onClick={this.showDeleteModal}
            spacing="cozy"
            variant="subtle"
            style={{ height: 24 }}
          >
            <TrashcanIcon />
            <A11yText>Remove</A11yText>
          </Button>
          {this.renderDeleteModal()}
        </BodyCell>
        <BodyCell>
          <Button
            appearance="primary"
            to={link({ path: list.path, id: item.id })}
            spacing="cozy"
            variant="subtle"
            style={{ height: 24 }}
          >
            <ArrowRightIcon />
            <A11yText>Open</A11yText>
          </Button>
        </BodyCell>
        {fields.map(field => {
          const { path } = field;

          const isLoading = !item.hasOwnProperty(path);
          if (isLoading) {
            // TODO: Better loading state?
            return <BodyCell key={path} />;
          }

          if (itemErrors[path] instanceof Error && itemErrors[path].name === 'AccessDeniedError') {
            return (
              <BodyCell key={path}>
                <ShieldIcon title={itemErrors[path].message} css={{ color: colors.N10 }} />
                <A11yText>{itemErrors[path].message}</A11yText>
              </BodyCell>
            );
          }

          if (path === '_label_') {
            return (
              <BodyCell key={path}>
                <ItemLink to={link({ path: list.path, id: item.id })}>{item._label_}</ItemLink>
              </BodyCell>
            );
          }

          let content;

          const Cell = FieldTypes[list.key][path].Cell;

          if (Cell) {
            // fix this later, creating a react component on every render is really bad
            // react will rerender into the DOM on every react render
            // probably not a huge deal on a leaf component like this but still bad
            const LinkComponent = ({ children, ...data }) => (
              <ItemLink to={link(data)}>{children}</ItemLink>
            );
            content = <Cell list={list} data={item[path]} field={field} Link={LinkComponent} />;
          } else {
            content = item[path];
          }

          return <BodyCell key={path}>{content}</BodyCell>;
        })}

    );
  }
}

function isKeyboardEvent(e) {
  return e.clientX === 0 && e.clientY === 0;
}

class ListManageRow extends Component {
  onMouseDown = e => {
    // bail when MouseClick on the actual input, which calls onClick twice
    if (e.target.nodeName === 'INPUT' && !isKeyboardEvent(e)) return;

    // trigger onClick with the current ID
    const { isSelected, item, onSelect, onSelectStart } = this.props;
    onSelectStart(!isSelected);
    onSelect([item.id]);
  };
  onMouseUp = () => {
    // make keyboard selection easier following a mouse select
    this.checkbox.focus();
  };
  onMouseEnter = () => {
    const { isSelected, item, selectOnEnter, onSelect } = this.props;
    if (
      (selectOnEnter === 'select' && !isSelected) ||
      (selectOnEnter === 'deselect' && isSelected)
    ) {
      onSelect([item.id]);
    }
  };
  onCheckboxChange = () => {
    const { item, onSelect } = this.props;
    onSelect([item.id]);
  };
  onCheckboxMouseDown = e => {
    e.stopPropagation();
  };
  getCheckbox = ref => {
    this.checkbox = ref;
  };
  render() {
    const { fields, isSelected, item } = this.props;

    return (

        <BodyCell isSelected={isSelected} key="checkbox">
          <CheckboxPrimitive
            checked={isSelected}
            innerRef={this.getCheckbox}
            value={item.id}
            onChange={this.onCheckboxChange}
            onMouseDown={this.onCheckboxMouseDown}
            css={{ marginLeft: 1 }}
            tabIndex="0"
          />
        </BodyCell>
        {fields.map(({ path }) => (
          <BodyCell isSelected={isSelected} key={path}>
            {item[path]}
          </BodyCell>
        ))}

    );
  }
}

export default class ListTable extends Component {
  state = {
    mouseOverSelectsRow: false,
  };

  handleSelectAll = () => {
    const { items, onSelectAll, selectedItems } = this.props;
    const allSelected = items.length === selectedItems.length;
    const value = allSelected ? [] : items.map(i => i.id);
    onSelectAll(value);
  };

  onSelectStart = select => {
    const { isManaging } = this.props;
    if (!isManaging) return;
    this.setState({
      mouseOverSelectsRow: select ? 'select' : 'deselect',
    });
  };
  stopRowSelectOnEnter = () => {
    const { mouseOverSelectsRow } = this.state;
    if (mouseOverSelectsRow) {
      this.setState({ mouseOverSelectsRow: false });
    }
  };
  render() {
    const {
      adminPath,
      fields,
      isManaging,
      items,
      itemsErrors = [],
      list,
      noResultsMessage,
      onChange,
      onSelect,
      handleSortChange,
      sortBy,
      selectedItems,
    } = this.props;
    const { mouseOverSelectsRow } = this.state;

    return items.length ? (

        <colgroup>
          <col width="32" />
          <col width="32" />
        </colgroup>

            <HeaderCell>

                <CheckboxPrimitive
                  checked={items.length === selectedItems.length}
                  onChange={this.handleSelectAll}
                  tabIndex="0"
                />

            </HeaderCell>
            <HeaderCell />
            {fields.map(field => (
              <SortLink
                data-field={field.path}
                key={field.path}
                sortable={field.path !== '_label_'}
                field={field}
                handleSortChange={handleSortChange}
                active={sortBy.field.path === field.path}
                sortAscending={sortBy.direction === 'ASC'}
              />
            ))}

          {items.map(
            (item, itemIndex) =>
              isManaging ? (
                <ListManageRow
                  fields={fields}
                  item={item}
                  key={item.id}
                  list={list}
                  isSelected={selectedItems.includes(item.id)}
                  selectOnEnter={mouseOverSelectsRow}
                  onSelect={onSelect}
                  onSelectStart={this.onSelectStart}
                />
              ) : (
                <ListDisplayRow
                  fields={fields}
                  item={item}
                  itemErrors={itemsErrors[itemIndex] || {}}
                  key={item.id}
                  link={({ path, id }) => `${adminPath}/${path}/${id}`}
                  list={list}
                  onDelete={onChange}
                />
              )
          )}

    ) : (
      <NoResults>{noResultsMessage}</NoResults>
    );
  }
}
<commit_msg>Fix links in list view for one to many relationships (#413)<commit_after>import React, { Component } from 'react';
import styled from 'react-emotion';
import { Link } from 'react-router-dom';

import { ShieldIcon, InfoIcon, TrashcanIcon, ArrowRightIcon } from '@voussoir/icons';
import { colors } from '@voussoir/ui/src/theme';
import { Button } from '@voussoir/ui/src/primitives/buttons';
import { CheckboxPrimitive } from '@voussoir/ui/src/primitives/forms';
import { A11yText } from '@voussoir/ui/src/primitives/typography';
import DeleteItemModal from './DeleteItemModal';

// This import is loaded by the @voussoir/field-views-loader loader.
// It imports all the views required for a keystone app by looking at the adminMetaData
import FieldTypes from '../FIELD_TYPES';

// Styled Components
const Table = styled('table')({
  borderCollapse: 'collapse',
  borderSpacing: 0,
  tableLayout: 'fixed',
  width: '100%',
});
const HeaderCell = styled('td')({
  borderBottom: `2px solid ${colors.N10}`,
  color: '#999',
  display: 'table-cell',
  fontWeight: 'normal',
  paddingBottom: '8px',
  textAlign: 'left',
  verticalAlign: 'bottom',
});
const BodyCell = styled('td')(({ isSelected }) => ({
  backgroundColor: isSelected ? colors.B.L90 : null,
  boxShadow: isSelected
    ? `0 1px 0 ${colors.B.L75}, 0 -1px 0 ${colors.B.L75}`
    : `0 -1px 0 ${colors.N10}`,
  padding: '8px 0',
  position: 'relative',
  fontSize: 15,
}));
const ItemLink = styled(Link)`
  color: ${colors.text};

  /* Increase hittable area on item link */
  &:only-of-type::before {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
  }
`;

const NoResults = ({ children, ...props }) => (

    <InfoIcon css={{ height: 48, width: 48, marginBottom: '0.5em' }} />
    {children}

);

const SortDirectionArrow = styled.span(({ size = '0.2em', rotate = '0deg' }) => ({
  borderLeft: `${size} solid transparent`,
  borderRight: `${size} solid transparent`,
  borderTop: `${size} solid`,
  display: 'inline-block',
  height: 0,
  marginLeft: '0.33em',
  marginTop: '-0.125em',
  verticalAlign: 'middle',
  width: 0,
  transform: `rotate(${rotate})`,
}));

// Functional Components

type SortLinkProps = {
  handleSortChange: Function,
  active: boolean,
  sortAscending: boolean,
};

class SortLink extends React.Component<SortLinkProps> {
  onClick = () => {
    const { field, sortAscending, sortable } = this.props;
    if (sortable) {
      // Set direction to the opposite of the current sortAscending value
      this.props.handleSortChange({ field, direction: sortAscending ? 'DESC' : 'ASC' });
    }
  };

  render() {
    const styles = {
      borderBottom: `2px solid ${colors.N10}`,
      display: 'table-cell',
      fontWeight: 'normal',
      paddingBottom: '8px',
      textAlign: 'left',
      verticalAlign: 'bottom',
      color: this.props.active ? '#000' : '#999',
    };

    // TODO: Do we want to make `sortable` a field config option?
    return (

        {this.props.field.label}
        {this.props.sortable && (
          <SortDirectionArrow
            rotate={this.props.active && !this.props.sortAscending ? '180deg' : '0deg'}
          />
        )}

    );
  }
}

class ListDisplayRow extends Component {
  static defaultProps = {
    itemErrors: {},
  };

  state = {
    showDeleteModal: false,
  };
  componentDidMount() {
    this.mounted = true;
  }
  componentWillUnmount() {
    this.mounted = false;
  }
  showDeleteModal = () => {
    this.setState({ showDeleteModal: true });
  };
  closeDeleteModal = () => {
    this.setState({ showDeleteModal: false });
  };
  onDelete = result => {
    if (this.props.onDelete) this.props.onDelete(result);
    if (!this.mounted) return;
    this.setState({ showDeleteModal: false });
  };
  renderDeleteModal() {
    const { showDeleteModal } = this.state;
    const { item, list } = this.props;

    return (
      <DeleteItemModal
        isOpen={showDeleteModal}
        item={item}
        list={list}
        onClose={this.closeDeleteModal}
        onDelete={this.onDelete}
      />
    );
  }
  render() {
    const { list, link, item, itemErrors, fields } = this.props;

    return (

        <BodyCell>
          <Button
            appearance="warning"
            onClick={this.showDeleteModal}
            spacing="cozy"
            variant="subtle"
            style={{ height: 24 }}
          >
            <TrashcanIcon />
            <A11yText>Remove</A11yText>
          </Button>
          {this.renderDeleteModal()}
        </BodyCell>
        <BodyCell>
          <Button
            appearance="primary"
            to={link({ path: list.path, id: item.id })}
            spacing="cozy"
            variant="subtle"
            style={{ height: 24 }}
          >
            <ArrowRightIcon />
            <A11yText>Open</A11yText>
          </Button>
        </BodyCell>
        {fields.map(field => {
          const { path } = field;

          const isLoading = !item.hasOwnProperty(path);
          if (isLoading) {
            // TODO: Better loading state?
            return <BodyCell key={path} />;
          }

          if (itemErrors[path] instanceof Error && itemErrors[path].name === 'AccessDeniedError') {
            return (
              <BodyCell key={path}>
                <ShieldIcon title={itemErrors[path].message} css={{ color: colors.N10 }} />
                <A11yText>{itemErrors[path].message}</A11yText>
              </BodyCell>
            );
          }

          if (path === '_label_') {
            return (
              <BodyCell key={path}>
                <ItemLink to={link({ path: list.path, id: item.id })}>{item._label_}</ItemLink>
              </BodyCell>
            );
          }

          let content;

          const Cell = FieldTypes[list.key][path].Cell;

          if (Cell) {
            // fix this later, creating a react component on every render is really bad
            // react will rerender into the DOM on every react render
            // probably not a huge deal on a leaf component like this but still bad
            const LinkComponent = ({ children, ...data }) => (
              <ItemLink to={link(data)}>{children}</ItemLink>
            );
            content = <Cell list={list} data={item[path]} field={field} Link={LinkComponent} />;
          } else {
            content = item[path];
          }

          return <BodyCell key={path}>{content}</BodyCell>;
        })}

    );
  }
}

function isKeyboardEvent(e) {
  return e.clientX === 0 && e.clientY === 0;
}

class ListManageRow extends Component {
  onMouseDown = e => {
    // bail when MouseClick on the actual input, which calls onClick twice
    if (e.target.nodeName === 'INPUT' && !isKeyboardEvent(e)) return;

    // trigger onClick with the current ID
    const { isSelected, item, onSelect, onSelectStart } = this.props;
    onSelectStart(!isSelected);
    onSelect([item.id]);
  };
  onMouseUp = () => {
    // make keyboard selection easier following a mouse select
    this.checkbox.focus();
  };
  onMouseEnter = () => {
    const { isSelected, item, selectOnEnter, onSelect } = this.props;
    if (
      (selectOnEnter === 'select' && !isSelected) ||
      (selectOnEnter === 'deselect' && isSelected)
    ) {
      onSelect([item.id]);
    }
  };
  onCheckboxChange = () => {
    const { item, onSelect } = this.props;
    onSelect([item.id]);
  };
  onCheckboxMouseDown = e => {
    e.stopPropagation();
  };
  getCheckbox = ref => {
    this.checkbox = ref;
  };
  render() {
    const { fields, isSelected, item } = this.props;

    return (

        <BodyCell isSelected={isSelected} key="checkbox">
          <CheckboxPrimitive
            checked={isSelected}
            innerRef={this.getCheckbox}
            value={item.id}
            onChange={this.onCheckboxChange}
            onMouseDown={this.onCheckboxMouseDown}
            css={{ marginLeft: 1 }}
            tabIndex="0"
          />
        </BodyCell>
        {fields.map(({ path }) => (
          <BodyCell isSelected={isSelected} key={path}>
            {item[path]}
          </BodyCell>
        ))}

    );
  }
}

export default class ListTable extends Component {
  state = {
    mouseOverSelectsRow: false,
  };

  handleSelectAll = () => {
    const { items, onSelectAll, selectedItems } = this.props;
    const allSelected = items.length === selectedItems.length;
    const value = allSelected ? [] : items.map(i => i.id);
    onSelectAll(value);
  };

  onSelectStart = select => {
    const { isManaging } = this.props;
    if (!isManaging) return;
    this.setState({
      mouseOverSelectsRow: select ? 'select' : 'deselect',
    });
  };
  stopRowSelectOnEnter = () => {
    const { mouseOverSelectsRow } = this.state;
    if (mouseOverSelectsRow) {
      this.setState({ mouseOverSelectsRow: false });
    }
  };
  render() {
    const {
      adminPath,
      fields,
      isManaging,
      items,
      itemsErrors = [],
      list,
      noResultsMessage,
      onChange,
      onSelect,
      handleSortChange,
      sortBy,
      selectedItems,
    } = this.props;
    const { mouseOverSelectsRow } = this.state;

    return items.length ? (

        <colgroup>
          <col width="32" />
          <col width="32" />
        </colgroup>

            <HeaderCell>

                <CheckboxPrimitive
                  checked={items.length === selectedItems.length}
                  onChange={this.handleSelectAll}
                  tabIndex="0"
                />

            </HeaderCell>
            <HeaderCell />
            {fields.map(field => (
              <SortLink
                data-field={field.path}
                key={field.path}
                sortable={field.path !== '_label_'}
                field={field}
                handleSortChange={handleSortChange}
                active={sortBy.field.path === field.path}
                sortAscending={sortBy.direction === 'ASC'}
              />
            ))}

          {items.map(
            (item, itemIndex) =>
              isManaging ? (
                <ListManageRow
                  fields={fields}
                  item={item}
                  key={item.id}
                  list={list}
                  isSelected={selectedItems.includes(item.id)}
                  selectOnEnter={mouseOverSelectsRow}
                  onSelect={onSelect}
                  onSelectStart={this.onSelectStart}
                />
              ) : (
                <ListDisplayRow
                  fields={fields}
                  item={item}
                  itemErrors={itemsErrors[itemIndex] || {}}
                  key={item.id}
                  link={({ path, id }) => `${adminPath}/${path}/${id}`}
                  list={list}
                  onDelete={onChange}
                />
              )
          )}

    ) : (
      <NoResults>{noResultsMessage}</NoResults>
    );
  }
}
<|endoftext|>


<commit_before>/**
@module ember
@submodule ember-application
*/

var get = Ember.get, set = Ember.set;
var ControllersProxy = Ember.Object.extend({
  controller: null,

  unknownProperty: function(controllerName) {
    var controller = get(this, 'controller'),
      needs = get(controller, 'needs'),
      container = controller.get('container'),
      dependency;

    for (var i=0, l=needs.length; i<l; i++) {
      dependency = needs[i];
      if (dependency === controllerName) {
        return container.lookup('controller:' + controllerName);
      }
    }
  }
});

function verifyDependencies(controller) {
  var needs = get(controller, 'needs'),
      container = get(controller, 'container'),
      dependency, satisfied = true;

  for (var i=0, l=needs.length; i<l; i++) {
    dependency = needs[i];
    if (dependency.indexOf(':') === -1) {
      dependency = "controller:" + dependency;
    }

    if (!container.has(dependency)) {
      satisfied = false;
      Ember.assert(controller + " needs " + dependency + " but it does not exist", false);
    }
  }

  return satisfied;
}

Ember.ControllerMixin.reopen({
  concatenatedProperties: ['needs'],

  /**
    An array of other controller objects available inside
    instances of this controller via the `controllers`
    property:

    For example, when you define a controller:

    ```javascript
    App.CommentsController = Ember.ArrayController.extend({
      needs: ['post']
    });
    ```

    The application's single instance of these other
    controllers are accessible by name through the
    `controllers` property:

    ```javascript
    this.get('controllers.post'); // instance of App.PostController
    ```

    This is only available for singleton controllers.

    @property {Array} needs
    @default []
  */
  needs: [],

  init: function() {
    this._super.apply(this, arguments);

    // Structure asserts to still do verification but not string concat in production
    if (!verifyDependencies(this)) {
      Ember.assert("Missing dependencies", false);
    }
  },

  controllerFor: function(controllerName) {
    Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead");
    return Ember.controllerFor(get(this, 'container'), controllerName);
  },

  controllers: Ember.computed(function() {
    return ControllersProxy.create({ controller: this });
  })
});
<commit_msg>if you have needs just create the controllers proxy<commit_after>/**
@module ember
@submodule ember-application
*/

var get = Ember.get, set = Ember.set;

function verifyDependencies(controller) {
  var needs = get(controller, 'needs'),
      container = get(controller, 'container'),
      satisfied = true,
      dependency, i, l;

  for (i=0, l=needs.length; i<l; i++) {
    dependency = needs[i];
    if (dependency.indexOf(':') === -1) {
      dependency = "controller:" + dependency;
    }

    if (!container.has(dependency)) {
      satisfied = false;
      Ember.assert(controller + " needs " + dependency + " but it does not exist", false);
    }
  }

  if (l > 0) {
    set(controller, 'controllers', {
      unknownProperty: function(controllerName) {
        var dependency, i, l;
        for (i=0, l=needs.length; i<l; i++) {
          dependency = needs[i];
          if (dependency === controllerName) {
            return container.lookup('controller:' + controllerName);
          }
        }
      }
    });
  }
  return satisfied;
}

Ember.ControllerMixin.reopen({
  concatenatedProperties: ['needs'],

  /**
    An array of other controller objects available inside
    instances of this controller via the `controllers`
    property:

    For example, when you define a controller:

    ```javascript
    App.CommentsController = Ember.ArrayController.extend({
      needs: ['post']
    });
    ```

    The application's single instance of these other
    controllers are accessible by name through the
    `controllers` property:

    ```javascript
    this.get('controllers.post'); // instance of App.PostController
    ```

    This is only available for singleton controllers.

    @property {Array} needs
    @default []
  */
  needs: [],

  init: function() {
    this._super.apply(this, arguments);

    // Structure asserts to still do verification but not string concat in production
    if (!verifyDependencies(this)) {
      Ember.assert("Missing dependencies", false);
    }
  },

  controllerFor: function(controllerName) {
    Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead");
    return Ember.controllerFor(get(this, 'container'), controllerName);
  },

  controllers: null
});
<|endoftext|>


<commit_before>module.exports = {
	env: {
		es6: true,
	},

	parserOptions: {
		ecmaVersion: 6,
		sourceType: 'module',
	},

	plugins: [
		'import',
	],

	settings: {
		'import/resolver': {
			node: {
				extensions: ['.js', '.json', '.jsx'],
			},
		},
		'import/extensions': [
			'.js',
			'.jsx',
		],
		'import/core-modules': [
		],
		'import/ignore': [
			'node_modules',
			'\\.(coffee|scss|css|less|hbs|svg|json)$',
		],
	},

	rules: {
		// Static analysis:

		// ensure imports point to files/modules that can be resolved
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-unresolved.md
		'import/no-unresolved': 'off',

		// ensure named imports coupled with named exports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/named.md#when-not-to-use-it
		'import/named': 'off',

		// ensure default import coupled with default export
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/default.md#when-not-to-use-it
		'import/default': 'off',

		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/namespace.md
		'import/namespace': 'off',

		// Helpful warnings:

		// disallow invalid exports, e.g. multiple defaults
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/export.md
		'import/export': 'error',

		// do not allow a default import name to match a named export
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default.md
		'import/no-named-as-default': 'error',

		// warn on accessing default export property names that are also named exports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default-member.md
		'import/no-named-as-default-member': 'error',

		// disallow use of jsdoc-marked-deprecated imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-deprecated.md
		'import/no-deprecated': 'off',

		// Forbid the use of extraneous packages
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md
		'import/no-extraneous-dependencies': ['error', {
			devDependencies: false,
			optionalDependencies: false,
		}],

		// Forbid mutable exports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md
		'import/no-mutable-exports': 'error',

		// Module systems:

		// disallow require()
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-commonjs.md
		'import/no-commonjs': 'off',

		// disallow AMD require/define
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-amd.md
		'import/no-amd': 'error',

		// No Node.js builtin modules
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-nodejs-modules.md
		'import/no-nodejs-modules': 'off',

		// Style guide:

		// disallow non-import statements appearing before import statements
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md
		'import/imports-first': ['error', 'absolute-first'],

		// disallow duplicate imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md
		'import/no-duplicates': 'error',

		// disallow namespace imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-namespace.md
		'import/no-namespace': 'off',

		// Ensure consistent use of file extension within the import path
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
		// TODO: enable when https://github.com/benmosher/eslint-plugin-import/issues/390 is resolved
		'import/extensions': ['off', 'never'],

		// Enforce a convention in module import order
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md
		'import/order': ['off', {
			'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
			'newlines-between': 'never',
		}],

		// Require a newline after the last import/require in a group
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/newline-after-import.md
		'import/newline-after-import': 'error',

		// Require modules with a single export to use a default export
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md
		'import/prefer-default-export': 'error',

		// Restrict which files can be imported in a given folder
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-restricted-paths.md
		'import/no-restricted-paths': 'off',

		// Forbid modules to have too many dependencies
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/max-dependencies.md
		'import/max-dependencies': ['off', { max: 10 }],

		// Forbid import of modules using absolute paths
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-absolute-path.md
		'import/no-absolute-path': ['off'],
	},
};
<commit_msg>Relaxed no-extraneous dependencies rule<commit_after>module.exports = {
	env: {
		es6: true,
	},

	parserOptions: {
		ecmaVersion: 6,
		sourceType: 'module',
	},

	plugins: [
		'import',
	],

	settings: {
		'import/resolver': {
			node: {
				extensions: ['.js', '.json', '.jsx'],
			},
		},
		'import/extensions': [
			'.js',
			'.jsx',
		],
		'import/core-modules': [
		],
		'import/ignore': [
			'node_modules',
			'\\.(coffee|scss|css|less|hbs|svg|json)$',
		],
	},

	rules: {
		// Static analysis:

		// ensure imports point to files/modules that can be resolved
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-unresolved.md
		'import/no-unresolved': 'off',

		// ensure named imports coupled with named exports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/named.md#when-not-to-use-it
		'import/named': 'off',

		// ensure default import coupled with default export
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/default.md#when-not-to-use-it
		'import/default': 'off',

		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/namespace.md
		'import/namespace': 'off',

		// Helpful warnings:

		// disallow invalid exports, e.g. multiple defaults
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/export.md
		'import/export': 'error',

		// do not allow a default import name to match a named export
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default.md
		'import/no-named-as-default': 'error',

		// warn on accessing default export property names that are also named exports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default-member.md
		'import/no-named-as-default-member': 'error',

		// disallow use of jsdoc-marked-deprecated imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-deprecated.md
		'import/no-deprecated': 'off',

		// Forbid the use of extraneous packages
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md
		'import/no-extraneous-dependencies': ['off', {
			devDependencies: true,
			optionalDependencies: false,
			peerDependencies: true,
		}],

		// Forbid mutable exports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md
		'import/no-mutable-exports': 'error',

		// Module systems:

		// disallow require()
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-commonjs.md
		'import/no-commonjs': 'off',

		// disallow AMD require/define
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-amd.md
		'import/no-amd': 'error',

		// No Node.js builtin modules
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-nodejs-modules.md
		'import/no-nodejs-modules': 'off',

		// Style guide:

		// disallow non-import statements appearing before import statements
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md
		'import/imports-first': ['error', 'absolute-first'],

		// disallow duplicate imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md
		'import/no-duplicates': 'error',

		// disallow namespace imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-namespace.md
		'import/no-namespace': 'off',

		// Ensure consistent use of file extension within the import path
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
		// TODO: enable when https://github.com/benmosher/eslint-plugin-import/issues/390 is resolved
		'import/extensions': ['off', 'never'],

		// Enforce a convention in module import order
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md
		'import/order': ['off', {
			'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
			'newlines-between': 'never',
		}],

		// Require a newline after the last import/require in a group
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/newline-after-import.md
		'import/newline-after-import': 'error',

		// Require modules with a single export to use a default export
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md
		'import/prefer-default-export': 'error',

		// Restrict which files can be imported in a given folder
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-restricted-paths.md
		'import/no-restricted-paths': 'off',

		// Forbid modules to have too many dependencies
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/max-dependencies.md
		'import/max-dependencies': ['off', { max: 10 }],

		// Forbid import of modules using absolute paths
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-absolute-path.md
		'import/no-absolute-path': ['off'],
	},
};
<|endoftext|>


<commit_before>function defaultNotificationFormat(txt){
    if(txt.substr(0,txt.indexOf("[")-1) == 0)
      return txt.substring(txt.indexOf("]:")+2);
    return txt.substr(0,txt.indexOf("[")-1) + ": " + txt.substring(txt.indexOf("]:")+2);
}
var templateToLoad;
templateToLoad = templateToLoad || "https://rawgit.com/ocbaker/SMFPack-Chatbox-Tools/settings-update/settingsTemplate.html";
console.log(templateToLoad);
var notificationSettings = {
    phrases: ["ocbaker", "oliver", "admin", "mods", "moderator", "baker", "swear", "language"],
    general: true,
    mentions: true,
    generalTimeout: 5 * 1000,
    mentionTimeout: 10 * 1000,
    notifyWhenChatActive: false,
    generalFormat: defaultNotificationFormat,
    mentionFormat: defaultNotificationFormat
};

Shoutbox_PutMsgs = function Shoutbox_PutMsgs(XMLDoc)
{
    if (Shoutbox.msgs !== false)
        window.clearTimeout(Shoutbox.msgs);
    if (XMLDoc && XMLDoc.getElementsByTagName("banned")[0])
    {
        setOuterHTML(document.getElementById("shoutbox_banned"), '');
        document.getElementById("shoutbox_status").style.visibility = 'hidden';
        document.getElementById("shoutbox_message").value = '';
        document.getElementById("shoutbox_message").disabled = true;
        return window.alert(Shoutbox.lang.banned);
    }
    var error = false;
    if (XMLDoc && XMLDoc.getElementsByTagName("error")[0])
        error = XMLDoc.getElementsByTagName("error")[0].childNodes[0].nodeValue;
    if (!XMLDoc || !XMLDoc.getElementsByTagName("msgs")[0])
    {
        if (!Shoutbox.hide)
        {
            Shoutbox.msgs = window.setTimeout("Shoutbox_GetMsgs();", Shoutbox.refresh);
            Shoutbox.loading = false;
        }
        document.getElementById("shoutbox_status").style.visibility = 'hidden';
        if (error)
            window.alert(error);
        Shoutbox.first = false;
        return;
    }
    var toReset = !Shoutbox.first && XMLDoc.getElementsByTagName("reset")[0];
    if (!Shoutbox.ie)
    {
        if (toReset)
        {
            if (Shoutbox.msgdown)
                setInnerHTML(document.getElementById("shoutbox_banned"), '

');
            else
                setInnerHTML(document.getElementById("shoutbox_banned"), '

');
        }
        setOuterHTML(document.getElementById("shoutbox_msgs"), XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue);
    }
    else
    {
        var msgs = '';
        if (Shoutbox.msgdown)
        {
            msgs += '

';
            msgs += (toReset ? '' : getInnerHTML(document.getElementById("shoutbox_table"))) + XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue;
            msgs += '

';
        }
        else
        {
            msgs += '
';
            msgs += XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue + (toReset ? '' : getInnerHTML(document.getElementById("shoutbox_table")));
            msgs += '
';
        }
        setInnerHTML(document.getElementById("shoutbox_banned"), msgs);
    }
    if (toReset)
    {
        Shoutbox.currentmsg = 1;
        Shoutbox.countmsgs = Shoutbox.maxmsgs = 0;
    }
    var count = parseInt(XMLDoc.getElementsByTagName("count")[0].childNodes[0].nodeValue);
    if (Shoutbox.countmsgs + count > Shoutbox.keepmsgs)
    {
        for (var i = Shoutbox.currentmsg; i < Shoutbox.currentmsg + Shoutbox.countmsgs + count - Shoutbox.keepmsgs; i++)
            document.getElementById("shoutbox_row" + i).parentNode.removeChild(document.getElementById("shoutbox_row" + i));
        Shoutbox.currentmsg += Shoutbox.countmsgs + count - Shoutbox.keepmsgs;
    }
    Shoutbox.maxmsgs += count;
    Shoutbox.countmsgs = Shoutbox.countmsgs + count > Shoutbox.keepmsgs ? Shoutbox.keepmsgs : Shoutbox.countmsgs + count;
    if (!Shoutbox.hide)
    {
        Shoutbox.msgs = window.setTimeout("Shoutbox_GetMsgs();", Shoutbox.refresh);
        Shoutbox.loading = false;
    }
    document.getElementById("shoutbox_status").style.visibility = 'hidden';
    if (Shoutbox.msgdown && (document.getElementById("shoutbox_banned").scrollTop >= Shoutbox.scroll || Shoutbox.scroll == 0))
    {
        document.getElementById("shoutbox_banned").scrollTop = document.getElementById("shoutbox_banned").scrollHeight;
        document.getElementById("shoutbox_banned").scrollTop = document.getElementById("shoutbox_banned").scrollHeight;
        Shoutbox.scroll = document.getElementById("shoutbox_banned").scrollTop;
    }
    if (error)
        window.alert(error);
    if (!Shoutbox.first && XMLDoc.getElementsByTagName("newmsgs")[0]) {
        var msgs = $(XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue);
        if ((notificationSettings.notifyWhenChatActive || isHidden()) && (notificationSettings.general || notificationSettings.mentions))
            msgs.each(function(a, b, c) {
                var txt = $(b).text();
                if (txt != "") {
                    var stxt = txt.toLowerCase();
                    var found = false;
                    notificationSettings.phrases.forEach(function(phrase, i) {
                        if (!found && stxt.indexOf(phrase.toLowerCase()) != -1)
                            found = true;
                    });

                    if (found && notificationSettings.mentions)
                        notifyMe("LoE Chat Mention", notificationSettings.mentionFormat(txt), notificationSettings.mentionTimeout);
                    if (!(found && notificationSettings.mentions) && notificationSettings.general)
                        notifyMe("LoE Chat", notificationSettings.generalFormat(txt), notificationSettings.generalTimeout);
                }

            });
        Shoutbox_NewMsgs();
    }
    Shoutbox.first = false;
};

notifyMe = function notifyMe(title, msg, timeout) {
    // Let's check if the browser supports notifications
    if (!("Notification" in window)) {
        alert("This browser does not support desktop notification");
    }

    // Let's check whether notification permissions have alredy been granted
    else if (Notification.permission === "granted") {
        // If it's okay let's create a notification
        var notification = new Notification(title, {body: msg,icon: "https://pbs.twimg.com/profile_images/1910794026/LoE_Web_Logo2_400x400.png"});
        if (timeout != 0)
            setTimeout(function() {
                notification.close();
            }, timeout);
    }

    // Otherwise, we need to ask the user for permission
    else if (Notification.permission !== 'denied') {
        Notification.requestPermission(function(permission) {
            // If the user accepts, let's create a notification
            if (permission === "granted") {
                var notification = new Notification(title, {body: msg,icon: "https://pbs.twimg.com/profile_images/1910794026/LoE_Web_Logo2_400x400.png"});
                if (timeout != 0)
                    setTimeout(function() {
                        notification.close();
                    }, timeout);
            }
        });
    }

// At last, if the user has denied notifications, and you
// want to be respectful there is no need to bother them any more.
}
function getHiddenProp() {
    var prefixes = ['webkit', 'moz', 'ms', 'o'];

    // if 'hidden' is natively supported just return it
    if ('hidden' in document)
        return 'hidden';

    // otherwise loop over all the known prefixes until we find one
    for (var i = 0; i < prefixes.length; i++) {
        if ((prefixes[i] + 'Hidden') in document)
            return prefixes[i] + 'Hidden';
    }

    // otherwise it's not supported
    return null;
}
function isHidden() {
    var prop = getHiddenProp();
    if (!prop)
        return false;

    return document[prop];
}

function loadAngular() {

    angular.module('Foo', ['LocalStorageModule'], function($controllerProvider) {
        controllerProvider = $controllerProvider;
    });
    // Bootstrap Foo
    angular.bootstrap($('#shoutbox'), ['Foo']);

    // .. time passes ..

    // Load javascript file with Ctrl controller
    var myApp = angular.module('Foo');
    myApp.config(function(localStorageServiceProvider) {
        localStorageServiceProvider
        .setPrefix('myApp')
        .setStorageType('localStorage')
        .setNotify(true, true)
    });
    myApp.controller('Ctrl', function($scope, $rootScope, localStorageService) {
        console.log(localStorageService);
        $scope.settings = notificationSettings;
        $scope.msg = "It works! rootScope is " + $rootScope.$id +
        ", should be " + $('#shoutbox .content').scope().$id;
        console.log($scope);
    });
    // Load html file with content that uses Ctrl controller
    $('
').appendTo('#shoutbox .content');
    $('#nSettings').load(templateToLoad, function() {
        registerController("Foo", "Ctrl");
        // compile the new element
        $('#shoutbox .content').injector().invoke(function($compile, $rootScope, localStorageService) {
            $compile($('#ctrl'))($rootScope);
            $rootScope.$apply();
        });
    });
}

var controllerProvider = null;

jQuery.getScript("//ajax.googleapis.com/ajax/libs/angularjs/1.4.0-rc.2/angular.min.js", function() {
    jQuery.getScript("https://rawgit.com/grevory/angular-local-storage/master/dist/angular-local-storage.js", function() {
        loadAngular();
    });
});

// Register Ctrl controller manually
// If you can reference the controller function directly, just run:
// $controllerProvider.register(controllerName, controllerFunction);
// Note: I haven't found a way to get $controllerProvider at this stage
//    so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
    // Here I cannot get the controller function directly so I
    // need to loop through the module's _invokeQueue to get it
    var queue = angular.module(moduleName)._invokeQueue;
    for (var i = 0; i < queue.length; i++) {
        var call = queue[i];
        if (call[0] == "$controllerProvider" &&
        call[1] == "register" &&
        call[2][0] == controllerName) {
            controllerProvider.register(controllerName, call[2][1]);
        }
    }
}

notifyMe("LoE Chat", "Loaded Notifications", 10000);
<commit_msg>Shoutbox<commit_after>function defaultNotificationFormat(txt){
    if(txt.substr(0,txt.indexOf("[")-1) == 0)
      return txt.substring(txt.indexOf("]:")+2);
    return txt.substr(0,txt.indexOf("[")-1) + ": " + txt.substring(txt.indexOf("]:")+2);
}
var templateToLoad;
templateToLoad = templateToLoad || "https://rawgit.com/ocbaker/SMFPack-Chatbox-Tools/settings-update/settingsTemplate.html";
var notificationSettings = {
    phrases: ["ocbaker", "oliver", "admin", "mods", "moderator", "baker", "swear", "language"],
    general: true,
    mentions: true,
    generalTimeout: 5 * 1000,
    mentionTimeout: 10 * 1000,
    notifyWhenChatActive: false,
    generalFormat: defaultNotificationFormat,
    mentionFormat: defaultNotificationFormat
};
function loadChatbox(){
    Shoutbox_PutMsgs = function Shoutbox_PutMsgs(XMLDoc)
    {
        if (Shoutbox.msgs !== false)
            window.clearTimeout(Shoutbox.msgs);
        if (XMLDoc && XMLDoc.getElementsByTagName("banned")[0])
        {
            setOuterHTML(document.getElementById("shoutbox_banned"), '');
            document.getElementById("shoutbox_status").style.visibility = 'hidden';
            document.getElementById("shoutbox_message").value = '';
            document.getElementById("shoutbox_message").disabled = true;
            return window.alert(Shoutbox.lang.banned);
        }
        var error = false;
        if (XMLDoc && XMLDoc.getElementsByTagName("error")[0])
            error = XMLDoc.getElementsByTagName("error")[0].childNodes[0].nodeValue;
        if (!XMLDoc || !XMLDoc.getElementsByTagName("msgs")[0])
        {
            if (!Shoutbox.hide)
            {
                Shoutbox.msgs = window.setTimeout("Shoutbox_GetMsgs();", Shoutbox.refresh);
                Shoutbox.loading = false;
            }
            document.getElementById("shoutbox_status").style.visibility = 'hidden';
            if (error)
                window.alert(error);
            Shoutbox.first = false;
            return;
        }
        var toReset = !Shoutbox.first && XMLDoc.getElementsByTagName("reset")[0];
        if (!Shoutbox.ie)
        {
            if (toReset)
            {
                if (Shoutbox.msgdown)
                    setInnerHTML(document.getElementById("shoutbox_banned"), '

');
                else
                    setInnerHTML(document.getElementById("shoutbox_banned"), '

');
            }
            setOuterHTML(document.getElementById("shoutbox_msgs"), XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue);
        }
        else
        {
            var msgs = '';
            if (Shoutbox.msgdown)
            {
                msgs += '

';
                msgs += (toReset ? '' : getInnerHTML(document.getElementById("shoutbox_table"))) + XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue;
                msgs += '

';
            }
            else
            {
                msgs += '
';
                msgs += XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue + (toReset ? '' : getInnerHTML(document.getElementById("shoutbox_table")));
                msgs += '
';
            }
            setInnerHTML(document.getElementById("shoutbox_banned"), msgs);
        }
        if (toReset)
        {
            Shoutbox.currentmsg = 1;
            Shoutbox.countmsgs = Shoutbox.maxmsgs = 0;
        }
        var count = parseInt(XMLDoc.getElementsByTagName("count")[0].childNodes[0].nodeValue);
        if (Shoutbox.countmsgs + count > Shoutbox.keepmsgs)
        {
            for (var i = Shoutbox.currentmsg; i < Shoutbox.currentmsg + Shoutbox.countmsgs + count - Shoutbox.keepmsgs; i++)
                document.getElementById("shoutbox_row" + i).parentNode.removeChild(document.getElementById("shoutbox_row" + i));
            Shoutbox.currentmsg += Shoutbox.countmsgs + count - Shoutbox.keepmsgs;
        }
        Shoutbox.maxmsgs += count;
        Shoutbox.countmsgs = Shoutbox.countmsgs + count > Shoutbox.keepmsgs ? Shoutbox.keepmsgs : Shoutbox.countmsgs + count;
        if (!Shoutbox.hide)
        {
            Shoutbox.msgs = window.setTimeout("Shoutbox_GetMsgs();", Shoutbox.refresh);
            Shoutbox.loading = false;
        }
        document.getElementById("shoutbox_status").style.visibility = 'hidden';
        if (Shoutbox.msgdown && (document.getElementById("shoutbox_banned").scrollTop >= Shoutbox.scroll || Shoutbox.scroll == 0))
        {
            document.getElementById("shoutbox_banned").scrollTop = document.getElementById("shoutbox_banned").scrollHeight;
            document.getElementById("shoutbox_banned").scrollTop = document.getElementById("shoutbox_banned").scrollHeight;
            Shoutbox.scroll = document.getElementById("shoutbox_banned").scrollTop;
        }
        if (error)
            window.alert(error);
        if (!Shoutbox.first && XMLDoc.getElementsByTagName("newmsgs")[0]) {
            var msgs = $(XMLDoc.getElementsByTagName("msgs")[0].childNodes[0].nodeValue);
            if ((notificationSettings.notifyWhenChatActive || isHidden()) && (notificationSettings.general || notificationSettings.mentions))
                msgs.each(function(a, b, c) {
                    var txt = $(b).text();
                    if (txt != "") {
                        var stxt = txt.toLowerCase();
                        var found = false;
                        notificationSettings.phrases.forEach(function(phrase, i) {
                            if (!found && stxt.indexOf(phrase.toLowerCase()) != -1)
                                found = true;
                        });

                        if (found && notificationSettings.mentions)
                            notifyMe("LoE Chat Mention", notificationSettings.mentionFormat(txt), notificationSettings.mentionTimeout);
                        if (!(found && notificationSettings.mentions) && notificationSettings.general)
                            notifyMe("LoE Chat", notificationSettings.generalFormat(txt), notificationSettings.generalTimeout);
                    }

                });
            Shoutbox_NewMsgs();
        }
        Shoutbox.first = false;
    };
}

notifyMe = function notifyMe(title, msg, timeout) {
    // Let's check if the browser supports notifications
    if (!("Notification" in window)) {
        alert("This browser does not support desktop notification");
    }

    // Let's check whether notification permissions have alredy been granted
    else if (Notification.permission === "granted") {
        // If it's okay let's create a notification
        var notification = new Notification(title, {body: msg,icon: "https://pbs.twimg.com/profile_images/1910794026/LoE_Web_Logo2_400x400.png"});
        if (timeout != 0)
            setTimeout(function() {
                notification.close();
            }, timeout);
    }

    // Otherwise, we need to ask the user for permission
    else if (Notification.permission !== 'denied') {
        Notification.requestPermission(function(permission) {
            // If the user accepts, let's create a notification
            if (permission === "granted") {
                var notification = new Notification(title, {body: msg,icon: "https://pbs.twimg.com/profile_images/1910794026/LoE_Web_Logo2_400x400.png"});
                if (timeout != 0)
                    setTimeout(function() {
                        notification.close();
                    }, timeout);
            }
        });
    }

// At last, if the user has denied notifications, and you
// want to be respectful there is no need to bother them any more.
}
function getHiddenProp() {
    var prefixes = ['webkit', 'moz', 'ms', 'o'];

    // if 'hidden' is natively supported just return it
    if ('hidden' in document)
        return 'hidden';

    // otherwise loop over all the known prefixes until we find one
    for (var i = 0; i < prefixes.length; i++) {
        if ((prefixes[i] + 'Hidden') in document)
            return prefixes[i] + 'Hidden';
    }

    // otherwise it's not supported
    return null;
}
function isHidden() {
    var prop = getHiddenProp();
    if (!prop)
        return false;

    return document[prop];
}

function loadSettings(localStorageService){

}

function loadAngular() {

    angular.module('Foo', ['LocalStorageModule'], function($controllerProvider) {
        controllerProvider = $controllerProvider;
    });
    // Bootstrap Foo
    angular.bootstrap($('#shoutbox'), ['Foo']);

    // .. time passes ..

    // Load javascript file with Ctrl controller
    var myApp = angular.module('Foo');
    myApp.config(function(localStorageServiceProvider) {
        localStorageServiceProvider
        .setPrefix('myApp')
        .setStorageType('localStorage')
        .setNotify(true, true)
    });
    myApp.controller('Ctrl', function($scope, $rootScope, localStorageService) {
        console.log(localStorageService);
        $scope.settings = notificationSettings;
        console.log($scope);
        loadChatbox();
        notifyMe("LoE Chat", "Loaded Notifications", 10000);
    });
    // Load html file with content that uses Ctrl controller
    $('
').appendTo('#shoutbox .content');
    $('#nSettings').load(templateToLoad, function() {
        registerController("Foo", "Ctrl");
        // compile the new element
        $('#shoutbox .content').injector().invoke(function($compile, $rootScope, localStorageService) {
            $compile($('#ctrl'))($rootScope);
            $rootScope.$apply();
        });
    });
}

var controllerProvider = null;

jQuery.getScript("//ajax.googleapis.com/ajax/libs/angularjs/1.4.0-rc.2/angular.min.js", function() {
    jQuery.getScript("https://rawgit.com/grevory/angular-local-storage/master/dist/angular-local-storage.js", function() {
        loadAngular();
    });
});

// Register Ctrl controller manually
// If you can reference the controller function directly, just run:
// $controllerProvider.register(controllerName, controllerFunction);
// Note: I haven't found a way to get $controllerProvider at this stage
//    so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
    // Here I cannot get the controller function directly so I
    // need to loop through the module's _invokeQueue to get it
    var queue = angular.module(moduleName)._invokeQueue;
    for (var i = 0; i < queue.length; i++) {
        var call = queue[i];
        if (call[0] == "$controllerProvider" &&
        call[1] == "register" &&
        call[2][0] == controllerName) {
            controllerProvider.register(controllerName, call[2][1]);
        }
    }
}

<|endoftext|>