<commit_before>var System;
(function (System) {
    var Reflection = (function () {
        function Reflection() {
        }
        // PUBLIC: caching of function paths
        Reflection.cacheNameSpace = function () {
            var namespaces = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                namespaces[_i - 0] = arguments[_i];
            }
            for (var i = 0, len_i = namespaces.length; i < len_i; i++) {
                var elem = namespaces[i];
                if (elem in Reflection.cachedNameSpaces)
                    continue;
                var objtype = Reflection.checkCallPath(window, elem.split('.'));
                if (objtype)
                    Reflection.cachedNameSpaces[elem] = objtype;
            }
        };
        // PUBLIC: caching of object prototypes
        Reflection.cacheType = function (proto) {
            if (!proto)
                return;
            if (typeof proto == "string") {
                // allready cached
                if (Reflection.cachedTypes[proto])
                    return proto;
                var classname = proto.substr(proto.lastIndexOf('.') + 1, proto.length);
                if (!classname)
                    return null;
                // not yet cahed
                for (var ns in Reflection.cachedNameSpaces) {
                    var ref = Reflection.cachedNameSpaces[ns];
                    for (var cls in ref) {
                        var tempObj = ref[cls];
                        if (tempObj && Reflection.getClassName(tempObj) == classname) {
                            var path = ns + '.' + classname;
                            Reflection.cachedTypes[path] = tempObj.prototype;
                            return path;
                        }
                    }
                }
            }
            else {
                if (typeof proto != "object")
                    return null;
                var classname = Reflection.getClassName(proto);
                if (!classname || classname == "Object")
                    return null;
                var path = null;
                // allready cached
                for (path in Reflection.cachedTypes)
                    if (Reflection.cachedTypes[path] === proto.__proto__) {
                        return path;
                        break;
                    }
                // not yet cached
                for (var ns in Reflection.cachedNameSpaces) {
                    var ref = Reflection.cachedNameSpaces[ns];
                    for (var cls in ref) {
                        var tempObj = ref[cls];
                        if (tempObj && tempObj.prototype == proto.__proto__) {
                            var path = ns + '.' + classname;
                            Reflection.cachedTypes[path] = proto.__proto__;
                            return path;
                        }
                    }
                }
            }
            return null;
        };
        // PRIVATE: decaching of function paths
        Reflection.decacheType = function (classpath) {
            if (!classpath)
                return null;
            if (Reflection.cachedTypes[classpath] != undefined
                || Reflection.cacheType(classpath) == classpath)
                return Reflection.cachedTypes[classpath];
            return null;
        };
        // PUBLIC: gets object protoype name
        Reflection.getClassName = function (obj) {
            if (!obj)
                return "undefined";
            var str = (obj.prototype ? obj.prototype.constructor : obj.constructor).toString();
            var cname = str.match(/function\s(\w*)/)[1];
            return ["", "anonymous", "Anonymous"].indexOf(cname) > -1 ? "Function" : cname;
        };
        // PUBLIC: serializes an object
        Reflection.serialize = function (obj) {
            var namespaces = [];
            for (var _i = 1; _i < arguments.length; _i++) {
                namespaces[_i - 1] = arguments[_i];
            }
            Reflection.cacheNameSpace.apply(this, namespaces);
            return JSON.stringify(Reflection.decycle(obj));
        };
        // PRIVATE: decycles an object
        Reflection.decycle = function (value, ret, map, keys, lastname, seq) {
            if (ret === void 0) { ret = null; }
            if (map === void 0) { map = []; }
            if (keys === void 0) { keys = []; }
            if (lastname === void 0) { lastname = "."; }
            if (seq === void 0) { seq = 0; }
            if (value && typeof value == 'object')
                if (value instanceof Date)
                    return { $date: value.toJSON() };
                else if (value instanceof RegExp)
                    return { $regex: value.toString() };
                else {
                    if (ret == null) {
                        Reflection.seq = -1;
                        if (value instanceof Array)
                            ret = [];
                        else
                            ret = {};
                    }
                    var i = 0, len_i = map.length;
                    for (; i < len_i && map[i] !== value; i++)
                        ;
                    if (i < len_i)
                        return { $ref: keys[i] };
                    if (value instanceof Array) {
                        if (value.length > 0) {
                            map.push(value);
                            keys.push(seq + '.' + lastname);
                            for (var i = 0, len_i = value.length; i < len_i; i++) {
                                var value_i = value[i];
                                if (value_i && typeof value_i == "object")
                                    if (value_i instanceof Date)
                                        ret[i] = { $date: value_i.toJSON() };
                                    else if (value_i instanceof RegExp)
                                        ret[i] = { $regex: value_i.toString() };
                                    else
                                        ret[i] = Reflection.decycle(value_i, (value_i instanceof Array) ? [] : {}, map, keys, lastname + '.' + i, seq);
                                else
                                    ret.push(value_i);
                            }
                        }
                    }
                    else {
                        Reflection.seq++;
                        map.push(value);
                        keys.push(ret["$id"] = Reflection.seq.toString());
                        var vp = Reflection.cacheType(value);
                        if (vp)
                            ret["$type"] = vp;
                        for (var name in value)
                            if (name[0] != '$') {
                                var property = value[name];
                                if (property && typeof property == "object")
                                    if (property instanceof Date)
                                        ret[name] = { $date: property.toJSON() };
                                    else if (property instanceof RegExp)
                                        ret[name] = { $regex: property.toString() };
                                    else
                                        ret[name] = Reflection.decycle(property, (property instanceof Array) ? [] : {}, map, keys, name, Reflection.seq);
                                else
                                    ret[name] = property;
                            }
                    }
                    return ret;
                }
            return value;
        };
        // PUBLIC: deseriale a JSON string
        Reflection.deserialize = function (s) {
            var namespaces = [];
            for (var _i = 1; _i < arguments.length; _i++) {
                namespaces[_i - 1] = arguments[_i];
            }
            Reflection.cacheNameSpace.apply(this, namespaces);
            return Reflection.retrocycle(JSON.parse(s, function (k, v) {
                if (v && typeof v == "object")
                    if (v.$type) {
                        var dt = Reflection.decacheType(v.$type);
                        if (dt) {
                            v.__proto__ = dt;
                        }
                        delete v.$type;
                    }
                    else if (v.$date)
                        return new Date(v.$date);
                    else if (v.$regex) {
                        var regexp = v.$regex.match(/\/(.*)\/(.*)?/);
                        regexp.shift();
                        return RegExp.apply(null, regexp);
                    }
                return v;
            }));
        };
        // PRIVATE: retrocycles an object
        Reflection.retrocycle = function (value, map, lastname, lastseq) {
            if (map === void 0) { map = {}; }
            if (lastname === void 0) { lastname = "."; }
            if (lastseq === void 0) { lastseq = "0"; }
            if (value && typeof value == 'object') {
                if (value instanceof Array) {
                    map[lastseq + '.' + lastname] = value;
                    for (var i = 0, len_i = value.length; i < len_i; i++) {
                        var value_i = value[i];
                        if (value_i && typeof value_i == "object")
                            value[i] = Reflection.retrocycle(value_i, map, lastname + '.' + i, lastseq);
                    }
                }
                else if (value.$ref)
                    value = map[value.$ref];
                else
                    for (var name in value) {
                        var property = value[name];
                        if (property && typeof property == "object")
                            if (value.$id != undefined) {
                                map[value.$id] = value;
                                value[name] = Reflection.retrocycle(property, map, name, value.$id);
                                delete value.$id;
                            }
                            else
                                value[name] = Reflection.retrocycle(property, map, name);
                    }
            }
            return value;
        };
        // PRIVATE: search for paths
        Reflection.checkCallPath = function (scope, path) {
            for (var i = 0, len_i = path.length; i < len_i; i++) {
                scope = scope[path[i]];
                if (typeof scope != "object")
                    return undefined;
            }
            return scope;
        };
        Reflection.cachedNameSpaces = {};
        Reflection.cachedTypes = {};
        Reflection.seq = 0;
        return Reflection;
    })();
    System.Reflection = Reflection;
})(System || (System = {}));
<commit_msg>Update Reflection.js<commit_after>/*
    Reflection.js
    2015-12-08

    The MIT License (MIT)

    Copyright (c) 2015 Davide Mannone

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
*/
var System;
(function (System) {
    var Reflection = (function () {
        function Reflection() {
        }
        Reflection.serialize = function (obj) {
            var callbackandnamespace = [];
            for (var _i = 1; _i < arguments.length; _i++) {
                callbackandnamespace[_i - 1] = arguments[_i];
            }
            var callback;
            if (callbackandnamespace && callbackandnamespace.length > 0) {
                if (typeof callbackandnamespace[0] == "function")
                    callback = callbackandnamespace.shift();
                Reflection.cacheNameSpace.apply(this, callbackandnamespace);
            }
            return JSON.stringify(Reflection.decycle(obj, callback));
        };
        Reflection.deserialize = function (s) {
            var callbackandnamespace = [];
            for (var _i = 1; _i < arguments.length; _i++) {
                callbackandnamespace[_i - 1] = arguments[_i];
            }
            var callback;
            if (callbackandnamespace && callbackandnamespace.length > 0) {
                if (typeof callbackandnamespace[0] == "function")
                    callback = callbackandnamespace.shift();
                Reflection.cacheNameSpace.apply(this, callbackandnamespace);
            }
            //return Reflection._deserialize(Reflection.retype(JSON.parse(s)));
            return Reflection.retrocycle(JSON.parse(s, function (k, v) {
                if (callback) {
                    var uservalue = callback(k, v);
                    if (uservalue != undefined)
                        return uservalue;
                }
                if (v && typeof v == "object")
                    if (v[Reflection.OBJECTTYPETOKEN]) {
                        var dt = Reflection.decacheType(v[Reflection.OBJECTTYPETOKEN]);
                        if (dt) {
                            v.__proto__ = dt;
                        }
                        delete v[Reflection.OBJECTTYPETOKEN];
                    }
                    else if (v[Reflection.DATETOKEN])
                        return new Date(v[Reflection.DATETOKEN]);
                    else if (v[Reflection.REGEXPTOKEN]) {
                        var regexp = v[Reflection.REGEXPTOKEN].match(/\/(.*)\/(.*)?/);
                        regexp.shift();
                        return RegExp.apply(null, regexp);
                    }
                return v;
            }));
        };
        // returns class/function name
        Reflection.getClassName = function (obj) {
            if (!obj)
                return "undefined";
            var str = (obj.prototype ? obj.prototype.constructor : obj.constructor).toString();
            var cname = str.match(/function\s(\w*)/)[1];
            return ["", "anonymous", "Anonymous"].indexOf(cname) > -1 ? "Function" : cname;
        };
        //public static getClassName(str: string = ""): string {  // alternative use this§: is slightly faster
        //  return str.substring(str.indexOf("n ") + 2, str.indexOf("("));
        //}
        // caches namespace/module
        Reflection.cacheNameSpace = function () {
            var namespaces = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                namespaces[_i - 0] = arguments[_i];
            }
            for (var i = 0, len_i = namespaces.length; i < len_i; i++) {
                var elem = namespaces[i];
                if (elem in Reflection.cachedNameSpaces)
                    continue;
                var objtype = Reflection.checkCallPath(window, elem.split('.'));
                if (objtype)
                    Reflection.cachedNameSpaces[elem] = objtype;
            }
        };
        // caches classes
        Reflection.cacheType = function (proto) {
            if (!proto)
                return;
            if (typeof proto == "string") {
                // already cached
                if (Reflection.cachedTypes[proto])
                    return proto;
                var classname = proto.substr(proto.lastIndexOf('.') + 1, proto.length);
                if (!classname)
                    return null;
                // not already cached
                for (var ns in Reflection.cachedNameSpaces) {
                    var ref = Reflection.cachedNameSpaces[ns];
                    for (var cls in ref) {
                        var tempObj = ref[cls];
                        if (tempObj && Reflection.getClassName(tempObj) == classname) {
                            var path = ns + '.' + classname;
                            Reflection.cachedTypes[path] = tempObj.prototype;
                            return path;
                        }
                    }
                }
            }
            else {
                if (typeof proto != "object")
                    return null;
                var classname = Reflection.getClassName(proto);
                if (!classname || classname == "Object")
                    return null;
                var path = null;
                // already cached
                for (path in Reflection.cachedTypes)
                    if (Reflection.cachedTypes[path] === proto.__proto__) {
                        return path;
                        break;
                    }
                // not already cached
                for (var ns in Reflection.cachedNameSpaces) {
                    var ref = Reflection.cachedNameSpaces[ns];
                    for (var cls in ref) {
                        var tempObj = ref[cls];
                        if (tempObj && tempObj.prototype == proto.__proto__) {
                            var path = ns + '.' + classname;
                            Reflection.cachedTypes[path] = proto.__proto__;
                            return path;
                        }
                    }
                }
            }
            return null;
        };
        Reflection.decycle = function (value, callback, ret, map, keys, lastname) {
            if (ret === void 0) { ret = null; }
            if (map === void 0) { map = []; }
            if (keys === void 0) { keys = []; }
            if (lastname === void 0) { lastname = ""; }
            if (ret == null && callback) {
                var uservalue = callback("", value);
                if (uservalue != undefined)
                    return uservalue;
            }
            if (value && typeof value == 'object')
                if (value instanceof Date)
                    return Reflection.createSimpleToken(Reflection.DATETOKEN, value.toJSON());
                else if (value instanceof RegExp)
                    return Reflection.createSimpleToken(Reflection.REGEXPTOKEN, value.toString());
                else {
                    if (ret == null) {
                        Reflection.seq = -1;
                        ret = (value instanceof Array) ? [] : {};
                    }
                    var i = 0, len_i = map.length;
                    for (; i < len_i && map[i] !== value; i++)
                        ;
                    if (i < len_i)
                        return { $ref: keys[i] };
                    //var index = map.indexOf(value);  // could be used instead of before but do not use it: to 3 times slower!!!!!!
                    //if (index >= 0)
                    //  return { $ref: keys[index] };
                    if (value instanceof Array) {
                        if (value.length > 0) {
                            map.push(value);
                            keys.push((lastname) ? lastname : Reflection.ARRAYPATHDELIMITER);
                            lastname += Reflection.ARRAYPATHDELIMITER;
                            for (var i = 0, len_i = value.length; i < len_i; i++) {
                                var value_i = value[i];
                                if (callback) {
                                    var uservalue = callback(lastname + i, value_i);
                                    if (uservalue != undefined) {
                                        ret[i] = uservalue;
                                        continue;
                                    }
                                }
                                if (value_i == undefined) {
                                    if (Reflection.IncludeUndefined)
                                        ret.push({ $undef: 1 });
                                    continue;
                                }
                                if (typeof value_i == "object")
                                    if (value_i instanceof Date)
                                        ret[i] = Reflection.createSimpleToken(Reflection.DATETOKEN, value_i.toJSON());
                                    else if (value_i instanceof RegExp)
                                        ret[i] = Reflection.createSimpleToken(Reflection.REGEXPTOKEN, value_i.toString());
                                    else
                                        ret[i] = Reflection.decycle(value_i, callback, (value_i instanceof Array) ? [] : {}, map, keys, lastname + i);
                                else
                                    ret.push(value_i);
                            }
                        }
                    }
                    else {
                        Reflection.seq++;
                        map.push(value);
                        keys.push(ret[Reflection.OBJECTIDTOKEN] = Reflection.seq.toString());
                        var vp = Reflection.cacheType(value);
                        if (vp)
                            ret[Reflection.OBJECTTYPETOKEN] = vp;
                        for (var name in value) {
                            if (callback) {
                                var uservalue = callback(name, value);
                                if (uservalue != undefined) {
                                    ret[name] = uservalue;
                                    continue;
                                }
                            }
                            if (name[0] != Reflection.NOTSERIALIZESTARTDELIMITER) {
                                var property = value[name];
                                if (property == undefined) {
                                    if (Reflection.IncludeUndefined)
                                        ret[name] = ({ $undef: 1 });
                                    continue;
                                }
                                if (typeof property == "object")
                                    if (property instanceof Date)
                                        ret[name] = Reflection.createSimpleToken(Reflection.DATETOKEN, property.toJSON());
                                    else if (property instanceof RegExp)
                                        ret[name] = Reflection.createSimpleToken(Reflection.REGEXPTOKEN, property.toString());
                                    else if (property instanceof Array)
                                        ret[name] = Reflection.decycle(property, callback, [], map, keys, Reflection.seq + Reflection.ARRAYPATHDELIMITER + name);
                                    else
                                        ret[name] = Reflection.decycle(property, callback, {}, map, keys, name);
                                else
                                    ret[name] = property;
                            }
                        }
                    }
                    return ret;
                }
            else if (value == undefined && Reflection.IncludeUndefined)
                return { $undef: 1 };
            else
                return value;
        };
        // does retrocycling of reflection cycled objects
        Reflection.retrocycle = function (value, map, lastname) {
            if (map === void 0) { map = {}; }
            if (lastname === void 0) { lastname = ""; }
            if (value && typeof value == 'object')
                if (value instanceof Array) {
                    map[(lastname) ? lastname : Reflection.ARRAYPATHDELIMITER] = value;
                    for (var i = 0, len_i = value.length; i < len_i; i++) {
                        var value_i = value[i];
                        if (value_i && typeof value_i == "object")
                            value[i] = Reflection.retrocycle(value_i, map, lastname + Reflection.ARRAYPATHDELIMITER + i);
                    }
                }
                else if (value.$ref)
                    return map[value.$ref];
                else if (value[Reflection.UNDEFINEDTOKEN])
                    return undefined;
                else if (value[Reflection.OBJECTIDTOKEN] != undefined) {
                    map[value[Reflection.OBJECTIDTOKEN]] = value;
                    for (var name in value) {
                        var property = value[name];
                        if (property && typeof property == "object")
                            value[name] = Reflection.retrocycle(property, map, value[Reflection.OBJECTIDTOKEN] + Reflection.ARRAYPATHDELIMITER + name);
                        else
                            value[name] = Reflection.retrocycle(property, map, name);
                    }
                    delete value[Reflection.OBJECTIDTOKEN];
                }
            return value;
        };
        // does retyping of object: NOT USED ANYMORE
        // instead used standard JSON.stringify that seems to have same performance
        Reflection.retype = function (obj) {
            if (obj && typeof obj == "object")
                for (var propr in obj) {
                    var elem = obj[propr];
                    if (elem) {
                        if (elem instanceof Array)
                            for (var i = 0, len_i = elem.length; i < len_i; i++)
                                Reflection.retype(elem[i]);
                        else
                            Reflection.retype(elem);
                    }
                    else if (propr == Reflection.OBJECTTYPETOKEN) {
                        var dt = Reflection.decacheType(elem[Reflection.OBJECTTYPETOKEN]);
                        if (dt)
                            obj.__proto__ = dt;
                        delete obj[Reflection.OBJECTTYPETOKEN];
                    }
                }
            return obj;
        };
        // uncaches a class
        Reflection.decacheType = function (classpath) {
            if (!classpath)
                return null;
            if (Reflection.cachedTypes[classpath] != undefined
                || Reflection.cacheType(classpath) == classpath)
                return Reflection.cachedTypes[classpath];
            return null;
        };
        // finds type path
        Reflection.checkCallPath = function (scope, path) {
            for (var i = 0, len_i = path.length; i < len_i; i++) {
                scope = scope[path[i]];
                if (typeof scope != "object")
                    return undefined;
            }
            return scope;
        };
        // creates serialization token
        Reflection.createSimpleToken = function (symbol, value) {
            var ret = {};
            ret[symbol] = value;
            return ret;
        };
        // PUBLIC REGION:
        // used tokens: by change it you can choose custom symbols/coding
        Reflection.NOTSERIALIZESTARTDELIMITER = '$';
        Reflection.OBJECTIDTOKEN = "$id";
        Reflection.OBJECTTYPETOKEN = "$type";
        Reflection.DATETOKEN = "$date";
        Reflection.REGEXPTOKEN = "$regex";
        Reflection.UNDEFINEDTOKEN = "$undef";
        Reflection.ARRAYPATHDELIMITER = '.';
        Reflection.IncludeUndefined = false; // true for preserve undefined values from be dropped by serialization
        // PRIVATE REGION:
        // cached namespaces/modules and types
        Reflection.cachedNameSpaces = {};
        Reflection.cachedTypes = {};
        // does decycling
        Reflection.seq = 0;
        return Reflection;
    })();
    System.Reflection = Reflection;
})(System || (System = {}));
<|endoftext|>


