<commit_before>const { evaluators } = require('arsenal').policies;
const constants = require('../../../../constants');

const actionMap = {
    's3:AbortMultipartUpload': 'multipartDelete',
    's3:DeleteBucket': 'bucketDelete',
    's3:DeleteBucketPolicy': 'bucketDeletePolicy',
    's3:DeleteBucketWebsite': 'bucketDeleteWebsite',
    's3:DeleteObject': 'objectDelete',
    's3:DeleteObjectTagging': 'objectDeleteTagging',
    's3:GetBucketAcl': 'bucketGetACL',
    's3:GetBucketCORS': 'bucketGetCors',
    's3:GetBucketLocation': 'bucketGetLocation',
    's3:GetBucketPolicy': 'bucketGetPolicy',
    's3:GetBucketVersioning': 'bucketGetVersioning',
    's3:GetBucketWebsite': 'bucketGetWebsite',
    's3:GetLifecycleConfiguration': 'bucketGetLifecycle',
    's3:GetObject': 'objectGet',
    's3:GetObjectAcl': 'objectGetACL',
    's3:GetObjectTagging': 'objectGetTagging',
    's3:GetReplicationConfiguration': 'bucketGetReplication',
    's3:ListBucket': 'bucketHead',
    's3:ListBucketMultipartUploads': 'listMultipartUploads',
    's3:ListMultipartUploadParts': 'listParts',
    's3:PutBucketAcl': 'bucketPutACL',
    's3:PutBucketCORS': 'bucketPutCors',
    's3:PutBucketPolicy': 'bucketPutPolicy',
    's3:PutBucketVersioning': 'bucketPutVersioning',
    's3:PutBucketWebsite': 'bucketPutWebsite',
    's3:PutLifecycleConfiguration': 'bucketPutLifecycle',
    's3:PutObject': 'objectPut',
    's3:PutObjectAcl': 'objectPutACL',
    's3:PutObjectTagging': 'objectPutTagging',
    's3:PutReplicationConfiguration': 'bucketPutReplication',
};

// whitelist buckets to allow public read on objects
const publicReadBuckets = process.env.ALLOW_PUBLIC_READ_BUCKETS ?
    process.env.ALLOW_PUBLIC_READ_BUCKETS.split(',') : [];

