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

var when = require("when");
var crypto = require('crypto');
var settings;
var log;

var encryptedCredentials = null;
var credentialCache = {};
var credentialsDef = {};
var dirty = false;

var removeDefaultKey = false;
var encryptionEnabled = null;
var encryptionAlgorithm = "aes-256-ctr";
var encryptionKey;

function decryptCredentials(key,credentials) {
    var creds = credentials["$"];
    var initVector = new Buffer(creds.substring(0, 32),'hex');
    creds = creds.substring(32);
    var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
    var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
    return JSON.parse(decrypted);
}

var api = module.exports = {
    init: function(runtime) {
        log = runtime.log;
        settings = runtime.settings;
        dirty = false;
        credentialCache = {};
        credentialsDef = {};
        encryptionEnabled = null;
    },

    /**
     * Sets the credentials from storage.
     */
    load: function (credentials) {
        dirty = false;
        /*
          - if encryptionEnabled === null, check the current configuration
        */
        var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
        var setupEncryptionPromise = when.resolve();
        if (encryptionEnabled === null) {
            var defaultKey;
            try {
                defaultKey = settings.get('_credentialSecret');
            } catch(err) {
            }
            if (defaultKey) {
                defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
            }
            var userKey;
            try {
                userKey = settings.get('credentialSecret');
            } catch(err) {
                userKey = false;
            }
            if (userKey === false) {
                log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
                // User has disabled encryption
                encryptionEnabled = false;
                // Check if we have a generated _credSecret to decrypt with and remove
                if (defaultKey) {
                    log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
                    if (credentialsEncrypted) {
                        try {
                            credentials = decryptCredentials(defaultKey,credentials)
                        } catch(err) {
                            credentials = {};
                            log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                        }
                    }
                    dirty = true;
                    removeDefaultKey = true;
                }
            } else if (typeof userKey === 'string') {
                log.debug("red/runtime/nodes/credentials.load : user provided key");
                // User has provided own encryption key, get the 32-byte hash of it
                encryptionKey = crypto.createHash('sha256').update(userKey).digest();
                encryptionEnabled = true;

                if (defaultKey) {
                    log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
                    // User has provided their own key, but we already have a default key
                    // Decrypt using default key
                    if (credentialsEncrypted) {
                        try {
                            credentials = decryptCredentials(defaultKey,credentials)
                        } catch(err) {
                            credentials = {};
                            log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                        }
                    }
                    dirty = true;
                    removeDefaultKey = true;
                }
            } else {
                log.debug("red/runtime/nodes/credentials.load : no user key present");
                // User has not provide their own key
                encryptionKey = defaultKey;
                encryptionEnabled = true;
                if (encryptionKey === undefined) {
                    log.debug("red/runtime/nodes/credentials.load : no default key present - generating one");
                    // No user-provided key, no generated key
                    // Generate a new key
                    defaultKey = crypto.randomBytes(32).toString('hex');
                    try {
                        setupEncryptionPromise = settings.set('_credentialSecret',defaultKey);
                        encryptionKey = crypto.createHash('sha256').update(defaultKey).digest();
                    } catch(err) {
                        log.debug("red/runtime/nodes/credentials.load : settings unavailable - disabling encryption");
                        // Settings unavailable
                        encryptionEnabled = false;
                        encryptionKey = null;
                    }
                    dirty = true;
                } else {
                    log.debug("red/runtime/nodes/credentials.load : using default key");
                }
            }
        }
        return setupEncryptionPromise.then(function() {
            if (credentials.hasOwnProperty("$")) {
                // These are encrypted credentials
                try {
                    credentialCache = decryptCredentials(encryptionKey,credentials)
                } catch(err) {
                    credentialCache = {};
                    dirty = true;
                    log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                }
            } else {
                credentialCache = credentials;
            }
        });
    },

    /**
     * Adds a set of credentials for the given node id.
     * @param id the node id for the credentials
     * @param creds an object of credential key/value pairs
     * @return a promise for backwards compatibility TODO: can this be removed?
     */
    add: function (id, creds) {
        credentialCache[id] = creds;
        dirty = true;
        return when.resolve();
    },

    /**
     * Gets the credentials for the given node id.
     * @param id the node id for the credentials
     * @return the credentials
     */
    get: function (id) {
        return credentialCache[id];
    },

    /**
     * Deletes the credentials for the given node id.
     * @param id the node id for the credentials
     * @return a promise for the saving of credentials to storage
     */
    delete: function (id) {
        delete credentialCache[id];
        dirty = true;
    },

    /**
     * Deletes any credentials for nodes that no longer exist
     * @param config a flow config
     * @return a promise for the saving of credentials to storage
     */
    clean: function (config) {
        var existingIds = {};
        config.forEach(function(n) {
            existingIds[n.id] = true;
            if (n.credentials) {
                api.extract(n);
            }
        });
        var deletedCredentials = false;
        for (var c in credentialCache) {
            if (credentialCache.hasOwnProperty(c)) {
                if (!existingIds[c]) {
                    deletedCredentials = true;
                    delete credentialCache[c];
                }
            }
        }
        if (deletedCredentials) {
            dirty = true;
        }
        return when.resolve();
    },

    /**
     * Registers a node credential definition.
     * @param type the node type
     * @param definition the credential definition
     */
    register: function (type, definition) {
        var dashedType = type.replace(/\s+/g, '-');
        credentialsDef[dashedType] = definition;
    },

    /**
     * Extracts and stores any credential updates in the provided node.
     * The provided node may have a .credentials property that contains
     * new credentials for the node.
     * This function loops through the credentials in the definition for
     * the node-type and applies any of the updates provided in the node.
     *
     * This function does not save the credentials to disk as it is expected
     * to be called multiple times when a new flow is deployed.
     *
     * @param node the node to extract credentials from
     */
    extract: function(node) {
        var nodeID = node.id;
        var nodeType = node.type;
        var newCreds = node.credentials;
        if (newCreds) {
            delete node.credentials;
            var savedCredentials = credentialCache[nodeID] || {};
            var dashedType = nodeType.replace(/\s+/g, '-');
            var definition = credentialsDef[dashedType];
            if (!definition) {
                log.warn(log._("nodes.credentials.not-registered",{type:nodeType}));
                return;
            }

            for (var cred in definition) {
                if (definition.hasOwnProperty(cred)) {
                    if (newCreds[cred] === undefined) {
                        continue;
                    }
                    if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') {
                        continue;
                    }
                    if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) {
                        delete savedCredentials[cred];
                        continue;
                    }
                    savedCredentials[cred] = newCreds[cred];
                }
            }
            credentialCache[nodeID] = savedCredentials;
            dirty = true;
        }
    },

    /**
     * Gets the credential definition for the given node type
     * @param type the node type
     * @return the credential definition
     */
    getDefinition: function (type) {
        return credentialsDef[type];
    },

    dirty: function() {
        return dirty;
    },

    export: function() {
        var result = credentialCache;
        if (dirty && encryptionEnabled) {
            try {
                log.debug("red/runtime/nodes/credentials.export : encrypting");
                var initVector = crypto.randomBytes(16);
                var cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, initVector);
                result = {"$":initVector.toString('hex') + cipher.update(JSON.stringify(credentialCache), 'utf8', 'base64') + cipher.final('base64')};
            } catch(err) {
                log.warn(log._("nodes.credentials.error-saving",{message:err.toString()}))
            }
        }
        dirty = false;
        if (removeDefaultKey) {
            log.debug("red/runtime/nodes/credentials.export : removing unused default key");
            return settings.delete('_credentialSecret').then(function() {
                removeDefaultKey = false;
                return result;
            })
        } else {
            return when.resolve(result);
        }
    }
}
<commit_msg>Avoid reencyrpting credentials if they haven't changed<commit_after>/**
 * Copyright 2014, 2016 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var when = require("when");
var crypto = require('crypto');
var settings;
var log;

var encryptedCredentials = null;
var credentialCache = {};
var credentialsDef = {};
var dirty = false;

var removeDefaultKey = false;
var encryptionEnabled = null;
var encryptionAlgorithm = "aes-256-ctr";
var encryptionKey;

function decryptCredentials(key,credentials) {
    var creds = credentials["$"];
    var initVector = new Buffer(creds.substring(0, 32),'hex');
    creds = creds.substring(32);
    var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
    var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
    return JSON.parse(decrypted);
}