<commit_before>'use strict';

var Promise = require('bluebird');
const oadaLib = require('../../libs/oada-lib-arangodb');
const Responder = require('../../libs/oada-lib-kafka').Responder;
const pointer = require('json-pointer');
const hash = require('object-hash');
const error = require('debug')('write-handler:error');
const info = require('debug')('write-handler:info');
const trace = require('debug')('write-handler:trace');

var config = require('./config');

var responder = new Responder(
    config.get('kafka:topics:writeRequest'),
    config.get('kafka:topics:httpResponse'),
    'write-handlers'
);

responder.on('request', function handleReq(req, msg) {
    // TODO: Check scope/permission?
    req.source = req.source || '';
    var id = req['resource_id'];

    // Get body and check permission in parallel
    var body = Promise.try(function getBody() {
        return req.body ||
                req.bodyid && oadaLib.putBodies.getPutBody(req.bodyid);
    });
    let existing_resource_info = {};
    var permitted = Promise.try(function checkPermissions() {
        if (req.source === 'rev-graph-update') {
            // no need to check permission for rev graph updates
            return;
        }

        if (id) { // Only run checks if resource exists
            // TODO: Support sharing (i.e., not just owner has permission)
            var start = new Date().getTime();
            info(`Checking permissions of "${id}".`);
            return oadaLib.resources.getResourceOwnerIdRev(id) // { _id, _rev, _meta._owner }
                .then(results => {
                  existing_resource_info = results;
                  var end = new Date().getTime();
                  info(`Got owner (${results._meta._owner}) of "${id}" from arango. +${end-start}ms`);
                    if (existing_resource_info._meta._owner !== req['user_id']) {
                        return Promise.reject(new Error('permission'));
                    }
                });
        }
    });

    var start = new Date().getTime();
    info(`PUTing to "${req['path_leftover']}" in "${id}"`);
    var upsert = Promise.join(body, permitted, function doUpsert(body) {
        var path = pointer.parse(req['path_leftover'].replace(/\/*$/, ''));

        // Perform delete
        // TODO: Should deletes be a separate topic?
        if (body === undefined) {
            // TODO: How to handle rev etc. on DELETE?
            if (path.length > 0) {
                return oadaLib.resources.deleteSubResource(id, path);
            } else {
                return oadaLib.resources.deleteResource(id);
            }
        }

        var obj = {};
        var ts = Date.now();
        // TODO: Sanitize keys?

        // Create new resource
        if (!id) {
            id = 'resources/' + path[1];
            path = path.slice(2);

            // Initialize resource stuff
            obj = {
                '_type': req['content_type'],
                '_meta': {
                    '_id': id + '/_meta',
                    '_type': req['content_type'],
                    '_owner': req['user_id'],
                    'stats': {
                        'createdBy': req['user_id'],
                        'created': ts
                    },
                }
            };
        }
        // Create object to recursively merge into the resource
        if (path.length > 0) {
            let o = obj;

            let endk = path.pop();
            path.forEach(k => {
                if (!(k in o)) {
                    // TODO: Support arrays better?
                    o[k] = {};
                }
                o = o[k];
            });

            o[endk] = body;
        } else {
            obj = Object.assign(obj, body);
        }

        // Update meta
        var meta = {
            'modifiedBy': req['user_id'],
            'modified': ts
        };
        obj['_meta'] = Object.assign(obj['_meta'] || {}, meta);

        // Precompute new rev: using only the body so that subsequent PUT's
        var rev = msg.offset + '-' + hash(JSON.stringify(body), {algorithm: 'md5'});

        // If the hash part of the rev is identical to last time, don't re-execute a PUT to keep it idempotent
        existing_resource_info._rev = existing_resource_info._rev || '0-0';
        const old_rev_hash = existing_resource_info._rev.split('-')[1];
        const new_rev_hash = rev.split('-')[1];
        if (old_rev_hash === new_rev_hash) {
          info('PUT would result in same rev hash as the current one, skipping write.');
          return rev;
        } else {
          trace('PUT is a new hash ('+new_rev_hash+', old = '+ old_rev_hash+ '), performing write');
        }

        obj['_rev'] = rev;
        pointer.set(obj, '/_meta/_rev', rev);

        // Compute new change
        var change = {
            '_id': id + '/_meta/_changes',
            '_rev': rev,
            [rev]: {
                'merge': Object.assign({}, obj),
                'userId': req['user_id'],
                'authorizationID': req['authorizationid']
            },
        };
        obj['_meta'] = Object.assign({'_changes': change}, obj['_meta']);

        // Update rev of meta?
        obj['_meta']['_rev'] = rev;

        return oadaLib.resources.putResource(id, obj).return(rev);
    }).then(function respond(rev) {
        var end = new Date().getTime();
        info(`Finished PUTing to "${req['path_leftover']}". + ${end-start}ms`);
        return {
            'msgtype': 'write-response',
            'code': 'success',
            'resource_id': id,
            '_rev': rev,
            'user_id': req['user_id'],
            'authorizationid': req['authorizationid'],
        };
    }).catch(function respondErr(err) {
        error(err);
        return {
            'msgtype': 'write-response',
            'code': err.message || 'error',
            'user_id': req['user_id'],
            'authorizationid': req['authorizationid'],
        };
    });

    var cleanup = body.then(() => {
        // Remove putBody, if there was one
        return req.bodyid && oadaLib.putBodies.removePutBody(req.bodyid);
    });

    return Promise.join(upsert, cleanup, resp => resp);
});
<commit_msg>Improve hash computation for revs<commit_after>'use strict';

var Promise = require('bluebird');
const oadaLib = require('../../libs/oada-lib-arangodb');
const Responder = require('../../libs/oada-lib-kafka').Responder;
const pointer = require('json-pointer');
const hash = require('object-hash');
const error = require('debug')('write-handler:error');
const info = require('debug')('write-handler:info');
const trace = require('debug')('write-handler:trace');

var config = require('./config');

var responder = new Responder(
    config.get('kafka:topics:writeRequest'),
    config.get('kafka:topics:httpResponse'),
    'write-handlers'
);

responder.on('request', function handleReq(req, msg) {
    // TODO: Check scope/permission?
    req.source = req.source || '';
    var id = req['resource_id'];

    // Get body and check permission in parallel
    var body = Promise.try(function getBody() {
        return req.body ||
                req.bodyid && oadaLib.putBodies.getPutBody(req.bodyid);
    });
    let existing_resource_info = {};
    var permitted = Promise.try(function checkPermissions() {
        if (req.source === 'rev-graph-update') {
            // no need to check permission for rev graph updates
            return;
        }

        if (id) { // Only run checks if resource exists
            // TODO: Support sharing (i.e., not just owner has permission)
            var start = new Date().getTime();
            info(`Checking permissions of "${id}".`);
            return oadaLib.resources.getResourceOwnerIdRev(id) // { _id, _rev, _meta._owner }
                .then(results => {
                  existing_resource_info = results;
                  var end = new Date().getTime();
                  info(`Got owner (${results._meta._owner}) of "${id}" from arango. +${end-start}ms`);
                    if (existing_resource_info._meta._owner !== req['user_id']) {
                        return Promise.reject(new Error('permission'));
                    }
                });
        }
    });

    var start = new Date().getTime();
    info(`PUTing to "${req['path_leftover']}" in "${id}"`);
    var upsert = Promise.join(body, permitted, function doUpsert(body) {
        var path = pointer.parse(req['path_leftover'].replace(/\/*$/, ''));

        // Perform delete
        // TODO: Should deletes be a separate topic?
        if (body === undefined) {
            // TODO: How to handle rev etc. on DELETE?
            if (path.length > 0) {
                return oadaLib.resources.deleteSubResource(id, path);
            } else {
                return oadaLib.resources.deleteResource(id);
            }
        }

        var obj = {};
        var ts = Date.now();
        // TODO: Sanitize keys?

        // Create new resource
        if (!id) {
            id = 'resources/' + path[1];
            path = path.slice(2);

            // Initialize resource stuff
            obj = {
                '_type': req['content_type'],
                '_meta': {
                    '_id': id + '/_meta',
                    '_type': req['content_type'],
                    '_owner': req['user_id'],
                    'stats': {
                        'createdBy': req['user_id'],
                        'created': ts
                    },
                }
            };
        }
        // Create object to recursively merge into the resource
        if (path.length > 0) {
            let o = obj;

            let endk = path.pop();
            path.forEach(k => {
                if (!(k in o)) {
                    // TODO: Support arrays better?
                    o[k] = {};
                }
                o = o[k];
            });

            o[endk] = body;
        } else {
            obj = Object.assign(obj, body);
        }

        // Precompute new rev ignoring _meta and such
        let new_rev_hash = hash(JSON.stringify(obj), {algorithm: 'md5'});

        // Update meta
        var meta = {
            'modifiedBy': req['user_id'],
            'modified': ts
        };
        obj['_meta'] = Object.assign(obj['_meta'] || {}, meta);

        var rev = msg.offset + '-' + new_rev_hash;

        // If the hash part of the rev is identical to last time, don't re-execute a PUT to keep it idempotent
        existing_resource_info._rev = existing_resource_info._rev || '0-0';
        const old_rev_hash = existing_resource_info._rev.split('-')[1];
        if (old_rev_hash === new_rev_hash) {
          info('PUT would result in same rev hash as the current one, skipping write.');
          return rev;
        } else {
          trace('PUT is a new hash ('+new_rev_hash+', old = '+ old_rev_hash+ '), performing write');
        }

        obj['_rev'] = rev;
        pointer.set(obj, '/_meta/_rev', rev);

        // Compute new change
        var change = {
            '_id': id + '/_meta/_changes',
            '_rev': rev,
            [rev]: {
                'merge': Object.assign({}, obj),
                'userId': req['user_id'],
                'authorizationID': req['authorizationid']
            },
        };
        obj['_meta'] = Object.assign({'_changes': change}, obj['_meta']);

        // Update rev of meta?
        obj['_meta']['_rev'] = rev;

        return oadaLib.resources.putResource(id, obj).return(rev);
    }).then(function respond(rev) {
        var end = new Date().getTime();
        info(`Finished PUTing to "${req['path_leftover']}". + ${end-start}ms`);
        return {
            'msgtype': 'write-response',
            'code': 'success',
            'resource_id': id,
            '_rev': rev,
            'user_id': req['user_id'],
            'authorizationid': req['authorizationid'],
        };
    }).catch(function respondErr(err) {
        error(err);
        return {
            'msgtype': 'write-response',
            'code': err.message || 'error',
            'user_id': req['user_id'],
            'authorizationid': req['authorizationid'],
        };
    });

    var cleanup = body.then(() => {
        // Remove putBody, if there was one
        return req.bodyid && oadaLib.putBodies.removePutBody(req.bodyid);
    });

    return Promise.join(upsert, cleanup, resp => resp);
});
<|endoftext|>


<commit_before>(function (angular) {
    'use strict';
    var archivoPerfilControllerApp = angular.module('archivoPerfil', ['ngAnimate', 'ngSanitize', 'ui.bootstrap', 'ui.grid', 'ngTouch', 'ui.grid.edit', 'ui.grid.autoResize', 'ui.grid.selection', 'ui.grid.cellNav', 'xeditable'])
    .controller('archivoPerfilController', ['$scope', '$http', '$window', '$location', '$filter', 'uiGridConstants', 'i18nService', '$scope', function ($scope, $http, $window, $location, $filter, i18nService, uiGridConstants) {
        var init = function () {
            $scope.archivoID = $window.localStorage.getItem("archivoID");
            // DATOS PRINCIPALES
            $scope.cargarDatosPrincipales();
            // ANOTACIONES
            screenTop.cargarNotas();
        };

        //Bandera para saber cuando guardar o no
        $scope.datosPrincipales.editado = false;
        $scope.guardarDatos = false;

        //Datos principales
        $scope.archivoDatos = {};
        $scope.datosPrincipales = {};
        $scope.datosPrincipales.archivoTitulo = "";
        $scope.datosPrincipales.archivoNombre = "";
        $scope.datosPrincipales.institucionFondo = "";
        $scope.datosPrincipales.seccion = "";
        $scope.datosPrincipales.numRefDentroFondo = "";
        $scope.datosPrincipales.archivoFecha = "";
        $scope.datosPrincipales.folioInicial = "";
        $scope.datosPrincipales.folioFinal = "";
        $scope.datosPrincipales.legajo = "";
        $scope.datosPrincipales.numOrden = "";
        $scope.datosPrincipales.numPaginas = "";
        $scope.datosPrincipales.palabrasClaves = {};
        $scope.datosPrincipales.disponibilidad = "";

        $scope.cargarDatosPrincipales = function () {
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/ConsultaArchivo?archivoID=' + $scope.archivoID).then(function (data) {
                //Obtener los datos JSON
                $scope.archivoDatos = data.data[0];
                //Log
                console.log($scope.archivoDatos);

                //Llenamos los datos del archivo
                $scope.datosPrincipales.archivoTitulo = $scope.archivoDatos.titulo;
                $scope.datosPrincipales.archivoNombre = $scope.archivoDatos.fondo;
                $scope.datosPrincipales.institucionFondo = $scope.archivoDatos.institucionFondo;
                $scope.datosPrincipales.seccion = $scope.archivoDatos.seccion;
                $scope.datosPrincipales.numRefDentroFondo = $scope.archivoDatos.numRefDentroFondo;
                $scope.datosPrincipales.archivoFecha = $filter('date')(new Date($scope.archivoDatos.fechaInicial), String($scope.archivoDatos.fechaInicialFormato).toLowerCase()) + " - " + $filter('date')(new Date($scope.archivoDatos.fechaFinal), String($scope.archivoDatos.fechaFinalFormato).toLowerCase());
                $scope.datosPrincipales.folioInicial = $scope.archivoDatos.folioInicial;
                $scope.datosPrincipales.folioFinal = $scope.archivoDatos.folioFinal;
                $scope.datosPrincipales.legajo = $scope.archivoDatos.legajo;
                $scope.datosPrincipales.numOrden = $scope.archivoDatos.numOrden;
                $scope.datosPrincipales.numPaginas = $scope.archivoDatos.numPaginas;
                $scope.datosPrincipales.palabrasClaves = $scope.archivoDatos.palabrasClaves.split(",");
                $scope.datosPrincipales.palabrasClaves = $scope.datosPrincipales.palabrasClaves.map(function (e) { return e.trim(); });

                $scope.datosPrincipales.disponibilidad = $scope.archivoDatos.disponibilidad;
                //Limpiamos la bandera de editado
                $scope.datosPrincipales.editado = false;

                //Para palabras claves
                $scope.palabraNueva = {mensaje:"+Agregar"};

            });
        };

        // Anotaciones
        $scope.notas = {};
        $scope.cargarNotas = function () {
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/Consulta/ArchivosNotas?archivoID=' + $scope.archivoID).then(function (data) {
                if (data[0:8] != "[WARNING]") {
                    $scope.notas = data.data;
                    // LOG
                    console.log($scope.notas);
                }
            });

        };
        $scope.agregarNotaVacia = function () {
            $scope.notas.push({
                nota:"",
                referencia:"",
                fechaHistorica:"",
                fechaHistFormato:""
            });

        }

        // Initialization fucntion
        init();

        // Button functions
        $scope.borrarCambios = function () {
            if (window.confirm("Esta Seguro que quiere borrar los cambios?") === true) {
                init();
            }
        };
        $scope.guardarCambios = function () {
            if($scope.guardarDatos) {

                //Revisamos datos principales editados
                if ($scope.datosPrincipales.editado) {

                }
            }
        };

        // Para borrar palabras claves
        $scope.borrarSiVacio = function (indexEditada, palabra) {
            if (palabra == "") {
                $scope.datosPrincipales.palabrasClaves.splice(indexEditada,1);
            }
        }
        //Para agregar palabras claves
        $scope.palabraNueva = {mensaje: '+Agregar'};

        $scope.borrarCampo = function () {
            $scope.palabraNueva.mensaje = "";
        }
        $scope.mostrarCampo = function () {
            $scope.palabraNueva.mensaje = "+Agregar";
            //$scope.;
        }
        $scope.agregarPalabraNueva = function (palabra) {
            if (!$scope.datosPrincipales.palabrasClaves.includes(palabra) && palabra.length != 0) {
                $scope.datosPrincipales.palabrasClaves.push(palabra);
            }
            $scope.palabraNueva.mensaje = "+Agregar";
        }
    }]);
    archivoPerfilControllerApp.run(function (editableOptions,editableThemes) {
        editableThemes.bs3.inputClass = 'input-sm';
        editableThemes.bs3.buttonsClass = 'btn-sm';
        editableOptions.theme = 'bs3';
        editableOptions.buttons = 'no';
    });
})(window.angular);

<commit_msg>agregando notas<commit_after>(function (angular) {
    'use strict';
    var archivoPerfilControllerApp = angular.module('archivoPerfil', ['ngAnimate', 'ngSanitize', 'ui.bootstrap', 'ui.grid', 'ngTouch', 'ui.grid.edit', 'ui.grid.autoResize', 'ui.grid.selection', 'ui.grid.cellNav', 'xeditable'])
    .controller('archivoPerfilController', ['$scope', '$http', '$window', '$location', '$filter', 'uiGridConstants', 'i18nService', '$scope', function ($scope, $http, $window, $location, $filter, i18nService, uiGridConstants) {
        var init = function () {
            $scope.archivoID = $window.localStorage.getItem("archivoID");
            // DATOS PRINCIPALES
            $scope.cargarDatosPrincipales();
            // ANOTACIONES
            screenTop.cargarNotas();
        };

        //Bandera para saber cuando guardar o no
        $scope.datosPrincipales.editado = false;
        $scope.guardarDatos = false;

        //Datos principales
        $scope.archivoDatos = {};
        $scope.datosPrincipales = {};
        $scope.datosPrincipales.archivoTitulo = "";
        $scope.datosPrincipales.archivoNombre = "";
        $scope.datosPrincipales.institucionFondo = "";
        $scope.datosPrincipales.seccion = "";
        $scope.datosPrincipales.numRefDentroFondo = "";
        $scope.datosPrincipales.archivoFecha = "";
        $scope.datosPrincipales.folioInicial = "";
        $scope.datosPrincipales.folioFinal = "";
        $scope.datosPrincipales.legajo = "";
        $scope.datosPrincipales.numOrden = "";
        $scope.datosPrincipales.numPaginas = "";
        $scope.datosPrincipales.palabrasClaves = {};
        $scope.datosPrincipales.disponibilidad = "";

        $scope.cargarDatosPrincipales = function () {
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/ConsultaArchivo?archivoID=' + $scope.archivoID).then(function (data) {
                //Obtener los datos JSON
                $scope.archivoDatos = data.data[0];
                //Log
                console.log($scope.archivoDatos);

                //Llenamos los datos del archivo
                $scope.datosPrincipales.archivoTitulo = $scope.archivoDatos.titulo;
                $scope.datosPrincipales.archivoNombre = $scope.archivoDatos.fondo;
                $scope.datosPrincipales.institucionFondo = $scope.archivoDatos.institucionFondo;
                $scope.datosPrincipales.seccion = $scope.archivoDatos.seccion;
                $scope.datosPrincipales.numRefDentroFondo = $scope.archivoDatos.numRefDentroFondo;
                $scope.datosPrincipales.archivoFecha = $filter('date')(new Date($scope.archivoDatos.fechaInicial), String($scope.archivoDatos.fechaInicialFormato).toLowerCase()) + " - " + $filter('date')(new Date($scope.archivoDatos.fechaFinal), String($scope.archivoDatos.fechaFinalFormato).toLowerCase());
                $scope.datosPrincipales.folioInicial = $scope.archivoDatos.folioInicial;
                $scope.datosPrincipales.folioFinal = $scope.archivoDatos.folioFinal;
                $scope.datosPrincipales.legajo = $scope.archivoDatos.legajo;
                $scope.datosPrincipales.numOrden = $scope.archivoDatos.numOrden;
                $scope.datosPrincipales.numPaginas = $scope.archivoDatos.numPaginas;
                $scope.datosPrincipales.palabrasClaves = $scope.archivoDatos.palabrasClaves.split(",");
                $scope.datosPrincipales.palabrasClaves = $scope.datosPrincipales.palabrasClaves.map(function (e) { return e.trim(); });

                $scope.datosPrincipales.disponibilidad = $scope.archivoDatos.disponibilidad;
                //Limpiamos la bandera de editado
                $scope.datosPrincipales.editado = false;

                //Para palabras claves
                $scope.palabraNueva = {mensaje:"+Agregar"};

            });
        };

        // Anotaciones
        $scope.notas = {};
        $scope.cargarNotas = function () {
            $http.get('http://monsalvediaz.com:5000/PIMC0.1/Consulta/ArchivosNotas?archivoID=' + $scope.archivoID).then(function (data) {
                if (str(data).substr(0, 8) != "[WARNING]") {
                    $scope.notas = data.data;
                    // LOG
                    console.log($scope.notas);
                }
            });

        };
        $scope.agregarNotaVacia = function () {
            $scope.notas.push({
                nota:"",
                referencia:"",
                fechaHistorica:"",
                fechaHistFormato:""
            });

        }

        // Initialization fucntion
        init();

        // Button functions
        $scope.borrarCambios = function () {
            if (window.confirm("Esta Seguro que quiere borrar los cambios?") === true) {
                init();
            }
        };
        $scope.guardarCambios = function () {
            if($scope.guardarDatos) {

                //Revisamos datos principales editados
                if ($scope.datosPrincipales.editado) {

                }
            }
        };

        // Para borrar palabras claves
        $scope.borrarSiVacio = function (indexEditada, palabra) {
            if (palabra == "") {
                $scope.datosPrincipales.palabrasClaves.splice(indexEditada,1);
            }
        }
        //Para agregar palabras claves
        $scope.palabraNueva = {mensaje: '+Agregar'};

        $scope.borrarCampo = function () {
            $scope.palabraNueva.mensaje = "";
        }
        $scope.mostrarCampo = function () {
            $scope.palabraNueva.mensaje = "+Agregar";
            //$scope.;
        }
        $scope.agregarPalabraNueva = function (palabra) {
            if (!$scope.datosPrincipales.palabrasClaves.includes(palabra) && palabra.length != 0) {
                $scope.datosPrincipales.palabrasClaves.push(palabra);
            }
            $scope.palabraNueva.mensaje = "+Agregar";
        }
    }]);
    archivoPerfilControllerApp.run(function (editableOptions,editableThemes) {
        editableThemes.bs3.inputClass = 'input-sm';
        editableThemes.bs3.buttonsClass = 'btn-sm';
        editableOptions.theme = 'bs3';
        editableOptions.buttons = 'no';
    });
})(window.angular);

<|endoftext|>


<commit_before>/*
---
name: ART
description: "The heart of ART."
requires: [Core/Class, Color/Color]
provides: [ART, ART.Element, ART.Container, ART.Transform]
...
*/

(function(){

this.ART = new Class;

ART.version = '09.dev';
ART.build = 'DEV';

ART.Element = new Class({

	/* dom */

	inject: function(element){
		if (element.element) element = element.element;
		element.appendChild(this.element);
		return this;
	},

	eject: function(){
		var element = this.element, parent = element.parentNode;
		if (parent) parent.removeChild(element);
		return this;
	},

	/* events */

	subscribe: function(type, fn, bind){
		if (typeof type != 'string'){ // listen type / fn with object
			var subscriptions = [];
			for (var t in type) subscriptions.push(this.subscribe(t, type[t]));
			return function(){ // unsubscribe
				for (var i = 0, l = subscriptions.length; i < l; i++)
					subscriptions[i]();
				return this;
			};
		} else { // listen to one
			var bound = fn.bind(bind || this);
			var element = this.element;
			if (element.addEventListener){
				element.addEventListener(type, bound, false);
				return function(){ // unsubscribe
					element.removeEventListener(type, bound, false);
					return this;
				};
			} else {
				element.attachEvent('on' + type, bound);
				return function(){ // unsubscribe
					element.detachEvent('on' + type, bound);
					return this;
				};
			}
		}
	}

});

ART.Container = new Class({

	grab: function(){
		for (var i = 0; i < arguments.length; i++) arguments[i].inject(this);
		return this;
	}

});

var transformTo = function(xx, yx, xy, yy, tx, ty){
	if (xx && typeof xx == 'object'){
		yx = xx.yx; yy = xx.yy; ty = xx.ty;
		xy = xx.xy; tx = xx.tx; xx = xx.xx;
	}
	this.xx = xx == null ? 1 : xx;
	this.yx = yx || 0;
	this.xy = xy || 0;
	this.yy = yy == null ? 1 : yy;
	this.tx = (tx == null ? this.tx : tx) || 0;
	this.ty = (ty == null ? this.ty : ty) || 0;
	this._transform();
	return this;
};

var translate = function(x, y){
	this.tx += x || 0;
	this.ty += y || 0;
	this._transform();
	return this;
};

ART.Transform = new Class({

	initialize: transformTo,

	_transform: function(){},

	xx: 1, yx: 0, tx: 0,
	xy: 0, yy: 1, ty: 0,

	transform: function(xx, yx, xy, yy, tx, ty){
		var m = this;
		if (xx && typeof xx == 'object'){
			yx = xx.yx; yy = xx.yy; ty = xx.ty;
			xy = xx.xy; tx = xx.tx; xx = xx.xx;
		}
		if (!tx) tx = 0;
		if (!ty) ty = 0;
		return this.transformTo(
			m.xx * xx + m.xy * yx,
			m.yx * xx + m.yy * yx,
			m.xx * xy + m.xy * yy,
			m.yx * xy + m.yy * yy,
			m.xx * tx + m.xy * ty + m.tx,
			m.yx * tx + m.yy * ty + m.ty
		);
	},

	transformTo: transformTo,

	translate: translate,
	move: translate,

	scale: function(x, y){
		if (y == null) y = x;
		return this.transform(x, 0, 0, y, 0, 0);
	},

	rotate: function(deg, x, y){
		if (x == null || y == null){
			x = (this.left || 0) + (this.width || 0) / 2;
			y = (this.top || 0) + (this.height || 0) / 2;
		}

		var rad = deg * Math.PI / 180, sin = Math.sin(rad), cos = Math.cos(rad);

		this.transform(1, 0, 0, 1, x, y);
		var m = this;

		return this.transformTo(
			cos * m.xx - sin * m.yx,
			sin * m.xx + cos * m.yx,
			cos * m.xy - sin * m.yy,
			sin * m.xy + cos * m.yy,
			m.tx,
			m.ty
		).transform(1, 0, 0, 1, -x, -y);
	},

	moveTo: function(x, y){
		var m = this;
		return this.transformTo(m.xx, m.yx, m.xy, m.yy, x, y);
	},

	rotateTo: function(deg, x, y){
		var m = this;
	    var flip = m.yx / m.xx > m.yy / m.xy ? -1 : 1;
		if (m.xx < 0 ? m.xy >= 0 : m.xy < 0) flip = -flip;
		return this.rotate(deg - Math.atan2(flip * m.yx, flip * m.xx) * 180 / Math.PI, x, y);
	},

	scaleTo: function(x, y){
		// Normalize
		var m = this;

		var h = Math.sqrt(m.xx * m.xx + m.yx * m.yx);
		m.xx /= h; m.yx /= h;

		h = Math.sqrt(m.yy * m.yy + m.xy * m.xy);
		m.yy /= h; m.xy /= h;

		return this.scale(x, y);
	},

	resizeTo: function(width, height){
		var w = this.width, h = this.height;
		if (!w || !h) return this;
		return this.scaleTo(width / w, height / h);
	},

	point: function(x, y){
		var m = this;
		return {
			x: m.xx * x + m.xy * y + m.tx,
			y: m.yx * x + m.yy * y + m.ty
		};
	}

});

Color.detach = function(color){
	color = new Color(color);
	return [Color.rgb(color.red, color.green, color.blue).toString(), color.alpha];
};

})();

<commit_msg>Fix indentation in ART.js<commit_after>/*
---
name: ART
description: "The heart of ART."
requires: [Core/Class, Color/Color]
provides: [ART, ART.Element, ART.Container, ART.Transform]
...
*/

(function(){

this.ART = new Class;

ART.version = '09.dev';
ART.build = 'DEV';

ART.Element = new Class({

	/* dom */

	inject: function(element){
		if (element.element) element = element.element;
		element.appendChild(this.element);
		return this;
	},

	eject: function(){
		var element = this.element, parent = element.parentNode;
		if (parent) parent.removeChild(element);
		return this;
	},

	/* events */

	subscribe: function(type, fn, bind){
		if (typeof type != 'string'){ // listen type / fn with object
			var subscriptions = [];
			for (var t in type) subscriptions.push(this.subscribe(t, type[t]));
			return function(){ // unsubscribe
				for (var i = 0, l = subscriptions.length; i < l; i++)
					subscriptions[i]();
				return this;
			};
		} else { // listen to one
			var bound = fn.bind(bind || this);
			var element = this.element;
			if (element.addEventListener){
				element.addEventListener(type, bound, false);
				return function(){ // unsubscribe
					element.removeEventListener(type, bound, false);
					return this;
				};
			} else {
				element.attachEvent('on' + type, bound);
				return function(){ // unsubscribe
					element.detachEvent('on' + type, bound);
					return this;
				};
			}
		}
	}

});

ART.Container = new Class({

	grab: function(){
		for (var i = 0; i < arguments.length; i++) arguments[i].inject(this);
		return this;
	}

});

var transformTo = function(xx, yx, xy, yy, tx, ty){
	if (xx && typeof xx == 'object'){
		yx = xx.yx; yy = xx.yy; ty = xx.ty;
		xy = xx.xy; tx = xx.tx; xx = xx.xx;
	}
	this.xx = xx == null ? 1 : xx;
	this.yx = yx || 0;
	this.xy = xy || 0;
	this.yy = yy == null ? 1 : yy;
	this.tx = (tx == null ? this.tx : tx) || 0;
	this.ty = (ty == null ? this.ty : ty) || 0;
	this._transform();
	return this;
};

var translate = function(x, y){
	this.tx += x || 0;
	this.ty += y || 0;
	this._transform();
	return this;
};

ART.Transform = new Class({

	initialize: transformTo,

	_transform: function(){},

	xx: 1, yx: 0, tx: 0,
	xy: 0, yy: 1, ty: 0,

	transform: function(xx, yx, xy, yy, tx, ty){
		var m = this;
		if (xx && typeof xx == 'object'){
			yx = xx.yx; yy = xx.yy; ty = xx.ty;
			xy = xx.xy; tx = xx.tx; xx = xx.xx;
		}
		if (!tx) tx = 0;
		if (!ty) ty = 0;
		return this.transformTo(
			m.xx * xx + m.xy * yx,
			m.yx * xx + m.yy * yx,
			m.xx * xy + m.xy * yy,
			m.yx * xy + m.yy * yy,
			m.xx * tx + m.xy * ty + m.tx,
			m.yx * tx + m.yy * ty + m.ty
		);
	},

	transformTo: transformTo,

	translate: translate,
	move: translate,

	scale: function(x, y){
		if (y == null) y = x;
		return this.transform(x, 0, 0, y, 0, 0);
	},

	rotate: function(deg, x, y){
		if (x == null || y == null){
			x = (this.left || 0) + (this.width || 0) / 2;
			y = (this.top || 0) + (this.height || 0) / 2;
		}

		var rad = deg * Math.PI / 180, sin = Math.sin(rad), cos = Math.cos(rad);

		this.transform(1, 0, 0, 1, x, y);
		var m = this;

		return this.transformTo(
			cos * m.xx - sin * m.yx,
			sin * m.xx + cos * m.yx,
			cos * m.xy - sin * m.yy,
			sin * m.xy + cos * m.yy,
			m.tx,
			m.ty
		).transform(1, 0, 0, 1, -x, -y);
	},

	moveTo: function(x, y){
		var m = this;
		return this.transformTo(m.xx, m.yx, m.xy, m.yy, x, y);
	},

	rotateTo: function(deg, x, y){
		var m = this;
		var flip = m.yx / m.xx > m.yy / m.xy ? -1 : 1;
		if (m.xx < 0 ? m.xy >= 0 : m.xy < 0) flip = -flip;
		return this.rotate(deg - Math.atan2(flip * m.yx, flip * m.xx) * 180 / Math.PI, x, y);
	},

	scaleTo: function(x, y){
		// Normalize
		var m = this;

		var h = Math.sqrt(m.xx * m.xx + m.yx * m.yx);
		m.xx /= h; m.yx /= h;

		h = Math.sqrt(m.yy * m.yy + m.xy * m.xy);
		m.yy /= h; m.xy /= h;

		return this.scale(x, y);
	},

	resizeTo: function(width, height){
		var w = this.width, h = this.height;
		if (!w || !h) return this;
		return this.scaleTo(width / w, height / h);
	},

	point: function(x, y){
		var m = this;
		return {
			x: m.xx * x + m.xy * y + m.tx,
			y: m.yx * x + m.yy * y + m.ty
		};
	}

});

Color.detach = function(color){
	color = new Color(color);
	return [Color.rgb(color.red, color.green, color.blue).toString(), color.alpha];
};

})();

<|endoftext|>


<commit_before>/* Gratefully copied from https://github.com/brentvatne/react-native-animated-demo-tinder */
'use strict';

import React, { Component } from 'react';

import {
  StyleSheet,
  Text,
  View,
  Animated,
  PanResponder,
  Image
} from 'react-native';

import clamp from 'clamp';

import Defaults from './Defaults.js';

const SWIPE_THRESHOLD = 120;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'transparent',
  },
  yup: {
    borderColor: 'green',
    borderWidth: 2,
    position: 'absolute',
    padding: 20,
    bottom: 20,
    borderRadius: 5,
    right: 20,
  },
  yupText: {
    fontSize: 16,
    color: 'green',
  },
  nope: {
    borderColor: 'red',
    borderWidth: 2,
    position: 'absolute',
    bottom: 20,
    padding: 20,
    borderRadius: 5,
    left: 20,
  },
  nopeText: {
    fontSize: 16,
    color: 'red',
  }
});

//Components could be unloaded and loaded and we will loose the users currentIndex, we can persist it here.
let currentIndex = {};
let guid = 0;

export default class SwipeCards extends Component {

  static propTypes = {
    cards: React.PropTypes.array,
    cardKey: React.PropTypes.string,
    loop: React.PropTypes.bool,
    allowGestureTermination: React.PropTypes.bool,
    stack: React.PropTypes.bool,
    stackGuid: React.PropTypes.string,
    stackDepth: React.PropTypes.number,
    stackOffsetX: React.PropTypes.number,
    stackOffsetY: React.PropTypes.number,
    renderNoMoreCards: React.PropTypes.func,
    showYup: React.PropTypes.bool,
    showNope: React.PropTypes.bool,
    handleYup: React.PropTypes.func,
    handleNope: React.PropTypes.func,
    yupText: React.PropTypes.string,
    noText: React.PropTypes.string,
    onClickHandler: React.PropTypes.func,
    renderCard: React.PropTypes.func,
    cardRemoved: React.PropTypes.func,
    dragY: React.PropTypes.bool
  };

  static defaultProps = {
    cards: [],
    cardKey: 'key',
    loop: false,
    allowGestureTermination: true,
    stack: false,
    stackDepth: 5,
    stackOffsetX: 25,
    stackOffsetY: 0,
    showYup: true,
    showNope: true,
    handleYup: (card) => null,
    handleNope: (card) => null,
    nopeText: "Nope!",
    yupText: "Yup!",
    onClickHandler: () => { alert('tap') },
    cardRemoved: (ix) => null,
    renderCard: (card) => null,
    style: styles.container,
    dragY: true
  };

  constructor(props) {
    super(props);

    //Use a persistent variable to track currentIndex instead of a local one.
    this.guid = this.props.guid || guid++;
    if (!currentIndex[this.guid]) currentIndex[this.guid] = 0;

    this.state = {
      pan: new Animated.ValueXY(),
      enter: new Animated.Value(0.5),
      cards: [].concat(this.props.cards),
      card: this.props.cards[currentIndex[this.guid]],
    };

    this.lastX = 0;
    this.lastY = 0;

    this.cardAnimation = null;

    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponderCapture: (e, gestureState) => {
        this.lastX = gestureState.moveX;
        this.lastY = gestureState.moveY;
        return false;
      },
      onMoveShouldSetPanResponderCapture: (e, gestureState) => {
        return (Math.abs(this.lastX - gestureState.moveX) > 5 || Math.abs(this.lastY - gestureState.moveY) > 5);
      },

      onPanResponderGrant: (e, gestureState) => {
        this.state.pan.setOffset({ x: this.state.pan.x._value, y: this.state.pan.y._value });
        this.state.pan.setValue({ x: 0, y: 0 });
      },

      onPanResponderTerminationRequest: (evt, gestureState) => this.props.allowGestureTermination,

      onPanResponderMove: Animated.event([
        null, { dx: this.state.pan.x, dy: this.props.dragY ? this.state.pan.y : 0 },
      ]),

      onPanResponderRelease: (e, {vx, vy, dx, dy}) => {
        this.state.pan.flattenOffset();
        let velocity;
        if ((dx === 0) && (dy === 0))   //meaning the gesture did not cover any distance
        {
          this.props.onClickHandler(this.state.card)
        }

        if (vx > 0) {
          velocity = clamp(vx, 3, 5);
        } else if (vx < 0) {
          velocity = clamp(vx * -1, 3, 5) * -1;
        }

        if (Math.abs(this.state.pan.x._value) > SWIPE_THRESHOLD) {

          let cancelled = false;

          if (this.state.pan.x._value > 0) {
            cancelled = this.props.handleYup(this.state.card);
          } else {
            cancelled = this.props.handleNope(this.state.card);
          }

          //Yup or nope was cancelled, return the card to normal.
          if (cancelled) {
            this._resetPan();
            return;
          };

          this.props.cardRemoved(currentIndex[this.guid]);

          this.cardAnimation = Animated.decay(this.state.pan, {
            velocity: { x: velocity, y: vy },
            deceleration: 0.98
          });
          this.cardAnimation.start(status => {
            if (status.finished) this._advanceState();
            else this._resetState();

            this.cardAnimation = null;
          }
          );

        } else {
          this._resetPan();
        }
      }
    });
  }

  _forceLeftSwipe() {
    this.cardAnimation = Animated.timing(this.state.pan, {
      toValue: { x: -500, y: 0 },
    }).start(status => {
      if (status.finished) this._advanceState();
      else this._resetState();

      this.cardAnimation = null;
    }
      );
    this.props.cardRemoved(currentIndex[this.guid]);
  }

  _forceRightSwipe() {
    this.cardAnimation = Animated.timing(this.state.pan, {
      toValue: { x: 500, y: 0 },
    }).start(status => {
      if (status.finished) this._advanceState();
      else this._resetState();

      this.cardAnimation = null;
    }
      );
    this.props.cardRemoved(currentIndex[this.guid]);
  }

  _goToNextCard() {
    currentIndex[this.guid]++;

    // Checks to see if last card.
    // If props.loop=true, will start again from the first card.
    if (currentIndex[this.guid] > this.state.cards.length - 1 && this.props.loop) {
      currentIndex[this.guid] = 0;
    }

    this.setState({
      card: this.state.cards[currentIndex[this.guid]]
    });
  }

  _goToPrevCard() {
    this.state.pan.setValue({ x: 0, y: 0 });
    this.state.enter.setValue(0);
    this._animateEntrance();

    currentIndex[this.guid]--;

    if (currentIndex[this.guid] < 0) {
      currentIndex[this.guid] = 0;
    }

    this.setState({
      card: this.state.cards[currentIndex[this.guid]]
    });
  }

  componentDidMount() {
    this._animateEntrance();
  }

  _animateEntrance() {
    Animated.spring(
      this.state.enter,
      { toValue: 1, friction: 8 }
    ).start();
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.cards !== this.props.cards) {

      if (this.cardAnimation) {
        this.cardAnimation.stop();
        this.cardAnimation = null;
      }

      currentIndex[this.guid] = 0;
      this.setState({
        cards: [].concat(nextProps.cards),
        card: nextProps.cards[0]
      });
    }
  }

  _resetPan() {
    Animated.spring(this.state.pan, {
      toValue: { x: 0, y: 0 },
      friction: 4
    }).start();
  }

  _resetState() {
    this.state.pan.setValue({ x: 0, y: 0 });
    this.state.enter.setValue(0);
    this._animateEntrance();
  }

  _advanceState() {
    this.state.pan.setValue({ x: 0, y: 0 });
    this.state.enter.setValue(0);
    this._animateEntrance();
    this._goToNextCard();
  }

  renderNoMoreCards() {
    if (this.props.renderNoMoreCards) {
      return this.props.renderNoMoreCards();
    }

    return <Defaults.NoMoreCards />;
  }

  /**
   * Renders the cards as a stack with props.stackDepth cards deep.
   */
  renderStack() {
    if (!this.state.card) {
      return this.renderNoMoreCards();
    }

    //Get the next stack of cards to render.
    let cards = this.state.cards.slice(currentIndex[this.guid], currentIndex[this.guid] + this.props.stackDepth).reverse();

    return cards.map((card, i) => {

      let offsetX = this.props.stackOffsetX * cards.length - i * this.props.stackOffsetX;
      let lastOffsetX = offsetX + this.props.stackOffsetX;

      let offsetY = this.props.stackOffsetY * cards.length - i * this.props.stackOffsetY;
      let lastOffsetY = offsetY + this.props.stackOffsetY;

      let opacity = 0.25 + (0.75 / cards.length) * (i + 1);
      let lastOpacity = 0.25 + (0.75 / cards.length) * i;

      let scale = 0.85 + (0.15 / cards.length) * (i + 1);
      let lastScale = 0.85 + (0.15 / cards.length) * i;

      let style = {
        position: 'absolute',
        top: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastOffsetY, offsetY] }),
        left: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastOffsetX, offsetX] }),
        opacity: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastOpacity, opacity] }),
        transform: [{ scale: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastScale, scale] }) }],
        elevation: i * 10
      };

      //Is this the top card?  If so animate it and hook up the pan handlers.
      if (i + 1 === cards.length) {
        let {pan} = this.state;
        let [translateX, translateY] = [pan.x, pan.y];

        let rotate = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: ["-30deg", "0deg", "30deg"] });
        let opacity = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: [0.5, 1, 0.5] });

        let animatedCardStyles = {
          ...style,
          transform: [
            { translateX: translateX },
            { translateY: translateY },
            { rotate: rotate },
            { scale: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastScale, scale] }) }
          ]
        };

        return <Animated.View key={card[this.props.cardKey]} style={[styles.card, animatedCardStyles]} {... this._panResponder.panHandlers}>
          {this.props.renderCard(this.state.card)}
        </Animated.View>;
      }

      return <Animated.View key={card[this.props.cardKey]} style={style}>{this.props.renderCard(card)}</Animated.View>;
    });
  }

  renderCard() {
    if (!this.state.card) {
      return this.renderNoMoreCards();
    }

    let {pan, enter} = this.state;
    let [translateX, translateY] = [pan.x, pan.y];

    let rotate = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: ["-30deg", "0deg", "30deg"] });
    let opacity = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: [0.5, 1, 0.5] });

    let scale = enter;

    let animatedCardStyles = { transform: [{ translateX }, { translateY }, { rotate }, { scale }], opacity };

    return <Animated.View key={"top"} style={[styles.card, animatedCardStyles]} {... this._panResponder.panHandlers}>
      {this.props.renderCard(this.state.card)}
    </Animated.View>;
  }

  renderNope() {
    let {pan} = this.state;

    let nopeOpacity = pan.x.interpolate({ inputRange: [-150, 0], outputRange: [1, 0] });
    let nopeScale = pan.x.interpolate({ inputRange: [-150, 0], outputRange: [1, 0.5], extrapolate: 'clamp' });
    let animatedNopeStyles = { transform: [{ scale: nopeScale }], opacity: nopeOpacity };

    if (this.props.renderNope) {
      return this.props.renderNope(pan);
    }

    if (this.props.showNope) {
      return <Animated.View style={[styles.nope, animatedNopeStyles]}>
        <Text style={styles.nopeText}>{this.props.nopeText}</Text>
      </Animated.View>;
    }

    return null;
  }

  renderYup() {
    let {pan} = this.state;

    let yupOpacity = pan.x.interpolate({ inputRange: [0, 150], outputRange: [0, 1] });
    let yupScale = pan.x.interpolate({ inputRange: [0, 150], outputRange: [0.5, 1], extrapolate: 'clamp' });
    let animatedYupStyles = { transform: [{ scale: yupScale }], opacity: yupOpacity };

    if (this.props.renderYup) {
      return this.props.renderYup(pan);
    }

    if (this.props.showYup) {
      return <Animated.View style={[styles.yup, animatedYupStyles]}>
        <Text style={styles.yupText}>{this.props.yupText}</Text>
      </Animated.View>;
    }

    return null;
  }

  render() {
    return (
      <View style={styles.container}>
        {this.props.stack ? this.renderStack() : this.renderCard()}
        {this.renderNope()}
        {this.renderYup()}
      </View>
    );
  }
}
<commit_msg>add vert check to allow scrolling<commit_after>/* Gratefully copied from https://github.com/brentvatne/react-native-animated-demo-tinder */
'use strict';