function checkBucketAcls(bucket, requestType, canonicalID) {
    if (bucket.getOwner() === canonicalID) {
        return true;
    }

    // any requestType outside of those checked in this function is
    // outside the scope of ACL permissions and will be denied unless
    // allowed by a different permissions granter
    const bucketAcl = bucket.getAcl();
    if (requestType === 'bucketGet' || requestType === 'bucketHead') {
        if (bucketAcl.Canned === 'public-read'
            || bucketAcl.Canned === 'public-read-write'
            || (bucketAcl.Canned === 'authenticated-read'
                && canonicalID !== constants.publicId)) {
            return true;
        } else if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.READ.indexOf(canonicalID) > -1) {
            return true;
        }
    }
    if (requestType === 'bucketGetACL') {
        if ((bucketAcl.Canned === 'log-delivery-write'
                && canonicalID === constants.logId)
            || bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.READ_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    if (requestType === 'bucketPutACL') {
        if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.WRITE_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    if (requestType === 'bucketDelete' && bucket.getOwner() === canonicalID) {
        return true;
    }

    if (requestType === 'objectDelete' || requestType === 'objectPut') {
        if (bucketAcl.Canned === 'public-read-write'
            || bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.WRITE.indexOf(canonicalID) > -1) {
            return true;
        }
    }
    // Note that an account can have the ability to do objectPutACL,
    // objectGetACL, objectHead or objectGet even if the account has no rights
    // to the bucket holding the object.  So, if the request type is
    // objectPutACL, objectGetACL, objectHead or objectGet, the bucket
    // authorization check should just return true so can move on to check
    // rights at the object level.
    return (requestType === 'objectPutACL' || requestType === 'objectGetACL' ||
    requestType === 'objectGet' || requestType === 'objectHead');
}

function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
    const bucketOwner = bucket.getOwner();
    // acls don't distinguish between users and accounts, so both should be allowed
    if (constants.bucketOwnerActions.includes(requestType)
    && (bucketOwner === canonicalID)) {
        return true;
    }
    if (objectMD['owner-id'] === canonicalID) {
        return true;
    }
    if (!objectMD.acl) {
        return false;
    }

    if (requestType === 'objectGet' || requestType === 'objectHead') {
        if (objectMD.acl.Canned === 'public-read'
            || objectMD.acl.Canned === 'public-read-write'
            || (objectMD.acl.Canned === 'authenticated-read'
                && canonicalID !== constants.publicId)) {
            return true;
        } else if (objectMD.acl.Canned === 'bucket-owner-read'
                && bucketOwner === canonicalID) {
            return true;
        } else if ((objectMD.acl.Canned === 'bucket-owner-full-control'
                && bucketOwner === canonicalID)
            || objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
            || objectMD.acl.READ.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    // User is already authorized on the bucket for FULL_CONTROL or WRITE or
    // bucket has canned ACL public-read-write
    if (requestType === 'objectPut' || requestType === 'objectDelete') {
        return true;
    }

    if (requestType === 'objectPutACL') {
        if ((objectMD.acl.Canned === 'bucket-owner-full-control'
                && bucketOwner === canonicalID)
            || objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
            || objectMD.acl.WRITE_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    if (requestType === 'objectGetACL') {
        if ((objectMD.acl.Canned === 'bucket-owner-full-control'
                && bucketOwner === canonicalID)
            || objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
            || objectMD.acl.READ_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    // allow public reads on buckets that are whitelisted for anonymous reads
    // TODO: remove this after bucket policies are implemented
    const bucketAcl = bucket.getAcl();
    const allowPublicReads = publicReadBuckets.includes(bucket.getName()) &&
        bucketAcl.Canned === 'public-read' &&
        (requestType === 'objectGet' || requestType === 'objectHead');
    if (allowPublicReads) {
        return true;
    }
    return false;
}

function _checkActions(requestType, actions, log) {
    // if requestType isn't in list of controlled actions
    if (!Object.values(actionMap).includes(requestType)) {
        return true;
    }
    const mappedAction = Object.keys(actionMap)
        [Object.values(actionMap).indexOf(requestType)];
    return evaluators.isActionApplicable(mappedAction, actions, log);
}

function _getAccountId(arn) {
    // account or user arn is of format 'arn:aws:iam::<12-digit-acct-id>:etc...
    return arn.substr(13, 12);
}

function _isAccountId(principal) {
    return (principal.length === 12 && /^\d+$/.test(principal));
}

function _checkPrincipal(requester, principal) {
    if (principal === '*') {
        return true;
    }
    if (principal === requester) {
        return true;
    }
    if (_isAccountId(principal)) {
        return _getAccountId(requester) === principal;
    }
    if (principal.endsWith('root')) {
        return _getAccountId(requester) === _getAccountId(principal);
    }
    return false;
}

function _checkPrincipals(canonicalID, arn, principal) {
    if (principal === '*') {
        return true;
    }
    if (principal.CanonicalUser) {
        if (Array.isArray(principal.CanonicalUser)) {
            return principal.CanonicalUser.some(p => _checkPrincipal(canonicalID, p));
        }
        return _checkPrincipal(canonicalID, principal.CanonicalUser);
    }
    if (principal.AWS) {
        if (Array.isArray(principal.AWS)) {
            return principal.AWS.some(p => _checkPrincipal(arn, p));
        }
        return _checkPrincipal(arn, principal.AWS);
    }
    return false;
}

function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, log) {
    let permission = 'defaultDeny';
    // if requester is user within bucket owner account, actions should be
    // allowed unless explicitly denied (assumes allowed by IAM policy)
    if (bucketOwner === canonicalID) {
        permission = 'allow';
    }
    let copiedStatement = JSON.parse(JSON.stringify(policy.Statement));
    while (copiedStatement.length > 0) {
        const s = copiedStatement[0];
        const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal);
        const actionMatch = _checkActions(requestType, s.Action, log);

        if (principalMatch && actionMatch && s.Effect === 'Deny') {
            // explicit deny trumps any allows, so return immediately
            return 'explicitDeny';
        }
        if (principalMatch && actionMatch && s.Effect === 'Allow') {
            permission = 'allow';
        }
        copiedStatement = copiedStatement.splice(1);
    }
    return permission;
}

function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log) {
    // Check to see if user is authorized to perform a
    // particular action on bucket based on ACLs.
    // TODO: Add IAM checks
    let requesterIsNotUser = true;
    let arn = null;
    if (authInfo) {
        requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
        arn = authInfo.getArn();
    }
    // if the bucket owner is an account, users should not have default access
    if ((bucket.getOwner() === canonicalID) && requesterIsNotUser) {
        return true;
    }
    const aclPermission = checkBucketAcls(bucket, requestType, canonicalID);
    const bucketPolicy = bucket.getBucketPolicy();
    if (!bucketPolicy) {
        return aclPermission;
    }
    const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
        canonicalID, arn, bucket.getOwner(), log);
    if (bucketPolicyPermission === 'explicitDeny') {
        return false;
    }
    return (aclPermission || (bucketPolicyPermission === 'allow'));
}

function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, log) {
    const bucketOwner = bucket.getOwner();
    if (!objectMD) {
        return false;
    }
    let requesterIsNotUser = true;
    let arn = null;
    if (authInfo) {
        requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
        arn = authInfo.getArn();
    }
    if (objectMD['owner-id'] === canonicalID && requesterIsNotUser) {
        return true;
    }
    // account is authorized if:
    // - requesttype is included in bucketOwnerActions and
    // - account is the bucket owner
    // - requester is account, not user
    if (constants.bucketOwnerActions.includes(requestType)
    && (bucketOwner === canonicalID)
    && requesterIsNotUser) {
        return true;
    }
    const aclPermission = checkObjectAcls(bucket, objectMD, requestType,
        canonicalID);
    const bucketPolicy = bucket.getBucketPolicy();
    if (!bucketPolicy) {
        return aclPermission;
    }
    const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
        canonicalID, arn, bucket.getOwner(), log);
    if (bucketPolicyPermission === 'explicitDeny') {
        return false;
    }
    return (aclPermission || (bucketPolicyPermission === 'allow'));
}

function _checkResource(resource, bucketArn) {
    if (resource === bucketArn) {
        return true;
    }
    if (resource.includes('/')) {
        const rSubs = resource.split('/');
        return rSubs[0] === bucketArn;
    }
    return false;
}

// the resources specified in the bucket policy should contain the bucket name
function validatePolicyResource(bucketName, policy) {
    const bucketArn = `arn:aws:s3:::${bucketName}`;

    return policy.Statement.every(s => {
        if (Array.isArray(s.Resource)) {
            return s.Resource.every(r => _checkResource(r, bucketArn));
        }
        if (typeof s.Resource === 'string') {
            return _checkResource(s.Resource, bucketArn);
        }
        return false;
    });
}

module.exports = {
    isBucketAuthorized,
    isObjAuthorized,
    checkBucketAcls,
    checkObjectAcls,
    validatePolicyResource,
};
<commit_msg>ft: S3C-3040 add obj lock actions to bucket policy<commit_after>const { evaluators } = require('arsenal').policies;
const constants = require('../../../../constants');

const actionMap = {
    's3:AbortMultipartUpload': 'multipartDelete',
    's3:DeleteBucket': 'bucketDelete',
    's3:DeleteBucketPolicy': 'bucketDeletePolicy',
    's3:DeleteBucketWebsite': 'bucketDeleteWebsite',
    's3:DeleteObject': 'objectDelete',
    's3:DeleteObjectTagging': 'objectDeleteTagging',
    's3:GetBucketAcl': 'bucketGetACL',
    's3:GetBucketCORS': 'bucketGetCors',
    's3:GetBucketLocation': 'bucketGetLocation',
    's3:GetBucketObjectLockConfiguration': 'bucketGetObjectLock',
    's3:GetBucketPolicy': 'bucketGetPolicy',
    's3:GetBucketVersioning': 'bucketGetVersioning',
    's3:GetBucketWebsite': 'bucketGetWebsite',
    's3:GetLifecycleConfiguration': 'bucketGetLifecycle',
    's3:GetObject': 'objectGet',
    's3:GetObjectAcl': 'objectGetACL',
    's3:GetObjectLegalHold': 'objectGetLegalHold',
    's3:GetObjectRetention': 'objectGetRetention',
    's3:GetObjectTagging': 'objectGetTagging',
    's3:GetReplicationConfiguration': 'bucketGetReplication',
    's3:ListBucket': 'bucketHead',
    's3:ListBucketMultipartUploads': 'listMultipartUploads',
    's3:ListMultipartUploadParts': 'listParts',
    's3:PutBucketAcl': 'bucketPutACL',
    's3:PutBucketCORS': 'bucketPutCors',
    's3:PutBucketObjectLockConfiguration': 'bucketPutObjectLock',
    's3:PutBucketPolicy': 'bucketPutPolicy',
    's3:PutBucketVersioning': 'bucketPutVersioning',
    's3:PutBucketWebsite': 'bucketPutWebsite',
    's3:PutLifecycleConfiguration': 'bucketPutLifecycle',
    's3:PutObject': 'objectPut',
    's3:PutObjectAcl': 'objectPutACL',
    's3:PutObjectTagging': 'objectPutTagging',
    's3:PutObjectLegalHold': 'objectPutLegalHold',
    's3:PutObjectRetention': 'objectPutRetention',
    's3:PutReplicationConfiguration': 'bucketPutReplication',
};

// whitelist buckets to allow public read on objects
const publicReadBuckets = process.env.ALLOW_PUBLIC_READ_BUCKETS ?
    process.env.ALLOW_PUBLIC_READ_BUCKETS.split(',') : [];

function checkBucketAcls(bucket, requestType, canonicalID) {
    if (bucket.getOwner() === canonicalID) {
        return true;
    }

    // any requestType outside of those checked in this function is
    // outside the scope of ACL permissions and will be denied unless
    // allowed by a different permissions granter
    const bucketAcl = bucket.getAcl();
    if (requestType === 'bucketGet' || requestType === 'bucketHead') {
        if (bucketAcl.Canned === 'public-read'
            || bucketAcl.Canned === 'public-read-write'
            || (bucketAcl.Canned === 'authenticated-read'
                && canonicalID !== constants.publicId)) {
            return true;
        } else if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.READ.indexOf(canonicalID) > -1) {
            return true;
        }
    }
    if (requestType === 'bucketGetACL') {
        if ((bucketAcl.Canned === 'log-delivery-write'
                && canonicalID === constants.logId)
            || bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.READ_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    if (requestType === 'bucketPutACL') {
        if (bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.WRITE_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    if (requestType === 'bucketDelete' && bucket.getOwner() === canonicalID) {
        return true;
    }

    if (requestType === 'objectDelete' || requestType === 'objectPut') {
        if (bucketAcl.Canned === 'public-read-write'
            || bucketAcl.FULL_CONTROL.indexOf(canonicalID) > -1
            || bucketAcl.WRITE.indexOf(canonicalID) > -1) {
            return true;
        }
    }
    // Note that an account can have the ability to do objectPutACL,
    // objectGetACL, objectHead or objectGet even if the account has no rights
    // to the bucket holding the object.  So, if the request type is
    // objectPutACL, objectGetACL, objectHead or objectGet, the bucket
    // authorization check should just return true so can move on to check
    // rights at the object level.
    return (requestType === 'objectPutACL' || requestType === 'objectGetACL' ||
    requestType === 'objectGet' || requestType === 'objectHead');
}

function checkObjectAcls(bucket, objectMD, requestType, canonicalID) {
    const bucketOwner = bucket.getOwner();
    // acls don't distinguish between users and accounts, so both should be allowed
    if (constants.bucketOwnerActions.includes(requestType)
    && (bucketOwner === canonicalID)) {
        return true;
    }
    if (objectMD['owner-id'] === canonicalID) {
        return true;
    }
    if (!objectMD.acl) {
        return false;
    }

    if (requestType === 'objectGet' || requestType === 'objectHead') {
        if (objectMD.acl.Canned === 'public-read'
            || objectMD.acl.Canned === 'public-read-write'
            || (objectMD.acl.Canned === 'authenticated-read'
                && canonicalID !== constants.publicId)) {
            return true;
        } else if (objectMD.acl.Canned === 'bucket-owner-read'
                && bucketOwner === canonicalID) {
            return true;
        } else if ((objectMD.acl.Canned === 'bucket-owner-full-control'
                && bucketOwner === canonicalID)
            || objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
            || objectMD.acl.READ.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    // User is already authorized on the bucket for FULL_CONTROL or WRITE or
    // bucket has canned ACL public-read-write
    if (requestType === 'objectPut' || requestType === 'objectDelete') {
        return true;
    }

    if (requestType === 'objectPutACL') {
        if ((objectMD.acl.Canned === 'bucket-owner-full-control'
                && bucketOwner === canonicalID)
            || objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
            || objectMD.acl.WRITE_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    if (requestType === 'objectGetACL') {
        if ((objectMD.acl.Canned === 'bucket-owner-full-control'
                && bucketOwner === canonicalID)
            || objectMD.acl.FULL_CONTROL.indexOf(canonicalID) > -1
            || objectMD.acl.READ_ACP.indexOf(canonicalID) > -1) {
            return true;
        }
    }

    // allow public reads on buckets that are whitelisted for anonymous reads
    // TODO: remove this after bucket policies are implemented
    const bucketAcl = bucket.getAcl();
    const allowPublicReads = publicReadBuckets.includes(bucket.getName()) &&
        bucketAcl.Canned === 'public-read' &&
        (requestType === 'objectGet' || requestType === 'objectHead');
    if (allowPublicReads) {
        return true;
    }
    return false;
}

function _checkActions(requestType, actions, log) {
    // if requestType isn't in list of controlled actions
    if (!Object.values(actionMap).includes(requestType)) {
        return true;
    }
    const mappedAction = Object.keys(actionMap)
        [Object.values(actionMap).indexOf(requestType)];
    return evaluators.isActionApplicable(mappedAction, actions, log);
}

function _getAccountId(arn) {
    // account or user arn is of format 'arn:aws:iam::<12-digit-acct-id>:etc...
    return arn.substr(13, 12);
}

function _isAccountId(principal) {
    return (principal.length === 12 && /^\d+$/.test(principal));
}

function _checkPrincipal(requester, principal) {
    if (principal === '*') {
        return true;
    }
    if (principal === requester) {
        return true;
    }
    if (_isAccountId(principal)) {
        return _getAccountId(requester) === principal;
    }
    if (principal.endsWith('root')) {
        return _getAccountId(requester) === _getAccountId(principal);
    }
    return false;
}

function _checkPrincipals(canonicalID, arn, principal) {
    if (principal === '*') {
        return true;
    }
    if (principal.CanonicalUser) {
        if (Array.isArray(principal.CanonicalUser)) {
            return principal.CanonicalUser.some(p => _checkPrincipal(canonicalID, p));
        }
        return _checkPrincipal(canonicalID, principal.CanonicalUser);
    }
    if (principal.AWS) {
        if (Array.isArray(principal.AWS)) {
            return principal.AWS.some(p => _checkPrincipal(arn, p));
        }
        return _checkPrincipal(arn, principal.AWS);
    }
    return false;
}

function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, log) {
    let permission = 'defaultDeny';
    // if requester is user within bucket owner account, actions should be
    // allowed unless explicitly denied (assumes allowed by IAM policy)
    if (bucketOwner === canonicalID) {
        permission = 'allow';
    }
    let copiedStatement = JSON.parse(JSON.stringify(policy.Statement));
    while (copiedStatement.length > 0) {
        const s = copiedStatement[0];
        const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal);
        const actionMatch = _checkActions(requestType, s.Action, log);

        if (principalMatch && actionMatch && s.Effect === 'Deny') {
            // explicit deny trumps any allows, so return immediately
            return 'explicitDeny';
        }
        if (principalMatch && actionMatch && s.Effect === 'Allow') {
            permission = 'allow';
        }
        copiedStatement = copiedStatement.splice(1);
    }
    return permission;
}

function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log) {
    // Check to see if user is authorized to perform a
    // particular action on bucket based on ACLs.
    // TODO: Add IAM checks
    let requesterIsNotUser = true;
    let arn = null;
    if (authInfo) {
        requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
        arn = authInfo.getArn();
    }
    // if the bucket owner is an account, users should not have default access
    if ((bucket.getOwner() === canonicalID) && requesterIsNotUser) {
        return true;
    }
    const aclPermission = checkBucketAcls(bucket, requestType, canonicalID);
    const bucketPolicy = bucket.getBucketPolicy();
    if (!bucketPolicy) {
        return aclPermission;
    }
    const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
        canonicalID, arn, bucket.getOwner(), log);
    if (bucketPolicyPermission === 'explicitDeny') {
        return false;
    }
    return (aclPermission || (bucketPolicyPermission === 'allow'));
}

function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, log) {
    const bucketOwner = bucket.getOwner();
    if (!objectMD) {
        return false;
    }
    let requesterIsNotUser = true;
    let arn = null;
    if (authInfo) {
        requesterIsNotUser = !authInfo.isRequesterAnIAMUser();
        arn = authInfo.getArn();
    }
    if (objectMD['owner-id'] === canonicalID && requesterIsNotUser) {
        return true;
    }
    // account is authorized if:
    // - requesttype is included in bucketOwnerActions and
    // - account is the bucket owner
    // - requester is account, not user
    if (constants.bucketOwnerActions.includes(requestType)
    && (bucketOwner === canonicalID)
    && requesterIsNotUser) {
        return true;
    }
    const aclPermission = checkObjectAcls(bucket, objectMD, requestType,
        canonicalID);
    const bucketPolicy = bucket.getBucketPolicy();
    if (!bucketPolicy) {
        return aclPermission;
    }
    const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
        canonicalID, arn, bucket.getOwner(), log);
    if (bucketPolicyPermission === 'explicitDeny') {
        return false;
    }
    return (aclPermission || (bucketPolicyPermission === 'allow'));
}

function _checkResource(resource, bucketArn) {
    if (resource === bucketArn) {
        return true;
    }
    if (resource.includes('/')) {
        const rSubs = resource.split('/');
        return rSubs[0] === bucketArn;
    }
    return false;
}

// the resources specified in the bucket policy should contain the bucket name
function validatePolicyResource(bucketName, policy) {
    const bucketArn = `arn:aws:s3:::${bucketName}`;

    return policy.Statement.every(s => {
        if (Array.isArray(s.Resource)) {
            return s.Resource.every(r => _checkResource(r, bucketArn));
        }
        if (typeof s.Resource === 'string') {
            return _checkResource(s.Resource, bucketArn);
        }
        return false;
    });
}

module.exports = {
    isBucketAuthorized,
    isObjAuthorized,
    checkBucketAcls,
    checkObjectAcls,
    validatePolicyResource,
};
<|endoftext|>


<commit_before>import { Component } from 'react'
import fetch from 'isomorphic-unfetch'
import CircleProgress from '../../circle-progress'
import Widget from '../../widget'

export default class PageSpeedInsights extends Component {
  static defaultProps = {
    filterThirdPartyResources: true,
    locale: 'de_DE',
    strategy: 'desktop',
    title: 'PageSpeed Score'
  }

  state = {
    score: 0,
    loading: true,
    error: false
  }

  async componentDidMount () {
    const { url, filterThirdPartyResources, locale, strategy } = this.props

    const searchParams = [
      `url=${url}`,
      `filter_third_party_resources=${filterThirdPartyResources}`,
      `locale=${locale}`,
      `strategy=${strategy}`
    ].join('&')

    try {
      const res = await fetch(`https://www.googleapis.com/pagespeedonline/v2/runPagespeed?${searchParams}`)
      const json = await res.json()

      this.setState({ loading: false, score: json.ruleGroups.SPEED.score })
    } catch (error) {
      this.setState({ loading: false, error: true })
    }
  }

  render () {
    const { error, loading, score } = this.state
    const { title } = this.props
    return (
      <Widget title='PageSpeed Score' loading={loading} error={error}>
        <CircleProgress value={score} />
      </Widget>
    )
  }
}
<commit_msg>Fix merge error<commit_after>import { Component } from 'react'
import fetch from 'isomorphic-unfetch'
import CircleProgress from '../../circle-progress'
import Widget from '../../widget'

export default class PageSpeedInsights extends Component {
  static defaultProps = {
    filterThirdPartyResources: true,
    locale: 'de_DE',
    strategy: 'desktop',
    title: 'PageSpeed Score'
  }

  state = {
    score: 0,
    loading: true,
    error: false
  }

  async componentDidMount () {
    const { url, filterThirdPartyResources, locale, strategy } = this.props

    const searchParams = [
      `url=${url}`,
      `filter_third_party_resources=${filterThirdPartyResources}`,
      `locale=${locale}`,
      `strategy=${strategy}`
    ].join('&')

    try {
      const res = await fetch(`https://www.googleapis.com/pagespeedonline/v2/runPagespeed?${searchParams}`)
      const json = await res.json()

      this.setState({ loading: false, score: json.ruleGroups.SPEED.score })
    } catch (error) {
      this.setState({ loading: false, error: true })
    }
  }

  render () {
    const { error, loading, score } = this.state
    const { title } = this.props
    return (
      <Widget title={title} loading={loading} error={error}>
        <CircleProgress value={score} />
      </Widget>
    )
  }
}
<|endoftext|>


<commit_before>export { Base };
export { BasePool };

import * as Pool from "../util/Pool.js";
import * as ActivationEscaping from "./ActivationEscaping.js";

/**
 * A placeholder for tensor.
 *
 *   - In Operation.TwinArray.operation_append_Xxx(), it is used for tracking a tensor's final operation which should be responsible
 *       for destroying the tensor.
 *
 *   - In operation's .apply(), it is used for transferring tensor to the next sub operation.
 *
 *
 *
 * @member {Operation.Base} finalOperation
 *   The operation uses this tensor at final. It should be responsible for destroying this tensor. If null, this tensor is
 * not used by any operation.
 *
 * @member {tf.tensor} realTensor
 *   The real tensor represented by this placeholder. It is filled dynamically in an operation's apply() method.
 *
 * @member {ActivationEscaping.ScaleBoundsArray} scaleBoundsArray
 *   The element value bounds (per channel) of the tensor. Usually, it is from the output of the previous operation's value bounds
 * set. It will be kept (not cloned) directly. So caller should use them carefully.
 *
 */
class Base {

  /**
   *
   */
  constructor() {
    this.setAsConstructor();
  }

  /**
   * Initialize to empty.
   *
   * @return {Base}
   *   Return the this object.
   */
  setAsConstructor() {
    this.finalOperation = null;
    this.realTensor = null;
    this.set_height_width_channelCount_scaleBoundsArray_byTensorPlaceholder( null );
    return this;
  }

  /**
   * Release the .scaleBoundsArray
   *
   * Usually, this method is not responsible for releasing .finalOperation and .realTensor. They should be handled
   * by the caller of apply().
   */
  disposeResources() {
    if ( this.scaleBoundsArray ) {
      ActivationEscaping.ScaleBoundsArrayPool.Singleton.recycle( this.scaleBoundsArray );
    }

    this.finalOperation = null;
    this.realTensor = null;
    this.set_height_width_channelCount_scaleBoundsArray_byTensorPlaceholder( null );
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    BasePool.Singleton.recycle( this );
  }

  /**
   *
   * @param {ActivationEscaping.ScaleBoundsArray} scaleBoundsArray  The tensor placeholder's ScaleBoundsArray.
   */
  set_height_width_channelCount_scaleBoundsArray(
    height, width, channelCount, channelCount_lowerHalf, channelCount_higherHalf, scaleBoundsArray ) {
    this.height = height;
    this.width = width;
    this.channelCount = channelCount;
    this.channelCount_lowerHalf = channelCount_lowerHalf;
    this.channelCount_higherHalf = channelCount_higherHalf;
    this.scaleBoundsArray = scaleBoundsArray;
  }

  /**
   * Note: The .finalOperation and .realTensor are not modified by this method.
   *
   * @param {TensorPlaceholder} aTensorPlaceholder
   *   The tensor placeholder's height, width, channelCount, scaleBoundsArray will be used directly (i.e. not cloned) by this
   * tensor placeholder. If null, these properties will be set to undefined.
   */
  set_height_width_channelCount_scaleBoundsArray_byTensorPlaceholder( aTensorPlaceholder ) {
    if ( aTensorPlaceholder ) {
      this.set_height_width_channelCount_scaleBoundsArray(
        aTensorPlaceholder.height, aTensorPlaceholder.width,
        aTensorPlaceholder.channelCount, aTensorPlaceholder.channelCount_lowerHalf, aTensorPlaceholder.channelCount_higherHalf,
        aTensorPlaceholder.scaleBoundsArray );
    } else {
      this.set_height_width_channelCount_scaleBoundsArray(
        undefined, undefined,
        undefined, undefined, undefined,
        undefined );
    }
  }

  /**
   *
   * @param {TensorPlaceholder} aTensorPlaceholder  The tensor placeholder to be comapred.
   *
   * @return {boolean}
   *   Return true, if this tensor placeholder's height, width, channelCount are the same as aTensorPlaceholder. Note: the
   * .channelCount_lowerHalf and .channelCount_higherHalf are not compared.
   */
  is_height_width_channelCount_same_byTensorPlaceholder( aTensorPlaceholder ) {
    if (   ( this.height != aTensorPlaceholder.height )
        || ( this.width != aTensorPlaceholder.width )
        || ( this.channelCount != aTensorPlaceholder.channelCount )

        //|| ( this.channelCount_lowerHalf != aTensorPlaceholder.channelCount_lowerHalf )
        //|| ( this.channelCount_higherHalf != aTensorPlaceholder.channelCount_higherHalf )
       )
      return false;

    return true;
  }

}

/**
 * Providing TensorPlaceholder.Base
 *
 */
class BasePool extends Pool.Root {

  constructor() {
    super( Base, Base.setAsConstructor );
  }

//!!! (2022/06/22 Remarked) Base.setAsConstructor() should be enough.
//   /**
//    * @param {Base} this
//    *   The TensorPlaceholder.Base object to be initialized.
//    *
//    * @return {Base}
//    *   Return the this object.
//    */
//   static setAsConstructor() {
//     this.setAsConstructor();
//     return this;
//   }

}

/**
 * Used as default TensorPlaceholder.Base provider.
 */
BasePool.Singleton = new BasePool();
<commit_msg>Update TensorPlaceholder.js<commit_after>export { Base };
export { BasePool };

import * as Pool from "../util/Pool.js";
import * as ActivationEscaping from "./ActivationEscaping.js";

/**
 * A placeholder for tensor.
 *
 *   - In Operation.TwinArray.operation_append_Xxx(), it is used for tracking a tensor's final operation which should be responsible
 *       for destroying the tensor.
 *
 *   - In operation's .apply(), it is used for transferring tensor to the next sub operation.
 *
 *
 *
 * @member {Operation.Base} finalOperation
 *   The operation uses this tensor at final. It should be responsible for destroying this tensor. If null, this tensor is
 * not used by any operation.
 *
 * @member {tf.tensor} realTensor
 *   The real tensor represented by this placeholder. It is filled dynamically in an operation's apply() method.
 *
 * @member {ActivationEscaping.ScaleBoundsArray} scaleBoundsArray
 *   The element value bounds (per channel) of the tensor. Usually, it is from the output of the previous operation's value bounds
 * set. It will be kept (not cloned) directly. So caller should use them carefully.
 *
 */
class Base {

  /**
   *
   */
  constructor() {
    this.setAsConstructor();
  }

  /**
   * Initialize to empty.
   *
   * @return {Base}
   *   Return the this object.
   */
  setAsConstructor() {
    this.finalOperation = null;
    this.realTensor = null;
    this.set_height_width_channelCount_scaleBoundsArray_byTensorPlaceholder( null );
    return this;
  }

  /**
   * Release the .scaleBoundsArray
   *
   * Usually, this method is not responsible for releasing .finalOperation and .realTensor. They should be handled
   * by the caller of apply().
   */
  disposeResources() {
    if ( this.scaleBoundsArray ) {
      ActivationEscaping.ScaleBoundsArrayPool.Singleton.recycle( this.scaleBoundsArray );
      this.scaleBoundsArray = null;
    }

    this.finalOperation = null;
    this.realTensor = null;
    this.set_height_width_channelCount_scaleBoundsArray_byTensorPlaceholder( null );
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    BasePool.Singleton.recycle( this );
  }

  /**
   *
   * @param {ActivationEscaping.ScaleBoundsArray} scaleBoundsArray  The tensor placeholder's ScaleBoundsArray.
   */
  set_height_width_channelCount_scaleBoundsArray(
    height, width, channelCount, channelCount_lowerHalf, channelCount_higherHalf, scaleBoundsArray ) {
    this.height = height;
    this.width = width;
    this.channelCount = channelCount;
    this.channelCount_lowerHalf = channelCount_lowerHalf;
    this.channelCount_higherHalf = channelCount_higherHalf;
    this.scaleBoundsArray = scaleBoundsArray;
  }

  /**
   * Note: The .finalOperation and .realTensor are not modified by this method.
   *
   * @param {TensorPlaceholder} aTensorPlaceholder
   *   The tensor placeholder's height, width, channelCount, scaleBoundsArray will be used directly (i.e. not cloned) by this
   * tensor placeholder. If null, these properties will be set to undefined.
   */
  set_height_width_channelCount_scaleBoundsArray_byTensorPlaceholder( aTensorPlaceholder ) {
    if ( aTensorPlaceholder ) {
      this.set_height_width_channelCount_scaleBoundsArray(
        aTensorPlaceholder.height, aTensorPlaceholder.width,
        aTensorPlaceholder.channelCount, aTensorPlaceholder.channelCount_lowerHalf, aTensorPlaceholder.channelCount_higherHalf,
        aTensorPlaceholder.scaleBoundsArray );
    } else {
      this.set_height_width_channelCount_scaleBoundsArray(
        undefined, undefined,
        undefined, undefined, undefined,
        undefined );
    }
  }

  /**
   *
   * @param {TensorPlaceholder} aTensorPlaceholder  The tensor placeholder to be comapred.
   *
   * @return {boolean}
   *   Return true, if this tensor placeholder's height, width, channelCount are the same as aTensorPlaceholder. Note: the
   * .channelCount_lowerHalf and .channelCount_higherHalf are not compared.
   */
  is_height_width_channelCount_same_byTensorPlaceholder( aTensorPlaceholder ) {
    if (   ( this.height != aTensorPlaceholder.height )
        || ( this.width != aTensorPlaceholder.width )
        || ( this.channelCount != aTensorPlaceholder.channelCount )

        //|| ( this.channelCount_lowerHalf != aTensorPlaceholder.channelCount_lowerHalf )
        //|| ( this.channelCount_higherHalf != aTensorPlaceholder.channelCount_higherHalf )
       )
      return false;

    return true;
  }

}

/**
 * Providing TensorPlaceholder.Base
 *
 */
class BasePool extends Pool.Root {

  constructor() {
    super( Base, Base.setAsConstructor );
  }

//!!! (2022/06/22 Remarked) Base.setAsConstructor() should be enough.
//   /**
//    * @param {Base} this
//    *   The TensorPlaceholder.Base object to be initialized.
//    *
//    * @return {Base}
//    *   Return the this object.
//    */
//   static setAsConstructor() {
//     this.setAsConstructor();
//     return this;
//   }

}

/**
 * Used as default TensorPlaceholder.Base provider.
 */
BasePool.Singleton = new BasePool();
<|endoftext|>


<commit_before>/*! NProgress (c) 2013, Rico Sta. Cruz
 *  http://ricostacruz.com/nprogress */

;(function(factory) {

  if (typeof module === 'function') {
    module.exports = factory();
  } else if (typeof define === 'function' && define.amd) {
    define(function() {
      return factory();
    });
  } else {
    this.NProgress = factory();
  }

})(function() {
  var NProgress = {};

  NProgress.version = '0.1.2';

  var Settings = NProgress.settings = {
    minimum: 0.08,
    easing: 'ease',
    positionUsing: '',
    speed: 200,
    trickle: true,
    trickleRate: 0.02,
    trickleSpeed: 800,
    showSpinner: true,
    barSelector: '[role="bar"]',
    spinnerSelector: '[role="spinner"]',
    template: '

'
  };

  /**
   * Updates configuration.
   *
   *     NProgress.configure({
   *       minimum: 0.1
   *     });
   */
  NProgress.configure = function(options) {
    var key, value;
    for (key in options) {
      value = options[key];
      if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
    }

    return this;
  };

  /**
   * Last number.
   */

  NProgress.status = null;

  /**
   * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
   *
   *     NProgress.set(0.4);
   *     NProgress.set(1.0);
   */

  NProgress.set = function(n) {
    var started = NProgress.isStarted();

    n = clamp(n, Settings.minimum, 1);
    NProgress.status = (n === 1 ? null : n);

    var progress = NProgress.render(!started),
        bar      = progress.querySelector(Settings.barSelector),
        speed    = Settings.speed,
        ease     = Settings.easing;

    progress.offsetWidth; /* Repaint */

    queue(function(next) {
      // Set positionUsing if it hasn't already been set
      if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();

      // Add transition
      css(bar, barPositionCSS(n, speed, ease));

      if (n === 1) {
        // Fade out
        css(progress, {
          transition: 'none',
          opacity: 1
        });
        progress.offsetWidth; /* Repaint */

        setTimeout(function() {
          css(progress, {
            transition: 'all ' + speed + 'ms linear',
            opacity: 0
          });
          setTimeout(function() {
            NProgress.remove();
            next();
          }, speed);
        }, speed);
      } else {
        setTimeout(next, speed);
      }
    });

    return this;
  };

  NProgress.isStarted = function() {
    return typeof NProgress.status === 'number';
  };

  /**
   * Shows the progress bar.
   * This is the same as setting the status to 0%, except that it doesn't go backwards.
   *
   *     NProgress.start();
   *
   */
  NProgress.start = function() {
    if (!NProgress.status) NProgress.set(0);

    var work = function() {
      setTimeout(function() {
        if (!NProgress.status) return;
        NProgress.trickle();
        work();
      }, Settings.trickleSpeed);
    };

    if (Settings.trickle) work();

    return this;
  };

  /**
   * Hides the progress bar.
   * This is the *sort of* the same as setting the status to 100%, with the
   * difference being `done()` makes some placebo effect of some realistic motion.
   *
   *     NProgress.done();
   *
   * If `true` is passed, it will show the progress bar even if its hidden.
   *
   *     NProgress.done(true);
   */

  NProgress.done = function(force) {
    if (!force && !NProgress.status) return this;

    return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
  };

  /**
   * Increments by a random amount.
   */

  NProgress.inc = function(amount) {
    var n = NProgress.status;

    if (!n) {
      return NProgress.start();
    } else {
      if (typeof amount !== 'number') {
        amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
      }

      n = clamp(n + amount, 0, 0.994);
      return NProgress.set(n);
    }
  };

  NProgress.trickle = function() {
    return NProgress.inc(Math.random() * Settings.trickleRate);
  };

  /**
   * (Internal) renders the progress bar markup based on the `template`
   * setting.
   */

  NProgress.render = function(fromStart) {
    if (NProgress.isRendered()) return document.getElementById('nprogress');

    addClass(document.documentElement, 'nprogress-busy');

    var progress = document.createElement('div');
    progress.id = 'nprogress';
    progress.innerHTML = Settings.template;

    var bar      = progress.querySelector(Settings.barSelector),
        perc     = fromStart ? '-100' : toBarPerc(NProgress.status || 0),
        spinner;

    css(bar, {
      transition: 'all 0 linear',
      transform: 'translate3d(' + perc + '%,0,0)'
    });

    if (!Settings.showSpinner) {
      spinner = progress.querySelector(Settings.spinnerSelector);
      spinner && removeElement(spinner);
    }

    document.body.appendChild(progress);
    return progress;
  };

  /**
   * Removes the element. Opposite of render().
   */

  NProgress.remove = function() {
    removeClass(document.documentElement, 'nprogress-busy');
    var progress = document.getElementById('nprogress');
    progress && removeElement(progress);
  };

  /**
   * Checks if the progress bar is rendered.
   */

  NProgress.isRendered = function() {
    return !!document.getElementById('nprogress');
  };

  /**
   * Determine which positioning CSS rule to use.
   */

  NProgress.getPositioningCSS = function() {
    // Sniff on document.body.style
    var bodyStyle = document.body.style;

    // Sniff prefixes
    var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
                       ('MozTransform' in bodyStyle) ? 'Moz' :
                       ('msTransform' in bodyStyle) ? 'ms' :
                       ('OTransform' in bodyStyle) ? 'O' : '';

    if (vendorPrefix + 'Perspective' in bodyStyle) {
      // Modern browsers with 3D support, e.g. Webkit, IE10
      return 'translate3d';
    } else if (vendorPrefix + 'Transform' in bodyStyle) {
      // Browsers without 3D support, e.g. IE9
      return 'translate';
    } else {
      // Browsers without translate() support, e.g. IE7-8
      return 'margin';
    }
  };

  /**
   * Helpers
   */

  function clamp(n, min, max) {
    if (n < min) return min;
    if (n > max) return max;
    return n;
  }

  /**
   * (Internal) converts a percentage (`0..1`) to a bar translateX
   * percentage (`-100%..0%`).
   */

  function toBarPerc(n) {
    return (-1 + n) * 100;
  }

  /**
   * (Internal) returns the correct CSS for changing the bar's
   * position given an n percentage, and speed and ease from Settings
   */

  function barPositionCSS(n, speed, ease) {
    var barCSS;

    if (Settings.positionUsing === 'translate3d') {
      barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
    } else if (Settings.positionUsing === 'translate') {
      barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
    } else {
      barCSS = { 'margin-left': toBarPerc(n)+'%' };
    }

    barCSS.transition = 'all '+speed+'ms '+ease;

    return barCSS;
  }

  /**
   * (Internal) Queues a function to be executed.
   */

  var queue = (function() {
    var pending = [];

    function next() {
      var fn = pending.shift();
      if (fn) {
        fn(next);
      }
    }

    return function(fn) {
      pending.push(fn);
      if (pending.length == 1) next();
    };
  })();

  /**
   * (Internal) Applies css properties to an element, similar to the jQuery
   * css method.
   *
   * While this helper does assist with vendor prefixed property names, it
   * does not perform any manipulation of values prior to setting styles.
   */

  var css = (function() {
    var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ],
        cssProps    = {};

    function camelCase(string) {
      return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) {
        return letter.toUpperCase();
      });
    }

    function getVendorProp(name) {
      var style = document.body.style;
      if (name in style) return name;

      var i = cssPrefixes.length,
          capName = name.charAt(0).toUpperCase() + name.slice(1),
          vendorName;
      while (i--) {
        vendorName = cssPrefixes[i] + capName;
        if (vendorName in style) return vendorName;
      }

      return name;
    }

    function getStyleProp(name) {
      name = camelCase(name);
      return cssProps[name] || (cssProps[name] = getVendorProp(name));
    }

    function applyCss(element, prop, value) {
      prop = getStyleProp(prop);
      element.style[prop] = value;
    }

    return function(element, properties) {
      var args = arguments,
          prop,
          value;

      if (args.length == 2) {
        for (prop in properties) {
          value = properties[prop];
          if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value);
        }
      } else {
        applyCss(element, args[1], args[2]);
      }
    }
  })();

  /**
   * (Internal) Determines if an element or space separated list of class names contains a class name.
   */

  function hasClass(element, name) {
    var list = typeof element == 'string' ? element : classList(element);
    return list.indexOf(' ' + name + ' ') >= 0;
  }

  /**
   * (Internal) Adds a class to an element.
   */

  function addClass(element, name) {
    var oldList = classList(element),
        newList = oldList + name;

    if (hasClass(oldList, name)) return;

    // Trim the opening space.
    element.className = newList.substring(1);
  }

  /**
   * (Internal) Removes a class from an element.
   */

  function removeClass(element, name) {
    var oldList = classList(element),
        newList;

    if (!hasClass(element, name)) return;

    // Replace the class name.
    newList = oldList.replace(' ' + name + ' ', ' ');

    // Trim the opening and closing spaces.
    element.className = newList.substring(1, newList.length - 1);
  }

  /**
   * (Internal) Gets a space separated list of the class names on the element.
   * The list is wrapped with a single space on each end to facilitate finding
   * matches within the list.
   */

  function classList(element) {
    return (' ' + (element.className || '') + ' ').replace(/\s+/gi, ' ');
  }

  /**
   * (Internal) Removes an element from the DOM.
   */

  function removeElement(element) {
    element && element.parentNode && element.parentNode.removeChild(element);
  }

  return NProgress;
});