var api = module.exports = {
    init: function(runtime) {
        log = runtime.log;
        settings = runtime.settings;
        dirty = false;
        credentialCache = {};
        credentialsDef = {};
        encryptionEnabled = null;
    },

    /**
     * Sets the credentials from storage.
     */
    load: function (credentials) {
        dirty = false;
        /*
          - if encryptionEnabled === null, check the current configuration
        */
        var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
        var setupEncryptionPromise = when.resolve();
        if (encryptionEnabled === null) {
            var defaultKey;
            try {
                defaultKey = settings.get('_credentialSecret');
            } catch(err) {
            }
            if (defaultKey) {
                defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
            }
            var userKey;
            try {
                userKey = settings.get('credentialSecret');
            } catch(err) {
                userKey = false;
            }
            if (userKey === false) {
                log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
                // User has disabled encryption
                encryptionEnabled = false;
                // Check if we have a generated _credSecret to decrypt with and remove
                if (defaultKey) {
                    log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
                    if (credentialsEncrypted) {
                        try {
                            credentials = decryptCredentials(defaultKey,credentials)
                        } catch(err) {
                            credentials = {};
                            log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                        }
                    }
                    dirty = true;
                    removeDefaultKey = true;
                }
            } else if (typeof userKey === 'string') {
                log.debug("red/runtime/nodes/credentials.load : user provided key");
                // User has provided own encryption key, get the 32-byte hash of it
                encryptionKey = crypto.createHash('sha256').update(userKey).digest();
                encryptionEnabled = true;

                if (defaultKey) {
                    log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
                    // User has provided their own key, but we already have a default key
                    // Decrypt using default key
                    if (credentialsEncrypted) {
                        try {
                            credentials = decryptCredentials(defaultKey,credentials)
                        } catch(err) {
                            credentials = {};
                            log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                        }
                    }
                    dirty = true;
                    removeDefaultKey = true;
                }
            } else {
                log.debug("red/runtime/nodes/credentials.load : no user key present");
                // User has not provide their own key
                encryptionKey = defaultKey;
                encryptionEnabled = true;
                if (encryptionKey === undefined) {
                    log.debug("red/runtime/nodes/credentials.load : no default key present - generating one");
                    // No user-provided key, no generated key
                    // Generate a new key
                    defaultKey = crypto.randomBytes(32).toString('hex');
                    try {
                        setupEncryptionPromise = settings.set('_credentialSecret',defaultKey);
                        encryptionKey = crypto.createHash('sha256').update(defaultKey).digest();
                    } catch(err) {
                        log.debug("red/runtime/nodes/credentials.load : settings unavailable - disabling encryption");
                        // Settings unavailable
                        encryptionEnabled = false;
                        encryptionKey = null;
                    }
                    dirty = true;
                } else {
                    log.debug("red/runtime/nodes/credentials.load : using default key");
                }
            }
        }
        if (encryptionEnabled && !dirty) {
            encryptedCredentials = credentials;
        }
        return setupEncryptionPromise.then(function() {
            if (credentials.hasOwnProperty("$")) {
                // These are encrypted credentials
                try {
                    credentialCache = decryptCredentials(encryptionKey,credentials)
                } catch(err) {
                    credentialCache = {};
                    dirty = true;
                    log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                }
            } else {
                credentialCache = credentials;
            }
        });
    },

    /**
     * Adds a set of credentials for the given node id.
     * @param id the node id for the credentials
     * @param creds an object of credential key/value pairs
     * @return a promise for backwards compatibility TODO: can this be removed?
     */
    add: function (id, creds) {
        if (!credentialCache.hasOwnProperty(id) || JSON.stringify(creds) !== JSON.stringify(credentialCache[id])) {
            credentialCache[id] = creds;
            dirty = true;
        }
        return when.resolve();
    },

    /**
     * Gets the credentials for the given node id.
     * @param id the node id for the credentials
     * @return the credentials
     */
    get: function (id) {
        return credentialCache[id];
    },

    /**
     * Deletes the credentials for the given node id.
     * @param id the node id for the credentials
     * @return a promise for the saving of credentials to storage
     */
    delete: function (id) {
        delete credentialCache[id];
        dirty = true;
    },

    /**
     * Deletes any credentials for nodes that no longer exist
     * @param config a flow config
     * @return a promise for the saving of credentials to storage
     */
    clean: function (config) {
        var existingIds = {};
        config.forEach(function(n) {
            existingIds[n.id] = true;
            if (n.credentials) {
                api.extract(n);
            }
        });
        var deletedCredentials = false;
        for (var c in credentialCache) {
            if (credentialCache.hasOwnProperty(c)) {
                if (!existingIds[c]) {
                    deletedCredentials = true;
                    delete credentialCache[c];
                }
            }
        }
        if (deletedCredentials) {
            dirty = true;
        }
        return when.resolve();
    },

    /**
     * Registers a node credential definition.
     * @param type the node type
     * @param definition the credential definition
     */
    register: function (type, definition) {
        var dashedType = type.replace(/\s+/g, '-');
        credentialsDef[dashedType] = definition;
    },

    /**
     * Extracts and stores any credential updates in the provided node.
     * The provided node may have a .credentials property that contains
     * new credentials for the node.
     * This function loops through the credentials in the definition for
     * the node-type and applies any of the updates provided in the node.
     *
     * This function does not save the credentials to disk as it is expected
     * to be called multiple times when a new flow is deployed.
     *
     * @param node the node to extract credentials from
     */
    extract: function(node) {
        var nodeID = node.id;
        var nodeType = node.type;
        var newCreds = node.credentials;
        if (newCreds) {
            delete node.credentials;
            var savedCredentials = credentialCache[nodeID] || {};
            var dashedType = nodeType.replace(/\s+/g, '-');
            var definition = credentialsDef[dashedType];
            if (!definition) {
                log.warn(log._("nodes.credentials.not-registered",{type:nodeType}));
                return;
            }

            for (var cred in definition) {
                if (definition.hasOwnProperty(cred)) {
                    if (newCreds[cred] === undefined) {
                        continue;
                    }
                    if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') {
                        continue;
                    }
                    if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) {
                        delete savedCredentials[cred];
                        dirty = true;
                        continue;
                    }
                    if (!savedCredentials.hasOwnProperty(cred) || JSON.stringify(savedCredentials[cred]) !== JSON.stringify(newCreds[cred])) {
                        savedCredentials[cred] = newCreds[cred];
                        dirty = true;
                    }
                }
            }
            credentialCache[nodeID] = savedCredentials;
        }
    },

    /**
     * Gets the credential definition for the given node type
     * @param type the node type
     * @return the credential definition
     */
    getDefinition: function (type) {
        return credentialsDef[type];
    },

    dirty: function() {
        return dirty;
    },

    export: function() {
        var result = credentialCache;
        if (encryptionEnabled) {
            if (dirty) {
                try {
                    log.debug("red/runtime/nodes/credentials.export : encrypting");
                    var initVector = crypto.randomBytes(16);
                    var cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, initVector);
                    result = {"$":initVector.toString('hex') + cipher.update(JSON.stringify(credentialCache), 'utf8', 'base64') + cipher.final('base64')};
                } catch(err) {
                    log.warn(log._("nodes.credentials.error-saving",{message:err.toString()}))
                }
            } else {
                result = encryptedCredentials;
            }
        }
        dirty = false;
        if (removeDefaultKey) {
            log.debug("red/runtime/nodes/credentials.export : removing unused default key");
            return settings.delete('_credentialSecret').then(function() {
                removeDefaultKey = false;
                return result;
            })
        } else {
            return when.resolve(result);
        }
    }
}
<|endoftext|>


<commit_before>import ApolloClient, { createNetworkInterface } from 'apollo-client';
import ROOT_URL from 'config/rootUrl';

const networkInterface = createNetworkInterface({
  uri: `${ROOT_URL}/graphql`,
  opts: {
    credentials: (process.env.NODE_ENV === 'development') ? 'include' : 'same-origin'
  }
});

export default new ApolloClient({
  networkInterface,
  dataIdFromObject: (result) => {
    if (result.id && result.__typename) {
      return result.__typename + result.id;
    }
    return null;
  }
});
<commit_msg>Active query deduplication on apollo<commit_after>import ApolloClient, { createNetworkInterface } from 'apollo-client';
import ROOT_URL from 'config/rootUrl';

const networkInterface = createNetworkInterface({
  uri: `${ROOT_URL}/graphql`,
  opts: {
    credentials: (process.env.NODE_ENV === 'development') ? 'include' : 'same-origin'
  }
});

export default new ApolloClient({
  networkInterface,
  dataIdFromObject: (result) => {
    if (result.id && result.__typename) {
      return result.__typename + result.id;
    }
    return null;
  },
  queryDeduplication: true
});
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var url = require('url');
var mkdirp = require('mkdirp');
var express = require('express');

function protocolOf(server) {
    server = url.parse(server);
    return server.protocol === 'https:' ? require('https') : require('http');
}