import React, { Component } from 'react';

import {
  StyleSheet,
  Text,
  View,
  Animated,
  PanResponder,
  Image
} from 'react-native';

import clamp from 'clamp';

import Defaults from './Defaults.js';

const SWIPE_THRESHOLD = 120;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'transparent',
  },
  yup: {
    borderColor: 'green',
    borderWidth: 2,
    position: 'absolute',
    padding: 20,
    bottom: 20,
    borderRadius: 5,
    right: 20,
  },
  yupText: {
    fontSize: 16,
    color: 'green',
  },
  nope: {
    borderColor: 'red',
    borderWidth: 2,
    position: 'absolute',
    bottom: 20,
    padding: 20,
    borderRadius: 5,
    left: 20,
  },
  nopeText: {
    fontSize: 16,
    color: 'red',
  }
});

//Components could be unloaded and loaded and we will loose the users currentIndex, we can persist it here.
let currentIndex = {};
let guid = 0;

export default class SwipeCards extends Component {

  static propTypes = {
    cards: React.PropTypes.array,
    cardKey: React.PropTypes.string,
    loop: React.PropTypes.bool,
    allowGestureTermination: React.PropTypes.bool,
    stack: React.PropTypes.bool,
    stackGuid: React.PropTypes.string,
    stackDepth: React.PropTypes.number,
    stackOffsetX: React.PropTypes.number,
    stackOffsetY: React.PropTypes.number,
    renderNoMoreCards: React.PropTypes.func,
    showYup: React.PropTypes.bool,
    showNope: React.PropTypes.bool,
    handleYup: React.PropTypes.func,
    handleNope: React.PropTypes.func,
    yupText: React.PropTypes.string,
    noText: React.PropTypes.string,
    onClickHandler: React.PropTypes.func,
    renderCard: React.PropTypes.func,
    cardRemoved: React.PropTypes.func,
    dragY: React.PropTypes.bool
  };