<commit_msg>simplify AMD definition<commit_after>/*! NProgress (c) 2013, Rico Sta. Cruz
 *  http://ricostacruz.com/nprogress */

;(function(factory) {

  if (typeof module === 'function') {
    module.exports = factory();
  } else if (typeof define === 'function' && define.amd) {
    define(factory);
  } else {
    this.NProgress = factory();
  }

})(function() {
  var NProgress = {};

  NProgress.version = '0.1.2';

  var Settings = NProgress.settings = {
    minimum: 0.08,
    easing: 'ease',
    positionUsing: '',
    speed: 200,
    trickle: true,
    trickleRate: 0.02,
    trickleSpeed: 800,
    showSpinner: true,
    barSelector: '[role="bar"]',
    spinnerSelector: '[role="spinner"]',
    template: '

'
  };

  /**
   * Updates configuration.
   *
   *     NProgress.configure({
   *       minimum: 0.1
   *     });
   */
  NProgress.configure = function(options) {
    var key, value;
    for (key in options) {
      value = options[key];
      if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
    }

    return this;
  };

  /**
   * Last number.
   */

  NProgress.status = null;

  /**
   * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
   *
   *     NProgress.set(0.4);
   *     NProgress.set(1.0);
   */

  NProgress.set = function(n) {
    var started = NProgress.isStarted();

    n = clamp(n, Settings.minimum, 1);
    NProgress.status = (n === 1 ? null : n);

    var progress = NProgress.render(!started),
        bar      = progress.querySelector(Settings.barSelector),
        speed    = Settings.speed,
        ease     = Settings.easing;

    progress.offsetWidth; /* Repaint */

    queue(function(next) {
      // Set positionUsing if it hasn't already been set
      if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();

      // Add transition
      css(bar, barPositionCSS(n, speed, ease));

      if (n === 1) {
        // Fade out
        css(progress, {
          transition: 'none',
          opacity: 1
        });
        progress.offsetWidth; /* Repaint */

        setTimeout(function() {
          css(progress, {
            transition: 'all ' + speed + 'ms linear',
            opacity: 0
          });
          setTimeout(function() {
            NProgress.remove();
            next();
          }, speed);
        }, speed);
      } else {
        setTimeout(next, speed);
      }
    });

    return this;
  };

  NProgress.isStarted = function() {
    return typeof NProgress.status === 'number';
  };

  /**
   * Shows the progress bar.
   * This is the same as setting the status to 0%, except that it doesn't go backwards.
   *
   *     NProgress.start();
   *
   */
  NProgress.start = function() {
    if (!NProgress.status) NProgress.set(0);

    var work = function() {
      setTimeout(function() {
        if (!NProgress.status) return;
        NProgress.trickle();
        work();
      }, Settings.trickleSpeed);
    };

    if (Settings.trickle) work();

    return this;
  };

  /**
   * Hides the progress bar.
   * This is the *sort of* the same as setting the status to 100%, with the
   * difference being `done()` makes some placebo effect of some realistic motion.
   *
   *     NProgress.done();
   *
   * If `true` is passed, it will show the progress bar even if its hidden.
   *
   *     NProgress.done(true);
   */

  NProgress.done = function(force) {
    if (!force && !NProgress.status) return this;

    return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
  };

  /**
   * Increments by a random amount.
   */

  NProgress.inc = function(amount) {
    var n = NProgress.status;

    if (!n) {
      return NProgress.start();
    } else {
      if (typeof amount !== 'number') {
        amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
      }

      n = clamp(n + amount, 0, 0.994);
      return NProgress.set(n);
    }
  };

  NProgress.trickle = function() {
    return NProgress.inc(Math.random() * Settings.trickleRate);
  };

  /**
   * (Internal) renders the progress bar markup based on the `template`
   * setting.
   */

  NProgress.render = function(fromStart) {
    if (NProgress.isRendered()) return document.getElementById('nprogress');

    addClass(document.documentElement, 'nprogress-busy');

    var progress = document.createElement('div');
    progress.id = 'nprogress';
    progress.innerHTML = Settings.template;

    var bar      = progress.querySelector(Settings.barSelector),
        perc     = fromStart ? '-100' : toBarPerc(NProgress.status || 0),
        spinner;

    css(bar, {
      transition: 'all 0 linear',
      transform: 'translate3d(' + perc + '%,0,0)'
    });

    if (!Settings.showSpinner) {
      spinner = progress.querySelector(Settings.spinnerSelector);
      spinner && removeElement(spinner);
    }

    document.body.appendChild(progress);
    return progress;
  };

  /**
   * Removes the element. Opposite of render().
   */

  NProgress.remove = function() {
    removeClass(document.documentElement, 'nprogress-busy');
    var progress = document.getElementById('nprogress');
    progress && removeElement(progress);
  };

  /**
   * Checks if the progress bar is rendered.
   */

  NProgress.isRendered = function() {
    return !!document.getElementById('nprogress');
  };

  /**
   * Determine which positioning CSS rule to use.
   */

  NProgress.getPositioningCSS = function() {
    // Sniff on document.body.style
    var bodyStyle = document.body.style;

    // Sniff prefixes
    var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
                       ('MozTransform' in bodyStyle) ? 'Moz' :
                       ('msTransform' in bodyStyle) ? 'ms' :
                       ('OTransform' in bodyStyle) ? 'O' : '';

    if (vendorPrefix + 'Perspective' in bodyStyle) {
      // Modern browsers with 3D support, e.g. Webkit, IE10
      return 'translate3d';
    } else if (vendorPrefix + 'Transform' in bodyStyle) {
      // Browsers without 3D support, e.g. IE9
      return 'translate';
    } else {
      // Browsers without translate() support, e.g. IE7-8
      return 'margin';
    }
  };

  /**
   * Helpers
   */

  function clamp(n, min, max) {
    if (n < min) return min;
    if (n > max) return max;
    return n;
  }

  /**
   * (Internal) converts a percentage (`0..1`) to a bar translateX
   * percentage (`-100%..0%`).
   */

  function toBarPerc(n) {
    return (-1 + n) * 100;
  }

  /**
   * (Internal) returns the correct CSS for changing the bar's
   * position given an n percentage, and speed and ease from Settings
   */

  function barPositionCSS(n, speed, ease) {
    var barCSS;

    if (Settings.positionUsing === 'translate3d') {
      barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
    } else if (Settings.positionUsing === 'translate') {
      barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
    } else {
      barCSS = { 'margin-left': toBarPerc(n)+'%' };
    }

    barCSS.transition = 'all '+speed+'ms '+ease;

    return barCSS;
  }

  /**
   * (Internal) Queues a function to be executed.
   */

  var queue = (function() {
    var pending = [];

    function next() {
      var fn = pending.shift();
      if (fn) {
        fn(next);
      }
    }

    return function(fn) {
      pending.push(fn);
      if (pending.length == 1) next();
    };
  })();

  /**
   * (Internal) Applies css properties to an element, similar to the jQuery
   * css method.
   *
   * While this helper does assist with vendor prefixed property names, it
   * does not perform any manipulation of values prior to setting styles.
   */

  var css = (function() {
    var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ],
        cssProps    = {};

    function camelCase(string) {
      return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) {
        return letter.toUpperCase();
      });
    }

    function getVendorProp(name) {
      var style = document.body.style;
      if (name in style) return name;

      var i = cssPrefixes.length,
          capName = name.charAt(0).toUpperCase() + name.slice(1),
          vendorName;
      while (i--) {
        vendorName = cssPrefixes[i] + capName;
        if (vendorName in style) return vendorName;
      }

      return name;
    }

    function getStyleProp(name) {
      name = camelCase(name);
      return cssProps[name] || (cssProps[name] = getVendorProp(name));
    }

    function applyCss(element, prop, value) {
      prop = getStyleProp(prop);
      element.style[prop] = value;
    }

    return function(element, properties) {
      var args = arguments,
          prop,
          value;

      if (args.length == 2) {
        for (prop in properties) {
          value = properties[prop];
          if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value);
        }
      } else {
        applyCss(element, args[1], args[2]);
      }
    }
  })();

  /**
   * (Internal) Determines if an element or space separated list of class names contains a class name.
   */

  function hasClass(element, name) {
    var list = typeof element == 'string' ? element : classList(element);
    return list.indexOf(' ' + name + ' ') >= 0;
  }

  /**
   * (Internal) Adds a class to an element.
   */

  function addClass(element, name) {
    var oldList = classList(element),
        newList = oldList + name;

    if (hasClass(oldList, name)) return;

    // Trim the opening space.
    element.className = newList.substring(1);
  }

  /**
   * (Internal) Removes a class from an element.
   */

  function removeClass(element, name) {
    var oldList = classList(element),
        newList;

    if (!hasClass(element, name)) return;

    // Replace the class name.
    newList = oldList.replace(' ' + name + ' ', ' ');

    // Trim the opening and closing spaces.
    element.className = newList.substring(1, newList.length - 1);
  }

  /**
   * (Internal) Gets a space separated list of the class names on the element.
   * The list is wrapped with a single space on each end to facilitate finding
   * matches within the list.
   */

  function classList(element) {
    return (' ' + (element.className || '') + ' ').replace(/\s+/gi, ' ');
  }

  /**
   * (Internal) Removes an element from the DOM.
   */

  function removeElement(element) {
    element && element.parentNode && element.parentNode.removeChild(element);
  }

  return NProgress;
});

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, window, PathUtils */