function urlPathFor(moduleName, version) {
    if (/^\/(text|raw);/.test(moduleName)) {
        return 'v=' + version + '/apps/' + moduleName.replace(/^\/(text|raw);/, '');
    }
    return 'api/apps/load/' + version + ',' + moduleName.replace(/^apps\//, '');
}

function createPathFinder(fileName) {
    return function fileExists(basePath) {
        return new Promise(function (resolve, reject) {
            var filePath = path.join(basePath, fileName);
            fs.access(
                filePath,
                'r',
                (err) => err ? resolve(err) : reject(filePath)
            );
        });
    };
}

function createRemoteReadStream(request, urlPath) {
    return new Promise(function (resolve, reject) {
        var options = request.app.get('options'),
            server = url.parse(options.server),
            URL = url.resolve(server, urlPath),
            protocol = protocolOf(options.server);

        if (options.verbose.remote || options.verbose['local:error']) console.log(URL);

        var opt = url.parse(URL);
        opt.headers = request.headers;
        delete opt.headers['accept-encoding'];
        delete opt.headers.host;
        opt.rejectUnauthorized = options.rejectUnauthorized;
        protocol.get(opt, function (res) {
            if (res.statusCode !== 200) return reject(res.statusMessage);
            resolve(res);
        })
        .on('error', reject);
    });
}

function readFromStream(options, request, fileName, urlPath) {
    var pathFinder = createPathFinder(
        //remove basePath, for local search
        fileName.replace(path.join(options.prefixes[0]), '')
    );
    return Promise.all([
        new Promise(function (resolve, reject) {
            mkdirp(path.dirname(fileName), function (err) {
                if (err) return reject();
                resolve(fileName);
            });
        }),
        Promise.all(
            options.prefixes.slice(1)
                .map(pathFinder)
        )
        .then(
            () =>
                createRemoteReadStream(request, urlPath)
                    .catch((err) => options.verbose.remote ? console.error(urlPath, err) : ''),
            function (localFile) {
                if (options.verbose.local) console.log(localFile);
                return fs.createReadStream(localFile);
            }
        )
    ]);
}

function writeToStream(fileInfo) {
    var fileName = fileInfo[0];
    var readStream = fileInfo[1];

    return new Promise(function (resolve, reject) {
        readStream.pipe(fs.createWriteStream(fileName))
            .on('finish', () => resolve({ createdFile: fileName, from: readStream }))
            .on('error', reject);
    });
}

function fileExistsInBasepath(options, fileName) {
    return new Promise(function (resolve, reject) {
        if (!fileName) reject({});
        var filePath = path.join(options.prefixes[0], fileName);
        fs.access(
            filePath
            ,'wx',
            (err) => err ? resolve(err) : reject({ fileExists: true })
        );
    });
}

function needsUpdate(options, fileName) {
    var pathFinder = createPathFinder(
        //remove basePath, for local search
        fileName.replace(path.join(options.prefixes[0]), '')
    );

    return function (err) {
        if (!err.fileExists) throw err;

        var mtime = fs.statSync(path.join(options.prefixes[0], fileName)).mtime
	return Promise.all(
            options.prefixes.slice(1)
                .map(pathFinder)
        ).catch(function (file) {
            if (mtime < fs.statSync(file).mtime) fs.unlinkSync(path.join(options.prefixes[0], fileName));
            return fileExistsInBasepath(options, fileName);
        })
    };
}

function appsLoadFile(options, request, fileName, urlPath) {
    return fileExistsInBasepath(options, 'apps/' + fileName)
        .catch(needsUpdate(options, 'apps/' + fileName))
        .then(function (err) {
            if (err.code === 'ENOENT')
                return err.path;
            throw err;
        })
        .then((fileName) => readFromStream(options, request, fileName, urlPath))
        .then(writeToStream)
        .catch(() => '');
}

function appsLoad(request, response, next) {
    var URL = url.parse(request.url, true);
    var options = request.app.get('options');

    // parse request URL
    var list = URL.pathname.split(',');
    var version = list.shift();
    version = version.slice(version.lastIndexOf('/') + 1);
    request.app.set('version', version);

    Promise.all(list.map(function (moduleName) {
        var fileName = /^(?:\/(text|raw);)?([\w/+-]+(?:\.[\w/+-]+)*)$/.exec(moduleName)[2],
            urlPath = urlPathFor(moduleName, version);

        return appsLoadFile(options, request, fileName, urlPath)
        .then(function () {
            if (!/\.css$/.test(fileName)) return;

            fileName = fileName.replace(/\.css$/, '.less');
            urlPath = urlPath.replace(/\.css$/, '.less');

            if (!/themes\/[^/]+\/style.less/.test(fileName) && !/themes\/[^/]+\/common.less/.test(fileName)) {
                fileName = fileName.replace(/themes\/[^/]+\//, '');
                urlPath = urlPath.replace(/themes\/[^/]+\//, '');
            } else {
                // TODO: special handling for basic theme files
                return;
            }

            return appsLoadFile(options, request, fileName, urlPath);
        })
    })).then(() => next());
}

function staticFiles(request, response, next) {
    var moduleName = request.params[0] || 'ui',
        options = request.app.get('options'),
        version = 'v=' + request.params.version + '/' || '';
    return fileExistsInBasepath(options, moduleName)
        .catch(needsUpdate(options, moduleName))
        .then(function (err) {
            if (err.code === 'ENOENT')
                return err.path;
            throw err;
        })
        .then((fileName) => readFromStream(options, request, fileName, fileName.replace(options.prefixes[0], version)))
        .then(writeToStream)
        .catch(() => '')
        .then(() => next());
}

function fetchManifests(options) {
    return fileExistsInBasepath(options, 'manifests/server.json')
        .then(function (err) {
            if (err.code === 'ENOENT')
                return err.path;
            throw err;
        })
        .then(function (fileName) {
            return new Promise(function (resolve, reject) {
                protocolOf(options.server).get(
                    url.parse(options.server + '/api/apps/manifests?action=config'),
                    function (res) {
                        if (res.statusCode !== 200) return reject(res.statusMessage);
                        resolve([fileName, res]);
                    }
                )
                .on('error', reject);
            });
        })
        .then(function ([fileName, res]) {
            if (res.statusCode !== 200) return [fileName, res];
            let buf = '';
            res.on('data', function (chunk) {
                buf += chunk.toString();
            });
            return new Promise(function (resolve, reject) {
                res.on('end', function () {
                    const Readable = require('stream').Readable,
                        manifests = JSON.parse(buf).data.manifests,
                        stream = new Readable;

                    stream.push(JSON.stringify(manifests, null, 4));
                    stream.push(null);

                    resolve([fileName, stream]);
                });
                res.on('error', function (err) {
                    reject(err);
                });
            });
        })
        .then(function (fileInfo) {
            return new Promise(function (resolve, reject) {
                mkdirp(path.dirname(fileInfo[0]), function (err) {
                    if (err) return reject(err);
                    resolve(fileInfo);
                });
            })
        })
        .then(writeToStream)
        .catch(function (err) {
            if (err.fileExists) return;
            throw err;
        });
}

function create(options) {
    fetchManifests(options)
        .catch((err) => console.error('Unable to fetch manifests from server:', err));

    var app = express();

    app.set('options', options);

    app.get(options.urlPath + 'api/apps/load/**', appsLoad);

    // index (=> /ui)
    app.get(options.urlPath + '/', staticFiles);
    // static files
    app.get(options.urlPath + '/**', staticFiles);
    // static files with version parameter
    app.get(options.urlPath + 'v=:version/**', staticFiles);

    return app;
}

module.exports = {
    create: create,
    mirrorFile: function (fileName, urlPath, options) {
        var request = {
                app: {
                    get: () => options
                },
                headers: {}
            };
        return readFromStream(options, request, fileName, urlPath)
            .then(writeToStream)
            .then(
                () => console.log('fetched', fileName),
                (err) => console.error('failed to fetch', fileName, err));
    }
};
<commit_msg>fix a few obvious mistakes<commit_after>var fs = require('fs');
var path = require('path');
var url = require('url');
var mkdirp = require('mkdirp');
var express = require('express');

function protocolOf(server) {
    server = url.parse(server);
    return server.protocol === 'https:' ? require('https') : require('http');
}

function urlPathFor(moduleName, version) {
    if (/^\/(text|raw);/.test(moduleName)) {
        return 'v=' + version + '/apps/' + moduleName.replace(/^\/(text|raw);/, '');
    }
    return 'api/apps/load/' + version + ',' + moduleName.replace(/^apps\//, '');
}

function createPathFinder(fileName) {
    return function fileExists(basePath) {
        return new Promise(function (resolve, reject) {
            var filePath = path.join(basePath, fileName);
            fs.access(
                filePath,
                'r',
                (err) => err ? resolve(err) : reject(filePath)
            );
        });
    };
}

function createRemoteReadStream(request, urlPath) {
    return new Promise(function (resolve, reject) {
        var options = request.app.get('options'),
            server = url.parse(options.server),
            URL = url.resolve(server, urlPath),
            protocol = protocolOf(options.server);

        if (options.verbose.remote || options.verbose['local:error']) console.log(URL);

        var opt = url.parse(URL);
        opt.headers = request.headers;
        delete opt.headers['accept-encoding'];
        delete opt.headers.host;
        opt.rejectUnauthorized = options.rejectUnauthorized;
        protocol.get(opt, function (res) {
            if (res.statusCode !== 200) return reject(res.statusMessage);
            resolve(res);
        })
        .on('error', reject);
    });
}

function readFromStream(options, request, fileName, urlPath) {
    var pathFinder = createPathFinder(
        //remove basePath, for local search
        fileName.replace(path.join(options.prefixes[0]), '')
    );
    return Promise.all([
        new Promise(function (resolve, reject) {
            mkdirp(path.dirname(fileName), function (err) {
                if (err) return reject();
                resolve(fileName);
            });
        }),
        Promise.all(
            options.prefixes.slice(1)
                .map(pathFinder)
        )
        .then(
            () =>
                createRemoteReadStream(request, urlPath)
                    .catch((err) => options.verbose.remote ? console.error(urlPath, err) : ''),
            function (localFile) {
                if (options.verbose.local) console.log(localFile);
                return fs.createReadStream(localFile);
            }
        )
    ]);
}

function writeToStream(fileInfo) {
    var fileName = fileInfo[0];
    var readStream = fileInfo[1];

    return new Promise(function (resolve, reject) {
        readStream.pipe(fs.createWriteStream(fileName))
            .on('finish', () => resolve({ createdFile: fileName, from: readStream }))
            .on('error', reject);
    });
}

function fileExistsInBasepath(options, fileName) {
    return new Promise(function (resolve, reject) {
        if (!fileName) reject({});
        var filePath = path.join(options.prefixes[0], fileName);
        fs.access(
            filePath
            ,'wx',
            (err) => err ? resolve(err) : reject({ fileExists: true })
        );
    });
}

function needsUpdate(options, fileName) {
    var pathFinder = createPathFinder(
        //remove basePath, for local search
        fileName.replace(path.join(options.prefixes[0]), '')
    );

    return function (err) {
        if (!err.fileExists) throw err;

        var mtime = fs.statSync(path.join(options.prefixes[0], fileName)).mtime
	return Promise.all(
            options.prefixes.slice(1)
                .map(pathFinder)
        ).catch(function (file) {
            if (mtime < fs.statSync(file).mtime) fs.unlinkSync(path.join(options.prefixes[0], fileName));
            return fileExistsInBasepath(options, fileName);
        })
    };
}

function appsLoadFile(options, request, fileName, urlPath) {
    return fileExistsInBasepath(options, 'apps/' + fileName)
        .catch(needsUpdate(options, 'apps/' + fileName))
        .then(function (err) {
            if (err.code === 'ENOENT')
                return err.path;
            throw err;
        })
        .then((fileName) => readFromStream(options, request, fileName, urlPath))
        .then(writeToStream)
        .catch(() => '');
}

function appsLoad(request, response, next) {
    var URL = url.parse(request.url, true);
    var options = request.app.get('options');

    // parse request URL
    var list = URL.pathname.split(',');
    var version = list.shift();
    version = version.slice(version.lastIndexOf('/') + 1);
    request.app.set('version', version);

    Promise.all(list.map(function (moduleName) {
        var fileName = /^(?:\/(text|raw);)?([\w/+-]+(?:\.[\w/+-]+)*)$/.exec(moduleName)[2],
            urlPath = urlPathFor(moduleName, version);

        return appsLoadFile(options, request, fileName, urlPath)
        .then(function () {
            if (!/\.css$/.test(fileName)) return;

            fileName = fileName.replace(/\.css$/, '.less');
            urlPath = urlPath.replace(/\.css$/, '.less');

            if (!/themes\/[^/]+\/style.less/.test(fileName) && !/themes\/[^/]+\/common.less/.test(fileName)) {
                fileName = fileName.replace(/themes\/[^/]+\//, '');
                urlPath = urlPath.replace(/themes\/[^/]+\//, '');
            } else {
                // TODO: special handling for basic theme files
                return;
            }

            return appsLoadFile(options, request, fileName, urlPath);
        })
    })).then(() => next());
}

function staticFiles(request, response, next) {
    var moduleName = request.params[0] || 'ui',
        options = request.app.get('options'),
        version = '';

    if (request.params.version) version = 'v=' + request.params.version + '/';
    return fileExistsInBasepath(options, moduleName)
        .catch(needsUpdate(options, moduleName))
        .then(function (err) {
            if (err.code === 'ENOENT')
                return err.path;
            throw err;
        })
        .then((fileName) => readFromStream(options, request, fileName, fileName.replace(options.prefixes[0], version)))
        .then(writeToStream)
        .catch(() => '')
        .then(() => next());
}

function fetchManifests(options) {
    return fileExistsInBasepath(options, 'manifests/server.json')
        .then(function (err) {
            if (err.code === 'ENOENT')
                return err.path;
            throw err;
        })
        .then(function (fileName) {
            return new Promise(function (resolve, reject) {
                protocolOf(options.server).get(
                    url.resolve(url.parse(options.server), 'api/apps/manifests?action=config'),
                    function (res) {
                        if (res.statusCode !== 200) return reject(res.statusMessage);
                        resolve([fileName, res]);
                    }
                )
                .on('error', reject);
            });
        })
        .then(function ([fileName, res]) {
            if (res.statusCode !== 200) return [fileName, res];
            let buf = '';
            res.on('data', function (chunk) {
                buf += chunk.toString();
            });
            return new Promise(function (resolve, reject) {
                res.on('end', function () {
                    const Readable = require('stream').Readable,
                        manifests = JSON.parse(buf).data.manifests,
                        stream = new Readable;

                    stream.push(JSON.stringify(manifests, null, 4));
                    stream.push(null);

                    resolve([fileName, stream]);
                });
                res.on('error', function (err) {
                    reject(err);
                });
            });
        })
        .then(function (fileInfo) {
            return new Promise(function (resolve, reject) {
                mkdirp(path.dirname(fileInfo[0]), function (err) {
                    if (err) return reject(err);
                    resolve(fileInfo);
                });
            })
        })
        .then(writeToStream)
        .catch(function (err) {
            if (err.fileExists) return;
            throw err;
        });
}

function create(options) {
    fetchManifests(options)
        .catch((err) => console.error('Unable to fetch manifests from server:', err));

    var app = express();

    app.set('options', options);

    app.get(options.urlPath + 'api/apps/load/**', appsLoad);

    // index (=> /ui)
    app.get(options.urlPath, staticFiles);
    // static files
    app.get(options.urlPath + '**', staticFiles);
    // static files with version parameter
    app.get(options.urlPath + 'v=:version/**', staticFiles);

    return app;
}

module.exports = {
    create: create,
    mirrorFile: function (fileName, urlPath, options) {
        var request = {
                app: {
                    get: () => options
                },
                headers: {}
            };
        return readFromStream(options, request, fileName, urlPath)
            .then(writeToStream)
            .then(
                () => console.log('fetched', fileName),
                (err) => console.error('failed to fetch', fileName, err));
    }
};
<|endoftext|>


<commit_before>var hasInherit = require('./has-inherit');
var everyValuesPair = require('./every-values-pair');
var findComponentIn = require('./find-component-in');
var isComponentOf = require('./is-component-of');
var isMergeableShorthand = require('./is-mergeable-shorthand');
var overridesNonComponentShorthand = require('./overrides-non-component-shorthand');
var sameVendorPrefixesIn = require('./vendor-prefixes').same;

var compactable = require('../compactable');
var deepClone = require('../clone').deep;
var deepClone = require('../clone').deep;
var restoreWithComponents = require('../restore-with-components');
var shallowClone = require('../clone').shallow;

var restoreFromOptimizing = require('../../restore-from-optimizing');

var Token = require('../../../tokenizer/token');
var Marker = require('../../../tokenizer/marker');

var serializeProperty = require('../../../writer/one-time').property;

function wouldBreakCompatibility(property, validator) {
  for (var i = 0; i < property.components.length; i++) {
    var component = property.components[i];
    var descriptor = compactable[component.name];
    var canOverride = descriptor && descriptor.canOverride || canOverride.sameValue;

    var _component = shallowClone(component);
    _component.value = [[Token.PROPERTY_VALUE, descriptor.defaultValue]];

    if (!everyValuesPair(canOverride.bind(null, validator), _component, component)) {
      return true;
    }
  }

  return false;
}

function overrideIntoMultiplex(property, by) {
  by.unused = true;

  turnIntoMultiplex(by, multiplexSize(property));
  property.value = by.value;
}

function overrideByMultiplex(property, by) {
  by.unused = true;
  property.multiplex = true;
  property.value = by.value;
}

function overrideSimple(property, by) {
  by.unused = true;
  property.value = by.value;
}

function override(property, by) {
  if (by.multiplex)
    overrideByMultiplex(property, by);
  else if (property.multiplex)
    overrideIntoMultiplex(property, by);
  else
    overrideSimple(property, by);
}

function overrideShorthand(property, by) {
  by.unused = true;

  for (var i = 0, l = property.components.length; i < l; i++) {
    override(property.components[i], by.components[i], property.multiplex);
  }
}

function turnIntoMultiplex(property, size) {
  property.multiplex = true;

  if (compactable[property.name].shorthand) {
    turnShorthandValueIntoMultiplex(property, size);
  } else {
    turnLonghandValueIntoMultiplex(property, size);
  }
}

function turnShorthandValueIntoMultiplex(property, size) {
  var component;
  var i, l;

  for (i = 0, l = property.components.length; i < l; i++) {
    component = property.components[i];

    if (!component.multiplex) {
      turnLonghandValueIntoMultiplex(component, size);
    }
  }
}

function turnLonghandValueIntoMultiplex(property, size) {
  var descriptor = compactable[property.name];
  var withRealValue = descriptor.intoMultiplexMode == 'real';
  var withValue = descriptor.intoMultiplexMode == 'real' ?
    property.value.slice(0) :
    (descriptor.intoMultiplexMode == 'placeholder' ? descriptor.placeholderValue : descriptor.defaultValue);
  var i = multiplexSize(property);
  var j;
  var m = withValue.length;

  for (; i < size; i++) {
    property.value.push([Token.PROPERTY_VALUE, Marker.COMMA]);

    if (Array.isArray(withValue)) {
      for (j = 0; j < m; j++) {
        property.value.push(withRealValue ? withValue[j] : [Token.PROPERTY_VALUE, withValue[j]]);
      }
    } else {
      property.value.push(withRealValue ? withValue : [Token.PROPERTY_VALUE, withValue]);
    }
  }
}

function multiplexSize(component) {
  var size = 0;

  for (var i = 0, l = component.value.length; i < l; i++) {
    if (component.value[i][1] == Marker.COMMA)
      size++;
  }

  return size + 1;
}

function lengthOf(property) {
  var fakeAsArray = [
    Token.PROPERTY,
    [Token.PROPERTY_NAME, property.name]
  ].concat(property.value);
  return serializeProperty([fakeAsArray], 0).length;
}

function moreSameShorthands(properties, startAt, name) {
  // Since we run the main loop in `compactOverrides` backwards, at this point some
  // properties may not be marked as unused.
  // We should consider reverting the order if possible
  var count = 0;

  for (var i = startAt; i >= 0; i--) {
    if (properties[i].name == name && !properties[i].unused)
      count++;
    if (count > 1)
      break;
  }

  return count > 1;
}

function overridingFunction(shorthand, validator) {
  for (var i = 0, l = shorthand.components.length; i < l; i++) {
    if (!anyValue(validator.isUrl, shorthand.components[i]) && anyValue(validator.isFunction, shorthand.components[i])) {
      return true;
    }
  }

  return false;
}

function anyValue(fn, property) {
  for (var i = 0, l = property.value.length; i < l; i++) {
    if (property.value[i][1] == Marker.COMMA)
      continue;

    if (fn(property.value[i][1]))
      return true;
  }

  return false;
}

function wouldResultInLongerValue(left, right) {
  if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex)
    return false;

  var multiplex = left.multiplex ? left : right;
  var simple = left.multiplex ? right : left;
  var component;

  var multiplexClone = deepClone(multiplex);
  restoreFromOptimizing([multiplexClone], restoreWithComponents);

  var simpleClone = deepClone(simple);
  restoreFromOptimizing([simpleClone], restoreWithComponents);

  var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone);

  if (left.multiplex) {
    component = findComponentIn(multiplexClone, simpleClone);
    overrideIntoMultiplex(component, simpleClone);
  } else {
    component = findComponentIn(simpleClone, multiplexClone);
    turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone));
    overrideByMultiplex(component, multiplexClone);
  }

  restoreFromOptimizing([simpleClone], restoreWithComponents);

  var lengthAfter = lengthOf(simpleClone);

  return lengthBefore <= lengthAfter;
}

function isCompactable(property) {
  return property.name in compactable;
}

function noneOverrideHack(left, right) {
  return !left.multiplex &&
    (left.name == 'background' || left.name == 'background-image') &&
    right.multiplex &&
    (right.name == 'background' || right.name == 'background-image') &&
    anyLayerIsNone(right.value);
}

function anyLayerIsNone(values) {
  var layers = intoLayers(values);

  for (var i = 0, l = layers.length; i < l; i++) {
    if (layers[i].length == 1 && layers[i][0][1] == 'none')
      return true;
  }

  return false;
}

function intoLayers(values) {
  var layers = [];

  for (var i = 0, layer = [], l = values.length; i < l; i++) {
    var value = values[i];
    if (value[1] == Marker.COMMA) {
      layers.push(layer);
      layer = [];
    } else {
      layer.push(value);
    }
  }

  layers.push(layer);
  return layers;
}

function overrideProperties(properties, withMerging, compatibility, validator) {
  var mayOverride, right, left, component;
  var overriddenComponents;
  var overriddenComponent;
  var overridingComponent;
  var overridable;
  var i, j, k;

  propertyLoop:
  for (i = properties.length - 1; i >= 0; i--) {
    right = properties[i];

    if (!isCompactable(right))
      continue;

    if (right.block)
      continue;

    mayOverride = compactable[right.name].canOverride;

    traverseLoop:
    for (j = i - 1; j >= 0; j--) {
      left = properties[j];

      if (!isCompactable(left))
        continue;

      if (left.block)
        continue;

      if (left.unused || right.unused)
        continue;

      if (left.hack && !right.hack && !right.important || !left.hack && !left.important && right.hack)
        continue;

      if (left.important == right.important && left.hack[0] != right.hack[0])
        continue;

      if (left.important == right.important && (left.hack[0] != right.hack[0] || (left.hack[1] && left.hack[1] != right.hack[1])))
        continue;

      if (hasInherit(right))
        continue;

      if (noneOverrideHack(left, right))
        continue;

      if (right.shorthand && isComponentOf(right, left)) {
        // maybe `left` can be overridden by `right` which is a shorthand?
        if (!right.important && left.important)
          continue;

        if (!sameVendorPrefixesIn([left], right.components))
          continue;

        if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator))
          continue;

        if (!isMergeableShorthand(right)) {
          left.unused = true;
          continue;
        }

        component = findComponentIn(right, left);
        mayOverride = compactable[left.name].canOverride;
        if (everyValuesPair(mayOverride.bind(null, validator), left, component)) {
          left.unused = true;
        }
      } else if (right.shorthand && overridesNonComponentShorthand(right, left)) {
        // `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top`
        if (!right.important && left.important) {
          continue;
        }

        if (!sameVendorPrefixesIn([left], right.components)) {
          continue;
        }

        if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) {
          continue;
        }

        overriddenComponents = left.shorthand ?
          left.components:
          [left];

        for (k = overriddenComponents.length - 1; k >= 0; k--) {
          overriddenComponent = overriddenComponents[k];
          overridingComponent = findComponentIn(right, overriddenComponent);
          mayOverride = compactable[overriddenComponent.name].canOverride;

          if (!everyValuesPair(mayOverride.bind(null, validator), left, overridingComponent)) {
            continue traverseLoop;
          }
        }

        left.unused = true;
      } else if (withMerging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) {
        // maybe `right` can be pulled into `left` which is a shorthand?
        if (right.important && !left.important)
          continue;

        if (!right.important && left.important) {
          right.unused = true;
          continue;
        }

        // Pending more clever algorithm in #527
        if (moreSameShorthands(properties, i - 1, left.name))
          continue;

        if (overridingFunction(left, validator))
          continue;

        if (!isMergeableShorthand(left))
          continue;

        component = findComponentIn(left, right);
        if (everyValuesPair(mayOverride.bind(null, validator), component, right)) {
          var disabledBackgroundMerging =
            !compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 ||
            !compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1 ||
            !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1;
          var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][1];

          if (disabledBackgroundMerging || nonMergeableValue)
            continue;

          if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator))
            continue;

          if (component.value[0][1] != right.value[0][1] && (hasInherit(left) || hasInherit(right)))
            continue;

          if (wouldResultInLongerValue(left, right))
            continue;

          if (!left.multiplex && right.multiplex)
            turnIntoMultiplex(left, multiplexSize(right));

          override(component, right);
          left.dirty = true;
        }
      } else if (withMerging && left.shorthand && right.shorthand && left.name == right.name) {
        // merge if all components can be merged

        if (!left.multiplex && right.multiplex)
          continue;

        if (!right.important && left.important) {
          right.unused = true;
          continue propertyLoop;
        }

        if (right.important && !left.important) {
          left.unused = true;
          continue;
        }

        if (!isMergeableShorthand(right)) {
          left.unused = true;
          continue;
        }

        for (k = left.components.length - 1; k >= 0; k--) {
          var leftComponent = left.components[k];
          var rightComponent = right.components[k];

          mayOverride = compactable[leftComponent.name].canOverride;
          if (!everyValuesPair(mayOverride.bind(null, validator), leftComponent, rightComponent))
            continue propertyLoop;
        }

        overrideShorthand(left, right);
        left.dirty = true;
      } else if (withMerging && left.shorthand && right.shorthand && isComponentOf(left, right)) {
        // border is a shorthand but any of its components is a shorthand too

        if (!left.important && right.important)
          continue;

        component = findComponentIn(left, right);
        mayOverride = compactable[right.name].canOverride;
        if (!everyValuesPair(mayOverride.bind(null, validator), component, right))
          continue;

        if (left.important && !right.important) {
          right.unused = true;
          continue;
        }

        var rightRestored = compactable[right.name].restore(right, compactable);
        if (rightRestored.length > 1)
          continue;

        component = findComponentIn(left, right);
        override(component, right);
        right.dirty = true;
      } else if (left.name == right.name) {
        // two non-shorthands should be merged based on understandability
        overridable = true;

        if (right.shorthand) {
          for (k = right.components.length - 1; k >= 0 && overridable; k--) {
            overriddenComponent = left.components[k];
            overridingComponent = right.components[k];
            mayOverride = compactable[overridingComponent.name].canOverride;

            overridable = overridable && everyValuesPair(mayOverride.bind(null, validator), overriddenComponent, overridingComponent);
          }
        } else {
          mayOverride = compactable[right.name].canOverride;
          overridable = everyValuesPair(mayOverride.bind(null, validator), left, right);
        }

        if (left.important && !right.important && overridable) {
          right.unused = true;
          continue;
        }

        if (!left.important && right.important && overridable) {
          left.unused = true;
          continue;
        }

        if (!overridable) {
          continue;
        }

        left.unused = true;
      }
    }
  }
}