  static defaultProps = {
    cards: [],
    cardKey: 'key',
    loop: false,
    allowGestureTermination: true,
    stack: false,
    stackDepth: 5,
    stackOffsetX: 25,
    stackOffsetY: 0,
    showYup: true,
    showNope: true,
    handleYup: (card) => null,
    handleNope: (card) => null,
    nopeText: "Nope!",
    yupText: "Yup!",
    onClickHandler: () => { alert('tap') },
    cardRemoved: (ix) => null,
    renderCard: (card) => null,
    style: styles.container,
    dragY: true
  };

  constructor(props) {
    super(props);

    //Use a persistent variable to track currentIndex instead of a local one.
    this.guid = this.props.guid || guid++;
    if (!currentIndex[this.guid]) currentIndex[this.guid] = 0;

    this.state = {
      pan: new Animated.ValueXY(0),
      enter: new Animated.Value(0.5),
      cards: [].concat(this.props.cards),
      card: this.props.cards[currentIndex[this.guid]],
    };

    this.lastX = 0;
    this.lastY = 0;

    this.cardAnimation = null;

    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponderCapture: (e, gestureState) => {
        this.lastX = gestureState.moveX;
        this.lastY = gestureState.moveY;
        return false;
      },
      onMoveShouldSetPanResponderCapture: (e, gestureState) => {
        if (Math.abs(gestureState.dx) < Math.abs(gestureState.dy)) return false;
        if ((gestureState.dx === 0) && (gestureState.dy === 0))   return false;
        return (Math.abs(this.lastX - gestureState.moveX) > 5 || Math.abs(this.lastY - gestureState.moveY) > 5);
      },

      onPanResponderGrant: (e, gestureState) => {
        this.state.pan.setOffset({ x: this.state.pan.x._value, y: this.state.pan.y._value });
        this.state.pan.setValue({ x: 0, y: 0 });
      },

      onPanResponderTerminationRequest: (evt, gestureState) => this.props.allowGestureTermination,

      onPanResponderMove: Animated.event([
        null, { dx: this.state.pan.x, dy: this.props.dragY ? this.state.pan.y : 0 },
      ]),

      onPanResponderRelease: (e, {vx, vy, dx, dy}) => {
        this.state.pan.flattenOffset();
        let velocity;
        if ((dx === 0) && (dy === 0))   //meaning the gesture did not cover any distance
        {
          this.props.onClickHandler(this.state.card)
        }

        if (vx > 0) {
          velocity = clamp(vx, 3, 5);
        } else if (vx < 0) {
          velocity = clamp(vx * -1, 3, 5) * -1;
        }

        if (Math.abs(this.state.pan.x._value) > SWIPE_THRESHOLD) {

          let cancelled = false;

          if (this.state.pan.x._value > 0) {
            cancelled = this.props.handleYup(this.state.card);
          } else {
            cancelled = this.props.handleNope(this.state.card);
          }

          //Yup or nope was cancelled, return the card to normal.
          if (cancelled) {
            this._resetPan();
            return;
          };

          this.props.cardRemoved(currentIndex[this.guid]);

          this.cardAnimation = Animated.decay(this.state.pan, {
            velocity: { x: velocity, y: vy },
            deceleration: 0.98
          });
          this.cardAnimation.start(status => {
            if (status.finished) this._advanceState();
            else this._resetState();

            this.cardAnimation = null;
          }
          );

        } else {
          this._resetPan();
        }
      }
    });
  }

  _forceLeftSwipe() {
    this.cardAnimation = Animated.timing(this.state.pan, {
      toValue: { x: -500, y: 0 },
    }).start(status => {
      if (status.finished) this._advanceState();
      else this._resetState();

      this.cardAnimation = null;
    }
      );
    this.props.cardRemoved(currentIndex[this.guid]);
  }

  _forceRightSwipe() {
    this.cardAnimation = Animated.timing(this.state.pan, {
      toValue: { x: 500, y: 0 },
    }).start(status => {
      if (status.finished) this._advanceState();
      else this._resetState();

      this.cardAnimation = null;
    }
      );
    this.props.cardRemoved(currentIndex[this.guid]);
  }

  _goToNextCard() {
    currentIndex[this.guid]++;

    // Checks to see if last card.
    // If props.loop=true, will start again from the first card.
    if (currentIndex[this.guid] > this.state.cards.length - 1 && this.props.loop) {
      currentIndex[this.guid] = 0;
    }

    this.setState({
      card: this.state.cards[currentIndex[this.guid]]
    });
  }

  _goToPrevCard() {
    this.state.pan.setValue({ x: 0, y: 0 });
    this.state.enter.setValue(0);
    this._animateEntrance();

    currentIndex[this.guid]--;

    if (currentIndex[this.guid] < 0) {
      currentIndex[this.guid] = 0;
    }

    this.setState({
      card: this.state.cards[currentIndex[this.guid]]
    });
  }

  componentDidMount() {
    this._animateEntrance();
  }

  _animateEntrance() {
    Animated.spring(
      this.state.enter,
      { toValue: 1, friction: 8 }
    ).start();
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.cards !== this.props.cards) {

      if (this.cardAnimation) {
        this.cardAnimation.stop();
        this.cardAnimation = null;
      }

      currentIndex[this.guid] = 0;
      this.setState({
        cards: [].concat(nextProps.cards),
        card: nextProps.cards[0]
      });
    }
  }

  _resetPan() {
    Animated.spring(this.state.pan, {
      toValue: { x: 0, y: 0 },
      friction: 4
    }).start();
  }

  _resetState() {
    this.state.pan.setValue({ x: 0, y: 0 });
    this.state.enter.setValue(0);
    this._animateEntrance();
  }

  _advanceState() {
    this.state.pan.setValue({ x: 0, y: 0 });
    this.state.enter.setValue(0);
    this._animateEntrance();
    this._goToNextCard();
  }

  renderNoMoreCards() {
    if (this.props.renderNoMoreCards) {
      return this.props.renderNoMoreCards();
    }

    return <Defaults.NoMoreCards />;
  }

  /**
   * Renders the cards as a stack with props.stackDepth cards deep.
   */
  renderStack() {
    if (!this.state.card) {
      return this.renderNoMoreCards();
    }

    //Get the next stack of cards to render.
    let cards = this.state.cards.slice(currentIndex[this.guid], currentIndex[this.guid] + this.props.stackDepth).reverse();

    return cards.map((card, i) => {

      let offsetX = this.props.stackOffsetX * cards.length - i * this.props.stackOffsetX;
      let lastOffsetX = offsetX + this.props.stackOffsetX;

      let offsetY = this.props.stackOffsetY * cards.length - i * this.props.stackOffsetY;
      let lastOffsetY = offsetY + this.props.stackOffsetY;

      let opacity = 0.25 + (0.75 / cards.length) * (i + 1);
      let lastOpacity = 0.25 + (0.75 / cards.length) * i;

      let scale = 0.85 + (0.15 / cards.length) * (i + 1);
      let lastScale = 0.85 + (0.15 / cards.length) * i;

      let style = {
        position: 'absolute',
        top: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastOffsetY, offsetY] }),
        left: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastOffsetX, offsetX] }),
        opacity: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastOpacity, opacity] }),
        transform: [{ scale: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastScale, scale] }) }],
        elevation: i * 10
      };

      //Is this the top card?  If so animate it and hook up the pan handlers.
      if (i + 1 === cards.length) {
        let {pan} = this.state;
        let [translateX, translateY] = [pan.x, pan.y];

        let rotate = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: ["-30deg", "0deg", "30deg"] });
        let opacity = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: [0.5, 1, 0.5] });

        let animatedCardStyles = {
          ...style,
          transform: [
            { translateX: translateX },
            { translateY: translateY },
            { rotate: rotate },
            { scale: this.state.enter.interpolate({ inputRange: [0, 1], outputRange: [lastScale, scale] }) }
          ]
        };

        return <Animated.View key={card[this.props.cardKey]} style={[styles.card, animatedCardStyles]} {... this._panResponder.panHandlers}>
          {this.props.renderCard(this.state.card)}
        </Animated.View>;
      }

      return <Animated.View key={card[this.props.cardKey]} style={style}>{this.props.renderCard(card)}</Animated.View>;
    });
  }

  renderCard() {
    if (!this.state.card) {
      return this.renderNoMoreCards();
    }

    let {pan, enter} = this.state;
    let [translateX, translateY] = [pan.x, pan.y];

    let rotate = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: ["-30deg", "0deg", "30deg"] });
    let opacity = pan.x.interpolate({ inputRange: [-200, 0, 200], outputRange: [0.5, 1, 0.5] });

    let scale = enter;

    let animatedCardStyles = { transform: [{ translateX }, { translateY }, { rotate }, { scale }], opacity };

    return <Animated.View key={"top"} style={[styles.card, animatedCardStyles]} {... this._panResponder.panHandlers}>
      {this.props.renderCard(this.state.card)}
    </Animated.View>;
  }

  renderNope() {
    let {pan} = this.state;

    let nopeOpacity = pan.x.interpolate({ inputRange: [-150, 0], outputRange: [1, 0] });
    let nopeScale = pan.x.interpolate({ inputRange: [-150, 0], outputRange: [1, 0.5], extrapolate: 'clamp' });
    let animatedNopeStyles = { transform: [{ scale: nopeScale }], opacity: nopeOpacity };

    if (this.props.renderNope) {
      return this.props.renderNope(pan);
    }

    if (this.props.showNope) {
      return <Animated.View style={[styles.nope, animatedNopeStyles]}>
        <Text style={styles.nopeText}>{this.props.nopeText}</Text>
      </Animated.View>;
    }

    return null;
  }

  renderYup() {
    let {pan} = this.state;

    let yupOpacity = pan.x.interpolate({ inputRange: [0, 150], outputRange: [0, 1] });
    let yupScale = pan.x.interpolate({ inputRange: [0, 150], outputRange: [0.5, 1], extrapolate: 'clamp' });
    let animatedYupStyles = { transform: [{ scale: yupScale }], opacity: yupOpacity };

    if (this.props.renderYup) {
      return this.props.renderYup(pan);
    }

    if (this.props.showYup) {
      return <Animated.View style={[styles.yup, animatedYupStyles]}>
        <Text style={styles.yupText}>{this.props.yupText}</Text>
      </Animated.View>;
    }

    return null;
  }

  render() {
    return (
      <View style={styles.container}>
        {this.props.stack ? this.renderStack() : this.renderCard()}
        {this.renderNope()}
        {this.renderYup()}
      </View>
    );
  }
}
<|endoftext|>