define(function (require, exports, module) {
    "use strict";

    var FileSystemError = require("filesystem/FileSystemError"),
        FileSystemStats = require("filesystem/FileSystemStats"),
        AjaxFileSystem  = require("filesystem/impls/demo/AjaxFileSystem");

    // Brackets uses FileSystem to read from various internal paths that are not in the user's project storage. We
    // redirect core-extension access to a simple $.ajax() to read from the source code location we're running from,
    // and for now we ignore we possibility of user-installable extensions or persistent user preferences.
    var CORE_EXTENSIONS_PREFIX = PathUtils.directory(window.location.href) + "extensions/default/";
//    var USER_EXTENSIONS_PREFIX = "/.brackets.user.extensions$/";
//    var CONFIG_PREFIX = "/.$brackets.config$/";

    var fs = new WebDAV.Fs('http://webdav-' + window.location.host);
    WebDAV.useCredentials = true;

    function _startsWith(path, prefix) {
        return (path.substr(0, prefix.length) === prefix);
    }

    function _stripTrailingSlash(path) {
        return path[path.length - 1] === "/" ? path.substr(0, path.length - 1) : path;
    }

    function _nameFromPath(path) {
        var segments = _stripTrailingSlash(path).split("/");
        return segments[segments.length - 1];
    }

    function _makeStat(webdavFile) {
        return {
            isFile: webdavFile.type === 'file',
            mtime: webdavFile.mtime,
            hash: webdavFile.size * webdavFile.size * webdavFile.mtime.getTime(),
            size: webdavFile.size
        };
    }

    function _getFile(path, callback) {
        var f = fs.file(path);

        f.propfind(function(props) {
            if (!f.exists) {
                callback(FileSystemError.NOT_FOUND);
            } else {
                callback(f);
            }
        });
    }

    function stat(path, callback) {
        if (_startsWith(path, CORE_EXTENSIONS_PREFIX)) {
            AjaxFileSystem.stat(path, callback);
            return;
        }

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                var stat = _makeStat(f);
                callback(null, stat);
            }
        });
    }

    function exists(path, callback) {
        stat(path, function (err) {
            if (err) {
                callback(null, false);
            } else {
                callback(null, true);
            }
        });
    }

    function readdir(path, callback) {
        if (_startsWith(path, CORE_EXTENSIONS_PREFIX)) {
            callback("Directory listing unavailable: " + path);
            return;
        }

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.children(function(children) {
                    var names = [];
                    var stats = [];
                    children.forEach(function (child) {
                        names.push(child.name);
                        stats.push(_makeStat(child));
                    });
                    callback(null, names, stats);
                });
            }
        });
    }

    function mkdir(path, mode, callback) {
        console.log("Make directory: " + path + " [mode " + mode + "]");

        if (typeof mode === "function") {
            callback = mode;
            mode = parseInt("0755", 8);
        }

        // TODO: add support for setting the mode
        var f = fs.dir(path);
        f.mkdir(function(data, status) {
            if (status >= 300) {
                // TODO: better error handling
                callback(FileSystemError.UNKNOWN);
            } else {
                stat(path, function (err, stat) {
                    callback(err, stat);
                });
            }
        });
    }

    function rename(oldPath, newPath, callback) {
        console.log("Rename file: " + oldPath + " -> " + newPath);

        _getFile(oldPath, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.mv(newPath, function(data, status) {
                    if (status >= 300) {
                        // TODO: better error handling
                        callback(FileSystemError.UNKNOWN);
                    } else {
                        callback(null, null);
                    }
                });
            }
        });
    }

    function readFile(path, options, callback) {
        console.log("Reading 'file': " + path);

        if (typeof options === "function") {
            callback = options;
        }

        if (_startsWith(path, CORE_EXTENSIONS_PREFIX)) {
            AjaxFileSystem.readFile(path, callback);
            return;
        }

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.read(function(data, status) {
                    var stat = _makeStat(f);
                    callback(null, data, stat);
                });
            }
        });
    }

    function writeFile(path, data, options, callback) {
        console.log("Write file: " + path + " [length " + data.length + "]");

        // TODO: Make use of the options for verifying writes
        _getFile(path, function(f) {
            var created = false;
            if (f === 'NotFound') {
                created = true;
                f = fs.file(path);
            } else if (typeof f === 'string') {
                callback(f);
                return
            }

            f.write(data, function(data, status) {
                if (status >= 300) {
                    // TODO: better error handling
                    callback(FileSystemError.UNKNOWN);
                } else {
                    stat(path, function (err, stat) {
                        callback(err, stat, created);
                    });
                }
            });
        });
    }

    function unlink(path, callback) {
        console.log("Unlink: " + path);

        // FIXME
        throw new Error();
    }

    function moveToTrash(path, callback) {
        console.log("Trash file: " + path);

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.rm(function(data, status) {
                    if (status >= 300) {
                        // TODO: better error handling
                        callback(FileSystemError.UNKNOWN);
                    } else {
                        callback();
                    }
                });
            }
        });
    }

    function initWatchers(changeCallback, offlineCallback) {
        // Ignore - since this FS is immutable, we're never going to call these
    }

    function watchPath(path, callback) {
        console.warn("File watching (watchPath) is not supported");
        callback();
    }

    function unwatchPath(path, callback) {
        console.warn("File watching (unwatchPath) is not supported");
        callback();
    }

    function unwatchAll(callback) {
        console.warn("File watching (unwatchAll) is not supported");
        callback();
    }

    function showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, callback) {
        // FIXME
        throw new Error();
    }

    function showSaveDialog(title, initialPath, proposedNewFilename, callback) {
        // FIXME
        throw new Error();
    }

    // Export public API
    exports.showOpenDialog  = showOpenDialog;
    exports.showSaveDialog  = showSaveDialog;
    exports.exists          = exists;
    exports.readdir         = readdir;
    exports.mkdir           = mkdir;
    exports.rename          = rename;
    exports.stat            = stat;
    exports.readFile        = readFile;
    exports.writeFile       = writeFile;
    exports.unlink          = unlink;
    exports.moveToTrash     = moveToTrash;
    exports.initWatchers    = initWatchers;
    exports.watchPath       = watchPath;
    exports.unwatchPath     = unwatchPath;
    exports.unwatchAll      = unwatchAll;

    exports.recursiveWatch    = true;
    exports.normalizeUNCPaths = false;
});<commit_msg>webdavfs fix for null read data<commit_after>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, window, PathUtils */