module.exports = overrideProperties;
<commit_msg>Fixes #1036 - double `deepClone` declaration.<commit_after>var hasInherit = require('./has-inherit');
var everyValuesPair = require('./every-values-pair');
var findComponentIn = require('./find-component-in');
var isComponentOf = require('./is-component-of');
var isMergeableShorthand = require('./is-mergeable-shorthand');
var overridesNonComponentShorthand = require('./overrides-non-component-shorthand');
var sameVendorPrefixesIn = require('./vendor-prefixes').same;

var compactable = require('../compactable');
var deepClone = require('../clone').deep;
var restoreWithComponents = require('../restore-with-components');
var shallowClone = require('../clone').shallow;

var restoreFromOptimizing = require('../../restore-from-optimizing');

var Token = require('../../../tokenizer/token');
var Marker = require('../../../tokenizer/marker');

var serializeProperty = require('../../../writer/one-time').property;

function wouldBreakCompatibility(property, validator) {
  for (var i = 0; i < property.components.length; i++) {
    var component = property.components[i];
    var descriptor = compactable[component.name];
    var canOverride = descriptor && descriptor.canOverride || canOverride.sameValue;

    var _component = shallowClone(component);
    _component.value = [[Token.PROPERTY_VALUE, descriptor.defaultValue]];

    if (!everyValuesPair(canOverride.bind(null, validator), _component, component)) {
      return true;
    }
  }

  return false;
}