<commit_before>/**
 * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.html or http://ckeditor.com/license
 */

CKEDITOR.editorConfig = function( config ) {
	// Define changes to default configuration here. For example:
	// config.language = 'fr';
	// config.uiColor = '#AADC6E';
};
<commit_msg>update vendor path<commit_after>/**
 * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.html or http://ckeditor.com/license
 */

CKEDITOR.editorConfig = function( config ) {
	// Define changes to default configuration here. For example:
	// config.language = 'fr';
	// config.uiColor = '#AADC6E';
  myFonts = ['Muli'];

  config.font_names = 'sans serif';

  for(var i = 0; i<myFonts.length; i++){
     config.font_names = config.font_names+';'+myFonts[i];
     myFonts[i] = 'http://fonts.googleapis.com/css?family='+myFonts[i].replace(' ','+');

     //assuming you use jquery
     $("head").append("<link rel='stylesheet' href='"+ myFonts[i] +"'>");
  }
};
<|endoftext|>


<commit_before>'use strict';

const request = require('request');
const core = require('./../core/core.js');
const db = require('./../aslan-messenger/aslan-db.js');

/*
* Fonction appelé par les routes lors de la reception d'un message
*/
function receivedMessage(message, user) {
    console.log("Aslan received a message");
    var m = {
        channel: "Aslan",
        senderID: user._id,
        timestamp: new Date(),
        text: message.text,
        first_name: user.firstname,
        last_name: user.name
    };
    console.log(JSON.stringify(m));
    core.receivedMessage(m);
}