define(function (require, exports, module) {
    "use strict";

    var FileSystemError = require("filesystem/FileSystemError"),
        FileSystemStats = require("filesystem/FileSystemStats"),
        AjaxFileSystem  = require("filesystem/impls/demo/AjaxFileSystem");

    // Brackets uses FileSystem to read from various internal paths that are not in the user's project storage. We
    // redirect core-extension access to a simple $.ajax() to read from the source code location we're running from,
    // and for now we ignore we possibility of user-installable extensions or persistent user preferences.
    var CORE_EXTENSIONS_PREFIX = PathUtils.directory(window.location.href) + "extensions/default/";
//    var USER_EXTENSIONS_PREFIX = "/.brackets.user.extensions$/";
//    var CONFIG_PREFIX = "/.$brackets.config$/";

    var fs = new WebDAV.Fs('http://webdav-' + window.location.host);
    WebDAV.useCredentials = true;

    function _startsWith(path, prefix) {
        return (path.substr(0, prefix.length) === prefix);
    }

    function _stripTrailingSlash(path) {
        return path[path.length - 1] === "/" ? path.substr(0, path.length - 1) : path;
    }

    function _nameFromPath(path) {
        var segments = _stripTrailingSlash(path).split("/");
        return segments[segments.length - 1];
    }

    function _makeStat(webdavFile) {
        return {
            isFile: webdavFile.type === 'file',
            mtime: webdavFile.mtime,
            hash: webdavFile.size * webdavFile.size * webdavFile.mtime.getTime(),
            size: webdavFile.size
        };
    }

    function _getFile(path, callback) {
        var f = fs.file(path);

        f.propfind(function(props) {
            if (!f.exists) {
                callback(FileSystemError.NOT_FOUND);
            } else {
                callback(f);
            }
        });
    }

    function stat(path, callback) {
        if (_startsWith(path, CORE_EXTENSIONS_PREFIX)) {
            AjaxFileSystem.stat(path, callback);
            return;
        }

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                var stat = _makeStat(f);
                callback(null, stat);
            }
        });
    }

    function exists(path, callback) {
        stat(path, function (err) {
            if (err) {
                callback(null, false);
            } else {
                callback(null, true);
            }
        });
    }

    function readdir(path, callback) {
        if (_startsWith(path, CORE_EXTENSIONS_PREFIX)) {
            callback("Directory listing unavailable: " + path);
            return;
        }

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.children(function(children) {
                    var names = [];
                    var stats = [];
                    children.forEach(function (child) {
                        names.push(child.name);
                        stats.push(_makeStat(child));
                    });
                    callback(null, names, stats);
                });
            }
        });
    }

    function mkdir(path, mode, callback) {
        console.log("Make directory: " + path + " [mode " + mode + "]");

        if (typeof mode === "function") {
            callback = mode;
            mode = parseInt("0755", 8);
        }

        // TODO: add support for setting the mode
        var f = fs.dir(path);
        f.mkdir(function(data, status) {
            if (status >= 300) {
                // TODO: better error handling
                callback(FileSystemError.UNKNOWN);
            } else {
                stat(path, function (err, stat) {
                    callback(err, stat);
                });
            }
        });
    }

    function rename(oldPath, newPath, callback) {
        console.log("Rename file: " + oldPath + " -> " + newPath);

        _getFile(oldPath, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.mv(newPath, function(data, status) {
                    if (status >= 300) {
                        // TODO: better error handling
                        callback(FileSystemError.UNKNOWN);
                    } else {
                        callback(null, null);
                    }
                });
            }
        });
    }

    function readFile(path, options, callback) {
        console.log("Reading 'file': " + path);

        if (typeof options === "function") {
            callback = options;
        }

        if (_startsWith(path, CORE_EXTENSIONS_PREFIX)) {
            AjaxFileSystem.readFile(path, callback);
            return;
        }

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.read(function(data, status) {
                    var stat = _makeStat(f);
                    callback(null, data || '', stat);
                });
            }
        });
    }

    function writeFile(path, data, options, callback) {
        console.log("Write file: " + path + " [length " + data.length + "]");

        // TODO: Make use of the options for verifying writes
        _getFile(path, function(f) {
            var created = false;
            if (f === 'NotFound') {
                created = true;
                f = fs.file(path);
            } else if (typeof f === 'string') {
                callback(f);
                return
            }

            f.write(data, function(data, status) {
                if (status >= 300) {
                    // TODO: better error handling
                    callback(FileSystemError.UNKNOWN);
                } else {
                    stat(path, function (err, stat) {
                        callback(err, stat, created);
                    });
                }
            });
        });
    }

    function unlink(path, callback) {
        console.log("Unlink: " + path);

        // FIXME
        throw new Error();
    }

    function moveToTrash(path, callback) {
        console.log("Trash file: " + path);

        _getFile(path, function(f) {
            if (typeof f === 'string') {
                callback(f);
            } else {
                f.rm(function(data, status) {
                    if (status >= 300) {
                        // TODO: better error handling
                        callback(FileSystemError.UNKNOWN);
                    } else {
                        callback();
                    }
                });
            }
        });
    }

    function initWatchers(changeCallback, offlineCallback) {
        // Ignore - since this FS is immutable, we're never going to call these
    }

    function watchPath(path, callback) {
        console.warn("File watching (watchPath) is not supported");
        callback();
    }

    function unwatchPath(path, callback) {
        console.warn("File watching (unwatchPath) is not supported");
        callback();
    }

    function unwatchAll(callback) {
        console.warn("File watching (unwatchAll) is not supported");
        callback();
    }

    function showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, callback) {
        // FIXME
        throw new Error();
    }

    function showSaveDialog(title, initialPath, proposedNewFilename, callback) {
        // FIXME
        throw new Error();
    }

    // Export public API
    exports.showOpenDialog  = showOpenDialog;
    exports.showSaveDialog  = showSaveDialog;
    exports.exists          = exists;
    exports.readdir         = readdir;
    exports.mkdir           = mkdir;
    exports.rename          = rename;
    exports.stat            = stat;
    exports.readFile        = readFile;
    exports.writeFile       = writeFile;
    exports.unlink          = unlink;
    exports.moveToTrash     = moveToTrash;
    exports.initWatchers    = initWatchers;
    exports.watchPath       = watchPath;
    exports.unwatchPath     = unwatchPath;
    exports.unwatchAll      = unwatchAll;

    exports.recursiveWatch    = true;
    exports.normalizeUNCPaths = false;
});<|endoftext|>