function overrideIntoMultiplex(property, by) {
  by.unused = true;

  turnIntoMultiplex(by, multiplexSize(property));
  property.value = by.value;
}

function overrideByMultiplex(property, by) {
  by.unused = true;
  property.multiplex = true;
  property.value = by.value;
}

function overrideSimple(property, by) {
  by.unused = true;
  property.value = by.value;
}

function override(property, by) {
  if (by.multiplex)
    overrideByMultiplex(property, by);
  else if (property.multiplex)
    overrideIntoMultiplex(property, by);
  else
    overrideSimple(property, by);
}

function overrideShorthand(property, by) {
  by.unused = true;

  for (var i = 0, l = property.components.length; i < l; i++) {
    override(property.components[i], by.components[i], property.multiplex);
  }
}

function turnIntoMultiplex(property, size) {
  property.multiplex = true;

  if (compactable[property.name].shorthand) {
    turnShorthandValueIntoMultiplex(property, size);
  } else {
    turnLonghandValueIntoMultiplex(property, size);
  }
}

function turnShorthandValueIntoMultiplex(property, size) {
  var component;
  var i, l;

  for (i = 0, l = property.components.length; i < l; i++) {
    component = property.components[i];

    if (!component.multiplex) {
      turnLonghandValueIntoMultiplex(component, size);
    }
  }
}