/*
* Fonction appelé par le core pour envoyer un message sur la channel Aslan Messenger
*/
function sendMessage(message) {
	switch(message.type) {
        case "text":
            sendTextMessage(message);
        break;
        case "quickreply":
            sendQuickReplyMessage(message);
        break;
    }
}

/*
* Fonction appelé par le core pour envoyer des messages sur la channel Aslan Messenger
*/
function sendMessages(messages) {
    for(var i=0;i<messages.length;i++) {
        sendMessage(messages[i]);
    }
}

/*
* Fonction qui sauvegarde un message en DB
*/
function save(m) {
    console.log(JSON.stringify(m));
    var user_id = m.senderID;
    var message = m.message;
    if(user_id != undefined && message != undefined) {
        db.createMessage(user_id, message, true, function(data){
            console.log(data)
            if(data.length == 0) {
                console.log("Error create message");
            } else {
                console.log(JSON.stringify(data[0]));
            }
        });
    }
}

/*
* Fonction qui envoie un message de type text
*/
function sendTextMessage(message) {
    if(message.senderID != undefined && message.text != undefined) {
        message.message = {text : message.text, type: "text"};
        save(message);
    }
}

/*
* Fonction qui envoie un message de type quickreply
*/
function sendQuickReplyMessage(message) {
    if(message.senderID != undefined && message.text != undefined) {
        message.message = {text : message.text, type: "quickreplies"};
        save(message);
    }
}