<commit_before>var async = require('async'),
    phantom = require('phantom');

/*
phantom.create('--disk-cache=true', '--ignore-ssl-errors=true',
    '--load-images=false','--max-disk-cache-size=512',
    function (ph){
    ph.createPage(function(page){
        page.open('http://what.thedailywtf.com/',function(status){
            console.log(arguments);
            ph.exit();
        });
    });
})
*/

function create(cb){
    phantom.create('--disk-cache=true', '--ignore-ssl-errors=true',
        '--load-images=false','--max-disk-cache-size=512', cb);

}

function auth(page, username, password, cb){
    async.series([
        function(cb){
            page.open('http://what.thedailywtf.com/',function(){cb();});
        },function(cb){
            console.log(4);
            page.evaluate(function(username, password){
                $('button.login-button').click();
                $('#login-account-name').focus().val(username).blur().change();
                $('#login-account-password').focus().val(password).blur().change();
                $('div.modal-footer button.btn-primary').click();
            }, function(val){cb(null,val)}, username, password);
        },function(cb){
            console.log(5);
                setTimeout(function() {page.evaluate(function(){
                    $('div#current-username').click();
                    return document.querySelector('ul.user-dropdown-links a.user-activity-link').href
                },function(val){console.log(val);
                page.render('page.png','PNG',cb);
                });
            }, 3000);
        }],
        function(){
            console.log(6);

            (cb || function(){})()
        });
}