function turnLonghandValueIntoMultiplex(property, size) {
  var descriptor = compactable[property.name];
  var withRealValue = descriptor.intoMultiplexMode == 'real';
  var withValue = descriptor.intoMultiplexMode == 'real' ?
    property.value.slice(0) :
    (descriptor.intoMultiplexMode == 'placeholder' ? descriptor.placeholderValue : descriptor.defaultValue);
  var i = multiplexSize(property);
  var j;
  var m = withValue.length;

  for (; i < size; i++) {
    property.value.push([Token.PROPERTY_VALUE, Marker.COMMA]);

    if (Array.isArray(withValue)) {
      for (j = 0; j < m; j++) {
        property.value.push(withRealValue ? withValue[j] : [Token.PROPERTY_VALUE, withValue[j]]);
      }
    } else {
      property.value.push(withRealValue ? withValue : [Token.PROPERTY_VALUE, withValue]);
    }
  }
}

function multiplexSize(component) {
  var size = 0;

  for (var i = 0, l = component.value.length; i < l; i++) {
    if (component.value[i][1] == Marker.COMMA)
      size++;
  }

  return size + 1;
}

function lengthOf(property) {
  var fakeAsArray = [
    Token.PROPERTY,
    [Token.PROPERTY_NAME, property.name]
  ].concat(property.value);
  return serializeProperty([fakeAsArray], 0).length;
}

function moreSameShorthands(properties, startAt, name) {
  // Since we run the main loop in `compactOverrides` backwards, at this point some
  // properties may not be marked as unused.
  // We should consider reverting the order if possible
  var count = 0;

  for (var i = startAt; i >= 0; i--) {
    if (properties[i].name == name && !properties[i].unused)
      count++;
    if (count > 1)
      break;
  }

  return count > 1;
}

function overridingFunction(shorthand, validator) {
  for (var i = 0, l = shorthand.components.length; i < l; i++) {
    if (!anyValue(validator.isUrl, shorthand.components[i]) && anyValue(validator.isFunction, shorthand.components[i])) {
      return true;
    }
  }

  return false;
}

function anyValue(fn, property) {
  for (var i = 0, l = property.value.length; i < l; i++) {
    if (property.value[i][1] == Marker.COMMA)
      continue;

    if (fn(property.value[i][1]))
      return true;
  }

  return false;
}

function wouldResultInLongerValue(left, right) {
  if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex)
    return false;

  var multiplex = left.multiplex ? left : right;
  var simple = left.multiplex ? right : left;
  var component;

  var multiplexClone = deepClone(multiplex);
  restoreFromOptimizing([multiplexClone], restoreWithComponents);

  var simpleClone = deepClone(simple);
  restoreFromOptimizing([simpleClone], restoreWithComponents);

  var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone);

  if (left.multiplex) {
    component = findComponentIn(multiplexClone, simpleClone);
    overrideIntoMultiplex(component, simpleClone);
  } else {
    component = findComponentIn(simpleClone, multiplexClone);
    turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone));
    overrideByMultiplex(component, multiplexClone);
  }

  restoreFromOptimizing([simpleClone], restoreWithComponents);

  var lengthAfter = lengthOf(simpleClone);

  return lengthBefore <= lengthAfter;
}

function isCompactable(property) {
  return property.name in compactable;
}

function noneOverrideHack(left, right) {
  return !left.multiplex &&
    (left.name == 'background' || left.name == 'background-image') &&
    right.multiplex &&
    (right.name == 'background' || right.name == 'background-image') &&
    anyLayerIsNone(right.value);
}

function anyLayerIsNone(values) {
  var layers = intoLayers(values);

  for (var i = 0, l = layers.length; i < l; i++) {
    if (layers[i].length == 1 && layers[i][0][1] == 'none')
      return true;
  }

  return false;
}

function intoLayers(values) {
  var layers = [];

  for (var i = 0, layer = [], l = values.length; i < l; i++) {
    var value = values[i];
    if (value[1] == Marker.COMMA) {
      layers.push(layer);
      layer = [];
    } else {
      layer.push(value);
    }
  }

  layers.push(layer);
  return layers;
}

function overrideProperties(properties, withMerging, compatibility, validator) {
  var mayOverride, right, left, component;
  var overriddenComponents;
  var overriddenComponent;
  var overridingComponent;
  var overridable;
  var i, j, k;

  propertyLoop:
  for (i = properties.length - 1; i >= 0; i--) {
    right = properties[i];

    if (!isCompactable(right))
      continue;

    if (right.block)
      continue;

    mayOverride = compactable[right.name].canOverride;

    traverseLoop:
    for (j = i - 1; j >= 0; j--) {
      left = properties[j];

      if (!isCompactable(left))
        continue;

      if (left.block)
        continue;

      if (left.unused || right.unused)
        continue;

      if (left.hack && !right.hack && !right.important || !left.hack && !left.important && right.hack)
        continue;

      if (left.important == right.important && left.hack[0] != right.hack[0])
        continue;

      if (left.important == right.important && (left.hack[0] != right.hack[0] || (left.hack[1] && left.hack[1] != right.hack[1])))
        continue;

      if (hasInherit(right))
        continue;

      if (noneOverrideHack(left, right))
        continue;

      if (right.shorthand && isComponentOf(right, left)) {
        // maybe `left` can be overridden by `right` which is a shorthand?
        if (!right.important && left.important)
          continue;

        if (!sameVendorPrefixesIn([left], right.components))
          continue;

        if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator))
          continue;

        if (!isMergeableShorthand(right)) {
          left.unused = true;
          continue;
        }

        component = findComponentIn(right, left);
        mayOverride = compactable[left.name].canOverride;
        if (everyValuesPair(mayOverride.bind(null, validator), left, component)) {
          left.unused = true;
        }
      } else if (right.shorthand && overridesNonComponentShorthand(right, left)) {
        // `right` is a shorthand while `left` can be overriden by it, think `border` and `border-top`
        if (!right.important && left.important) {
          continue;
        }

        if (!sameVendorPrefixesIn([left], right.components)) {
          continue;
        }

        if (!anyValue(validator.isFunction, left) && overridingFunction(right, validator)) {
          continue;
        }

        overriddenComponents = left.shorthand ?
          left.components:
          [left];

        for (k = overriddenComponents.length - 1; k >= 0; k--) {
          overriddenComponent = overriddenComponents[k];
          overridingComponent = findComponentIn(right, overriddenComponent);
          mayOverride = compactable[overriddenComponent.name].canOverride;

          if (!everyValuesPair(mayOverride.bind(null, validator), left, overridingComponent)) {
            continue traverseLoop;
          }
        }

        left.unused = true;
      } else if (withMerging && left.shorthand && !right.shorthand && isComponentOf(left, right, true)) {
        // maybe `right` can be pulled into `left` which is a shorthand?
        if (right.important && !left.important)
          continue;

        if (!right.important && left.important) {
          right.unused = true;
          continue;
        }

        // Pending more clever algorithm in #527
        if (moreSameShorthands(properties, i - 1, left.name))
          continue;

        if (overridingFunction(left, validator))
          continue;

        if (!isMergeableShorthand(left))
          continue;

        component = findComponentIn(left, right);
        if (everyValuesPair(mayOverride.bind(null, validator), component, right)) {
          var disabledBackgroundMerging =
            !compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 ||
            !compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1 ||
            !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1;
          var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][1];

          if (disabledBackgroundMerging || nonMergeableValue)
            continue;

          if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator))
            continue;

          if (component.value[0][1] != right.value[0][1] && (hasInherit(left) || hasInherit(right)))
            continue;

          if (wouldResultInLongerValue(left, right))
            continue;

          if (!left.multiplex && right.multiplex)
            turnIntoMultiplex(left, multiplexSize(right));

          override(component, right);
          left.dirty = true;
        }
      } else if (withMerging && left.shorthand && right.shorthand && left.name == right.name) {
        // merge if all components can be merged

        if (!left.multiplex && right.multiplex)
          continue;

        if (!right.important && left.important) {
          right.unused = true;
          continue propertyLoop;
        }

        if (right.important && !left.important) {
          left.unused = true;
          continue;
        }

        if (!isMergeableShorthand(right)) {
          left.unused = true;
          continue;
        }

        for (k = left.components.length - 1; k >= 0; k--) {
          var leftComponent = left.components[k];
          var rightComponent = right.components[k];

          mayOverride = compactable[leftComponent.name].canOverride;
          if (!everyValuesPair(mayOverride.bind(null, validator), leftComponent, rightComponent))
            continue propertyLoop;
        }

        overrideShorthand(left, right);
        left.dirty = true;
      } else if (withMerging && left.shorthand && right.shorthand && isComponentOf(left, right)) {
        // border is a shorthand but any of its components is a shorthand too

        if (!left.important && right.important)
          continue;

        component = findComponentIn(left, right);
        mayOverride = compactable[right.name].canOverride;
        if (!everyValuesPair(mayOverride.bind(null, validator), component, right))
          continue;

        if (left.important && !right.important) {
          right.unused = true;
          continue;
        }

        var rightRestored = compactable[right.name].restore(right, compactable);
        if (rightRestored.length > 1)
          continue;

        component = findComponentIn(left, right);
        override(component, right);
        right.dirty = true;
      } else if (left.name == right.name) {
        // two non-shorthands should be merged based on understandability
        overridable = true;

        if (right.shorthand) {
          for (k = right.components.length - 1; k >= 0 && overridable; k--) {
            overriddenComponent = left.components[k];
            overridingComponent = right.components[k];
            mayOverride = compactable[overridingComponent.name].canOverride;

            overridable = overridable && everyValuesPair(mayOverride.bind(null, validator), overriddenComponent, overridingComponent);
          }
        } else {
          mayOverride = compactable[right.name].canOverride;
          overridable = everyValuesPair(mayOverride.bind(null, validator), left, right);
        }

        if (left.important && !right.important && overridable) {
          right.unused = true;
          continue;
        }

        if (!left.important && right.important && overridable) {
          left.unused = true;
          continue;
        }

        if (!overridable) {
          continue;
        }

        left.unused = true;
      }
    }
  }
}