/*
* Fonction qui envoie un message audio/file/image/video
*/
function sendFileMessage(message) {

}

exports.receivedMessage = receivedMessage;
exports.sendMessage = sendMessage;
exports.sendMessages = sendMessages;<commit_msg>Fix bugs Aslan-Messenger<commit_after>'use strict';

const request = require('request');
const core = require('./../core/core.js');
const db = require('./../aslan-messenger/aslan-db.js');

/*
* Fonction appelé par les routes lors de la reception d'un message
*/
function receivedMessage(message, user) {
    console.log("Aslan received a message");
    var m = {
        channel: "Aslan",
        senderID: user._id,
        timestamp: new Date(),
        text: message.text,
        first_name: user.firstname,
        last_name: user.name
    };
    console.log(JSON.stringify(m));
    core.receivedMessage(m);
}

/*
* Fonction appelé par le core pour envoyer un message sur la channel Aslan Messenger
*/
function sendMessage(message) {
	switch(message.type) {
        case "text":
            sendTextMessage(message);
        break;
        case "quickreply":
            sendQuickReplyMessage(message);
        break;
    }
}

/*
* Fonction appelé par le core pour envoyer des messages sur la channel Aslan Messenger
*/
function sendMessages(messages) {
    for(var i=0;i<messages.length;i++) {
        sendMessage(messages[i]);
    }
}