function post(page, url, text){
    //http://what.thedailywtf.com/t/signature-dev-guy-bot-thread/3031/49
}

create(function(ph){
    console.log(1);
    ph.createPage(function(page){
        console.log(2);
        auth(page, 'sockbot', 'sockbotsockbot', function(){
            ph.exit()
        })

    });
})
<commit_msg>auth sorted post attempt. failing for now some javascript issue<commit_after>var async = require('async'),
    phantom = require('phantom');

function create(cb){
    phantom.create('--disk-cache=true', '--ignore-ssl-errors=true',
        '--load-images=false','--max-disk-cache-size=512', cb);
}

function auth(page, username, password, cb){
    async.series([
        function(cb){
            page.open('http://what.thedailywtf.com/',function(){cb();});
        },function(cb){
            page.evaluate(function(username, password){
                $('button.login-button').click();
                $('#login-account-name').focus().val(username).blur().change();
                $('#login-account-password').focus().val(password).blur().change();
                $('div.modal-footer button.btn-primary').click();
            }, function(val){cb(null,val)}, username, password);
        },function(cb){
                setTimeout(function() {page.evaluate(function(){
                    $('div#current-username').click();
                    return document.querySelector('ul.user-dropdown-links a.user-activity-link').href
                },function(val){console.log(val);
                page.render('page.png','PNG',cb);
                });
            }, 3000);
        }],
        function(){
            (cb || function(){})();
        });
}

function post(page, url, text, cb){
    async.series([
       function(cb) {
           console.log(4);
           page.open(url, function() {
               console.log(4);
               cb();
           });
       },
       function(cb) {
           console.log(4);
           page.evaluate(function() {
               $('#topic-footer-buttons .btn-primary').click();
           }, function() {
               console.log(6);
               setTimeout(function() {
                   cb()
               }, 100);
           });
       },
       function(cb) {
           console.log(5);
           page.evaluate(function(text) {
               $('#wmd-input').text(text);
               $('.submit-panel .btn-primary').click();
           }, function(val) {
               console.log(val);
               //page.render('page.png', 'PNG', cb);
               cb();
           }, text)
       }
   ],
        function(){
            (cb || function(){})();
        });
}