module.exports = overrideProperties;
<|endoftext|>


<commit_before><commit_msg>Get Programming<commit_after><|endoftext|>


<commit_before>const ExtractTextPlugin = require('extract-text-webpack-plugin')
const { env } = require('../configuration.js')

module.exports = {
  test: /\.(scss|sass|css)$/i,
  use: ExtractTextPlugin.extract({
    fallback: 'style-loader',
    use: [
      { loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
      { loader: 'postcss-loader', options: { sourceMap: true } },
      'resolve-url-loader',
      { loader: 'sass-loader', options: { sourceMap: true } }
    ]
  })
}
<commit_msg>Active css module.<commit_after>const ExtractTextPlugin = require('extract-text-webpack-plugin')
const { env } = require('../configuration.js')

module.exports = {
  test: /\.(scss|sass|css)$/i,
  use: ExtractTextPlugin.extract({
    fallback: 'style-loader',
    use: [
      {
        loader: 'css-loader',
        options: {
          minimize: env.NODE_ENV === 'production',
          modules: true,
          localIdentName: '[path][name]__[local]--[hash:base64:5]',
        }
      },
      {
        loader: 'postcss-loader',
        options: {
          sourceMap: true
        }
      },
      'resolve-url-loader',
      {
        loader: 'sass-loader',
        options: {
          sourceMap: true
        }
      }
    ]
  })
}
<|endoftext|>


<commit_before>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

var Frame = require('./Frame');
var TextureSource = require('./TextureSource');

/**
* A Texture consists of a source, usually an Image from the Cache, or a Canvas, and a collection
* of Frames. The Frames represent the different areas of the Texture. For example a texture atlas
* may have many Frames, one for each element within the atlas. Where-as a single image would have
* just one frame, that encompasses the whole image.
*
* Textures are managed by the global TextureManager. This is a singleton class that is
* responsible for creating and delivering Textures and their corresponding Frames to Game Objects.
*
* Sprites and other Game Objects get the texture data they need from the TextureManager.
*
* @class Phaser.Texture
* @constructor
* @param {object} source
* @param {number} scaleMode
*/
var Texture = function (manager, key, source)
{
    this.manager = manager;

    if (!Array.isArray(source))
    {
        source = [ source ];
    }

    this.key = key;

    /**
    * The source that is used to create the texture.
    * Usually an Image, but can also be a Canvas.
    *
    * @property source
    * @type array
    */
    this.source = [];

    /**
    * @property {object} frames - Frames
    */
    this.frames = {};

    this.firstFrame = '__BASE';

    this.frameTotal = 0;

    //  Load the Sources
    for (var i = 0; i < source.length; i++)
    {
        this.source.push(new TextureSource(this, source[i]));
    }
};

Texture.prototype.constructor = Texture;

Texture.prototype = {

    add: function (name, sourceIndex, x, y, width, height)
    {
        var frame = new Frame(this, name, sourceIndex, x, y, width, height);

        this.frames[name] = frame;

        //  Set the first frame of the Texture (other than __BASE)
        //  This is used to ensure we don't spam the display with entire
        //  atlases of sprite sheets, but instead just the first frame of them
        //  should the dev incorrectly specify the frame index
        if (this.frameTotal === 1)
        {
            this.firstFrame = name;
        }

        this.frameTotal++;

        return frame;
    },

    get: function (name)
    {
        if (name === undefined || name === null)
        {
            name = (this.frameTotal === 1) ? '__BASE' : this.firstFrame;
        }

        var frame = this.frames[name];

        if (!frame)
        {
            console.warn('No Texture.frame found with name ' + name);

            return this.frames['__BASE'];
        }
        else
        {
            return frame;
        }
    },

    getSourceImage: function (name)
    {
        if (name === undefined || name === null || this.frameTotal === 1)
        {
            name = '__BASE';
        }

        var frame = this.frames[name];

        if (!frame)
        {
            console.warn('No Texture.frame found with name ' + name);

            return this.frames['__BASE'].source.image;
        }
        else
        {
            return frame.source.image;
        }
    },

    setTextureIndex: function (index)
    {
        for (var i = 0; i < this.source.length; i++)
        {
            this.source[i].glTextureIndex = index;

            console.log(this.source[i].image.currentSrc, 'index = ', index);

            index++;
        }

        return index;
    },

    /**
    * Destroys this base texture
    *
    * @method destroy
    */
    destroy: function ()
    {
        //  TODO
    }

};

module.exports = Texture;
<commit_msg>Added Texture.has(frame) check<commit_after>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

var Frame = require('./Frame');
var TextureSource = require('./TextureSource');

/**
* A Texture consists of a source, usually an Image from the Cache, or a Canvas, and a collection
* of Frames. The Frames represent the different areas of the Texture. For example a texture atlas
* may have many Frames, one for each element within the atlas. Where-as a single image would have
* just one frame, that encompasses the whole image.
*
* Textures are managed by the global TextureManager. This is a singleton class that is
* responsible for creating and delivering Textures and their corresponding Frames to Game Objects.
*
* Sprites and other Game Objects get the texture data they need from the TextureManager.
*
* @class Phaser.Texture
* @constructor
* @param {object} source
* @param {number} scaleMode
*/
var Texture = function (manager, key, source)
{
    this.manager = manager;

    if (!Array.isArray(source))
    {
        source = [ source ];
    }

    this.key = key;

    /**
    * The source that is used to create the texture.
    * Usually an Image, but can also be a Canvas.
    *
    * @property source
    * @type array
    */
    this.source = [];

    /**
    * @property {object} frames - Frames
    */
    this.frames = {};

    this.firstFrame = '__BASE';

    this.frameTotal = 0;

    //  Load the Sources
    for (var i = 0; i < source.length; i++)
    {
        this.source.push(new TextureSource(this, source[i]));
    }
};

Texture.prototype.constructor = Texture;

Texture.prototype = {

    add: function (name, sourceIndex, x, y, width, height)
    {
        var frame = new Frame(this, name, sourceIndex, x, y, width, height);

        this.frames[name] = frame;

        //  Set the first frame of the Texture (other than __BASE)
        //  This is used to ensure we don't spam the display with entire
        //  atlases of sprite sheets, but instead just the first frame of them
        //  should the dev incorrectly specify the frame index
        if (this.frameTotal === 1)
        {
            this.firstFrame = name;
        }

        this.frameTotal++;

        return frame;
    },

    has: function (name)
    {
        return (this.frames[name]);
    },

    get: function (name)
    {
        if (name === undefined || name === null)
        {
            name = (this.frameTotal === 1) ? '__BASE' : this.firstFrame;
        }

        var frame = this.frames[name];

        if (!frame)
        {
            console.warn('No Texture.frame found with name ' + name);

            return this.frames['__BASE'];
        }
        else
        {
            return frame;
        }
    },

    getSourceImage: function (name)
    {
        if (name === undefined || name === null || this.frameTotal === 1)
        {
            name = '__BASE';
        }

        var frame = this.frames[name];

        if (!frame)
        {
            console.warn('No Texture.frame found with name ' + name);

            return this.frames['__BASE'].source.image;
        }
        else
        {
            return frame.source.image;
        }
    },

    setTextureIndex: function (index)
    {
        for (var i = 0; i < this.source.length; i++)
        {
            this.source[i].glTextureIndex = index;

            console.log(this.source[i].image.currentSrc, 'index = ', index);

            index++;
        }

        return index;
    },

    /**
    * Destroys this base texture
    *
    * @method destroy
    */
    destroy: function ()
    {
        //  TODO
    }

};

module.exports = Texture;
<|endoftext|>


<commit_before>import {
  assign,
  forEach,
  isArray
} from 'min-dash';

import { setSnapped } from '../snapping/SnapUtil';

var abs= Math.abs,
    round = Math.round;

var TOLERANCE = 10;

export default function BendpointSnapping(eventBus) {

  function snapTo(values, value) {

    if (isArray(values)) {
      var i = values.length;

      while (i--) if (abs(values[i] - value) <= TOLERANCE) {
        return values[i];
      }
    } else {
      values = +values;
      var rem = value % values;

      if (rem < TOLERANCE) {
        return value - rem;
      }

      if (rem > values - TOLERANCE) {
        return value - rem + values;
      }
    }

    return value;
  }

  function mid(element) {
    if (element.width) {
      return {
        x: round(element.width / 2 + element.x),
        y: round(element.height / 2 + element.y)
      };
    }
  }

  // connection segment snapping //////////////////////

  function getConnectionSegmentSnaps(context) {

    var snapPoints = context.snapPoints,
        connection = context.connection,
        waypoints = connection.waypoints,
        segmentStart = context.segmentStart,
        segmentStartIndex = context.segmentStartIndex,
        segmentEnd = context.segmentEnd,
        segmentEndIndex = context.segmentEndIndex,
        axis = context.axis;

    if (snapPoints) {
      return snapPoints;
    }

    var referenceWaypoints = [
      waypoints[segmentStartIndex - 1],
      segmentStart,
      segmentEnd,
      waypoints[segmentEndIndex + 1]
    ];

    if (segmentStartIndex < 2) {
      referenceWaypoints.unshift(mid(connection.source));
    }

    if (segmentEndIndex > waypoints.length - 3) {
      referenceWaypoints.unshift(mid(connection.target));
    }

    context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };

    forEach(referenceWaypoints, function(p) {

      // we snap on existing bendpoints only,
      // not placeholders that are inserted during add
      if (p) {
        p = p.original || p;

        if (axis === 'y') {
          snapPoints.horizontal.push(p.y);
        }

        if (axis === 'x') {
          snapPoints.vertical.push(p.x);
        }
      }
    });

    return snapPoints;
  }

  eventBus.on('connectionSegment.move.move', 1500, function(event) {
    var context = event.context,
        snapPoints = getConnectionSegmentSnaps(context),
        x = event.x,
        y = event.y,
        sx, sy;

    if (!snapPoints) {
      return;
    }

    // snap
    sx = snapTo(snapPoints.vertical, x);
    sy = snapTo(snapPoints.horizontal, y);

    // correction x/y
    var cx = (x - sx),
        cy = (y - sy);

    // update delta
    assign(event, {
      dx: event.dx - cx,
      dy: event.dy - cy,
      x: sx,
      y: sy
    });

    // only set snapped if actually snapped
    if (cx || snapPoints.vertical.indexOf(x) !== -1) {
      setSnapped(event, 'x', sx);
    }

    if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
      setSnapped(event, 'y', sy);
    }
  });

  // bendpoint snapping //////////////////////

  function getBendpointSnaps(context) {

    var snapPoints = context.snapPoints,
        waypoints = context.connection.waypoints,
        bendpointIndex = context.bendpointIndex;

    if (snapPoints) {
      return snapPoints;
    }

    var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ];

    context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };

    forEach(referenceWaypoints, function(p) {

      // we snap on existing bendpoints only,
      // not placeholders that are inserted during add
      if (p) {
        p = p.original || p;

        snapPoints.horizontal.push(p.y);
        snapPoints.vertical.push(p.x);
      }
    });

    return snapPoints;
  }

  eventBus.on([ 'bendpoint.move.move', 'bendpoint.move.end' ], 1500, function(event) {

    var context = event.context,
        snapPoints = getBendpointSnaps(context),
        hover = context.hover,
        hoverMid = hover && mid(hover),
        x = event.x,
        y = event.y,
        sx, sy;

    if (!snapPoints) {
      return;
    }

    // snap to target mid
    sx = snapTo(hoverMid ? snapPoints.vertical.concat([ hoverMid.x ]) : snapPoints.vertical, x);
    sy = snapTo(hoverMid ? snapPoints.horizontal.concat([ hoverMid.y ]) : snapPoints.horizontal, y);

    // correction x/y
    var cx = (x - sx),
        cy = (y - sy);

    // update delta
    assign(event, {
      dx: event.dx - cx,
      dy: event.dy - cy,
      x: event.x - cx,
      y: event.y - cy
    });

    // only set snapped if actually snapped
    if (cx || snapPoints.vertical.indexOf(x) !== -1) {
      setSnapped(event, 'x', sx);
    }

    if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
      setSnapped(event, 'y', sy);
    }
  });
}