/*
* Fonction qui sauvegarde un message en DB
*/
function save(m) {
    console.log(JSON.stringify(m));
    var user_id = m.senderID.toString();
    var message = m.message;
    if(user_id != undefined && message != undefined) {
        db.createMessage(user_id, message, true, function(data){
            console.log(data)
            if(data.length == 0) {
                console.log("Error create message");
            } else {
                console.log(JSON.stringify(data[0]));
            }
        });
    }
}

/*
* Fonction qui envoie un message de type text
*/
function sendTextMessage(message) {
    if(message.senderID != undefined && message.text != undefined) {
        message.message = {text : message.text, type: "text"};
        save(message);
    }
}

/*
* Fonction qui envoie un message de type quickreply
*/
function sendQuickReplyMessage(message) {
    if(message.senderID != undefined && message.text != undefined) {
        message.message = {text : message.text, type: "quickreplies"};
        save(message);
    }
}

/*
* Fonction qui envoie un message audio/file/image/video
*/
function sendFileMessage(message) {

}

exports.receivedMessage = receivedMessage;
exports.sendMessage = sendMessage;
exports.sendMessages = sendMessages;<|endoftext|>


<commit_before>backupApp.controller('LogController', function($scope,$routeParams, SystemInfo, ServerStatus, AppService, DialogService) {
    $scope.state = ServerStatus.watch($scope);
    $scope.BackupID = $routeParams.backupid;
    $scope.SystemInfo = SystemInfo.watch($scope);

    var liveRefreshTimer = null;
    var PAGE_SIZE = 100;

    function updateLivePoll() {
    	if ($scope.LiveRefreshing) {
			$scope.LiveRefreshPending = true;
			return;
    	}

    	if (liveRefreshTimer != null) {
    		clearTimeout(liveRefreshTimer);
    		liveRefreshTimer = null;
    	}

		if ($scope.Page != 'live' || $scope.LiveLogLevel == '')
			return;

		$scope.LiveRefreshPending = false;
		$scope.LiveRefreshing = true;
		AppService.get('/logdata/poll?level=' + $scope.LiveLogLevel + '&id=' + $scope.LiveRefreshID + '&pagesize=' + PAGE_SIZE).then(
			function(resp) {
                for(var n in resp.data)
                    $scope.LiveRefreshID = Math.max($scope.LiveRefreshID, resp.data[n].ID);

                if ($scope.LiveData == null)
                	$scope.LiveData = [];

				resp.data.reverse();
                $scope.LiveData.unshift.apply($scope.LiveData, resp.data);
                $scope.LiveData.Length = Math.min(1000, $scope.LiveData.length);

                $scope.LiveRefreshing = false;
                if ($scope.LiveRefreshPending)
                	updateLivePoll();
                else
                	if ($scope.Page == 'live' && $scope.LiveLogLevel != '')
                		liveRefreshTimer = setTimeout(updateLivePoll, 3000);

			}, function(resp) {
            	if ($scope.Page == 'live' && $scope.LiveLogLevel != '')
            		liveRefreshTimer = setTimeout(updateLivePoll, 3000);
			}
		);

    };

    function LoadMoreData(url, key, idfield) {
    	if ($scope.LoadingData)
    		return;

    	var last = null;
    	if ($scope[key] != null && $scope[key].length > 0 )
    		last = $scope[key][$scope[key].length - 1][idfield];

    	$scope.LoadingData = true;
    	AppService.get(url + '?pagesize=' + PAGE_SIZE + (last == null ? '' : ('&offset=' + last))).then(
    		function(resp) {
    			if ($scope[key] == null)
    				$scope[key] = [];
    			$scope[key].push.apply($scope[key], resp.data);
    			$scope.LoadingData = false;
    			$scope[key + 'Complete'] = resp.data.length < PAGE_SIZE;
    		}, function(resp) {
                var message = resp.statusText;
                if (resp.data != null && resp.data.Message != null)
                    message = resp.data.Message;

                $scope.LoadingData = false;
                DialogService.dialog('Error', 'Failed to connect: ' + message);
    		});
    };

    $scope.$watch('LiveLogLevel', updateLivePoll);
    $scope.$watch('Page', updateLivePoll);
    $scope.$watch('Page', function() {
    	if ($scope.Page == 'remote' && $scope.RemoteData == null)
    		$scope.LoadMoreRemoteData();
    });

    if ($scope.BackupID == null) {
    	$scope.Page = 'stored';
    	$scope.LiveLogLevel = '';
    	$scope.LiveRefreshID = 0;
    	$scope.LiveRefreshing = false;
    	$scope.LiveRefreshPending = false;

		$scope.LoadMoreStoredData = function() { LoadMoreData('/logdata/log', 'LogData', 'Timestamp'); };
    	$scope.LoadMoreStoredData();
    } else {
    	$scope.Page = 'general';

		$scope.LoadMoreGeneralData = function() { LoadMoreData('/backup/' + $scope.BackupID + '/log', 'GeneralData', 'ID'); };
		$scope.LoadMoreRemoteData = function() { LoadMoreData('/backup/' + $scope.BackupID + '/remotelog', 'RemoteData', 'ID'); };

		$scope.LoadMoreGeneralData();
    }

});<commit_msg>Bugfix for log running multiple checks<commit_after>backupApp.controller('LogController', function($scope, $routeParams, SystemInfo, ServerStatus, AppService, DialogService) {
    $scope.state = ServerStatus.watch($scope);
    $scope.BackupID = $routeParams.backupid;
    $scope.SystemInfo = SystemInfo.watch($scope);

    var liveRefreshTimer = null;
    var PAGE_SIZE = 100;

    function updateLivePoll() {
    	if ($scope.LiveRefreshing) {
			$scope.LiveRefreshPending = true;
			return;
    	}

    	if (liveRefreshTimer != null) {
    		clearTimeout(liveRefreshTimer);
    		liveRefreshTimer = null;
    	}

		if ($scope.Page != 'live' || ($scope.LiveLogLevel || '') == '')
			return;

		$scope.LiveRefreshPending = false;
		$scope.LiveRefreshing = true;
		AppService.get('/logdata/poll?level=' + $scope.LiveLogLevel + '&id=' + $scope.LiveRefreshID + '&pagesize=' + PAGE_SIZE).then(
			function(resp) {
                for(var n in resp.data)
                    $scope.LiveRefreshID = Math.max($scope.LiveRefreshID, resp.data[n].ID);

                if ($scope.LiveData == null)
                	$scope.LiveData = [];

				resp.data.reverse();
                $scope.LiveData.unshift.apply($scope.LiveData, resp.data);
                $scope.LiveData.Length = Math.min(1000, $scope.LiveData.length);

                $scope.LiveRefreshing = false;
                if ($scope.LiveRefreshPending)
                	updateLivePoll();
                else
                	if ($scope.Page == 'live' && $scope.LiveLogLevel != '')
                		liveRefreshTimer = setTimeout(updateLivePoll, 3000);

			}, function(resp) {
            	if ($scope.Page == 'live' && $scope.LiveLogLevel != '')
            		liveRefreshTimer = setTimeout(updateLivePoll, 3000);
			}
		);

    };

    function LoadMoreData(url, key, idfield) {
    	if ($scope.LoadingData)
    		return;

    	var last = null;
    	if ($scope[key] != null && $scope[key].length > 0 )
    		last = $scope[key][$scope[key].length - 1][idfield];

    	$scope.LoadingData = true;
    	AppService.get(url + '?pagesize=' + PAGE_SIZE + (last == null ? '' : ('&offset=' + last))).then(
    		function(resp) {
    			if ($scope[key] == null)
    				$scope[key] = [];
    			$scope[key].push.apply($scope[key], resp.data);
    			$scope.LoadingData = false;
    			$scope[key + 'Complete'] = resp.data.length < PAGE_SIZE;
    		}, function(resp) {
                var message = resp.statusText;
                if (resp.data != null && resp.data.Message != null)
                    message = resp.data.Message;

                $scope.LoadingData = false;
                DialogService.dialog('Error', 'Failed to connect: ' + message);
    		});
    };

    $scope.$watch('LiveLogLevel', updateLivePoll);
    $scope.$watch('Page', updateLivePoll);
    $scope.$watch('Page', function() {
    	if ($scope.Page == 'remote' && $scope.RemoteData == null)
    		$scope.LoadMoreRemoteData();
    });

    if ($scope.BackupID == null) {
    	$scope.Page = 'stored';
    	$scope.LiveLogLevel = '';
    	$scope.LiveRefreshID = 0;
    	$scope.LiveRefreshing = false;
    	$scope.LiveRefreshPending = false;

		$scope.LoadMoreStoredData = function() { LoadMoreData('/logdata/log', 'LogData', 'Timestamp'); };
    	$scope.LoadMoreStoredData();
    } else {
    	$scope.Page = 'general';

		$scope.LoadMoreGeneralData = function() { LoadMoreData('/backup/' + $scope.BackupID + '/log', 'GeneralData', 'ID'); };
		$scope.LoadMoreRemoteData = function() { LoadMoreData('/backup/' + $scope.BackupID + '/remotelog', 'RemoteData', 'ID'); };

		$scope.LoadMoreGeneralData();
    }

});<|endoftext|>