create(function(ph){
    async.waterfall([
        function(cb){
            console.log(1);
            ph.createPage(function(page){cb(null,page);});
        }, function (page, cb){
            console.log(2);
            auth(page, 'sockbot', 'sockbotsockbot', function(){cb(null,page)});
        }, function(page, cb) {
            console.log(3);
            post(page, 'http://what.thedailywtf.com/t/signature-dev-guy-bot-thread/3031/49', 'This is a sockbot test post.

 Hello World!', cb);
        }, function(cb){
            console.log(4);
            ph.exit()
        }
    ]);
})
<|endoftext|>


<commit_before>/**
 * Created by Alexey Merzlikin on 14.04.2017.
 */

var de;
var dc = new DataCollector();

function onPolygonClosed(data) {
    de = new DataEntity();
    dc.addEntity(de);
    de.setPoints(data.slice(0, pointsList.length - 1));
}

function onBoolParamUpdate(name, isChecked) {
    var key = name.toString();
    de.setParams({[name]: isChecked});
}

function onObjectClassUpdate(value) {
    de.setParams({"class": value});
}

function onSelectParamUpdate(name, value) {
    de.setParams({[name]: value});
}

function onStringParamUpdate(name, value) {
    de.setParams({[name]: value});
}

function onSave() {
    dc.getJSON();
}<commit_msg>UIHandler no-undef issues fixed<commit_after>/**
 * Created by Alexey Merzlikin on 14.04.2017.
 */

var de;
/*global DataCollector*/
/*eslint no-undef: "error"*/
var dc = new DataCollector();

function onPolygonClosed(data) {
    /*global DataEntity*/
    /*eslint no-undef: "error"*/
    de = new DataEntity();
    dc.addEntity(de);
    /*global setPoints*/
    /*eslint no-undef: "error"*/
    de.setPoints(data.slice(0, pointsList.length - 1));
}

function onBoolParamUpdate(name, isChecked) {
    var key = name.toString();
    de.setParams({[name]: isChecked});
}

function onObjectClassUpdate(value) {
    de.setParams({"class": value});
}

function onSelectParamUpdate(name, value) {
    de.setParams({[name]: value});
}

function onStringParamUpdate(name, value) {
    de.setParams({[name]: value});
}

function onSave() {
    dc.getJSON();
}<|endoftext|>


<commit_before>'use strict';

module.exports = function(game, opts) {
  return new FullscreenPlugin(game, opts);
};
module.exports.pluginInfo = {
  loadAfter: ['voxel-keys']
};

function FullscreenPlugin(game, opts) {
  this.shell = game.shell;

  this.keys = game.plugins.get('voxel-keys');
  if (!this.keys) throw new Error('voxel-fullscreen requires voxel-keys');

  this.element = opts.element !== undefined ? opts.element : document.body;

  this.pendingRequest = false;

  this.enable();
}

FullscreenPlugin.prototype.enable = function() {
  // ensure the full screen is covered on WebKit browsers
  // https://github.com/deathcap/voxel-fullscreen/issues/1
  document.body.style.width = '100%';

  this.shell.bind('fullscreen', 'F11');
  //this.keys.down.on('fullscreen', this.onToggle = this.toggle.bind(this));
  document.addEventListener('keydown', this.onKeydown = this.keydown.bind(this));
  this.onChange = this.changed.bind(this);
  document.addEventListener('fullscreenchange', this.onChange);
  document.addEventListener('webkitfullscreenchange', this.onChange);
  document.addEventListener('mozfullscreenchange', this.onChange);
  document.addEventListener('MSFullscreenchange', this.onChange);
  document.addEventListener('click', this.onClick = this.click.bind(this));
};

FullscreenPlugin.prototype.disable = function() {
  document.removeEventListener('keydown', this.onKeydown);
  document.removeEventListener('click', this.onClick);
  document.removeEventListener('MSFullscreenchange', this.onChange);
  document.removeEventListener('mozfullscreenchange', this.onChange);
  document.removeEventListener('webkitfullscreenchange', this.onChange);
  document.removeEventListener('fullscreenchange', this.onChange);
  //this.keys.down.removeListener('fullscreen', this.onToggle);
  this.shell.unbind('fullscreen');
};

FullscreenPlugin.prototype.isFullscreen = function() {
  return !!(
    document.fullscreenElement ||
    document.webkitFullscreenElement ||
    document.mozFullScreenElement ||
    document.msFullscreenElement);
  //return document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen; // non-standard
};

FullscreenPlugin.prototype.keydown = function(ev) {
  var code = ev.keyCode;
  var bindingNames = this.keys.getBindingsNames(code);
  if (bindingNames.indexOf('fullscreen') == -1) return; // not us
  // TODO: does this event need to be debounced? repeatedly sent when key is down,
  // we could track keyup like voxel-keys to avoid repeat handling, but doesn't seem
  // to be a problem right now (if user really wants to hold F11 to keep toggling...)

  console.log('fullscreen keydown');
  this.toggle();
};

// try to enter or exit fullscreen
// note: if not called in response to user input, entering will be delayed until the next
// time the user clicks their mouse, which counts as required user interaction (browser feature)
FullscreenPlugin.prototype.toggle = function() {
  if (this.isFullscreen()) {
    console.log('fullscreen leaving');
    this.leave();
  } else {
    console.log('fullscreen entering');
    this.enter();
  }
};

FullscreenPlugin.prototype.enter = function() {
  // request might be denied: cleared in change(), or replayed in click()
  this.pendingRequest = true;

  var f =
    this.element.requestFullscreen ||
    this.element.requestFullScreen ||
    this.element.webkitRequestFullscreen ||
    this.element.webkitRequestFullScreen ||
    this.element.mozRequestFullscreen ||
    this.element.mozRequestFullScreen ||
    this.element.msRequestFullscreen;
  if (!f) throw new Error('no requestFullscreen found on '+this.element);

  f.call(this.element, this.element.ALLOW_KEYBOARD_INPUT)
};

FullscreenPlugin.prototype.leave = function() {
  var f =
    document.exitFullscreen ||
    document.cancelFullscreen ||
    document.cancelFullScreen ||
    document.webkitCancelFullscreen ||
    document.webkitCancelFullScreen ||
    document.mozCancelFullscreen ||
    document.mozCancelFullScreen ||
    document.msExitFullscreen;
  if (!f) throw new Error('no exitFullscreen found on documnet');

  f.call(document);

  // leaving fullscreen for some reason releases pointer lock,
  // but we can't re-request it here since it wasn't a mouse action :(
  //this.shell.pointerLock = true;
};

FullscreenPlugin.prototype.changed = function() {
  console.log('fullscreen changed', this.isFullscreen());
  if (this.isFullscreen() && this.pendingRequest) {
    this.pendingRequest = false; // fullfilled!
  }
};

FullscreenPlugin.prototype.click = function() {
  if (this.pendingRequest) {
    // this is our chance to try again
    this.enter();
  }
};
<commit_msg>Use static property ALLOW_KEYBOARD_INPUT on Element instead of this.element instance<commit_after>'use strict';

module.exports = function(game, opts) {
  return new FullscreenPlugin(game, opts);
};
module.exports.pluginInfo = {
  loadAfter: ['voxel-keys']
};

function FullscreenPlugin(game, opts) {
  this.shell = game.shell;

  this.keys = game.plugins.get('voxel-keys');
  if (!this.keys) throw new Error('voxel-fullscreen requires voxel-keys');

  this.element = opts.element !== undefined ? opts.element : document.body;

  this.pendingRequest = false;

  this.enable();
}

FullscreenPlugin.prototype.enable = function() {
  // ensure the full screen is covered on WebKit browsers
  // https://github.com/deathcap/voxel-fullscreen/issues/1
  document.body.style.width = '100%';

  this.shell.bind('fullscreen', 'F11');
  //this.keys.down.on('fullscreen', this.onToggle = this.toggle.bind(this));
  document.addEventListener('keydown', this.onKeydown = this.keydown.bind(this));
  this.onChange = this.changed.bind(this);
  document.addEventListener('fullscreenchange', this.onChange);
  document.addEventListener('webkitfullscreenchange', this.onChange);
  document.addEventListener('mozfullscreenchange', this.onChange);
  document.addEventListener('MSFullscreenchange', this.onChange);
  document.addEventListener('click', this.onClick = this.click.bind(this));
};

FullscreenPlugin.prototype.disable = function() {
  document.removeEventListener('keydown', this.onKeydown);
  document.removeEventListener('click', this.onClick);
  document.removeEventListener('MSFullscreenchange', this.onChange);
  document.removeEventListener('mozfullscreenchange', this.onChange);
  document.removeEventListener('webkitfullscreenchange', this.onChange);
  document.removeEventListener('fullscreenchange', this.onChange);
  //this.keys.down.removeListener('fullscreen', this.onToggle);
  this.shell.unbind('fullscreen');
};

FullscreenPlugin.prototype.isFullscreen = function() {
  return !!(
    document.fullscreenElement ||
    document.webkitFullscreenElement ||
    document.mozFullScreenElement ||
    document.msFullscreenElement);
  //return document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen; // non-standard
};

FullscreenPlugin.prototype.keydown = function(ev) {
  var code = ev.keyCode;
  var bindingNames = this.keys.getBindingsNames(code);
  if (bindingNames.indexOf('fullscreen') == -1) return; // not us
  // TODO: does this event need to be debounced? repeatedly sent when key is down,
  // we could track keyup like voxel-keys to avoid repeat handling, but doesn't seem
  // to be a problem right now (if user really wants to hold F11 to keep toggling...)

  console.log('fullscreen keydown');
  this.toggle();
};

// try to enter or exit fullscreen
// note: if not called in response to user input, entering will be delayed until the next
// time the user clicks their mouse, which counts as required user interaction (browser feature)
FullscreenPlugin.prototype.toggle = function() {
  if (this.isFullscreen()) {
    console.log('fullscreen leaving');
    this.leave();
  } else {
    console.log('fullscreen entering');
    this.enter();
  }
};

FullscreenPlugin.prototype.enter = function() {
  // request might be denied: cleared in change(), or replayed in click()
  this.pendingRequest = true;

  var f =
    this.element.requestFullscreen ||
    this.element.requestFullScreen ||
    this.element.webkitRequestFullscreen ||
    this.element.webkitRequestFullScreen ||
    this.element.mozRequestFullscreen ||
    this.element.mozRequestFullScreen ||
    this.element.msRequestFullscreen;
  if (!f) throw new Error('no requestFullscreen found on '+this.element);

  f.call(this.element, Element.ALLOW_KEYBOARD_INPUT)
};

FullscreenPlugin.prototype.leave = function() {
  var f =
    document.exitFullscreen ||
    document.cancelFullscreen ||
    document.cancelFullScreen ||
    document.webkitCancelFullscreen ||
    document.webkitCancelFullScreen ||
    document.mozCancelFullscreen ||
    document.mozCancelFullScreen ||
    document.msExitFullscreen;
  if (!f) throw new Error('no exitFullscreen found on documnet');

  f.call(document);

  // leaving fullscreen for some reason releases pointer lock,
  // but we can't re-request it here since it wasn't a mouse action :(
  //this.shell.pointerLock = true;
};

FullscreenPlugin.prototype.changed = function() {
  console.log('fullscreen changed', this.isFullscreen());
  if (this.isFullscreen() && this.pendingRequest) {
    this.pendingRequest = false; // fullfilled!
  }
};

FullscreenPlugin.prototype.click = function() {
  if (this.pendingRequest) {
    // this is our chance to try again
    this.enter();
  }
};
<|endoftext|>