BendpointSnapping.$inject = [ 'eventBus' ];<commit_msg>chore(bendpoints): fix comment<commit_after>import {
  assign,
  forEach,
  isArray
} from 'min-dash';

import { setSnapped } from '../snapping/SnapUtil';

var abs= Math.abs,
    round = Math.round;

var TOLERANCE = 10;

export default function BendpointSnapping(eventBus) {

  function snapTo(values, value) {

    if (isArray(values)) {
      var i = values.length;

      while (i--) if (abs(values[i] - value) <= TOLERANCE) {
        return values[i];
      }
    } else {
      values = +values;
      var rem = value % values;

      if (rem < TOLERANCE) {
        return value - rem;
      }

      if (rem > values - TOLERANCE) {
        return value - rem + values;
      }
    }

    return value;
  }

  function mid(element) {
    if (element.width) {
      return {
        x: round(element.width / 2 + element.x),
        y: round(element.height / 2 + element.y)
      };
    }
  }

  // connection segment snapping //////////////////////

  function getConnectionSegmentSnaps(context) {

    var snapPoints = context.snapPoints,
        connection = context.connection,
        waypoints = connection.waypoints,
        segmentStart = context.segmentStart,
        segmentStartIndex = context.segmentStartIndex,
        segmentEnd = context.segmentEnd,
        segmentEndIndex = context.segmentEndIndex,
        axis = context.axis;

    if (snapPoints) {
      return snapPoints;
    }

    var referenceWaypoints = [
      waypoints[segmentStartIndex - 1],
      segmentStart,
      segmentEnd,
      waypoints[segmentEndIndex + 1]
    ];

    if (segmentStartIndex < 2) {
      referenceWaypoints.unshift(mid(connection.source));
    }

    if (segmentEndIndex > waypoints.length - 3) {
      referenceWaypoints.unshift(mid(connection.target));
    }

    context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };

    forEach(referenceWaypoints, function(p) {

      // we snap on existing bendpoints only,
      // not placeholders that are inserted during add
      if (p) {
        p = p.original || p;

        if (axis === 'y') {
          snapPoints.horizontal.push(p.y);
        }

        if (axis === 'x') {
          snapPoints.vertical.push(p.x);
        }
      }
    });

    return snapPoints;
  }

  eventBus.on('connectionSegment.move.move', 1500, function(event) {
    var context = event.context,
        snapPoints = getConnectionSegmentSnaps(context),
        x = event.x,
        y = event.y,
        sx, sy;

    if (!snapPoints) {
      return;
    }

    // snap
    sx = snapTo(snapPoints.vertical, x);
    sy = snapTo(snapPoints.horizontal, y);

    // correction x/y
    var cx = (x - sx),
        cy = (y - sy);

    // update delta
    assign(event, {
      dx: event.dx - cx,
      dy: event.dy - cy,
      x: sx,
      y: sy
    });

    // only set snapped if actually snapped
    if (cx || snapPoints.vertical.indexOf(x) !== -1) {
      setSnapped(event, 'x', sx);
    }

    if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
      setSnapped(event, 'y', sy);
    }
  });

  // bendpoint snapping //////////////////////

  function getBendpointSnaps(context) {

    var snapPoints = context.snapPoints,
        waypoints = context.connection.waypoints,
        bendpointIndex = context.bendpointIndex;

    if (snapPoints) {
      return snapPoints;
    }

    var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ];

    context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };

    forEach(referenceWaypoints, function(p) {

      // we snap on existing bendpoints only,
      // not placeholders that are inserted during add
      if (p) {
        p = p.original || p;

        snapPoints.horizontal.push(p.y);
        snapPoints.vertical.push(p.x);
      }
    });

    return snapPoints;
  }

  eventBus.on([ 'bendpoint.move.move', 'bendpoint.move.end' ], 1500, function(event) {

    var context = event.context,
        snapPoints = getBendpointSnaps(context),
        hover = context.hover,
        hoverMid = hover && mid(hover),
        x = event.x,
        y = event.y,
        sx, sy;

    if (!snapPoints) {
      return;
    }

    // snap to hover mid
    sx = snapTo(hoverMid ? snapPoints.vertical.concat([ hoverMid.x ]) : snapPoints.vertical, x);
    sy = snapTo(hoverMid ? snapPoints.horizontal.concat([ hoverMid.y ]) : snapPoints.horizontal, y);

    // correction x/y
    var cx = (x - sx),
        cy = (y - sy);

    // update delta
    assign(event, {
      dx: event.dx - cx,
      dy: event.dy - cy,
      x: event.x - cx,
      y: event.y - cy
    });

    // only set snapped if actually snapped
    if (cx || snapPoints.vertical.indexOf(x) !== -1) {
      setSnapped(event, 'x', sx);
    }

    if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
      setSnapped(event, 'y', sy);
    }
  });
}

BendpointSnapping.$inject = [ 'eventBus' ];<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require ./constants
//= require ./promise
//= require ./client
//= require ./hosted_form
//= require ./paypal_button
<commit_msg>Use sprockets virtual paths in frontend manifest<commit_after>// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require solidus_paypal_braintree/constants
//= require solidus_paypal_braintree/promise
//= require solidus_paypal_braintree/client
//= require solidus_paypal_braintree/hosted_form
//= require solidus_paypal_braintree/paypal_button
<|endoftext|>